summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSpike Sprague <spikuru@google.com>2014-08-13 21:14:50 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-08-13 16:52:03 +0000
commiteb9443226ee117779e2396c2f7cee9789af80cf8 (patch)
tree72bd740d96c75a26d56d390a1a277abd0bb6b3ff
parent62c367f2199d826646cd2c665ed358b339c4b313 (diff)
parent0496fcaa51f88f6c753975473b971941e4090cfa (diff)
downloadandroid_packages_apps_Camera2-eb9443226ee117779e2396c2f7cee9789af80cf8.tar.gz
android_packages_apps_Camera2-eb9443226ee117779e2396c2f7cee9789af80cf8.tar.bz2
android_packages_apps_Camera2-eb9443226ee117779e2396c2f7cee9789af80cf8.zip
Merge "update mode switch transition anims" into ub-camera-glacier
-rw-r--r--res/values/colors.xml2
-rw-r--r--src/com/android/camera/app/CameraAppUI.java1
-rw-r--r--src/com/android/camera/ui/ModeIconView.java86
-rw-r--r--src/com/android/camera/ui/ModeListView.java243
-rw-r--r--src/com/android/camera/ui/ModeSelectorItem.java26
-rw-r--r--src/com/android/camera/ui/TouchCircleDrawable.java310
6 files changed, 477 insertions, 191 deletions
diff --git a/res/values/colors.xml b/res/values/colors.xml
index f8bd430a8..a8ee96b61 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -102,4 +102,6 @@
<color name="dialog_text_color">#6D6D6D</color>
<color name="settings_cling_color">#2962FF</color>
+
+ <color name="mode_icon_hover_highlight">#2DFFFFFF</color>
</resources>
diff --git a/src/com/android/camera/app/CameraAppUI.java b/src/com/android/camera/app/CameraAppUI.java
index 43e207c25..8a10e2cb6 100644
--- a/src/com/android/camera/app/CameraAppUI.java
+++ b/src/com/android/camera/app/CameraAppUI.java
@@ -1396,6 +1396,7 @@ public class CameraAppUI implements ModeListView.ModeSwitchListener,
mModeCoverState = COVER_SHOWN;
int lastIndex = mController.getCurrentModuleIndex();
+ // Actual mode teardown / new mode initialization happens here
mController.onModeSelected(modeIndex);
int currentIndex = mController.getCurrentModuleIndex();
diff --git a/src/com/android/camera/ui/ModeIconView.java b/src/com/android/camera/ui/ModeIconView.java
index 42389f842..e7e2701c6 100644
--- a/src/com/android/camera/ui/ModeIconView.java
+++ b/src/com/android/camera/ui/ModeIconView.java
@@ -16,8 +16,6 @@
package com.android.camera.ui;
-import android.animation.Animator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
@@ -36,19 +34,16 @@ import com.android.camera2.R;
* whereas a state list drawable would require a different drawable for each state.
*/
public class ModeIconView extends View {
-
- private static final int SELECTION_ANIMATION_DURATION_MS = 500;
- private static final int HIGHLIGHT_STATE_ALPHA = 0x4C;
private boolean mHighlightIsOn = false;
private final GradientDrawable mBackground;
- private final GradientDrawable mHighlightDrawable;
+ private final GradientDrawable mHoverDrawable;
+
private final int mIconBackgroundSize;
private int mHighlightColor;
private final int mBackgroundDefaultColor;
private final int mIconDrawableSize;
private Drawable mIconDrawable = null;
private boolean mSelected = false;
- private ValueAnimator mSelectionAnimation;
public ModeIconView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -58,11 +53,13 @@ public class ModeIconView extends View {
mBackground = (GradientDrawable) getResources()
.getDrawable(R.drawable.mode_icon_background).mutate();
mBackground.setBounds(0, 0, mIconBackgroundSize, mIconBackgroundSize);
- mHighlightDrawable = (GradientDrawable) getResources()
+ mHoverDrawable = (GradientDrawable) getResources()
.getDrawable(R.drawable.mode_icon_background).mutate();
- mHighlightDrawable.setBounds(0, 0, mIconBackgroundSize, mIconBackgroundSize);
+ mHoverDrawable.setBounds(0, 0, mIconBackgroundSize, mIconBackgroundSize);
mIconDrawableSize = getResources().getDimensionPixelSize(
R.dimen.mode_selector_icon_drawable_size);
+
+ mHoverDrawable.setColor(getResources().getColor(R.color.mode_icon_hover_highlight));
}
/**
@@ -72,6 +69,7 @@ public class ModeIconView extends View {
*/
public void setIconDrawable(Drawable drawable) {
mIconDrawable = drawable;
+
// Center icon in the background.
if (mIconDrawable != null) {
mIconDrawable.setBounds(mIconBackgroundSize / 2 - mIconDrawableSize / 2,
@@ -86,14 +84,27 @@ public class ModeIconView extends View {
public void draw(Canvas canvas) {
super.draw(canvas);
if (mHighlightIsOn && !mSelected) {
- mHighlightDrawable.draw(canvas);
+ mHoverDrawable.draw(canvas);
} else {
mBackground.draw(canvas);
}
if (mIconDrawable != null) {
mIconDrawable.draw(canvas);
}
+ }
+ /**
+ * @return A clone of the icon drawable associated with this view.
+ */
+ public Drawable getIconDrawableClone() {
+ return mIconDrawable.getConstantState().newDrawable();
+ }
+
+ /**
+ * @return The size of the icon drawable.
+ */
+ public int getIconDrawableSize() {
+ return mIconDrawableSize;
}
/**
@@ -102,6 +113,7 @@ public class ModeIconView extends View {
*
* @param selected true when selected, false otherwise.
*/
+ @Override
public void setSelected(boolean selected) {
if (selected) {
mBackground.setColor(mHighlightColor);
@@ -109,55 +121,12 @@ public class ModeIconView extends View {
} else {
mBackground.setColor(mBackgroundDefaultColor);
}
+
mSelected = selected;
invalidate();
}
/**
- * Animate mode icon background from highlight state to selected state.
- * TODO: Remove the selection animation if UX agrees to do so.
- */
- public void selectWithAnimation() {
- mSelected = true;
- mHighlightIsOn = false;
- // Animate alpha between highlight alpha to selected state alpha.
- mSelectionAnimation = ValueAnimator.ofInt(HIGHLIGHT_STATE_ALPHA, 255);
- mSelectionAnimation.setDuration(SELECTION_ANIMATION_DURATION_MS);
- mSelectionAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int alpha = (Integer) animation.getAnimatedValue();
- int backgroundColor = (mHighlightColor & 0xffffff) | (alpha << 24);
- mBackground.setColor(backgroundColor);
- invalidate();
- }
- });
- mSelectionAnimation.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- // Do nothing.
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mSelectionAnimation = null;
- invalidate();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- // Do nothing.
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- // Do nothing.
- }
- });
- mSelectionAnimation.start();
- }
-
- /**
* This gets called when the highlighted state is changed. When highlighted,
* a ring shaped drawable of a solid pre-defined color will be drawn on top
* of the background drawable to indicate highlight state.
@@ -176,7 +145,12 @@ public class ModeIconView extends View {
*/
public void setHighlightColor(int highlightColor) {
mHighlightColor = highlightColor;
- highlightColor = (highlightColor & 0xffffff) | 0x4C000000;
- mHighlightDrawable.setColor(highlightColor);
+ }
+
+ /**
+ * @return The highlightColor color the the highlight state.
+ */
+ public int getHighlightColor() {
+ return mHighlightColor;
}
}
diff --git a/src/com/android/camera/ui/ModeListView.java b/src/com/android/camera/ui/ModeListView.java
index bca4c3f8f..78ab9863c 100644
--- a/src/com/android/camera/ui/ModeListView.java
+++ b/src/com/android/camera/ui/ModeListView.java
@@ -17,6 +17,7 @@
package com.android.camera.ui;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
@@ -25,13 +26,13 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
-import android.os.AsyncTask;
import android.os.SystemClock;
import android.util.AttributeSet;
-import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -826,7 +827,6 @@ public class ModeListView extends FrameLayout
* mode list will transition into fully hidden state.
*/
private class SelectedState extends ModeListState {
-
public SelectedState(ModeSelectorItem selectedItem) {
final int modeId = selectedItem.getModeId();
// Un-highlight all the modes.
@@ -834,8 +834,7 @@ public class ModeListView extends FrameLayout
mModeSelectorItems[i].setHighlighted(false);
mModeSelectorItems[i].setSelected(false);
}
- // Select the focused item.
- selectedItem.setSelected(true);
+
PeepholeAnimationEffect effect = new PeepholeAnimationEffect();
effect.setSize(mWidth, mHeight);
@@ -853,6 +852,7 @@ public class ModeListView extends FrameLayout
iconY -= location[1];
effect.setAnimationStartingPosition(iconX, iconY);
+ effect.setModeSpecificColor(selectedItem.getHighlightColor());
if (mScreenShotProvider != null) {
effect.setBackground(mScreenShotProvider
.getPreviewFrame(PREVIEW_DOWN_SAMPLE_FACTOR),
@@ -860,17 +860,8 @@ public class ModeListView extends FrameLayout
effect.setBackgroundOverlay(mScreenShotProvider.getPreviewOverlayAndControls());
}
mCurrentAnimationEffects = effect;
+ effect.startFadeoutAnimation(null, selectedItem, iconX, iconY, modeId);
invalidate();
-
- // Post mode selection runnable to the end of the message queue
- // so that current UI changes can finish before mode initialization
- // clogs up UI thread.
- post(new Runnable() {
- @Override
- public void run() {
- onModeSelected(modeId);
- }
- });
}
@Override
@@ -880,27 +871,12 @@ public class ModeListView extends FrameLayout
@Override
public void startModeSelectionAnimation() {
- mCurrentAnimationEffects.startAnimation(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
-
- }
-
+ mCurrentAnimationEffects.startAnimation(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimationEffects = null;
mCurrentStateManager.setCurrentState(new FullyHiddenState());
}
-
- @Override
- public void onAnimationCancel(Animator animation) {
-
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
-
- }
});
}
@@ -1137,7 +1113,7 @@ public class ModeListView extends FrameLayout
// Mark the supported modes in a boolean array to preserve the
// sequence of the modes
- SparseArray<Boolean> modeIsSupported = new SparseArray<Boolean>();
+ SparseBooleanArray modeIsSupported = new SparseBooleanArray();
for (int i = 0; i < modeIndexList.size(); i++) {
int mode = modeIndexList.get(i);
modeIsSupported.put(mode, true);
@@ -1668,6 +1644,7 @@ public class ModeListView extends FrameLayout
* When visible width of list is changed, the background of the list needs
* to darken/lighten correspondingly.
*/
+ @Override
public void onVisibleWidthChanged(int visibleWidth) {
mVisibleWidth = visibleWidth;
@@ -1835,7 +1812,6 @@ public class ModeListView extends FrameLayout
animateModeItemsInOrder = false;
delay *= -1;
}
- int focusItem = mFocusItem == NO_ITEM_SELECTED ? 0 : mFocusItem;
for (int i = 0; i < mTotalModes; i++) {
ObjectAnimator animator;
if (animateModeItemsInOrder) {
@@ -1908,11 +1884,11 @@ public class ModeListView extends FrameLayout
float position;
int slowZone = (int) (maxWidth * SLOW_ZONE_PERCENTAGE);
if (lastVisibleWidth < (maxWidth - slowZone)) {
- position = VELOCITY_THRESHOLD * (float) timeElapsed + lastVisibleWidth;
+ position = VELOCITY_THRESHOLD * timeElapsed + lastVisibleWidth;
} else {
float percentageIntoSlowZone = (lastVisibleWidth - (maxWidth - slowZone)) / slowZone;
float velocity = (1 - percentageIntoSlowZone) * VELOCITY_THRESHOLD;
- position = velocity * (float) timeElapsed + lastVisibleWidth;
+ position = velocity * timeElapsed + lastVisibleWidth;
}
position = Math.min(maxWidth, position);
return position;
@@ -1924,22 +1900,29 @@ public class ModeListView extends FrameLayout
private final static int PEEP_HOLE_ANIMATION_DURATION_MS = 300;
private final Paint mMaskPaint = new Paint();
- private final Paint mBackgroundPaint = new Paint();
private final RectF mBackgroundDrawArea = new RectF();
- private int mWidth;
- private int mHeight;
private int mPeepHoleCenterX = UNSET;
private int mPeepHoleCenterY = UNSET;
private float mRadius = 0f;
private ValueAnimator mPeepHoleAnimator;
private Bitmap mBackground;
- private Bitmap mBlurredBackground;
private Bitmap mBackgroundOverlay;
+ private Paint mCirclePaint = new Paint();
+ private Paint mCoverPaint = new Paint();
+
+ private TouchCircleDrawable mCircleDrawable;
+
public PeepholeAnimationEffect() {
mMaskPaint.setAlpha(0);
mMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+ mCirclePaint.setColor(0);
+ mCirclePaint.setAlpha(0);
+
+ mCoverPaint.setColor(0);
+ mCoverPaint.setAlpha(0);
}
@Override
@@ -1953,24 +1936,18 @@ public class ModeListView extends FrameLayout
return true;
}
- @Override
- public void drawForeground(Canvas canvas) {
- // Draw the circle in clear mode
- if (mPeepHoleAnimator != null) {
- // Draw a transparent circle using clear mode
- canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mMaskPaint);
- }
- }
-
public void setAnimationStartingPosition(int x, int y) {
mPeepHoleCenterX = x;
mPeepHoleCenterY = y;
}
+ public void setModeSpecificColor(int color) {
+ mCirclePaint.setColor(color & 0x00ffffff);
+ }
+
/**
* Sets the bitmap to be drawn in the background and the drawArea to draw
- * the bitmap. In the meantime, start processing the image in a background
- * thread to get a blurred background image.
+ * the bitmap.
*
* @param background image to be drawn in the background
* @param drawArea area to draw the background image
@@ -1978,8 +1955,6 @@ public class ModeListView extends FrameLayout
public void setBackground(Bitmap background, RectF drawArea) {
mBackground = background;
mBackgroundDrawArea.set(drawArea);
- new BlurTask().execute(Bitmap.createScaledBitmap(background, background.getWidth(),
- background.getHeight(), true));
}
/**
@@ -1989,37 +1964,26 @@ public class ModeListView extends FrameLayout
mBackgroundOverlay = overlay;
}
- /**
- * This gets called when a blurred image of the background is generated.
- * Start an animation to fade in the blur.
- *
- * @param blur blurred image of the background.
- */
- public void setBlurredBackground(Bitmap blur) {
- mBlurredBackground = blur;
- // Start fade in.
- ObjectAnimator alpha = ObjectAnimator.ofInt(mBackgroundPaint, "alpha", 80, 255);
- alpha.setDuration(250);
- alpha.setInterpolator(Gusterpolator.INSTANCE);
- alpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidate();
- }
- });
- alpha.start();
- invalidate();
- }
-
@Override
public void drawBackground(Canvas canvas) {
if (mBackground != null && mBackgroundOverlay != null) {
- canvas.drawARGB(255, 0, 0, 0);
canvas.drawBitmap(mBackground, null, mBackgroundDrawArea, null);
- if (mBlurredBackground != null) {
- canvas.drawBitmap(mBlurredBackground, null, mBackgroundDrawArea, mBackgroundPaint);
- }
+ canvas.drawPaint(mCoverPaint);
canvas.drawBitmap(mBackgroundOverlay, 0, 0, null);
+
+ if (mCircleDrawable != null) {
+ mCircleDrawable.draw(canvas);
+ }
+ }
+ }
+
+ @Override
+ public void drawForeground(Canvas canvas) {
+ // Draw the circle in clear mode
+ if (mPeepHoleAnimator != null) {
+ // Draw a transparent circle using clear mode
+ canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mMaskPaint);
+ canvas.drawCircle(mPeepHoleCenterX, mPeepHoleCenterY, mRadius, mCirclePaint);
}
}
@@ -2030,6 +1994,62 @@ public class ModeListView extends FrameLayout
return (mBackground == null || mBackgroundOverlay == null);
}
+ public void startFadeoutAnimation(Animator.AnimatorListener listener,
+ final ModeSelectorItem selectedItem,
+ int x, int y, final int modeId) {
+ mCoverPaint.setColor(0);
+ mCoverPaint.setAlpha(0);
+
+ ValueAnimator alphaAnimator = ValueAnimator.ofInt(0, 255);
+ alphaAnimator.setDuration(100);
+ alphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
+ alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mCoverPaint.setAlpha((Integer) animation.getAnimatedValue());
+ invalidate();
+ }
+ });
+ if (listener != null) {
+ alphaAnimator.addListener(listener);
+ }
+
+ int size = getContext().getResources()
+ .getDimensionPixelSize(R.dimen.mode_selector_icon_block_width);
+ mCircleDrawable = new TouchCircleDrawable(getContext().getResources());
+ mCircleDrawable.setIconDrawable(
+ selectedItem.getIcon().getIconDrawableClone(),
+ selectedItem.getIcon().getIconDrawableSize());
+ mCircleDrawable.setSize(size, size);
+ mCircleDrawable.setCenter(new Point(x, y));
+ mCircleDrawable.setColor(selectedItem.getHighlightColor());
+ mCircleDrawable.setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ invalidate();
+ }
+ });
+
+ mCircleDrawable.setAnimatorListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Post mode selection runnable to the end of the message queue
+ // so that current UI changes can finish before mode initialization
+ // clogs up UI thread.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ // Select the focused item.
+ selectedItem.setSelected(true);
+ onModeSelected(modeId);
+ }
+ });
+ }
+ });
+ mCircleDrawable.animate();
+ alphaAnimator.start();
+ }
+
@Override
public void startAnimation(Animator.AnimatorListener listener) {
if (mPeepHoleAnimator != null && mPeepHoleAnimator.isRunning()) {
@@ -2047,7 +2067,7 @@ public class ModeListView extends FrameLayout
int startRadius = getResources().getDimensionPixelSize(
R.dimen.mode_selector_icon_block_width) / 2;
- mPeepHoleAnimator = ValueAnimator.ofFloat(0, endRadius);
+ mPeepHoleAnimator = ValueAnimator.ofFloat(startRadius, endRadius);
mPeepHoleAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
mPeepHoleAnimator.setInterpolator(Gusterpolator.INSTANCE);
mPeepHoleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -2063,7 +2083,7 @@ public class ModeListView extends FrameLayout
mPeepHoleAnimator.addListener(listener);
}
- mPeepHoleAnimator.addListener(new Animator.AnimatorListener() {
+ mPeepHoleAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// Sets a HW layer on the view for the animation.
@@ -2075,19 +2095,38 @@ public class ModeListView extends FrameLayout
// Sets the layer type back to NONE as a workaround for b/12594617.
setLayerType(LAYER_TYPE_NONE, null);
}
+ });
+ mCirclePaint.setAlpha(255);
+ mCoverPaint.setAlpha(255);
+ ValueAnimator alphaAnimator = ValueAnimator.ofInt(255, 0);
+ alphaAnimator.setDuration(PEEP_HOLE_ANIMATION_DURATION_MS);
+ alphaAnimator.setInterpolator(Gusterpolator.INSTANCE);
+ alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
- public void onAnimationCancel(Animator animation) {
-
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int alpha = (Integer) animation.getAnimatedValue();
+ mCirclePaint.setAlpha(alpha);
+ mCoverPaint.setAlpha(alpha);
}
-
+ });
+ alphaAnimator.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationRepeat(Animator animation) {
+ public void onAnimationStart(Animator animation) {
+ // Sets a HW layer on the view for the animation.
+ setLayerType(LAYER_TYPE_HARDWARE, null);
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Sets the layer type back to NONE as a workaround for b/12594617.
+ setLayerType(LAYER_TYPE_NONE, null);
}
});
+
mPeepHoleAnimator.start();
+ alphaAnimator.start();
}
@Override
@@ -2103,41 +2142,5 @@ public class ModeListView extends FrameLayout
return true;
}
}
-
- private class BlurTask extends AsyncTask<Bitmap, Integer, Bitmap> {
-
- // Gaussian blur mask size.
- private static final int MASK_SIZE = 7;
- @Override
- protected Bitmap doInBackground(Bitmap... params) {
-
- Bitmap intermediateBitmap = params[0];
- int factor = 4;
- Bitmap lowResPreview = Bitmap.createScaledBitmap(intermediateBitmap,
- intermediateBitmap.getWidth() / factor,
- intermediateBitmap.getHeight() / factor, true);
-
- int width = lowResPreview.getWidth();
- int height = lowResPreview.getHeight();
-
- if (mInputPixels == null || mInputPixels.length < width * height) {
- mInputPixels = new int[width * height];
- mOutputPixels = new int[width * height];
- }
- lowResPreview.getPixels(mInputPixels, 0, width, 0, 0, width, height);
- CameraUtil.blur(mInputPixels, mOutputPixels, width, height, MASK_SIZE);
- lowResPreview.setPixels(mOutputPixels, 0, width, 0, 0, width, height);
-
- intermediateBitmap.recycle();
- return Bitmap.createScaledBitmap(lowResPreview, width * factor,
- height * factor, true);
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- setBlurredBackground(bitmap);
- }
- };
}
-
}
diff --git a/src/com/android/camera/ui/ModeSelectorItem.java b/src/com/android/camera/ui/ModeSelectorItem.java
index c23641d64..8b535b4c5 100644
--- a/src/com/android/camera/ui/ModeSelectorItem.java
+++ b/src/com/android/camera/ui/ModeSelectorItem.java
@@ -22,7 +22,6 @@ import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
-import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -48,19 +47,13 @@ class ModeSelectorItem extends FrameLayout {
public static final int FLY_IN = 1;
public static final int FLY_OUT = 2;
- private static final int SHADE_WIDTH_PIX = 100;
-
private TextView mText;
private ModeIconView mIcon;
private int mVisibleWidth = 0;
private final int mMinVisibleWidth;
private VisibleWidthChangedListener mListener = null;
- private int mDrawingMode = FLY_IN;
- private int mHeight;
private int mWidth;
- private int mDefaultBackgroundColor;
- private int mDefaultTextColor;
private int mModeId;
/**
@@ -92,11 +85,9 @@ class ModeSelectorItem extends FrameLayout {
"Roboto-Medium.ttf");
}
mText.setTypeface(typeface);
- mDefaultTextColor = mText.getCurrentTextColor();
}
public void setDefaultBackgroundColor(int color) {
- mDefaultBackgroundColor = color;
setBackgroundColor(color);
}
@@ -112,6 +103,7 @@ class ModeSelectorItem extends FrameLayout {
mIcon.setHighlighted(highlighted);
}
+ @Override
public void setSelected(boolean selected) {
mIcon.setSelected(selected);
}
@@ -145,7 +137,6 @@ class ModeSelectorItem extends FrameLayout {
* to right)
*/
public void onSwipeModeChanged(boolean swipeIn) {
- mDrawingMode = swipeIn ? FLY_IN : FLY_OUT;
mText.setTranslationX(0);
}
@@ -157,11 +148,9 @@ class ModeSelectorItem extends FrameLayout {
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mWidth = right - left;
- mHeight = bottom - top;
if (changed && mVisibleWidth > 0) {
// Reset mode list to full screen
setVisibleWidth(mWidth);
- mDrawingMode = FLY_OUT;
}
}
@@ -244,6 +233,13 @@ class ModeSelectorItem extends FrameLayout {
}
/**
+ * @return highlightColor color for the highlight state
+ */
+ public int getHighlightColor() {
+ return mIcon.getHighlightColor();
+ }
+
+ /**
* Gets the maximum visible width of the mode icon. The mode item will be
* full shown when the mode icon has max visible width.
*/
@@ -279,10 +275,10 @@ class ModeSelectorItem extends FrameLayout {
}
/**
- * Animate mode icon to selected state.
+ * @return The {@link ModeIconView} attached to this item.
*/
- public void selectWithAnimation() {
- mIcon.selectWithAnimation();
+ public ModeIconView getIcon() {
+ return mIcon;
}
/**
diff --git a/src/com/android/camera/ui/TouchCircleDrawable.java b/src/com/android/camera/ui/TouchCircleDrawable.java
new file mode 100644
index 000000000..f177a1767
--- /dev/null
+++ b/src/com/android/camera/ui/TouchCircleDrawable.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2014 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.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+
+import com.android.camera.util.Gusterpolator;
+import com.android.camera2.R;
+
+/**
+ * This class implements a circular drawable that starts with a zero radius
+ * and can be triggered to animate expand to a given radius.
+ * <p>
+ * There are three colors associated with this drawable:
+ * <p>
+ * A background color, which is loaded from a resource
+ * R.color.mode_icon_hover_highlight.
+ * <p>
+ * A base color, which is attached a circle that is animate expanded first and
+ * drawn underneath the main color.
+ * <p>
+ * And, a main color, which is attached to the main circle that is expanded last
+ * and is drawn on top of the other colors.
+ * <p>
+ * The driving purpose for this class is to implement a Material-like look and
+ * feel for mode switcher touch events.
+ */
+public class TouchCircleDrawable extends Drawable {
+ private static final int BASE_CIRCLE_ANIM_DURATION_MS = 200;
+ private static final int CIRCLE_ANIM_DURATION_MS = 130;
+ private static final int CIRCLE_ANIM_DURATION_DELAY_MS = 100;
+
+ private Paint mColorPaint = new Paint();
+ private Paint mBasePaint = new Paint();
+ private Paint mBackgroundPaint = new Paint();
+ private int mColor;
+ private int mBaseColor;
+ private int mColorAlpha = 0xff;
+ private int mBaseAlpha = 0x4b;
+ private int mColorRadius;
+ private int mBaseRadius;
+ private int mBackgroundRadius;
+ private Drawable mIconDrawable;
+ private int mIconDrawableSize;
+ private boolean mDrawBackground;
+
+ private Animator.AnimatorListener mAnimatorListener;
+ private ValueAnimator.AnimatorUpdateListener mUpdateListener;
+
+ private static final int INVALID = -1;
+ private int mW = INVALID;
+ private int mH = INVALID;
+ private Point mCenter;
+
+ /**
+ * Constructor
+ *
+ * @param resources Resources, needed to poke around for the background
+ * color value.
+ * @param color The main this circle drawable expands to.
+ * @param baseColor The color of the initial expanded circle
+ * (draws behind the main color).
+ */
+ public TouchCircleDrawable(Resources resources, int color, int baseColor) {
+ super();
+
+ mColorPaint.setAntiAlias(true);
+ mBasePaint.setAntiAlias(true);
+ mBackgroundPaint.setAntiAlias(true);
+ mBackgroundPaint.setColor(resources.getColor(R.color.mode_icon_hover_highlight));
+
+ setColor(color);
+ setBaseColor(baseColor);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param resources Resources, needed to poke around for the background color value.
+ */
+ public TouchCircleDrawable(Resources resources) {
+ this(resources, 0xffffff, 0xffffff);
+ }
+
+ /**
+ * Set the size of this drawable.
+ *
+ * @param w Width to set.
+ * @param h Height to set.
+ */
+ public void setSize(int w, int h) {
+ mW = w;
+ mH = h;
+ }
+
+ /**
+ * Set the center of the circle for this drawable.
+ *
+ * @param p The center point.
+ */
+ public void setCenter(Point p) {
+ mCenter = p;
+ updateIconBounds();
+ }
+
+ /**
+ * @return The center of this drawable.
+ */
+ public Point getCenter() {
+ return mCenter;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int w = mW;
+ int h = mH;
+
+ if (w == INVALID || h == INVALID || mCenter == null) {
+ return;
+ }
+
+ if (mDrawBackground) {
+ canvas.drawCircle(mCenter.x, mCenter.y, mBackgroundRadius, mBackgroundPaint);
+ }
+ canvas.drawCircle(mCenter.x, mCenter.y, mBaseRadius, mBasePaint);
+ canvas.drawCircle(mCenter.x, mCenter.y, mColorRadius, mColorPaint);
+ if (mIconDrawable != null) {
+ mIconDrawable.draw(canvas);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mColorAlpha = alpha;
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ mColorPaint.setColorFilter(cf);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ /**
+ * Set the main color.
+ *
+ * @param color The main color.
+ */
+ public void setColor(int color) {
+ mColor = color;
+ mColorPaint.setColor(mColor);
+ mColorPaint.setAlpha(mColorAlpha);
+ }
+
+ /**
+ * Set the base color
+ *
+ * @param color The color of the initial expanded circle (draws behind the main color).
+ */
+ public void setBaseColor(int color) {
+ mBaseColor = color;
+ mBasePaint.setColor(mBaseColor);
+ mBasePaint.setAlpha(mBaseAlpha);
+ }
+
+ public void setIconDrawable(Drawable d, int size) {
+ mIconDrawable = d;
+ mIconDrawableSize = size;
+ updateIconBounds();
+ }
+
+ private void updateIconBounds() {
+ if (mCenter != null) {
+ mIconDrawable.setBounds(
+ mCenter.x - mIconDrawableSize/2, mCenter.y - mIconDrawableSize/2,
+ mCenter.x + mIconDrawableSize/2, mCenter.y + mIconDrawableSize/2);
+ }
+ }
+
+ /**
+ * Start the expand animation.
+ */
+ public void animate() {
+ mBackgroundRadius = Math.min(mW/2, mH/2);
+
+ final ValueAnimator baseAnimator =
+ ValueAnimator.ofInt(0, Math.min(mW/2, mH/2));
+ baseAnimator.setDuration(BASE_CIRCLE_ANIM_DURATION_MS);
+ baseAnimator.setInterpolator(Gusterpolator.INSTANCE);
+ baseAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mBaseRadius = (Integer) animation.getAnimatedValue();
+ invalidateSelf();
+ if (mUpdateListener != null) {
+ mUpdateListener.onAnimationUpdate(animation);
+ }
+ }
+ });
+
+ final ValueAnimator colorAnimator =
+ ValueAnimator.ofInt(0, Math.min(mW/2, mH/2));
+ colorAnimator.setDuration(CIRCLE_ANIM_DURATION_MS);
+ colorAnimator.setStartDelay(CIRCLE_ANIM_DURATION_DELAY_MS);
+ colorAnimator.setInterpolator(Gusterpolator.INSTANCE);
+ colorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mColorRadius = (Integer) animation.getAnimatedValue();
+ invalidateSelf();
+ if (mUpdateListener != null) {
+ mUpdateListener.onAnimationUpdate(animation);
+ }
+ }
+ });
+
+ AnimatorSet s = new AnimatorSet();
+ s.addListener(new AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mDrawBackground = true;
+
+ if (mAnimatorListener != null) {
+ mAnimatorListener.onAnimationStart(animation);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDrawBackground = false;
+
+ if (mAnimatorListener != null) {
+ mAnimatorListener.onAnimationEnd(animation);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mDrawBackground = false;
+
+ if (mAnimatorListener != null) {
+ mAnimatorListener.onAnimationCancel(animation);
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ if (mAnimatorListener != null) {
+ mAnimatorListener.onAnimationRepeat(animation);
+ }
+ }
+ });
+ s.playTogether(baseAnimator, colorAnimator);
+ s.start();
+ }
+
+ /**
+ * Reset this drawable to its initial, preanimated state.
+ */
+ public void reset() {
+ mBaseRadius = mColorRadius = 0;
+ }
+
+ /**
+ * Set an {@link android.animation.Animator.AnimatorListener} to be
+ * attached to the animation.
+ *
+ * @param listener The listener.
+ */
+ public void setAnimatorListener(Animator.AnimatorListener listener) {
+ mAnimatorListener = listener;
+ }
+
+ /**
+ * Set an {@link android.animation.ValueAnimator} to be
+ * attached to the animation.
+ *
+ * @param listener The listener.
+ */
+ public void setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) {
+ mUpdateListener = listener;
+ }
+}