From aebd173e8df893302eb5537d4e10058b9d48cdac Mon Sep 17 00:00:00 2001 From: vibhutomer Date: Thu, 14 May 2026 10:34:34 +0530 Subject: [PATCH] fix: resolve silent document upload failures on Android 13+ by implementing granular media permissions Signed-off-by: vibhutomer --- android/app/src/main/AndroidManifest.xml | 2 + .../widgets/document_upload_control.dart | 81 +++++++++++++++---- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7004ae5a5..d0d065c33 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ + + diff --git a/lib/ui/process_ui/widgets/document_upload_control.dart b/lib/ui/process_ui/widgets/document_upload_control.dart index f1103f61d..3e75eeb40 100644 --- a/lib/ui/process_ui/widgets/document_upload_control.dart +++ b/lib/ui/process_ui/widgets/document_upload_control.dart @@ -7,6 +7,8 @@ import 'dart:io'; +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -560,6 +562,31 @@ class _DocumentUploadControlState extends State { ? null : () async { _documentScanClickedAudit(); + bool isGranted = false; + if (Platform.isAndroid) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt >= 33) { + // Android 13+: Granular media permission + final photosStatus = await Permission.photos.request(); + isGranted = photosStatus.isGranted; + } else { + // Android 12 and below: Legacy storage permission + final storageStatus = await Permission.storage.request(); + isGranted = storageStatus.isGranted; + } + } else { + final storageStatus = await Permission.storage.request(); + isGranted = storageStatus.isGranted; + } + if (!isGranted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Storage permission is required to select documents.'), + backgroundColor: Colors.red, + ), + ); + return; + } var doc = await Navigator.push( context, MaterialPageRoute( @@ -567,7 +594,6 @@ class _DocumentUploadControlState extends State { CustomScanner( field: widget.field)), ); - await addDocument( doc, widget.field, referenceNumber); await getScannedDocuments(widget.field); @@ -828,20 +854,45 @@ class _DocumentUploadControlState extends State { minimumSize: Size(100.w, 48.h), ), onPressed: (documentController.text == "") - ? null - : () async { - _documentScanClickedAudit(); - var doc = await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => CustomScanner( - field: widget.field)), - ); - await addDocument( - doc, widget.field, referenceNumber); - - await getScannedDocuments(widget.field); - }, + ? null + : () async { + _documentScanClickedAudit(); + bool isGranted = false; + if (Platform.isAndroid) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt >= 33) { + // Android 13+: Granular media permission + final photosStatus = await Permission.photos.request(); + isGranted = photosStatus.isGranted; + } else { + // Android 12 and below: Legacy storage permission + final storageStatus = await Permission.storage.request(); + isGranted = storageStatus.isGranted; + } + } else { + final storageStatus = await Permission.storage.request(); + isGranted = storageStatus.isGranted; + } + if (!isGranted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Storage permission is required to select documents.'), + backgroundColor: Colors.red, + ), + ); + return; + } + var doc = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + CustomScanner( + field: widget.field)), + ); + await addDocument( + doc, widget.field, referenceNumber); + await getScannedDocuments(widget.field); + }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [