[mob][photos] Fix wakelock bugs (#5691)
## Description #### New `EnteWakelockService` singleton that wraps wakelock_plus APIs - Persist enable/disable (across sessions) state in `SharedPreferences` - Re apply wakelock on app init based on stored state - Makes sure the wakelock setting across sessions if set is respected when wakelock is updated for other non-across-session purposes. ### Bugs fixed: - App not staying awake after disabling auto lock in back up settings when killed and reopened. - App not staying awake when video is playing (only on native_video_player) ## Tests Tested all cases.
This commit is contained in:
@@ -33,7 +33,6 @@ import 'package:photos/services/sync/sync_service.dart';
|
||||
import 'package:photos/utils/file_uploader.dart';
|
||||
import "package:photos/utils/lock_screen_settings.dart";
|
||||
import 'package:photos/utils/validator_util.dart';
|
||||
import "package:photos/utils/wakelock_util.dart";
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import "package:tuple/tuple.dart";
|
||||
import 'package:uuid/uuid.dart';
|
||||
@@ -53,10 +52,6 @@ class Configuration {
|
||||
static const keyKey = "key";
|
||||
static const keyShouldBackupOverMobileData = "should_backup_over_mobile_data";
|
||||
static const keyShouldBackupVideos = "should_backup_videos";
|
||||
|
||||
// keyShouldKeepDeviceAwake is used to determine whether the device screen
|
||||
// should be kept on while the app is in foreground.
|
||||
static const keyShouldKeepDeviceAwake = "should_keep_device_awake";
|
||||
static const keyShowSystemLockScreen = "should_show_lock_screen";
|
||||
static const keyHasSelectedAnyBackupFolder =
|
||||
"has_selected_any_folder_for_backup";
|
||||
@@ -578,16 +573,6 @@ class Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldKeepDeviceAwake() {
|
||||
final keepAwake = _preferences.get(keyShouldKeepDeviceAwake);
|
||||
return keepAwake == null ? false : keepAwake as bool;
|
||||
}
|
||||
|
||||
Future<void> setShouldKeepDeviceAwake(bool value) async {
|
||||
await _preferences.setBool(keyShouldKeepDeviceAwake, value);
|
||||
await EnteWakeLock.toggle(enable: value);
|
||||
}
|
||||
|
||||
Future<void> setShouldBackupVideos(bool value) async {
|
||||
await _preferences.setBool(keyShouldBackupVideos, value);
|
||||
if (value) {
|
||||
|
||||
@@ -44,6 +44,7 @@ import 'package:photos/services/search_service.dart';
|
||||
import 'package:photos/services/sync/local_sync_service.dart';
|
||||
import 'package:photos/services/sync/remote_sync_service.dart';
|
||||
import "package:photos/services/sync/sync_service.dart";
|
||||
import "package:photos/services/wake_lock_service.dart";
|
||||
import 'package:photos/ui/tools/app_lock.dart';
|
||||
import 'package:photos/ui/tools/lock_screen.dart';
|
||||
import "package:photos/utils/email_util.dart";
|
||||
@@ -276,6 +277,7 @@ Future<void> _init(bool isBackground, {String via = ''}) async {
|
||||
MLDataDB.instance,
|
||||
preferences,
|
||||
);
|
||||
EnteWakeLockService.instance.init(preferences);
|
||||
logLocalSettings();
|
||||
initComplete = true;
|
||||
_logger.info("Initialization done $tlog");
|
||||
|
||||
51
mobile/lib/services/wake_lock_service.dart
Normal file
51
mobile/lib/services/wake_lock_service.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import "package:shared_preferences/shared_preferences.dart";
|
||||
import "package:wakelock_plus/wakelock_plus.dart";
|
||||
|
||||
enum WakeLockFor {
|
||||
videoPlayback,
|
||||
fasterBackupsOniOSByKeepingScreenAwake,
|
||||
machineLearningSettingsScreen,
|
||||
handlingMediaKitEdgeCase,
|
||||
}
|
||||
|
||||
/// Use this wrapper to use wakelock. This class makes sure that the wakelock
|
||||
/// setting across sessions if set is respected when wakelock is updated for
|
||||
/// other non across session purposes.
|
||||
/// Only place where this wrapper is not used for accessing wakelock APIs is
|
||||
/// in media_kit package.
|
||||
class EnteWakeLockService {
|
||||
static const String kKeepAppAwakeAcrossSessions =
|
||||
"keepAppAwakeAcrossSessions";
|
||||
|
||||
EnteWakeLockService._privateConstructor();
|
||||
|
||||
static final EnteWakeLockService instance =
|
||||
EnteWakeLockService._privateConstructor();
|
||||
|
||||
late SharedPreferences _prefs;
|
||||
|
||||
void init(SharedPreferences prefs) {
|
||||
_prefs = prefs;
|
||||
if (_prefs.getBool(kKeepAppAwakeAcrossSessions) ?? false) {
|
||||
WakelockPlus.enable();
|
||||
}
|
||||
}
|
||||
|
||||
void updateWakeLock({
|
||||
required bool enable,
|
||||
required WakeLockFor wakeLockFor,
|
||||
}) {
|
||||
if (wakeLockFor == WakeLockFor.fasterBackupsOniOSByKeepingScreenAwake ||
|
||||
wakeLockFor == WakeLockFor.handlingMediaKitEdgeCase) {
|
||||
WakelockPlus.toggle(enable: enable);
|
||||
_prefs.setBool(kKeepAppAwakeAcrossSessions, enable);
|
||||
} else {
|
||||
if (!shouldKeepAppAwakeAcrossSessions) {
|
||||
WakelockPlus.toggle(enable: enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool get shouldKeepAppAwakeAcrossSessions =>
|
||||
_prefs.getBool(kKeepAppAwakeAcrossSessions) ?? false;
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'dart:io';
|
||||
import "dart:io";
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/wake_lock_service.dart";
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/components/buttons/icon_button_widget.dart';
|
||||
import 'package:photos/ui/components/captioned_text_widget.dart';
|
||||
@@ -134,13 +135,15 @@ class BackupSettingsScreen extends StatelessWidget {
|
||||
),
|
||||
menuItemColor: colorScheme.fillFaint,
|
||||
trailingWidget: ToggleSwitchWidget(
|
||||
value: () => Configuration.instance
|
||||
.shouldKeepDeviceAwake(),
|
||||
onChanged: () {
|
||||
return Configuration.instance
|
||||
.setShouldKeepDeviceAwake(
|
||||
!Configuration.instance
|
||||
.shouldKeepDeviceAwake(),
|
||||
value: () => EnteWakeLockService.instance
|
||||
.shouldKeepAppAwakeAcrossSessions,
|
||||
onChanged: () async {
|
||||
EnteWakeLockService.instance
|
||||
.updateWakeLock(
|
||||
enable: !EnteWakeLockService.instance
|
||||
.shouldKeepAppAwakeAcrossSessions,
|
||||
wakeLockFor: WakeLockFor
|
||||
.fasterBackupsOniOSByKeepingScreenAwake,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -12,6 +12,7 @@ import "package:photos/services/machine_learning/semantic_search/clip/clip_image
|
||||
import "package:photos/services/machine_learning/semantic_search/clip/clip_text_encoder.dart";
|
||||
import 'package:photos/services/machine_learning/semantic_search/semantic_search_service.dart';
|
||||
import "package:photos/services/remote_assets_service.dart";
|
||||
import "package:photos/services/wake_lock_service.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/common/loading_widget.dart";
|
||||
import "package:photos/ui/common/web_page.dart";
|
||||
@@ -31,7 +32,6 @@ import "package:photos/ui/settings/ml/enable_ml_consent.dart";
|
||||
import "package:photos/ui/settings/ml/ml_user_dev_screen.dart";
|
||||
import "package:photos/utils/ml_util.dart";
|
||||
import "package:photos/utils/network_util.dart";
|
||||
import "package:photos/utils/wakelock_util.dart";
|
||||
|
||||
class MachineLearningSettingsPage extends StatefulWidget {
|
||||
const MachineLearningSettingsPage({super.key});
|
||||
@@ -43,7 +43,6 @@ class MachineLearningSettingsPage extends StatefulWidget {
|
||||
|
||||
class _MachineLearningSettingsPageState
|
||||
extends State<MachineLearningSettingsPage> {
|
||||
final EnteWakeLock _wakeLock = EnteWakeLock();
|
||||
Timer? _timer;
|
||||
int _titleTapCount = 0;
|
||||
Timer? _advancedOptionsTimer;
|
||||
@@ -51,7 +50,10 @@ class _MachineLearningSettingsPageState
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_wakeLock.enable();
|
||||
EnteWakeLockService.instance.updateWakeLock(
|
||||
enable: true,
|
||||
wakeLockFor: WakeLockFor.machineLearningSettingsScreen,
|
||||
);
|
||||
machineLearningController.forceOverrideML(turnOn: true);
|
||||
if (!MLIndexingIsolate.instance.areModelsDownloaded) {
|
||||
_timer = Timer.periodic(const Duration(seconds: 10), (timer) {
|
||||
@@ -68,7 +70,10 @@ class _MachineLearningSettingsPageState
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_wakeLock.disable();
|
||||
EnteWakeLockService.instance.updateWakeLock(
|
||||
enable: false,
|
||||
wakeLockFor: WakeLockFor.machineLearningSettingsScreen,
|
||||
);
|
||||
machineLearningController.forceOverrideML(turnOn: false);
|
||||
_timer?.cancel();
|
||||
_advancedOptionsTimer?.cancel();
|
||||
|
||||
@@ -16,6 +16,7 @@ import "package:photos/models/file/extensions/file_props.dart";
|
||||
import "package:photos/models/file/file.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/files_service.dart";
|
||||
import "package:photos/services/wake_lock_service.dart";
|
||||
import "package:photos/theme/colors.dart";
|
||||
import "package:photos/ui/actions/file/file_actions.dart";
|
||||
import "package:photos/ui/common/loading_widget.dart";
|
||||
@@ -161,6 +162,12 @@ class _VideoWidgetMediaKitNewState extends State<VideoWidgetMediaKitNew>
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
player.dispose();
|
||||
_captionUpdatedSubscription.cancel();
|
||||
if (EnteWakeLockService.instance.shouldKeepAppAwakeAcrossSessions) {
|
||||
EnteWakeLockService.instance.updateWakeLock(
|
||||
enable: true,
|
||||
wakeLockFor: WakeLockFor.handlingMediaKitEdgeCase,
|
||||
);
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import "package:photos/models/file/file.dart";
|
||||
import "package:photos/models/preview/playlist_data.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/files_service.dart";
|
||||
import "package:photos/services/wake_lock_service.dart";
|
||||
import "package:photos/theme/colors.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/actions/file/file_actions.dart";
|
||||
@@ -126,6 +127,9 @@ class _VideoWidgetNativeState extends State<VideoWidgetNative>
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
EnteWakeLockService.instance
|
||||
.updateWakeLock(enable: true, wakeLockFor: WakeLockFor.videoPlayback);
|
||||
}
|
||||
|
||||
Future<void> setVideoSource() async {
|
||||
@@ -220,6 +224,8 @@ class _VideoWidgetNativeState extends State<VideoWidgetNative>
|
||||
_debouncer.cancelDebounceTimer();
|
||||
_elTooltipController.dispose();
|
||||
_captionUpdatedSubscription.cancel();
|
||||
EnteWakeLockService.instance
|
||||
.updateWakeLock(enable: false, wakeLockFor: WakeLockFor.videoPlayback);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -478,6 +484,8 @@ class _VideoWidgetNativeState extends State<VideoWidgetNative>
|
||||
widget.playbackCallback!(false);
|
||||
}
|
||||
}
|
||||
|
||||
_handleWakeLockOnPlaybackChanges();
|
||||
}
|
||||
|
||||
void _onError(String errorMessage) {
|
||||
@@ -544,6 +552,21 @@ class _VideoWidgetNativeState extends State<VideoWidgetNative>
|
||||
}
|
||||
}
|
||||
|
||||
void _handleWakeLockOnPlaybackChanges() {
|
||||
final playbackStatus = _controller?.playbackStatus;
|
||||
if (playbackStatus == PlaybackStatus.playing) {
|
||||
EnteWakeLockService.instance.updateWakeLock(
|
||||
enable: true,
|
||||
wakeLockFor: WakeLockFor.videoPlayback,
|
||||
);
|
||||
} else {
|
||||
EnteWakeLockService.instance.updateWakeLock(
|
||||
enable: false,
|
||||
wakeLockFor: WakeLockFor.videoPlayback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _getLoadingWidget() {
|
||||
return Stack(
|
||||
key: const ValueKey("video_loading"),
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import "dart:async" show unawaited;
|
||||
|
||||
import "package:wakelock_plus/wakelock_plus.dart";
|
||||
|
||||
class EnteWakeLock {
|
||||
bool _wakeLockEnabledHere = false;
|
||||
|
||||
void enable() {
|
||||
WakelockPlus.enabled.then((value) {
|
||||
if (value == false) {
|
||||
WakelockPlus.enable();
|
||||
//wakeLockEnabledHere will not be set to true if wakeLock is already enabled from settings on iOS.
|
||||
//We shouldn't disable when video is not playing if it was enabled manually by the user from ente settings by user.
|
||||
_wakeLockEnabledHere = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void disable() {
|
||||
if (_wakeLockEnabledHere) {
|
||||
WakelockPlus.disable();
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (_wakeLockEnabledHere) {
|
||||
unawaited(
|
||||
WakelockPlus.enabled.then((isEnabled) {
|
||||
isEnabled ? WakelockPlus.disable() : null;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> toggle({required bool enable}) async {
|
||||
await WakelockPlus.toggle(enable: enable);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user