/* * Copyright (C) 2009 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.camera.ui; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.media.ThumbnailUtils; import android.util.AttributeSet; import android.view.ViewGroup.LayoutParams; import android.view.animation.AnimationUtils; import android.widget.ImageView; import com.android.camera.util.CameraUtil; /** * A @{code ImageView} which can rotate it's content. */ public class RotateImageView extends TwoStateImageView implements Rotatable { @SuppressWarnings("unused") private static final String TAG = "RotateImageView"; private static final int ANIMATION_SPEED = 270; // 270 deg/sec private int mCurrentDegree = 0; // [0, 359] private int mStartDegree = 0; private int mTargetDegree = 0; private boolean mClockwise = false, mEnableAnimation = true; private long mAnimationStartTime = 0; private long mAnimationEndTime = 0; private boolean mNaturalPortrait = CameraUtil.isDefaultToPortrait( (Activity) getContext()); public RotateImageView(Context context, AttributeSet attrs) { super(context, attrs); } public RotateImageView(Context context) { super(context); } protected int getDegree() { return mTargetDegree; } private boolean isOrientationPortrait() { int mapTo180Degree = (mCurrentDegree % 180); // Assumging the devic's natural orientation is portrait and // check if the current orienataion is within the landscape range boolean isLandscape = ((mapTo180Degree > 45) && (mapTo180Degree < 135)); if (mNaturalPortrait) return !isLandscape; else return isLandscape; } // Rotate the view counter-clockwise @Override public void setOrientation(int degree, boolean animation) { mEnableAnimation = animation; // make sure in the range of [0, 359] degree = degree >= 0 ? degree % 360 : degree % 360 + 360; if (degree == mTargetDegree) return; mTargetDegree = degree; if (mEnableAnimation) { mStartDegree = mCurrentDegree; mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis(); int diff = mTargetDegree - mCurrentDegree; diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359] // Make it in range [-179, 180]. That's the shorted distance between the // two angles diff = diff > 180 ? diff - 360 : diff; mClockwise = diff >= 0; mAnimationEndTime = mAnimationStartTime + Math.abs(diff) * 1000 / ANIMATION_SPEED; } else { mCurrentDegree = mTargetDegree; } invalidate(); } @Override protected void onDraw(Canvas canvas) { Drawable drawable = getDrawable(); if (drawable == null) return; Rect bounds = drawable.getBounds(); int w = bounds.right - bounds.left; int h = bounds.bottom - bounds.top; if (w == 0 || h == 0) return; // nothing to draw if (mCurrentDegree != mTargetDegree) { long time = AnimationUtils.currentAnimationTimeMillis(); if (time < mAnimationEndTime) { int deltaTime = (int)(time - mAnimationStartTime); int degree = mStartDegree + ANIMATION_SPEED * (mClockwise ? deltaTime : -deltaTime) / 1000; degree = degree >= 0 ? degree % 360 : degree % 360 + 360; mCurrentDegree = degree; invalidate(); } else { mCurrentDegree = mTargetDegree; } } int left = getPaddingLeft(); int top = getPaddingTop(); int right = getPaddingRight(); int bottom = getPaddingBottom(); int width = getWidth() - left - right; int height = getHeight() - top - bottom; int saveCount = canvas.getSaveCount(); // Scale down or up the image first if required. if (getScaleType() == ImageView.ScaleType.FIT_CENTER) { float ratio; // As camera layout is fixed to portrait, we need to // swap canvas width and height when calculating the // ratio for landscape mode. if (isOrientationPortrait()) ratio = Math.min((float) width / w, (float) height / h); else ratio = Math.min((float) height / w, (float) width / h); canvas.scale(ratio, ratio, width / 2.0f, height / 2.0f); } canvas.translate(left + width / 2, top + height / 2); canvas.rotate(-mCurrentDegree); canvas.translate(-w / 2, -h / 2); drawable.draw(canvas); canvas.restoreToCount(saveCount); } private Bitmap mThumb; private Drawable[] mThumbs; private TransitionDrawable mThumbTransition; public void setBitmap(Bitmap bitmap) { // Make sure uri and original are consistently both null or both // non-null. if (bitmap == null) { mThumb = null; mThumbs = null; setImageDrawable(null); setVisibility(GONE); return; } LayoutParams param = getLayoutParams(); final int miniThumbWidth = param.width - getPaddingLeft() - getPaddingRight(); final int miniThumbHeight = param.height - getPaddingTop() - getPaddingBottom(); mThumb = ThumbnailUtils.extractThumbnail( bitmap, miniThumbWidth, miniThumbHeight); Drawable drawable; if (mThumbs == null || !mEnableAnimation) { mThumbs = new Drawable[2]; mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); setImageDrawable(mThumbs[1]); } else { mThumbs[0] = mThumbs[1]; mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); mThumbTransition = new TransitionDrawable(mThumbs); setImageDrawable(mThumbTransition); mThumbTransition.startTransition(500); } setVisibility(VISIBLE); } }