Application lock (iOS)
This commit is contained in:
parent
7a8e70e693
commit
5f3b73d12c
|
@ -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
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(() {});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
|
@ -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(
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue