/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.almalence.googsharing;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.ImageColumns;
import android.provider.MediaStore.Video;
import android.provider.MediaStore.Video.VideoColumns;
import android.support.v4.provider.DocumentFile;
import android.util.Log;
/* <!-- +++
import com.almalence.opencam_plus.PluginManagerBase;
+++ --> */
//<!-- -+-
import com.almalence.opencam.PluginManagerBase;
//-+- -->
import com.almalence.util.Util;
public class Thumbnail
{
private static final String TAG = "Thumbnail";
public static final String LAST_THUMB_FILENAME = "last_thumb";
private static final int BUFSIZE = 4096;
private Uri mUri;
private Bitmap mBitmap;
private Bitmap mFullBitmap;
// whether this thumbnail is read from file
private boolean mFromFile = false;
private static ContentResolver mResolver = null;
public static final String DCIM = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM).toString();
public static final String DIRECTORY = DCIM + "/Camera";
// Match the code in MediaProvider.computeBucketValues().
public static final String BUCKET_ID = String.valueOf(DIRECTORY.toLowerCase().hashCode());
public Thumbnail(Uri uri, Bitmap bitmap, Bitmap fullBitmap, int orientation)
{
mUri = uri;
mBitmap = rotateImage(bitmap, orientation);
if (fullBitmap != null)
mFullBitmap = rotateImage(fullBitmap, orientation);
if (mBitmap == null)
throw new IllegalArgumentException("null bitmap");
}
public Uri getUri()
{
return mUri;
}
public Bitmap getBitmap()
{
return mBitmap;
}
public Bitmap getFullBitmap()
{
Media image = getLastImageThumbnail(mResolver);
if (image == null)
return null;
Bitmap fullBitmap = null;
try
{
fullBitmap = Images.Media.getBitmap(mResolver, image.uri);
} catch (FileNotFoundException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
mFullBitmap = null;
System.gc();
mUri = image.uri;
mFullBitmap = rotateImage(fullBitmap, image.orientation);
if (mFullBitmap == null)
throw new IllegalArgumentException("null bitmap");
return mFullBitmap;
}
public void setFromFile(boolean fromFile)
{
mFromFile = fromFile;
}
public boolean fromFile()
{
return mFromFile;
}
private static Bitmap rotateImage(Bitmap bitmap, int orientation)
{
if (orientation != 0)
{
// We only rotate the thumbnail once even if we get OOM.
Matrix m = new Matrix();
m.setRotate(orientation, bitmap.getWidth() * 0.5f, bitmap.getHeight() * 0.5f);
try
{
Bitmap rotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
// If the rotated bitmap is the original bitmap, then it
// should not be recycled.
if (rotated != bitmap)
bitmap.recycle();
return rotated;
} catch (Exception t)
{
Log.w(TAG, "Failed to rotate thumbnail", t);
}
}
return bitmap;
}
// Stores the bitmap to the specified file.
public void saveTo(File file)
{
FileOutputStream f = null;
BufferedOutputStream b = null;
DataOutputStream d = null;
try
{
f = new FileOutputStream(file);
b = new BufferedOutputStream(f, BUFSIZE);
d = new DataOutputStream(b);
d.writeUTF(mUri.toString());
mBitmap.compress(Bitmap.CompressFormat.JPEG, 90, d);
d.close();
} catch (IOException e)
{
Log.e(TAG, "Fail to store bitmap. path=" + file.getPath(), e);
} finally
{
Util.closeSilently(f);
Util.closeSilently(b);
Util.closeSilently(d);
}
}
// Loads the data from the specified file.
// Returns null if failure.
public static Thumbnail loadFrom(File file)
{
Uri uri = null;
Bitmap bitmap = null;
FileInputStream f = null;
BufferedInputStream b = null;
DataInputStream d = null;
try
{
f = new FileInputStream(file);
b = new BufferedInputStream(f, BUFSIZE);
d = new DataInputStream(b);
uri = Uri.parse(d.readUTF());
bitmap = BitmapFactory.decodeStream(d);
d.close();
} catch (IOException e)
{
Log.i(TAG, "Fail to load bitmap. " + e);
return null;
} finally
{
Util.closeSilently(f);
Util.closeSilently(b);
Util.closeSilently(d);
}
Thumbnail thumbnail = createThumbnail(uri, bitmap, null, 0);
if (thumbnail != null)
thumbnail.setFromFile(true);
return thumbnail;
}
public static Thumbnail getLastThumbnail(ContentResolver resolver)
{
mResolver = resolver;
Media image = getLastImageThumbnail(resolver);
Media video = getLastVideoThumbnail(resolver);
if (image == null && video == null)
return null;
Bitmap bitmap = null;
Media lastMedia = null;
try
{
// If there is only image or video, get its thumbnail. If both
// exist,
// get the thumbnail of the one that is newer.
if (image != null && (video == null || image.dateTaken >= video.dateTaken))
{
bitmap = Images.Thumbnails.getThumbnail(resolver, image.id, Images.Thumbnails.MICRO_KIND, null);
lastMedia = image;
} else if (video != null)
{
bitmap = Video.Thumbnails.getThumbnail(resolver, video.id, Video.Thumbnails.MICRO_KIND, null);
lastMedia = video;
}
} catch (Exception ex)
{
Log.e("getLastThumbnail", "createThumbnail exception " + ex.getMessage());
return null;
}
try
{
return createThumbnail(lastMedia.uri, bitmap, null, lastMedia.orientation);
} catch (Exception ex)
{
Log.e("getLastThumbnail", "createThumbnail exception " + ex.getMessage());
return null;
}
}
private static class Media
{
public Media(long id, int orientation, long dateTaken, Uri uri)
{
this.id = id;
this.orientation = orientation;
this.dateTaken = dateTaken;
this.uri = uri;
}
public final long id;
public final int orientation;
public final long dateTaken;
public final Uri uri;
}
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int size, int pixels)
{
final int side = Math.min(bitmap.getWidth(), bitmap.getHeight());
final Bitmap bitmapCropped = Bitmap.createBitmap(bitmap, (bitmap.getWidth() - side) / 2,
(bitmap.getHeight() - side) / 2, side, side);
final Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xffffffff;
final Paint paint = new Paint();
final Rect rectSrc = new Rect(0, 0, bitmapCropped.getWidth(), bitmapCropped.getHeight());
final Rect rect = new Rect(6, 6, output.getWidth() - 6, output.getHeight() - 6);
final RectF rectF = new RectF(rect);
final RectF rectFBorder = new RectF(0, 0, output.getWidth(), output.getHeight());
final float roundPx = pixels;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmapCropped, rectSrc, rect, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_ATOP));
canvas.drawRoundRect(rectFBorder, roundPx, roundPx, paint);
return output;
}
public static String getName() {
String name = "";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
DocumentFile saveDir = PluginManagerBase.getSaveDirNew(false);
if(saveDir != null)
{
if (!saveDir.canWrite())
{
saveDir = PluginManagerBase.getSaveDirNew(true);
}
// Try to build max deep file path
// DocumentFile parentFile = saveDir.getParentFile();
// name = saveDir.getName();
// while (parentFile != null)
// {
// name = parentFile.getName() + "/" + name;
// parentFile = parentFile.getParentFile();
// }
//
// // If we able to get File object, than get path from it
// try
// {
// File file = new File(URI.create(saveDir.getUri().toString()));
// name = file.getAbsolutePath();
// } catch (Exception e)
// {
// } finally
// {
// }
// If we able to get File object, than get path from it.
// fileObject should not be null for files on phone memory.
File fileObject = Util.getFileFromDocumentFile(saveDir);
if (fileObject != null)
{
name = fileObject.getAbsolutePath();
} else
{
// This case should typically happen for files saved to SD
// card.
name = Util.getAbsolutePathFromDocumentFile(saveDir);
}
}
} else
{
File saveDir = PluginManagerBase.getSaveDir(false);
if (!saveDir.canWrite())
{
saveDir = PluginManagerBase.getSaveDir(true);
}
name = saveDir.getAbsolutePath();
}
return name;
}
public static Media getLastImageThumbnail(ContentResolver resolver)
{
Media internalMedia = null;
Media externalMedia = null;
String name = getName();
try
{
Uri baseUri = Images.Media.INTERNAL_CONTENT_URI;
Uri query = baseUri.buildUpon().appendQueryParameter("limit", "1").build();
String[] projection = new String[] { ImageColumns._ID, ImageColumns.ORIENTATION, ImageColumns.DATE_TAKEN };
String selection = ImageColumns.DATA + " like '%" + name + "%' AND " + ImageColumns.MIME_TYPE
+ "='image/jpeg'";
String order = ImageColumns.DATE_TAKEN + " DESC," + ImageColumns._ID + " DESC";
Cursor cursor = null;
try
{
cursor = resolver.query(query, projection, selection, null, order);
if (cursor != null && cursor.moveToFirst())
{
final long id = cursor.getLong(0);
internalMedia = new Media(id, cursor.getInt(1), cursor.getLong(2), ContentUris.withAppendedId(
baseUri, id));
}
} finally
{
if (cursor != null)
{
cursor.close();
}
}
} catch (Exception e)
{
}
try
{
Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
Uri query = baseUri.buildUpon().appendQueryParameter("limit", "1").build();
String[] projection = new String[] { ImageColumns._ID, ImageColumns.ORIENTATION, ImageColumns.DATE_TAKEN };
String selection = ImageColumns.DATA + " like '%" + name + "%' AND " + ImageColumns.MIME_TYPE
+ "='image/jpeg'";
String order = ImageColumns.DATE_TAKEN + " DESC," + ImageColumns._ID + " DESC";
Cursor cursor = null;
try
{
cursor = resolver.query(query, projection, selection, null, order);
if (cursor != null && cursor.moveToFirst())
{
final long id = cursor.getLong(0);
externalMedia = new Media(id, cursor.getInt(1), cursor.getLong(2), ContentUris.withAppendedId(
baseUri, id));
}
} finally
{
if (cursor != null)
{
cursor.close();
}
}
} catch (Exception e)
{
}
if (internalMedia == null)
{
return externalMedia;
} else if (externalMedia == null)
{
return internalMedia;
} else
{
return internalMedia.dateTaken > externalMedia.dateTaken ? internalMedia : externalMedia;
}
}
public static Thumbnail createThumbnail(byte[] jpeg, int orientation, int inSampleSize, Uri uri)
{
// Create the thumbnail.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = inSampleSize;
Bitmap bitmap = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length, options);
return createThumbnail(uri, bitmap, null, orientation);
}
private static Thumbnail createThumbnail(Uri uri, Bitmap bitmap, Bitmap fullBitmap, int orientation)
{
if (bitmap == null)
{
Log.e(TAG, "Failed to create thumbnail from null bitmap");
return null;
}
try
{
return new Thumbnail(uri, bitmap, fullBitmap, orientation);
} catch (IllegalArgumentException e)
{
Log.e(TAG, "Failed to construct thumbnail", e);
return null;
}
}
private static Media getLastVideoThumbnail(ContentResolver resolver)
{
Media internalMedia = null;
Media externalMedia = null;
String name = getName();
try
{
Uri baseUri = Video.Media.INTERNAL_CONTENT_URI;
Uri query = baseUri.buildUpon().appendQueryParameter("limit", "1").build();
String[] projection = new String[] { VideoColumns._ID, VideoColumns.DATA, VideoColumns.DATE_TAKEN };
String selection = VideoColumns.DATA + " like '%" + name + "%' AND " + VideoColumns.MIME_TYPE
+ "='video/mp4'";
String order = VideoColumns.DATE_TAKEN + " DESC," + VideoColumns._ID + " DESC";
Cursor cursor = null;
try
{
cursor = resolver.query(query, projection, selection, null, order);
if (cursor != null && cursor.moveToFirst())
{
final long id = cursor.getLong(0);
internalMedia = new Media(id, 0, cursor.getLong(2), ContentUris.withAppendedId(baseUri, id));
}
} finally
{
if (cursor != null)
{
cursor.close();
}
}
} catch (Exception e)
{
}
try
{
Uri baseUri = Video.Media.EXTERNAL_CONTENT_URI;
Uri query = baseUri.buildUpon().appendQueryParameter("limit", "1").build();
String[] projection = new String[] { VideoColumns._ID, VideoColumns.DATA, VideoColumns.DATE_TAKEN };
String selection = VideoColumns.DATA + " like '%" + name + "%' AND " + VideoColumns.MIME_TYPE
+ "='video/mp4'";
String order = VideoColumns.DATE_TAKEN + " DESC," + VideoColumns._ID + " DESC";
Cursor cursor = null;
try
{
cursor = resolver.query(query, projection, selection, null, order);
if (cursor != null && cursor.moveToFirst())
{
final long id = cursor.getLong(0);
externalMedia = new Media(id, 0, cursor.getLong(2), ContentUris.withAppendedId(baseUri, id));
}
} finally
{
if (cursor != null)
{
cursor.close();
}
}
} catch (Exception e)
{
}
if (internalMedia == null)
{
return externalMedia;
} else if (externalMedia == null)
{
return internalMedia;
} else
{
return internalMedia.dateTaken > externalMedia.dateTaken ? internalMedia : externalMedia;
}
}
public static Bitmap createVideoThumbnail(FileDescriptor fd, int targetWidth)
{
return createVideoThumbnail(null, fd, targetWidth);
}
public static Bitmap createVideoThumbnail(String filePath, int targetWidth)
{
return createVideoThumbnail(filePath, null, targetWidth);
}
private static Bitmap createVideoThumbnail(String filePath, FileDescriptor fd, int targetWidth)
{
Bitmap bitmap = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try
{
if (filePath != null)
{
retriever.setDataSource(filePath);
} else
{
retriever.setDataSource(fd);
}
bitmap = retriever.getFrameAtTime(-1);
} 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.
}
}
if (bitmap == null)
return null;
// Scale down the bitmap if it is bigger than we need.
int width = bitmap.getWidth();
int height = bitmap.getHeight();
if (width > targetWidth)
{
float scale = (float) targetWidth / width;
int w = Math.round(scale * width);
int h = Math.round(scale * height);
bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
}
return bitmap;
}
}