/*
* Copyright (C) 2013 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.android.gallery3d.ingest.ui;
import android.content.Context;
import android.graphics.Matrix;
import android.mtp.MtpDevice;
import android.mtp.MtpObjectInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.widget.ImageView;
import com.android.gallery3d.ingest.data.BitmapWithMetadata;
import com.android.gallery3d.ingest.data.MtpBitmapFetch;
import java.lang.ref.WeakReference;
public class MtpImageView extends ImageView {
private int mObjectHandle;
private int mGeneration;
private WeakReference<MtpImageView> mWeakReference = new WeakReference<MtpImageView>(this);
private Object mFetchLock = new Object();
private boolean mFetchPending = false;
private MtpObjectInfo mFetchObjectInfo;
private MtpDevice mFetchDevice;
private Object mFetchResult;
private static final FetchImageHandler sFetchHandler = FetchImageHandler.createOnNewThread();
private static final ShowImageHandler sFetchCompleteHandler = new ShowImageHandler();
private void init() {
showPlaceholder();
}
public MtpImageView(Context context) {
super(context);
init();
}
public MtpImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MtpImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void showPlaceholder() {
setImageResource(android.R.color.transparent);
}
public void setMtpDeviceAndObjectInfo(MtpDevice device, MtpObjectInfo object, int gen) {
int handle = object.getObjectHandle();
if (handle == mObjectHandle && gen == mGeneration) {
return;
}
cancelLoadingAndClear();
showPlaceholder();
mGeneration = gen;
mObjectHandle = handle;
synchronized (mFetchLock) {
mFetchObjectInfo = object;
mFetchDevice = device;
if (mFetchPending) return;
mFetchPending = true;
sFetchHandler.sendMessage(
sFetchHandler.obtainMessage(0, mWeakReference));
}
}
protected Object fetchMtpImageDataFromDevice(MtpDevice device, MtpObjectInfo info) {
return MtpBitmapFetch.getFullsize(device, info);
}
private float mLastBitmapWidth;
private float mLastBitmapHeight;
private int mLastRotationDegrees;
private Matrix mDrawMatrix = new Matrix();
private void updateDrawMatrix() {
mDrawMatrix.reset();
float dwidth;
float dheight;
float vheight = getHeight();
float vwidth = getWidth();
float scale;
boolean rotated90 = (mLastRotationDegrees % 180 != 0);
if (rotated90) {
dwidth = mLastBitmapHeight;
dheight = mLastBitmapWidth;
} else {
dwidth = mLastBitmapWidth;
dheight = mLastBitmapHeight;
}
if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min(vwidth / dwidth, vheight / dheight);
}
mDrawMatrix.setScale(scale, scale);
if (rotated90) {
mDrawMatrix.postTranslate(-dheight * scale * 0.5f,
-dwidth * scale * 0.5f);
mDrawMatrix.postRotate(mLastRotationDegrees);
mDrawMatrix.postTranslate(dwidth * scale * 0.5f,
dheight * scale * 0.5f);
}
mDrawMatrix.postTranslate((vwidth - dwidth * scale) * 0.5f,
(vheight - dheight * scale) * 0.5f);
if (!rotated90 && mLastRotationDegrees > 0) {
// rotated by a multiple of 180
mDrawMatrix.postRotate(mLastRotationDegrees, vwidth / 2, vheight / 2);
}
setImageMatrix(mDrawMatrix);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed && getScaleType() == ScaleType.MATRIX) {
updateDrawMatrix();
}
}
protected void onMtpImageDataFetchedFromDevice(Object result) {
BitmapWithMetadata bitmapWithMetadata = (BitmapWithMetadata)result;
if (getScaleType() == ScaleType.MATRIX) {
mLastBitmapHeight = bitmapWithMetadata.bitmap.getHeight();
mLastBitmapWidth = bitmapWithMetadata.bitmap.getWidth();
mLastRotationDegrees = bitmapWithMetadata.rotationDegrees;
updateDrawMatrix();
} else {
setRotation(bitmapWithMetadata.rotationDegrees);
}
setAlpha(0f);
setImageBitmap(bitmapWithMetadata.bitmap);
animate().alpha(1f);
}
protected void cancelLoadingAndClear() {
synchronized (mFetchLock) {
mFetchDevice = null;
mFetchObjectInfo = null;
mFetchResult = null;
}
animate().cancel();
setImageResource(android.R.color.transparent);
}
@Override
public void onDetachedFromWindow() {
cancelLoadingAndClear();
super.onDetachedFromWindow();
}
private static class FetchImageHandler extends Handler {
public FetchImageHandler(Looper l) {
super(l);
}
public static FetchImageHandler createOnNewThread() {
HandlerThread t = new HandlerThread("MtpImageView Fetch");
t.start();
return new FetchImageHandler(t.getLooper());
}
@Override
public void handleMessage(Message msg) {
@SuppressWarnings("unchecked")
MtpImageView parent = ((WeakReference<MtpImageView>) msg.obj).get();
if (parent == null) return;
MtpObjectInfo objectInfo;
MtpDevice device;
synchronized (parent.mFetchLock) {
parent.mFetchPending = false;
device = parent.mFetchDevice;
objectInfo = parent.mFetchObjectInfo;
}
if (device == null) return;
Object result = parent.fetchMtpImageDataFromDevice(device, objectInfo);
if (result == null) return;
synchronized (parent.mFetchLock) {
if (parent.mFetchObjectInfo != objectInfo) return;
parent.mFetchResult = result;
parent.mFetchDevice = null;
parent.mFetchObjectInfo = null;
sFetchCompleteHandler.sendMessage(
sFetchCompleteHandler.obtainMessage(0, parent.mWeakReference));
}
}
}
private static class ShowImageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
@SuppressWarnings("unchecked")
MtpImageView parent = ((WeakReference<MtpImageView>) msg.obj).get();
if (parent == null) return;
Object result;
synchronized (parent.mFetchLock) {
result = parent.mFetchResult;
}
if (result == null) return;
parent.onMtpImageDataFetchedFromDevice(result);
}
}
}