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 RotateImageView(Context context, AttributeSet attrs)51 public RotateImageView(Context context, AttributeSet attrs) { 52 super(context, attrs); 53 } 54 RotateImageView(Context context)55 public RotateImageView(Context context) { 56 super(context); 57 } 58 getDegree()59 protected int getDegree() { 60 return mTargetDegree; 61 } 62 63 // Rotate the view counter-clockwise 64 @Override setOrientation(int degree, boolean animation)65 public void setOrientation(int degree, boolean animation) { 66 mEnableAnimation = animation; 67 // make sure in the range of [0, 359] 68 degree = degree >= 0 ? degree % 360 : degree % 360 + 360; 69 if (degree == mTargetDegree) return; 70 71 mTargetDegree = degree; 72 if (mEnableAnimation) { 73 mStartDegree = mCurrentDegree; 74 mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis(); 75 76 int diff = mTargetDegree - mCurrentDegree; 77 diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359] 78 79 // Make it in range [-179, 180]. That's the shorted distance between the 80 // two angles 81 diff = diff > 180 ? diff - 360 : diff; 82 83 mClockwise = diff >= 0; 84 mAnimationEndTime = mAnimationStartTime 85 + Math.abs(diff) * 1000 / ANIMATION_SPEED; 86 } else { 87 mCurrentDegree = mTargetDegree; 88 } 89 90 invalidate(); 91 } 92 93 @Override onDraw(Canvas canvas)94 protected void onDraw(Canvas canvas) { 95 Drawable drawable = getDrawable(); 96 if (drawable == null) return; 97 98 Rect bounds = drawable.getBounds(); 99 int w = bounds.right - bounds.left; 100 int h = bounds.bottom - bounds.top; 101 102 if (w == 0 || h == 0) return; // nothing to draw 103 104 if (mCurrentDegree != mTargetDegree) { 105 long time = AnimationUtils.currentAnimationTimeMillis(); 106 if (time < mAnimationEndTime) { 107 int deltaTime = (int)(time - mAnimationStartTime); 108 int degree = mStartDegree + ANIMATION_SPEED 109 * (mClockwise ? deltaTime : -deltaTime) / 1000; 110 degree = degree >= 0 ? degree % 360 : degree % 360 + 360; 111 mCurrentDegree = degree; 112 invalidate(); 113 } else { 114 mCurrentDegree = mTargetDegree; 115 } 116 } 117 118 int left = getPaddingLeft(); 119 int top = getPaddingTop(); 120 int right = getPaddingRight(); 121 int bottom = getPaddingBottom(); 122 int width = getWidth() - left - right; 123 int height = getHeight() - top - bottom; 124 125 int saveCount = canvas.getSaveCount(); 126 127 // Scale down the image first if required. 128 if ((getScaleType() == ImageView.ScaleType.FIT_CENTER) && 129 ((width < w) || (height < h))) { 130 float ratio = Math.min((float) width / w, (float) height / h); 131 canvas.scale(ratio, ratio, width / 2.0f, height / 2.0f); 132 } 133 canvas.translate(left + width / 2, top + height / 2); 134 canvas.rotate(-mCurrentDegree); 135 canvas.translate(-w / 2, -h / 2); 136 drawable.draw(canvas); 137 canvas.restoreToCount(saveCount); 138 } 139 140 private Bitmap mThumb; 141 private Drawable[] mThumbs; 142 private TransitionDrawable mThumbTransition; 143 setBitmap(Bitmap bitmap)144 public void setBitmap(Bitmap bitmap) { 145 // Make sure uri and original are consistently both null or both 146 // non-null. 147 if (bitmap == null) { 148 mThumb = null; 149 mThumbs = null; 150 setImageDrawable(null); 151 setVisibility(GONE); 152 return; 153 } 154 155 LayoutParams param = getLayoutParams(); 156 final int miniThumbWidth = param.width 157 - getPaddingLeft() - getPaddingRight(); 158 final int miniThumbHeight = param.height 159 - getPaddingTop() - getPaddingBottom(); 160 mThumb = ThumbnailUtils.extractThumbnail( 161 bitmap, miniThumbWidth, miniThumbHeight); 162 Drawable drawable; 163 if (mThumbs == null || !mEnableAnimation) { 164 mThumbs = new Drawable[2]; 165 mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); 166 setImageDrawable(mThumbs[1]); 167 } else { 168 mThumbs[0] = mThumbs[1]; 169 mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); 170 mThumbTransition = new TransitionDrawable(mThumbs); 171 setImageDrawable(mThumbTransition); 172 mThumbTransition.startTransition(500); 173 } 174 setVisibility(VISIBLE); 175 } 176 } 177