mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 16:56:19 +01:00
Require auth again after suspending for some time
This commit is contained in:
parent
38fe935764
commit
24e5130535
6 changed files with 173 additions and 6 deletions
|
@ -40,6 +40,7 @@ import 'package:nc_photos/k.dart' as k;
|
|||
import 'package:nc_photos/mobile/android/android_info.dart';
|
||||
import 'package:nc_photos/mobile/self_signed_cert_manager.dart';
|
||||
import 'package:nc_photos/platform/features.dart' as features;
|
||||
import 'package:nc_photos/session_storage.dart';
|
||||
import 'package:nc_photos/touch_manager.dart';
|
||||
import 'package:np_db/np_db.dart';
|
||||
import 'package:np_gps_map/np_gps_map.dart';
|
||||
|
@ -73,6 +74,8 @@ Future<void> init(InitIsolateType isolateType) async {
|
|||
await _initDiContainer(isolateType);
|
||||
_initVisibilityDetector();
|
||||
GpsMap.init();
|
||||
// init session storage
|
||||
SessionStorage();
|
||||
|
||||
_hasInitedInThisIsolate = true;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ extension ProtectedPageBuildContextExtension on NavigatorState {
|
|||
U? result,
|
||||
Object? arguments,
|
||||
}) async {
|
||||
if (await _auth()) {
|
||||
if (await authProtectedPage()) {
|
||||
return pushReplacementNamed(routeName,
|
||||
arguments: arguments, result: result);
|
||||
} else {
|
||||
|
@ -46,7 +46,7 @@ extension ProtectedPageBuildContextExtension on NavigatorState {
|
|||
String routeName, {
|
||||
Object? arguments,
|
||||
}) async {
|
||||
if (await _auth()) {
|
||||
if (await authProtectedPage()) {
|
||||
return pushNamed(routeName, arguments: arguments);
|
||||
} else {
|
||||
throw const ProtectedPageAuthException();
|
||||
|
@ -54,14 +54,14 @@ extension ProtectedPageBuildContextExtension on NavigatorState {
|
|||
}
|
||||
|
||||
Future<T?> pushProtected<T extends Object?>(Route<T> route) async {
|
||||
if (await _auth()) {
|
||||
if (await authProtectedPage()) {
|
||||
return push(route);
|
||||
} else {
|
||||
throw const ProtectedPageAuthException();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _auth() async {
|
||||
Future<bool> authProtectedPage() async {
|
||||
final securePrefController = context.read<SecurePrefController>();
|
||||
switch (securePrefController.protectedPageAuthTypeValue) {
|
||||
case null:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:clock/clock.dart';
|
||||
|
||||
/// Hold non-persisted global variables
|
||||
class SessionStorage {
|
||||
factory SessionStorage() {
|
||||
|
@ -15,5 +17,7 @@ class SessionStorage {
|
|||
/// Whether the dynamic_color library is supported in this platform
|
||||
bool isSupportDynamicColor = false;
|
||||
|
||||
DateTime lastSuspendTime = clock.now();
|
||||
|
||||
static final _inst = SessionStorage._();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:copy_with/copy_with.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
@ -15,6 +18,7 @@ import 'package:nc_photos/language_util.dart' as language_util;
|
|||
import 'package:nc_photos/legacy/connect.dart' as legacy;
|
||||
import 'package:nc_photos/legacy/sign_in.dart' as legacy;
|
||||
import 'package:nc_photos/navigation_manager.dart';
|
||||
import 'package:nc_photos/protected_page_handler.dart';
|
||||
import 'package:nc_photos/session_storage.dart';
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
|
@ -54,6 +58,7 @@ import 'package:np_db/np_db.dart';
|
|||
import 'package:to_string/to_string.dart';
|
||||
|
||||
part 'my_app.g.dart';
|
||||
part 'my_app/app_lock.dart';
|
||||
part 'my_app/bloc.dart';
|
||||
part 'my_app/state_event.dart';
|
||||
|
||||
|
@ -171,9 +176,9 @@ class _WrappedAppState extends State<_WrappedApp>
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
SnackBarManager().unregisterHandler(this);
|
||||
NavigationManager().unsetHandler(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -560,7 +565,7 @@ class _ThemedMyApp extends StatelessWidget {
|
|||
systemNavigationBarColor: theme.colorScheme.secondaryContainer,
|
||||
systemNavigationBarIconBrightness: theme.brightness.invert(),
|
||||
),
|
||||
child: child,
|
||||
child: _AppLockMyApp(child: child),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,20 @@ extension _$_WrappedAppStateNpLog on _WrappedAppState {
|
|||
static final log = Logger("widget.my_app._WrappedAppState");
|
||||
}
|
||||
|
||||
extension _$_AppLockMyAppStateNpLog on _AppLockMyAppState {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
||||
static final log = Logger("widget.my_app._AppLockMyAppState");
|
||||
}
|
||||
|
||||
extension _$_AppLockOverlayPageStateNpLog on _AppLockOverlayPageState {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
||||
static final log = Logger("widget.my_app._AppLockOverlayPageState");
|
||||
}
|
||||
|
||||
extension _$_BlocNpLog on _Bloc {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
|
141
app/lib/widget/my_app/app_lock.dart
Normal file
141
app/lib/widget/my_app/app_lock.dart
Normal file
|
@ -0,0 +1,141 @@
|
|||
part of '../my_app.dart';
|
||||
|
||||
class _AppLockMyApp extends StatefulWidget {
|
||||
const _AppLockMyApp({
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _AppLockMyAppState();
|
||||
|
||||
final Widget child;
|
||||
}
|
||||
|
||||
@npLog
|
||||
class _AppLockMyAppState extends State<_AppLockMyApp> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_lifecycleListener = AppLifecycleListener(
|
||||
onHide: () {
|
||||
SessionStorage().lastSuspendTime = clock.now();
|
||||
},
|
||||
onShow: () async {
|
||||
final now = clock.now();
|
||||
final diff = now.difference(SessionStorage().lastSuspendTime);
|
||||
_log.info("Suspended for: $diff");
|
||||
if (diff >= const Duration(seconds: 30) && !_shouldLock) {
|
||||
_log.info("Suspended for too long, auth required");
|
||||
setState(() {
|
||||
_shouldLock = true;
|
||||
});
|
||||
late final OverlayEntry authOverlay;
|
||||
authOverlay = OverlayEntry(
|
||||
builder: (_) => _AppLockOverlay(
|
||||
onAuthSuccess: () {
|
||||
authOverlay.remove();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_shouldLock = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
_key.currentState?.insert(authOverlay);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_lifecycleListener.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Overlay(
|
||||
key: _key,
|
||||
initialEntries: [
|
||||
OverlayEntry(
|
||||
maintainState: true,
|
||||
builder: (_) => widget.child,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
late final AppLifecycleListener _lifecycleListener;
|
||||
final _key = GlobalKey<OverlayState>();
|
||||
var _shouldLock = false;
|
||||
}
|
||||
|
||||
class _AppLockOverlay extends StatelessWidget {
|
||||
const _AppLockOverlay({
|
||||
required this.onAuthSuccess,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return HeroControllerScope.none(
|
||||
child: Navigator(
|
||||
onGenerateRoute: (_) => MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
_AppLockOverlayPage(onAuthSuccess: onAuthSuccess),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final VoidCallback onAuthSuccess;
|
||||
}
|
||||
|
||||
class _AppLockOverlayPage extends StatefulWidget {
|
||||
const _AppLockOverlayPage({
|
||||
required this.onAuthSuccess,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _AppLockOverlayPageState();
|
||||
|
||||
final VoidCallback onAuthSuccess;
|
||||
}
|
||||
|
||||
@npLog
|
||||
class _AppLockOverlayPageState extends State<_AppLockOverlayPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
_auth();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: Container(
|
||||
color: Colors.black,
|
||||
child: const Align(
|
||||
alignment: Alignment(0, .75),
|
||||
child: Icon(Icons.lock_outlined, size: 64),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _auth() async {
|
||||
if (mounted && await Navigator.of(context).authProtectedPage()) {
|
||||
widget.onAuthSuccess();
|
||||
} else {
|
||||
_log.warning("[_auth] Auth failed");
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
unawaited(_auth());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue