Application lock (iOS)

This commit is contained in:
niuhuan 2022-05-14 21:45:00 +08:00
parent 7a8e70e693
commit 5f3b73d12c
8 changed files with 157 additions and 30 deletions

View File

@ -37,7 +37,7 @@ PODS:
- image_cropper (0.0.4): - image_cropper (0.0.4):
- Flutter - Flutter
- TOCropViewController (~> 2.6.1) - TOCropViewController (~> 2.6.1)
- image_picker (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- "permission_handler (5.1.0+2)": - "permission_handler (5.1.0+2)":
- Flutter - Flutter
@ -53,7 +53,7 @@ DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- image_cropper (from `.symlinks/plugins/image_cropper/ios`) - image_cropper (from `.symlinks/plugins/image_cropper/ios`)
- image_picker (from `.symlinks/plugins/image_picker/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- permission_handler (from `.symlinks/plugins/permission_handler/ios`) - permission_handler (from `.symlinks/plugins/permission_handler/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@ -72,8 +72,8 @@ EXTERNAL SOURCES:
:path: Flutter :path: Flutter
image_cropper: image_cropper:
:path: ".symlinks/plugins/image_cropper/ios" :path: ".symlinks/plugins/image_cropper/ios"
image_picker: image_picker_ios:
:path: ".symlinks/plugins/image_picker/ios" :path: ".symlinks/plugins/image_picker_ios/ios"
permission_handler: permission_handler:
:path: ".symlinks/plugins/permission_handler/ios" :path: ".symlinks/plugins/permission_handler/ios"
url_launcher_ios: url_launcher_ios:
@ -85,7 +85,7 @@ SPEC CHECKSUMS:
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98 image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98
image_picker: 541dcbb3b9cf32d87eacbd957845d8651d6c62c3 image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0
SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
@ -94,4 +94,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
COCOAPODS: 1.11.2 COCOAPODS: 1.11.3

View File

@ -1,6 +1,7 @@
import UIKit import UIKit
import Flutter import Flutter
import Mobile import Mobile
import LocalAuthentication
@UIApplicationMain @UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate {
@ -34,6 +35,18 @@ import Mobile
result(FlutterError(code: "", message: "params error", details: "")) result(FlutterError(code: "", message: "params error", details: ""))
} }
} }
else if call.method == "verifyAuthentication"{
let context = LAContext()
let can = context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
guard can == true else {
result(false)
return
}
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "身份验证") { (success, error) in
result(success)
}
}
else if call.method == "iosSaveFileToImage"{ else if call.method == "iosSaveFileToImage"{
if let args = call.arguments as? Dictionary<String, Any>, if let args = call.arguments as? Dictionary<String, Any>,
let path = args["path"] as? String{ let path = args["path"] as? String{

View File

@ -717,4 +717,8 @@ class Method {
List list = json.decode(rsp); List list = json.decode(rsp);
return list.map((e) => Collection.fromJson(e)).toList(); return list.map((e) => Collection.fromJson(e)).toList();
} }
Future<bool> verifyAuthentication() async {
return await _channel.invokeMethod("verifyAuthentication");
}
} }

View File

@ -0,0 +1,48 @@
import 'dart:io';
import 'package:flutter/material.dart';
import '../Common.dart';
import '../Method.dart';
const _propertyName = "authentication";
late bool _authentication;
Future<void> initAuthentication() async {
_authentication =
(await method.loadProperty(_propertyName, "false")) == "true";
}
bool currentAuthentication() {
return _authentication;
}
Future<void> _chooseAuthentication(BuildContext context) async {
if (await method.verifyAuthentication()) {
String? result =
await chooseListDialog<String>(context, "进入APP时验证身份", ["", ""]);
if (result != null) {
var target = result == "";
await method.saveProperty(_propertyName, "$target");
_authentication = target;
}
}
}
Widget authenticationSetting() {
if (Platform.isIOS != true) {
return Container();
}
return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) {
return ListTile(
title: const Text("进入APP时验证身份"),
subtitle: Text(_authentication ? "" : ""),
onTap: () async {
await _chooseAuthentication(context);
setState(() {});
},
);
},
);
}

View File

@ -63,6 +63,9 @@ Future<dynamic> _inputChooserRoot(BuildContext context) async {
} }
Widget chooserRootSetting() { Widget chooserRootSetting() {
if (Platform.isIOS) {
return Container();
}
return StatefulBuilder( return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) { builder: (BuildContext context, void Function(void Function()) setState) {
return ListTile( return ListTile(

View File

@ -5,6 +5,7 @@ import 'package:pikapika/basic/Method.dart';
import 'package:pikapika/basic/config/Themes.dart'; import 'package:pikapika/basic/config/Themes.dart';
import 'package:pikapika/basic/enum/ErrorTypes.dart'; import 'package:pikapika/basic/enum/ErrorTypes.dart';
import 'package:pikapika/screens/RegisterScreen.dart'; import 'package:pikapika/screens/RegisterScreen.dart';
import 'package:pikapika/screens/SettingsScreen.dart';
import 'package:pikapika/screens/components/NetworkSetting.dart'; import 'package:pikapika/screens/components/NetworkSetting.dart';
import 'AppScreen.dart'; import 'AppScreen.dart';
@ -59,6 +60,19 @@ class _AccountScreenState extends State<AccountScreen> {
appBar: AppBar( appBar: AppBar(
title: const Text('配置选项'), title: const Text('配置选项'),
actions: [ actions: [
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SettingsScreen(
hiddenAccountInfo: true,
),
),
);
},
icon: const Text('设置'),
),
IconButton( IconButton(
onPressed: () { onPressed: () {
if (androidNightModeDisplay) { if (androidNightModeDisplay) {

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:pikapika/basic/config/Address.dart'; import 'package:pikapika/basic/config/Address.dart';
import 'package:pikapika/basic/config/AndroidDisplayMode.dart'; import 'package:pikapika/basic/config/AndroidDisplayMode.dart';
import 'package:pikapika/basic/config/AndroidSecureFlag.dart'; import 'package:pikapika/basic/config/AndroidSecureFlag.dart';
import 'package:pikapika/basic/config/Authentication.dart';
import 'package:pikapika/basic/config/AutoClean.dart'; import 'package:pikapika/basic/config/AutoClean.dart';
import 'package:pikapika/basic/config/AutoFullScreen.dart'; import 'package:pikapika/basic/config/AutoFullScreen.dart';
import 'package:pikapika/basic/config/ChooserRoot.dart'; import 'package:pikapika/basic/config/ChooserRoot.dart';
@ -43,6 +44,8 @@ class InitScreen extends StatefulWidget {
} }
class _InitScreenState extends State<InitScreen> { class _InitScreenState extends State<InitScreen> {
var _authenticating = false;
@override @override
initState() { initState() {
_init(); _init();
@ -83,7 +86,51 @@ class _InitScreenState extends State<InitScreen> {
await initExportRename(); await initExportRename();
await initVersion(); await initVersion();
await initUsingRightClickPop(); await initUsingRightClickPop();
await initAuthentication();
autoCheckNewVersion(); autoCheckNewVersion();
setState(() {
_authenticating = currentAuthentication();
});
if (_authenticating) {
_goAuthentication();
} else {
_goApplication();
}
}
@override
Widget build(BuildContext context) {
if (_authenticating) {
return Scaffold(
appBar: AppBar(
title: const Text("身份验证"),
),
body: Center(
child: Container(
padding: const EdgeInsets.all(20),
child: MaterialButton(
onPressed: () {
_goAuthentication();
},
child: const Text('您在之前使用APP时开启了身份验证, 请点这段文字进行身份核查, 核查通过后将会进入APP'),
),
),
),
);
}
return Scaffold(
backgroundColor: const Color(0xfffffced),
body: ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: Image.asset(
"lib/assets/init.jpg",
fit: BoxFit.contain,
),
),
);
}
Future _goApplication() async {
// , token失效重新登录, 1 // , token失效重新登录, 1
if (await method.preLogin()) { if (await method.preLogin()) {
// token或username+password有效则直接进入登录好的界面 // token或username+password有效则直接进入登录好的界面
@ -100,17 +147,9 @@ class _InitScreenState extends State<InitScreen> {
} }
} }
@override Future _goAuthentication() async {
Widget build(BuildContext context) { if (await method.verifyAuthentication()) {
return Scaffold( _goApplication();
backgroundColor: const Color(0xfffffced), }
body: ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: Image.asset(
"lib/assets/init.jpg",
fit: BoxFit.contain,
),
),
);
} }
} }

View File

@ -29,13 +29,17 @@ import 'package:pikapika/basic/config/shadowCategoriesMode.dart';
import 'package:pikapika/screens/components/NetworkSetting.dart'; import 'package:pikapika/screens/components/NetworkSetting.dart';
import 'package:pikapika/screens/components/RightClickPop.dart'; import 'package:pikapika/screens/components/RightClickPop.dart';
import '../basic/config/Authentication.dart';
import '../basic/config/UsingRightClickPop.dart'; import '../basic/config/UsingRightClickPop.dart';
import 'CleanScreen.dart'; import 'CleanScreen.dart';
import 'MigrateScreen.dart'; import 'MigrateScreen.dart';
import 'ModifyPasswordScreen.dart'; import 'ModifyPasswordScreen.dart';
class SettingsScreen extends StatelessWidget { class SettingsScreen extends StatelessWidget {
const SettingsScreen({Key? key}) : super(key: key); final bool hiddenAccountInfo;
const SettingsScreen({Key? key, this.hiddenAccountInfo = false})
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -51,16 +55,18 @@ class SettingsScreen extends StatelessWidget {
body: ListView( body: ListView(
children: [ children: [
const Divider(), const Divider(),
ListTile( hiddenAccountInfo
onTap: () async { ? Container()
Navigator.push( : ListTile(
context, onTap: () async {
MaterialPageRoute( Navigator.push(
builder: (context) => const ModifyPasswordScreen()), context,
); MaterialPageRoute(
}, builder: (context) => const ModifyPasswordScreen()),
title: const Text('修改密码'), );
), },
title: const Text('修改密码'),
),
const Divider(), const Divider(),
const NetworkSetting(), const NetworkSetting(),
const Divider(), const Divider(),
@ -94,6 +100,7 @@ class SettingsScreen extends StatelessWidget {
const Divider(), const Divider(),
androidDisplayModeSetting(), androidDisplayModeSetting(),
androidSecureFlagSetting(), androidSecureFlagSetting(),
authenticationSetting(),
const Divider(), const Divider(),
chooserRootSetting(), chooserRootSetting(),
downloadThreadCountSetting(), downloadThreadCountSetting(),
@ -131,5 +138,4 @@ class SettingsScreen extends StatelessWidget {
} }
return Container(); return Container();
} }
} }