[mob] Remove UserRemoteFlagService & switch to flagService (#5219)
## Description ## Tests
This commit is contained in:
@@ -13,7 +13,6 @@ import "package:photos/services/magic_cache_service.dart";
|
||||
import "package:photos/services/storage_bonus_service.dart";
|
||||
import "package:photos/services/trash_sync_service.dart";
|
||||
import "package:photos/services/update_service.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:photos/utils/local_settings.dart";
|
||||
import "package:shared_preferences/shared_preferences.dart";
|
||||
|
||||
@@ -101,15 +100,6 @@ LocationService get locationService {
|
||||
return _locationService!;
|
||||
}
|
||||
|
||||
UserRemoteFlagService? _userRemoteFlagService;
|
||||
UserRemoteFlagService get userRemoteFlagService {
|
||||
_userRemoteFlagService ??= UserRemoteFlagService(
|
||||
ServiceLocator.instance.enteDio,
|
||||
ServiceLocator.instance.prefs,
|
||||
);
|
||||
return _userRemoteFlagService!;
|
||||
}
|
||||
|
||||
MagicCacheService? _magicCacheService;
|
||||
MagicCacheService get magicCacheService {
|
||||
_magicCacheService ??= MagicCacheService(
|
||||
|
||||
@@ -21,7 +21,6 @@ import "package:photos/services/machine_learning/face_ml/person/person_service.d
|
||||
import "package:photos/services/machine_learning/ml_indexing_isolate.dart";
|
||||
import 'package:photos/services/machine_learning/ml_result.dart';
|
||||
import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:photos/utils/ml_util.dart";
|
||||
import "package:photos/utils/network_util.dart";
|
||||
import "package:photos/utils/ram_check_util.dart";
|
||||
@@ -58,8 +57,7 @@ class MLService {
|
||||
/// Only call this function once at app startup, after that you can directly call [runAllML]
|
||||
Future<void> init() async {
|
||||
if (_isInitialized) return;
|
||||
if (!userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled)) {
|
||||
if (!flagService.hasGrantedMLConsent) {
|
||||
return;
|
||||
}
|
||||
_logger.info("init called");
|
||||
@@ -74,8 +72,7 @@ class MLService {
|
||||
|
||||
// Listen on MachineLearningController
|
||||
Bus.instance.on<MachineLearningControlEvent>().listen((event) {
|
||||
if (!userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled)) {
|
||||
if (!flagService.hasGrantedMLConsent) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import "package:photos/services/collections_service.dart";
|
||||
import "package:photos/services/machine_learning/ml_computer.dart";
|
||||
import "package:photos/services/machine_learning/ml_result.dart";
|
||||
import "package:photos/services/machine_learning/semantic_search/clip/clip_image_encoder.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:shared_preferences/shared_preferences.dart";
|
||||
|
||||
class SemanticSearchService {
|
||||
@@ -48,8 +47,7 @@ class SemanticSearchService {
|
||||
_logger.info("Initialized already");
|
||||
return;
|
||||
}
|
||||
final hasGivenConsent = userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled);
|
||||
final hasGivenConsent = flagService.hasGrantedMLConsent;
|
||||
if (!hasGivenConsent) return;
|
||||
|
||||
_logger.info("init called");
|
||||
@@ -67,9 +65,7 @@ class SemanticSearchService {
|
||||
}
|
||||
|
||||
bool isMagicSearchEnabledAndReady() {
|
||||
return userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled) &&
|
||||
_textModelIsLoaded;
|
||||
return flagService.hasGrantedMLConsent && _textModelIsLoaded;
|
||||
}
|
||||
|
||||
// searchScreenQuery should only be used for the user initiate query on the search screen.
|
||||
@@ -78,7 +74,7 @@ class SemanticSearchService {
|
||||
if (!isMagicSearchEnabledAndReady()) {
|
||||
if (flagService.internalUser) {
|
||||
_logger.info(
|
||||
"ML global consent: ${userRemoteFlagService.getCachedBoolValue(UserRemoteFlagService.mlEnabled)}, loaded: $_textModelIsLoaded ",
|
||||
"ML global consent: ${flagService.hasGrantedMLConsent}, loaded: $_textModelIsLoaded ",
|
||||
);
|
||||
}
|
||||
return (query, <EnteFile>[]);
|
||||
|
||||
@@ -23,7 +23,6 @@ import "package:photos/service_locator.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/search_service.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:photos/ui/viewer/search/result/magic_result_screen.dart";
|
||||
import "package:photos/utils/file_util.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
@@ -203,8 +202,7 @@ class MagicCacheService {
|
||||
return _prefs.getInt(_lastMagicCacheUpdateTime) ?? 0;
|
||||
}
|
||||
|
||||
bool get enableDiscover =>
|
||||
userRemoteFlagService.getCachedBoolValue(UserRemoteFlagService.mlEnabled);
|
||||
bool get enableDiscover => flagService.hasGrantedMLConsent;
|
||||
|
||||
void queueUpdate(String reason) {
|
||||
_pendingUpdateReason.add(reason);
|
||||
|
||||
@@ -45,7 +45,6 @@ import "package:photos/services/machine_learning/face_ml/face_filtering/face_fil
|
||||
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
|
||||
import "package:photos/services/machine_learning/ml_computer.dart";
|
||||
import 'package:photos/services/machine_learning/semantic_search/semantic_search_service.dart';
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:photos/services/user_service.dart";
|
||||
import "package:photos/states/location_screen_state.dart";
|
||||
import "package:photos/ui/viewer/location/add_location_sheet.dart";
|
||||
@@ -249,8 +248,7 @@ class SearchService {
|
||||
Future<List<GenericSearchResult>> getMagicSectionResults(
|
||||
BuildContext context,
|
||||
) async {
|
||||
if (userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled)) {
|
||||
if (flagService.hasGrantedMLConsent) {
|
||||
return magicCacheService.getMagicGenericSearchResult(context);
|
||||
} else {
|
||||
return <GenericSearchResult>[];
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import "package:dio/dio.dart";
|
||||
import "package:flutter/foundation.dart";
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
import 'package:photos/events/notification_event.dart';
|
||||
import "package:photos/service_locator.dart";
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class UserRemoteFlagService {
|
||||
final Dio _enteDio;
|
||||
late final _logger = Logger((UserRemoteFlagService).toString());
|
||||
final SharedPreferences _prefs;
|
||||
|
||||
static const String recoveryVerificationFlag = "recoveryKeyVerified";
|
||||
static const String mapEnabled = "mapEnabled";
|
||||
static const String mlEnabled = "faceSearchEnabled";
|
||||
static const String videoStreamingEnabled = "videoStreamingEnabled";
|
||||
static const String needRecoveryKeyVerification =
|
||||
"needRecoveryKeyVerification";
|
||||
|
||||
UserRemoteFlagService(this._enteDio, this._prefs) {
|
||||
debugPrint("UserRemoteFlagService constructor");
|
||||
}
|
||||
|
||||
bool shouldShowRecoveryVerification() {
|
||||
if (!_prefs.containsKey(needRecoveryKeyVerification)) {
|
||||
// fetch the status from remote
|
||||
_refreshRecoveryVerificationFlag().ignore();
|
||||
return false;
|
||||
} else {
|
||||
final bool shouldShow = _prefs.getBool(needRecoveryKeyVerification)!;
|
||||
if (shouldShow) {
|
||||
// refresh the status to check if user marked it as done on another device
|
||||
_refreshRecoveryVerificationFlag().ignore();
|
||||
}
|
||||
return shouldShow;
|
||||
}
|
||||
}
|
||||
|
||||
bool getCachedBoolValue(String key) {
|
||||
bool defaultValue = false;
|
||||
if (key == mapEnabled) {
|
||||
defaultValue = flagService.mapEnabled;
|
||||
} else if (key == mlEnabled) {
|
||||
defaultValue = flagService.hasGrantedMLConsent;
|
||||
}
|
||||
return _prefs.getBool(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
Future<bool> setBoolValue(String key, bool value) async {
|
||||
await _updateKeyValue(key, value.toString());
|
||||
return _prefs.setBool(key, value);
|
||||
}
|
||||
|
||||
// markRecoveryVerificationAsDone is used to track if user has verified their
|
||||
// recovery key in the past or not. This helps in avoid showing the same
|
||||
// prompt to the user on re-install or signing into a different device
|
||||
Future<void> markRecoveryVerificationAsDone() async {
|
||||
await _updateKeyValue(recoveryVerificationFlag, true.toString());
|
||||
await _prefs.setBool(needRecoveryKeyVerification, false);
|
||||
}
|
||||
|
||||
Future<void> _refreshRecoveryVerificationFlag() async {
|
||||
_logger.finest('refresh recovery key verification flag');
|
||||
final remoteStatusValue =
|
||||
await _getValue(recoveryVerificationFlag, "false");
|
||||
final bool isNeedVerificationFlagSet =
|
||||
_prefs.containsKey(needRecoveryKeyVerification);
|
||||
if (remoteStatusValue.toLowerCase() == "true") {
|
||||
await _prefs.setBool(needRecoveryKeyVerification, false);
|
||||
// If the user verified on different device, then we should refresh
|
||||
// the UI to dismiss the Notification.
|
||||
if (isNeedVerificationFlagSet) {
|
||||
Bus.instance.fire(NotificationEvent());
|
||||
}
|
||||
} else if (!isNeedVerificationFlagSet) {
|
||||
// Verification is not done yet as remoteStatus is false and local flag to
|
||||
// show notification isn't set. Set the flag to true if any active
|
||||
// session is older than 1 day.
|
||||
final activeSessions = await UserService.instance.getActiveSessions();
|
||||
final int microSecondsInADay = const Duration(days: 1).inMicroseconds;
|
||||
final bool anyActiveSessionOlderThanADay =
|
||||
activeSessions.sessions.firstWhereOrNull(
|
||||
(e) =>
|
||||
(e.creationTime + microSecondsInADay) <
|
||||
DateTime.now().microsecondsSinceEpoch,
|
||||
) !=
|
||||
null;
|
||||
if (anyActiveSessionOlderThanADay) {
|
||||
await _prefs.setBool(needRecoveryKeyVerification, true);
|
||||
Bus.instance.fire(NotificationEvent());
|
||||
} else {
|
||||
// continue defaulting to no verification prompt
|
||||
_logger.finest('No active session older than 1 day');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _getValue(String key, String? defaultValue) async {
|
||||
try {
|
||||
final Map<String, dynamic> queryParams = {"key": key};
|
||||
if (defaultValue != null) {
|
||||
queryParams["defaultValue"] = defaultValue;
|
||||
}
|
||||
final response =
|
||||
await _enteDio.get("/remote-store", queryParameters: queryParams);
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception("Unexpected status code ${response.statusCode}");
|
||||
}
|
||||
return response.data["value"];
|
||||
} catch (e) {
|
||||
_logger.info("Error while fetching bool status for $key", e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// _setBooleanFlag sets the corresponding flag on remote
|
||||
// to mark recovery as completed
|
||||
Future<void> _updateKeyValue(String key, String value) async {
|
||||
try {
|
||||
final response = await _enteDio.post(
|
||||
"/remote-store/update",
|
||||
data: {
|
||||
"key": key,
|
||||
"value": value,
|
||||
},
|
||||
);
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception("Unexpected state");
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning("Failed to set flag for $key", e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
|
||||
final String recoveryKeyWords = bip39.entropyToMnemonic(recoveryKey);
|
||||
if (inputKey == recoveryKey || inputKey == recoveryKeyWords) {
|
||||
try {
|
||||
await userRemoteFlagService.markRecoveryVerificationAsDone();
|
||||
await flagService.setRecoveryKeyVerified(true);
|
||||
} catch (e) {
|
||||
await dialog.hide();
|
||||
if (e is DioException && e.type == DioExceptionType.connectionError) {
|
||||
|
||||
@@ -13,7 +13,6 @@ import "package:photos/models/preview/preview_item_status.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/preview_video_store.dart";
|
||||
import 'package:photos/services/sync_service.dart';
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import 'package:photos/theme/text_style.dart';
|
||||
import 'package:photos/ui/account/verify_recovery_page.dart';
|
||||
@@ -43,8 +42,7 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
|
||||
|
||||
bool _showStatus = false;
|
||||
bool _showErrorBanner = false;
|
||||
bool _showMlBanner = !userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled) &&
|
||||
bool _showMlBanner = !flagService.hasGrantedMLConsent &&
|
||||
!localSettings.hasSeenMLEnablingBanner;
|
||||
Error? _syncError;
|
||||
|
||||
@@ -81,8 +79,7 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
|
||||
_notificationSubscription =
|
||||
Bus.instance.on<NotificationEvent>().listen((event) {
|
||||
if (mounted) {
|
||||
_showMlBanner = !userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled) &&
|
||||
_showMlBanner = !flagService.hasGrantedMLConsent &&
|
||||
!localSettings.hasSeenMLEnablingBanner;
|
||||
setState(() {});
|
||||
}
|
||||
@@ -115,7 +112,6 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
|
||||
_subscription.cancel();
|
||||
_notificationSubscription.cancel();
|
||||
_previewSubscription.cancel();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -179,9 +175,7 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
userRemoteFlagService.shouldShowRecoveryVerification() &&
|
||||
!_showErrorBanner &&
|
||||
!_showMlBanner
|
||||
_showVerificationBanner()
|
||||
? Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12),
|
||||
@@ -203,6 +197,17 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// _showVerificationBanner after 3 days of installation
|
||||
bool _showVerificationBanner() {
|
||||
if (_showErrorBanner ||
|
||||
_showErrorBanner ||
|
||||
flagService.recoveryKeyVerified) {
|
||||
return false;
|
||||
}
|
||||
final DateTime installTime = localSettings.getInstallDateTime();
|
||||
return DateTime.now().difference(installTime).inDays >= 3;
|
||||
}
|
||||
}
|
||||
|
||||
class SyncStatusWidget extends StatefulWidget {
|
||||
|
||||
@@ -2,15 +2,13 @@ import "package:flutter/cupertino.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/models/button_result.dart';
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:photos/ui/components/buttons/button_widget.dart";
|
||||
import "package:photos/ui/components/dialog_widget.dart";
|
||||
import "package:photos/ui/components/models/button_type.dart";
|
||||
import "package:photos/utils/toast_util.dart";
|
||||
|
||||
Future<bool> requestForMapEnable(BuildContext context) async {
|
||||
const String flagName = UserRemoteFlagService.mapEnabled;
|
||||
if (userRemoteFlagService.getCachedBoolValue(flagName)) {
|
||||
if (flagService.mapEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,10 +24,7 @@ Future<bool> requestForMapEnable(BuildContext context) async {
|
||||
labelText: S.of(context).enableMaps,
|
||||
isInAlert: true,
|
||||
onTap: () async {
|
||||
await userRemoteFlagService.setBoolValue(
|
||||
flagName,
|
||||
true,
|
||||
);
|
||||
await flagService.setMapEnabled(true);
|
||||
},
|
||||
),
|
||||
ButtonWidget(
|
||||
@@ -52,5 +47,5 @@ Future<bool> requestForMapEnable(BuildContext context) async {
|
||||
|
||||
//For debugging.
|
||||
void disableMap() {
|
||||
userRemoteFlagService.setBoolValue(UserRemoteFlagService.mapEnabled, false);
|
||||
flagService.setMapEnabled(false);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import "package:photos/core/error-reporting/super_logging.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/preview_video_store.dart";
|
||||
import "package:photos/services/user_remote_flag_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';
|
||||
@@ -101,19 +100,10 @@ class AdvancedSettingsScreen extends StatelessWidget {
|
||||
singleBorderRadius: 8,
|
||||
alignCaptionedTextToLeft: true,
|
||||
trailingWidget: ToggleSwitchWidget(
|
||||
value: () => userRemoteFlagService.getCachedBoolValue(
|
||||
UserRemoteFlagService.mapEnabled,
|
||||
),
|
||||
value: () => flagService.mapEnabled,
|
||||
onChanged: () async {
|
||||
final isEnabled =
|
||||
userRemoteFlagService.getCachedBoolValue(
|
||||
UserRemoteFlagService.mapEnabled,
|
||||
);
|
||||
|
||||
await userRemoteFlagService.setBoolValue(
|
||||
UserRemoteFlagService.mapEnabled,
|
||||
!isEnabled,
|
||||
);
|
||||
final isEnabled = flagService.mapEnabled;
|
||||
await flagService.setMapEnabled(!isEnabled);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -11,7 +11,6 @@ import "package:photos/services/machine_learning/face_ml/person/person_service.d
|
||||
import "package:photos/services/machine_learning/ml_indexing_isolate.dart";
|
||||
import 'package:photos/services/machine_learning/ml_service.dart';
|
||||
import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/components/captioned_text_widget.dart';
|
||||
import 'package:photos/ui/components/expandable_menu_item_widget.dart';
|
||||
@@ -81,17 +80,12 @@ class _MLDebugSectionWidgetState extends State<MLDebugSectionWidget> {
|
||||
},
|
||||
),
|
||||
trailingWidget: ToggleSwitchWidget(
|
||||
value: () => userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled),
|
||||
value: () => flagService.hasGrantedMLConsent,
|
||||
onChanged: () async {
|
||||
try {
|
||||
final oldMlConsent = userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled);
|
||||
final oldMlConsent = flagService.hasGrantedMLConsent;
|
||||
final mlConsent = !oldMlConsent;
|
||||
await userRemoteFlagService.setBoolValue(
|
||||
UserRemoteFlagService.mlEnabled,
|
||||
mlConsent,
|
||||
);
|
||||
await flagService.setMLConsent(mlConsent);
|
||||
logger.info('ML consent turned ${mlConsent ? 'on' : 'off'}');
|
||||
if (!mlConsent) {
|
||||
MLService.instance.pauseIndexingAndClustering();
|
||||
|
||||
@@ -3,7 +3,6 @@ import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/events/notification_event.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/common/web_page.dart";
|
||||
import "package:photos/ui/components/buttons/button_widget.dart";
|
||||
@@ -153,10 +152,7 @@ class _EnableMachineLearningConsentState
|
||||
|
||||
Future<void> enableMlConsent(BuildContext context) async {
|
||||
try {
|
||||
await userRemoteFlagService.setBoolValue(
|
||||
UserRemoteFlagService.mlEnabled,
|
||||
true,
|
||||
);
|
||||
await flagService.setMLConsent(true);
|
||||
Bus.instance.fire(NotificationEvent());
|
||||
Navigator.of(context).pop(true);
|
||||
} catch (e) {
|
||||
|
||||
@@ -12,7 +12,6 @@ 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/user_remote_flag_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";
|
||||
@@ -77,8 +76,7 @@ class _MachineLearningSettingsPageState
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hasEnabled = userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled);
|
||||
final hasEnabled = flagService.hasGrantedMLConsent;
|
||||
return Scaffold(
|
||||
body: CustomScrollView(
|
||||
primary: false,
|
||||
@@ -211,8 +209,7 @@ class _MachineLearningSettingsPageState
|
||||
}
|
||||
|
||||
Future<void> toggleMlConsent() async {
|
||||
final oldMlConsent = userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled);
|
||||
final oldMlConsent = flagService.hasGrantedMLConsent;
|
||||
// Go to consent page first if not enabled
|
||||
if (!oldMlConsent) {
|
||||
final result = await Navigator.push(
|
||||
@@ -228,10 +225,7 @@ class _MachineLearningSettingsPageState
|
||||
}
|
||||
}
|
||||
final mlConsent = !oldMlConsent;
|
||||
await userRemoteFlagService.setBoolValue(
|
||||
UserRemoteFlagService.mlEnabled,
|
||||
mlConsent,
|
||||
);
|
||||
await flagService.setMLConsent(mlConsent);
|
||||
if (!mlConsent) {
|
||||
MLService.instance.pauseIndexingAndClustering();
|
||||
unawaited(
|
||||
@@ -248,8 +242,7 @@ class _MachineLearningSettingsPageState
|
||||
}
|
||||
|
||||
Widget _getMlSettings(BuildContext context) {
|
||||
final hasEnabled = userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled);
|
||||
final hasEnabled = flagService.hasGrantedMLConsent;
|
||||
if (!hasEnabled) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import "package:photos/models/metadata/file_magic.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/file_magic_service.dart";
|
||||
import "package:photos/services/filedata/filedata_service.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/components/buttons/icon_button_widget.dart';
|
||||
import "package:photos/ui/components/divider_widget.dart";
|
||||
@@ -298,8 +297,7 @@ class _FileDetailsWidgetState extends State<FileDetailsWidget> {
|
||||
]);
|
||||
}
|
||||
|
||||
if (userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mlEnabled)) {
|
||||
if (flagService.hasGrantedMLConsent) {
|
||||
fileDetailsTiles.addAll([
|
||||
FacesItemWidget(file),
|
||||
const FileDetailsDivider(),
|
||||
|
||||
@@ -11,7 +11,6 @@ import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/models/file/file.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:photos/states/location_screen_state.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/components/buttons/chip_button_widget.dart";
|
||||
@@ -183,8 +182,7 @@ class _InfoMapState extends State<InfoMap> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_hasEnabledMap = userRemoteFlagService
|
||||
.getCachedBoolValue(UserRemoteFlagService.mapEnabled);
|
||||
_hasEnabledMap = flagService.mapEnabled;
|
||||
_fileLat = widget.file.location!.latitude!;
|
||||
_fileLng = widget.file.location!.longitude!;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import "package:photos/models/search/index_of_indexed_stack.dart";
|
||||
import "package:photos/models/search/search_result.dart";
|
||||
import "package:photos/models/search/search_types.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/user_remote_flag_service.dart";
|
||||
import "package:photos/states/all_sections_examples_state.dart";
|
||||
import "package:photos/ui/common/loading_widget.dart";
|
||||
import "package:photos/ui/viewer/search/result/no_result_widget.dart";
|
||||
@@ -119,9 +118,7 @@ class _AllSearchSectionsState extends State<AllSearchSections> {
|
||||
itemBuilder: (context, index) {
|
||||
switch (searchTypes[index]) {
|
||||
case SectionType.face:
|
||||
if (!userRemoteFlagService.getCachedBoolValue(
|
||||
UserRemoteFlagService.mlEnabled,
|
||||
)) {
|
||||
if (!flagService.hasGrantedMLConsent) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return PeopleSection(
|
||||
|
||||
@@ -55,6 +55,20 @@ class LocalSettings {
|
||||
}
|
||||
}
|
||||
|
||||
// getEstimatedInstallTimeInMs returns the time when the app was installed
|
||||
// The time is stored in shared preferences and will be reset on logout
|
||||
DateTime getInstallDateTime() {
|
||||
if (_prefs.containsKey('ls.install_time')) {
|
||||
return DateTime.fromMillisecondsSinceEpoch(
|
||||
_prefs.getInt('ls.install_time')!,
|
||||
);
|
||||
} else {
|
||||
final installTime = DateTime.now();
|
||||
_prefs.setInt('ls.install_time', installTime.millisecondsSinceEpoch);
|
||||
return installTime;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setRateUsShownCount(int value) async {
|
||||
await _prefs.setInt(kRateUsShownCount, value);
|
||||
}
|
||||
|
||||
@@ -26,12 +26,36 @@ class RemoteFlags {
|
||||
required this.castUrl,
|
||||
});
|
||||
|
||||
RemoteFlags copyWith({
|
||||
bool? enableStripe,
|
||||
bool? disableCFWorker,
|
||||
bool? mapEnabled,
|
||||
bool? faceSearchEnabled,
|
||||
bool? recoveryKeyVerified,
|
||||
bool? internalUser,
|
||||
bool? betaUser,
|
||||
bool? enableMobMultiPart,
|
||||
String? castUrl,
|
||||
}) {
|
||||
return RemoteFlags(
|
||||
enableStripe: enableStripe ?? this.enableStripe,
|
||||
disableCFWorker: disableCFWorker ?? this.disableCFWorker,
|
||||
mapEnabled: mapEnabled ?? this.mapEnabled,
|
||||
faceSearchEnabled: faceSearchEnabled ?? this.faceSearchEnabled,
|
||||
recoveryKeyVerified: recoveryKeyVerified ?? this.recoveryKeyVerified,
|
||||
internalUser: internalUser ?? this.internalUser,
|
||||
betaUser: betaUser ?? this.betaUser,
|
||||
enableMobMultiPart: enableMobMultiPart ?? this.enableMobMultiPart,
|
||||
castUrl: castUrl ?? this.castUrl,
|
||||
);
|
||||
}
|
||||
|
||||
static RemoteFlags defaultValue = RemoteFlags(
|
||||
enableStripe: Platform.isAndroid,
|
||||
disableCFWorker: false,
|
||||
mapEnabled: false,
|
||||
faceSearchEnabled: false,
|
||||
recoveryKeyVerified: false,
|
||||
recoveryKeyVerified: true,
|
||||
internalUser: kDebugMode,
|
||||
betaUser: kDebugMode,
|
||||
enableMobMultiPart: false,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// ignore_for_file: always_use_package_imports
|
||||
|
||||
import "dart:async";
|
||||
import "dart:convert";
|
||||
import "dart:developer";
|
||||
import "dart:io";
|
||||
@@ -13,10 +14,8 @@ import "model.dart";
|
||||
class FlagService {
|
||||
final SharedPreferences _prefs;
|
||||
final Dio _enteDio;
|
||||
late final bool _usingEnteEmail;
|
||||
|
||||
FlagService(this._prefs, this._enteDio) {
|
||||
_usingEnteEmail = _prefs.getString("email")?.endsWith("@ente.io") ?? false;
|
||||
Future.delayed(const Duration(seconds: 5), () {
|
||||
_fetch();
|
||||
});
|
||||
@@ -39,25 +38,9 @@ class FlagService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetch() async {
|
||||
try {
|
||||
if (!_prefs.containsKey("token")) {
|
||||
log("token not found, skip", name: "FlagService");
|
||||
return;
|
||||
}
|
||||
log("fetching feature flags", name: "FlagService");
|
||||
final response = await _enteDio.get("/remote-store/feature-flags");
|
||||
final remoteFlags = RemoteFlags.fromMap(response.data);
|
||||
await _prefs.setString("remote_flags", remoteFlags.toJson());
|
||||
_flags = remoteFlags;
|
||||
} catch (e) {
|
||||
debugPrint("Failed to sync feature flags $e");
|
||||
}
|
||||
}
|
||||
|
||||
bool get disableCFWorker => flags.disableCFWorker;
|
||||
|
||||
bool get internalUser => flags.internalUser || _usingEnteEmail || kDebugMode;
|
||||
bool get internalUser => flags.internalUser || kDebugMode;
|
||||
|
||||
bool get betaUser => flags.betaUser;
|
||||
|
||||
@@ -76,4 +59,70 @@ class FlagService {
|
||||
bool get enableMobMultiPart => flags.enableMobMultiPart || internalUser;
|
||||
|
||||
String get castUrl => flags.castUrl;
|
||||
|
||||
Future<void> setMapEnabled(bool isEnabled) async {
|
||||
await _updateKeyValue("mapEnabled", isEnabled.toString());
|
||||
_updateFlags(flags.copyWith(mapEnabled: isEnabled));
|
||||
}
|
||||
|
||||
Future<void> setMLConsent(bool isEnabled) async {
|
||||
await _updateKeyValue("faceSearchEnabled", isEnabled.toString());
|
||||
_updateFlags(flags.copyWith(faceSearchEnabled: isEnabled));
|
||||
}
|
||||
|
||||
Future<void> setRecoveryKeyVerified(bool isVerified) async {
|
||||
await _updateKeyValue("recoveryKeyVerified", isVerified.toString());
|
||||
_updateFlags(flags.copyWith(recoveryKeyVerified: isVerified));
|
||||
}
|
||||
|
||||
Completer<void>? _fetchCompleter;
|
||||
Future<void> _fetch() async {
|
||||
if (_fetchCompleter != null) {
|
||||
await _fetchCompleter!.future;
|
||||
return;
|
||||
}
|
||||
_fetchCompleter = Completer<void>();
|
||||
try {
|
||||
if (!_prefs.containsKey("token")) {
|
||||
log("token not found, skip", name: "FlagService");
|
||||
_fetchCompleter!.complete();
|
||||
_fetchCompleter = null;
|
||||
return;
|
||||
}
|
||||
log("fetching feature flags", name: "FlagService");
|
||||
final response = await _enteDio.get("/remote-store/feature-flags");
|
||||
final remoteFlags = RemoteFlags.fromMap(response.data);
|
||||
await _prefs.setString("remote_flags", remoteFlags.toJson());
|
||||
_flags = remoteFlags;
|
||||
} catch (e) {
|
||||
debugPrint("Failed to sync feature flags $e");
|
||||
} finally {
|
||||
_fetchCompleter!.complete();
|
||||
_fetchCompleter = null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateKeyValue(String key, String value) async {
|
||||
try {
|
||||
final response = await _enteDio.post(
|
||||
"/remote-store/update",
|
||||
data: {
|
||||
"key": key,
|
||||
"value": value,
|
||||
},
|
||||
);
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception("Unexpected state");
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Failed to set flag for $key $e");
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
void _updateFlags(RemoteFlags flags) {
|
||||
_flags = flags;
|
||||
_prefs.setString("remote_flags", flags.toJson());
|
||||
_fetch().ignore();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user