diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a978256..4ee78e7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ android:name="${applicationName}" android:icon="@mipmap/launcher_icon" android:enableOnBackInvokedCallback="true" + requestLegacyStorage="true" requestLegacyExternalStorage="true"> { : SelectableText(content); } + GestureDetector getFileShow(String filepath, IconData icon) { + return GestureDetector( + onTap: () async { + isMobile + ? OpenFilex.open(filepath) + : launchUrlString('file://$filepath'); + }, + child: SizedBox( + width: 200, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + icon, + size: 30, + ), + const SizedBox( + width: 15, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(path.basename(filepath).length <= maxlen + ? path.basename(filepath) + : '${path.basename(filepath).substring(0, maxlen)}...'), + Text("${getFileSize(filepath).toStringAsFixed(2)} MB") + ], + ), + ], + ), + ), + ); + } + Widget showMsgContent(Message message) { switch (message.type) { case "TEXT": return showText(message.data); + case "IMAGE": - return GestureDetector( - onTap: () async { - isMobile - ? OpenFilex.open(message.data) - : launchUrlString('file://${message.data}'); - }, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.file(File(message.data)), - const SizedBox(height: 5), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text(path.basename(message.data)), - Text("${getFileSize(message.data).toStringAsFixed(2)} MB") - ], - ), - ], - )); + return getFileShow(message.data, Icons.image_outlined); case "VIDEO": - return GestureDetector( - onTap: () { - isMobile - ? OpenFilex.open(message.data) - : launchUrlString('file://${message.data}'); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const Icon(Icons.play_arrow_outlined), - Text(path.basename(message.data)), - Text("${getFileSize(message.data).toStringAsFixed(2)} MB") - ], - ), - ); - // case "AUDIO": - // return AudioPlayer(message.data); + return getFileShow(message.data, Icons.videocam_outlined); case "FILE": - return GestureDetector( - onTap: () { - isMobile - ? OpenFilex.open(message.data) - : launchUrlString('file://${message.data}'); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const Icon(Icons.file_open_outlined), - Text(path.basename(message.data)), - Text("${getFileSize(message.data).toStringAsFixed(2)} MB") - ], - ), - ); + return getFileShow(message.data, Icons.insert_drive_file_outlined); default: return const Icon(Icons.question_mark_outlined); } @@ -379,16 +365,18 @@ class _ChatScreenState extends ConsumerState { children: [ IconButton( onPressed: () async { - final FilePickerResult? files = - await FilePicker.platform.pickFiles(); + final FilePickerResult? files = await FilePicker.platform + .pickFiles(allowMultiple: true); Navigator.pop(context); - if (files != null && - files.files.isNotEmpty && - files.files[0].path != null) { - final XFile file = XFile(files.files[0].path ?? "/"); - sendFile( - widget.peer.ip, widget.port, file, "FILE", false); - notifier.addMessage(file.path, true, "FILE"); + if (files != null && files.files.isNotEmpty) { + for (PlatformFile pfile in files.files) { + if (pfile.path != null) { + final XFile file = XFile(pfile.path ?? "/"); + sendFile(widget.peer.ip, widget.port, file, "FILE", + false); + notifier.addMessage(file.path, true, "FILE"); + } + } } }, icon: const Icon(Icons.insert_drive_file_outlined), diff --git a/lib/main.dart b/lib/main.dart index 6e440a1..de87f7d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -371,9 +371,16 @@ class _HomePageState extends State { ) ], onChanged: (value) { - localIP = value!; - startDeviceDiscovery(tapped: true); - refresh(); + if (value != null) { + localIP = value; + if (httpServer != null) { + httpServer!.close(); + } + startHttpServer(httpServer, localIP, port, getAcceptAns, + cancelPopup); + startDeviceDiscovery(tapped: true); + refresh(); + } }, ), ], diff --git a/lib/setting.dart b/lib/setting.dart index 383758a..af7f051 100644 --- a/lib/setting.dart +++ b/lib/setting.dart @@ -7,9 +7,10 @@ import 'package:flutter/services.dart'; import 'package:localconnect/data.dart'; import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import 'package:adaptive_theme/adaptive_theme.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:toggle_switch/toggle_switch.dart'; -import 'package:file_picker/file_picker.dart'; import 'package:url_launcher/url_launcher_string.dart'; +import 'package:file_picker/file_picker.dart'; class Settings extends StatefulWidget { String initialDeviceName; @@ -48,8 +49,13 @@ class _SettingsState extends State { // setting folder Future setDestinationFolder() async { - final String? path; - path = await FilePicker.platform.getDirectoryPath(); + if (Platform.isAndroid && + !await Permission.manageExternalStorage.status.isGranted) { + await Permission.manageExternalStorage.request(); + } + + String? path = await FilePicker.platform.getDirectoryPath(); + if (path != null) { destination = path.replaceAll('\\', '/'); if (!destination.contains("LocalConnect")) { diff --git a/pubspec.lock b/pubspec.lock index 023e29d..a9a623a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -696,6 +696,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: ad65ba9af42a3d067203641de3fd9f547ded1410bad3b84400c2b4899faede70 + url: "https://pub.dev" + source: hosted + version: "11.0.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: f2543a236584a5e8be79076f858022f100ce690e31530e6fa4c32ac94f276d3a + url: "https://pub.dev" + source: hosted + version: "11.0.3" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + url: "https://pub.dev" + source: hosted + version: "9.1.4" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: f2343e9fa9c22ae4fd92d4732755bfe452214e7189afcc097380950cf567b4b2 + url: "https://pub.dev" + source: hosted + version: "3.11.5" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + url: "https://pub.dev" + source: hosted + version: "0.1.3" petitparser: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d0e053c..091ab13 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: path_provider: 2.1.1 open_filex: 4.3.4 path: 1.8.3 + permission_handler: 11.0.0 window_size: git: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index cd2d5c1..27b85cf 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,12 +7,15 @@ #include "generated_plugin_registrant.h" #include +#include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); WindowSizePluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index a22fdb0..6191fb4 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows + permission_handler_windows url_launcher_windows window_size )