web-dev-qa-db-fra.com

Comment interroger Android MediaStore Content Provider, en évitant les images orphelines?

J'essaie de fournir une activité dans l'application qui affiche des miniatures de photos dans le magasin multimédia de l'appareil et permet à l'utilisateur d'en sélectionner une. Une fois que l'utilisateur a fait une sélection, l'application lit l'image originale en taille réelle et fait les choses avec.

J'utilise le code suivant pour créer un Cursor sur toutes les images sur le stockage externe:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView( R.layout.image_select );

    mGridView = (GridView) findViewById( R.id.image_select_grid );

    // Query for all images on external storage
    String[] projection = { MediaStore.Images.Media._ID };
    String selection = "";
    String [] selectionArgs = null;
    mImageCursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
                                 projection, selection, selectionArgs, null );

    // Initialize an adapter to display images in grid
    if ( mImageCursor != null ) {
        mImageCursor.moveToFirst();
        mAdapter = new LazyCursorAdapter(this, mImageCursor, R.drawable.image_select_default);
        mGridView.setAdapter( mAdapter );
    } else {
        Log.i(TAG, "System media store is empty.");
    }
}

Et le code suivant pour charger l'image miniature (le code Android 2.x est affiché):

// ...
// Build URI to the main image from the cursor
int imageID = cursor.getInt( cursor.getColumnIndex(MediaStore.Images.Media._ID) );
Uri uri = Uri.withAppendedPath( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                Integer.toString(imageID) );
loadThumbnailImage( uri.toString() );
// ...

protected Bitmap loadThumbnailImage( String url ) {
    // Get original image ID
    int originalImageId = Integer.parseInt(url.substring(url.lastIndexOf("/") + 1, url.length()));

    // Get (or create upon demand) the micro thumbnail for the original image.
    return MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(),
                        originalImageId, MediaStore.Images.Thumbnails.MICRO_KIND, null);
}

Et le code suivant pour charger l'image d'origine à partir de l'URL une fois que l'utilisateur a fait une sélection:

public Bitmap loadFullImage( Context context, Uri photoUri  ) {
    Cursor photoCursor = null;

    try {
        // Attempt to fetch asset filename for image
        String[] projection = { MediaStore.Images.Media.DATA };
        photoCursor = context.getContentResolver().query( photoUri, 
                                                    projection, null, null, null );

        if ( photoCursor != null && photoCursor.getCount() == 1 ) {
            photoCursor.moveToFirst();
            String photoFilePath = photoCursor.getString(
                photoCursor.getColumnIndex(MediaStore.Images.Media.DATA) );

            // Load image from path
            return BitmapFactory.decodeFile( photoFilePath, null );
        }
    } finally {
        if ( photoCursor != null ) {
            photoCursor.close();
        }
    }

    return null;
}

Le problème que je vois sur certains appareils Android, y compris mon propre téléphone personnel, est que le curseur que je reçois de la requête dans onCreate() contient quelques entrées pour lesquelles le il manque un fichier image réel (JPG ou PNG) (dans le cas de mon téléphone, les images ont été importées puis effacées par iPhoto).

Les entrées orphelines peuvent ou non avoir des miniatures, selon que les miniatures ont été générées avant le fichier multimédia réel lors de l'AWOL. Le résultat final est que l'application affiche des vignettes pour les images qui n'existent pas réellement.

J'ai quelques questions:

  1. Y a-t-il une requête que je peux faire au fournisseur de contenu MediaStore qui filtrera les images avec des médias manquants dans le Cursor retourné?
  2. Existe-t-il un moyen ou une API pour forcer le MediaStore à ré-analyser et éliminer les entrées orphelines? Sur mon téléphone, j'ai monté sur USB puis démonté le support externe, qui est censé déclencher une nouvelle analyse. Mais les entrées orphelines restent.
  3. Ou y a-t-il quelque chose de fondamentalement mauvais dans mon approche qui cause ce problème?

Merci.

37
mportuesisf

D'accord, j'ai trouvé le problème avec cet exemple de code.

Dans la méthode onCreate(), j'avais cette ligne:

mImageCursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
                             projection, selection, selectionArgs, null );

Le problème ici est qu'il interroge les vignettes, plutôt que les images réelles. L'application appareil photo sur les appareils HTC ne crée pas de vignettes par défaut, et cette requête ne retournera donc pas les images qui n'ont pas encore de vignettes calculées.

Recherchez plutôt les images elles-mêmes:

mImageCursor = managedQuery( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                             projection, selection, selectionArgs, null );

Cela renverra un curseur contenant toutes les images en taille réelle sur le système. Vous pouvez ensuite appeler:

Bitmap bm = MediaStore.Images.Thumbnails.getThumbnail(context.getContentResolver(),
        imageId, MediaStore.Images.Thumbnails.MINI_KIND, null);

qui renverra la miniature de taille moyenne pour l'image en taille réelle associée, en la générant si nécessaire. Pour obtenir la vignette micro-taille, utilisez simplement MediaStore.Images.Thumbnails.MICRO_KIND au lieu.

Cela a également résolu le problème de la recherche de miniatures ayant des références pendantes aux images originales en taille réelle.

61
mportuesisf

Veuillez noter que les choses vont bientôt changer, la méthode managedQuery est déconseillée. Utilisez plutôt CursorLoader (depuis le niveau 11 de l'API).

7
Tamas