miguelpruivo / flutter_file_picker

File picker plugin for Flutter, compatible with mobile (iOS & Android), Web, Desktop (Mac, Linux, Windows) platforms with Flutter Go support.
MIT License
1.32k stars 653 forks source link

On Android targetSdkVersion 30 getDirectoryPath() always returns '/' #681

Closed BenjaminZaiser closed 3 years ago

BenjaminZaiser commented 3 years ago

Describe the bug Android 11; targetSdkVersion 30, file_picker 3.0.1 Call of var result = await FilePicker.platform.getDirectoryPath(); always returns '/'

Platform

Platform OS version Android 11

How are you picking?

var result = await FilePicker.platform.getDirectoryPath();

Details to reproduce the issue Just call the code snippet above

Error Log log shows: Accessing hidden method Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String; (greylist-max-q,test-api, reflection, allowed)

Screenshots and/or video

Flutter Version details `flutter doctor -v [✓] Flutter (Channel stable, 2.0.4, on macOS 11.2.3 20D91 darwin-x64, locale en-DE) • Flutter version 2.0.4 at /Users/***/sdk-flutter • Framework revision b1395592de (9 days ago), 2021-04-01 14:25:01 -0700 • Engine revision 2dce47073a • Dart version 2.12.2

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3) • Android SDK at /Users/***/Library/Android/sdk • Platform android-30, build-tools 30.0.3 • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495) • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS • Xcode at /Applications/Xcode.app/Contents/Developer • Xcode 12.4, Build version 12D4e • CocoaPods version 1.10.1

[✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 4.1) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)

[✓] IntelliJ IDEA Community Edition (version 2017.2.3) • IntelliJ at /Applications/IntelliJ IDEA CE.app • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart

• No issues found! `

Additional context If I change targetSdkVersion to 29 everything works fine. Looks like Android is tightening the restrictions when targeting version 30, see https://developer.android.com/about/versions/10/non-sdk-q

Please can you update this awesome flutter package?

Thanks Ben

miguelpruivo commented 3 years ago

Hi, this is not actually "an issue". It could happen in some directories. It's also pointed here in the documentation.

Sorry for any inconvenience.

BenjaminZaiser commented 3 years ago

Thanks for your feedback. But I'm getting this issue with every directory when targeting version 30. With 29 everything is working fine. Can you reproduce the issue?

elmsec commented 3 years ago

I can reproduce the problem and it only returns '/' for any folder. It's about the scoped storage feature. This feature is a bit different in the API level 30. With 29, everything seems to be working fine for me.

With the API 30, the requestLegacyExternalStorage flag is ignored. This may be the reason why we are encountering this issue:

"Caution: After you update your app to target Android 11 (API level 30), the system ignores the requestLegacyExternalStorage attribute when your app is running on Android 11 devices, so your app must be ready to support scoped storage and to migrate app data for users on those devices."

Source: https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage

compileSdkVersion 30
targetSdkVersion 30
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <application
        ...
        android:requestLegacyExternalStorage="true"
        >
BenjaminZaiser commented 3 years ago

Hi @miguelpruivo please can you reopen this issue, as @elmsec was able to reproduce it as well. Latest in November 2021, every Android apps must target sdkVersion 30. Then your Flutter library will get unusable. From my point of view, this should be treated as a blocker with top priority. (see also https://developer.android.com/distribute/best-practices/develop/target-sdk)

miguelpruivo commented 3 years ago

@BenjaminZaiser this plugin is already migrated to the new API AFAIK. It only allows the requestLegacy for those that want it, but since Android 30 it is no longer supported. The library is already for it.

Despite the changes, there are a few directories that can't be accessed directly for CRUD operations. I believe that's when they return / but prove me wrong.

jmshrv commented 3 years ago

I am also getting this issue. you can see in the logs where it is trying to access an API that is only supported by Android Q:

W/om.example.asd( 8502): Accessing hidden method Landroid/os/storage/StorageManager;->getVolumeList()[Landroid/os/storage/StorageVolume; (greylist, reflection, allowed)
W/om.example.asd( 8502): Accessing hidden method Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String; (greylist-max-q,test-api, reflection, denied)

Here's some example code, I'm running file_picker 3.0.1:

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<String?> getDirectories = FilePicker.platform.getDirectoryPath();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<String?>(
        future: getDirectories,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return Text(snapshot.data!);
          } else {
            return CircularProgressIndicator();
          }
        },
      ),
    );
  }
}
fsonntag commented 3 years ago

Also struggling with that issue. When I select a newly created folder, I get the dialog Allow APP to access files in DIRECTORY. After clicking Allow, I also see Accessing hidden method Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String; (greylist-max-q,test-api, reflection, denied) and get a / returned.

algoexpt commented 3 years ago

This is clearly an issue, I don't get @miguelpruivo 's comments about this being about "special" directories. On my phone, Android doens't even allow me to select these special directories.

Simply closing the issue without sufficiently analyzing this issue is not very helpful 🤔

I'm trying to find other ways for now, if anyone has an update can we keep this thread updated?

miguelpruivo commented 3 years ago

@algoexpt this has been researched multiple times and the further I could found was that for some reason, these directories, returned / as a path, since they can't be have a path — you can however, use URIs to write directly into it, but those aren't helpful since Flutter needs an absolute path to open a descriptor.

If you manage to find a valid workaround or a solution for this, feel free to purpose it and I'll reopen the issue. I don't see a way around right now, I'm sorry.

jmshrv commented 3 years ago

It looks like #763 may fix this. This issue will become more prevalent as Google will soon require all apps on the Play Store to target SDK 30.