it-roy-ru.com

Oreo (ЗАПИСЬ ВНЕШНЕГО ХРАНЕНИЯ) Разрешение

По словам "developer.Android.com"

If the app targets Android 8.0 (API level 26), the system grants only 
READ_EXTERNAL_STORAGE at that time; however, if the app later requests 
WRITE_EXTERNAL_STORAGE, the system immediately grants that privilege 
without prompting the user.

Теперь у меня есть READ_EXTERNAL_STORAGE, и я запрашиваю DownloadManager для загрузки файла в папке Public Download

downloadRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, mAttachmentItem.getFilename());

К сожалению, я получил 

E/UncaughtException: Java.lang.IllegalStateException: Unable to create directory: /storage/emulated/0/data/user/0/com.abc.cba/files
                                                                           at Android.app.DownloadManager$Request.setDestinationInExternalPublicDir(DownloadManager.Java:699)

Я объявил оба разрешения в Манифесте

<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />

Разрешение на чтение уже предоставлено, и во время выполнения я запрашиваю разрешение на запись, система не запрашивает никакого сообщения или диалогового окна, оно всегда отклонено (не предоставлено) в «onRequestPermissionsResult»

public static boolean isPermissionsToAccessStorageAreGranted(Context context, Activity activity) {
    ArrayList<String> permissions = new ArrayList<>();

    if (!PermissionUtil.checkPermissions(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
        permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    }

    if (!permissions.isEmpty()) {
        String[] permissionsList = permissions.toArray(new String[permissions.size()]);
        PermissionUtil.requestPermissions(activity, permissionsList,
                PermissionUtil.REQUESTCODE_ACCESS_STORAGE);
        return false;
    } else {
        return true;
    }
}


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(requestCode==REQUESTCODE_ACCESS_STORAGE && grantResults[0]== PackageManager.PERMISSION_GRANTED){
 /* downloadFile(); */       
    }
}

Я пытаюсь предоставить эти два разрешения через ADB, он показывает мне ошибку:

adb Shell pm grant com.abc.cba 
Android.permission.WRITE_EXTERNAL_STORAGE
Operation not allowed: Java.lang.SecurityException: Package com.abc.cbs 
has not requested permission Android.permission.WRITE_EXTERNAL_STORAGE

adb Shell pm grant com.abc.cba 
Android.permission.READ_EXTERNAL_STORAGE
Operation not allowed: Java.lang.SecurityException: Can't change 
Android.permission.READ_EXTERNAL_STORAGE. It is required by the 
application
13
SoulaimenK

На самом деле, это была внешняя проблема. Один из приложений App я использую запрос WRITE_EXTERNAL_PERMISSION с Android: maxSdkVersion. Так что при слиянии с моим манифестом это приведет к удалению разрешения для всего приложения.

Решение состоит в том, чтобы добавить:

<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/> 
14
SoulaimenK

Вы правы в добавлении разрешений в манифест:

<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />

Для версий Lollipop и выше вам также необходимо запрашивать разрешения во время выполнения. Чтобы решить эту проблему, я создал новый метод requestAppPermission, который я вызываю при создании основного действия. Этот метод работает только для Lollipop и выше, а в противном случае возвращает рано:

private void requestAppPermissions() {
    if (Android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Lollipop) {
        return;
    }

    if (hasReadPermissions() && hasWritePermissions()) {
        return;
    }

    ActivityCompat.requestPermissions(this,
            new String[] {
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            }, REQUEST_WRITE_STORAGE_REQUEST_CODE); // your request code
}

private boolean hasReadPermissions() {
    return (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
}

private boolean hasWritePermissions() {
    return (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
}

Я вызываю этот метод в onCreate действия:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Other things
    requestAppPermissions();
}

При первой загрузке пользователю будет предложено принять разрешения.

Затем, прежде чем запускать любой код, который должен читать и записывать в хранилище, вы можете проверить эти разрешения, либо сохранив значения, либо снова выполнив эти проверки, используя методы hasReadPermissions() и hasWritePermissions(), определенные выше.

7
Tina

Для Орео, 

вам нужно явно выполнить запрос READ_EXTERNAL_STORAGE (по коду), даже если вы уже запросили и получили разрешение WRITE_EXTERNAL_STORAGE, и наоборот. 

до этого READ_EXTERNAL_STORAGE автоматически предоставляется, когда предоставляется WRITE_EXTERNAL_STORAGE.

так что вам нужно сделать, это 

1) попросите разрешения WRITE в ваших кодах.

2) когда пользователь предоставит разрешение WRITE, снова запросите разрешение READ - оно будет предоставлено автоматически (вы получите исключение, если не будете запрашивать READ явно)

Изменения для Oreo не имеют для меня особого смысла (не знаю, зачем нам запрашивать разрешение, которое предоставляется автоматически), но это именно так.

https://developer.Android.com/about/versions/oreo/Android-8.0-changes.html#rmp

3
Angel Koh

Вы должны предоставить разрешения во время выполнения на Marshmallow или выше . Пример фрагмента: 

private static final int REQUEST_WRITE_STORAGE = 112;

if (Android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            boolean hasPermission = (ContextCompat.checkSelfPermission(getBaseContext(),
                    Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
            if (!hasPermission) {
                ActivityCompat.requestPermissions(SplashScreen.this,
                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.INTERNET
                        },
                        REQUEST_WRITE_STORAGE);
            }else{ startMainActivity(); }
        }

Надеюсь, поможет.

1
Maykel Llanes Garcia

Просто замечание: на Android P мне пришлось показать диалоговое окно завершения пользователю, который просит перезапустить приложение, потому что даже после того, как все написанное выше, приложение не может прочитать внешнее хранилище, только после перезагрузки. Серьезно, эти ребята из Google делают Android лучше с каждым разом. 6, 8, а сейчас столько проблем с 9, еще одно такое глупое обновление и я пойду на iOS :)

0
Tertium