From c3d9cc273fa661df1c303d738a88678c2bb5d5d2 Mon Sep 17 00:00:00 2001 From: bvandeusen Date: Mon, 16 Mar 2026 07:54:00 -0400 Subject: [PATCH 1/2] fix: quick capture incorrectly treated as offline on LLM timeout The global receiveTimeout is 30s but quick capture runs LLM inference that can take much longer. receiveTimeout fired, fell through to the NetworkException fallback in dioToApp, and the work queue queued it as an offline item. - quick_capture_api.dart: override receiveTimeout to 120s for the /api/quick-capture request - api_client.dart: handle receiveTimeout/sendTimeout explicitly in the error interceptor, converting them to AppException (not NetworkException) so slow responses are never mistaken for being offline Co-Authored-By: Claude Sonnet 4.6 --- lib/data/api/api_client.dart | 10 ++++++++++ lib/data/api/quick_capture_api.dart | 1 + 2 files changed, 11 insertions(+) diff --git a/lib/data/api/api_client.dart b/lib/data/api/api_client.dart index 1419f92..e88b917 100644 --- a/lib/data/api/api_client.dart +++ b/lib/data/api/api_client.dart @@ -44,6 +44,16 @@ class _ErrorInterceptor extends Interceptor { )); return; } + if (err.type == DioExceptionType.receiveTimeout || + err.type == DioExceptionType.sendTimeout) { + handler.reject(DioException( + requestOptions: err.requestOptions, + error: const AppException('Request timed out. The server is taking too long to respond.'), + type: err.type, + response: err.response, + )); + return; + } handler.next(err); } } diff --git a/lib/data/api/quick_capture_api.dart b/lib/data/api/quick_capture_api.dart index 19ca190..485b28c 100644 --- a/lib/data/api/quick_capture_api.dart +++ b/lib/data/api/quick_capture_api.dart @@ -40,6 +40,7 @@ class QuickCaptureApi { final response = await _dio.post( '/api/quick-capture', data: {'text': text}, + options: Options(receiveTimeout: const Duration(seconds: 120)), ); return CaptureResult.fromJson(response.data as Map); } on DioException catch (e) { From 9f1d2317afdae9d53e1c06a8b43c801c2ab28564 Mon Sep 17 00:00:00 2001 From: bvandeusen Date: Mon, 16 Mar 2026 11:56:24 -0400 Subject: [PATCH 2/2] fix: request install-packages permission at runtime before APK install Android 8+ requires canRequestPackageInstalls() to return true even when REQUEST_INSTALL_PACKAGES is declared in the manifest. Add permission_handler and check/request Permission.requestInstallPackages before downloading; redirects user to Settings if not granted and shows a clear error message. Co-Authored-By: Claude Sonnet 4.6 --- lib/providers/update_provider.dart | 17 +++++++++++++++++ pubspec.yaml | 1 + 2 files changed, 18 insertions(+) diff --git a/lib/providers/update_provider.dart b/lib/providers/update_provider.dart index 73dbdcc..b5aac9c 100644 --- a/lib/providers/update_provider.dart +++ b/lib/providers/update_provider.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:open_file/open_file.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; enum UpdateStatus { idle, checking, available, downloading, upToDate, error } @@ -102,6 +103,22 @@ class UpdateNotifier extends Notifier { Future downloadAndInstall() async { if (state.downloadUrl == null) return; + + // Android 8+ requires explicit per-app "Install unknown apps" approval + // beyond the manifest declaration. Check and redirect to Settings if needed. + final installPermission = await Permission.requestInstallPackages.status; + if (!installPermission.isGranted) { + final result = await Permission.requestInstallPackages.request(); + if (!result.isGranted) { + state = state.copyWith( + status: UpdateStatus.error, + errorMessage: + 'Grant "Install unknown apps" permission for Fabled in Settings, then try again.', + ); + return; + } + } + state = state.copyWith(status: UpdateStatus.downloading, downloadProgress: 0); try { diff --git a/pubspec.yaml b/pubspec.yaml index 299298d..3830495 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: markdown: ^7.2.2 package_info_plus: ^9.0.0 open_file: ^3.3.2 + permission_handler: ^11.3.1 flutter_inappwebview: ^6.1.5 flutter_markdown_plus: ^1.0.7 google_fonts: ^8.0.2