Ich versuche, meine App auf die neuen Änderungen der Android M-Berechtigungen vorzubereiten, und habe ein seltsames Verhalten festgestellt. Meine App verwendet den Camera Intent-Mechanismus, damit der Benutzer ein Bild von der Kamera erhalten kann. In einer anderen Aktivität muss jedoch die Kamera selbst mit der Erlaubnis der Kamera verwendet werden (aufgrund einer Bibliotheksabhängigkeit card.io, die dies erfordert).
Wenn jedoch M in der Aktivität eine Kameraabsicht benötigt, wenn ich versuche, die Kameraabsicht zu starten, wird der folgende Absturz angezeigt (dies geschieht nicht, wenn ich die Kameraberechtigung aus dem Manifest entferne).
> 09-25 21:57:55.260 774-8053/? I/ActivityManager: START u0
> {act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras)} from uid 10098 on display 0 09-25
> 21:57:55.261 774-8053/? W/ActivityManager: Permission Denial: starting
> Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity: Unable to launch as uid 10098 package
> com.example.me.mycamerselectapp, while running in android:ui 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity:
> java.lang.SecurityException: Permission Denial: starting Intent {
> act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity: at
> android.os.Parcel.readException(Parcel.java:1599) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Parcel.readException(Parcel.java:1552) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.app.ActivityManagerProxy.startActivityAsCaller(ActivityManagerNative.java:2730)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.app.Instrumentation.execStartActivityAsCaller(Instrumentation.java:1725)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.app.Activity.startActivityAsCaller(Activity.java:4047) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity$DisplayResolveInfo.startAsCaller(ResolverActivity.java:983)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.safelyStartActivity(ResolverActivity.java:772)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.onTargetSelected(ResolverActivity.java:754)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity.onTargetSelected(ChooserActivity.java:305)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ResolverActivity.startSelected(ResolverActivity.java:603)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity.startSelected(ChooserActivity.java:310)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.app.ChooserActivity$ChooserRowAdapter$2.onClick(ChooserActivity.java:990)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> android.view.View.performClick(View.java:5198) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.view.View$PerformClick.run(View.java:21147) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Handler.handleCallback(Handler.java:739) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Handler.dispatchMessage(Handler.java:95) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.os.Looper.loop(Looper.java:148) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> android.app.ActivityThread.main(ActivityThread.java:5417) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity: at
> java.lang.reflect.Method.invoke(Native Method) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity: at
> com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity: at
> com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 09-25
> 21:57:55.286 1159-1159/? I/Keyboard.Facilitator: onFinishInput() 09-25
> 21:57:55.297 32657-32676/? E/Surface: getSlotFromBufferLocked: unknown
> buffer: 0xaec352e0 09-25 21:57:55.344 325-349/? V/RenderScript:
> 0xb3693000 Launching thread(s), CPUs 4 09-25 21:57:57.290 325-349/?
> E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb3f88240
Ist dies ein bekanntes Problem mit Android M? Und was noch wichtiger ist, wie kann ich das umgehen?
Im Manifest habe ich Folgendes:
<uses-permission android:name="android.permission.CAMERA" />
Dies ist der Code, mit dem der Benutzer mit der Kamera auf ein Bild klicken und / oder ein Bild auswählen kann
public static Intent openImageIntent(Context context, Uri cameraOutputFile) {
// Camera.
final List<Intent> cameraIntents = new ArrayList<Intent>();
final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
final PackageManager packageManager = context.getPackageManager();
final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
for(ResolveInfo res : listCam) {
final String packageName = res.activityInfo.packageName;
final Intent intent = new Intent(captureIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(packageName);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraOutputFile);
cameraIntents.add(intent);
}
// Filesystem.
final Intent galleryIntent = new Intent();
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
// Chooser of filesystem options.
final Intent chooserIntent = Intent.createChooser(galleryIntent, "Take or select pic");
// Add the camera options.
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
return chooserIntent;
}
Ich rufe den openImageIntent()
auf Knopfdruck in meiner Aktivität an. Wenn ich nicht die CAMERA-Berechtigung in meiner App habe, funktioniert es einwandfrei, aber mit dieser Hinzufügung erhalte ich die oben angegebene Ausnahme.
@Override
public void onClick(View v) {
Intent picCaptureIntenet = openImageIntent(MainActivity.this, getTempImageFileUri(MainActivity.this));
try {
startActivityForResult(picCaptureIntenet, 100);
} catch(Exception e) {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
quelle
Antworten:
Ich hatte das gleiche Problem und finde dieses Dokument bei Google: https://developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE
"Hinweis: Wenn Ihre App auf M und höher abzielt und die Verwendung der nicht erteilten CAMERA-Berechtigung deklariert, führt der Versuch, diese Aktion zu verwenden, zu einer SecurityException."
Das ist wirklich komisch. Macht überhaupt keinen Sinn. Die App deklariert die Kameraberechtigung mit Absicht mit der Aktion IMAGE_CAPTURE, die gerade in SecurityException ausgeführt wird. Wenn Ihre App jedoch keine Kameraberechtigung mit Absicht mit Aktion deklariert, kann IMAGE_CAPTURE die Kamera-App ohne Probleme starten.
Die Problemumgehung besteht darin, zu überprüfen, ob die App über eine Kamera-Berechtigung im Manifest verfügt. Wenn dies der Fall ist, fordern Sie vor dem Starten der Absicht eine Kamera-Berechtigung an.
Hier können Sie überprüfen, ob die Berechtigung im Manifest enthalten ist, unabhängig davon, ob die Berechtigung erteilt wurde oder nicht.
public boolean hasPermissionInManifest(Context context, String permissionName) { final String packageName = context.getPackageName(); try { final PackageInfo packageInfo = context.getPackageManager() .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); final String[] declaredPermisisons = packageInfo.requestedPermissions; if (declaredPermisisons != null && declaredPermisisons.length > 0) { for (String p : declaredPermisisons) { if (p.equals(permissionName)) { return true; } } } } catch (NameNotFoundException e) { } return false; }
quelle
Wenn Sie das Android M-Berechtigungsmodell verwenden, müssen Sie zunächst überprüfen, ob die App zur Laufzeit über diese Berechtigung verfügt, und den Benutzer zur Laufzeit zur Eingabe dieser Berechtigung auffordern. Die Berechtigung, die Sie in Ihrem Manifest definieren, wird bei der Installation nicht automatisch erteilt.
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.CAMERA}, MY_REQUEST_CODE); }
MY_REQUEST_CODE ist eine statische Konstante, die Sie definieren können und die erneut für den Rückruf des Dialogfelds requestPermission verwendet wird.
Für das Dialogergebnis benötigen Sie einen Rückruf:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == MY_REQUEST_CODE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Now user should be able to use camera } else { // Your app will not have this permission. Turn off all functions // that require this permission or it will force close like your // original question } } }
bearbeiten
Beim Lesen aus dem Stack-Trace sieht es so aus, als hätte Google Camera die CAMERA-Berechtigung nicht aktiviert. Dies könnte schließlich tatsächlich wie eine Abwärtskompatibilitätssache aussehen.
Nehmen wir an, dass Google Camera (oder eine andere Anwendung, die Ihre ACTION-Absicht behandelt) eine bestimmte Berechtigung benötigt.
Wenn Ihre App nicht über die Berechtigung CAMERA verfügt, lässt sie Google Camera nur mit dem alten Berechtigungsmodell arbeiten.
Mit der in Ihrem Manifest deklarierten CAMERA-Berechtigung wird jedoch auch die CAMERA-Berechtigung in Google Camera (ohne Android M-Berechtigungsmodell) zur Verwendung des Android M-Berechtigungsmodells (glaube ich) erzwungen.
Wenn Sie also die oben beschriebene Methode verwenden, müssen Sie Ihre App-Berechtigung zur Laufzeit angeben. Dies bedeutet, dass die untergeordnete Aufgabe (in diesem Fall Google Camera) nun auch über diese Berechtigung verfügt.
quelle
Was Ihre Frage betrifft: "Ist dies ein bekanntes Problem in M?" Ein Google-Entwickler hat auf jemanden geantwortet, der dieses Problem als Fehler gemeldet hat.
Siehe hier: https://issuetracker.google.com/issues/37063818#comment8
Hier ist das Wort des Google-Mannes: „Dies ist ein beabsichtigtes Verhalten, um Frustrationen der Nutzer zu vermeiden, wenn sie die Kameraerlaubnis einer App widerrufen und die App weiterhin über die Absicht Fotos aufnehmen kann. Benutzern ist nicht bekannt, dass das nach dem Widerruf der Berechtigung aufgenommene Foto über einen anderen Mechanismus erfolgt und die Richtigkeit des Berechtigungsmodells in Frage stellen würde. Dies gilt für MediaStore.ACTION_IMAGE_CAPTURE, MediaStore.ACTION_VIDEO_CAPTURE und Intent.ACTION_CALL, deren Dokumente die Verhaltensänderung für Apps dokumentieren, die auf M abzielen. "
Da es Google nichts ausmacht, die Mechanismen der Verwendung der Kamera von Ihrem Nutzer zu abstrahieren, können Sie auch die erste Anforderung der Kameraerlaubnis strategisch auslösen und auf die Funktionalität der Aktivität verweisen, die die Kamera als Begründung für die Anforderung verwendet. Wenn Sie Ihrer App erlauben, diese Berechtigungsanforderung zuerst zu stellen, wenn der Benutzer lediglich versucht, ein Bild aufzunehmen, denkt der Benutzer möglicherweise, dass sich Ihre App seltsam verhält, da für das Aufnehmen eines Fotos normalerweise keine Berechtigung erforderlich ist.
quelle
Wenn Sie Google M verwenden, gehen Sie zu Einstellungen -> Apps -> Ihre App -> und geben Sie die entsprechenden Berechtigungen ein.
quelle
Ich blieb bei diesem Problem hängen und benutzte bereits die Antwort von JTY. Das Problem ist, dass irgendwann der Anforderungsberechtigungsdialog auf "Nie wieder fragen" aktiviert wurde. Ich entwickle auf SDK 24.
Mein vollständiger Code für die Verarbeitung von Berechtigungen (in meinem Fall die Kamera) lautete wie folgt:
public void checksCameraPermission(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Log.d("MyApp", "SDK >= 23"); if (this.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { Log.d("MyApp", "Request permission"); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, MY_REQUEST_CODE); if (! shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { showMessageOKCancel("You need to allow camera usage", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCompat.requestPermissions(FotoPerfil.this, new String[] {Manifest.permission.CAMERA}, MY_REQUEST_CODE); } }); } } else { Log.d("MyApp", "Permission granted: taking pic"); takePicture(); } } else { Log.d("MyApp", "Android < 6.0"); } }
dann
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) { new AlertDialog.Builder(this) .setMessage(message) .setPositiveButton("OK", okListener) .setNegativeButton("Cancel", null) .create() .show(); }
und dann
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_REQUEST_CODE: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { criarFoto(); } else { Toast.makeText(this, "You did not allow camera usage :(", Toast.LENGTH_SHORT).show(); noFotoTaken(); } return; } } }
Das beabsichtigte Verhalten ist, dass für den Fall, dass der Benutzer versehentlich "Nie wieder fragen" aktiviert hat, Ihre App hängen bleibt (der Anforderungsdialog wird nicht angezeigt) und der Benutzer sich möglicherweise frustriert fühlt. Auf diese Weise sagt ihm eine Nachricht, dass er diese Erlaubnis benötigt.
quelle
Ich entfernte:
uses-permission android:name="android.permission.CAMERA"
und verließ sich nur auf:
uses-feature android:name="android.hardware.camera" android:required="true"
in der Manifestdatei.
quelle
uses-feature
gewährt Ihnen nicht automatischuses-permission
. Es bedeutet nur, dass Sie die Kamera wollen. Werden Sie es verwenden oder wird der Zugriff darauf vom Benutzer gewährt, ist eine ganz andere Geschichte.uses-permission
weil Sie ihn habenuses-feature
?Diese Methode von mir überprüft nicht nur die Kamera, sondern alle Berechtigungen, die meine App beim Start benötigt ... Ich habe dies in meiner Helper.java-Datei. Beachten Sie auch, dass ich für den Dialog diese Bibliothek verwende: https: // github. com / afollestad / material-dialogs
///check camera permission public static boolean hasPermissions(final Activity activity){ //add your permissions here String[] AppPermissions = { Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; //ungranted permissions ArrayList<String> ungrantedPerms = new ArrayList<String>(); //loop //lets set a boolean of hasUngrantedPerm to false Boolean needsPermRequest = false; //permissionGranted int permGranted = PackageManager.PERMISSION_GRANTED; //permission required content String permRequestStr = activity.getString(R.string.the_following_perm_required); //loop for(String permission : AppPermissions){ //check if perm is granted int checkPerm = ContextCompat.checkSelfPermission(activity,permission); //if the permission is not granted if(ContextCompat.checkSelfPermission(activity,permission) != permGranted){ needsPermRequest = true; //add the permission to the ungranted permission list ungrantedPerms.add(permission); //permssion name String[] splitPerm = permission.split(Pattern.quote(".")); String permName = splitPerm[splitPerm.length-1].concat("\n"); permRequestStr = permRequestStr.concat(permName); }//end if }//end loop //if all permission is granted end exec //then continue code exec if(!needsPermRequest) { return true; }//end if //convert array list to array string final String[] ungrantedPermsArray = ungrantedPerms.toArray(new String[ungrantedPerms.size()]); //show alert Dialog requesting permission new MaterialDialog.Builder(activity) .title(R.string.permission_required) .content(permRequestStr) .positiveText(R.string.enable) .negativeText(R.string.cancel) .onPositive(new MaterialDialog.SingleButtonCallback(){ @Override public void onClick(@NonNull MaterialDialog dialog,@NonNull DialogAction which){ //request the permission now ActivityCompat.requestPermissions(activity,ungrantedPermsArray,0); } }) .show(); //return false so that code exec in that script will not be allowed //to continue return false; }//end checkPermissions
Sie werden Ihre Berechtigungslisten hier hinzufügen oder entfernen:
//add your permissions here String[] AppPermissions = { Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE };
In meiner Aktivitätsdatei überprüfe ich die Berechtigung wie folgt. In der Helper-Klasse habe ich die hasPermissions-Methode beibehalten
if(Helper.hasPermissions(this) == false){ return; }//end if
Dies bedeutet, dass wir die Ausführung nicht fortsetzen müssen, wenn keine Berechtigung erteilt wurde. Auch hier müssen wir die Berechtigungsanforderung abhören, nachdem sie abgeschlossen wurde. Fügen Sie dazu den folgenden Code zu Ihrer Aktivitätsdatei hinzu (optional).
//Listen to Permission request completion //put in your activity file @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { int permGranted = PackageManager.PERMISSION_GRANTED; Boolean permissionRequired = false; for(int perm : grantResults){ if(perm != permGranted){ permissionRequired = true; } } //if permission is still required if(permissionRequired){ //recheck and enforce permission again Helper.hasPermissions(this); }//end if }//end method
quelle
Es ist etwas spät. aber ich möchte noch etwas hinzufügen. Wenn Sie Methoden aufrufen, die Kamerafunktionen enthalten, verwenden Sie diese im try catch-Block. Wenn nicht, stürzt die App auf einigen Geräten wie dem Moto G4 Plus oder einem Plus ab.
private static final int CAMERA_REQUEST_CODE = 10; //TODO add camera opening functionality here. try { captureImage(); Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); startActivityForResult(intent,CAMERA_REQUEST_CODE); } catch (Exception e){ e.printStackTrace(); } private void captureImage(){ if( ContextCompat.checkSelfPermission(getContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{android.Manifest.permission.CAMERA}, CAMERA_REQUEST_CODE); } else { // Open your camera here. } } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == CAMERA_REQUEST_CODE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Now user should be able to use camera } else { // Your app will not have this permission. Turn off all functions // that require this permission or it will force close like your // original question } } }
PS: Stellen Sie sicher, dass Sie die überschriebene Methode nicht kopieren und einfügen.
quelle
Sie müssen die App-Berechtigung für die Verwendung der Kamera aktivieren. Ich bevorzuge es, diesen Befehl add method hinzuzufügen, der die Kamera aktiviert:
public static async Task<bool> HasPermission() { var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera); if (status == PermissionStatus.Granted) return true; if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Camera)) { ShowDialogOk("Error", "Please allow access to the camera.");//that is my custom method for allert } var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Camera); status = results[Permission.Camera]; return status == PermissionStatus.Granted; }
quelle