1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera.ui; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.Canvas; 22 import android.graphics.Rect; 23 import android.graphics.drawable.BitmapDrawable; 24 import android.graphics.drawable.Drawable; 25 import android.graphics.drawable.TransitionDrawable; 26 import android.media.ThumbnailUtils; 27 import android.util.AttributeSet; 28 import android.view.ViewGroup.LayoutParams; 29 import android.view.animation.AnimationUtils; 30 import android.widget.ImageView; 31 32 /** 33 * A @{code ImageView} which can rotate it's content. 34 */ 35 public class RotateImageView extends TwoStateImageView implements Rotatable { 36 37 @SuppressWarnings("unused") 38 private static final String TAG = "RotateImageView"; 39 40 private static final int ANIMATION_SPEED = 270; // 270 deg/sec 41 42 private int mCurrentDegree = 0; // [0, 359] 43 private int mStartDegree = 0; 44 private int mTargetDegree = 0; 45 46 private boolean mClockwise = false, mEnableAnimation = true; 47 48 private long mAnimationStartTime = 0; 49 private long mAnimationEndTime = 0; 50 51 public RotateImageView(Context context, AttributeSet attrs) { 52 super(context, attrs); 53 } 54 55 public RotateImageView(Context context) { 56 super(context); 57 } 58 59 public void enableAnimation(boolean enable) { 60 mEnableAnimation = enable; 61 } 62 63 protected int getDegree() { 64 return mTargetDegree; 65 } 66 67 // Rotate the view counter-clockwise 68 public void setOrientation(int degree) { 69 // make sure in the range of [0, 359] 70 degree = degree >= 0 ? degree % 360 : degree % 360 + 360; 71 if (degree == mTargetDegree) return; 72 73 mTargetDegree = degree; 74 mStartDegree = mCurrentDegree; 75 mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis(); 76 77 int diff = mTargetDegree - mCurrentDegree; 78 diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359] 79 80 // Make it in range [-179, 180]. That's the shorted distance between the 81 // two angles 82 diff = diff > 180 ? diff - 360 : diff; 83 84 mClockwise = diff >= 0; 85 mAnimationEndTime = mAnimationStartTime 86 + Math.abs(diff) * 1000 / ANIMATION_SPEED; 87 88 invalidate(); 89 } 90 91 @Override 92 protected void onDraw(Canvas canvas) { 93 Drawable drawable = getDrawable(); 94 if (drawable == null) return; 95 96 Rect bounds = drawable.getBounds(); 97 int w = bounds.right - bounds.left; 98 int h = bounds.bottom - bounds.top; 99 100 if (w == 0 || h == 0) return; // nothing to draw 101 102 if (mCurrentDegree != mTargetDegree) { 103 long time = AnimationUtils.currentAnimationTimeMillis(); 104 if (time < mAnimationEndTime) { 105 int deltaTime = (int)(time - mAnimationStartTime); 106 int degree = mStartDegree + ANIMATION_SPEED 107 * (mClockwise ? deltaTime : -deltaTime) / 1000; 108 degree = degree >= 0 ? degree % 360 : degree % 360 + 360; 109 mCurrentDegree = degree; 110 invalidate(); 111 } else { 112 mCurrentDegree = mTargetDegree; 113 } 114 } 115 116 int left = getPaddingLeft(); 117 int top = getPaddingTop(); 118 int right = getPaddingRight(); 119 int bottom = getPaddingBottom(); 120 int width = getWidth() - left - right; 121 int height = getHeight() - top - bottom; 122 123 int saveCount = canvas.getSaveCount(); 124 125 // Scale down the image first if required. 126 if ((getScaleType() == ImageView.ScaleType.FIT_CENTER) && 127 ((width < w) || (height < h))) { 128 float ratio = Math.min((float) width / w, (float) height / h); 129 canvas.scale(ratio, ratio, width / 2.0f, height / 2.0f); 130 } 131 canvas.translate(left + width / 2, top + height / 2); 132 canvas.rotate(-mCurrentDegree); 133 canvas.translate(-w / 2, -h / 2); 134 drawable.draw(canvas); 135 canvas.restoreToCount(saveCount); 136 } 137 138 private Bitmap mThumb; 139 private Drawable[] mThumbs; 140 private TransitionDrawable mThumbTransition; 141 142 public void setBitmap(Bitmap bitmap) { 143 // Make sure uri and original are consistently both null or both 144 // non-null. 145 if (bitmap == null) { 146 mThumb = null; 147 mThumbs = null; 148 setImageDrawable(null); 149 setVisibility(GONE); 150 return; 151 } 152 153 LayoutParams param = getLayoutParams(); 154 final int miniThumbWidth = param.width 155 - getPaddingLeft() - getPaddingRight(); 156 final int miniThumbHeight = param.height 157 - getPaddingTop() - getPaddingBottom(); 158 mThumb = ThumbnailUtils.extractThumbnail( 159 bitmap, miniThumbWidth, miniThumbHeight); 160 Drawable drawable; 161 if (mThumbs == null || !mEnableAnimation) { 162 mThumbs = new Drawable[2]; 163 mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); 164 setImageDrawable(mThumbs[1]); 165 } else { 166 mThumbs[0] = mThumbs[1]; 167 mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); 168 mThumbTransition = new TransitionDrawable(mThumbs); 169 setImageDrawable(mThumbTransition); 170 mThumbTransition.startTransition(500); 171 } 172 setVisibility(VISIBLE); 173 } 174 } 175