request notification permission when launching scanning service

This commit is contained in:
Thibault Deckers 2024-09-01 01:08:07 +02:00
parent 5a273737fb
commit 9b1694cc36
6 changed files with 14 additions and 6 deletions

View file

@ -10,8 +10,13 @@ All notable changes to this project will be documented in this file.
### Changed ### Changed
- request notification permission when launching scanning service
- upgraded Flutter to stable v3.24.1 - upgraded Flutter to stable v3.24.1
### Fixed
- duplicates from new item loading/refreshing
## <a id="v1.11.9"></a>[v1.11.9] - 2024-08-07 ## <a id="v1.11.9"></a>[v1.11.9] - 2024-08-07
### Added ### Added

View file

@ -76,7 +76,7 @@
--> -->
<uses-sdk tools:overrideLibrary="com.arthenica.ffmpegkit.flutter" /> <uses-sdk tools:overrideLibrary="com.arthenica.ffmpegkit.flutter" />
<!-- from Android 11, we should define <queries> to make other apps visible to this app --> <!-- from Android 11 (API 30), we should define <queries> to make other apps visible to this app -->
<queries> <queries>
<intent> <intent>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -92,7 +92,7 @@
<data android:mimeType="video/*" /> <data android:mimeType="video/*" />
</intent> </intent>
<!-- <!--
from Android 11, `url_launcher` method `canLaunchUrl()` will return false, from Android 11 (API 30), `url_launcher` method `canLaunchUrl()` will return false,
if appropriate intents are not declared, cf https://pub.dev/packages/url_launcher#configuration= if appropriate intents are not declared, cf https://pub.dev/packages/url_launcher#configuration=
--> -->
<!-- to open https URLs --> <!-- to open https URLs -->

View file

@ -96,7 +96,7 @@ object PermissionManager {
segments.volumePath?.let { volumePath -> segments.volumePath?.let { volumePath ->
val dirSet = dirsPerVolume[volumePath] ?: HashSet() val dirSet = dirsPerVolume[volumePath] ?: HashSet()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// request primary directory on volume from Android 11 // request primary directory on volume from Android 11 (API 30)
val relativeDir = segments.relativeDir val relativeDir = segments.relativeDir
if (relativeDir != null) { if (relativeDir != null) {
val dirSegments = relativeDir.split(File.separator).takeWhile { it.isNotEmpty() } val dirSegments = relativeDir.split(File.separator).takeWhile { it.isNotEmpty() }
@ -172,7 +172,6 @@ object PermissionManager {
val accessibleDirs = HashSet(getGrantedDirs(context)) val accessibleDirs = HashSet(getGrantedDirs(context))
accessibleDirs.addAll(context.getExternalFilesDirs(null).filterNotNull().map { it.path }) accessibleDirs.addAll(context.getExternalFilesDirs(null).filterNotNull().map { it.path })
// from API 19 / Android 4.4 / KitKat, removable storage requires access permission, at the file level
// from API 21 / Android 5.0 / Lollipop, removable storage requires access permission, but directory access grant is possible // from API 21 / Android 5.0 / Lollipop, removable storage requires access permission, but directory access grant is possible
// from API 30 / Android 11 / R, any storage requires access permission // from API 30 / Android 11 / R, any storage requires access permission
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {

View file

@ -565,7 +565,7 @@ object StorageUtils {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && isMediaStoreContentUri(uri)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && isMediaStoreContentUri(uri)) {
val path = uri.path val path = uri.path
path ?: return uri path ?: return uri
// from Android 11, accessing the original URI for a `file` or `downloads` media content yields a `SecurityException` // from Android 11 (API 30), accessing the original URI for a `file` or `downloads` media content yields a `SecurityException`
if (path.startsWith(IMAGE_PATH_ROOT) || path.startsWith(VIDEO_PATH_ROOT)) { if (path.startsWith(IMAGE_PATH_ROOT) || path.startsWith(VIDEO_PATH_ROOT)) {
// "Caller must hold ACCESS_MEDIA_LOCATION permission to access original" // "Caller must hold ACCESS_MEDIA_LOCATION permission to access original"
if (context.checkSelfPermission(Manifest.permission.ACCESS_MEDIA_LOCATION) == PackageManager.PERMISSION_GRANTED) { if (context.checkSelfPermission(Manifest.permission.ACCESS_MEDIA_LOCATION) == PackageManager.PERMISSION_GRANTED) {

View file

@ -497,7 +497,6 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
} }
} }
if (startAnalysisService) { if (startAnalysisService) {
// TODO TLAD [tiramisu] explain foreground service and request POST_NOTIFICATIONS permission
await AnalysisService.startService( await AnalysisService.startService(
force: force, force: force,
entryIds: entries?.map((entry) => entry.id).toList(), entryIds: entries?.map((entry) => entry.id).toList(),

View file

@ -13,6 +13,7 @@ import 'package:aves_model/aves_model.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:permission_handler/permission_handler.dart';
class AnalysisService { class AnalysisService {
static const _platform = MethodChannel('deckers.thibault/aves/analysis'); static const _platform = MethodChannel('deckers.thibault/aves/analysis');
@ -29,6 +30,10 @@ class AnalysisService {
} }
static Future<void> startService({required bool force, List<int>? entryIds}) async { static Future<void> startService({required bool force, List<int>? entryIds}) async {
// from Android 13 (API 33), notifications are off by default,
// so the user needs to grant the permission to see the service notification
unawaited(Permission.notification.request());
await reportService.log('Start analysis service${entryIds != null ? ' for ${entryIds.length} items' : ''}'); await reportService.log('Start analysis service${entryIds != null ? ' for ${entryIds.length} items' : ''}');
try { try {
await _platform.invokeMethod('startAnalysis', <String, dynamic>{ await _platform.invokeMethod('startAnalysis', <String, dynamic>{