web-dev-qa-db-fra.com

Oreo (WRITE EXTERNAL STORAGE) Permission

Selon "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.

Maintenant, j'ai le READ_EXTERNAL_STORAGE et je demande au DownloadManager de télécharger un fichier dans le dossier de téléchargement public

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

Malheureusement, j'ai eu 

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)

J'ai déclaré les deux autorisations dans le manifeste

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

L'autorisation de lecture est déjà accordée et, au moment de l'exécution, je demande l'autorisation d'écriture, le système n'invite aucun message ni aucune boîte de dialogue. Il est toujours refusé (non accordé) dans "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(); */       
    }
}

J'essaie d'accorder ces deux autorisations via ADB, cela me montre une erreur:

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

En fait, c'était un problème externe. Une des applications que j'utilise demande le WRITE_EXTERNAL_PERMISSION avec Android: maxSdkVersion. Ainsi, lors de la fusion avec mon manifeste, l’autorisation de l’application entière sera supprimée.

La solution consiste à ajouter:

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

Vous avez raison d'ajouter les autorisations au manifeste:

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

Pour les versions de Lollipop et des versions ultérieures, vous devez également demander les autorisations au moment de l'exécution. Pour résoudre ce problème, j'ai créé une nouvelle méthode requestAppPermission que j'appelle lorsque l'activité principale est créée. Cette méthode ne fonctionne que pour Lollipop et versions supérieures, et retourne plus tôt sinon:

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);
}

J'appelle cette méthode dans la onCreate de l'activité:

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

Lors du premier chargement, cela incitera l'utilisateur à accepter les autorisations.

Ensuite, avant d'exécuter tout code devant être lu et écrit sur le stockage, vous pouvez vérifier ces autorisations en stockant les valeurs ou en réexécutant ces vérifications à l'aide des méthodes hasReadPermissions() et hasWritePermissions() définies ci-dessus.

7
Tina

Pour Oreo, 

vous devez explicitement faire une demande READ_EXTERNAL_STORAGE (par code) même si vous avez déjà demandé et obtenu l'autorisation WRITE_EXTERNAL_STORAGE, et inversement. 

auparavant, READ_EXTERNAL_STORAGE est automatiquement attribué lorsque WRITE_EXTERNAL_STORAGE est attribué.

alors ce que vous devez faire, c'est 

1) demander la permission WRITE dans vos codes.

2) lorsque l'utilisateur accorde la permission WRITE, redemande la permission READ - elle sera automatiquement accordée (vous obtiendrez une exception si vous ne demandez pas de READ explicitement)

Les changements pour Oreo n’ont pas beaucoup de sens pour moi (je ne sais pas pourquoi nous devons demander une permission qui est automatiquement accordée), mais c’est ce que c’est.

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

3
Angel Koh

Vous devez accorder des autorisations à l'exécution sur Marshmallow ou supérieur . 

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(); }
        }

J'espère que ça aide.

1

Juste un avis: sur Android P, je devais afficher une boîte de dialogue de fin permettant à l'utilisateur de redémarrer l'application, car même après que toutes les applications écrites ci-dessus ne pouvaient pas lire le stockage externe, ce n'était qu'après le redémarrage. Sérieusement, ces gars de Google améliorent Android à chaque fois. 6, 8, et maintenant tellement de problèmes avec 9, encore une telle mise à jour stupide et je vais aller sur iOS :)

0
Tertium