privacy: reviewed policy, welcome & settings pages for app inventory access
This commit is contained in:
parent
b5c25656b8
commit
941288b5fc
15 changed files with 166 additions and 145 deletions
|
@ -1,25 +1,25 @@
|
|||
# Terms of Service
|
||||
## Terms of Service
|
||||
|
||||
Aves is an open-source gallery and metadata explorer app allowing you to access and manage your local photos.
|
||||
“Aves Gallery” is an open-source gallery and metadata explorer app allowing you to access and manage your local photos and videos.
|
||||
|
||||
You must use the app for legal, authorized and acceptable purposes.
|
||||
|
||||
# Disclaimer
|
||||
## Disclaimer
|
||||
|
||||
This app is released “as-is”, without any warranty, responsibility or liability. Use of the app is at your own risk.
|
||||
The app is released “as-is”, without any warranty, responsibility or liability. Use of the app is at your own risk.
|
||||
|
||||
# Privacy policy
|
||||
## Privacy policy
|
||||
|
||||
Aves does not collect any personal data in its standard use. We never have access to your photos and videos. This also means that we cannot get them back for you if you delete them without backing them up.
|
||||
The app does not collect any personal data. We never have access to your photos and videos. This also means that we cannot get them back for you if you delete them without backing them up.
|
||||
|
||||
In the “Play” edition of Aves, __with the user's consent, anonymous data is collected to improve the app.__ We use Firebase Crashlytics, and the anonymous data are stored on their servers. Please note that those are anonymous data, there is absolutely nothing personal about those data.
|
||||
__Optionally, with your consent, the app accesses the inventory of installed apps__ to improve album display.
|
||||
|
||||
__Optionally, with your consent, the app collects anonymous error and diagnostic data__ to improve the app quality. We use Firebase Crashlytics, and the anonymous data are stored on their servers. Please note that those are anonymous data, there is absolutely nothing personal about those data.
|
||||
|
||||
## Contact
|
||||
|
||||
[gallery.aves@gmail.com](mailto:gallery.aves@gmail.com)
|
||||
Developer: Thibault Deckers
|
||||
|
||||
## Links
|
||||
Email: [gallery.aves@gmail.com](mailto:gallery.aves@gmail.com)
|
||||
|
||||
[Sources](https://github.com/deckerst/aves)
|
||||
|
||||
[License](https://github.com/deckerst/aves/blob/main/LICENSE)
|
||||
Website: [https://github.com/deckerst/aves](https://github.com/deckerst/aves)
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
"@appName": {},
|
||||
"welcomeMessage": "Welcome to Aves",
|
||||
"@welcomeMessage": {},
|
||||
"welcomeCrashReportToggle": "Allow anonymous error reporting (optional)",
|
||||
"@welcomeCrashReportToggle": {},
|
||||
"welcomeOptional": "Optional",
|
||||
"welcomeTermsToggle": "I agree to the terms and conditions",
|
||||
"@welcomeTermsToggle": {},
|
||||
"itemCount": "{count, plural, =1{1 item} other{{count} items}}",
|
||||
|
@ -850,8 +849,12 @@
|
|||
|
||||
"settingsSectionPrivacy": "Privacy",
|
||||
"@settingsSectionPrivacy": {},
|
||||
"settingsEnableErrorReporting": "Allow anonymous error reporting",
|
||||
"@settingsEnableErrorReporting": {},
|
||||
"settingsAllowInstalledAppAccess": "Allow access to app inventory",
|
||||
"@settingsAllowInstalledAppAccess": {},
|
||||
"settingsAllowInstalledAppAccessSubtitle": "Used to improve album display",
|
||||
"@settingsAllowInstalledAppAccessSubtitle": {},
|
||||
"settingsAllowErrorReporting": "Allow anonymous error reporting",
|
||||
"@settingsAllowErrorReporting": {},
|
||||
"settingsSaveSearchHistory": "Save search history",
|
||||
"@settingsSaveSearchHistory": {},
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"appName": "아베스",
|
||||
"welcomeMessage": "아베스 사용을 환영합니다",
|
||||
"welcomeCrashReportToggle": "오류 보고서를 보내는 것에 동의합니다 (선택)",
|
||||
"welcomeOptional": "선택",
|
||||
"welcomeTermsToggle": "이용약관에 동의합니다",
|
||||
"itemCount": "{count, plural, other{{count}개}}",
|
||||
|
||||
|
@ -393,7 +393,7 @@
|
|||
"settingsSubtitleThemeTextAlignmentRight": "오른쪽",
|
||||
|
||||
"settingsSectionPrivacy": "개인정보 보호",
|
||||
"settingsEnableErrorReporting": "오류 보고서 보내기",
|
||||
"settingsAllowErrorReporting": "오류 보고서 보내기",
|
||||
"settingsSaveSearchHistory": "검색기록",
|
||||
|
||||
"settingsHiddenFiltersTile": "숨겨진 필터",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"appName": "Aves",
|
||||
"welcomeMessage": "Добро пожаловать в Aves",
|
||||
"welcomeCrashReportToggle": "Разрешить анонимную отправку ошибок (опционально)",
|
||||
"welcomeOptional": "Опционально",
|
||||
"welcomeTermsToggle": "Я согласен с условиями и положениями",
|
||||
"itemCount": "{count, plural, =1{1 объект} few{{count} объекта} other{{count} объектов}}",
|
||||
|
||||
|
@ -392,7 +392,7 @@
|
|||
"settingsSubtitleThemeTextAlignmentRight": "По правой стороне",
|
||||
|
||||
"settingsSectionPrivacy": "Конфиденциальность",
|
||||
"settingsEnableErrorReporting": "Разрешить анонимную отправку логов",
|
||||
"settingsAllowErrorReporting": "Разрешить анонимную отправку логов",
|
||||
"settingsSaveSearchHistory": "Сохранять историю поиска",
|
||||
|
||||
"settingsHiddenFiltersTile": "Скрытые фильтры",
|
||||
|
|
|
@ -14,7 +14,9 @@ class SettingsDefaults {
|
|||
// app
|
||||
static const hasAcceptedTerms = false;
|
||||
static const canUseAnalysisService = true;
|
||||
static const isErrorReportingEnabled = false;
|
||||
// TODO TLAD currently opt-out for transition (v1.5.4 -> vNext), should make it opt-in for vNext+1
|
||||
static const isInstalledAppAccessAllowed = true;
|
||||
static const isErrorReportingAllowed = false;
|
||||
static const mustBackTwiceToExit = true;
|
||||
static const keepScreenOn = KeepScreenOn.viewerOnly;
|
||||
static const homePage = HomePageSetting.collection;
|
||||
|
|
|
@ -41,7 +41,8 @@ class Settings extends ChangeNotifier {
|
|||
// app
|
||||
static const hasAcceptedTermsKey = 'has_accepted_terms';
|
||||
static const canUseAnalysisServiceKey = 'can_use_analysis_service';
|
||||
static const isErrorReportingEnabledKey = 'is_crashlytics_enabled';
|
||||
static const isInstalledAppAccessAllowedKey = 'is_installed_app_access_allowed';
|
||||
static const isErrorReportingAllowedKey = 'is_crashlytics_enabled';
|
||||
static const localeKey = 'locale';
|
||||
static const mustBackTwiceToExitKey = 'must_back_twice_to_exit';
|
||||
static const keepScreenOnKey = 'keep_screen_on';
|
||||
|
@ -174,9 +175,13 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
set canUseAnalysisService(bool newValue) => setAndNotify(canUseAnalysisServiceKey, newValue);
|
||||
|
||||
bool get isErrorReportingEnabled => getBoolOrDefault(isErrorReportingEnabledKey, SettingsDefaults.isErrorReportingEnabled);
|
||||
bool get isInstalledAppAccessAllowed => getBoolOrDefault(isInstalledAppAccessAllowedKey, SettingsDefaults.isInstalledAppAccessAllowed);
|
||||
|
||||
set isErrorReportingEnabled(bool newValue) => setAndNotify(isErrorReportingEnabledKey, newValue);
|
||||
set isInstalledAppAccessAllowed(bool newValue) => setAndNotify(isInstalledAppAccessAllowedKey, newValue);
|
||||
|
||||
bool get isErrorReportingAllowed => getBoolOrDefault(isErrorReportingAllowedKey, SettingsDefaults.isErrorReportingAllowed);
|
||||
|
||||
set isErrorReportingAllowed(bool newValue) => setAndNotify(isErrorReportingAllowedKey, newValue);
|
||||
|
||||
static const localeSeparator = '-';
|
||||
|
||||
|
@ -568,7 +573,8 @@ class Settings extends ChangeNotifier {
|
|||
debugPrint('failed to import key=$key, value=$value is not a double');
|
||||
}
|
||||
break;
|
||||
case isErrorReportingEnabledKey:
|
||||
case isInstalledAppAccessAllowedKey:
|
||||
case isErrorReportingAllowedKey:
|
||||
case mustBackTwiceToExitKey:
|
||||
case showThumbnailLocationKey:
|
||||
case showThumbnailMotionPhotoKey:
|
||||
|
|
|
@ -39,12 +39,19 @@ class AndroidFileUtils {
|
|||
|
||||
Future<void> initAppNames() async {
|
||||
if (_packages.isEmpty) {
|
||||
debugPrint('Access installed app inventory');
|
||||
_packages = await androidAppService.getPackages();
|
||||
_potentialAppDirs = _launcherPackages.expand((package) => package.potentialDirs).toList();
|
||||
areAppNamesReadyNotifier.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> resetAppNames() async {
|
||||
_packages.clear();
|
||||
_potentialAppDirs.clear();
|
||||
areAppNamesReadyNotifier.value = false;
|
||||
}
|
||||
|
||||
bool isCameraPath(String path) => path.startsWith(dcimPath) && (path.endsWith('${separator}Camera') || path.endsWith('${separator}100ANDRO'));
|
||||
|
||||
bool isScreenshotsPath(String path) => (path.startsWith(dcimPath) || path.startsWith(picturesPath)) && path.endsWith('${separator}Screenshots');
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:aves/services/common/services.dart';
|
|||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/utils/debouncer.dart';
|
||||
import 'package:aves/widgets/common/behaviour/route_tracker.dart';
|
||||
import 'package:aves/widgets/common/behaviour/routes.dart';
|
||||
|
@ -168,12 +169,23 @@ class _AvesAppState extends State<AvesApp> {
|
|||
);
|
||||
settings.keepScreenOn.apply();
|
||||
|
||||
// installed app access
|
||||
settings.updateStream.where((key) => key == Settings.isInstalledAppAccessAllowedKey).listen(
|
||||
(_) {
|
||||
if (settings.isInstalledAppAccessAllowed) {
|
||||
androidFileUtils.initAppNames();
|
||||
} else {
|
||||
androidFileUtils.resetAppNames();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// error reporting
|
||||
await reportService.init();
|
||||
settings.updateStream.where((key) => key == Settings.isErrorReportingEnabledKey).listen(
|
||||
(_) => reportService.setCollectionEnabled(settings.isErrorReportingEnabled),
|
||||
settings.updateStream.where((key) => key == Settings.isErrorReportingAllowedKey).listen(
|
||||
(_) => reportService.setCollectionEnabled(settings.isErrorReportingAllowed),
|
||||
);
|
||||
await reportService.setCollectionEnabled(settings.isErrorReportingEnabled);
|
||||
await reportService.setCollectionEnabled(settings.isErrorReportingAllowed);
|
||||
|
||||
FlutterError.onError = reportService.recordFlutterError;
|
||||
final now = DateTime.now();
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LabeledCheckbox extends StatefulWidget {
|
||||
final bool value;
|
||||
final ValueChanged<bool?> onChanged;
|
||||
final String text;
|
||||
|
||||
const LabeledCheckbox({
|
||||
Key? key,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
required this.text,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LabeledCheckboxState createState() => _LabeledCheckboxState();
|
||||
}
|
||||
|
||||
class _LabeledCheckboxState extends State<LabeledCheckbox> {
|
||||
late TapGestureRecognizer _tapRecognizer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tapRecognizer = TapGestureRecognizer()..onTap = () => widget.onChanged(!widget.value);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tapRecognizer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Checkbox(
|
||||
value: widget.value,
|
||||
onChanged: widget.onChanged,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: widget.text,
|
||||
recognizer: _tapRecognizer,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -14,9 +14,16 @@ class AvesOutlinedButton extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final style = ButtonStyle(
|
||||
side: MaterialStateProperty.all<BorderSide>(BorderSide(color: Theme.of(context).colorScheme.secondary)),
|
||||
foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
|
||||
side: MaterialStateProperty.resolveWith<BorderSide>((states) {
|
||||
return BorderSide(
|
||||
color: states.contains(MaterialState.disabled) ? theme.disabledColor : theme.colorScheme.secondary,
|
||||
);
|
||||
}),
|
||||
foregroundColor: MaterialStateProperty.resolveWith<Color>((states) {
|
||||
return states.contains(MaterialState.disabled) ? theme.disabledColor : Colors.white;
|
||||
}),
|
||||
);
|
||||
return icon != null
|
||||
? OutlinedButton.icon(
|
||||
|
|
|
@ -68,7 +68,9 @@ class _HomePageState extends State<HomePage> {
|
|||
}
|
||||
|
||||
await androidFileUtils.init();
|
||||
if (settings.isInstalledAppAccessAllowed) {
|
||||
unawaited(androidFileUtils.initAppNames());
|
||||
}
|
||||
|
||||
var appMode = AppMode.main;
|
||||
final intentData = widget.intentData ?? await ViewerService.getIntentData();
|
||||
|
|
|
@ -32,13 +32,22 @@ class PrivacySection extends StatelessWidget {
|
|||
expandedNotifier: expandedNotifier,
|
||||
showHighlight: false,
|
||||
children: [
|
||||
if (canEnableErrorReporting)
|
||||
Selector<Settings, bool>(
|
||||
selector: (context, s) => s.isErrorReportingEnabled,
|
||||
selector: (context, s) => s.isInstalledAppAccessAllowed,
|
||||
builder: (context, current, child) => SwitchListTile(
|
||||
value: current,
|
||||
onChanged: (v) => settings.isErrorReportingEnabled = v,
|
||||
title: Text(context.l10n.settingsEnableErrorReporting),
|
||||
onChanged: (v) => settings.isInstalledAppAccessAllowed = v,
|
||||
title: Text(context.l10n.settingsAllowInstalledAppAccess),
|
||||
subtitle: Text(context.l10n.settingsAllowInstalledAppAccessSubtitle),
|
||||
),
|
||||
),
|
||||
if (canEnableErrorReporting)
|
||||
Selector<Settings, bool>(
|
||||
selector: (context, s) => s.isErrorReportingAllowed,
|
||||
builder: (context, current, child) => SwitchListTile(
|
||||
value: current,
|
||||
onChanged: (v) => settings.isErrorReportingAllowed = v,
|
||||
title: Text(context.l10n.settingsAllowErrorReporting),
|
||||
),
|
||||
),
|
||||
Selector<Settings, bool>(
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import 'package:aves/app_flavor.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/widgets/common/basic/labeled_checkbox.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/identity/aves_logo.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/home_page.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -26,6 +26,8 @@ class _WelcomePageState extends State<WelcomePage> {
|
|||
bool _hasAcceptedTerms = false;
|
||||
late Future<String> _termsLoader;
|
||||
|
||||
static const double maxWidth = 460;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
@ -38,15 +40,14 @@ class _WelcomePageState extends State<WelcomePage> {
|
|||
return MediaQueryDataProvider(
|
||||
child: Scaffold(
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Center(
|
||||
child: FutureBuilder<String>(
|
||||
future: _termsLoader,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError || snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
||||
final terms = snapshot.data!;
|
||||
final durations = context.watch<DurationsData>();
|
||||
final isPortrait = context.select<MediaQueryData, Orientation>((mq) => mq.orientation) == Orientation.portrait;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _toStaggeredList(
|
||||
|
@ -59,10 +60,29 @@ class _WelcomePageState extends State<WelcomePage> {
|
|||
),
|
||||
),
|
||||
children: [
|
||||
..._buildTop(context),
|
||||
..._buildHeader(context, isPortrait: isPortrait),
|
||||
if (isPortrait) ...[
|
||||
Flexible(child: _buildTerms(terms)),
|
||||
const SizedBox(height: 16),
|
||||
..._buildBottomControls(context),
|
||||
..._buildControls(context),
|
||||
] else
|
||||
Flexible(
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: _buildTerms(terms),
|
||||
)),
|
||||
Flexible(
|
||||
child: ListView(
|
||||
// shrinkWrap: true,
|
||||
children: _buildControls(context),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -74,13 +94,15 @@ class _WelcomePageState extends State<WelcomePage> {
|
|||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildTop(BuildContext context) {
|
||||
List<Widget> _buildHeader(BuildContext context, {required bool isPortrait}) {
|
||||
final message = Text(
|
||||
context.l10n.welcomeMessage,
|
||||
style: Theme.of(context).textTheme.headline5,
|
||||
);
|
||||
final padding = isPortrait ? 16.0 : 8.0;
|
||||
return [
|
||||
...(context.select<MediaQueryData, Orientation>((mq) => mq.orientation) == Orientation.portrait
|
||||
SizedBox(height: padding),
|
||||
...(isPortrait
|
||||
? [
|
||||
const AvesLogo(size: 64),
|
||||
const SizedBox(height: 16),
|
||||
|
@ -96,38 +118,50 @@ class _WelcomePageState extends State<WelcomePage> {
|
|||
],
|
||||
)
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(height: padding),
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildBottomControls(BuildContext context) {
|
||||
List<Widget> _buildControls(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final canEnableErrorReporting = context.select<AppFlavor, bool>((v) => v.canEnableErrorReporting);
|
||||
final checkboxes = Column(
|
||||
const contentPadding = EdgeInsets.symmetric(horizontal: 8);
|
||||
final switches = ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: maxWidth),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (canEnableErrorReporting)
|
||||
LabeledCheckbox(
|
||||
value: settings.isErrorReportingEnabled,
|
||||
onChanged: (v) {
|
||||
if (v != null) setState(() => settings.isErrorReportingEnabled = v);
|
||||
},
|
||||
text: context.l10n.welcomeCrashReportToggle,
|
||||
SwitchListTile(
|
||||
value: settings.isInstalledAppAccessAllowed,
|
||||
onChanged: (v) => setState(() => settings.isInstalledAppAccessAllowed = v),
|
||||
title: Text(l10n.settingsAllowInstalledAppAccess),
|
||||
subtitle: Text([l10n.welcomeOptional, l10n.settingsAllowInstalledAppAccessSubtitle].join(' • ')),
|
||||
contentPadding: contentPadding,
|
||||
),
|
||||
LabeledCheckbox(
|
||||
if (canEnableErrorReporting)
|
||||
SwitchListTile(
|
||||
value: settings.isErrorReportingAllowed,
|
||||
onChanged: (v) => setState(() => settings.isErrorReportingAllowed = v),
|
||||
title: Text(l10n.settingsAllowErrorReporting),
|
||||
subtitle: Text(l10n.welcomeOptional),
|
||||
contentPadding: contentPadding,
|
||||
),
|
||||
SwitchListTile(
|
||||
// key is expected by test driver
|
||||
key: const Key('agree-checkbox'),
|
||||
value: _hasAcceptedTerms,
|
||||
onChanged: (v) {
|
||||
if (v != null) setState(() => _hasAcceptedTerms = v);
|
||||
},
|
||||
text: context.l10n.welcomeTermsToggle,
|
||||
onChanged: (v) => setState(() => _hasAcceptedTerms = v),
|
||||
title: Text(l10n.welcomeTermsToggle),
|
||||
contentPadding: contentPadding,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
final button = ElevatedButton(
|
||||
final button = AvesOutlinedButton(
|
||||
// key is expected by test driver
|
||||
key: const Key('continue-button'),
|
||||
label: context.l10n.continueButtonLabel,
|
||||
onPressed: _hasAcceptedTerms
|
||||
? () {
|
||||
settings.hasAcceptedTerms = true;
|
||||
|
@ -140,33 +174,23 @@ class _WelcomePageState extends State<WelcomePage> {
|
|||
);
|
||||
}
|
||||
: null,
|
||||
child: Text(context.l10n.continueButtonLabel),
|
||||
);
|
||||
|
||||
return context.select<MediaQueryData, Orientation>((mq) => mq.orientation) == Orientation.portrait
|
||||
? [
|
||||
checkboxes,
|
||||
button,
|
||||
]
|
||||
: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
checkboxes,
|
||||
const Spacer(),
|
||||
button,
|
||||
],
|
||||
),
|
||||
return [
|
||||
switches,
|
||||
Center(child: button),
|
||||
const SizedBox(height: 8),
|
||||
];
|
||||
}
|
||||
|
||||
Widget _buildTerms(String terms) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Colors.white10,
|
||||
),
|
||||
constraints: const BoxConstraints(maxWidth: 460),
|
||||
constraints: const BoxConstraints(maxWidth: maxWidth),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||
child: Theme(
|
||||
|
|
|
@ -30,7 +30,7 @@ Future<void> configureAndLaunch() async {
|
|||
settings
|
||||
..keepScreenOn = KeepScreenOn.always
|
||||
..hasAcceptedTerms = false
|
||||
..isErrorReportingEnabled = false
|
||||
..isErrorReportingAllowed = false
|
||||
..locale = const Locale('en')
|
||||
..homePage = HomePageSetting.collection
|
||||
..imageBackground = EntryBackground.checkered;
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
"collectionEditSuccessFeedback",
|
||||
"settingsCollectionBrowsingQuickActionsTile",
|
||||
"settingsCollectionBrowsingQuickActionEditorTitle",
|
||||
"settingsCollectionBrowsingQuickActionEditorBanner"
|
||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||
"settingsAllowInstalledAppAccess",
|
||||
"settingsAllowInstalledAppAccessSubtitle"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
|
@ -21,6 +23,8 @@
|
|||
"collectionEditSuccessFeedback",
|
||||
"settingsCollectionBrowsingQuickActionsTile",
|
||||
"settingsCollectionBrowsingQuickActionEditorTitle",
|
||||
"settingsCollectionBrowsingQuickActionEditorBanner"
|
||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||
"settingsAllowInstalledAppAccess",
|
||||
"settingsAllowInstalledAppAccessSubtitle"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue