#1032 screen saver: black background

This commit is contained in:
Thibault Deckers 2024-05-30 01:34:18 +02:00
parent 171258ba2b
commit ece28db3f8
10 changed files with 75 additions and 31 deletions

View file

@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
### Changed
- Screen saver: black background, consistent with slideshow
- upgraded Flutter to stable v3.22.1
### Removed

View file

@ -9,6 +9,7 @@ import deckers.thibault.aves.channel.calls.*
import deckers.thibault.aves.channel.calls.window.ServiceWindowHandler
import deckers.thibault.aves.channel.calls.window.WindowHandler
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
import deckers.thibault.aves.channel.streams.MediaCommandStreamHandler
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
import deckers.thibault.aves.utils.LogUtils
import io.flutter.FlutterInjector
@ -18,12 +19,14 @@ import io.flutter.embedding.android.FlutterView
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel
// for FlutterView-level integration, cf https://docs.flutter.dev/development/add-to-app/android/add-flutter-view
class ScreenSaverService : DreamService() {
private var flutterEngine: FlutterEngine? = null
private var flutterView: FlutterView? = null
private lateinit var mediaSessionHandler: MediaSessionHandler
override fun onAttachedToWindow() {
Log.i(LOG_TAG, "onAttachedToWindow")
@ -77,6 +80,7 @@ class ScreenSaverService : DreamService() {
private fun release() {
destroyView()
mediaSessionHandler.dispose()
flutterEngine = null
flutterView = null
}
@ -96,12 +100,19 @@ class ScreenSaverService : DreamService() {
private fun initChannels() {
val messenger = flutterEngine!!.dartExecutor
// notification: platform -> dart
val mediaCommandStreamHandler = MediaCommandStreamHandler().apply {
EventChannel(messenger, MediaCommandStreamHandler.CHANNEL).setStreamHandler(this)
}
// dart -> platform -> dart
// - need Context
mediaSessionHandler = MediaSessionHandler(this, mediaCommandStreamHandler)
MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(this))
MethodChannel(messenger, EmbeddedDataHandler.CHANNEL).setMethodCallHandler(EmbeddedDataHandler(this))
MethodChannel(messenger, MediaFetchBytesHandler.CHANNEL, AvesByteSendingMethodCodec.INSTANCE).setMethodCallHandler(MediaFetchBytesHandler(this))
MethodChannel(messenger, MediaFetchObjectHandler.CHANNEL).setMethodCallHandler(MediaFetchObjectHandler(this))
MethodChannel(messenger, MediaSessionHandler.CHANNEL).setMethodCallHandler(mediaSessionHandler)
MethodChannel(messenger, MediaStoreHandler.CHANNEL).setMethodCallHandler(MediaStoreHandler(this))
MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(this))
MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(this))

View file

@ -1,4 +1,5 @@
enum AppMode {
initialization,
main,
pickCollectionFiltersExternal,
pickSingleMediaExternal,

View file

@ -86,6 +86,8 @@ class AvesApp extends StatefulWidget {
// so that we can react to fullscreen `PageRoute`s only
static final RouteObserver<PageRoute> pageRouteObserver = RouteObserver<PageRoute>();
static ScreenBrightness? get screenBrightness => _AvesAppState._screenBrightness;
const AvesApp({
super.key,
required this.flavor,
@ -159,7 +161,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
final ValueNotifier<PageTransitionsBuilder> _pageTransitionsBuilderNotifier = ValueNotifier(defaultPageTransitionsBuilder);
final ValueNotifier<TvMediaQueryModifier?> _tvMediaQueryModifierNotifier = ValueNotifier(null);
final ValueNotifier<AppMode> _appModeNotifier = ValueNotifier(AppMode.main);
final ValueNotifier<AppMode> _appModeNotifier = ValueNotifier(AppMode.initialization);
// observers are not registered when using the same list object with different items
// the list itself needs to be reassigned
@ -175,6 +177,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
// - `ZoomPageTransitionsBuilder` on Android 10 / API 29 and above (default in Flutter v3.0.0)
static const defaultPageTransitionsBuilder = FadeUpwardsPageTransitionsBuilder();
static final GlobalKey<NavigatorState> _navigatorKey = GlobalKey(debugLabel: 'app-navigator');
static ScreenBrightness? _screenBrightness;
@override
void initState() {
@ -187,6 +190,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
_subscriptions.add(_analysisCompletionChannel.receiveBroadcastStream().listen((event) => _onAnalysisCompletion()));
_subscriptions.add(_errorChannel.receiveBroadcastStream().listen((event) => _onError(event as String?)));
_updateCutoutInsets();
_appModeNotifier.addListener(_onAppModeChanged);
WidgetsBinding.instance.addObserver(this);
}
@ -539,9 +543,9 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
switch (settings.maxBrightness) {
case MaxBrightness.never:
case MaxBrightness.viewerOnly:
ScreenBrightness().resetScreenBrightness();
AvesApp.screenBrightness?.resetScreenBrightness();
case MaxBrightness.always:
ScreenBrightness().setScreenBrightness(1);
AvesApp.screenBrightness?.setScreenBrightness(1);
}
}
@ -627,6 +631,20 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
}
void _onError(String? error) => reportService.recordError(error, null);
void _onAppModeChanged() {
final appMode = _appModeNotifier.value;
debugPrint('App mode set to $appMode');
switch (appMode) {
case AppMode.screenSaver:
// we cannot modify brightness without access to the activity
_screenBrightness = null;
break;
default:
_screenBrightness = ScreenBrightness();
break;
}
}
}
class AvesScrollBehavior extends MaterialScrollBehavior {

View file

@ -49,13 +49,7 @@ class InteractiveTile extends StatelessWidget {
case AppMode.pickFilteredMediaInternal:
case AppMode.pickUnfilteredMediaInternal:
Navigator.maybeOf(context)?.pop(entry);
case AppMode.pickCollectionFiltersExternal:
case AppMode.pickFilterInternal:
case AppMode.screenSaver:
case AppMode.setWallpaper:
case AppMode.slideshow:
case AppMode.view:
case AppMode.edit:
default:
break;
}
},

View file

@ -10,6 +10,7 @@ import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/aves_app.dart';
import 'package:aves/widgets/common/behaviour/springy_scroll_physics.dart';
import 'package:aves/widgets/common/extensions/theme.dart';
import 'package:aves/widgets/viewer/action/entry_action_delegate.dart';
@ -26,7 +27,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:screen_brightness/screen_brightness.dart';
class ViewerVerticalPageView extends StatefulWidget {
final CollectionLens? collection;
@ -86,7 +86,7 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
_registerWidget(widget);
if (settings.maxBrightness == MaxBrightness.viewerOnly) {
_systemBrightness = ScreenBrightness().system;
_systemBrightness = AvesApp.screenBrightness?.system;
}
}
@ -328,7 +328,7 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
if (settings.maxBrightness == MaxBrightness.viewerOnly) {
_systemBrightness?.then((system) {
final value = lerpDouble(maximumBrightness, system, ((1 - page).abs() * 2).clamp(0, 1))!;
ScreenBrightness().setScreenBrightness(value);
AvesApp.screenBrightness?.setScreenBrightness(value);
});
}

View file

@ -45,7 +45,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:screen_brightness/screen_brightness.dart';
class EntryViewerStack extends StatefulWidget {
final CollectionLens? collection;
@ -106,7 +105,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
void initState() {
super.initState();
if (settings.maxBrightness == MaxBrightness.viewerOnly) {
ScreenBrightness().setScreenBrightness(1);
AvesApp.screenBrightness?.setScreenBrightness(1);
}
if (settings.keepScreenOn == KeepScreenOn.viewerOnly) {
windowService.keepScreenOn(true);
@ -901,15 +900,15 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
switch (settings.maxBrightness) {
case MaxBrightness.never:
case MaxBrightness.viewerOnly:
await ScreenBrightness().resetScreenBrightness();
await AvesApp.screenBrightness?.resetScreenBrightness();
case MaxBrightness.always:
await ScreenBrightness().setScreenBrightness(1);
await AvesApp.screenBrightness?.setScreenBrightness(1);
}
if (settings.keepScreenOn == KeepScreenOn.viewerOnly) {
await windowService.keepScreenOn(false);
}
await mediaSessionService.release();
await AvesApp.showSystemUI();
await _showSystemUI(context, true);
AvesApp.setSystemUIStyle(theme);
if (!settings.useTvLayout) {
await windowService.requestOrientation();
@ -950,10 +949,16 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
// overlay
Future<void> _initOverlay() async {
// wait for MaterialPageRoute.transitionDuration
// to show overlay after hero animation is complete
await Future.delayed(ModalRoute.of(context)!.transitionDuration * timeDilation);
await _onOverlayVisibleChanged();
final appMode = context.read<ValueNotifier<AppMode>>().value;
if (appMode == AppMode.screenSaver) {
_overlayVisible.value = false;
await _onOverlayVisibleChanged(animate: false);
} else {
// wait for MaterialPageRoute.transitionDuration
// to show overlay after hero animation is complete
await Future.delayed(ModalRoute.of(context)!.transitionDuration * timeDilation);
await _onOverlayVisibleChanged();
}
_overlayInitialized = true;
}
@ -963,7 +968,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
if (_viewLocked.value) {
await _startOverlayHidingTimer();
} else {
await AvesApp.showSystemUI();
await _showSystemUI(context, true);
AvesApp.setSystemUIStyle(Theme.of(context));
}
if (animate) {
@ -978,7 +983,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
_frozenViewInsets = mediaQuery.viewInsets;
_frozenViewPadding = mediaQuery.viewPadding;
});
await AvesApp.hideSystemUI();
await _showSystemUI(context, false);
if (animate) {
await _overlayAnimationController.reverse();
} else {
@ -993,10 +998,10 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
Future<void> _onViewLockedChanged() async {
if (_viewLocked.value) {
await AvesApp.hideSystemUI();
await _showSystemUI(context, false);
await _startOverlayHidingTimer();
} else {
await AvesApp.showSystemUI();
await _showSystemUI(context, true);
AvesApp.setSystemUIStyle(Theme.of(context));
_stopOverlayHidingTimer();
_overlayVisible.value = true;
@ -1010,4 +1015,18 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
}
void _stopOverlayHidingTimer() => _overlayHidingTimer?.cancel();
Future<void> _showSystemUI(BuildContext context, bool show) async {
final appMode = context.read<ValueNotifier<AppMode>>().value;
if (appMode == AppMode.screenSaver) {
// as of Flutter v3.22.1, calls to `SystemChrome.setEnabledSystemUIMode` hang when app is used as a screen saver
return;
}
if (show) {
await AvesApp.showSystemUI();
} else {
await AvesApp.hideSystemUI();
}
}
}

View file

@ -488,6 +488,7 @@ class _EntryPageViewState extends State<EntryPageView> with TickerProviderStateM
}
void _onViewStateChanged(MagnifierState v) {
if (!mounted) return;
_viewStateNotifier.value = _viewStateNotifier.value.copyWith(
position: v.position,
scale: v.scale,

View file

@ -1,9 +1,9 @@
import 'dart:async';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/aves_app.dart';
import 'package:decorated_icon/decorated_icon.dart';
import 'package:flutter/material.dart';
import 'package:screen_brightness/screen_brightness.dart';
import 'package:volume_controller/volume_controller.dart';
enum SwipeAction { brightness, volume }
@ -12,7 +12,7 @@ extension ExtraSwipeAction on SwipeAction {
Future<double> get() {
switch (this) {
case SwipeAction.brightness:
return ScreenBrightness().current;
return AvesApp.screenBrightness?.current ?? Future.value(1);
case SwipeAction.volume:
return VolumeController().getVolume();
}
@ -21,7 +21,7 @@ extension ExtraSwipeAction on SwipeAction {
Future<void> set(double value) async {
switch (this) {
case SwipeAction.brightness:
await ScreenBrightness().setScreenBrightness(value);
await AvesApp.screenBrightness?.setScreenBrightness(value);
case SwipeAction.volume:
VolumeController().setVolume(value, showSystemUI: false);
}

View file

@ -24,7 +24,6 @@ import 'package:aves_model/aves_model.dart';
import 'package:aves_video/aves_video.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:screen_brightness/screen_brightness.dart';
class WallpaperPage extends StatelessWidget {
static const routeName = '/set_wallpaper';
@ -89,7 +88,7 @@ class _EntryEditorState extends State<EntryEditor> with EntryViewControllerMixin
void initState() {
super.initState();
if (settings.maxBrightness == MaxBrightness.viewerOnly) {
ScreenBrightness().setScreenBrightness(1);
AvesApp.screenBrightness?.setScreenBrightness(1);
}
if (settings.keepScreenOn == KeepScreenOn.viewerOnly) {
windowService.keepScreenOn(true);