¿Cómo consultar a Android MediaStore Content Provider, evitando imágenes huérfanas?


Estoy tratando de proporcionar una actividad en la aplicación que muestra miniaturas de fotos en el almacenamiento de medios del dispositivo, y permitir al usuario seleccionar uno. Después de que el usuario hace un selección, la aplicación lee la imagen original de tamaño completo y hace cosas con ella.

Estoy usando el siguiente código para crear un Cursor sobre todas las imágenes en el externo almacenamiento:

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

Y el siguiente código para cargar la imagen en miniatura (Android 2.se muestra el código x):

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

Y lo siguiente código para cargar la imagen original desde la URL una vez que el usuario hace una selección:

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

El problema que estoy viendo en algunos dispositivos Android, incluyendo mi propio teléfono personal, es que la el cursor que obtengo de la consulta en onCreate() contiene algunas entradas para las que falta el archivo de imagen real de tamaño completo (JPG o PNG). (En el caso de mi teléfono, las imágenes habían sido importadas y posteriormente borradas por iPhoto).

Las entradas huérfanas pueden o no tener miniaturas, dependiendo de si las miniaturas se generaron antes que el archivo multimedia real cuando se ausentó sin permiso. El resultado final es que la aplicación muestra miniaturas para imágenes que en realidad no existen.

Tengo algunas preguntas:

  1. ¿Hay una consulta que pueda hacer al proveedor de contenido MediaStore que filtrará ¿imágenes con medios faltantes en el Cursor devuelto?
  2. ¿Hay un medio, o una API para forzar a MediaStore a volver a escanear, y eliminar las entradas huérfanas? En mi teléfono, monté USB y luego desmonté el externo medios de comunicación, que se supone que desencadena un nuevo análisis. Pero las entradas huérfanas permanecen.
  3. ¿O hay algo fundamentalmente incorrecto en mi enfoque que está causando este problema?

Gracias.

Author: mportuesisf, 2010-09-10

2 answers

Bien, he encontrado el problema con este ejemplo de código.

En el método onCreate(), tenía esta línea:

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

El problema aquí es que está consultando las miniaturas, en lugar de las imágenes reales. La aplicación de la cámara en los dispositivos HTC no crea miniaturas de forma predeterminada, por lo que esta consulta no devolverá imágenes que aún no tengan miniaturas calculadas.

En su lugar, consulta las imágenes reales:

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

Esto devolverá un cursor conteniendo todo las imágenes de tamaño completo en el sistema. A continuación, puede llamar a:

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

Que devolverá la miniatura de tamaño mediano para la imagen de tamaño completo asociada, generándola si es necesario. Para obtener la miniatura de tamaño micro, simplemente use MediaStore.Images.Thumbnails.MICRO_KIND en su lugar.

Esto también resolvió el problema de encontrar miniaturas que tienen referencias colgantes a las imágenes originales de tamaño completo.

 61
Author: mportuesisf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-11 22:08:55

Tenga en cuenta que las cosas cambiarán pronto, el método managedQuery está obsoleto. Utilice CursorLoader en su lugar (desde el nivel de api 11).

 7
Author: Tamas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-25 08:33:01