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
)