do not force quit on storage permission denial

This commit is contained in:
Thibault Deckers 2022-06-04 12:24:19 +09:00
parent b7a4da17d8
commit 4079e5bddd
5 changed files with 86 additions and 23 deletions

View file

@ -529,6 +529,7 @@
"collectionEmptyFavourites": "No favorites",
"collectionEmptyVideos": "No videos",
"collectionEmptyImages": "No images",
"collectionEmptyGrantAccessButtonLabel": "Grant access",
"collectionSelectSectionTooltip": "Select section",
"collectionDeselectSectionTooltip": "Deselect section",

View file

@ -29,6 +29,7 @@ import 'package:aves/widgets/common/grid/section_layout.dart';
import 'package:aves/widgets/common/grid/selector.dart';
import 'package:aves/widgets/common/grid/sliver.dart';
import 'package:aves/widgets/common/grid/theme.dart';
import 'package:aves/widgets/common/identity/buttons.dart';
import 'package:aves/widgets/common/identity/empty.dart';
import 'package:aves/widgets/common/identity/scroll_thumb.dart';
import 'package:aves/widgets/common/providers/tile_extent_controller_provider.dart';
@ -39,6 +40,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:intl/intl.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
@ -305,13 +307,15 @@ class _CollectionScrollView extends StatefulWidget {
State<_CollectionScrollView> createState() => _CollectionScrollViewState();
}
class _CollectionScrollViewState extends State<_CollectionScrollView> {
class _CollectionScrollViewState extends State<_CollectionScrollView> with WidgetsBindingObserver {
Timer? _scrollMonitoringTimer;
bool _checkingStoragePermission = false;
@override
void initState() {
super.initState();
_registerWidget(widget);
WidgetsBinding.instance.addObserver(this);
}
@override
@ -323,6 +327,7 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> {
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_unregisterWidget(widget);
_stopScrollMonitoringTimer();
super.dispose();
@ -340,6 +345,26 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> {
widget.scrollController.removeListener(_onScrollChange);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.inactive:
case AppLifecycleState.paused:
case AppLifecycleState.detached:
break;
case AppLifecycleState.resumed:
if (_checkingStoragePermission) {
_checkingStoragePermission = false;
_isStoragePermissionGranted.then((granted) {
if (granted) {
widget.collection.source.init();
}
});
}
break;
}
}
@override
Widget build(BuildContext context) {
final scrollView = _buildScrollView(widget.appBar, widget.collection);
@ -423,23 +448,47 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> {
valueListenable: collection.source.stateNotifier,
builder: (context, sourceState, child) {
if (sourceState == SourceState.loading) {
return const SizedBox.shrink();
return const SizedBox();
}
if (collection.filters.any((filter) => filter is FavouriteFilter)) {
return EmptyContent(
icon: AIcons.favourite,
text: context.l10n.collectionEmptyFavourites,
);
}
if (collection.filters.any((filter) => filter is MimeFilter && filter.mime == MimeTypes.anyVideo)) {
return EmptyContent(
icon: AIcons.video,
text: context.l10n.collectionEmptyVideos,
);
}
return EmptyContent(
icon: AIcons.image,
text: context.l10n.collectionEmptyImages,
return FutureBuilder<bool>(
future: _isStoragePermissionGranted,
builder: (context, snapshot) {
final granted = snapshot.data ?? true;
Widget? bottom = granted
? null
: Padding(
padding: const EdgeInsets.only(top: 16),
child: AvesOutlinedButton(
label: context.l10n.collectionEmptyGrantAccessButtonLabel,
onPressed: () async {
if (await openAppSettings()) {
_checkingStoragePermission = true;
}
},
),
);
if (collection.filters.any((filter) => filter is FavouriteFilter)) {
return EmptyContent(
icon: AIcons.favourite,
text: context.l10n.collectionEmptyFavourites,
bottom: bottom,
);
}
if (collection.filters.any((filter) => filter is MimeFilter && filter.mime == MimeTypes.anyVideo)) {
return EmptyContent(
icon: AIcons.video,
text: context.l10n.collectionEmptyVideos,
bottom: bottom,
);
}
return EmptyContent(
icon: AIcons.image,
text: context.l10n.collectionEmptyImages,
bottom: bottom,
);
},
);
},
);
@ -519,4 +568,6 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> {
}
return crumbs;
}
Future<bool> get _isStoragePermissionGranted => Permission.storage.status.then((status) => status.isGranted);
}

View file

@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
class EmptyContent extends StatelessWidget {
final IconData? icon;
final String text;
final Widget? bottom;
final AlignmentGeometry alignment;
final double fontSize;
final bool safeBottom;
@ -13,6 +14,7 @@ class EmptyContent extends StatelessWidget {
super.key,
this.icon,
required this.text,
this.bottom,
this.alignment = const FractionalOffset(.5, .35),
this.fontSize = 22,
this.safeBottom = true,
@ -48,6 +50,7 @@ class EmptyContent extends StatelessWidget {
),
textAlign: TextAlign.center,
),
if (bottom != null) bottom!,
],
),
),

View file

@ -24,7 +24,6 @@ import 'package:aves/widgets/search/search_delegate.dart';
import 'package:aves/widgets/viewer/entry_viewer_page.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
@ -66,15 +65,14 @@ class _HomePageState extends State<HomePage> {
Future<void> _setup() async {
final stopwatch = Stopwatch()..start();
final permissions = await [
// do not check whether permission was granted,
// as some app stores hide in some countries
// apps that force quit on permission denial
await [
Permission.storage,
// to access media with unredacted metadata with scoped storage (Android 10+)
Permission.accessMediaLocation,
].request();
if (permissions[Permission.storage] != PermissionStatus.granted) {
unawaited(SystemNavigator.pop());
return;
}
await androidFileUtils.init();
if (settings.isInstalledAppAccessAllowed) {

View file

@ -1,43 +1,53 @@
{
"de": [
"collectionEmptyGrantAccessButtonLabel",
"settingsThemeEnableDynamicColor"
],
"es": [
"collectionEmptyGrantAccessButtonLabel",
"settingsShowBottomNavigationBar",
"settingsThumbnailShowTagIcon",
"settingsThemeEnableDynamicColor"
],
"fr": [
"collectionEmptyGrantAccessButtonLabel",
"settingsThemeEnableDynamicColor"
],
"id": [
"collectionEmptyGrantAccessButtonLabel",
"settingsThemeEnableDynamicColor"
],
"it": [
"collectionEmptyGrantAccessButtonLabel",
"settingsThemeEnableDynamicColor"
],
"ja": [
"collectionEmptyGrantAccessButtonLabel",
"settingsThemeEnableDynamicColor"
],
"ko": [
"collectionEmptyGrantAccessButtonLabel",
"settingsThemeEnableDynamicColor"
],
"pt": [
"collectionEmptyGrantAccessButtonLabel",
"settingsThemeEnableDynamicColor"
],
"ru": [
"collectionEmptyGrantAccessButtonLabel",
"settingsThemeEnableDynamicColor"
],
"zh": [
"collectionEmptyGrantAccessButtonLabel",
"settingsThemeEnableDynamicColor"
]
}