web-dev-qa-db-fra.com

Android ACTION_IMAGE_CAPTURE Intention

Nous essayons d'utiliser l'application native de l'appareil photo pour permettre à l'utilisateur de prendre une nouvelle photo. Cela fonctionne très bien si nous omettons le EXTRA_OUTPUT extra et retournons la petite image Bitmap. Cependant, si nous putExtra(EXTRA_OUTPUT,...) sur l'intention avant de commencer, tout fonctionne jusqu'à ce que vous essayiez d'appuyer sur le bouton "Ok" dans l'application Appareil photo. Le bouton "Ok" ne fait rien. L'application de la caméra reste ouverte et rien ne se bloque. Nous pouvons annuler cette opération, mais le fichier n'est jamais écrit. Que devons-nous faire exactement pour que ACTION_IMAGE_CAPTURE écrive la photo prise dans un fichier?

Edit: Ceci est fait via l'intention MediaStore.ACTION_IMAGE_CAPTURE, juste pour être clair

155
Drew

c'est un bogue bien documenté dans certaines versions d'Android. Autrement dit, sur les versions d'expérience Google d'Android, la capture d'image ne fonctionne pas comme décrit. ce que j'ai généralement utilisé est quelque chose comme ça dans une classe d'utilitaires.

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

}

puis, lorsque je lance la capture d'image, je crée une intention qui vérifie le bogue.

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

puis dans l'activité que je retourne à, je fais différentes choses en fonction de l'appareil.

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

cela vous évite d'avoir à écrire une nouvelle application pour appareil photo, mais ce code n'est pas génial non plus. les gros problèmes sont

  1. vous ne recevez jamais d'images en taille réelle de les appareils avec le bogue. vous obtenez des images larges de 512 pixels qui sont insérées dans le fournisseur de contenu image sur les appareils sans le bogue , tout fonctionne comme un document, vous obtenez une grande image normale.

  2. vous devez maintenir la liste. comme écrit, il est possible que les appareils soient flashés avec une version de Android (par exemple, cyanogenmod builds ) qui corrige le bogue . si cela se produit, votre code va se bloquer. le correctif consiste à utiliser l'intégralité de l'empreinte digitale du périphérique

143
yanokwa

Je sais qu'on a déjà répondu à cette question, mais je sais que beaucoup de gens ont été pris au piège, alors je vais ajouter un commentaire. 

J'ai eu exactement ce même problème sur mon Nexus One. Cela provenait du fichier qui n'existait pas sur le disque avant le démarrage de l'application de l'appareil photo. Par conséquent, je me suis assuré que le fichier existant auparavant démarrait l'application Appareil photo. Voici un exemple de code que j'ai utilisé: 

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

Je crée d'abord un nom de fichier unique (quelque peu) à l'aide d'un hachage MD5 et le place dans le dossier approprié. Je vérifie ensuite s'il existe (il ne devrait pas, mais c'est une bonne pratique de vérifier quand même). S'il n'existe pas, j'obtiens le répertoire parent (un dossier) et crée la hiérarchie des dossiers jusqu'à celui-ci (par conséquent, si les dossiers menant à l'emplacement du fichier n'existent pas, ils seront placés après cette ligne. Ensuite, Je crée le fichier. Une fois le fichier créé, je récupère l'URI et le passe à l'intention, puis le bouton OK fonctionne comme prévu et tout est en or. 

À présent, lorsque vous appuyez sur le bouton Ok dans l'application Appareil photo, le fichier sera présent à l'emplacement indiqué. Dans cet exemple, il s'agirait de /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg

Il n'est pas nécessaire de copier le fichier dans "onActivityResult" comme indiqué ci-dessus.

50
Donn Felker

Je suis passé par plusieurs stratégies de capture de photo, et il semble toujours y avoir un cas, une plate-forme ou certains dispositifs, où tout ou partie des stratégies ci-dessus vont échouer de manière inattendue. J'ai pu trouver une stratégie qui utilise le code de génération d'URI ci-dessous, qui semble fonctionner dans la plupart des cas, voire dans tous les cas.

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

Pour contribuer davantage à la discussion et aider les nouveaux arrivants, j'ai créé un exemple/application de test qui présente plusieurs stratégies différentes pour la mise en œuvre de la capture de photos. Les contributions d'autres implémentations sont certainement encouragées à ajouter à la discussion.

https://github.com/deepwinter/AndroidCameraTester

31
deepwinter

J'ai eu le même problème où le bouton OK dans l'application de la caméra n'a rien fait, à la fois sur l'émulateur et sur le lien un.

Le problème a disparu après la spécification d'un nom de fichier sûr, sans espaces, sans caractères spéciaux, dans MediaStore.EXTRA_OUTPUT. De même, si vous spécifiez un fichier qui réside dans un répertoire qui n'a pas encore été créé, vous devez d'abord le créer. Appareil photo ne fait pas mkdir pour vous.

29
Yenchi

Le flux de travail que vous décrivez devrait fonctionner comme vous l'avez décrit. Cela pourrait aider si vous pouviez nous montrer le code autour de la création de l'intention. En général, le modèle suivant devrait vous permettre de faire ce que vous essayez.

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
    }
  }
}

Notez que c’est l’activité Caméra qui va créer et enregistrer le fichier. Elle ne fait pas partie de votre application. Par conséquent, elle n’est pas autorisée en écriture dans votre dossier d’application. Pour enregistrer un fichier dans votre dossier d'application, créez un fichier temporaire sur la carte SD et déplacez-le dans votre dossier d'application dans le gestionnaire onActivityResult.

28
Reto Meier

Pour faire suite au commentaire de Yenchi ci-dessus, le bouton OK ne fera rien non plus si l'application Appareil photo ne peut pas écrire dans le répertoire en question. 

Cela signifie que vous ne pouvez pas créer le fichier dans un emplacement uniquement accessible en écriture par votre application (par exemple, quelque chose sous getCacheDir()). Quelque chose sous getExternalFilesDir() devrait fonctionner, cependant.

Ce serait bien si l'application de la caméra imprimait un message d'erreur aux journaux si elle ne pouvait pas écrire dans le chemin EXTRA_OUTPUT spécifié, mais je n'en ai pas trouvé.

7
Joe

pour que l'appareil photo écrive sur sdcard tout en conservant un nouvel album sur l'application de la galerie, j'utilise ceci:

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

Veuillez noter que vous devrez générer un nom de fichier unique à chaque fois et remplacer le 1111.jpg que j'ai écrit . Ceci a été testé avec nexus un . L'uri est déclaré dans la classe privée, ainsi le résultat d'activité I Je suis en mesure de charger l'image de l'uri dans imageView pour un aperçu si nécessaire.

4
nabulaer

Je vous recommande de suivre le post de formation Android pour capturer une photo. Ils montrent dans un exemple comment prendre des photos petites et grandes. Vous pouvez également télécharger le code source depuis ici

2
JuanVC

C'est très simple de résoudre ce problème avec Activity Result Code Simple essayez cette méthode 

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

J'ai eu le même problème et je l'ai corrigé avec ce qui suit:

Le problème est que lorsque vous spécifiez un fichier auquel seule votre application a accès (par exemple, en appelant getFileStreamPath("file");).

C'est pourquoi je viens de m'assurer que le fichier donné existe réellement et que TOUT LE MONDE y a un accès en écriture.

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

De cette façon, l'application de la caméra a un accès en écriture à l'URI donné et le bouton OK fonctionne bien :)

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

Le fichier doit pouvoir être écrit par la caméra, comme l'a souligné Praveen .

Dans mon utilisation, je voulais stocker le fichier dans la mémoire interne. J'ai fait ça avec:

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);
    }
}

Ici cacheFile est un fichier global utilisé pour faire référence au fichier qui est écrit . Ensuite, dans la méthode result, l'intention renvoyée est null . La méthode de traitement de l'intention ressemble alors à:

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){}
    }
}

Ici, getImageFile() est une méthode utilitaire permettant de nommer et de créer le fichier dans lequel l'image doit être stockée, et copyFile() est une méthode permettant de copier un fichier.

0
Duncan

Vous pouvez utiliser ce code 

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

J'ai créé une bibliothèque simple qui va gérer le choix des images provenant de différentes sources (Galerie, Appareil photo), peut-être l'enregistrer dans un emplacement particulier (carte SD ou mémoire interne) et la restituer, donc n'hésitez pas à l'utiliser et à l'améliorer - Android -ImageChooser .

0
svenkapudija