Android ACTION_IMAGE_CAPTURE Absicht

163

Wir versuchen, die native Kamera-App zu verwenden, damit der Benutzer ein neues Bild aufnehmen kann. Es funktioniert einwandfrei, wenn wir das weglassen EXTRA_OUTPUT extraund das kleine Bitmap-Bild zurückgeben. Wenn wir jedochputExtra(EXTRA_OUTPUT,...) die Absicht haben, bevor es starten, funktioniert alles, bis Sie versuchen, die "Ok" -Taste in der Kamera-App zu drücken. Die "Ok" -Taste macht einfach nichts. Die Kamera-App bleibt geöffnet und nichts blockiert. Wir können es abbrechen, aber die Datei wird nie geschrieben. Was genau müssen wir tun, um ACTION_IMAGE_CAPTUREdas aufgenommene Bild in eine Datei zu schreiben?

Bearbeiten: Dies erfolgt über die MediaStore.ACTION_IMAGE_CAPTUREAbsicht, nur um klar zu sein

Drew
quelle

Antworten:

144

Dies ist ein gut dokumentierter Fehler in einigen Versionen von Android. Das heißt, bei Google Experience Builds von Android funktioniert die Bilderfassung nicht wie dokumentiert. Was ich allgemein verwendet habe, ist so etwas in einer Utilities-Klasse.

public boolean hasImageCaptureBug() {

    // list of known devices that have the bug
    ArrayList<String> devices = new ArrayList<String>();
    devices.add("android-devphone1/dream_devphone/dream");
    devices.add("generic/sdk/generic");
    devices.add("vodafone/vfpioneer/sapphire");
    devices.add("tmobile/kila/dream");
    devices.add("verizon/voles/sholes");
    devices.add("google_ion/google_ion/sapphire");

    return devices.contains(android.os.Build.BRAND + "/" + android.os.Build.PRODUCT + "/"
            + android.os.Build.DEVICE);

}

Wenn ich dann die Bilderfassung starte, erstelle ich eine Absicht, die nach dem Fehler sucht.

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
if (hasImageCaptureBug()) {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("/sdcard/tmp")));
} else {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(i, mRequestCode);

dann mache ich in der Aktivität, zu der ich zurückkehre, verschiedene Dinge basierend auf dem Gerät.

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
     switch (requestCode) {
         case GlobalConstants.IMAGE_CAPTURE:
             Uri u;
             if (hasImageCaptureBug()) {
                 File fi = new File("/sdcard/tmp");
                 try {
                     u = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(), fi.getAbsolutePath(), null, null));
                     if (!fi.delete()) {
                         Log.i("logMarker", "Failed to delete " + fi);
                     }
                 } catch (FileNotFoundException e) {
                     e.printStackTrace();
                 }
             } else {
                u = intent.getData();
            }
    }

Dies erspart Ihnen das Schreiben einer neuen Kamera-App, aber dieser Code ist auch nicht besonders gut. Die großen Probleme sind

  1. Mit dem Fehler erhalten Sie niemals Bilder in voller Größe von den Geräten. Sie erhalten Bilder mit einer Breite von 512 Pixel, die in den Bildinhaltsanbieter eingefügt werden. Auf Geräten ohne Fehler funktioniert alles als Dokument, Sie erhalten ein großes normales Bild.

  2. Sie müssen die Liste pflegen. Wie geschrieben, ist es möglich, dass Geräte mit einer Android-Version (z. B. den Builds von Cyanogenmod ) geflasht werden, bei der der Fehler behoben ist. In diesem Fall stürzt Ihr Code ab. Das Update besteht darin, den gesamten Gerätefingerabdruck zu verwenden.

yanokwa
quelle
21
auf Galaxy S: intent.getData () gibt null zurück, und außerdem fügt die Kamera-App auf Galaxy s selbst ein neues Foto in die Galerie ein, aber es wird kein Uri zurückgegeben. Wenn Sie also ein Foto aus einer Datei in die Galerie einfügen, wird das Foto dupliziert.
Arseniy
1
Hey, mein Gerät ist hier nicht aufgeführt und ich habe es auf Ihre Weise implementiert. Aber meine Anwendung stürzt immer noch ab. Ich kenne den Grund nicht. Bitte helfen Sie mir
Geetanjali
Verwenden dieses Codes auf Droid-X- und Sony Xpheria-Geräten. Beide Geräte geben den Intent-Wert als null zurück. Außerdem gibt droidx den Ergebniscode als 0 zurück, während xpheria den Ergebniscode als -1 zurückgibt. Jeder weiß, warum das so sein sollte.
rOrlig
5
Der Link zu dem "gut dokumentierten Fehler", der am Anfang von Yanokwas Antwort steht, ist falsch. Bei diesem Fehler geht es darum, die Kamera-App ohne putExtra (EXTRA_OUTPUT, ...) aufzurufen
Frank Harper
code.google.com/p/android/issues/detail?id=1480 Wie erklären Sie das dann?
Pencilcheck
50

Ich weiß, dass dies bereits beantwortet wurde, aber ich weiß, dass viele Leute darüber gestolpert sind, deshalb werde ich einen Kommentar hinzufügen.

Ich hatte genau das gleiche Problem auf meinem Nexus One. Dies war aus der Datei, die vor dem Start der Kamera-App nicht auf der Festplatte vorhanden war. Daher habe ich sichergestellt, dass die Datei, die vor dem Start der Kamera-App vorhanden war. Hier ist ein Beispielcode, den ich verwendet habe:

String storageState = Environment.getExternalStorageState();
        if(storageState.equals(Environment.MEDIA_MOUNTED)) {

            String path = Environment.getExternalStorageDirectory().getName() + File.separatorChar + "Android/data/" + MainActivity.this.getPackageName() + "/files/" + md5(upc) + ".jpg";
            _photoFile = new File(path);
            try {
                if(_photoFile.exists() == false) {
                    _photoFile.getParentFile().mkdirs();
                    _photoFile.createNewFile();
                }

            } catch (IOException e) {
                Log.e(TAG, "Could not create file.", e);
            }
            Log.i(TAG, path);

            _fileUri = Uri.fromFile(_photoFile);
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE );
            intent.putExtra( MediaStore.EXTRA_OUTPUT, _fileUri);
            startActivityForResult(intent, TAKE_PICTURE);
        }   else {
            new AlertDialog.Builder(MainActivity.this)
            .setMessage("External Storeage (SD Card) is required.\n\nCurrent state: " + storageState)
            .setCancelable(true).create().show();
        }

Ich erstelle zuerst einen eindeutigen (etwas) Dateinamen mit einem MD5-Hash und lege ihn in den entsprechenden Ordner. Ich überprüfe dann, ob es existiert (sollte nicht, aber es ist eine gute Praxis, es trotzdem zu überprüfen). Wenn es nicht existiert, erhalte ich das übergeordnete Verzeichnis (einen Ordner) und erstelle die Ordnerhierarchie bis dahin (wenn also die Ordner, die zum Speicherort der Datei führen, nicht existieren, werden sie nach dieser Zeile angezeigt. Danach Ich erstelle die Datei. Sobald die Datei erstellt ist, erhalte ich den Uri und übergebe ihn an die Absicht. Dann funktioniert die Schaltfläche OK wie erwartet und alles ist golden.

Wenn Sie nun in der Kamera-App auf die Schaltfläche OK klicken, befindet sich die Datei am angegebenen Speicherort. In diesem Beispiel wäre dies /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg

Es ist nicht erforderlich, die Datei in das oben angegebene "onActivityResult" zu kopieren.

Donn Felker
quelle
13
Stellen Sie sicher, dass Sie <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />in Ihrem Manifest haben, wenn Sie neuer als Android 1.5 verwenden - haben ein paar Stunden damit verbracht, das Kamera-Zeug zu debuggen, und dies war nicht enthalten, so dass meine Speicherungen immer fehlschlagen würden.
Swanson
Das funktioniert gut für mich, aber alle 10 Bilder oder so wird die leere Datei nicht überschrieben und ich erhalte eine 0-Byte-Datei, in der sich das Bild befinden sollte. Es ist sehr nervig und schwer zu finden.
joey_g216
2
Auf einigen Geräten, wie dem Nexus 7 mit Jelly Bean, müssen Sie Environment.getExternalStorageDirectory (). getName () ändern, um .getAbsolutePath () anstelle von .getName () zu verwenden
Keith
35

Ich habe eine Reihe von Fotoerfassungsstrategien durchlaufen, und es scheint immer einen Fall zu geben, eine Plattform oder bestimmte Geräte, bei denen einige oder alle der oben genannten Strategien auf unerwartete Weise fehlschlagen. Ich konnte eine Strategie finden, die den folgenden URI-Generierungscode verwendet, der in den meisten, wenn nicht allen Fällen zu funktionieren scheint.

mPhotoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
            new ContentValues());
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoUri);
startActivityForResult(intent,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE_CONTENT_RESOLVER);

Um einen weiteren Beitrag zur Diskussion zu leisten und Neulingen zu helfen, habe ich eine Beispiel- / Test-App erstellt, die verschiedene Strategien für die Implementierung der Fotoaufnahme zeigt. Beiträge anderer Implementierungen werden definitiv empfohlen, um die Diskussion zu ergänzen.

https://github.com/deepwinter/AndroidCameraTester

Tiefwinter
quelle
Das war großartig. Ich habe dies noch nicht auf allen meinen Geräten getestet, aber ich gehe davon aus, dass Sie dies bereits getan haben. Es gibt so viel Lärm zu diesem Thema und diese spezielle Antwort ist so einfach und sauber. Funktioniert bisher auf Nexus 5, wo ich zum ersten Mal viele Berichte erhielt, dass ACTION_IMAGE_CAPTURE ohne EXTRA_OUTPUT nicht funktioniert hat. Ich hoffe, dass dies auf ganzer Linie funktioniert, aber bisher so gut. Thx
Rich
1
@dewwinter - Vielen Dank, Sir. Ich habe mir die Haare ausgezogen, aber Ihre Content Resolver-Lösung funktioniert in allen problematischen Szenarien, die ich hatte, gut.
Billy Coover
Etwas spät zu dieser Party, aber dies war die einzige Lösung, die für mich funktioniert hat. Vielen Dank!
StackJP
Kann ich den MediaStore.EXTRA_OUTPUT irgendwie abrufen onActivityResultoder muss ich mich wirklich daran erinnern? Ich müsste es dauerhaft speichern, damit sich meine App sogar daran erinnert, wenn es zerstört wird, während ein Foto aufgenommen wird ...
prom85
Es funktioniert nicht in Samsung Galaxy Note 3 mit lolipop: ava.lang.NullPointerException: Versuch, die virtuelle Methode 'java.lang.String android.net.Uri.getScheme ()' für eine Nullobjektreferenz aufzurufen
Igor Janković
30

Ich hatte das gleiche Problem, bei dem die Schaltfläche OK in der Kamera-App nichts tat, sowohl auf dem Emulator als auch auf dem Nexus 1.

Das Problem wurde behoben, nachdem ein sicherer Dateiname ohne Leerzeichen und ohne Sonderzeichen in angegeben MediaStore.EXTRA_OUTPUT wurde. Wenn Sie außerdem eine Datei angeben, die sich in einem Verzeichnis befindet, das noch nicht erstellt wurde, müssen Sie sie zuerst erstellen. Die Kamera-App erledigt mkdir nicht für Sie.

Yenchi
quelle
1
Und stellen Sie sicher, dass Sie mkdirs()anstelle von verwenden, mkdir()wenn Ihr Pfad mehr als eine Verzeichnisebene hat und Sie möchten, dass alle erstellt werden ( mkdirerstellt nur die letzte, das 'Blatt'-Verzeichnis). Ich hatte einige Probleme damit und diese Antwort half mir.
Diana
Können wir einfache Zeitstempel verwenden? oder kann das auch einige fehler machen ??
Bitte
@ user2247689 Ich bin mir nicht sicher über den Dateinamen, den Sie mit "einfachen Zeitstempeln" erstellt haben, aber solange sie keine Sonderzeichen enthalten, die vom FAT-Format auf der SD-Karte nicht zugelassen sind, sollte es meiner Meinung nach in Ordnung sein.
Yenchi
28

Der von Ihnen beschriebene Workflow sollte so funktionieren, wie Sie ihn beschrieben haben. Es könnte hilfreich sein, wenn Sie uns den Code zur Erstellung der Absicht zeigen könnten. Im Allgemeinen sollten Sie mit dem folgenden Muster das tun können, was Sie versuchen.

private void saveFullImage() {
  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
  outputFileUri = Uri.fromFile(file);
  intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
  startActivityForResult(intent, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if ((requestCode == TAKE_PICTURE) && (resultCode == Activity.RESULT_OK)) {
    // Check if the result includes a thumbnail Bitmap
    if (data == null) {    
      // TODO Do something with the full image stored
      // in outputFileUri. Perhaps copying it to the app folder
    }
  }
}

Beachten Sie, dass es ist die Kamera Aktivität, die die Datei wird das Erstellen und Speichern, und es ist nicht wirklich Teil Ihrer Anwendung, so dass es keine Schreibrechte für Ihren Anwendungsordner haben. Um eine Datei in Ihrem App-Ordner zu speichern, erstellen Sie eine temporäre Datei auf der SD-Karte und verschieben Sie sie in Ihren App-Ordner im onActivityResultHandler.

Reto Meier
quelle
Dies / sollte / funktionieren, aber die OK-Schaltfläche der Kamera-App macht einfach nichts. Wir haben es geschafft, auf die SD-Karte zu schreiben, daher vermute ich, dass dies ein Problem mit den Dateiberechtigungen ist (obwohl ich dachte, dass eine Anwendung automatisch Schreibberechtigungen für ihr persönliches Verzeichnis hat). Danke für die Hilfe!
Drew
1
Ihre App hat Schreibberechtigung für ihr persönliches Verzeichnis - die Kamera-App (die das Bild aufnimmt) nicht. Sie können die Datei jedoch in onActivityResult verschieben, da dies in Ihrer App enthalten ist. Alternativ können Sie eine Kameraaktivität selbst implementieren, aber das scheint übertrieben.
Reto Meier
Das Wiederherstellen der Kamera-App scheint ein gängiger, aber langwieriger Ansatz zu sein. Wenn wir getDir ("images", MODE_WORLD_WRITEABLE) verwenden, gilt diese Berechtigung nicht für Dateien, die in diesem Verzeichnis erstellt werden sollen?
Drew
Nicht unbedingt. Die Berechtigung gilt für den Ordner, nicht unbedingt für die darin enthaltenen Dateien.
Reto Meier
Ich habe den Code mit Android-Geräten mit Android 2.2 und neuer getestet und alle haben das Bild im angegebenen Pfad gespeichert. Ich denke also, dass dies Standardverhalten sein sollte, um Bilder zu erhalten.
Janusz
7

Um den obigen Kommentar von Yenchi zu verfolgen, führt die Schaltfläche OK auch nichts aus, wenn die Kamera-App nicht in das betreffende Verzeichnis schreiben kann.

Das bedeutet, dass Sie die Datei nicht an einem Ort erstellen können, der nur von Ihrer Anwendung beschreibbar ist (z. B. etwas unter getCacheDir())Etwas untergetExternalFilesDir() funktionieren.

Es wäre schön, wenn die Kamera-App eine Fehlermeldung in die Protokolle drucken würde, wenn sie nicht in den angegebenen EXTRA_OUTPUTPfad schreiben könnte , aber ich habe keine gefunden.

Joe
quelle
4

Damit die Kamera auf SD-Karte schreibt, aber in der Galerie-App ein neues Album enthält, verwende ich Folgendes:

 File imageDirectory = new File("/sdcard/signifio");
          String path = imageDirectory.toString().toLowerCase();
           String name = imageDirectory.getName().toLowerCase();


            ContentValues values = new ContentValues(); 
            values.put(Media.TITLE, "Image"); 
            values.put(Images.Media.BUCKET_ID, path.hashCode());
            values.put(Images.Media.BUCKET_DISPLAY_NAME,name);

            values.put(Images.Media.MIME_TYPE, "image/jpeg");
            values.put(Media.DESCRIPTION, "Image capture by camera");
           values.put("_data", "/sdcard/signifio/1111.jpg");
         uri = getContentResolver().insert( Media.EXTERNAL_CONTENT_URI , values);
            Intent i = new Intent("android.media.action.IMAGE_CAPTURE"); 

            i.putExtra(MediaStore.EXTRA_OUTPUT, uri);

            startActivityForResult(i, 0); 

Bitte beachten Sie, dass Sie jedes Mal einen eindeutigen Dateinamen generieren und die von mir geschriebene 1111.jpg ersetzen müssen. Dies wurde mit Nexus 1 getestet. Die URL wird in der privaten Klasse deklariert, sodass ich das Bild nach dem Aktivitätsergebnis bei Bedarf aus der URL in imageView laden kann, um eine Vorschau anzuzeigen.

Nabulaer
quelle
Woher kommt die Spalte "_data"? Gibt es keine Konstante dafür?
Matthias
3
ah, es ist developer.android.com/reference/android/provider/… Sie sollten wirklich keine magischen Zeichenfolgen im Code verwenden.
Matthias
1

Ich hatte das gleiche Problem und habe es mit folgendem behoben:

Das Problem ist, dass wenn Sie eine Datei angeben, auf die nur Ihre App Zugriff hat (z. B. durch Aufrufen) getFileStreamPath("file"); )

Deshalb habe ich nur sichergestellt, dass die angegebene Datei wirklich existiert und dass JEDER Schreibzugriff darauf hat.

Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File outFile = getFileStreamPath(Config.IMAGE_FILENAME);
outFile.createNewFile();
outFile.setWritable(true, false);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,Uri.fromFile(outFile));
startActivityForResult(intent, 2);

Auf diese Weise hat die Kamera-App Schreibzugriff auf den angegebenen Uri und die OK-Taste funktioniert einwandfrei :)

Goddchen
quelle
AndroidRuntime (2.1 Emulator) löst NoSuchMethodError: java.io.File.setWritable
jwadsack
1

Ich empfehle Ihnen, dem Android-Trainning-Beitrag zu folgen, um ein Foto aufzunehmen. Sie zeigen in einem Beispiel, wie man kleine und große Bilder macht. Sie können auch den Quellcode herunterladen von hier

JuanVC
quelle
0

Ich habe eine einfache Bibliothek erstellt, die die Auswahl von Bildern aus verschiedenen Quellen (Galerie, Kamera) verwaltet, sie möglicherweise an einem Ort (SD-Karte oder interner Speicher) speichert und das Bild zurückgibt. Bitte verwenden Sie es kostenlos und verbessern Sie es - Android-ImageChooser .

svenkapudija
quelle
Ihr Link funktioniert nicht mehr !! Ich habe es vor 2 Wochen gesehen und wollte es verwenden :(
Mohammad Ersan
0

Die Datei muss von der Kamera beschreibbar sein, wie Praveen betonte.

In meiner Verwendung wollte ich die Datei im internen Speicher speichern. Ich habe das gemacht mit:

Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (i.resolveActivity(getPackageManager()!=null)){
    try{
        cacheFile = createTempFile("img",".jpg",getCacheDir());
        cacheFile.setWritavle(true,false);
    }catch(IOException e){}
    if(cacheFile != null){
        i.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(cacheFile));
        startActivityForResult(i,REQUEST_IMAGE_CAPTURE);
    }
}

Hier cacheFileist eine globale Datei, die verwendet wird, um auf die geschriebene Datei zu verweisen. Dann ist in der Ergebnismethode die zurückgegebene Absicht null. Dann sieht die Methode zur Verarbeitung der Absicht folgendermaßen aus:

protected void onActivityResult(int requestCode,int resultCode,Intent data){
    if(requestCode != RESULT_OK){
        return;
    }
    if(requestCode == REQUEST_IMAGE_CAPTURE){
        try{
            File output = getImageFile();
            if(output != null && cacheFile != null){
                copyFile(cacheFile,output);

                //Process image file stored at output

                cacheFile.delete();
                cacheFile=null;
            }
        }catch(IOException e){}
    }
}

Hier getImageFile()ist eine Dienstprogrammmethode zum Benennen und Erstellen der Datei, in der das Bild gespeichert werden soll, und copyFile()eine Methode zum Kopieren einer Datei.

Duncan
quelle
0

Sie können diesen Code verwenden

private Intent getCameraIntent() {

    PackageManager packageManager = mContext.getPackageManager();
    List<ApplicationInfo> list = packageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
    Intent main = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    List<ResolveInfo> launchables = packageManager.queryIntentActivities(main, 0);
    if (launchables.size() == 1)
        return packageManager.getLaunchIntentForPackage(launchables.get(0).activityInfo.packageName);
    else
        for (int n = 0; n < list.size(); n++) {
            if ((list.get(n).flags & ApplicationInfo.FLAG_SYSTEM) == 1) {
                Log.d("TAG", "Installed Applications  : " + list.get(n).loadLabel(packageManager).toString());
                Log.d("TAG", "package name  : " + list.get(n).packageName);
                String defaultCameraPackage = list.get(n).packageName;


                if (launchables.size() > 1)
                    for (int i = 0; i < launchables.size(); i++) {
                        if (defaultCameraPackage.equals(launchables.get(i).activityInfo.packageName)) {
                            return packageManager.getLaunchIntentForPackage(defaultCameraPackage);
                        }
                    }
            }
        }
    return null;
}
Ngông Cuồng
quelle
0

Es ist sehr einfach, dieses Problem mit dem Aktivitätsergebniscode zu lösen. Probieren Sie diese Methode einfach aus

if (reqCode == RECORD_VIDEO) {
   if(resCode == RESULT_OK) {
       if (uri != null) {
           compress();
       }
    } else if(resCode == RESULT_CANCELED && data!=null){
       Toast.makeText(MainActivity.this,"No Video Recorded",Toast.LENGTH_SHORT).show();
   }
}
Savita
quelle
Das obige unvollständige Snippet verdient nicht die von ihm behauptete Einfachheit. Sicherlich gibt es versteckte Komplexitäten für den unbekannten Reisenden. B. das Scannen auf Vorhandensein oder Nichtvorhandensein der Datei. Zum Beispiel ist es für die App öffentlich verfügbar oder privat. Zum Beispiel die Notwendigkeit eines Anbieters oder das Erhalten einer Größe nur für Miniaturansichten. Dies sind Informationslücken, die der unachtsame Reisende beachten muss.
Truthadjustr
0
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_CANCELED)
    {
        //do not process data, I use return; to resume activity calling camera intent
        enter code here
    }
}
Ajesh Gupta
quelle