mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-23 17:26:18 +01:00
313 lines
11 KiB
Dart
313 lines
11 KiB
Dart
import 'package:copy_with/copy_with.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:nc_photos/app_localizations.dart';
|
|
import 'package:nc_photos/bloc_util.dart';
|
|
import 'package:nc_photos/controller/pref_controller.dart';
|
|
import 'package:nc_photos/k.dart' as k;
|
|
import 'package:nc_photos/protected_page_handler.dart';
|
|
import 'package:nc_photos/snack_bar_manager.dart';
|
|
import 'package:nc_photos/widget/protected_page_password_auth_dialog.dart';
|
|
import 'package:nc_photos/widget/protected_page_pin_auth_dialog.dart';
|
|
import 'package:np_codegen/np_codegen.dart';
|
|
import 'package:np_string/np_string.dart';
|
|
import 'package:np_ui/np_ui.dart';
|
|
import 'package:to_string/to_string.dart';
|
|
|
|
part 'app_lock/bloc.dart';
|
|
part 'app_lock/state_event.dart';
|
|
part 'app_lock_settings.g.dart';
|
|
|
|
class AppLockSettings extends StatelessWidget {
|
|
const AppLockSettings({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BlocProvider(
|
|
create: (_) => _Bloc(
|
|
securePrefController: context.read(),
|
|
),
|
|
child: const _WrappedAppLockSettings(),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _WrappedAppLockSettings extends StatelessWidget {
|
|
const _WrappedAppLockSettings();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(L10n.global().settingsAppLock),
|
|
actions: [
|
|
Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Flexible(
|
|
flex: 1,
|
|
child: Align(
|
|
alignment: Alignment.centerRight,
|
|
child: _BlocSelector<ProtectedPageAuthType?>(
|
|
selector: (state) => state.appLockType,
|
|
builder: (context, appLockType) => Icon(
|
|
appLockType == null
|
|
? Icons.lock_open_outlined
|
|
: Icons.lock_outlined,
|
|
size: 24,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Flexible(
|
|
flex: 1,
|
|
child: Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: _BlocSelector<ProtectedPageAuthType?>(
|
|
selector: (state) => state.appLockType,
|
|
builder: (context, appLockType) => Text(
|
|
appLockType == null
|
|
? L10n.global().disabledText
|
|
: L10n.global().enabledText,
|
|
textAlign: TextAlign.center,
|
|
style: Theme.of(context).textStyleColored(
|
|
(textTheme) => textTheme.titleSmall,
|
|
(colorScheme) => appLockType == null
|
|
? colorScheme.error
|
|
: colorScheme.primary,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
body: Column(
|
|
children: [
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const SizedBox(height: 16),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
child: Text(L10n.global().settingsAppLockDescription),
|
|
),
|
|
const SizedBox(height: 16),
|
|
_BlocSelector<ProtectedPageAuthType?>(
|
|
selector: (state) => state.appLockType,
|
|
builder: (context, appLockType) => RadioListTile(
|
|
value: null,
|
|
groupValue: appLockType,
|
|
title: Text(L10n.global().disabledText),
|
|
onChanged: (value) async {
|
|
if (await _confirmDisable(context)) {
|
|
context.addEvent(_SetAppLockType(value));
|
|
}
|
|
},
|
|
),
|
|
),
|
|
_BlocSelector<ProtectedPageAuthType?>(
|
|
selector: (state) => state.appLockType,
|
|
builder: (context, appLockType) => RadioListTile(
|
|
value: ProtectedPageAuthType.biometric,
|
|
groupValue: appLockType,
|
|
title: Text(L10n.global().settingsAppLockTypeBiometric),
|
|
onChanged: (value) async {
|
|
if (await _confirmBiometric(context) == true) {
|
|
context.addEvent(_SetAppLockType(value));
|
|
}
|
|
},
|
|
),
|
|
),
|
|
_BlocSelector<ProtectedPageAuthType?>(
|
|
selector: (state) => state.appLockType,
|
|
builder: (context, appLockType) => RadioListTile(
|
|
value: ProtectedPageAuthType.pin,
|
|
groupValue: appLockType,
|
|
title: Text(L10n.global().settingsAppLockTypePin),
|
|
onChanged: (value) async {
|
|
if (await _confirmPin(context) == true) {
|
|
context.addEvent(_SetAppLockType(value));
|
|
}
|
|
},
|
|
),
|
|
),
|
|
_BlocSelector<ProtectedPageAuthType?>(
|
|
selector: (state) => state.appLockType,
|
|
builder: (context, appLockType) => RadioListTile(
|
|
value: ProtectedPageAuthType.password,
|
|
groupValue: appLockType,
|
|
title: Text(L10n.global().settingsAppLockTypePassword),
|
|
onChanged: (value) async {
|
|
if (await _confirmPassword(context) == true) {
|
|
context.addEvent(_SetAppLockType(value));
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<bool> _confirmDisable(BuildContext context) async {
|
|
final prefController = context.read<SecurePrefController>();
|
|
final result = await prefController.setProtectedPageAuthType(null);
|
|
if (result) {
|
|
return true;
|
|
} else {
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
duration: k.snackBarDurationNormal,
|
|
));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<bool?> _confirmBiometric(BuildContext context) async {
|
|
final result = await showDialog<bool>(
|
|
context: context,
|
|
builder: (_) => SimpleDialog(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(24, 12, 24, 0),
|
|
child: Text(
|
|
L10n.global().settingsAppLockSetupBiometricFallbackDialogTitle,
|
|
style: Theme.of(context).textTheme.titleLarge,
|
|
),
|
|
),
|
|
SimpleDialogOption(
|
|
child: ListTile(
|
|
title: Text(L10n.global().settingsAppLockTypePin),
|
|
),
|
|
onPressed: () async {
|
|
final result = await _enterPin(context);
|
|
if (result == true) {
|
|
Navigator.of(context).pop(true);
|
|
} else if (result == false) {
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
content:
|
|
Text(L10n.global().writePreferenceFailureNotification),
|
|
duration: k.snackBarDurationNormal,
|
|
));
|
|
}
|
|
},
|
|
),
|
|
SimpleDialogOption(
|
|
child: ListTile(
|
|
title: Text(L10n.global().settingsAppLockTypePassword),
|
|
),
|
|
onPressed: () async {
|
|
final result = await _enterPassword(context);
|
|
if (result == true) {
|
|
Navigator.of(context).pop(true);
|
|
} else if (result == false) {
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
content:
|
|
Text(L10n.global().writePreferenceFailureNotification),
|
|
duration: k.snackBarDurationNormal,
|
|
));
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
if (result != true) {
|
|
return result;
|
|
}
|
|
return context.bloc.securePrefController
|
|
.setProtectedPageAuthType(ProtectedPageAuthType.biometric);
|
|
}
|
|
|
|
Future<bool?> _confirmPin(BuildContext context) async {
|
|
final result = await _enterPin(context);
|
|
if (result == true) {
|
|
return context.bloc.securePrefController
|
|
.setProtectedPageAuthType(ProtectedPageAuthType.pin);
|
|
} else if (result == false) {
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
duration: k.snackBarDurationNormal,
|
|
));
|
|
return false;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<bool?> _confirmPassword(BuildContext context) async {
|
|
final result = await _enterPassword(context);
|
|
if (result == true) {
|
|
return context.bloc.securePrefController
|
|
.setProtectedPageAuthType(ProtectedPageAuthType.password);
|
|
} else if (result == false) {
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
duration: k.snackBarDurationNormal,
|
|
));
|
|
return false;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<bool?> _enterPin(BuildContext context) async {
|
|
final result = await showDialog<CiString>(
|
|
context: context,
|
|
builder: (_) => const ProtectedPagePinSetupDialog(),
|
|
);
|
|
if (result == null) {
|
|
return null;
|
|
}
|
|
final isConfirmed = await showDialog<bool>(
|
|
context: context,
|
|
builder: (_) => ProtectedPagePinConfirmDialog(pin: result),
|
|
);
|
|
if (isConfirmed != true) {
|
|
return null;
|
|
}
|
|
return context.bloc.securePrefController.setProtectedPageAuthPin(result);
|
|
}
|
|
|
|
Future<bool?> _enterPassword(BuildContext context) async {
|
|
final result = await showDialog<CiString>(
|
|
context: context,
|
|
builder: (_) => const ProtectedPagePasswordSetupDialog(),
|
|
);
|
|
if (result == null) {
|
|
return null;
|
|
}
|
|
final isConfirmed = await showDialog<bool>(
|
|
context: context,
|
|
builder: (_) => ProtectedPagePasswordConfirmDialog(password: result),
|
|
);
|
|
if (isConfirmed != true) {
|
|
return null;
|
|
}
|
|
return context.bloc.securePrefController
|
|
.setProtectedPageAuthPassword(result);
|
|
}
|
|
}
|
|
|
|
// typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
|
// typedef _BlocListener = BlocListener<_Bloc, _State>;
|
|
// typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>;
|
|
typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
|
|
|
|
extension on BuildContext {
|
|
_Bloc get bloc => read<_Bloc>();
|
|
// _State get state => bloc.state;
|
|
void addEvent(_Event event) => bloc.add(event);
|
|
}
|