Android 6.0 Marshmallow. Ne peut pas écrire à la carte SD

j'ai une application qui utilise le stockage externe pour stocker des photographies. Comme requis, dans son manifeste, les permissions suivantes sont demandées

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

et il utilise ce qui suit pour récupérer le répertoire requis

File sdDir = Environment
            .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd", Locale.US);
String date = dateFormat.format(new Date());
storageDir = new File(sdDir, getResources().getString(
            R.string.storagedir)
            + "-" + date);

// Create directory, error handling
if (!storageDir.exists() && !storageDir.mkdirs()) {
 ... fails here

l'application fonctionne très bien sur Android 5.1 à 2.3; il a été sur Google Play depuis plus d'un an.

suite à une mise à niveau d'un de mes téléphones de test (Android One) à 6, il est maintenant de retour un erreur lors de la création du répertoire requis, "/sdcard/Pictures/myapp-yy-mm".

la carte sd est configurée comme"Stockage Portable". J'ai formaté la carte sd. Je l'ai remplacé. J'ai redémarré. Ce fut en vain.

aussi, la fonctionnalité de capture d'écran intégrée à android (via Power+Lower volume) est défaillante "en raison de l'espace de stockage limité, ou il n'est pas autorisé par l'application ou votre organisation".

des idées?

50
demandé sur RudyF 2015-10-15 07:10:38

6 réponses

j'ai fait face au même problème. Il existe deux types de permissions dans Android:

  • Dangereux (l'accès aux contacts, écrire stockage externe...)
  • Normal

les permissions normales sont automatiquement approuvées par Android tandis que les permissions dangereuses doivent être approuvées par les utilisateurs Android.

Voici la stratégie pour obtenir des permissions dangereuses dans Android 6.0

  1. vérifiez si vous avez la permission accordée
  2. si votre application a déjà obtenu la permission, allez de l'avant et perform normalement.
  3. si votre application n'a pas encore la permission, demandez à l'utilisateur d'approuver ""
  4. Écouter l'approbation de l'utilisateur dans onRequestPermissionsResult

Voici mon cas: je dois écrire au stockage externe.

d'Abord, je vérifie si j'ai le permission:

...
private static final int REQUEST_WRITE_STORAGE = 112;
...
boolean hasPermission = (ContextCompat.checkSelfPermission(activity,
            Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
if (!hasPermission) {
    ActivityCompat.requestPermissions(parentActivity,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_WRITE_STORAGE);
}

puis vérifier l'approbation de l'utilisateur:

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode)
    {
        case REQUEST_WRITE_STORAGE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
                //reload my activity with permission granted or use the features what required the permission
            } else
            {
                Toast.makeText(parentActivity, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show();
            }
        }
    }

}

vous pouvez en savoir plus sur le nouveau modèle de permission ici: https://developer.android.com/training/permissions/requesting.html

49
répondu codingpuss 2015-11-20 23:26:46

D'abord je vais vous donner liste de Permission dangereuse dans Android M et version ultérieure

enter image description here enter image description here

puis vous donner l'exemple de la façon de demander l'autorisation dans Android M et plus tard version.

je demande à l'utilisateur de 151980920" WRITE_EXTERNAL_STORAGE permission.

première autorisation d'ajouter dans votre fichier menifest android

Étape 1 Déclarer requestcode

 private static String TAG = "PermissionDemo";
 private static final int REQUEST_WRITE_STORAGE = 112; 

Étape 2 ajouter ce code lorsque vous voulez demander l'autorisation à l'utilisateur

 //ask for the permission in android M
    int permission = ContextCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        Log.i(TAG, "Permission to record denied");

        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("Permission to access the SD-CARD is required for this app to Download PDF.")
                    .setTitle("Permission required");

            builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int id) {
                    Log.i(TAG, "Clicked");
                    makeRequest();
                }
            });

            AlertDialog dialog = builder.create();
            dialog.show();

        } else {
            makeRequest();
        }
    }

    protected void makeRequest() {
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_WRITE_STORAGE);
    }

l'Étape 3 Ajouter remplacer la méthode pour la Requête

 @Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case REQUEST_WRITE_STORAGE: {

            if (grantResults.length == 0
                    || grantResults[0] !=
                    PackageManager.PERMISSION_GRANTED) {

                Log.i(TAG, "Permission has been denied by user");

            } else {

                Log.i(TAG, "Permission has been granted by user");

            }
            return;
        }
    }
}

Note: N'oubliez pas d'ajouter la permission dans le fichier menifest

MEILLEUR EXEMPLE CI-DESSOUS AVEC PERMISSION MULTIPLE PLUS COUVRIR TOUS LES SCÉNARIOS

j'ai ajouté des commentaires pour que vous puissiez comprendre facilement.

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.production.hometech.busycoder.R;

import java.util.ArrayList;

public class PermissionInActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int REQUEST_PERMISSION_SETTING = 99;
    private Button bt_camera;
    private static final String[] PARAMS_TAKE_PHOTO = {
            Manifest.permission.CAMERA,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };
    private static final int RESULT_PARAMS_TAKE_PHOTO = 11;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission_in);

        bt_camera = (Button) findViewById(R.id.bt_camera);

        bt_camera.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {

        switch (view.getId()) {

            case R.id.bt_camera:

                takePhoto();

                break;

        }
    }


    /**
     * shouldShowRequestPermissionRationale() = This will return true if the user had previously declined to grant you permission
     * NOTE :  that ActivityCompat also has a backwards-compatible implementation of
     * shouldShowRequestPermissionRationale(), so you can avoid your own API level
     * checks.
     * <p>
     * shouldShowRequestPermissionRationale() =  returns false if the user declined the permission and checked the checkbox to ask you to stop pestering the
     * user.
     * <p>
     * requestPermissions() = request for the permisssiion
     */
    private void takePhoto() {

        if (canTakePhoto()) {

            Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show();

        } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

            Toast.makeText(this, "You should give permission", Toast.LENGTH_SHORT).show();
            ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO);

        } else {
            ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO);
        }

    }

    //  This method return  permission denied String[] so we can request again
    private String[] netPermisssion(String[] wantedPermissions) {
        ArrayList<String> result = new ArrayList<>();

        for (String permission : wantedPermissions) {
            if (!hasPermission(permission)) {
                result.add(permission);
            }
        }

        return (result.toArray(new String[result.size()]));

    }

    private boolean canTakePhoto() {
        return (hasPermission(Manifest.permission.CAMERA) && hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE));
    }

    /**
     * checkSelfPermission() = you can check if you have been granted a runtime permission or not
     * ex = ContextCompat.checkSelfPermission(this,permissionString)== PackageManager.PERMISSION_GRANTED
     * <p>
     * ContextCompat offers a backwards-compatible implementation of checkSelfPermission(), ActivityCompat offers a backwards-compatible
     * implementation of requestPermissions() that you can use.
     *
     * @param permissionString
     * @return
     */
    private boolean hasPermission(String permissionString) {
        return (ContextCompat.checkSelfPermission(this, permissionString) == PackageManager.PERMISSION_GRANTED);
    }

    /**
     * requestPermissions() action goes to onRequestPermissionsResult() whether user can GARNT or DENIED those permisssions
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == RESULT_PARAMS_TAKE_PHOTO) {

            if (canTakePhoto()) {

                Toast.makeText(this, "You can take picture", Toast.LENGTH_SHORT).show();

            } else if (!(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE))) {


                final AlertDialog.Builder settingDialog = new AlertDialog.Builder(PermissionInActivity.this);
                settingDialog.setTitle("Permissioin");
                settingDialog.setMessage("Now you need to enable permisssion from the setting because without permission this app won't run properly \n\n  goto -> setting -> appInfo");
                settingDialog.setCancelable(false);

                settingDialog.setPositiveButton("Setting", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {

                        dialogInterface.cancel();

                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", getPackageName(), null);
                        intent.setData(uri);
                        startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
                        Toast.makeText(getBaseContext(), "Go to Permissions to Grant all permission ENABLE", Toast.LENGTH_LONG).show();

                    }
                });
                settingDialog.show();

                Toast.makeText(this, "You need to grant permission from setting", Toast.LENGTH_SHORT).show();

            }

        }

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_PERMISSION_SETTING) {

            if (canTakePhoto()) {

                Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show();

            }

        }

    }


}

cas spécial pour le changement de Configuration

Il est possible que l'utilisateur fait pivoter l'appareil ou sinon, déclenchez un changement de configuration pendant que notre dialogue de permission est au premier plan. Puisque notre activité est toujours visible derrière ce dialogue, nous sommes détruits et recréés... mais nous ne voulons pas relancer le dialogue de permission.

C'est pourquoi nous avons un booléen, nommé isInPermission, qui trace si oui ou non nous sommes dans le milieu de demander des autorisations. Nous maintenons cette valeur en onSaveInstanceState() :

@Override
protected void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  outState.putBoolean(STATE_IN_PERMISSION, isInPermission);
}

nous le restaurons en onCreate() . Si nous ne détenons pas toutes les autorisations souhaitées, mais isInPermission est vrai, nous sautons demander les autorisations, puisque nous sommes dans le moyen de le faire déjà.

20
répondu Arpit Patel 2017-07-07 09:57:57

Android a changé la façon dont les permissions fonctionnent avec Android 6.0 c'est la raison de vos erreurs. Vous devez réellement demander et vérifier si la permission a été accordée par l'utilisateur d'utiliser. Ainsi, les permissions dans le fichier manifest ne fonctionneront que pour les api inférieures à 21. Consultez ce lien pour un extrait de comment les permissions sont demandées dans api23 http://android-developers.blogspot.nl/2015/09/google-play-services-81-and-android-60.html?m=1

Code: -

If (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
                PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_RC);
            return;
        }`


` @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == STORAGE_PERMISSION_RC) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //permission granted  start reading
            } else {
                Toast.makeText(this, "No permission to read external storage.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}
7
répondu codeFreak 2015-11-12 15:55:38

peut-être que vous ne pouvez pas utiliser la classe manifeste à partir du code généré dans votre projet. Donc, vous pouvez utiliser la classe manifest d'android sdk "android.Manifester.autorisation.WRITE_EXTERNAL_STORAGE". Mais dans la version de Marsmallow ont 2 permission doit accorder sont écrire et lire stockage externe dans la catégorie de stockage. Voir mon programme, Mon programme demandera la permission jusqu'à ce que l'utilisateur choisir Oui et faire quelque chose après les permissions est accordée.

            if (Build.VERSION.SDK_INT >= 23) {
                if (ContextCompat.checkSelfPermission(LoginActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(LoginActivity.this, android.Manifest.permission.READ_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(LoginActivity.this,
                            new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE},
                            1);
                } else {
                    //do something
                }
            } else {
                    //do something
            }
6
répondu icaksama 2016-03-30 05:49:38

D'accord. J'ai donc finalement réussi à le fond du problème: il a été bâclé en place OTA mise à niveau.

mes soupçons se sont intensifiés après que mon Garmin Fenix 2 N'ait pas pu se connecter via bluetooth et après avoir googlé "Marshmallow upgrade issues". Quoi qu'il en soit, une" réinitialisation D'usine " a réglé le problème.

étonnamment, la réinitialisation n'a pas retourné le téléphone au Kitkat original; au lieu de cela, le processus de nettoyage a ramassé le paquet de mise à niveau 6.0 téléchargé par OTA et a couru avec, résultant (je suppose) dans une mise à niveau "plus propre".

bien sûr, cela signifie que le téléphone a perdu toutes les applications que j'avais installées. Mais, les applications fraîchement installées, y compris la mienne, fonctionnent sans aucun changement (c'est-à-dire qu'il y a une rétrocompatibilité). Ouf!

3
répondu RudyF 2015-10-18 15:12:32

documentation Android sur manifeste.autorisation.Manifester.autorisation.WRITE_EXTERNAL_STORAGE states:

à partir du niveau 19 de L'API, Cette permission est et non nécessaire pour lire/écrire des fichiers dans vos répertoires spécifiques à l'application retournés par getExternalFilesDir(String) et getExternalCacheDir().


je penser que cela signifie que vous n'avez pas à coder pour l'implémentation de l'exécution de la permission WRITE_EXTERNAL_STORAGE à moins que l'application n'écrive dans un répertoire qui n'est pas spécifique à votre application.

vous pouvez définir la version max sdk dans le manifeste par permission comme:

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="19" />

assurez-vous également de changer le SDK cible dans le build.graddle et non le manifeste, les réglages du gradle écraseront toujours le manifeste paramètre.

android {
compileSdkVersion 23
buildToolsVersion '23.0.1'
defaultConfig {
    minSdkVersion 17
    targetSdkVersion 22
}
3
répondu Stefano D 2016-03-27 11:39:27