web-dev-qa-db-de.com

Android ACTION_IMAGE_CAPTURE Absicht

Wir versuchen, die native Kamera-App zu verwenden, damit der Benutzer ein neues Bild aufnehmen kann. Es funktioniert gut, wenn wir den EXTRA_OUTPUT extra weglassen und das kleine Bitmap-Bild zurückgeben. Wenn wir uns jedoch vor dem Start putExtra(EXTRA_OUTPUT,...) mit der Absicht beschäftigen, funktioniert alles, bis Sie versuchen, in der Kamera-App auf "OK" zu klicken. Die "Ok" -Taste macht einfach nichts. Die Kamera-App bleibt geöffnet und nichts blockiert. Wir können abbrechen, aber die Datei wird niemals geschrieben. Was genau müssen wir tun, damit ACTION_IMAGE_CAPTURE das aufgenommene Bild in eine Datei schreibt?

Bearbeiten: Dies geschieht über die Absicht MediaStore.ACTION_IMAGE_CAPTURE, um nur klar zu sein

155
Drew

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

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 Image Capture 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);

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

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 auch dieser Code ist nicht besonders gut. die großen probleme sind

  1. sie erhalten nie Bilder in voller Größe von den Geräten mit dem Fehler. Sie erhaltenBilder, die 512px breit sind, dass in den Bildinhalt Anbieter eingefügt werden. Auf Geräten ohne den 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 Version von____Android (sagen wir cyanogenmods builds ) geflasht wird, die den Fehler behoben hat .. Ihr Code wird abstürzen. Die Lösung besteht darin, den gesamten Fingerabdruck des Geräts zu verwenden.

143
yanokwa

Ich weiß, dass dies schon früher beantwortet wurde, aber ich weiß, dass viele Leute in die Sache geraten, also werde ich einen Kommentar hinzufügen. 

Ich hatte genau das gleiche Problem auf meinem Nexus One. Dies war von der Datei, die vor dem Start der Kamera-App nicht auf der Festplatte vorhanden war. Daher habe ich sichergestellt, dass die Datei vor dem Start der Kamera-App vorhanden ist. 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 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 trotzdem empfehlenswert). Wenn es nicht existiert, erhalte ich das übergeordnete Verzeichnis (einen Ordner) und erstelle die Ordnerhierarchie (wenn also die Ordner, die zum Speicherort der Datei führen, nicht existieren, werden sie nach dieser Zeile angezeigt.) Ich erstelle die Datei. Sobald die Datei erstellt wurde, bekomme ich den Uri und übergebe ihn an die Absicht, und der OK-Button funktioniert wie erwartet und alles ist golden. 

Wenn nun in der Kamera-App die Taste Ok gedrückt wird, befindet sich die Datei am angegebenen Ort. In diesem Beispiel wäre dies /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg

Es ist nicht erforderlich, die Datei in "onActivityResult" wie oben angegeben zu kopieren.

50
Donn Felker

Ich habe eine Reihe von Fotoaufnahmestrategien durchlaufen, und es scheint immer einen Fall, eine Plattform oder bestimmte Geräte zu geben, bei denen einige oder alle der oben genannten Strategien auf unerwartete Weise versagen. 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 weiter zur Diskussion beizutragen und Neulingen zu helfen, habe ich eine Beispiel-/Test-App erstellt, die verschiedene Strategien für die Foto-Capture-Implementierung zeigt. Beiträge anderer Implementierungen werden auf jeden Fall ermutigt, zur Diskussion beizutragen.

https://github.com/deepwinter/AndroidCameraTester

31
deepwinter

Ich hatte das gleiche Problem, wo die OK-Taste in der Kamera-App nichts tat, sowohl auf dem Emulator als auch auf Nexus One.

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

29
Yenchi

Der von Ihnen beschriebene Workflow sollte so funktionieren, wie Sie ihn beschrieben haben. Es könnte hilfreich sein, wenn Sie uns den Code für die Erstellung der Absicht zeigen könnten. Im Allgemeinen sollten Sie mit dem folgenden Muster tun, 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 die Datei Camera Activity die Datei erstellt und speichert. Sie ist nicht Teil Ihrer Anwendung und verfügt daher nicht über Schreibrechte für Ihren Anwendungsordner. 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 onActivityResult-Handler.

28
Reto Meier

Um auf den obigen Kommentar von Yenchi einzugehen, macht die Schaltfläche OK auch nichts, 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 beschrieben werden kann (z. B. etwas unter getCacheDir()). Etwas unter getExternalFilesDir() sollte jedoch funktionieren.

Es wäre schön, wenn die Kamera-App eine Fehlermeldung an die Protokolle ausgab, wenn sie nicht in den angegebenen EXTRA_OUTPUT-Pfad schreiben konnte, aber ich keine gefunden habe.

7
Joe

um die Kamera auf die SD-Karte schreiben zu lassen, aber ein neues Album in der Galerie-App zu speichern, 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 1111.jpg ersetzen müssen, die ich geschrieben habe. Dies wurde mit Nexus Eins getestet. Der Uri wird in der privaten Klasse deklariert Ich kann das Bild bei Bedarf von der uri zur Vorschau in imageView laden.

4
nabulaer

Ich empfehle Ihnen, den Android-Trainingsposten für die Aufnahme eines Fotos zu befolgen. Sie zeigen in einem Beispiel, wie kleine und große Bilder gemacht werden. Sie können den Quellcode auch von hier herunterladen.

2
JuanVC

Es ist sehr einfach, dieses Problem mit dem Aktivitätsergebniscode zu lösen 

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

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

Wenn Sie eine Datei angeben, auf die nur Ihre App Zugriff hat (z. B. durch Aufruf von getFileStreamPath("file");)

Deshalb habe ich nur sichergestellt, dass die angegebene Datei wirklich existiert und JEDER über Schreibzugriff verfügt.

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 der OK-Button funktioniert einwandfrei :)

1
Goddchen
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
    }
}
0
Ajesh Gupta

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

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

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 ist cacheFile eine globale Datei, die verwendet wird, um auf die Datei zu verweisen, die geschrieben wird ..__ In der Ergebnismethode ist der zurückgegebene Intent null . Dann sieht die Methode zur Verarbeitung des Intents 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 ist getImageFile() eine Dienstprogrammmethode zum Benennen und Erstellen der Datei, in der das Image gespeichert werden soll, und copyFile() ist eine Methode zum Kopieren einer Datei.

0
Duncan

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;
}
0
Ngông Cuồng

Ich habe eine einfache Bibliothek erstellt, die es verwaltet, Bilder aus verschiedenen Quellen (Galerie, Kamera) auszuwählen, sie möglicherweise an einem bestimmten Ort (SD-Karte oder interner Speicher) zu speichern und das Bild zurückzusenden, damit Sie es verwenden und verbessern können - Android-ImageChooser .

0
svenkapudija