jeudi 14 juin 2012

Utilisation des images Picasa de la galerie Android après un ActivityForResult sur HoneyComb et ICS

Si vous avez déjà développé une application qui utilise la galerie pour sélectionner des images sous Ice Cream Sandwich, vous vous êtes peut être demandé comment récupérer une image hébergée par Picasa retournée par le choix de l'utilisateur. Ceux qui ont essayé par la manière classique se sont surement retrouvé face à cette erreur:


Failed to find provider info for com.android.gallery3d.provider


Et pour cause, ceci est un bug de la galerie, le provider com.android.gallery3d.provider n'existe pas! c'est une erreur de la part de Google, car la galerie répond plutôt à com.google.android.gallery3d.provider. Voici comment j'ai procédé pour arriver à ce résultat:


Je vous épargne le XML, rien de plus classique ici.
Au niveau Java on appelle les applications susceptibles de nous renvoyer une image:


Intent takePictureIntent =  new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(takePictureIntent, actionCode);

Ensuite on réceptionne le résultat:

@Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data)
 {
  switch (requestCode) {
   
   case ACTIVITY_SELECT_IMAGE:
   {
    if (resultCode == Activity.RESULT_OK)
    {
     Uri selectedImage = data.getData();
     final String[] columns = { MediaColumns.DATA, MediaColumns.DISPLAY_NAME };

     Cursor cursor = getContentResolver().query(selectedImage, columns, null, null, null);
     if (cursor != null)
     {
      cursor.moveToFirst();

      int columnIndex = cursor.getColumnIndex(MediaColumns.DATA);
      if (columnIndex != -1)
      {
       // regular processing for gallery files
       String fileName = cursor.getString(columnIndex);
       setPic(fileName);
      }
     } else
     {
      // Ce n'est pas une image stockée dans la gallerie
      {
       try
       {
        
        if (selectedImage.toString().startsWith("content://com.android.gallery3d.provider"))
        {
         // utiliser le provider com.google, pas
         // com.android provider.
         selectedImage = Uri.parse(selectedImage.toString().replace("com.android.gallery3d", "com.google.android.gallery3d"));
        }
        

        final InputStream inputStream = getContentResolver().openInputStream(selectedImage);
        // On lit l'image dans un InputStream

        // qu'on enregistre dans un FileOutputStream
        File f = createImageFile();
        OutputStream out = new FileOutputStream(f);

        int read = 0;
        byte[] bytes = new byte[1024];

        while ((read = inputStream.read(bytes)) != -1)
        {
         out.write(bytes, 0, read);
        }

        inputStream.close();
        out.flush();
        out.close();

        

        setPic(f.getAbsolutePath());
       }
       catch (IOException e)
       {
        Log.e("PhotoIntentActivity", "io", e);
       }
      }
     }
    }
    break;
   } // ACTIVITY_SELECT_IMAGE

  } // switch
 }

Et pour la forme voici la méthode setPic() qui me permet d'afficher l'image:

 private void setPic(String filePath)
 {

  mCurrentPhotoPath = filePath;
  // There isn't enough memory to open up more than a couple camera photos 
  // So pre-scale the target bitmap into which the file is decoded 

  // Get the size of the ImageView 
  int targetW = mImageView.getWidth();
  int targetH = mImageView.getHeight();

  // Get the size of the image 
  BitmapFactory.Options bmOptions = new BitmapFactory.Options();
  bmOptions.inJustDecodeBounds = true;
  BitmapFactory.decodeFile(filePath, bmOptions);
  int photoW = bmOptions.outWidth;
  int photoH = bmOptions.outHeight;



  // Figure out which way needs to be reduced less 
  int scaleFactor = 1;
  if ((targetW > 0) || (targetH > 0))
  {
   scaleFactor = Math.min(photoW / targetW, photoH / targetH);
  }

  // Set bitmap options to scale the image decode target 
  bmOptions.inJustDecodeBounds = false;
  bmOptions.inSampleSize = scaleFactor;
  bmOptions.inPurgeable = true;
  isFormReady = true;

  // Decode the JPEG file into a Bitmap 
  Bitmap bitmap = BitmapFactory.decodeFile(filePath, bmOptions);

  // Associate the Bitmap to the ImageView 
  mImageView.setImageBitmap(bitmap);
  mImageView.setVisibility(View.VISIBLE);
  mButtonSend.setEnabled(canSend());
 }

Il faut bien garder à l'esprit que la méthode donnée ici ne prend pas tous les cas de figure en compte, mais palie au bug de la galerie Android. Ceci n'est qu'un hack qui je l'espère sera provisoire. Si l'équipe d'Android change com.google.android.gallery3d.provider en com.android.gallery3d.provider alors cette méthode restera valide pour les futures versions, si c'est le contraire, alors il faudra mettre à jour vos appli avec de grosses verrues spécialement pour HoneyComb et IceScream Sandwich.

Le code montré ci-dessus est disponible dans les sources de mon appli: My Figure Collection

Twitter