66 votes

Suppression d'une image de la galerie après la prise d'une photo d'intention de l'appareil photo

Je sais que cette question a été posée de différentes manières, mais je n'arrive toujours pas à supprimer l'image de la galerie du dossier par défaut. J'enregistre correctement le fichier sur la carte SD et je peux supprimer ce fichier sans problème, mais le fichier image de la galerie par défaut qui apparaît sous le dossier Appareil photo ne sera pas supprimé.

Je voudrais que l'image soit supprimée une fois que l'activité est retournée puisque le fichier est déjà stocké sur la carte SD sous /Coupon2 .

Des suggestions ?

public void startCamera() {
    Log.d("ANDRO_CAMERA", "Starting camera on the phone...");

    mManufacturerText = (EditText) findViewById(R.id.manufacturer);
    String ManufacturerText = mManufacturerText.getText().toString();
    String currentDateTimeString = new Date().toString();

    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    File filedir = new File(Environment.getExternalStorageDirectory()+"/Coupon2");
    filedir.mkdirs();

    File file = new File(Environment.getExternalStorageDirectory()+"/Coupon2", ManufacturerText+"-test.png");
    outputFileUri = Uri.fromFile(file);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);

    startActivityForResult(intent, CAMERA_PIC_REQUEST);
}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CAMERA_PIC_REQUEST && resultCode == -1) {  
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.putExtra("crop", "true");
        intent.putExtra("scale", "true");

        intent.putExtra("return-data", false);
        intent.setDataAndType(outputFileUri, "image/*");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
        startActivityForResult(intent, CAMERA_CROP_REQUEST);
    }else { 
        SetImage();
        saveState();
    }
}

0 votes

Je suppose qu'il n'y a pas vraiment de bonne façon de procéder puisque j'utilise MediaStore.ACTION_IMAGE_CAPTURE et non une activité de caméra personnalisée. Si quelqu'un a un moyen d'enregistrer le fichier de sortie dans le dossier EXTRA_OUTPUT sans l'enregistrer dans la galerie, merci de me le faire savoir.

0 votes

Nous avons également constaté ce comportement mais seulement sur certains téléphones. (Notamment le myTouch 3G.) D'autres téléphones enregistrent uniquement dans le fichier EXTRA_OUTPUT comme prévu.

1 votes

stackoverflow.com/questions/4515032/ semble être le même problème, et suggère que cela arrive aussi sur les téléphones LG Ally.

70voto

Paul Points 1425

Mon application me demande d'appeler une intention pour prendre une photo. La photo ne peut pas être dans la galerie, mais doit se trouver dans un répertoire spécifique de la carte SD.

A l'origine, j'utilisais simplement l'EXTRA_OUTPUT, mais j'ai rapidement découvert ce qui suit : - Certains appareils l'utilisent complètement et sautent la galerie. - Certains appareils l'ignorent complètement et utilisent UNIQUEMENT la galerie. - Certains appareils sont vraiment nuls et sauvegardent une image en taille réelle dans la galerie, et sauvegardent une vignette uniquement à l'endroit que je voulais. (HTC vous savez qui vous êtes...)

Ainsi, je ne peux pas supprimer aveuglément un fichier de galerie lorsque j'ai terminé. La dernière photo ajoutée peut être ou ne pas être celle que je veux supprimer. De plus, il se peut que je doive copier ce fichier pour remplacer le mien par la suite. Comme mon activité compte 2000 lignes et que mon entreprise ne souhaite pas que l'ensemble de notre code soit publié, je ne publie que les méthodes utilisées pour réaliser cette opération. J'espère que cela vous aidera.

De plus, je précise qu'il s'agit de ma première application Android. Je ne serais pas surpris s'il y avait une meilleure façon de faire que je ne connais pas, mais c'est ce qui fonctionne pour moi !

Alors, voici ma solution :

Tout d'abord, dans le contexte de mon application, je définis une variable comme suit :

public ArrayList<String> GalleryList = new ArrayList<String>();

Ensuite, dans mon activité, je définis une méthode pour obtenir une liste de toutes les photos de la galerie :

private void FillPhotoList()
{
   // initialize the list!
   app.GalleryList.clear();
   String[] projection = { MediaStore.Images.ImageColumns.DISPLAY_NAME };
   // intialize the Uri and the Cursor, and the current expected size.
   Cursor c = null; 
   Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
   //
   // Query the Uri to get the data path.  Only if the Uri is valid.
   if (u != null)
   {
      c = managedQuery(u, projection, null, null, null);
   }

   // If we found the cursor and found a record in it (we also have the id).
   if ((c != null) && (c.moveToFirst())) 
   {
      do 
      {
        // Loop each and add to the list.
        app.GalleryList.add(c.getString(0));
      }     
      while (c.moveToNext());
   }
}

Voici une méthode pour renvoyer un nom de fichier unique pour ma nouvelle image :

private String getTempFileString()
{
   // Only one time will we grab this location.
   final File path = new File(Environment.getExternalStorageDirectory(), 
         getString(getApplicationInfo().labelRes));
   //
   // If this does not exist, we can create it here.
   if (!path.exists())
   {
      path.mkdir();
   }
   //
   return new File(path, String.valueOf(System.currentTimeMillis()) + ".jpg").getPath();
}

J'ai trois variables dans mon activité qui stockent des informations sur le fichier en cours. Une chaîne de caractères (chemin d'accès), une variable Fichier, et une URI vers ce fichier :

public static String sFilePath = ""; 
public static File CurrentFile = null;
public static Uri CurrentUri = null;

Je ne les définis jamais directement, j'appelle seulement un setter sur le chemin du fichier :

public void setsFilePath(String value)
{
   // We just updated this value. Set the property first.
   sFilePath = value;
   //
   // initialize these two
   CurrentFile = null;
   CurrentUri = null;
   //
   // If we have something real, setup the file and the Uri.
   if (!sFilePath.equalsIgnoreCase(""))
   {
      CurrentFile = new File(sFilePath);
      CurrentUri = Uri.fromFile(CurrentFile);
   }
}

Maintenant, j'appelle une intention de prendre une photo.

public void startCamera()
{
   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
   // Specify the output. This will be unique.
   setsFilePath(getTempFileString());
   //
   intent.putExtra(MediaStore.EXTRA_OUTPUT, CurrentUri);
   //
   // Keep a list for afterwards
   FillPhotoList();
   //
   // finally start the intent and wait for a result.
   startActivityForResult(intent, IMAGE_CAPTURE);
}

Une fois que cela est fait, et que l'activité revient, voici mon code :

protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
   if (requestCode == IMAGE_CAPTURE)
   {
      // based on the result we either set the preview or show a quick toast splash.
      if (resultCode == RESULT_OK)
      {
         // This is ##### ridiculous.  Some versions of Android save
         // to the MediaStore as well.  Not sure why!  We don't know what
         // name Android will give either, so we get to search for this
         // manually and remove it.  
         String[] projection = { MediaStore.Images.ImageColumns.SIZE,
                                 MediaStore.Images.ImageColumns.DISPLAY_NAME,
                                 MediaStore.Images.ImageColumns.DATA,
                                 BaseColumns._ID,};
         //    
         // intialize the Uri and the Cursor, and the current expected size.
         Cursor c = null; 
         Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
         //
         if (CurrentFile != null)
         {               
            // Query the Uri to get the data path.  Only if the Uri is valid,
            // and we had a valid size to be searching for.
            if ((u != null) && (CurrentFile.length() > 0))
            {
               c = managedQuery(u, projection, null, null, null);
            }
            //   
            // If we found the cursor and found a record in it (we also have the size).
            if ((c != null) && (c.moveToFirst())) 
            {
               do 
               {
                  // Check each area in the gallary we built before.
                  boolean bFound = false;
                  for (String sGallery : app.GalleryList)
                  {
                     if (sGallery.equalsIgnoreCase(c.getString(1)))
                     {
                        bFound = true;
                        break;
                     }
                  }
                  //       
                  // To here we looped the full gallery.
                  if (!bFound)
                  {
                     // This is the NEW image.  If the size is bigger, copy it.
                     // Then delete it!
                     File f = new File(c.getString(2));

                     // Ensure it's there, check size, and delete!
                     if ((f.exists()) && (CurrentFile.length() < c.getLong(0)) && (CurrentFile.delete()))
                     {
                        // Finally we can stop the copy.
                        try
                        {
                           CurrentFile.createNewFile();
                           FileChannel source = null;
                           FileChannel destination = null;
                           try 
                           {
                              source = new FileInputStream(f).getChannel();
                              destination = new FileOutputStream(CurrentFile).getChannel();
                              destination.transferFrom(source, 0, source.size());
                           }
                           finally 
                           {
                              if (source != null) 
                              {
                                 source.close();
                              }
                              if (destination != null) 
                              {
                                 destination.close();
                              }
                           }
                        }
                        catch (IOException e)
                        {
                           // Could not copy the file over.
                           app.CallToast(PhotosActivity.this, getString(R.string.ErrorOccured), 0);
                        }
                     }
                     //       
                     ContentResolver cr = getContentResolver();
                     cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
                        BaseColumns._ID + "=" + c.getString(3), null);
                     break;                        
                  }
               } 
               while (c.moveToNext());
            }
         }
      }
   }      
}

0 votes

Merci, cela a permis de résoudre le problème sur ces stupides dispositifs de retour de vignettes. C'est aussi le même problème que des millions d'autres messages sur stackoverflow dénoncent : "orientation des photos sur les appareils 2.1".

4 votes

Au lieu de garder une ArrayList des éléments de la galerie, envisagez d'utiliser un HashMap. Actuellement, votre code s'imbrique en bouclant deux fois les images de la galerie, donc avec N images, vous effectuez n^2 comparaisons. Cela provoquera un décalage notable sur les appareils avec beaucoup d'images. Si vous utilisez un HashMap, vous utiliserez le nom de fichier en minuscule comme clé. Ensuite, vous pouvez vérifier l'existence d'un fichier en utilisant containsKey, ce qui en fait une opération O(n).

0 votes

Salut ! J'ai suivi votre code et je suis confronté à un comportement câblé. Je récupère l'image et la sauvegarde dans ma base de données. Dans un autre point du programme, j'affiche toutes les images que j'ai dans ma BD. Cependant, l'orientation est fausse. J'ai également vérifié la base de données et elle est sauvegardée avec une mauvaise orientation. Cela vous arrive-t-il aussi ?

18voto

ElYeante Points 382

Cela supprimera le fichier de la galerie :

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == CAMERA_PIC_REQUEST && resultCode == RESULT_OK) { 

        /* Copy the file to other directory or whatever you want */

        // mContext is the context of the activity
        mContext.getContentResolver().delete(data.getData(), null, null);
    }
 }

À propos du comportement non standard de l'EXTRA_OUTPUT. Je pense que ce pseudo-algorithme devrait fonctionner dans tous les cas :

1) Ne pas utiliser EXTRA_OUTPUT. L'image/photo ira toujours à l'emplacement de la galerie.

2) Copiez le fichier depuis l'emplacement de la galerie vers l'emplacement souhaité.

3) Retirer (avec le code supérieur) le fichier de la galerie.

Mais bien sûr, cela semble être trop parfait... dans certains appareils (par exemple le Galaxy Tab original avec Android 2.3), vous devez utiliser EXTRA_OUTPUT avec ACTION_IMAGE_CAPTURE, sans cela l'intention ne fonctionne pas.

0 votes

Rien dans la documentation n'indique que intent.getData() sera l'URI de l'image. Il existe des preuves anecdotiques basées sur les commentaires des utilisateurs qui suggèrent que ce paramètre n'est pas défini sur certains téléphones.

15voto

Emil Davtyan Points 4724

Si quelqu'un cherche une solution plus simple à ce problème, voici comment je l'ai résolu.

J'ai un bouton de capture et lorsqu'il est pressé, l'intention est envoyée. Ce que j'ai ajouté, c'est que je vais aussi chercher le dernier identifiant dans l'image mediastore et le stocker :

/**
 * Gets the last image id from the media store
 * @return
 */
private int getLastImageId(){
    final String[] imageColumns = { MediaStore.Images.Media._ID };
    final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
    final String imageWhere = null;
    final String[] imageArguments = null;
    Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, imageWhere, imageArguments, imageOrderBy);
    if(imageCursor.moveToFirst()){
        int id = imageCursor.getInt(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
        imageCursor.close();
        return id;
    }else{
        return 0;
    }
}

Ensuite, lorsque l'activité revient, j'exécute ce code qui vérifie le dernier identifiant d'image avant la capture, puis recherche les images après la capture dont l'identifiant est plus grand que celui qui a été enregistré. s'il y en a plus d'un, supprime l'enregistrement situé à l'endroit que j'ai spécifié pour l'enregistrement de la caméra.

/*
 * Checking for duplicate images
 * This is necessary because some camera implementation not only save where you want them to save but also in their default location.
 */
final String[] imageColumns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATE_TAKEN, MediaStore.Images.Media.SIZE, MediaStore.Images.Media._ID };
final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
final String imageWhere = MediaStore.Images.Media._ID+">?";
final String[] imageArguments = { Integer.toString(MyActivity.this.captureLastId) };
Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, imageWhere, imageArguments, imageOrderBy);
if(imageCursor.getCount()>1){
    while(imageCursor.moveToNext()){
        int id = imageCursor.getInt(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
        String path = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
        Long takenTimeStamp = imageCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN));
        Long size = imageCursor.getLong(imageCursor.getColumnIndex(MediaStore.Images.Media.SIZE));
        if(path.contentEquals(MyActivity.this.capturePath)){
            // Remove it
            ContentResolver cr = getContentResolver();
            cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media._ID + "=?", new String[]{ Long.toString(id) } );
            break;
        }
    }               
}
imageCursor.close();

Pour moi, c'était une solution beaucoup plus simple, et j'ai testé sur mon HTC qui avait ce problème.

Autre remarque J'utilisais à l'origine *DATE_TAKEN* et non *_ID* comme paramètre, mais il semble qu'il y ait eu un problème sur l'émulateur : certaines des images capturées par l'intention avaient leur temps *DATE_TAKEN* en millisecondes multiplié par 1000. J'ai donc opté pour *_ID*, qui semble beaucoup plus robuste.

0 votes

@Emil MyActivity.this.captureLastId est l'appel à getLastImageId() ? Si c'est ça, votre code ne fonctionne pas pour moi. Le imageCursor est vide pour moi... le code à l'intérieur de l'objet if(imageCursor.getCount()>1) n'est pas exécuté. Et la méthode managedQuery est dépréciée.

2 votes

@Xithias Traitez ce pseudo-code pour obtenir l'essentiel de la méthode que vous pouvez mettre en œuvre, ne vous contentez pas de copier le code et de vous attendre à ce qu'il fonctionne.

4 votes

@Emil votre réponse n'est pas un pseudocode. Si je vois un code normal, je m'attends à ce qu'il fonctionne. Si vous savez que votre réponse n'est pas complète ou qu'il faut plus de code, indiquez-le quelque part, et dites au lecteur ce qu'il doit faire pour que cela fonctionne. Cela rendrait votre réponse plus complète et plus fructueuse.

5voto

bluefalcon Points 2888

Je pense que le problème vient du fait que vous attendez un certain résultat d'une autre application, ce qui n'est pas le cas. Ce qui est (juste pour être clair) -

  • Lancez une activité qui peut prendre des photos, puisque votre application ne capture pas d'images.

  • Indiquez à l'autre application l'endroit où vous souhaitez enregistrer la photo.

  • Utilisez l'image enregistrée à l'emplacement que vous avez spécifié.

  • Mais le problème est que l'autre application a également enregistré l'image à un autre endroit, ce que vous ne voulez pas.

Examinons maintenant la situation du point de vue de l'autre application, à savoir la capture de l'image. Considérons deux scénarios (ce qui est possible).

  • Tout d'abord, l'application est lancée avec l'option du nom de fichier ou de l'emplacement où l'image doit être enregistrée.
  • Deuxièmement, l'application est lancée sans aucune information supplémentaire comme le nom du fichier ou l'emplacement où l'image doit être enregistrée (cela peut se produire si l'application de capture est lancée directement à partir du menu).

Idéalement, ce qui aurait dû se passer, c'est que s'il y a des informations supplémentaires comme le nom du fichier ou l'emplacement, il aurait dû utiliser cet emplacement, sinon, il peut utiliser le sien. Mais hélas, nous ne sommes pas dans un monde idéal et puisque rien n'est écrit dans la pierre sur ce qui devrait se passer lorsque vous lancez une application caméra, le développeur de l'application caméra aura sa propre interprétation.

Étant donné que différents appareils ont différentes applications de caméra par défaut (oui, la caméra d'origine est généralement remplacée), les résultats obtenus sont/seront différents.

Ainsi, dans certains appareils photo, l'image peut être sauvegardée à un seul endroit, dans d'autres, elle peut être sauvegardée dans le dossier de l'appareil photo (puisque l'application de l'appareil photo peut toujours sauvegarder l'image capturée dans le dossier de l'appareil photo et la sauvegarder dans un autre endroit est un bonus donné par l'application)

Si vous passez simplement un nom de fichier à l'application de l'appareil photo, l'application doit-elle revenir après avoir pris une seule photo ? Je ne pense pas. Alors que faire dans un tel scénario ? C'est très ambigu ou une zone grise.

Maintenant, si vous ne voulez pas qu'elle soit sauvegardée dans le dossier de l'appareil photo, voyez si vous pouvez simplement obtenir le nom de fichier de l'image récemment capturée et puis le supprimer de votre application. Ou ne dites pas à l'application de la caméra où elle doit être enregistrée, récupérez simplement le nom du fichier et déplacez-le à l'endroit de votre choix.

Si vous n'obtenez pas le nom du fichier, vous êtes à la merci de l'autre développeur d'applications. (Hé ! ce n'est pas sa faute non plus, il l'a juste conçu comme il le souhaite).

2voto

Fedor Points 29890

Je pense que tu ne peux pas faire ce que tu veux. C'est triste mais je ne trouve pas d'autre réponse.

Si vous travaillez avec l'implémentation de google Camera, cela fonctionne bien. Il ne stocke pas la photo dans la galerie si EXTRA_OUTPUT est spécifié.

Mais lorsque vous faites face à d'autres appareils, ils peuvent faire quelque chose de complètement différent. C'est parce que HTC, LG et peut-être d'autres ont une implémentation personnalisée de l'appareil photo et vous ne pouvez rien y faire. Vous pouvez le laisser tel quel ou écrire votre propre appareil photo qui fonctionnera exactement comme vous le souhaitez.

En fait, cela n'a rien à voir avec cette question, mais vous découvrirez un jour que CROP intent ne fonctionne pas sur certains appareils ( http://groups.google.com/group/Android-developers/browse_frm/thread/2dd647523926192c/4b6d087073a39607?tvc=1&pli=1 ). Donc, une fois encore, si vous en avez besoin, vous devrez l'écrire vous-même.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X