diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 71b0a89..f74b367 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,10 +7,14 @@ + + + generateZipFilePath() async { } String generateZipFileName() { - return 'vaani-${DateTime.now().toIso8601String()}.zip'; + return 'vaani-${DateTime.now().microsecondsSinceEpoch}.zip'; } Level parseLevel(String level) { diff --git a/lib/features/logging/view/logs_page.dart b/lib/features/logging/view/logs_page.dart index f5ee104..9fbf50b 100644 --- a/lib/features/logging/view/logs_page.dart +++ b/lib/features/logging/view/logs_page.dart @@ -5,9 +5,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:share_plus/share_plus.dart'; import 'package:vaani/features/logging/providers/logs_provider.dart'; import 'package:vaani/main.dart'; +import 'package:vaani/settings/metadata/metadata_provider.dart'; class LogsPage extends HookConsumerWidget { const LogsPage({super.key}); @@ -81,6 +83,46 @@ class LogsPage extends HookConsumerWidget { icon: const Icon(Icons.download), onPressed: () async { appLogger.info('Preparing logs for download'); + + if (Platform.isAndroid) { + final androidVersion = + await ref.watch(deviceSdkVersionProvider.future); + + if ((int.parse(androidVersion)) > 29) { + final status = await Permission.manageExternalStorage.status; + if (!status.isGranted) { + appLogger + .info('Requesting manageExternalStorage permission'); + final newStatus = + await Permission.manageExternalStorage.request(); + if (!newStatus.isGranted) { + appLogger + .warning('manageExternalStorage permission denied'); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Storage permission denied'), + ), + ); + return; + } + } + } else { + final status = await Permission.storage.status; + if (!status.isGranted) { + appLogger.info('Requesting storage permission'); + final newStatus = await Permission.storage.request(); + if (!newStatus.isGranted) { + appLogger.warning('Storage permission denied'); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Storage permission denied'), + ), + ); + return; + } + } + } + } final zipLogFilePath = await ref.read(logsProvider.notifier).getZipFilePath(); @@ -88,12 +130,14 @@ class LogsPage extends HookConsumerWidget { String? outputFile = await FilePicker.platform.saveFile( dialogTitle: 'Please select an output file:', fileName: zipLogFilePath.split('/').last, + bytes: await File(zipLogFilePath).readAsBytes(), ); if (outputFile != null) { try { final file = File(outputFile); final zipFile = File(zipLogFilePath); await zipFile.copy(file.path); + appLogger.info('File saved to: $outputFile'); } catch (e) { appLogger.severe('Error saving file: $e'); } diff --git a/lib/shared/widgets/add_new_server.dart b/lib/shared/widgets/add_new_server.dart index c04d8fd..9114c2b 100644 --- a/lib/shared/widgets/add_new_server.dart +++ b/lib/shared/widgets/add_new_server.dart @@ -39,7 +39,10 @@ class AddNewServer extends HookConsumerWidget { controller: controller, keyboardType: TextInputType.url, autofillHints: const [AutofillHints.url], - textInputAction: TextInputAction.next, + textInputAction: TextInputAction.done, + onFieldSubmitted: (_) { + onPressed?.call(); + }, decoration: InputDecoration( labelText: 'Server URI', labelStyle: TextStyle( diff --git a/pubspec.lock b/pubspec.lock index b6de7a9..670cc8f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -991,6 +991,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb" + url: "https://pub.dev" + source: hosted + version: "11.3.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "76e4ab092c1b240d31177bb64d2b0bea43f43d0e23541ec866151b9f7b2490fa" + url: "https://pub.dev" + source: hosted + version: "12.0.12" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0 + url: "https://pub.dev" + source: hosted + version: "9.4.5" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851 + url: "https://pub.dev" + source: hosted + version: "0.1.3+2" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9 + url: "https://pub.dev" + source: hosted + version: "4.2.3" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" petitparser: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3ce174f..31df2ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -84,6 +84,7 @@ dependencies: package_info_plus: ^8.0.0 path: ^1.9.0 path_provider: ^2.1.0 + permission_handler: ^11.3.1 riverpod_annotation: ^2.3.5 scroll_loop_auto_scroll: ^0.0.5 sensors_plus: ^6.0.1 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8d09818..d043b0b 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -16,6 +17,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin")); MediaKitLibsWindowsAudioPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("MediaKitLibsWindowsAudioPluginCApi")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 51689fc..9839b50 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST isar_flutter_libs media_kit_libs_windows_audio + permission_handler_windows share_plus url_launcher_windows )