diff --git a/CHANGELOG.md b/CHANGELOG.md index 045018670..fee5b9fa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. ### Changed +- opening app from launcher shows home page only when exited by back button - Screen saver: black background, consistent with slideshow - upgraded Flutter to stable v3.22.2 diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt index c6858f552..5c4903840 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt @@ -285,10 +285,10 @@ open class MainActivity : FlutterFragmentActivity() { open fun extractIntentData(intent: Intent?): FieldMap { when (val action = intent?.action) { Intent.ACTION_MAIN -> { - val fields = hashMapOf( - INTENT_DATA_KEY_LAUNCHER to intent.hasCategory(Intent.CATEGORY_LAUNCHER), - INTENT_DATA_KEY_SAFE_MODE to intent.getBooleanExtra(EXTRA_KEY_SAFE_MODE, false), - ) + val fields = HashMap() + if (intent.getBooleanExtra(EXTRA_KEY_SAFE_MODE, false)) { + fields[INTENT_DATA_KEY_SAFE_MODE] = true + } intent.getStringExtra(EXTRA_KEY_PAGE)?.let { page -> val filters = extractFiltersFromIntent(intent) fields[INTENT_DATA_KEY_PAGE] = page @@ -497,7 +497,6 @@ open class MainActivity : FlutterFragmentActivity() { const val INTENT_DATA_KEY_ACTION = "action" const val INTENT_DATA_KEY_ALLOW_MULTIPLE = "allowMultiple" const val INTENT_DATA_KEY_FILTERS = "filters" - const val INTENT_DATA_KEY_LAUNCHER = "launcher" const val INTENT_DATA_KEY_MIME_TYPE = "mimeType" const val INTENT_DATA_KEY_PAGE = "page" const val INTENT_DATA_KEY_QUERY = "query" diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index e92220699..9b1ac3a71 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -26,6 +26,7 @@ import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/collection/collection_grid.dart'; import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; +import 'package:aves/widgets/common/behaviour/pop/scope.dart'; import 'package:aves/widgets/common/behaviour/route_tracker.dart'; import 'package:aves/widgets/common/behaviour/routes.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; @@ -178,6 +179,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { static const defaultPageTransitionsBuilder = FadeUpwardsPageTransitionsBuilder(); static final GlobalKey _navigatorKey = GlobalKey(debugLabel: 'app-navigator'); static ScreenBrightness? _screenBrightness; + static bool _exitedMainByPop = false; @override void initState() { @@ -224,83 +226,91 @@ class _AvesAppState extends State with WidgetsBindingObserver { DurationsProvider(), HighlightInfoProvider(), ], - child: OverlaySupport( - child: FutureBuilder( - future: _appSetup, - builder: (context, snapshot) { - final initialized = !snapshot.hasError && snapshot.connectionState == ConnectionState.done; - if (initialized) { - AvesApp.showSystemUI(); - } - final home = initialized - ? _getFirstPage(intentData: widget.debugIntentData) - : AvesScaffold( - body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(), - ); - return Selector( - selector: (context, s) => ( - s.locale, - s.initialized ? s.themeBrightness : SettingsDefaults.themeBrightness, - s.initialized ? s.enableDynamicColor : SettingsDefaults.enableDynamicColor, - ), - builder: (context, s, child) { - final (settingsLocale, themeBrightness, enableDynamicColor) = s; - return DynamicColorBuilder( - builder: (lightScheme, darkScheme) { - const defaultAccent = AvesColorsData.defaultAccent; - Color lightAccent = defaultAccent, darkAccent = defaultAccent; - if (enableDynamicColor) { - lightAccent = lightScheme?.primary ?? lightAccent; - darkAccent = darkScheme?.primary ?? darkAccent; - } - final lightTheme = Themes.lightTheme(lightAccent, initialized); - final darkTheme = themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent, initialized) : Themes.darkTheme(darkAccent, initialized); - return Shortcuts( - shortcuts: { - // handle Android TV remote `select` button (KEYCODE_DPAD_CENTER) - // the following keys are already handled by default: - // KEYCODE_ENTER, KEYCODE_BUTTON_A, KEYCODE_NUMPAD_ENTER - LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), - }, - child: Builder( - builder: (context) { - return MediaQuery( - data: MediaQuery.of(context).copyWith( - // disable accessible navigation, as it impacts snack bar action timer - // for all users of apps registered as accessibility services, - // even though they are not for accessibility purposes (like TalkBack is) - accessibleNavigation: false, - ), - child: MaterialApp( - navigatorKey: _navigatorKey, - home: home, - navigatorObservers: _navigatorObservers, - builder: (context, child) => _decorateAppChild( - context: context, - initialized: initialized, - child: child, - ), - onGenerateTitle: (context) => context.l10n.appName, - theme: lightTheme, - darkTheme: darkTheme, - themeMode: themeBrightness.appThemeMode, - locale: settingsLocale, - localizationsDelegates: const [ - ...AppLocalizations.localizationsDelegates, - ...LocalizationsNn.delegates, - ], - supportedLocales: AvesApp.supportedLocales, - scrollBehavior: AvesScrollBehavior(), - ), - ); - }, - ), + child: NotificationListener( + onNotification: (notification) { + if (_appModeNotifier.value == AppMode.main) { + _exitedMainByPop = true; + } + return true; + }, + child: OverlaySupport( + child: FutureBuilder( + future: _appSetup, + builder: (context, snapshot) { + final initialized = !snapshot.hasError && snapshot.connectionState == ConnectionState.done; + if (initialized) { + AvesApp.showSystemUI(); + } + final home = initialized + ? _getFirstPage(intentData: widget.debugIntentData) + : AvesScaffold( + body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(), ); - }, - ); - }, - ); - }, + return Selector( + selector: (context, s) => ( + s.locale, + s.initialized ? s.themeBrightness : SettingsDefaults.themeBrightness, + s.initialized ? s.enableDynamicColor : SettingsDefaults.enableDynamicColor, + ), + builder: (context, s, child) { + final (settingsLocale, themeBrightness, enableDynamicColor) = s; + return DynamicColorBuilder( + builder: (lightScheme, darkScheme) { + const defaultAccent = AvesColorsData.defaultAccent; + Color lightAccent = defaultAccent, darkAccent = defaultAccent; + if (enableDynamicColor) { + lightAccent = lightScheme?.primary ?? lightAccent; + darkAccent = darkScheme?.primary ?? darkAccent; + } + final lightTheme = Themes.lightTheme(lightAccent, initialized); + final darkTheme = themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent, initialized) : Themes.darkTheme(darkAccent, initialized); + return Shortcuts( + shortcuts: { + // handle Android TV remote `select` button (KEYCODE_DPAD_CENTER) + // the following keys are already handled by default: + // KEYCODE_ENTER, KEYCODE_BUTTON_A, KEYCODE_NUMPAD_ENTER + LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), + }, + child: Builder( + builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + // disable accessible navigation, as it impacts snack bar action timer + // for all users of apps registered as accessibility services, + // even though they are not for accessibility purposes (like TalkBack is) + accessibleNavigation: false, + ), + child: MaterialApp( + navigatorKey: _navigatorKey, + home: home, + navigatorObservers: _navigatorObservers, + builder: (context, child) => _decorateAppChild( + context: context, + initialized: initialized, + child: child, + ), + onGenerateTitle: (context) => context.l10n.appName, + theme: lightTheme, + darkTheme: darkTheme, + themeMode: themeBrightness.appThemeMode, + locale: settingsLocale, + localizationsDelegates: const [ + ...AppLocalizations.localizationsDelegates, + ...LocalizationsNn.delegates, + ], + supportedLocales: AvesApp.supportedLocales, + scrollBehavior: AvesScrollBehavior(), + ), + ); + }, + ), + ); + }, + ); + }, + ); + }, + ), ), ), ); @@ -615,6 +625,18 @@ class _AvesAppState extends State with WidgetsBindingObserver { void _onNewIntent(Map? intentData) { reportService.log('New intent data=$intentData'); + + if (_appModeNotifier.value == AppMode.main) { + // do not reset when relaunching the app, except when exiting by pop + final shouldReset = _exitedMainByPop; + _exitedMainByPop = false; + + if (!shouldReset && (intentData ?? {}).isEmpty) { + reportService.log('Relaunch'); + return; + } + } + _navigatorKey.currentState!.pushReplacement(DirectMaterialPageRoute( settings: const RouteSettings(name: HomePage.routeName), builder: (_) => _getFirstPage(intentData: intentData), diff --git a/lib/widgets/common/behaviour/pop/scope.dart b/lib/widgets/common/behaviour/pop/scope.dart index 72bf70eef..410d1bf83 100644 --- a/lib/widgets/common/behaviour/pop/scope.dart +++ b/lib/widgets/common/behaviour/pop/scope.dart @@ -1,4 +1,5 @@ import 'package:aves/services/common/services.dart'; +import 'package:aves/widgets/aves_app.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -28,6 +29,7 @@ class AvesPopScope extends StatelessWidget { } else { // exit reportService.log('Exit by pop'); + PopExitNotification().dispatch(context); SystemNavigator.pop(); } } @@ -36,3 +38,6 @@ class AvesPopScope extends StatelessWidget { ); } } + +@immutable +class PopExitNotification extends Notification {}