forked from cwtch.im/cwtch-ui
split settings panes into seperate files
This commit is contained in:
parent
2abbf223b2
commit
04985ee7eb
|
@ -0,0 +1,197 @@
|
|||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import '../config.dart';
|
||||
import '../cwtch_icons_icons.dart';
|
||||
import '../main.dart';
|
||||
import '../settings.dart';
|
||||
import '../themes/opaque.dart';
|
||||
import 'globalsettingsview.dart';
|
||||
|
||||
class GlobalSettingsAboutView extends StatefulWidget {
|
||||
@override
|
||||
_GlobalSettingsAboutViewState createState() => _GlobalSettingsAboutViewState();
|
||||
}
|
||||
|
||||
class _GlobalSettingsAboutViewState extends State<GlobalSettingsAboutView> {
|
||||
|
||||
ScrollController settingsListScrollController = ScrollController();
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
var appIcon = Icon(Icons.info, color: settings
|
||||
.current()
|
||||
.mainTextColor);
|
||||
return Scrollbar(
|
||||
key: Key("AboutSettingsView"),
|
||||
trackVisibility: true,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
AboutListTile(
|
||||
icon: appIcon,
|
||||
applicationIcon: Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: Icon(CwtchIcons.cwtch_knott)),
|
||||
applicationName: "Cwtch UI",
|
||||
applicationLegalese:
|
||||
'\u{a9} 2021-2023 Open Privacy Research Society',
|
||||
aboutBoxChildren: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
24.0 + 10.0 + (appIcon.size ?? 24.0),
|
||||
16.0,
|
||||
0.0,
|
||||
0.0),
|
||||
// About has 24 padding (ln 389) and there appears to be another 10 of padding in the widget
|
||||
child: SelectableText(
|
||||
AppLocalizations.of(context)!
|
||||
.versionBuilddate
|
||||
.replaceAll(
|
||||
"%1", EnvironmentConfig.BUILD_VER)
|
||||
.replaceAll("%2",
|
||||
EnvironmentConfig.BUILD_DATE)),
|
||||
)
|
||||
]),
|
||||
SwitchListTile(
|
||||
// TODO: Translate, Remove, OR Hide Prior to Release
|
||||
title: Text("Show Performance Overlay"),
|
||||
subtitle:
|
||||
Text("Display an overlay graph of render time."),
|
||||
value: settings.profileMode,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
if (value) {
|
||||
settings.profileMode = value;
|
||||
} else {
|
||||
settings.profileMode = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.bar_chart,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible:
|
||||
EnvironmentConfig.BUILD_VER == dev_version &&
|
||||
!Platform.isAndroid,
|
||||
child: SwitchListTile(
|
||||
title: Text("Show Semantic Debugger"),
|
||||
subtitle:
|
||||
Text("Show Accessibility Debugging View"),
|
||||
value: settings.useSemanticDebugger,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.useSemanticDebugger = value;
|
||||
} else {
|
||||
settings.useSemanticDebugger = value;
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.settings_accessibility,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
)),
|
||||
Visibility(
|
||||
visible: EnvironmentConfig.BUILD_VER == dev_version &&
|
||||
!Platform.isAndroid,
|
||||
child: FutureBuilder(
|
||||
future:
|
||||
EnvironmentConfig.BUILD_VER != dev_version ||
|
||||
Platform.isAndroid
|
||||
? null
|
||||
: Provider
|
||||
.of<FlwtchState>(context)
|
||||
.cwtch
|
||||
.GetDebugInfo(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return Column(
|
||||
children: [
|
||||
Text("libCwtch Debug Info: " +
|
||||
snapshot.data.toString()),
|
||||
Text("Message Cache Size (Mb): " +
|
||||
(Provider
|
||||
.of<FlwtchState>(context)
|
||||
.profs
|
||||
.cacheMemUsage() /
|
||||
(1024 * 1024))
|
||||
.toString())
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: EnvironmentConfig.BUILD_VER == dev_version,
|
||||
child: FutureBuilder(
|
||||
future: Provider
|
||||
.of<FlwtchState>(context)
|
||||
.cwtch
|
||||
.PlatformChannelInfo(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
HashMap<String, String> data = snapshot.data
|
||||
as HashMap<String, String>;
|
||||
return getPlatformInfo(settings, data);
|
||||
}
|
||||
return Container();
|
||||
}))
|
||||
]))));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getPlatformInfo(settings, HashMap<String, String> platformChannelInfo) {
|
||||
var sortedKeys = platformChannelInfo.keys.toList();
|
||||
sortedKeys.sort();
|
||||
var widgets = List<Widget>.empty(growable: true);
|
||||
sortedKeys.forEach((element) {
|
||||
widgets.add(ListTile(
|
||||
leading: Icon(Icons.android, color: settings.current().mainTextColor),
|
||||
title: Text(element),
|
||||
subtitle: Text(platformChannelInfo[element]!),
|
||||
));
|
||||
});
|
||||
return Column(
|
||||
children: widgets,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: deprecated ?
|
||||
/// Construct a version string from Package Info
|
||||
String constructVersionString(PackageInfo pinfo) {
|
||||
if (pinfo == null) {
|
||||
return "";
|
||||
}
|
||||
return pinfo.version + "." + pinfo.buildNumber;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,420 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import '../config.dart';
|
||||
import '../cwtch_icons_icons.dart';
|
||||
import '../settings.dart';
|
||||
import '../themes/cwtch.dart';
|
||||
import '../themes/opaque.dart';
|
||||
import 'globalsettingsview.dart';
|
||||
|
||||
class GlobalSettingsAppearanceView extends StatefulWidget {
|
||||
@override
|
||||
_GlobalSettingsAppearanceViewState createState() => _GlobalSettingsAppearanceViewState();
|
||||
}
|
||||
|
||||
class _GlobalSettingsAppearanceViewState extends State<GlobalSettingsAppearanceView> {
|
||||
|
||||
ScrollController settingsListScrollController = ScrollController();
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return Scrollbar(
|
||||
key: Key("AppearanceSettingsView"),
|
||||
trackVisibility: true,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.settingLanguage),
|
||||
leading: Icon(CwtchIcons.change_language,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: DropdownButton(
|
||||
key: Key("languagelist"),
|
||||
isExpanded: true,
|
||||
value: Provider
|
||||
.of<Settings>(context)
|
||||
.locale
|
||||
.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
EnvironmentConfig.debugLog(
|
||||
"setting language: $newValue");
|
||||
settings.switchLocaleByCode(newValue!);
|
||||
saveSettings(context);
|
||||
});
|
||||
},
|
||||
items: AppLocalizations.supportedLocales
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(Locale value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(
|
||||
key: Key("dropdownLanguage" +
|
||||
value.languageCode),
|
||||
getLanguageFull(
|
||||
context,
|
||||
value.languageCode,
|
||||
value.countryCode),
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}).toList()))),
|
||||
SwitchListTile(
|
||||
title:
|
||||
Text(AppLocalizations.of(context)!.settingTheme),
|
||||
value: settings
|
||||
.current()
|
||||
.mode == mode_light,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.setTheme(
|
||||
settings.theme.theme, mode_light);
|
||||
} else {
|
||||
settings.setTheme(
|
||||
settings.theme.theme, mode_dark);
|
||||
}
|
||||
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.change_theme,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.themeColorLabel),
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: DropdownButton<String>(
|
||||
key: Key("DropdownTheme"),
|
||||
isExpanded: true,
|
||||
value: Provider
|
||||
.of<Settings>(context)
|
||||
.theme
|
||||
.theme,
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
settings.setTheme(
|
||||
newValue!, settings.theme.mode);
|
||||
saveSettings(context);
|
||||
});
|
||||
},
|
||||
items: themes.keys
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(String themeId) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: themeId,
|
||||
child: Text(
|
||||
getThemeName(context, themeId),
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)), //"ddi_$themeId", key: Key("ddi_$themeId")),
|
||||
);
|
||||
}).toList())),
|
||||
leading: Icon(Icons.palette,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingUIColumnPortrait),
|
||||
leading: Icon(Icons.table_chart,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: DropdownButton(
|
||||
isExpanded: true,
|
||||
value: settings.uiColumnModePortrait
|
||||
.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
settings.uiColumnModePortrait =
|
||||
Settings.uiColumnModeFromString(
|
||||
newValue!);
|
||||
saveSettings(context);
|
||||
},
|
||||
items: Settings.uiColumnModeOptions(false)
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(DualpaneMode value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(
|
||||
Settings.uiColumnModeToString(
|
||||
value, context),
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList()))),
|
||||
ListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!
|
||||
.settingUIColumnLandscape,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
softWrap: true,
|
||||
),
|
||||
leading: Icon(Icons.stay_primary_landscape,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: Container(
|
||||
width:
|
||||
MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: DropdownButton(
|
||||
isExpanded: true,
|
||||
value: settings.uiColumnModeLandscape
|
||||
.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
settings.uiColumnModeLandscape =
|
||||
Settings.uiColumnModeFromString(
|
||||
newValue!);
|
||||
saveSettings(context);
|
||||
},
|
||||
items:
|
||||
Settings.uiColumnModeOptions(true)
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(DualpaneMode value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(
|
||||
Settings.uiColumnModeToString(
|
||||
value, context),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList())))),
|
||||
ListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.defaultScalingText),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.fontScalingDescription),
|
||||
trailing: Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width / 4,
|
||||
child: Slider(
|
||||
onChanged: (double value) {
|
||||
settings.fontScaling = value;
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
EnvironmentConfig.debugLog(
|
||||
"Font Scaling: $value");
|
||||
},
|
||||
min: 0.5,
|
||||
divisions: 12,
|
||||
max: 2.0,
|
||||
label: '${settings.fontScaling * 100}%',
|
||||
activeColor:
|
||||
settings
|
||||
.current()
|
||||
.defaultButtonColor,
|
||||
thumbColor: settings
|
||||
.current()
|
||||
.mainTextColor,
|
||||
overlayColor: MaterialStateProperty.all(
|
||||
settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
inactiveColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
value: settings.fontScaling)),
|
||||
leading: Icon(Icons.format_size,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.streamerModeLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionStreamerMode),
|
||||
value: settings.streamerMode,
|
||||
onChanged: (bool value) {
|
||||
settings.setStreamerMode(value);
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.streamer_bunnymask,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.formattingExperiment),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.messageFormattingDescription),
|
||||
value: settings
|
||||
.isExperimentEnabled(FormattingExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(FormattingExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(FormattingExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.text_fields,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
]))));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// A slightly verbose way to extract the full language name from
|
||||
/// an individual language code. There might be a more efficient way of doing this.
|
||||
String getLanguageFull(context, String languageCode, String? countryCode) {
|
||||
if (languageCode == "en") {
|
||||
return AppLocalizations.of(context)!.localeEn;
|
||||
}
|
||||
if (languageCode == "es") {
|
||||
return AppLocalizations.of(context)!.localeEs;
|
||||
}
|
||||
if (languageCode == "fr") {
|
||||
return AppLocalizations.of(context)!.localeFr;
|
||||
}
|
||||
if (languageCode == "pt" && countryCode == "BR") {
|
||||
return AppLocalizations.of(context)!.localePtBr;
|
||||
}
|
||||
if (languageCode == "pt") {
|
||||
return AppLocalizations.of(context)!.localePt;
|
||||
}
|
||||
if (languageCode == "de") {
|
||||
return AppLocalizations.of(context)!.localeDe;
|
||||
}
|
||||
if (languageCode == "el") {
|
||||
return AppLocalizations.of(context)!.localeEl;
|
||||
}
|
||||
if (languageCode == "it") {
|
||||
return AppLocalizations.of(context)!.localeIt;
|
||||
}
|
||||
if (languageCode == "no") {
|
||||
return AppLocalizations.of(context)!.localeNo;
|
||||
}
|
||||
if (languageCode == "pl") {
|
||||
return AppLocalizations.of(context)!.localePl;
|
||||
}
|
||||
if (languageCode == "lb") {
|
||||
return AppLocalizations.of(context)!.localeLb;
|
||||
}
|
||||
if (languageCode == "ru") {
|
||||
return AppLocalizations.of(context)!.localeRU;
|
||||
}
|
||||
if (languageCode == "ro") {
|
||||
return AppLocalizations.of(context)!.localeRo;
|
||||
}
|
||||
if (languageCode == "cy") {
|
||||
return AppLocalizations.of(context)!.localeCy;
|
||||
}
|
||||
if (languageCode == "da") {
|
||||
return AppLocalizations.of(context)!.localeDa;
|
||||
}
|
||||
if (languageCode == "tr") {
|
||||
return AppLocalizations.of(context)!.localeTr;
|
||||
}
|
||||
if (languageCode == "nl") {
|
||||
return AppLocalizations.of(context)!.localeNl;
|
||||
}
|
||||
if (languageCode == "sk") {
|
||||
return AppLocalizations.of(context)!.localeSk;
|
||||
}
|
||||
if (languageCode == "ko") {
|
||||
return AppLocalizations.of(context)!.localeKo;
|
||||
}
|
||||
if (languageCode == "ja") {
|
||||
return AppLocalizations.of(context)!.localeJa;
|
||||
}
|
||||
if (languageCode == "sv") {
|
||||
return AppLocalizations.of(context)!.localeSv;
|
||||
}
|
||||
if (languageCode == "sw") {
|
||||
return AppLocalizations.of(context)!.localeSw;
|
||||
}
|
||||
if (languageCode == "uk") {
|
||||
return AppLocalizations.of(context)!.localeUk;
|
||||
}
|
||||
if (languageCode == "uz") {
|
||||
return AppLocalizations.of(context)!.localeUzbek;
|
||||
}
|
||||
return languageCode;
|
||||
}
|
||||
|
||||
/// Since we don't seem to able to dynamically pull translations, this function maps themes to their names
|
||||
String getThemeName(context, String theme) {
|
||||
switch (theme) {
|
||||
case cwtch_theme:
|
||||
return AppLocalizations.of(context)!.themeNameCwtch;
|
||||
case "ghost":
|
||||
return AppLocalizations.of(context)!.themeNameGhost;
|
||||
case "mermaid":
|
||||
return AppLocalizations.of(context)!.themeNameMermaid;
|
||||
case "midnight":
|
||||
return AppLocalizations.of(context)!.themeNameMidnight;
|
||||
case "neon1":
|
||||
return AppLocalizations.of(context)!.themeNameNeon1;
|
||||
case "neon2":
|
||||
return AppLocalizations.of(context)!.themeNameNeon2;
|
||||
case "pumpkin":
|
||||
return AppLocalizations.of(context)!.themeNamePumpkin;
|
||||
case "vampire":
|
||||
return AppLocalizations.of(context)!.themeNameVampire;
|
||||
case "witch":
|
||||
return AppLocalizations.of(context)!.themeNameWitch;
|
||||
case "juniper":
|
||||
return "Juniper"; // Juniper is a noun, and doesn't get subject to translation...
|
||||
}
|
||||
return theme;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import '../config.dart';
|
||||
import '../cwtch_icons_icons.dart';
|
||||
import '../settings.dart';
|
||||
import '../themes/opaque.dart';
|
||||
import 'globalsettingsview.dart';
|
||||
|
||||
class GlobalSettingsBehaviourView extends StatefulWidget {
|
||||
@override
|
||||
_GlobalSettingsBehaviourViewState createState() => _GlobalSettingsBehaviourViewState();
|
||||
}
|
||||
|
||||
class _GlobalSettingsBehaviourViewState extends State<GlobalSettingsBehaviourView> {
|
||||
static const androidSettingsChannel = const MethodChannel('androidSettings');
|
||||
static const androidSettingsChangeChannel = const MethodChannel('androidSettingsChanged');
|
||||
|
||||
|
||||
ScrollController settingsListScrollController = ScrollController();
|
||||
bool powerExempt = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
androidSettingsChangeChannel.setMethodCallHandler(handleSettingsChanged);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
isBatteryExempt().then((value) => setState(() {
|
||||
powerExempt = value;
|
||||
}));
|
||||
} else {
|
||||
powerExempt = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handler on method channel for MainActivity/onActivityResult to report the user choice when we ask for power exemption
|
||||
Future<void> handleSettingsChanged(MethodCall call) async {
|
||||
if (call.method == "powerExemptionChange") {
|
||||
if (call.arguments) {
|
||||
setState(() {
|
||||
powerExempt = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
//* Android Only Requests
|
||||
|
||||
Future<bool> isBatteryExempt() async {
|
||||
return await androidSettingsChannel.invokeMethod('isBatteryExempt', {}) ??
|
||||
false;
|
||||
}
|
||||
|
||||
Future<void> requestBatteryExemption() async {
|
||||
await androidSettingsChannel.invokeMethod('requestBatteryExemption', {});
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
//* End Android Only Requests
|
||||
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return Scrollbar(
|
||||
key: Key("BehaviourSettingsView"),
|
||||
trackVisibility: true,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
Visibility(
|
||||
visible: Platform.isAndroid,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingAndroidPowerExemption),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.settingAndroidPowerExemptionDescription),
|
||||
value: powerExempt,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
requestBatteryExemption();
|
||||
} else {
|
||||
// We can't ask for it to be turned off, show an informational popup
|
||||
showBatteryDialog(context);
|
||||
}
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.power,
|
||||
color: settings.current().mainTextColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.notificationPolicySettingLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.notificationPolicySettingDescription),
|
||||
trailing: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: DropdownButton(
|
||||
isExpanded: true,
|
||||
value: settings.notificationPolicy,
|
||||
onChanged: (NotificationPolicy? newValue) {
|
||||
settings.notificationPolicy = newValue!;
|
||||
saveSettings(context);
|
||||
},
|
||||
items: NotificationPolicy.values.map<
|
||||
DropdownMenuItem<NotificationPolicy>>(
|
||||
(NotificationPolicy value) {
|
||||
return DropdownMenuItem<NotificationPolicy>(
|
||||
value: value,
|
||||
child: Text(
|
||||
Settings.notificationPolicyToString(
|
||||
value, context),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList())),
|
||||
leading: Icon(CwtchIcons.chat_bubble_empty_24px,
|
||||
color: settings.current().mainTextColor),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.notificationContentSettingLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.notificationContentSettingDescription),
|
||||
trailing: Container(
|
||||
width: MediaQuery.of(context).size.width / 4,
|
||||
child: DropdownButton(
|
||||
isExpanded: true,
|
||||
value: settings.notificationContent,
|
||||
onChanged: (NotificationContent? newValue) {
|
||||
settings.notificationContent = newValue!;
|
||||
saveSettings(context);
|
||||
},
|
||||
items: NotificationContent.values.map<
|
||||
DropdownMenuItem<
|
||||
NotificationContent>>(
|
||||
(NotificationContent value) {
|
||||
return DropdownMenuItem<
|
||||
NotificationContent>(
|
||||
value: value,
|
||||
child: Text(
|
||||
Settings.notificationContentToString(
|
||||
value, context),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle)),
|
||||
);
|
||||
}).toList())),
|
||||
leading: Icon(CwtchIcons.chat_bubble_empty_24px,
|
||||
color: settings.current().mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.blockUnknownLabel),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionBlockUnknownConnections),
|
||||
value: settings.blockUnknownConnections,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.forbidUnknownConnections();
|
||||
} else {
|
||||
settings.allowUnknownConnections();
|
||||
}
|
||||
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.block_unknown,
|
||||
color: settings.current().mainTextColor),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.defaultPreserveHistorySetting),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.preserveHistorySettingDescription),
|
||||
value: settings.preserveHistoryByDefault,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.setPreserveHistoryDefault();
|
||||
} else {
|
||||
settings.setDeleteHistoryDefault();
|
||||
}
|
||||
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.peer_history,
|
||||
color: settings.current().mainTextColor),
|
||||
),
|
||||
]))));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showBatteryDialog(BuildContext context) {
|
||||
Widget okButton = ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.okButton),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
|
||||
// set up the AlertDialog
|
||||
AlertDialog alert = AlertDialog(
|
||||
title:
|
||||
Text(AppLocalizations.of(context)!.settingsAndroidPowerReenablePopup),
|
||||
actions: [
|
||||
okButton,
|
||||
],
|
||||
);
|
||||
|
||||
// show the dialog
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,403 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import '../config.dart';
|
||||
import '../cwtch_icons_icons.dart';
|
||||
import '../main.dart';
|
||||
import '../models/servers.dart';
|
||||
import '../settings.dart';
|
||||
import '../themes/opaque.dart';
|
||||
import '../widgets/folderpicker.dart';
|
||||
import 'globalsettingsview.dart';
|
||||
|
||||
class GlobalSettingsExperimentsView extends StatefulWidget {
|
||||
@override
|
||||
_GlobalSettingsExperimentsViewState createState() => _GlobalSettingsExperimentsViewState();
|
||||
}
|
||||
|
||||
class _GlobalSettingsExperimentsViewState extends State<GlobalSettingsExperimentsView> {
|
||||
|
||||
ScrollController settingsListScrollController = ScrollController();
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (ccontext, settings, child) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints viewportConstraints) {
|
||||
return Scrollbar(
|
||||
key: Key("ExperimentsSettingsView"),
|
||||
trackVisibility: true,
|
||||
controller: settingsListScrollController,
|
||||
child: SingleChildScrollView(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
controller: settingsListScrollController,
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Column(children: [
|
||||
SwitchListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.experimentsEnabled),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionExperiments),
|
||||
value: settings.experimentsEnabled,
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiments();
|
||||
} else {
|
||||
settings.disableExperiments();
|
||||
}
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.enable_experiments,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: Column(
|
||||
children: [
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.enableGroups),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionExperimentsGroups),
|
||||
value: settings.isExperimentEnabled(
|
||||
TapirGroupsExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(
|
||||
TapirGroupsExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
TapirGroupsExperiment);
|
||||
}
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.enable_groups,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible:
|
||||
!Platform.isAndroid && !Platform.isIOS,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingServers),
|
||||
subtitle: Provider
|
||||
.of<FlwtchState>(
|
||||
context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsServersCompiled()
|
||||
? Text(AppLocalizations.of(context)!
|
||||
.settingServersDescription)
|
||||
: Text(
|
||||
"This version of Cwtch has been compiled without support for the server hosting experiment."),
|
||||
value: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsServersCompiled() &&
|
||||
settings.isExperimentEnabled(
|
||||
ServerManagementExperiment),
|
||||
onChanged: Provider
|
||||
.of<FlwtchState>(
|
||||
context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsServersCompiled()
|
||||
? (bool value) {
|
||||
Provider.of<ServerListState>(
|
||||
context,
|
||||
listen: false)
|
||||
.clear();
|
||||
if (value) {
|
||||
settings.enableExperiment(
|
||||
ServerManagementExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
ServerManagementExperiment);
|
||||
}
|
||||
// Save Settings...
|
||||
saveSettings(context);
|
||||
}
|
||||
: null,
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor: settings
|
||||
.theme.defaultButtonDisabledColor,
|
||||
inactiveThumbColor: settings
|
||||
.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.dns_24px),
|
||||
)),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingFileSharing),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.descriptionFileSharing),
|
||||
value: settings.isExperimentEnabled(
|
||||
FileSharingExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
if (checkDownloadDirectory(
|
||||
context, settings)) {
|
||||
settings.enableExperiment(
|
||||
FileSharingExperiment);
|
||||
} else {
|
||||
settings.enableExperiment(
|
||||
FileSharingExperiment);
|
||||
settings.disableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
}
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
FileSharingExperiment);
|
||||
settings.disableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(CwtchIcons.attached_file_3,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: settings.isExperimentEnabled(
|
||||
FileSharingExperiment),
|
||||
child: Column(children: [
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.settingImagePreviews),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!
|
||||
.settingImagePreviewsDescription),
|
||||
value: settings.isExperimentEnabled(
|
||||
ImagePreviewsExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
if (checkDownloadDirectory(
|
||||
context, settings)) {
|
||||
settings.enableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
}
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
ImagePreviewsExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor: settings
|
||||
.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor: settings
|
||||
.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.photo,
|
||||
color:
|
||||
settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: settings.isExperimentEnabled(
|
||||
ImagePreviewsExperiment) &&
|
||||
!Platform.isAndroid,
|
||||
child: CwtchFolderPicker(
|
||||
testKey: Key("DownloadFolderPicker"),
|
||||
label: AppLocalizations.of(context)!
|
||||
.settingDownloadFolder,
|
||||
initialValue: settings.downloadPath,
|
||||
textStyle: settings.scaleFonts(
|
||||
defaultDropDownMenuItemTextStyle),
|
||||
description: AppLocalizations.of(
|
||||
context)!
|
||||
.fileSharingSettingsDownloadFolderDescription,
|
||||
tooltip: AppLocalizations.of(context)!
|
||||
.fileSharingSettingsDownloadFolderTooltip,
|
||||
onSave: (newVal) {
|
||||
settings.downloadPath = newVal;
|
||||
saveSettings(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.blodeuweddExperimentEnable),
|
||||
subtitle: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsBlodeuweddSupported()
|
||||
? Text(AppLocalizations.of(context)!
|
||||
.blodeuweddDescription)
|
||||
: Text(AppLocalizations.of(context)!
|
||||
.blodeuweddNotSupported),
|
||||
value: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsBlodeuweddSupported() &&
|
||||
settings.isExperimentEnabled(
|
||||
BlodeuweddExperiment),
|
||||
onChanged: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsBlodeuweddSupported()
|
||||
? (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(
|
||||
BlodeuweddExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
BlodeuweddExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
}
|
||||
: null,
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
inactiveThumbColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.assistant,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
),
|
||||
Visibility(
|
||||
visible: Provider
|
||||
.of<FlwtchState>(context,
|
||||
listen: false)
|
||||
.cwtch
|
||||
.IsBlodeuweddSupported() &&
|
||||
settings.isExperimentEnabled(
|
||||
BlodeuweddExperiment),
|
||||
child: CwtchFolderPicker(
|
||||
testKey: Key("DownloadFolderPicker"),
|
||||
label: AppLocalizations.of(context)!
|
||||
.settingDownloadFolder,
|
||||
initialValue: settings.blodeuweddPath,
|
||||
description: AppLocalizations.of(context)!
|
||||
.blodeuweddPath,
|
||||
tooltip: AppLocalizations.of(context)!
|
||||
.blodeuweddPath,
|
||||
onSave: (newVal) {
|
||||
settings.blodeuweddPath = newVal;
|
||||
saveSettings(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.enableExperimentClickableLinks),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.experimentClickableLinksDescription),
|
||||
value: settings.isExperimentEnabled(
|
||||
ClickableLinksExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(
|
||||
ClickableLinksExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(
|
||||
ClickableLinksExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.link,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
)),
|
||||
Visibility(
|
||||
visible: settings.experimentsEnabled,
|
||||
child: SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!
|
||||
.enableExperimentQRCode),
|
||||
subtitle: Text(AppLocalizations.of(context)!
|
||||
.experimentQRCodeDescription),
|
||||
value: settings
|
||||
.isExperimentEnabled(QRCodeExperiment),
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.enableExperiment(QRCodeExperiment);
|
||||
} else {
|
||||
settings.disableExperiment(QRCodeExperiment);
|
||||
}
|
||||
saveSettings(context);
|
||||
},
|
||||
activeTrackColor:
|
||||
settings.theme.defaultButtonActiveColor,
|
||||
inactiveTrackColor:
|
||||
settings.theme.defaultButtonDisabledColor,
|
||||
secondary: Icon(Icons.qr_code,
|
||||
color: settings
|
||||
.current()
|
||||
.mainTextColor),
|
||||
)),
|
||||
]))));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool checkDownloadDirectory(context, settings) {
|
||||
bool showError = false;
|
||||
if (settings.downloadPath != "") {
|
||||
} else {
|
||||
// check if the default download path exists
|
||||
var path = Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.defaultDownloadPath();
|
||||
if (path != null) {
|
||||
settings.downloadPath = path;
|
||||
} else {
|
||||
showError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!showError && Directory(settings.downloadPath).existsSync()) {
|
||||
return true;
|
||||
} else {
|
||||
final snackBar = SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.errorDownloadDirectoryDoesNotExist),
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue