feat: Implement app lock

This commit is contained in:
Christian Pauly 2021-01-18 08:38:19 +01:00
parent 81e706aaf5
commit 77ee2ef3c9
8 changed files with 129 additions and 7 deletions

View File

@ -11,4 +11,5 @@ abstract class SettingKeys {
static const String showNoGoogle = 'chat.fluffy.show_no_google'; static const String showNoGoogle = 'chat.fluffy.show_no_google';
static const String showNoPid = 'chat.fluffy.show_no_pid'; static const String showNoPid = 'chat.fluffy.show_no_pid';
static const String databasePassword = 'database-password'; static const String databasePassword = 'database-password';
static const String appLockKey = 'chat.fluffy.app_lock';
} }

View File

@ -1604,6 +1604,26 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"pleaseEnter4Digits": "Please enter 4 digits or leave empty to disable app lock.",
"@pleaseEnter4Digits": {
"type": "text",
"placeholders": {}
},
"pleaseChooseAPasscode": "Please choose a pass code",
"@pleaseChooseAPasscode": {
"type": "text",
"placeholders": {}
},
"appLock": "App lock",
"@appLock": {
"type": "text",
"placeholders": {}
},
"security": "Security",
"@security": {
"type": "text",
"placeholders": {}
},
"sourceCode": "Source code", "sourceCode": "Source code",
"@sourceCode": { "@sourceCode": {
"type": "text", "type": "text",

View File

@ -5,11 +5,14 @@ import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/config/routes.dart'; import 'package:fluffychat/config/routes.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/sentry_controller.dart'; import 'package:fluffychat/utils/sentry_controller.dart';
import 'package:fluffychat/views/lock_screen.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_app_lock/flutter_app_lock.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:universal_html/prefer_universal/html.dart' as html; import 'package:universal_html/prefer_universal/html.dart' as html;
@ -23,7 +26,12 @@ void main() async {
FlutterError.onError = (FlutterErrorDetails details) => FlutterError.onError = (FlutterErrorDetails details) =>
Zone.current.handleUncaughtError(details.exception, details.stack); Zone.current.handleUncaughtError(details.exception, details.stack);
runZonedGuarded( runZonedGuarded(
() => runApp(App()), () => runApp(PlatformInfos.isMobile
? AppLock(
builder: (args) => App(),
lockScreen: LockScreen(),
)
: App()),
SentryController.captureException, SentryController.captureException,
); );
} }

View File

@ -9,10 +9,6 @@ class LoadingView extends StatelessWidget {
WidgetsBinding.instance.addPostFrameCallback((_) => WidgetsBinding.instance.addPostFrameCallback((_) =>
AdaptivePageLayout.of(context).pushNamedAndRemoveAllOthers('/')); AdaptivePageLayout.of(context).pushNamedAndRemoveAllOthers('/'));
} }
return Scaffold( return Scaffold(body: Center(child: CircularProgressIndicator()));
body: Center(
child: CircularProgressIndicator(),
),
);
} }
} }

View File

@ -0,0 +1,35 @@
import 'package:fluffychat/config/setting_keys.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_lock/flutter_app_lock.dart';
import 'package:flutter_screen_lock/lock_screen.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class LockScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: FlutterSecureStorage().read(key: SettingKeys.appLockKey),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Scaffold(body: Center(child: Text(snapshot.error.toString())));
}
if (snapshot.connectionState == ConnectionState.done) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (snapshot.data?.isNotEmpty ?? false) {
showLockScreen(
context: context,
correctString: snapshot.data,
onUnlocked: () => AppLock.of(context).didUnlock(),
canBiometric: true,
canCancel: false,
);
} else {
AppLock.of(context).didUnlock();
}
});
}
return Scaffold(body: Center(child: CircularProgressIndicator()));
},
);
}
}

View File

@ -12,6 +12,8 @@ import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/sentry_controller.dart'; import 'package:fluffychat/utils/sentry_controller.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_screen_lock/lock_screen.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -254,6 +256,44 @@ class _SettingsState extends State<Settings> {
} }
} }
void _setAppLockAction(BuildContext context) async {
final currentLock =
await FlutterSecureStorage().read(key: SettingKeys.appLockKey);
if (currentLock?.isNotEmpty ?? false) {
var unlocked = false;
await showLockScreen(
context: context,
correctString: currentLock,
onUnlocked: () => unlocked = true,
canBiometric: true,
);
if (unlocked != true) return;
}
final newLock = await showTextInputDialog(
context: context,
title: L10n.of(context).pleaseChooseAPasscode,
message: L10n.of(context).pleaseEnter4Digits,
textFields: [
DialogTextField(
validator: (text) {
if (text.length != 4 && text.isNotEmpty) {
return L10n.of(context).pleaseEnter4Digits;
}
return null;
},
keyboardType: TextInputType.number,
obscureText: true,
maxLines: 1,
minLines: 1,
)
],
);
if (newLock != null) {
await FlutterSecureStorage()
.write(key: SettingKeys.appLockKey, value: newLock.first);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final client = Matrix.of(context).client; final client = Matrix.of(context).client;
@ -439,13 +479,19 @@ class _SettingsState extends State<Settings> {
Divider(thickness: 1), Divider(thickness: 1),
ListTile( ListTile(
title: Text( title: Text(
L10n.of(context).encryption, L10n.of(context).security,
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
), ),
if (PlatformInfos.isMobile)
ListTile(
trailing: Icon(Icons.lock_outlined),
title: Text(L10n.of(context).appLock),
onTap: () => _setAppLockAction(context),
),
ListTile( ListTile(
trailing: Icon(Icons.compare_arrows_outlined), trailing: Icon(Icons.compare_arrows_outlined),
title: Text(client.encryption.crossSigning.enabled title: Text(client.encryption.crossSigning.enabled

View File

@ -309,6 +309,13 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_app_lock:
dependency: "direct main"
description:
name: flutter_app_lock
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0+1"
flutter_blurhash: flutter_blurhash:
dependency: "direct main" dependency: "direct main"
description: description:
@ -398,6 +405,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.11" version: "1.0.11"
flutter_screen_lock:
dependency: "direct main"
description:
name: flutter_screen_lock
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.6"
flutter_secure_storage: flutter_secure_storage:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -69,6 +69,8 @@ dependencies:
emoji_picker: ^0.1.0 emoji_picker: ^0.1.0
future_loading_dialog: ^0.1.2 future_loading_dialog: ^0.1.2
package_info: ^0.4.3+2 package_info: ^0.4.3+2
flutter_app_lock: ^1.4.0+1
flutter_screen_lock: ^1.2.6
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: