-
Notifications
You must be signed in to change notification settings - Fork 87
fix: resolve silent document upload failures on Android 13+ by implem… #1059
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,14 +562,38 @@ class _DocumentUploadControlState extends State<DocumentUploadControl> { | |
| ? 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; | ||
| } | ||
|
Comment on lines
+565
to
+589
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win Extract duplicate permission logic into a helper method. The identical permission-checking logic appears in both mobile (lines 565-589) and responsive (lines 860-895) layout handlers, violating the DRY principle. Extract this into a reusable helper method to improve maintainability. ♻️ Proposed refactor to eliminate duplicationAdd this helper method to the Future<bool> _requestStoragePermission() async {
if (Platform.isAndroid) {
final androidInfo = await DeviceInfoPlugin().androidInfo;
if (androidInfo.version.sdkInt >= 33) {
// Android 13+: Granular media permission
final photosStatus = await Permission.photos.request();
return photosStatus.isGranted;
} else {
// Android 12 and below: Legacy storage permission
final storageStatus = await Permission.storage.request();
return storageStatus.isGranted;
}
} else {
final storageStatus = await Permission.storage.request();
return storageStatus.isGranted;
}
}Then replace both occurrences with: _documentScanClickedAudit();
final isGranted = await _requestStoragePermission();
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);Also applies to: 860-895 🤖 Prompt for AI AgentsAdd mounted check before using context after async operations. The code uses 🛡️ Proposed fix to add mounted check if (!isGranted) {
+ if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Storage permission is required to select documents.'),
backgroundColor: Colors.red,
),
);
return;
}
+ if (!mounted) return;
var doc = await Navigator.push(🤖 Prompt for AI Agents |
||
| var doc = await Navigator.push( | ||
| context, | ||
| MaterialPageRoute( | ||
| builder: (context) => | ||
| CustomScanner( | ||
| field: widget.field)), | ||
| ); | ||
|
|
||
| await addDocument( | ||
| doc, widget.field, referenceNumber); | ||
| await getScannedDocuments(widget.field); | ||
|
|
@@ -828,20 +854,45 @@ class _DocumentUploadControlState extends State<DocumentUploadControl> { | |
| 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: [ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for device info retrieval.
The call to
DeviceInfoPlugin().androidInfocan throw an exception (e.g., on unsupported platforms or plugin initialization failures), but there's no try-catch block. Consider adding error handling to gracefully fallback or show a user-friendly message.🛡️ Proposed fix with error handling
🤖 Prompt for AI Agents