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