J'ai une application qui utilise un stockage externe pour stocker des photographies. Au besoin, dans son manifeste, les autorisations 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 correctement sur Android 5.1 à 2.3; Il est sur Google Play depuis plus d'un an.
Suite à la mise à niveau de l'un de mes téléphones testeurs (Android One) vers la version 6, une erreur est maintenant renvoyée lors de la tentative de création du répertoire requis, "/ sdcard/Pictures/myapp-yy-mm".
La carte SD est configurée en tant que "stockage portable". J'ai formaté la carte SD. Je l'ai remplacé. J'ai redémarré Tout en rien.
De plus, la fonctionnalité de capture d'écran intégrée Android (via Power + Volume inférieur) échoue "en raison d'un espace de stockage limité ou non autorisé par l'application ou votre organisation".
Des idées?
J'ai rencontré le même problème. Il existe deux types d'autorisations dans Android:
Les autorisations normales sont automatiquement approuvées par Android, tandis que les autorisations dangereuses doivent être approuvées par les utilisateurs de Android.
Voici la stratégie pour obtenir des autorisations dangereuses dans Android 6.0
Voici mon cas: j'ai besoin d'écrire sur un stockage externe.
Tout d'abord, je vérifie si j'ai l'autorisation:
...
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érifiez 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
Tout d'abord, je vous donnerai la liste des autorisations dangereuses dans Android M et la version ultérieure
Ensuite, vous donner un exemple de la façon de demander une autorisation dans Android M et plus tard version.
Je demande à l'utilisateur de l'autorisation WRITE_EXTERNAL_STORAGE .
Ajoutez d'abord la permission dans votre fichier de manifeste Android
Étape 1 Déclare requestcode
private static String TAG = "PermissionDemo";
private static final int REQUEST_WRITE_STORAGE = 112;
Étape 2 Ajoutez ce code lorsque vous souhaitez demander à l'utilisateur une permission.
//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);
}
Étape 3 Ajouter une méthode de substitution 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;
}
}
}
Remarque: N'oubliez pas d'ajouter une permission dans le fichier de manifeste
MEILLEUR EXEMPLE CI-DESSOUS AVEC PLUSIEURS AUTORISATIONS ET COUVRE TOUT LE SCÉNARIO
J'ai ajouté des commentaires pour que vous puissiez facilement comprendre.
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 particulier pour le changement de configuration
Il est possible que l'utilisateur fasse pivoter le périphérique ou déclenche une modification de configuration alors que notre boîte de dialogue d'autorisation est au premier plan. Puisque notre activité est toujours visible derrière cette boîte de dialogue, nous sommes détruits et recréés… mais nous ne souhaitons pas ressusciter à nouveau la boîte de dialogue d'autorisation.
C'est pourquoi nous avons un booléen, nommé isInPermission, qui indique si nous sommes en train de demander des autorisations. Nous conservons cette valeur dans onSaveInstanceState()
:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(STATE_IN_PERMISSION, isInPermission);
}
Nous le restaurons dans onCreate()
. Si nous ne détenons pas toutes les autorisations souhaitées, mais que isInPermission a la valeur true, nous ne demandons pas les autorisations, car nous sommes déjà en train de le faire.
Android a modifié le fonctionnement des autorisations avec Android 6.0, ce qui explique vos erreurs. Vous devez réellement demander et vérifier si l'autorisation a été accordée par l'utilisateur à utiliser. Ainsi, les autorisations dans le fichier manifeste ne fonctionneront que pour les api inférieurs à 21. Vérifiez sur ce lien un extrait de la manière dont les autorisations 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();
}
}
}
}
Peut-être que vous ne pouvez pas utiliser la classe de manifeste à partir du code généré dans votre projet. Vous pouvez donc utiliser la classe de manifestes à partir de Android sdk "Android.Manifest.permission.WRITE_EXTERNAL_STORAGE". Mais dans la version Marsmallow, 2 autorisations doivent être accordées sont WRITE et READ EXTERNAL STORAGE dans la catégorie de stockage. Voir mon programme, mon programme demandera une autorisation jusqu'à ce que l'utilisateur choisisse oui et fasse quelque chose une fois les autorisations accordées.
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
}
Droite. J'ai donc enfin compris le problème: il s'agissait d'une mise à niveau OTA in situ bâclée.
Mes soupçons se sont intensifiés après que mon Garmin Fenix 2 n’ait pas été en mesure de se connecter via Bluetooth et après la recherche de "problèmes de mise à niveau de Marshmallow" sur Google. Quoi qu'il en soit, une "réinitialisation d'usine" a corrigé le problème.
Étonnamment, la réinitialisation n'a pas renvoyé le téléphone au KitKat d'origine; Au lieu de cela, le processus de nettoyage a récupéré le package de mise à niveau OTA téléchargé 6.0 et s’exécute, ce qui a pour résultat (je suppose) une mise à niveau "plus propre".
Bien entendu, 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 aucune modification (c'est-à-dire qu'il existe une compatibilité ascendante). Ouf!
La documentation Android sur Manifest.permission.Manifest.permission.WRITE_EXTERNAL_STORAGE déclare:
À partir de l'API de niveau 19, cette autorisation n'est pas requise pour lire/écrire des fichiers dans vos répertoires spécifiques à l'application, renvoyés par getExternalFilesDir (String) et getExternalCacheDir (). .
Je pense que cela signifie que vous n'avez pas à coder pour l'implémentation à l'exécution du WRITE_EXTERNAL_STORAGE autorisation, sauf si l'application est en train d'écrire dans un répertoire qui n'est pas spécifique à votre application.
Vous pouvez définir la version max de sdk dans le manifeste par autorisation comme suit:
<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" Android:maxSdkVersion="19" />
Assurez-vous également de changer le SDK cible dans le fichier build.graddle et non le manifeste, les paramètres de gradation écrasent toujours les paramètres du manifeste.
Android {
compileSdkVersion 23
buildToolsVersion '23.0.1'
defaultConfig {
minSdkVersion 17
targetSdkVersion 22
}