1ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin/* 2ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * Copyright (C) 2009 The Android Open Source Project 3ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * 4ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * Licensed under the Apache License, Version 2.0 (the "License"); 5ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * you may not use this file except in compliance with the License. 6ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * You may obtain a copy of the License at 7ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * 8ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * http://www.apache.org/licenses/LICENSE-2.0 9ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * 10ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * Unless required by applicable law or agreed to in writing, software 11ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * distributed under the License is distributed on an "AS IS" BASIS, 12ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * See the License for the specific language governing permissions and 14ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * limitations under the License. 15ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin */ 16ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 176fb969e2edd99340b3b77eae4e7b313a82937930Andy Stadlerpackage com.android.camera.ui; 18ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 19ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Linimport android.content.Context; 209f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Liimport android.graphics.Bitmap; 21ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Linimport android.graphics.Canvas; 226fb969e2edd99340b3b77eae4e7b313a82937930Andy Stadlerimport android.graphics.Rect; 239f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Liimport android.graphics.drawable.BitmapDrawable; 24ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Linimport android.graphics.drawable.Drawable; 256fb969e2edd99340b3b77eae4e7b313a82937930Andy Stadlerimport android.graphics.drawable.TransitionDrawable; 269f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Liimport android.media.ThumbnailUtils; 27ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Linimport android.util.AttributeSet; 289f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Liimport android.view.ViewGroup.LayoutParams; 296fb969e2edd99340b3b77eae4e7b313a82937930Andy Stadlerimport android.view.animation.AnimationUtils; 30ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Linimport android.widget.ImageView; 31ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 32ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin/** 33ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin * A @{code ImageView} which can rotate it's content. 34ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin */ 35c12e12365fc3b0b3b4718eced8f3dc6a776c0851Angus Kongpublic class RotateImageView extends TwoStateImageView implements Rotatable { 36ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 37ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin @SuppressWarnings("unused") 38ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin private static final String TAG = "RotateImageView"; 39ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 405e270b255775e1aae76cb476873001798ad8e88aWu-cheng Li private static final int ANIMATION_SPEED = 270; // 270 deg/sec 41b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin 42b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin private int mCurrentDegree = 0; // [0, 359] 43b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin private int mStartDegree = 0; 44b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin private int mTargetDegree = 0; 45b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin 469f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li private boolean mClockwise = false, mEnableAnimation = true; 47b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin 48b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin private long mAnimationStartTime = 0; 49b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin private long mAnimationEndTime = 0; 50ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 51ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin public RotateImageView(Context context, AttributeSet attrs) { 52ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin super(context, attrs); 53ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin } 54ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 55cead50ba3ce2df3bd9fcf06c6f54ea0c6980f0d7repo sync public RotateImageView(Context context) { 56cead50ba3ce2df3bd9fcf06c6f54ea0c6980f0d7repo sync super(context); 57cead50ba3ce2df3bd9fcf06c6f54ea0c6980f0d7repo sync } 58cead50ba3ce2df3bd9fcf06c6f54ea0c6980f0d7repo sync 599f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li public void enableAnimation(boolean enable) { 609f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mEnableAnimation = enable; 619f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li } 629f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li 63cead50ba3ce2df3bd9fcf06c6f54ea0c6980f0d7repo sync protected int getDegree() { 64cead50ba3ce2df3bd9fcf06c6f54ea0c6980f0d7repo sync return mTargetDegree; 65cead50ba3ce2df3bd9fcf06c6f54ea0c6980f0d7repo sync } 66cead50ba3ce2df3bd9fcf06c6f54ea0c6980f0d7repo sync 67c5e37cbb96b8284203560c51dd5f41c705a21f58Wu-cheng Li // Rotate the view counter-clockwise 683ebe49d8f1b4defb7cfd4850a14e795aada2ebd1Chih-yu Huang public void setOrientation(int degree) { 69ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin // make sure in the range of [0, 359] 70ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin degree = degree >= 0 ? degree % 360 : degree % 360 + 360; 71b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin if (degree == mTargetDegree) return; 72b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin 73b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin mTargetDegree = degree; 74b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin mStartDegree = mCurrentDegree; 75b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis(); 76b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin 77b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin int diff = mTargetDegree - mCurrentDegree; 78b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359] 79b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin 80b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin // Make it in range [-179, 180]. That's the shorted distance between the 81b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin // two angles 82b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin diff = diff > 180 ? diff - 360 : diff; 83b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin 84b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin mClockwise = diff >= 0; 85b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin mAnimationEndTime = mAnimationStartTime 86b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin + Math.abs(diff) * 1000 / ANIMATION_SPEED; 87b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin 88ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin invalidate(); 89ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin } 90ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 91ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin @Override 92ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin protected void onDraw(Canvas canvas) { 93ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin Drawable drawable = getDrawable(); 946988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin if (drawable == null) return; 95b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin 96ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin Rect bounds = drawable.getBounds(); 97ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin int w = bounds.right - bounds.left; 98ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin int h = bounds.bottom - bounds.top; 99ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 100ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin if (w == 0 || h == 0) return; // nothing to draw 101ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin 102b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin if (mCurrentDegree != mTargetDegree) { 103b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin long time = AnimationUtils.currentAnimationTimeMillis(); 104b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin if (time < mAnimationEndTime) { 105b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin int deltaTime = (int)(time - mAnimationStartTime); 106b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin int degree = mStartDegree + ANIMATION_SPEED 107b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin * (mClockwise ? deltaTime : -deltaTime) / 1000; 108b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin degree = degree >= 0 ? degree % 360 : degree % 360 + 360; 109b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin mCurrentDegree = degree; 110b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin invalidate(); 111b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin } else { 112b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin mCurrentDegree = mTargetDegree; 113b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin } 114b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin } 1156988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin 1166988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin int left = getPaddingLeft(); 1176988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin int top = getPaddingTop(); 1186988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin int right = getPaddingRight(); 1196988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin int bottom = getPaddingBottom(); 1206988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin int width = getWidth() - left - right; 1216988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin int height = getHeight() - top - bottom; 1226988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin 123b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin int saveCount = canvas.getSaveCount(); 124b5c569214281e11dc7e8ffc313fbb27e5ff6e5c5Chung-yih Wang 125b5c569214281e11dc7e8ffc313fbb27e5ff6e5c5Chung-yih Wang // Scale down the image first if required. 126b5c569214281e11dc7e8ffc313fbb27e5ff6e5c5Chung-yih Wang if ((getScaleType() == ImageView.ScaleType.FIT_CENTER) && 127b5c569214281e11dc7e8ffc313fbb27e5ff6e5c5Chung-yih Wang ((width < w) || (height < h))) { 128b5c569214281e11dc7e8ffc313fbb27e5ff6e5c5Chung-yih Wang float ratio = Math.min((float) width / w, (float) height / h); 129b5c569214281e11dc7e8ffc313fbb27e5ff6e5c5Chung-yih Wang canvas.scale(ratio, ratio, width / 2.0f, height / 2.0f); 130b5c569214281e11dc7e8ffc313fbb27e5ff6e5c5Chung-yih Wang } 1316988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin canvas.translate(left + width / 2, top + height / 2); 132b77f928e01f24fc36ea4808b7373f502e7e6be41Owen Lin canvas.rotate(-mCurrentDegree); 1336988d4e2ef8d14d8bf3ee81c8eb3175bbf1b88ecOwen Lin canvas.translate(-w / 2, -h / 2); 134ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin drawable.draw(canvas); 135ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin canvas.restoreToCount(saveCount); 136ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin } 1379f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li 1389f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li private Bitmap mThumb; 1399f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li private Drawable[] mThumbs; 1409f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li private TransitionDrawable mThumbTransition; 1419f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li 1428ab2b624d51b3b8254ece98c46a7e22a6fb5d4aaWu-cheng Li public void setBitmap(Bitmap bitmap) { 1439f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li // Make sure uri and original are consistently both null or both 1449f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li // non-null. 1458ab2b624d51b3b8254ece98c46a7e22a6fb5d4aaWu-cheng Li if (bitmap == null) { 1469f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mThumb = null; 1479f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mThumbs = null; 1489f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li setImageDrawable(null); 1498ab2b624d51b3b8254ece98c46a7e22a6fb5d4aaWu-cheng Li setVisibility(GONE); 1509f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li return; 1519f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li } 1529f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li 1539f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li LayoutParams param = getLayoutParams(); 1549f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li final int miniThumbWidth = param.width 1559f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li - getPaddingLeft() - getPaddingRight(); 1569f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li final int miniThumbHeight = param.height 1579f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li - getPaddingTop() - getPaddingBottom(); 1589f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mThumb = ThumbnailUtils.extractThumbnail( 1598ab2b624d51b3b8254ece98c46a7e22a6fb5d4aaWu-cheng Li bitmap, miniThumbWidth, miniThumbHeight); 1609f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li Drawable drawable; 1619f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li if (mThumbs == null || !mEnableAnimation) { 1629f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mThumbs = new Drawable[2]; 1639f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); 1649f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li setImageDrawable(mThumbs[1]); 1659f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li } else { 1669f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mThumbs[0] = mThumbs[1]; 1679f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb); 1689f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mThumbTransition = new TransitionDrawable(mThumbs); 1699f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li setImageDrawable(mThumbTransition); 1709f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li mThumbTransition.startTransition(500); 1719f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li } 1728ab2b624d51b3b8254ece98c46a7e22a6fb5d4aaWu-cheng Li setVisibility(VISIBLE); 1739f73bd9a85d295091fae39dc256a122e1843e2e8Wu-cheng Li } 174ff58fb6921ccb7398ad30c8cec604c38c76be4fcOwen Lin} 175