Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 00c575a3 authored by Ray Chen's avatar Ray Chen
Browse files

Add new thumbnail API.

parent 9d12fdb1
Loading
Loading
Loading
Loading
+200 −0
Original line number Diff line number Diff line
@@ -114624,6 +114624,25 @@
<parameter name="volumeName" type="java.lang.String">
</parameter>
</method>
<method name="getThumbnail"
 return="android.graphics.Bitmap"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="cr" type="android.content.ContentResolver">
</parameter>
<parameter name="origId" type="long">
</parameter>
<parameter name="kind" type="int">
</parameter>
<parameter name="options" type="android.graphics.BitmapFactory.Options">
</parameter>
</method>
<method name="query"
 return="android.database.Cursor"
 abstract="false"
@@ -114787,6 +114806,17 @@
 visibility="public"
>
</field>
<field name="THUMB_DATA"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;thumb_data&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="WIDTH"
 type="java.lang.String"
 transient="false"
@@ -115005,6 +115035,176 @@
>
</field>
</class>
<class name="MediaStore.Video.Thumbnails"
 extends="java.lang.Object"
 abstract="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<implements name="android.provider.BaseColumns">
</implements>
<constructor name="MediaStore.Video.Thumbnails"
 type="android.provider.MediaStore.Video.Thumbnails"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</constructor>
<method name="getContentUri"
 return="android.net.Uri"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="volumeName" type="java.lang.String">
</parameter>
</method>
<method name="getThumbnail"
 return="android.graphics.Bitmap"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="cr" type="android.content.ContentResolver">
</parameter>
<parameter name="origId" type="long">
</parameter>
<parameter name="kind" type="int">
</parameter>
<parameter name="options" type="android.graphics.BitmapFactory.Options">
</parameter>
</method>
<field name="DATA"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;_data&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="DEFAULT_SORT_ORDER"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;video_id ASC&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="EXTERNAL_CONTENT_URI"
 type="android.net.Uri"
 transient="false"
 volatile="false"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="FULL_SCREEN_KIND"
 type="int"
 transient="false"
 volatile="false"
 value="2"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="HEIGHT"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;height&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="INTERNAL_CONTENT_URI"
 type="android.net.Uri"
 transient="false"
 volatile="false"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="KIND"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;kind&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="MICRO_KIND"
 type="int"
 transient="false"
 volatile="false"
 value="3"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="MINI_KIND"
 type="int"
 transient="false"
 volatile="false"
 value="1"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="VIDEO_ID"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;video_id&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="WIDTH"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;width&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
</class>
<interface name="MediaStore.Video.VideoColumns"
 abstract="true"
 static="true"
+259 −32
Original line number Diff line number Diff line
@@ -23,11 +23,15 @@ import android.content.ContentValues;
import android.content.ContentUris;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.MiniThumbFile;
import android.media.ThumbnailUtil;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.util.Log;

import java.io.FileInputStream;
@@ -42,8 +46,7 @@ import java.text.Collator;
 * The Media provider contains meta data for all available media on both internal
 * and external storage devices.
 */
public final class MediaStore
{
public final class MediaStore {
    private final static String TAG = "MediaStore";

    public static final String AUTHORITY = "media";
@@ -226,11 +229,129 @@ public final class MediaStore
        public static final String MIME_TYPE = "mime_type";
     }

    /**
     * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
     * to be accessed elsewhere.
     */
    private static class InternalThumbnails implements BaseColumns {
        private static final int MINI_KIND = 1;
        private static final int FULL_SCREEN_KIND = 2;
        private static final int MICRO_KIND = 3;
        private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};

        /**
         * This method ensure thumbnails associated with origId are generated and decode the byte
         * stream from database (MICRO_KIND) or file (MINI_KIND).
         *
         * Special optimization has been done to avoid further IPC communication for MICRO_KIND
         * thumbnails.
         *
         * @param cr ContentResolver
         * @param origId original image or video id
         * @param kind could be MINI_KIND or MICRO_KIND
         * @param options this is only used for MINI_KIND when decoding the Bitmap
         * @param baseUri the base URI of requested thumbnails
         * @return Bitmap bitmap of specified thumbnail kind
         */
        static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
                BitmapFactory.Options options, Uri baseUri, boolean isVideo) {
            Bitmap bitmap = null;
            String filePath = null;
            // some optimization for MICRO_KIND: if the magic is non-zero, we don't bother
            // querying MediaProvider and simply return thumbnail.
            if (kind == MICRO_KIND) {
                MiniThumbFile thumbFile = MiniThumbFile.instance(baseUri);
                if (thumbFile.getMagic(origId) != 0) {
                    byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
                    if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
                        bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                        if (bitmap == null) {
                            Log.w(TAG, "couldn't decode byte array.");
                        }
                    }
                    return bitmap;
                }
            }

            Cursor c = null;
            try {
                Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
                        .appendQueryParameter("orig_id", String.valueOf(origId)).build();
                c = cr.query(blockingUri, PROJECTION, null, null, null);
                // This happens when original image/video doesn't exist.
                if (c == null) return null;

                // Assuming thumbnail has been generated, at least original image exists.
                if (kind == MICRO_KIND) {
                    MiniThumbFile thumbFile = MiniThumbFile.instance(baseUri);
                    byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
                    if (thumbFile.getMiniThumbFromFile(origId, data) != null) {
                        bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                        if (bitmap == null) {
                            Log.w(TAG, "couldn't decode byte array.");
                        }
                    }
                } else if (kind == MINI_KIND) {
                    if (c.moveToFirst()) {
                        ParcelFileDescriptor pfdInput;
                        Uri thumbUri = null;
                        try {
                            long thumbId = c.getLong(0);
                            filePath = c.getString(1);
                            thumbUri = ContentUris.withAppendedId(baseUri, thumbId);
                            pfdInput = cr.openFileDescriptor(thumbUri, "r");
                            bitmap = BitmapFactory.decodeFileDescriptor(
                                    pfdInput.getFileDescriptor(), null, options);
                            pfdInput.close();
                        } catch (FileNotFoundException ex) {
                            Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
                        } catch (IOException ex) {
                            Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
                        } catch (OutOfMemoryError ex) {
                            Log.e(TAG, "failed to allocate memory for thumbnail "
                                    + thumbUri + "; " + ex);
                        }
                    }
                } else {
                    throw new IllegalArgumentException("Unsupported kind: " + kind);
                }

                // We probably run out of space, so create the thumbnail in memory.
                if (bitmap == null) {
                    Log.v(TAG, "We probably run out of space, so create the thumbnail in memory.");
                    int targetSize = kind == MINI_KIND ? ThumbnailUtil.THUMBNAIL_TARGET_SIZE :
                            ThumbnailUtil.MINI_THUMB_TARGET_SIZE;
                    int maxPixelNum = kind == MINI_KIND ? ThumbnailUtil.THUMBNAIL_MAX_NUM_PIXELS :
                            ThumbnailUtil.MINI_THUMB_MAX_NUM_PIXELS;
                    Uri uri = Uri.parse(
                            baseUri.buildUpon().appendPath(String.valueOf(origId))
                                    .toString().replaceFirst("thumbnails", "media"));
                    if (isVideo) {
                        c = cr.query(uri, PROJECTION, null, null, null);
                        if (c != null && c.moveToFirst()) {
                            bitmap = ThumbnailUtil.createVideoThumbnail(c.getString(1));
                            if (kind == MICRO_KIND) {
                                bitmap = ThumbnailUtil.extractMiniThumb(bitmap,
                                        targetSize, targetSize, ThumbnailUtil.RECYCLE_INPUT);
                            }
                        }
                    } else {
                        bitmap = ThumbnailUtil.makeBitmap(targetSize, maxPixelNum, uri, cr);
                    }
                }
            } catch (SQLiteException ex) {
                Log.w(TAG, ex);
            } finally {
                if (c != null) c.close();
            }
            return bitmap;
        }
    }

    /**
     * Contains meta data for all available images.
     */
    public static final class Images
    {
    public static final class Images {
        public interface ImageColumns extends MediaColumns {
            /**
             * The description of the image
@@ -298,21 +419,18 @@ public final class MediaStore
        }

        public static final class Media implements ImageColumns {
            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
            {
            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
                return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
            }

            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
                                           String where, String orderBy)
            {
                    String where, String orderBy) {
                return cr.query(uri, projection, where,
                                             null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
            }

            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
                    String selection, String [] selectionArgs, String orderBy)
            {
                    String selection, String [] selectionArgs, String orderBy) {
                return cr.query(uri, projection, selection,
                        selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
            }
@@ -326,8 +444,7 @@ public final class MediaStore
             * @throws IOException
             */
            public static final Bitmap getBitmap(ContentResolver cr, Uri url)
                    throws FileNotFoundException, IOException
            {
                    throws FileNotFoundException, IOException {
                InputStream input = cr.openInputStream(url);
                Bitmap bitmap = BitmapFactory.decodeStream(input);
                input.close();
@@ -344,9 +461,8 @@ public final class MediaStore
             * @return The URL to the newly created image
             * @throws FileNotFoundException
             */
            public static final String insertImage(ContentResolver cr, String imagePath, String name,
                                                   String description) throws FileNotFoundException
            {
            public static final String insertImage(ContentResolver cr, String imagePath,
                    String name, String description) throws FileNotFoundException {
                // Check if file exists with a FileInputStream
                FileInputStream stream = new FileInputStream(imagePath);
                try {
@@ -415,8 +531,7 @@ public final class MediaStore
             *              for any reason.
             */
            public static final String insertImage(ContentResolver cr, Bitmap source,
                                                   String title, String description)
            {
                                                   String title, String description) {
                ContentValues values = new ContentValues();
                values.put(Images.Media.TITLE, title);
                values.put(Images.Media.DESCRIPTION, description);
@@ -425,8 +540,7 @@ public final class MediaStore
                Uri url = null;
                String stringUrl = null;    /* value to be returned */

                try
                {
                try {
                    url = cr.insert(EXTERNAL_CONTENT_URI, values);

                    if (source != null) {
@@ -498,25 +612,45 @@ public final class MediaStore
            public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME;
        }

        public static class Thumbnails implements BaseColumns
        {
            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
            {
        /**
         * This class allows developers to query and get two kinds of thumbnails:
         * MINI_KIND: 512 x 384 thumbnail
         * MICRO_KIND: 96 x 96 thumbnail
         */
        public static class Thumbnails implements BaseColumns {
            public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
                return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
            }

            public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind, String[] projection)
            {
            public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
                    String[] projection) {
                return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
            }

            public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection)
            {
            public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
                    String[] projection) {
                return cr.query(EXTERNAL_CONTENT_URI, projection,
                        IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
                        kind, null, null);
            }

            /**
             * This method checks if the thumbnails of the specified image (origId) has been created.
             * It will be blocked until the thumbnails are generated.
             *
             * @param cr ContentResolver used to dispatch queries to MediaProvider.
             * @param origId Original image id associated with thumbnail of interest.
             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
             * @param options this is only used for MINI_KIND when decoding the Bitmap
             * @return A Bitmap instance. It could be null if the original image
             *         associated with origId doesn't exist or memory is not enough.
             */
            public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
                    BitmapFactory.Options options) {
                return InternalThumbnails.getThumbnail(cr, origId, kind, options,
                        EXTERNAL_CONTENT_URI, false);
            }

            /**
             * Get the content:// style URI for the image media table on the
             * given volume.
@@ -568,6 +702,11 @@ public final class MediaStore
            public static final int MINI_KIND = 1;
            public static final int FULL_SCREEN_KIND = 2;
            public static final int MICRO_KIND = 3;
            /**
             * The blob raw data of thumbnail
             * <P>Type: DATA STREAM</P>
             */
            public static final String THUMB_DATA = "thumb_data";

            /**
             * The width of the thumbnal
@@ -1259,8 +1398,7 @@ public final class MediaStore
         */
        public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;

        public static final Cursor query(ContentResolver cr, Uri uri, String[] projection)
        {
        public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
            return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
        }

@@ -1405,6 +1543,95 @@ public final class MediaStore
             */
            public static final String DEFAULT_SORT_ORDER = TITLE;
        }

        /**
         * This class allows developers to query and get two kinds of thumbnails:
         * MINI_KIND: 512 x 384 thumbnail
         * MICRO_KIND: 96 x 96 thumbnail
         *
         */
        public static class Thumbnails implements BaseColumns {
            /**
             * This method checks if the thumbnails of the specified image (origId) has been created.
             * It will be blocked until the thumbnails are generated.
             *
             * @param cr ContentResolver used to dispatch queries to MediaProvider.
             * @param origId Original image id associated with thumbnail of interest.
             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
             * @param options this is only used for MINI_KIND when decoding the Bitmap
             * @return A Bitmap instance. It could be null if the original image associated with
             *         origId doesn't exist or memory is not enough.
             */
            public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
                    BitmapFactory.Options options) {
                return InternalThumbnails.getThumbnail(cr, origId, kind, options,
                        EXTERNAL_CONTENT_URI, true);
            }

            /**
             * Get the content:// style URI for the image media table on the
             * given volume.
             *
             * @param volumeName the name of the volume to get the URI for
             * @return the URI to the image media table on the given volume
             */
            public static Uri getContentUri(String volumeName) {
                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
                        "/video/thumbnails");
            }

            /**
             * The content:// style URI for the internal storage.
             */
            public static final Uri INTERNAL_CONTENT_URI =
                    getContentUri("internal");

            /**
             * The content:// style URI for the "primary" external storage
             * volume.
             */
            public static final Uri EXTERNAL_CONTENT_URI =
                    getContentUri("external");

            /**
             * The default sort order for this table
             */
            public static final String DEFAULT_SORT_ORDER = "video_id ASC";

            /**
             * The data stream for the thumbnail
             * <P>Type: DATA STREAM</P>
             */
            public static final String DATA = "_data";

            /**
             * The original image for the thumbnal
             * <P>Type: INTEGER (ID from Video table)</P>
             */
            public static final String VIDEO_ID = "video_id";

            /**
             * The kind of the thumbnail
             * <P>Type: INTEGER (One of the values below)</P>
             */
            public static final String KIND = "kind";

            public static final int MINI_KIND = 1;
            public static final int FULL_SCREEN_KIND = 2;
            public static final int MICRO_KIND = 3;

            /**
             * The width of the thumbnal
             * <P>Type: INTEGER (long)</P>
             */
            public static final String WIDTH = "width";

            /**
             * The height of the thumbnail
             * <P>Type: INTEGER (long)</P>
             */
            public static final String HEIGHT = "height";
        }
    }

    /**
+2 −0
Original line number Diff line number Diff line
@@ -486,6 +486,8 @@ public class MediaScanner
        }

        public void scanFile(String path, long lastModified, long fileSize) {
            // This is the callback funtion from native codes.
            // Log.v(TAG, "scanFile: "+path);
            doScanFile(path, null, lastModified, fileSize, false);
        }

+280 −0

File added.

Preview size limit exceeded, changes collapsed.

+401 −0

File added.

Preview size limit exceeded, changes collapsed.