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

Commit bf124e7e authored by Ray Chen's avatar Ray Chen
Browse files

Unhide ThumbnailUtils to support Camera unbundling.

parent c88ed5dc
Loading
Loading
Loading
Loading
+116 −0
Original line number Diff line number Diff line
@@ -83775,6 +83775,122 @@
</parameter>
</method>
</class>
<class name="ThumbnailUtils"
 extends="java.lang.Object"
 abstract="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<constructor name="ThumbnailUtils"
 type="android.media.ThumbnailUtils"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
</constructor>
<method name="createImageThumbnail"
 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="filePath" type="java.lang.String">
</parameter>
<parameter name="uri" type="android.net.Uri">
</parameter>
<parameter name="origId" type="long">
</parameter>
<parameter name="kind" type="int">
</parameter>
<parameter name="saveMini" type="boolean">
</parameter>
</method>
<method name="createVideoThumbnail"
 return="android.graphics.Bitmap"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="filePath" type="java.lang.String">
</parameter>
</method>
<method name="extractMiniThumb"
 return="android.graphics.Bitmap"
 abstract="false"
 native="false"
 synchronized="false"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="source" type="android.graphics.Bitmap">
</parameter>
<parameter name="width" type="int">
</parameter>
<parameter name="height" type="int">
</parameter>
<parameter name="recycle" type="boolean">
</parameter>
</method>
<field name="MINI_THUMB_TARGET_SIZE"
 type="int"
 transient="false"
 volatile="false"
 value="96"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="NO_RECYCLE_INPUT"
 type="boolean"
 transient="false"
 volatile="false"
 value="false"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="RECYCLE_INPUT"
 type="boolean"
 transient="false"
 volatile="false"
 value="true"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="THUMBNAIL_TARGET_SIZE"
 type="int"
 transient="false"
 volatile="false"
 value="320"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
</class>
<class name="ToneGenerator"
 extends="java.lang.Object"
 abstract="false"
+7 −7
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.MiniThumbFile;
import android.media.ThumbnailUtil;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
@@ -381,15 +381,15 @@ public final class MediaStore {
                        filePath = c.getString(1);
                    }
                    if (isVideo) {
                        bitmap = ThumbnailUtil.createVideoThumbnail(filePath);
                        bitmap = ThumbnailUtils.createVideoThumbnail(filePath);
                        if (kind == MICRO_KIND && bitmap != null) {
                            bitmap = ThumbnailUtil.extractMiniThumb(bitmap,
                                    ThumbnailUtil.MINI_THUMB_TARGET_SIZE,
                                    ThumbnailUtil.MINI_THUMB_TARGET_SIZE,
                                    ThumbnailUtil.RECYCLE_INPUT);
                            bitmap = ThumbnailUtils.extractMiniThumb(bitmap,
                                    ThumbnailUtils.MINI_THUMB_TARGET_SIZE,
                                    ThumbnailUtils.MINI_THUMB_TARGET_SIZE,
                                    ThumbnailUtils.RECYCLE_INPUT);
                        }
                    } else {
                        bitmap = ThumbnailUtil.createImageThumbnail(cr, filePath, uri, origId,
                        bitmap = ThumbnailUtils.createImageThumbnail(cr, filePath, uri, origId,
                                kind, false);
                    }
                }
+0 −1
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package android.media;

import android.graphics.Bitmap;
import android.media.ThumbnailUtil;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
+230 −184
Original line number Diff line number Diff line
@@ -16,13 +16,6 @@

package android.media;

import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.BaseColumns;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.Thumbnails;
import android.util.Log;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -34,47 +27,171 @@ import android.graphics.Matrix;
import android.graphics.Rect;
import android.media.MediaMetadataRetriever;
import android.media.MediaFile.MediaFileType;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.BaseColumns;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.Thumbnails;
import android.util.Log;

import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Thumbnail generation routines for media provider. This class should only be used internaly.
 * {@hide} THIS IS NOT FOR PUBLIC API.
 * Thumbnail generation routines for media provider.
 */

public class ThumbnailUtil {
    private static final String TAG = "ThumbnailUtil";
    //Whether we should recycle the input (unless the output is the input).
public class ThumbnailUtils {
    private static final String TAG = "ThumbnailUtils";

    /* Maximum pixels size for created bitmap. */
    private static final int THUMBNAIL_MAX_NUM_PIXELS = 512 * 384;
    private static final int MINI_THUMB_MAX_NUM_PIXELS = 128 * 128;
    private static final int UNCONSTRAINED = -1;

    /* Whether we should rotate the resulting bitmap. */
    private static final boolean ROTATE_AS_NEEDED = true;
    private static final boolean NO_ROTATE = false;

    /* Whether we should create bitmap in native memory. */
    private static final boolean USE_NATIVE = true;
    private static final boolean NO_NATIVE = false;

    /**
     * Constant used to indicate we should recycle the input in
     * {@link #extractMiniThumb(Bitmap, int, int, boolean)} unless the output is the input.
     */
    public static final boolean RECYCLE_INPUT = true;

    /**
     * Constant used to indicate we should not recycle the input in
     * {@link #extractMiniThumb(Bitmap, int, int, boolean)}.
     */
    public static final boolean NO_RECYCLE_INPUT = false;
    public static final boolean ROTATE_AS_NEEDED = true;
    public static final boolean NO_ROTATE = false;
    public static final boolean USE_NATIVE = true;
    public static final boolean NO_NATIVE = false;

    /**
     * Constant used to indicate the dimension of normal thumbnail in
     * {@link #extractMiniThumb(Bitmap, int, int, boolean)}.
     */
    public static final int THUMBNAIL_TARGET_SIZE = 320;

    /**
     * Constant used to indicate the dimension of mini thumbnail in
     * {@link #extractMiniThumb(Bitmap, int, int, boolean)}.
     */
    public static final int MINI_THUMB_TARGET_SIZE = 96;
    public static final int THUMBNAIL_MAX_NUM_PIXELS = 512 * 384;
    public static final int MINI_THUMB_MAX_NUM_PIXELS = 128 * 128;
    public static final int UNCONSTRAINED = -1;

    // Returns Options that set the native alloc flag for Bitmap decode.
    public static BitmapFactory.Options createNativeAllocOptions() {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inNativeAlloc = true;
        return options;
    /**
     * This method first examines if the thumbnail embedded in EXIF is bigger than our target
     * size. If not, then it'll create a thumbnail from original image. Due to efficiency
     * consideration, we want to let MediaThumbRequest avoid calling this method twice for
     * both kinds, so it only requests for MICRO_KIND and set saveImage to true.
     *
     * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.
     *
     * @param cr ContentResolver
     * @param filePath file path needed by EXIF interface
     * @param uri URI of original image
     * @param origId image id
     * @param kind either MINI_KIND or MICRO_KIND
     * @param saveMini Whether to save MINI_KIND thumbnail obtained in this method.
     * @return Bitmap
     */
    public static Bitmap createImageThumbnail(ContentResolver cr, String filePath, Uri uri,
            long origId, int kind, boolean saveMini) {
        boolean wantMini = (kind == Images.Thumbnails.MINI_KIND || saveMini);
        int targetSize = wantMini ?
                THUMBNAIL_TARGET_SIZE : MINI_THUMB_TARGET_SIZE;
        int maxPixels = wantMini ?
                THUMBNAIL_MAX_NUM_PIXELS : MINI_THUMB_MAX_NUM_PIXELS;
        SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
        Bitmap bitmap = null;
        MediaFileType fileType = MediaFile.getFileType(filePath);
        if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) {
            createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);
            bitmap = sizedThumbnailBitmap.mBitmap;
        }

        if (bitmap == null) {
            bitmap = makeBitmap(targetSize, maxPixels, uri, cr);
        }

        if (bitmap == null) {
            return null;
        }

        if (saveMini) {
            if (sizedThumbnailBitmap.mThumbnailData != null) {
                storeThumbnail(cr, origId,
                        sizedThumbnailBitmap.mThumbnailData,
                        sizedThumbnailBitmap.mThumbnailWidth,
                        sizedThumbnailBitmap.mThumbnailHeight);
            } else {
                storeThumbnail(cr, origId, bitmap);
            }
        }

        if (kind == Images.Thumbnails.MICRO_KIND) {
            // now we make it a "square thumbnail" for MICRO_KIND thumbnail
            bitmap = extractMiniThumb(bitmap,
                    MINI_THUMB_TARGET_SIZE,
                    MINI_THUMB_TARGET_SIZE, RECYCLE_INPUT);
        }
        return bitmap;
    }

    /**
     * Make a bitmap from a given Uri.
     * Create a video thumbnail for a video. May return null if the video is
     * corrupt.
     *
     * @param uri
     * @param filePath
     */
    public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
            Uri uri, ContentResolver cr) {
        return makeBitmap(minSideLength, maxNumOfPixels, uri, cr,
                NO_NATIVE);
    public static Bitmap createVideoThumbnail(String filePath) {
        Bitmap bitmap = null;
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
            retriever.setDataSource(filePath);
            bitmap = retriever.captureFrame();
        } catch (IllegalArgumentException ex) {
            // Assume this is a corrupt video file
        } catch (RuntimeException ex) {
            // Assume this is a corrupt video file.
        } finally {
            try {
                retriever.release();
            } catch (RuntimeException ex) {
                // Ignore failures while cleaning up.
            }
        }
        return bitmap;
    }

    /**
     * Creates a centered bitmap of the desired size.
     *
     * @param source original bitmap source
     * @param width targeted width
     * @param height targeted height
     * @param recycle whether we want to recycle the input
     */
    public static Bitmap extractMiniThumb(
            Bitmap source, int width, int height, boolean recycle) {
        if (source == null) {
            return null;
        }

        float scale;
        if (source.getWidth() < source.getHeight()) {
            scale = width / (float) source.getWidth();
        } else {
            scale = height / (float) source.getHeight();
        }
        Matrix matrix = new Matrix();
        matrix.setScale(scale, scale);
        Bitmap miniThumbnail = transform(matrix, source, width, height, true, recycle);
        return miniThumbnail;
    }

    /*
@@ -96,7 +213,7 @@ public class ThumbnailUtil {
     * For example, BitmapFactory downsamples an image by 2 even though the
     * request is 3. So we round up the sample size to avoid OOM.
     */
    public static int computeSampleSize(BitmapFactory.Options options,
    private static int computeSampleSize(BitmapFactory.Options options,
            int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength,
                maxNumOfPixels);
@@ -140,7 +257,30 @@ public class ThumbnailUtil {
        }
    }

    public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
    /**
     *  Returns Options that set the native alloc flag for Bitmap decode.
     */
    private static BitmapFactory.Options createNativeAllocOptions() {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inNativeAlloc = true;
        return options;
    }

    /**
     * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
     */
    private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
            Uri uri, ContentResolver cr) {
        return makeBitmap(minSideLength, maxNumOfPixels, uri, cr,
                NO_NATIVE);
    }

    /**
     * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
     * The image data will be read from specified ContentResolver and clients are allowed to specify
     * whether they want the Bitmap be created in native memory.
     */
    private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
            Uri uri, ContentResolver cr, boolean useNative) {
        ParcelFileDescriptor input = null;
        try {
@@ -159,46 +299,15 @@ public class ThumbnailUtil {
        }
    }

    // Rotates the bitmap by the specified degree.
    // If a new bitmap is created, the original bitmap is recycled.
    public static Bitmap rotate(Bitmap b, int degrees) {
        if (degrees != 0 && b != null) {
            Matrix m = new Matrix();
            m.setRotate(degrees,
                    (float) b.getWidth() / 2, (float) b.getHeight() / 2);
            try {
                Bitmap b2 = Bitmap.createBitmap(
                        b, 0, 0, b.getWidth(), b.getHeight(), m, true);
                if (b != b2) {
                    b.recycle();
                    b = b2;
                }
            } catch (OutOfMemoryError ex) {
                // We have no memory to rotate. Return the original bitmap.
            }
        }
        return b;
    }

    private static void closeSilently(ParcelFileDescriptor c) {
      if (c == null) return;
      try {
          c.close();
      } catch (Throwable t) {
          // do nothing
      }
    }

    private static ParcelFileDescriptor makeInputStream(
            Uri uri, ContentResolver cr) {
        try {
            return cr.openFileDescriptor(uri, "r");
        } catch (IOException ex) {
            return null;
        }
    }

    public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
    /**
     * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
     * The image data will be read from specified pfd if it's not null, otherwise
     * a new input stream will be created using specified ContentResolver.
     *
     * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A
     * new BitmapFactory.Options will be created if options is null.
     */
    private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
            Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
            BitmapFactory.Options options) {
            Bitmap b = null;
@@ -232,115 +341,50 @@ public class ThumbnailUtil {
    }

    /**
     * Creates a centered bitmap of the desired size.
     * @param source
     * @param recycle whether we want to recycle the input
     * Rotates the bitmap by the specified degree.
     * If a new bitmap is created, the original bitmap is recycled.
     */
    public static Bitmap extractMiniThumb(
            Bitmap source, int width, int height, boolean recycle) {
        if (source == null) {
            return null;
        }

        float scale;
        if (source.getWidth() < source.getHeight()) {
            scale = width / (float) source.getWidth();
        } else {
            scale = height / (float) source.getHeight();
        }
        Matrix matrix = new Matrix();
        matrix.setScale(scale, scale);
        Bitmap miniThumbnail = transform(matrix, source, width, height, true, recycle);
        return miniThumbnail;
    }

    /**
     * Create a video thumbnail for a video. May return null if the video is
     * corrupt.
     *
     * @param filePath
     */
    public static Bitmap createVideoThumbnail(String filePath) {
        Bitmap bitmap = null;
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
            retriever.setDataSource(filePath);
            bitmap = retriever.captureFrame();
        } catch (IllegalArgumentException ex) {
            // Assume this is a corrupt video file
        } catch (RuntimeException ex) {
            // Assume this is a corrupt video file.
        } finally {
    private static Bitmap rotate(Bitmap b, int degrees) {
        if (degrees != 0 && b != null) {
            Matrix m = new Matrix();
            m.setRotate(degrees,
                    (float) b.getWidth() / 2, (float) b.getHeight() / 2);
            try {
                retriever.release();
            } catch (RuntimeException ex) {
                // Ignore failures while cleaning up.
            }
        }
        return bitmap;
                Bitmap b2 = Bitmap.createBitmap(
                        b, 0, 0, b.getWidth(), b.getHeight(), m, true);
                if (b != b2) {
                    b.recycle();
                    b = b2;
                }

    /**
     * This method first examines if the thumbnail embedded in EXIF is bigger than our target
     * size. If not, then it'll create a thumbnail from original image. Due to efficiency
     * consideration, we want to let MediaThumbRequest avoid calling this method twice for
     * both kinds, so it only requests for MICRO_KIND and set saveImage to true.
     *
     * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.
     *
     * @param cr ContentResolver
     * @param filePath file path needed by EXIF interface
     * @param uri URI of original image
     * @param origId image id
     * @param kind either MINI_KIND or MICRO_KIND
     * @param saveMini Whether to save MINI_KIND thumbnail obtained in this method.
     * @return Bitmap
     */
    public static Bitmap createImageThumbnail(ContentResolver cr, String filePath, Uri uri,
            long origId, int kind, boolean saveMini) {
        boolean wantMini = (kind == Images.Thumbnails.MINI_KIND || saveMini);
        int targetSize = wantMini ?
                ThumbnailUtil.THUMBNAIL_TARGET_SIZE : ThumbnailUtil.MINI_THUMB_TARGET_SIZE;
        int maxPixels = wantMini ?
                ThumbnailUtil.THUMBNAIL_MAX_NUM_PIXELS : ThumbnailUtil.MINI_THUMB_MAX_NUM_PIXELS;
        SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
        Bitmap bitmap = null;
        MediaFileType fileType = MediaFile.getFileType(filePath);
        if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) {
            createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);
            bitmap = sizedThumbnailBitmap.mBitmap;
            } catch (OutOfMemoryError ex) {
                // We have no memory to rotate. Return the original bitmap.
            }

        if (bitmap == null) {
            bitmap = ThumbnailUtil.makeBitmap(targetSize, maxPixels, uri, cr);
        }

        if (bitmap == null) {
            return null;
        return b;
    }

        if (saveMini) {
            if (sizedThumbnailBitmap.mThumbnailData != null) {
                ThumbnailUtil.storeThumbnail(cr, origId,
                        sizedThumbnailBitmap.mThumbnailData,
                        sizedThumbnailBitmap.mThumbnailWidth,
                        sizedThumbnailBitmap.mThumbnailHeight);
            } else {
                ThumbnailUtil.storeThumbnail(cr, origId, bitmap);
    private static void closeSilently(ParcelFileDescriptor c) {
      if (c == null) return;
      try {
          c.close();
      } catch (Throwable t) {
          // do nothing
      }
    }

        if (kind == Images.Thumbnails.MICRO_KIND) {
            // now we make it a "square thumbnail" for MICRO_KIND thumbnail
            bitmap = ThumbnailUtil.extractMiniThumb(bitmap,
                    ThumbnailUtil.MINI_THUMB_TARGET_SIZE,
                    ThumbnailUtil.MINI_THUMB_TARGET_SIZE, ThumbnailUtil.RECYCLE_INPUT);
    private static ParcelFileDescriptor makeInputStream(
            Uri uri, ContentResolver cr) {
        try {
            return cr.openFileDescriptor(uri, "r");
        } catch (IOException ex) {
            return null;
        }
        return bitmap;
    }

    public static Bitmap transform(Matrix scaler,
    /**
     * Transform source Bitmap to targeted width and height.
     */
    private static Bitmap transform(Matrix scaler,
            Bitmap source,
            int targetWidth,
            int targetHeight,
@@ -441,10 +485,6 @@ public class ThumbnailUtil {
    /**
     * Look up thumbnail uri by given imageId, it will be automatically created if it's not created
     * yet. Most of the time imageId is identical to thumbId, but it's not always true.
     * @param req
     * @param width
     * @param height
     * @return Uri Thumbnail uri
     */
    private static Uri getImageThumbnailUri(ContentResolver cr, long origId, int width, int height) {
        Uri thumbUri = Images.Thumbnails.EXTERNAL_CONTENT_URI;
@@ -513,10 +553,14 @@ public class ThumbnailUtil {
        }
    }

    // SizedThumbnailBitmap contains the bitmap, which is downsampled either from
    // the thumbnail in exif or the full image.
    // mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail is not null.
    // The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
    /**
     * SizedThumbnailBitmap contains the bitmap, which is downsampled either from
     * the thumbnail in exif or the full image.
     * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail
     * is not null.
     *
     * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
     */
    private static class SizedThumbnailBitmap {
        public byte[] mThumbnailData;
        public Bitmap mBitmap;
@@ -524,9 +568,11 @@ public class ThumbnailUtil {
        public int mThumbnailHeight;
    }

    // Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
    // The functions returns a SizedThumbnailBitmap,
    // which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
    /**
     * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
     * The functions returns a SizedThumbnailBitmap,
     * which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
     */
    private static void createThumbnailFromEXIF(String filePath, int targetSize,
            int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {
        if (filePath == null) return;