2021-06-23 10:15:25 +02:00
|
|
|
import 'package:event_bus/event_bus.dart';
|
2021-10-29 19:40:35 +02:00
|
|
|
import 'package:flutter/foundation.dart';
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:kiwi/kiwi.dart';
|
|
|
|
import 'package:logging/logging.dart';
|
|
|
|
import 'package:nc_photos/account.dart';
|
2021-07-25 07:00:38 +02:00
|
|
|
import 'package:nc_photos/app_localizations.dart';
|
2021-09-10 13:46:15 +02:00
|
|
|
import 'package:nc_photos/debug_util.dart';
|
2021-06-23 10:15:25 +02:00
|
|
|
import 'package:nc_photos/event/event.dart';
|
2021-10-19 22:11:35 +02:00
|
|
|
import 'package:nc_photos/exception_util.dart' as exception_util;
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:nc_photos/k.dart' as k;
|
2021-06-23 10:15:25 +02:00
|
|
|
import 'package:nc_photos/language_util.dart' as language_util;
|
2021-08-30 18:57:45 +02:00
|
|
|
import 'package:nc_photos/mobile/android/android_info.dart';
|
2021-09-10 13:46:15 +02:00
|
|
|
import 'package:nc_photos/mobile/notification.dart';
|
2021-08-23 16:19:33 +02:00
|
|
|
import 'package:nc_photos/platform/k.dart' as platform_k;
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:nc_photos/pref.dart';
|
|
|
|
import 'package:nc_photos/snack_bar_manager.dart';
|
|
|
|
import 'package:nc_photos/theme.dart';
|
2021-08-02 07:53:48 +02:00
|
|
|
import 'package:nc_photos/widget/fancy_option_picker.dart';
|
2021-10-19 22:11:35 +02:00
|
|
|
import 'package:nc_photos/widget/home.dart';
|
|
|
|
import 'package:nc_photos/widget/root_picker.dart';
|
2021-10-21 18:33:21 +02:00
|
|
|
import 'package:nc_photos/widget/share_folder_picker.dart';
|
2021-08-23 16:19:33 +02:00
|
|
|
import 'package:nc_photos/widget/stateful_slider.dart';
|
|
|
|
import 'package:screen_brightness/screen_brightness.dart';
|
2021-04-10 06:28:12 +02:00
|
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
|
|
|
|
class SettingsArguments {
|
|
|
|
SettingsArguments(this.account);
|
|
|
|
|
|
|
|
final Account account;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Settings extends StatefulWidget {
|
|
|
|
static const routeName = "/settings";
|
|
|
|
|
2021-07-23 22:05:57 +02:00
|
|
|
static Route buildRoute(SettingsArguments args) => MaterialPageRoute(
|
|
|
|
builder: (context) => Settings.fromArgs(args),
|
|
|
|
);
|
|
|
|
|
2021-09-15 08:58:06 +02:00
|
|
|
const Settings({
|
2021-07-23 22:05:57 +02:00
|
|
|
Key? key,
|
|
|
|
required this.account,
|
2021-04-10 06:28:12 +02:00
|
|
|
}) : super(key: key);
|
|
|
|
|
2021-07-23 22:05:57 +02:00
|
|
|
Settings.fromArgs(SettingsArguments args, {Key? key})
|
2021-04-10 06:28:12 +02:00
|
|
|
: this(
|
2021-09-15 08:58:06 +02:00
|
|
|
key: key,
|
2021-04-10 06:28:12 +02:00
|
|
|
account: args.account,
|
|
|
|
);
|
|
|
|
|
|
|
|
@override
|
|
|
|
createState() => _SettingsState();
|
|
|
|
|
|
|
|
final Account account;
|
|
|
|
}
|
|
|
|
|
|
|
|
class _SettingsState extends State<Settings> {
|
|
|
|
@override
|
|
|
|
initState() {
|
|
|
|
super.initState();
|
2021-10-27 22:40:54 +02:00
|
|
|
_isEnableExif = Pref().isEnableExifOr();
|
2021-04-10 06:28:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
2021-09-02 10:33:35 +02:00
|
|
|
build(BuildContext context) {
|
2021-04-10 06:28:12 +02:00
|
|
|
return AppTheme(
|
|
|
|
child: Scaffold(
|
|
|
|
body: Builder(
|
|
|
|
builder: (context) => _buildContent(context),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildContent(BuildContext context) {
|
2021-08-29 13:51:43 +02:00
|
|
|
final translator = L10n.global().translator;
|
2021-04-10 06:28:12 +02:00
|
|
|
return CustomScrollView(
|
|
|
|
slivers: [
|
|
|
|
SliverAppBar(
|
|
|
|
pinned: true,
|
2021-08-29 13:51:43 +02:00
|
|
|
title: Text(L10n.global().settingsWidgetTitle),
|
2021-04-10 06:28:12 +02:00
|
|
|
),
|
|
|
|
SliverList(
|
|
|
|
delegate: SliverChildListDelegate(
|
|
|
|
[
|
2021-06-23 10:15:25 +02:00
|
|
|
ListTile(
|
2021-08-29 13:51:43 +02:00
|
|
|
title: Text(L10n.global().settingsLanguageTitle),
|
2021-08-29 10:30:37 +02:00
|
|
|
subtitle: Text(language_util.getSelectedLanguageName()),
|
2021-06-23 10:15:25 +02:00
|
|
|
onTap: () => _onLanguageTap(context),
|
|
|
|
),
|
2021-04-10 06:28:12 +02:00
|
|
|
SwitchListTile(
|
2021-08-29 13:51:43 +02:00
|
|
|
title: Text(L10n.global().settingsExifSupportTitle),
|
2021-04-10 06:28:12 +02:00
|
|
|
subtitle: _isEnableExif
|
2021-08-29 13:51:43 +02:00
|
|
|
? Text(L10n.global().settingsExifSupportTrueSubtitle)
|
2021-04-10 06:28:12 +02:00
|
|
|
: null,
|
|
|
|
value: _isEnableExif,
|
|
|
|
onChanged: (value) => _onExifSupportChanged(context, value),
|
|
|
|
),
|
2021-10-19 22:11:35 +02:00
|
|
|
_buildSubSettings(
|
|
|
|
context,
|
2021-10-19 23:19:42 +02:00
|
|
|
leading: Icon(
|
|
|
|
Icons.manage_accounts_outlined,
|
|
|
|
color: AppTheme.getUnfocusedIconColor(context),
|
|
|
|
),
|
2021-10-19 22:11:35 +02:00
|
|
|
label: L10n.global().settingsAccountTitle,
|
|
|
|
builder: () => AccountSettingsWidget(account: widget.account),
|
|
|
|
),
|
2021-09-02 10:33:35 +02:00
|
|
|
if (platform_k.isMobile)
|
|
|
|
_buildSubSettings(
|
|
|
|
context,
|
2021-10-19 23:19:42 +02:00
|
|
|
leading: Icon(
|
|
|
|
Icons.view_carousel_outlined,
|
|
|
|
color: AppTheme.getUnfocusedIconColor(context),
|
|
|
|
),
|
2021-09-02 10:33:35 +02:00
|
|
|
label: L10n.global().settingsViewerTitle,
|
|
|
|
description: L10n.global().settingsViewerDescription,
|
|
|
|
builder: () => _ViewerSettings(),
|
2021-08-24 14:56:14 +02:00
|
|
|
),
|
2021-10-04 15:53:03 +02:00
|
|
|
_buildSubSettings(
|
|
|
|
context,
|
2021-10-19 23:19:42 +02:00
|
|
|
leading: Icon(
|
|
|
|
Icons.photo_album_outlined,
|
|
|
|
color: AppTheme.getUnfocusedIconColor(context),
|
|
|
|
),
|
2021-10-04 15:53:03 +02:00
|
|
|
label: L10n.global().settingsAlbumTitle,
|
|
|
|
description: L10n.global().settingsAlbumDescription,
|
|
|
|
builder: () => _AlbumSettings(),
|
|
|
|
),
|
2021-09-02 10:33:35 +02:00
|
|
|
_buildSubSettings(
|
|
|
|
context,
|
2021-10-19 23:19:42 +02:00
|
|
|
leading: Icon(
|
|
|
|
Icons.palette_outlined,
|
|
|
|
color: AppTheme.getUnfocusedIconColor(context),
|
|
|
|
),
|
2021-09-02 10:33:35 +02:00
|
|
|
label: L10n.global().settingsThemeTitle,
|
|
|
|
description: L10n.global().settingsThemeDescription,
|
|
|
|
builder: () => _ThemeSettings(),
|
2021-08-30 15:58:37 +02:00
|
|
|
),
|
2021-10-29 19:40:35 +02:00
|
|
|
if (_enabledExperiments.isNotEmpty)
|
|
|
|
_buildSubSettings(
|
|
|
|
context,
|
|
|
|
leading: Icon(
|
|
|
|
Icons.science_outlined,
|
|
|
|
color: AppTheme.getUnfocusedIconColor(context),
|
|
|
|
),
|
|
|
|
label: L10n.global().settingsExperimentalTitle,
|
|
|
|
description: L10n.global().settingsExperimentalDescription,
|
|
|
|
builder: () => _ExperimentalSettings(),
|
2021-10-25 16:51:04 +02:00
|
|
|
),
|
2021-08-29 13:51:43 +02:00
|
|
|
_buildCaption(context, L10n.global().settingsAboutSectionTitle),
|
2021-04-10 06:28:12 +02:00
|
|
|
ListTile(
|
2021-08-29 13:51:43 +02:00
|
|
|
title: Text(L10n.global().settingsVersionTitle),
|
2021-04-16 10:45:17 +02:00
|
|
|
subtitle: const Text(k.versionStr),
|
2021-04-10 06:28:12 +02:00
|
|
|
),
|
|
|
|
ListTile(
|
2021-08-29 13:51:43 +02:00
|
|
|
title: Text(L10n.global().settingsSourceCodeTitle),
|
2021-09-02 10:33:35 +02:00
|
|
|
subtitle: const Text(_sourceRepo),
|
|
|
|
onTap: () {
|
|
|
|
launch(_sourceRepo);
|
2021-04-10 06:28:12 +02:00
|
|
|
},
|
|
|
|
),
|
2021-06-10 15:49:17 +02:00
|
|
|
ListTile(
|
2021-08-29 13:51:43 +02:00
|
|
|
title: Text(L10n.global().settingsBugReportTitle),
|
2021-06-10 15:49:17 +02:00
|
|
|
onTap: () {
|
|
|
|
launch(_bugReportUrl);
|
|
|
|
},
|
|
|
|
),
|
2021-09-10 13:46:15 +02:00
|
|
|
SwitchListTile(
|
|
|
|
title: Text(L10n.global().settingsCaptureLogsTitle),
|
|
|
|
subtitle: Text(L10n.global().settingsCaptureLogsDescription),
|
|
|
|
value: LogCapturer().isEnable,
|
|
|
|
onChanged: (value) => _onCaptureLogChanged(context, value),
|
|
|
|
),
|
2021-06-22 20:18:21 +02:00
|
|
|
if (translator.isNotEmpty)
|
|
|
|
ListTile(
|
2021-08-29 13:51:43 +02:00
|
|
|
title: Text(L10n.global().settingsTranslatorTitle),
|
2021-06-22 20:18:21 +02:00
|
|
|
subtitle: Text(translator),
|
2021-07-03 16:36:18 +02:00
|
|
|
onTap: () {
|
|
|
|
launch(_translationUrl);
|
|
|
|
},
|
|
|
|
)
|
|
|
|
else
|
|
|
|
ListTile(
|
2021-09-02 10:33:35 +02:00
|
|
|
title: const Text("Improve translation"),
|
|
|
|
subtitle: const Text("Help translating to your language"),
|
2021-07-03 16:36:18 +02:00
|
|
|
onTap: () {
|
|
|
|
launch(_translationUrl);
|
|
|
|
},
|
2021-06-22 20:18:21 +02:00
|
|
|
),
|
2021-04-10 06:28:12 +02:00
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-09-02 10:33:35 +02:00
|
|
|
Widget _buildSubSettings(
|
|
|
|
BuildContext context, {
|
2021-10-19 23:19:42 +02:00
|
|
|
Widget? leading,
|
2021-09-02 10:33:35 +02:00
|
|
|
required String label,
|
|
|
|
String? description,
|
|
|
|
required Widget Function() builder,
|
|
|
|
}) {
|
|
|
|
return ListTile(
|
2021-10-19 23:19:42 +02:00
|
|
|
leading: leading == null
|
|
|
|
? null
|
|
|
|
: SizedBox(
|
|
|
|
height: double.infinity,
|
|
|
|
child: leading,
|
|
|
|
),
|
2021-09-02 10:33:35 +02:00
|
|
|
title: Text(label),
|
|
|
|
subtitle: description == null ? null : Text(description),
|
|
|
|
onTap: () {
|
|
|
|
Navigator.of(context).push(
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (_) => builder(),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-06-23 10:15:25 +02:00
|
|
|
void _onLanguageTap(BuildContext context) {
|
2021-08-02 07:53:48 +02:00
|
|
|
final selected =
|
2021-10-27 22:40:54 +02:00
|
|
|
Pref().getLanguageOr(language_util.supportedLanguages[0]!.langId);
|
2021-06-23 10:15:25 +02:00
|
|
|
showDialog(
|
|
|
|
context: context,
|
2021-08-02 07:53:48 +02:00
|
|
|
builder: (context) => FancyOptionPicker(
|
|
|
|
items: language_util.supportedLanguages.values
|
|
|
|
.map((lang) => FancyOptionPickerItem(
|
|
|
|
label: lang.nativeName,
|
2021-08-28 22:58:10 +02:00
|
|
|
description: lang.isoName,
|
2021-08-02 07:53:48 +02:00
|
|
|
isSelected: lang.langId == selected,
|
|
|
|
onSelect: () {
|
2021-06-23 10:15:25 +02:00
|
|
|
_log.info(
|
|
|
|
"[_onLanguageTap] Set language: ${lang.nativeName}");
|
|
|
|
Navigator.of(context).pop(lang.langId);
|
|
|
|
},
|
2021-08-02 07:53:48 +02:00
|
|
|
dense: true,
|
2021-06-23 10:15:25 +02:00
|
|
|
))
|
|
|
|
.toList(),
|
|
|
|
),
|
|
|
|
).then((value) {
|
|
|
|
if (value != null) {
|
2021-10-27 22:40:54 +02:00
|
|
|
Pref().setLanguage(value).then((_) {
|
2021-06-23 10:15:25 +02:00
|
|
|
KiwiContainer().resolve<EventBus>().fire(LanguageChangedEvent());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-04-10 06:28:12 +02:00
|
|
|
void _onExifSupportChanged(BuildContext context, bool value) {
|
|
|
|
if (value) {
|
|
|
|
showDialog(
|
|
|
|
context: context,
|
2021-08-30 12:14:16 +02:00
|
|
|
builder: (context) => AppTheme(
|
|
|
|
child: AlertDialog(
|
|
|
|
title: Text(L10n.global().exifSupportConfirmationDialogTitle),
|
|
|
|
content: Text(L10n.global().exifSupportDetails),
|
|
|
|
actions: <Widget>[
|
|
|
|
TextButton(
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
},
|
|
|
|
child:
|
|
|
|
Text(MaterialLocalizations.of(context).cancelButtonLabel),
|
|
|
|
),
|
|
|
|
TextButton(
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop(true);
|
|
|
|
},
|
|
|
|
child: Text(L10n.global().enableButtonLabel),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
2021-04-10 06:28:12 +02:00
|
|
|
),
|
|
|
|
).then((value) {
|
|
|
|
if (value == true) {
|
|
|
|
_setExifSupport(true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
_setExifSupport(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-10 13:46:15 +02:00
|
|
|
void _onCaptureLogChanged(BuildContext context, bool value) async {
|
|
|
|
if (value) {
|
|
|
|
final result = await showDialog<bool>(
|
|
|
|
context: context,
|
|
|
|
builder: (context) => AppTheme(
|
|
|
|
child: AlertDialog(
|
|
|
|
content: Text(L10n.global().captureLogDetails),
|
|
|
|
actions: [
|
|
|
|
TextButton(
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop(true);
|
|
|
|
},
|
|
|
|
child: Text(L10n.global().enableButtonLabel),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
if (result == true) {
|
|
|
|
setState(() {
|
|
|
|
LogCapturer().start();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (LogCapturer().isEnable) {
|
|
|
|
setState(() {
|
|
|
|
LogCapturer().stop().then((result) {
|
|
|
|
_onLogSaveSuccessful(result);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _onLogSaveSuccessful(dynamic result) {
|
2021-09-15 08:58:06 +02:00
|
|
|
dynamic notif;
|
2021-09-10 13:46:15 +02:00
|
|
|
if (platform_k.isAndroid) {
|
|
|
|
notif = AndroidLogSaveSuccessfulNotification(result);
|
|
|
|
}
|
|
|
|
if (notif != null) {
|
|
|
|
try {
|
|
|
|
notif.notify();
|
|
|
|
return;
|
|
|
|
} catch (e, stacktrace) {
|
|
|
|
_log.shout(
|
|
|
|
"[_onLogSaveSuccessful] Failed showing platform notification",
|
|
|
|
e,
|
|
|
|
stacktrace);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// fallback
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().downloadSuccessNotification),
|
|
|
|
duration: k.snackBarDurationShort,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2021-09-02 10:33:35 +02:00
|
|
|
Future<void> _setExifSupport(bool value) async {
|
|
|
|
final oldValue = _isEnableExif;
|
|
|
|
setState(() {
|
|
|
|
_isEnableExif = value;
|
|
|
|
});
|
2021-10-27 22:40:54 +02:00
|
|
|
if (!await Pref().setEnableExif(value)) {
|
2021-09-02 10:33:35 +02:00
|
|
|
_log.severe("[_setExifSupport] Failed writing pref");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
setState(() {
|
|
|
|
_isEnableExif = oldValue;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
late bool _isEnableExif;
|
|
|
|
|
|
|
|
static final _log = Logger("widget.settings._SettingsState");
|
|
|
|
|
|
|
|
static const String _sourceRepo = "https://gitlab.com/nkming2/nc-photos";
|
|
|
|
static const String _bugReportUrl =
|
|
|
|
"https://gitlab.com/nkming2/nc-photos/-/issues";
|
|
|
|
static const String _translationUrl =
|
|
|
|
"https://gitlab.com/nkming2/nc-photos/-/tree/master/lib/l10n";
|
|
|
|
}
|
|
|
|
|
2021-10-19 22:11:35 +02:00
|
|
|
class AccountSettingsWidgetArguments {
|
|
|
|
const AccountSettingsWidgetArguments(this.account);
|
|
|
|
|
|
|
|
final Account account;
|
|
|
|
}
|
|
|
|
|
|
|
|
class AccountSettingsWidget extends StatefulWidget {
|
|
|
|
static const routeName = "/account-settings";
|
|
|
|
|
|
|
|
static Route buildRoute(AccountSettingsWidgetArguments args) =>
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (context) => AccountSettingsWidget.fromArgs(args),
|
|
|
|
);
|
|
|
|
|
|
|
|
const AccountSettingsWidget({
|
|
|
|
Key? key,
|
|
|
|
required this.account,
|
|
|
|
}) : super(key: key);
|
|
|
|
|
|
|
|
AccountSettingsWidget.fromArgs(AccountSettingsWidgetArguments args,
|
|
|
|
{Key? key})
|
|
|
|
: this(
|
|
|
|
key: key,
|
|
|
|
account: args.account,
|
|
|
|
);
|
|
|
|
|
|
|
|
@override
|
|
|
|
createState() => _AccountSettingsState();
|
|
|
|
|
|
|
|
final Account account;
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AccountSettingsState extends State<AccountSettingsWidget> {
|
|
|
|
@override
|
|
|
|
initState() {
|
|
|
|
super.initState();
|
|
|
|
_account = widget.account;
|
2021-10-19 22:53:31 +02:00
|
|
|
|
2021-10-27 22:40:54 +02:00
|
|
|
final settings = Pref().getAccountSettings(_account);
|
2021-10-19 22:53:31 +02:00
|
|
|
_isEnableFaceRecognitionApp = settings.isEnableFaceRecognitionApp;
|
2021-10-21 18:33:21 +02:00
|
|
|
_shareFolder = settings.shareFolder;
|
2021-10-19 22:11:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
build(BuildContext context) {
|
|
|
|
return AppTheme(
|
|
|
|
child: Scaffold(
|
|
|
|
body: Builder(
|
|
|
|
builder: (context) => _buildContent(context),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildContent(BuildContext context) {
|
2021-10-25 18:17:10 +02:00
|
|
|
return WillPopScope(
|
|
|
|
onWillPop: () async => !_hasModified,
|
|
|
|
child: CustomScrollView(
|
|
|
|
slivers: [
|
|
|
|
SliverAppBar(
|
|
|
|
pinned: true,
|
|
|
|
title: Text(L10n.global().settingsAccountPageTitle),
|
|
|
|
leading: _hasModified
|
|
|
|
? IconButton(
|
|
|
|
icon: const Icon(Icons.check),
|
|
|
|
tooltip: L10n.global().doneButtonTooltip,
|
|
|
|
onPressed: () => _onDonePressed(context),
|
|
|
|
)
|
|
|
|
: null,
|
2021-10-19 22:11:35 +02:00
|
|
|
),
|
2021-10-25 18:17:10 +02:00
|
|
|
SliverList(
|
|
|
|
delegate: SliverChildListDelegate(
|
|
|
|
[
|
|
|
|
ListTile(
|
|
|
|
title: Text(L10n.global().settingsIncludedFoldersTitle),
|
|
|
|
subtitle: Text(_account.roots.map((e) => "/$e").join("; ")),
|
|
|
|
onTap: _onIncludedFoldersPressed,
|
|
|
|
),
|
|
|
|
ListTile(
|
|
|
|
title: Text(L10n.global().settingsShareFolderTitle),
|
|
|
|
subtitle: Text("/$_shareFolder"),
|
|
|
|
onTap: () => _onShareFolderPressed(context),
|
|
|
|
),
|
|
|
|
_buildCaption(
|
|
|
|
context, L10n.global().settingsServerAppSectionTitle),
|
|
|
|
SwitchListTile(
|
|
|
|
title: const Text("Face Recognition"),
|
|
|
|
value: _isEnableFaceRecognitionApp,
|
|
|
|
onChanged: _onEnableFaceRecognitionAppChanged,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
2021-10-19 22:11:35 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _onDonePressed(BuildContext context) {
|
|
|
|
Navigator.of(context).pushNamedAndRemoveUntil(
|
|
|
|
Home.routeName,
|
|
|
|
(route) => false,
|
|
|
|
arguments: HomeArguments(_account),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _onIncludedFoldersPressed() async {
|
|
|
|
try {
|
|
|
|
final result = await Navigator.of(context).pushNamed<Account>(
|
|
|
|
RootPicker.routeName,
|
|
|
|
arguments: RootPickerArguments(_account));
|
|
|
|
if (result == null) {
|
|
|
|
// user canceled
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// we've got a good account
|
|
|
|
if (result == _account) {
|
|
|
|
// no changes, do nothing
|
|
|
|
_log.fine("[_onIncludedFoldersPressed] No changes");
|
|
|
|
return;
|
|
|
|
}
|
2021-10-27 22:40:54 +02:00
|
|
|
final accounts = Pref().getAccounts2()!;
|
2021-10-19 22:11:35 +02:00
|
|
|
if (accounts.any((element) => element.account == result)) {
|
|
|
|
// conflict with another account. This normally won't happen because
|
|
|
|
// the app passwords are unique to each entry, but just in case
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().editAccountConflictFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
final index = _findAccount(_account, accounts);
|
|
|
|
if (index < 0) {
|
|
|
|
_log.shout("[_onIncludedFoldersPressed] Account not found: $_account");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
final newAccount = accounts[index].copyWith(
|
|
|
|
account: result,
|
|
|
|
);
|
|
|
|
accounts[index] = newAccount;
|
2021-10-27 22:40:54 +02:00
|
|
|
if (!await Pref().setAccounts2(accounts)) {
|
2021-10-19 22:11:35 +02:00
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setState(() {
|
|
|
|
_account = result;
|
|
|
|
_hasModified = true;
|
|
|
|
});
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
_log.shout("[_onIncludedFoldersPressed] Exception", e, stackTrace);
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(exception_util.toUserString(e)),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:33:21 +02:00
|
|
|
Future<void> _onShareFolderPressed(BuildContext context) async {
|
|
|
|
final path = await showDialog<String>(
|
|
|
|
context: context,
|
|
|
|
builder: (_) => _ShareFolderDialog(
|
|
|
|
account: widget.account,
|
|
|
|
initialValue: _shareFolder,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
if (path == null || path == _shareFolder) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return _setShareFolder(path);
|
|
|
|
}
|
|
|
|
|
2021-10-19 22:53:31 +02:00
|
|
|
Future<void> _onEnableFaceRecognitionAppChanged(bool value) async {
|
2021-10-21 18:34:00 +02:00
|
|
|
_log.info("[_onEnableFaceRecognitionAppChanged] New value: $value");
|
2021-10-19 22:53:31 +02:00
|
|
|
final oldValue = _isEnableFaceRecognitionApp;
|
|
|
|
setState(() {
|
|
|
|
_isEnableFaceRecognitionApp = value;
|
|
|
|
});
|
|
|
|
if (!await _modifyAccountSettings(
|
|
|
|
_account,
|
|
|
|
isEnableFaceRecognitionApp: value,
|
|
|
|
)) {
|
|
|
|
_log.severe("[_onEnableFaceRecognitionAppChanged] Failed writing pref");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
setState(() {
|
|
|
|
_isEnableFaceRecognitionApp = oldValue;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
setState(() {
|
|
|
|
_hasModified = true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:33:21 +02:00
|
|
|
Future<void> _setShareFolder(String value) async {
|
|
|
|
_log.info("[_setShareFolder] New value: $value");
|
|
|
|
final oldValue = _shareFolder;
|
|
|
|
setState(() {
|
|
|
|
_shareFolder = value;
|
|
|
|
});
|
|
|
|
if (!await _modifyAccountSettings(
|
|
|
|
_account,
|
|
|
|
shareFolder: value,
|
|
|
|
)) {
|
|
|
|
_log.severe("[_setShareFolder] Failed writing pref");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
setState(() {
|
|
|
|
_shareFolder = oldValue;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
setState(() {
|
|
|
|
_hasModified = true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-19 22:53:31 +02:00
|
|
|
static Future<bool> _modifyAccountSettings(
|
|
|
|
Account account, {
|
|
|
|
bool? isEnableFaceRecognitionApp,
|
2021-10-21 18:33:21 +02:00
|
|
|
String? shareFolder,
|
2021-10-19 22:53:31 +02:00
|
|
|
}) {
|
|
|
|
try {
|
2021-10-27 22:40:54 +02:00
|
|
|
final accounts = Pref().getAccounts2()!;
|
2021-10-19 22:53:31 +02:00
|
|
|
final index = _findAccount(account, accounts);
|
|
|
|
accounts[index] = accounts[index].copyWith(
|
|
|
|
settings: accounts[index].settings.copyWith(
|
|
|
|
isEnableFaceRecognitionApp: isEnableFaceRecognitionApp,
|
2021-10-21 18:33:21 +02:00
|
|
|
shareFolder: shareFolder,
|
2021-10-19 22:53:31 +02:00
|
|
|
),
|
|
|
|
);
|
2021-10-27 22:40:54 +02:00
|
|
|
return Pref().setAccounts2(accounts);
|
2021-10-19 22:53:31 +02:00
|
|
|
} catch (e, stackTrace) {
|
|
|
|
_log.severe(
|
|
|
|
"[_modifyAccountSettings] Failed while setting account settings",
|
|
|
|
e,
|
|
|
|
stackTrace);
|
|
|
|
return Future.value(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-19 22:11:35 +02:00
|
|
|
/// Return the index of [account] in [Pref.getAccounts2]
|
|
|
|
static int _findAccount(Account account, [List<PrefAccount>? accounts]) {
|
2021-10-27 22:40:54 +02:00
|
|
|
final from = accounts ?? Pref().getAccounts2Or([]);
|
2021-10-19 22:11:35 +02:00
|
|
|
return from.indexWhere((element) => element.account == account);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool _hasModified = false;
|
|
|
|
late Account _account;
|
2021-10-19 22:53:31 +02:00
|
|
|
late bool _isEnableFaceRecognitionApp;
|
2021-10-21 18:33:21 +02:00
|
|
|
late String _shareFolder;
|
2021-10-19 22:11:35 +02:00
|
|
|
|
|
|
|
static final _log = Logger("widget.settings._AccountSettingsState");
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:33:21 +02:00
|
|
|
class _ShareFolderDialog extends StatefulWidget {
|
|
|
|
const _ShareFolderDialog({
|
|
|
|
Key? key,
|
|
|
|
required this.account,
|
|
|
|
required this.initialValue,
|
|
|
|
}) : super(key: key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
createState() => _ShareFolderDialogState();
|
|
|
|
|
|
|
|
final Account account;
|
|
|
|
final String initialValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ShareFolderDialogState extends State<_ShareFolderDialog> {
|
|
|
|
@override
|
|
|
|
build(BuildContext context) {
|
|
|
|
return AlertDialog(
|
|
|
|
title: Text(L10n.global().settingsShareFolderDialogTitle),
|
|
|
|
content: Form(
|
|
|
|
key: _formKey,
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
Text(L10n.global().settingsShareFolderDialogDescription),
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
InkWell(
|
|
|
|
onTap: _onTextFieldPressed,
|
|
|
|
child: TextFormField(
|
|
|
|
enabled: false,
|
|
|
|
controller: _controller,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
actions: [
|
|
|
|
TextButton(
|
|
|
|
onPressed: _onOkPressed,
|
|
|
|
child: Text(MaterialLocalizations.of(context).okButtonLabel),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _onTextFieldPressed() async {
|
|
|
|
final pick = await Navigator.of(context).pushNamed<String>(
|
|
|
|
ShareFolderPicker.routeName,
|
|
|
|
arguments: ShareFolderPickerArguments(widget.account, _path));
|
|
|
|
if (pick != null) {
|
|
|
|
_path = pick;
|
|
|
|
_controller.text = "/$pick";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _onOkPressed() {
|
|
|
|
Navigator.of(context).pop(_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
final _formKey = GlobalKey<FormState>();
|
|
|
|
late final _controller =
|
|
|
|
TextEditingController(text: "/${widget.initialValue}");
|
|
|
|
late String _path = widget.initialValue;
|
|
|
|
}
|
|
|
|
|
2021-09-02 10:33:35 +02:00
|
|
|
class _ViewerSettings extends StatefulWidget {
|
|
|
|
@override
|
|
|
|
createState() => _ViewerSettingsState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ViewerSettingsState extends State<_ViewerSettings> {
|
|
|
|
@override
|
|
|
|
initState() {
|
|
|
|
super.initState();
|
2021-10-27 22:40:54 +02:00
|
|
|
_screenBrightness = Pref().getViewerScreenBrightnessOr(-1);
|
|
|
|
_isForceRotation = Pref().isViewerForceRotationOr(false);
|
2021-09-02 10:33:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
build(BuildContext context) {
|
|
|
|
return AppTheme(
|
|
|
|
child: Scaffold(
|
|
|
|
body: Builder(
|
|
|
|
builder: (context) => _buildContent(context),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildContent(BuildContext context) {
|
|
|
|
return CustomScrollView(
|
|
|
|
slivers: [
|
|
|
|
SliverAppBar(
|
|
|
|
pinned: true,
|
|
|
|
title: Text(L10n.global().settingsViewerPageTitle),
|
|
|
|
),
|
|
|
|
SliverList(
|
|
|
|
delegate: SliverChildListDelegate(
|
|
|
|
[
|
|
|
|
SwitchListTile(
|
|
|
|
title: Text(L10n.global().settingsScreenBrightnessTitle),
|
|
|
|
subtitle:
|
|
|
|
Text(L10n.global().settingsScreenBrightnessDescription),
|
|
|
|
value: _screenBrightness >= 0,
|
|
|
|
onChanged: (value) =>
|
|
|
|
_onScreenBrightnessChanged(context, value),
|
|
|
|
),
|
|
|
|
SwitchListTile(
|
|
|
|
title: Text(L10n.global().settingsForceRotationTitle),
|
|
|
|
subtitle: Text(L10n.global().settingsForceRotationDescription),
|
|
|
|
value: _isForceRotation,
|
|
|
|
onChanged: (value) => _onForceRotationChanged(value),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-08-23 16:19:33 +02:00
|
|
|
void _onScreenBrightnessChanged(BuildContext context, bool value) async {
|
|
|
|
if (value) {
|
|
|
|
var brightness = 0.5;
|
|
|
|
try {
|
|
|
|
await ScreenBrightness.setScreenBrightness(brightness);
|
|
|
|
final value = await showDialog<int>(
|
|
|
|
context: context,
|
|
|
|
builder: (_) => AppTheme(
|
|
|
|
child: AlertDialog(
|
2021-08-29 13:51:43 +02:00
|
|
|
title: Text(L10n.global().settingsScreenBrightnessTitle),
|
2021-08-23 16:19:33 +02:00
|
|
|
content: Column(
|
2021-08-24 15:50:44 +02:00
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
2021-08-23 16:19:33 +02:00
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
2021-08-29 13:51:43 +02:00
|
|
|
Text(L10n.global().settingsScreenBrightnessDescription),
|
2021-08-23 16:19:33 +02:00
|
|
|
const SizedBox(height: 8),
|
|
|
|
Row(
|
|
|
|
mainAxisSize: MainAxisSize.max,
|
|
|
|
children: [
|
2021-08-23 19:27:08 +02:00
|
|
|
Icon(
|
|
|
|
Icons.brightness_low,
|
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
2021-08-23 16:19:33 +02:00
|
|
|
Expanded(
|
|
|
|
child: StatefulSlider(
|
|
|
|
initialValue: brightness,
|
|
|
|
min: 0.01,
|
|
|
|
onChangeEnd: (value) async {
|
|
|
|
brightness = value;
|
|
|
|
try {
|
|
|
|
await ScreenBrightness.setScreenBrightness(value);
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
_log.severe("Failed while setScreenBrightness", e,
|
|
|
|
stackTrace);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
2021-08-23 19:27:08 +02:00
|
|
|
Icon(
|
|
|
|
Icons.brightness_high,
|
|
|
|
color: AppTheme.getSecondaryTextColor(context),
|
|
|
|
),
|
2021-08-23 16:19:33 +02:00
|
|
|
],
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
actions: <Widget>[
|
|
|
|
TextButton(
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop((brightness * 100).round());
|
|
|
|
},
|
|
|
|
child: Text(MaterialLocalizations.of(context).okButtonLabel),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
if (value != null) {
|
|
|
|
_setScreenBrightness(value);
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
ScreenBrightness.resetScreenBrightness();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_setScreenBrightness(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-24 14:56:14 +02:00
|
|
|
void _onForceRotationChanged(bool value) => _setForceRotation(value);
|
|
|
|
|
2021-09-02 10:33:35 +02:00
|
|
|
Future<void> _setScreenBrightness(int value) async {
|
|
|
|
final oldValue = _screenBrightness;
|
|
|
|
setState(() {
|
|
|
|
_screenBrightness = value;
|
|
|
|
});
|
2021-10-27 22:40:54 +02:00
|
|
|
if (!await Pref().setViewerScreenBrightness(value)) {
|
2021-09-02 10:33:35 +02:00
|
|
|
_log.severe("[_setScreenBrightness] Failed writing pref");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
setState(() {
|
|
|
|
_screenBrightness = oldValue;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _setForceRotation(bool value) async {
|
|
|
|
final oldValue = _isForceRotation;
|
|
|
|
setState(() {
|
|
|
|
_isForceRotation = value;
|
|
|
|
});
|
2021-10-27 22:40:54 +02:00
|
|
|
if (!await Pref().setViewerForceRotation(value)) {
|
2021-09-02 10:33:35 +02:00
|
|
|
_log.severe("[_setForceRotation] Failed writing pref");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
setState(() {
|
|
|
|
_isForceRotation = oldValue;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
late int _screenBrightness;
|
|
|
|
late bool _isForceRotation;
|
|
|
|
|
|
|
|
static final _log = Logger("widget.settings._ViewerSettingsState");
|
|
|
|
}
|
|
|
|
|
2021-10-04 15:53:03 +02:00
|
|
|
class _AlbumSettings extends StatefulWidget {
|
|
|
|
@override
|
|
|
|
createState() => _AlbumSettingsState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AlbumSettingsState extends State<_AlbumSettings> {
|
|
|
|
@override
|
|
|
|
initState() {
|
|
|
|
super.initState();
|
2021-10-27 22:40:54 +02:00
|
|
|
_isBrowserShowDate = Pref().isAlbumBrowserShowDateOr();
|
2021-10-04 15:53:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
build(BuildContext context) {
|
|
|
|
return AppTheme(
|
|
|
|
child: Scaffold(
|
|
|
|
body: Builder(
|
|
|
|
builder: (context) => _buildContent(context),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildContent(BuildContext context) {
|
|
|
|
return CustomScrollView(
|
|
|
|
slivers: [
|
|
|
|
SliverAppBar(
|
|
|
|
pinned: true,
|
|
|
|
title: Text(L10n.global().settingsAlbumPageTitle),
|
|
|
|
),
|
|
|
|
SliverList(
|
|
|
|
delegate: SliverChildListDelegate(
|
|
|
|
[
|
|
|
|
SwitchListTile(
|
|
|
|
title: Text(L10n.global().settingsShowDateInAlbumTitle),
|
|
|
|
subtitle:
|
|
|
|
Text(L10n.global().settingsShowDateInAlbumDescription),
|
|
|
|
value: _isBrowserShowDate,
|
|
|
|
onChanged: (value) => _onBrowserShowDateChanged(value),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _onBrowserShowDateChanged(bool value) async {
|
|
|
|
final oldValue = _isBrowserShowDate;
|
|
|
|
setState(() {
|
|
|
|
_isBrowserShowDate = value;
|
|
|
|
});
|
2021-10-27 22:40:54 +02:00
|
|
|
if (!await Pref().setAlbumBrowserShowDate(value)) {
|
2021-10-04 15:53:03 +02:00
|
|
|
_log.severe("[_onBrowserShowDateChanged] Failed writing pref");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
setState(() {
|
|
|
|
_isBrowserShowDate = oldValue;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
late bool _isBrowserShowDate;
|
|
|
|
|
|
|
|
static final _log = Logger("widget.settings._AlbumSettingsState");
|
|
|
|
}
|
|
|
|
|
2021-09-02 10:33:35 +02:00
|
|
|
class _ThemeSettings extends StatefulWidget {
|
|
|
|
@override
|
|
|
|
createState() => _ThemeSettingsState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ThemeSettingsState extends State<_ThemeSettings> {
|
|
|
|
@override
|
|
|
|
initState() {
|
|
|
|
super.initState();
|
2021-10-27 22:40:54 +02:00
|
|
|
_isFollowSystemTheme = Pref().isFollowSystemThemeOr(false);
|
|
|
|
_isUseBlackInDarkTheme = Pref().isUseBlackInDarkThemeOr(false);
|
2021-09-02 10:33:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
build(BuildContext context) {
|
|
|
|
return AppTheme(
|
|
|
|
child: Scaffold(
|
|
|
|
body: Builder(
|
|
|
|
builder: (context) => _buildContent(context),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildContent(BuildContext context) {
|
|
|
|
return CustomScrollView(
|
|
|
|
slivers: [
|
|
|
|
SliverAppBar(
|
|
|
|
pinned: true,
|
|
|
|
title: Text(L10n.global().settingsThemePageTitle),
|
|
|
|
),
|
|
|
|
SliverList(
|
|
|
|
delegate: SliverChildListDelegate(
|
|
|
|
[
|
|
|
|
if (platform_k.isAndroid && AndroidInfo().sdkInt >= 29)
|
|
|
|
SwitchListTile(
|
|
|
|
title: Text(L10n.global().settingsFollowSystemThemeTitle),
|
|
|
|
value: _isFollowSystemTheme,
|
|
|
|
onChanged: (value) => _onFollowSystemThemeChanged(value),
|
|
|
|
),
|
|
|
|
SwitchListTile(
|
|
|
|
title: Text(L10n.global().settingsUseBlackInDarkThemeTitle),
|
|
|
|
subtitle: Text(_isUseBlackInDarkTheme
|
|
|
|
? L10n.global().settingsUseBlackInDarkThemeTrueDescription
|
|
|
|
: L10n.global()
|
|
|
|
.settingsUseBlackInDarkThemeFalseDescription),
|
|
|
|
value: _isUseBlackInDarkTheme,
|
|
|
|
onChanged: (value) =>
|
|
|
|
_onUseBlackInDarkThemeChanged(context, value),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-08-30 18:57:45 +02:00
|
|
|
void _onFollowSystemThemeChanged(bool value) async {
|
|
|
|
final oldValue = _isFollowSystemTheme;
|
|
|
|
setState(() {
|
|
|
|
_isFollowSystemTheme = value;
|
|
|
|
});
|
2021-10-27 22:40:54 +02:00
|
|
|
if (await Pref().setFollowSystemTheme(value)) {
|
2021-08-30 18:57:45 +02:00
|
|
|
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
|
|
|
|
} else {
|
|
|
|
_log.severe("[_onFollowSystemThemeChanged] Failed writing pref");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
setState(() {
|
|
|
|
_isFollowSystemTheme = oldValue;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _onUseBlackInDarkThemeChanged(BuildContext context, bool value) async {
|
2021-08-30 15:58:37 +02:00
|
|
|
final oldValue = _isUseBlackInDarkTheme;
|
|
|
|
setState(() {
|
|
|
|
_isUseBlackInDarkTheme = value;
|
|
|
|
});
|
2021-10-27 22:40:54 +02:00
|
|
|
if (await Pref().setUseBlackInDarkTheme(value)) {
|
2021-08-30 18:57:45 +02:00
|
|
|
if (Theme.of(context).brightness == Brightness.dark) {
|
2021-08-30 15:58:37 +02:00
|
|
|
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_log.severe("[_onUseBlackInDarkThemeChanged] Failed writing pref");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
setState(() {
|
|
|
|
_isUseBlackInDarkTheme = oldValue;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-30 18:57:45 +02:00
|
|
|
late bool _isFollowSystemTheme;
|
2021-08-30 15:58:37 +02:00
|
|
|
late bool _isUseBlackInDarkTheme;
|
2021-04-10 06:28:12 +02:00
|
|
|
|
2021-09-02 10:33:35 +02:00
|
|
|
static final _log = Logger("widget.settings._ThemeSettingsState");
|
2021-04-10 06:28:12 +02:00
|
|
|
}
|
2021-10-19 22:11:35 +02:00
|
|
|
|
2021-10-25 16:51:04 +02:00
|
|
|
class _ExperimentalSettings extends StatefulWidget {
|
|
|
|
@override
|
|
|
|
createState() => _ExperimentalSettingsState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ExperimentalSettingsState extends State<_ExperimentalSettings> {
|
|
|
|
@override
|
|
|
|
initState() {
|
|
|
|
super.initState();
|
2021-10-27 22:40:54 +02:00
|
|
|
_isEnableSharedAlbum = Pref().isLabEnableSharedAlbumOr(false);
|
2021-10-25 16:51:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
build(BuildContext context) {
|
|
|
|
return AppTheme(
|
|
|
|
child: Scaffold(
|
|
|
|
body: Builder(
|
|
|
|
builder: (context) => _buildContent(context),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildContent(BuildContext context) {
|
|
|
|
return CustomScrollView(
|
|
|
|
slivers: [
|
|
|
|
SliverAppBar(
|
|
|
|
pinned: true,
|
|
|
|
title: Text(L10n.global().settingsExperimentalPageTitle),
|
|
|
|
),
|
|
|
|
SliverList(
|
|
|
|
delegate: SliverChildListDelegate(
|
|
|
|
[
|
2021-10-29 19:40:35 +02:00
|
|
|
if (_enabledExperiments.contains(_Experiment.sharedAlbum))
|
|
|
|
SwitchListTile(
|
|
|
|
title: const Text("Shared album"),
|
|
|
|
subtitle:
|
|
|
|
const Text("Share albums with users on the same server"),
|
|
|
|
value: _isEnableSharedAlbum,
|
|
|
|
onChanged: (value) => _onEnableSharedAlbumChanged(value),
|
|
|
|
),
|
2021-10-25 16:51:04 +02:00
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _onEnableSharedAlbumChanged(bool value) async {
|
|
|
|
final oldValue = _isEnableSharedAlbum;
|
|
|
|
setState(() {
|
|
|
|
_isEnableSharedAlbum = value;
|
|
|
|
});
|
2021-10-27 22:40:54 +02:00
|
|
|
if (!await Pref().setLabEnableSharedAlbum(value)) {
|
2021-10-25 16:51:04 +02:00
|
|
|
_log.severe("[_onEnableSharedAlbumChanged] Failed writing pref");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().writePreferenceFailureNotification),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
setState(() {
|
|
|
|
_isEnableSharedAlbum = oldValue;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
late bool _isEnableSharedAlbum;
|
|
|
|
|
|
|
|
static final _log = Logger("widget.settings._ExperimentalSettingsState");
|
|
|
|
}
|
|
|
|
|
2021-10-19 22:11:35 +02:00
|
|
|
Widget _buildCaption(BuildContext context, String label) {
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
|
|
|
child: Text(
|
|
|
|
label,
|
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.secondary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2021-10-29 19:40:35 +02:00
|
|
|
|
|
|
|
enum _Experiment {
|
|
|
|
sharedAlbum,
|
|
|
|
}
|
|
|
|
|
|
|
|
late final _enabledExperiments = [
|
|
|
|
if (kDebugMode) _Experiment.sharedAlbum,
|
|
|
|
];
|