1 /* 2 * Copyright (C) 2014 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 package com.android.camera.widget; 17 18 import android.animation.Animator; 19 import android.animation.AnimatorListenerAdapter; 20 import android.animation.AnimatorSet; 21 import android.animation.ValueAnimator; 22 import android.content.Context; 23 import android.content.res.Configuration; 24 import android.graphics.Canvas; 25 import android.graphics.Paint; 26 import android.graphics.RectF; 27 import android.util.AttributeSet; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.widget.FrameLayout; 31 import android.widget.ImageButton; 32 33 import com.android.camera.MultiToggleImageButton; 34 import com.android.camera.ui.RadioOptions; 35 import com.android.camera.ui.TopRightWeightedLayout; 36 import com.android.camera.util.Gusterpolator; 37 import com.android.camera2.R; 38 39 import java.util.ArrayList; 40 41 public class ModeOptions extends FrameLayout { 42 private int mBackgroundColor; 43 private final Paint mPaint = new Paint(); 44 private boolean mIsHiddenOrHiding; 45 private RectF mAnimateFrom = new RectF(); 46 private View mViewToShowHide; 47 private TopRightWeightedLayout mModeOptionsButtons; 48 private RadioOptions mModeOptionsPano; 49 private RadioOptions mModeOptionsExposure; 50 51 private AnimatorSet mVisibleAnimator; 52 private AnimatorSet mHiddenAnimator; 53 private boolean mDrawCircle; 54 private boolean mFill; 55 private static final int RADIUS_ANIMATION_TIME = 250; 56 private static final int SHOW_ALPHA_ANIMATION_TIME = 350; 57 private static final int HIDE_ALPHA_ANIMATION_TIME = 200; 58 private static final int PADDING_ANIMATION_TIME = 350; 59 60 private ViewGroup mMainBar; 61 private ViewGroup mActiveBar; 62 public static final int BAR_INVALID = -1; 63 public static final int BAR_STANDARD = 0; 64 public static final int BAR_PANO = 1; 65 66 private boolean mIsPortrait; 67 private float mRadius = 0f; 68 ModeOptions(Context context, AttributeSet attrs)69 public ModeOptions(Context context, AttributeSet attrs) { 70 super(context, attrs); 71 } 72 setViewToShowHide(View v)73 public void setViewToShowHide(View v) { 74 mViewToShowHide = v; 75 } 76 77 @Override onFinishInflate()78 public void onFinishInflate() { 79 mIsHiddenOrHiding = true; 80 mBackgroundColor = getResources().getColor(R.color.mode_options_background); 81 mPaint.setAntiAlias(true); 82 mPaint.setColor(mBackgroundColor); 83 mModeOptionsButtons = (TopRightWeightedLayout) findViewById(R.id.mode_options_buttons); 84 mModeOptionsPano = (RadioOptions) findViewById(R.id.mode_options_pano); 85 mModeOptionsExposure = (RadioOptions) findViewById(R.id.mode_options_exposure); 86 mMainBar = mActiveBar = mModeOptionsButtons; 87 88 ImageButton exposureButton = (ImageButton) findViewById(R.id.exposure_button); 89 exposureButton.setOnClickListener(new View.OnClickListener() { 90 @Override 91 public void onClick(View v) { 92 mActiveBar = mModeOptionsExposure; 93 mMainBar.setVisibility(View.INVISIBLE); 94 mActiveBar.setVisibility(View.VISIBLE); 95 } 96 }); 97 } 98 setMainBar(int b)99 public void setMainBar(int b) { 100 for (int i = 0; i < getChildCount(); i++) { 101 getChildAt(i).setVisibility(View.INVISIBLE); 102 } 103 switch (b) { 104 case BAR_STANDARD: 105 mMainBar = mActiveBar = mModeOptionsButtons; 106 break; 107 case BAR_PANO: 108 mMainBar = mActiveBar = mModeOptionsPano; 109 break; 110 } 111 mMainBar.setVisibility(View.VISIBLE); 112 } 113 getMainBar()114 public int getMainBar() { 115 if (mMainBar == mModeOptionsButtons) { 116 return BAR_STANDARD; 117 } 118 if (mMainBar == mModeOptionsPano) { 119 return BAR_PANO; 120 } 121 return BAR_INVALID; 122 } 123 124 @Override onWindowVisibilityChanged(int visibility)125 public void onWindowVisibilityChanged(int visibility) { 126 super.onWindowVisibilityChanged(visibility); 127 if (visibility != VISIBLE && !mIsHiddenOrHiding) { 128 // Collapse mode options when window is not visible. 129 setVisibility(INVISIBLE); 130 if (mMainBar != null) { 131 mMainBar.setVisibility(VISIBLE); 132 } 133 if (mActiveBar != null && mActiveBar != mMainBar) { 134 mActiveBar.setVisibility(INVISIBLE); 135 } 136 if (mViewToShowHide != null) { 137 mViewToShowHide.setVisibility(VISIBLE); 138 } 139 mIsHiddenOrHiding = true; 140 } 141 } 142 143 @Override onLayout(boolean changed, int left, int top, int right, int bottom)144 public void onLayout(boolean changed, int left, int top, int right, int bottom) { 145 if (changed) { 146 mIsPortrait = (getResources().getConfiguration().orientation == 147 Configuration.ORIENTATION_PORTRAIT); 148 149 int buttonSize = getResources() 150 .getDimensionPixelSize(R.dimen.option_button_circle_size); 151 int buttonPadding = getResources() 152 .getDimensionPixelSize(R.dimen.mode_options_toggle_padding); 153 154 float rLeft, rRight, rTop, rBottom; 155 if (mIsPortrait) { 156 rLeft = getWidth() - buttonPadding - buttonSize; 157 rTop = (getHeight() - buttonSize) / 2.0f; 158 } else { 159 rLeft = buttonPadding; 160 rTop = buttonPadding; 161 } 162 rRight = rLeft + buttonSize; 163 rBottom = rTop + buttonSize; 164 mAnimateFrom.set(rLeft, rTop, rRight, rBottom); 165 166 setupAnimators(); 167 setupToggleButtonParams(); 168 } 169 170 super.onLayout(changed, left, top, right, bottom); 171 } 172 173 @Override onDraw(Canvas canvas)174 public void onDraw(Canvas canvas) { 175 if (mDrawCircle) { 176 canvas.drawCircle(mAnimateFrom.centerX(), mAnimateFrom.centerY(), mRadius, mPaint); 177 } else if (mFill) { 178 canvas.drawPaint(mPaint); 179 } 180 super.onDraw(canvas); 181 } 182 setupToggleButtonParams()183 private void setupToggleButtonParams() { 184 int size = (mIsPortrait ? getHeight() : getWidth()); 185 186 for (int i = 0; i < mModeOptionsButtons.getChildCount(); i++) { 187 View button = mModeOptionsButtons.getChildAt(i); 188 if (button instanceof MultiToggleImageButton) { 189 MultiToggleImageButton toggleButton = (MultiToggleImageButton) button; 190 toggleButton.setParentSize(size); 191 toggleButton.setAnimDirection(mIsPortrait ? 192 MultiToggleImageButton.ANIM_DIRECTION_VERTICAL : 193 MultiToggleImageButton.ANIM_DIRECTION_HORIZONTAL); 194 } 195 } 196 } 197 setupAnimators()198 private void setupAnimators() { 199 if (mVisibleAnimator != null) { 200 mVisibleAnimator.end(); 201 } 202 if (mHiddenAnimator != null) { 203 mHiddenAnimator.end(); 204 } 205 206 final float fullSize = (mIsPortrait ? (float) getWidth() : (float) getHeight()); 207 208 // show 209 { 210 final ValueAnimator radiusAnimator = 211 ValueAnimator.ofFloat(mAnimateFrom.width()/2.0f, 212 fullSize-mAnimateFrom.width()/2.0f); 213 radiusAnimator.setDuration(RADIUS_ANIMATION_TIME); 214 radiusAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 215 @Override 216 public void onAnimationUpdate(ValueAnimator animation) { 217 mRadius = (Float) animation.getAnimatedValue(); 218 mDrawCircle = true; 219 mFill = false; 220 } 221 }); 222 radiusAnimator.addListener(new AnimatorListenerAdapter() { 223 @Override 224 public void onAnimationEnd(Animator animation) { 225 mDrawCircle = false; 226 mFill = true; 227 } 228 }); 229 230 final ValueAnimator alphaAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); 231 alphaAnimator.setDuration(SHOW_ALPHA_ANIMATION_TIME); 232 alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 233 @Override 234 public void onAnimationUpdate(ValueAnimator animation) { 235 mActiveBar.setAlpha((Float) animation.getAnimatedValue()); 236 } 237 }); 238 alphaAnimator.addListener(new AnimatorListenerAdapter() { 239 @Override 240 public void onAnimationEnd(Animator animation) { 241 mActiveBar.setAlpha(1.0f); 242 } 243 }); 244 245 final int deltaX = getResources() 246 .getDimensionPixelSize(R.dimen.mode_options_buttons_anim_delta_x); 247 int childCount = mActiveBar.getChildCount(); 248 ArrayList<Animator> paddingAnimators = new ArrayList<Animator>(); 249 for (int i = 0; i < childCount; i++) { 250 final View button; 251 if (mIsPortrait) { 252 button = mActiveBar.getChildAt(i); 253 } else { 254 button = mActiveBar.getChildAt(childCount-1-i); 255 } 256 257 final ValueAnimator paddingAnimator = 258 ValueAnimator.ofFloat(deltaX*(childCount-i), 0.0f); 259 paddingAnimator.setDuration(PADDING_ANIMATION_TIME); 260 paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 261 @Override 262 public void onAnimationUpdate(ValueAnimator animation) { 263 if (mIsPortrait) { 264 button.setTranslationX((Float) animation.getAnimatedValue()); 265 } else { 266 button.setTranslationY(-((Float) animation.getAnimatedValue())); 267 } 268 invalidate(); 269 } 270 }); 271 272 paddingAnimators.add(paddingAnimator); 273 } 274 275 AnimatorSet paddingAnimatorSet = new AnimatorSet(); 276 paddingAnimatorSet.playTogether(paddingAnimators); 277 278 mVisibleAnimator = new AnimatorSet(); 279 mVisibleAnimator.setInterpolator(Gusterpolator.INSTANCE); 280 mVisibleAnimator.playTogether(radiusAnimator, alphaAnimator, paddingAnimatorSet); 281 } 282 283 // hide 284 { 285 final ValueAnimator radiusAnimator = 286 ValueAnimator.ofFloat(fullSize-mAnimateFrom.width()/2.0f, 287 mAnimateFrom.width()/2.0f); 288 radiusAnimator.setDuration(RADIUS_ANIMATION_TIME); 289 radiusAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 290 @Override 291 public void onAnimationUpdate(ValueAnimator animation) { 292 mRadius = (Float) animation.getAnimatedValue(); 293 mDrawCircle = true; 294 mFill = false; 295 invalidate(); 296 } 297 }); 298 radiusAnimator.addListener(new AnimatorListenerAdapter() { 299 @Override 300 public void onAnimationEnd(Animator animation) { 301 if (mViewToShowHide != null) { 302 mViewToShowHide.setVisibility(View.VISIBLE); 303 mDrawCircle = false; 304 mFill = false; 305 invalidate(); 306 } 307 } 308 }); 309 310 final ValueAnimator alphaAnimator = ValueAnimator.ofFloat(1.0f, 0.0f); 311 alphaAnimator.setDuration(HIDE_ALPHA_ANIMATION_TIME); 312 alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 313 @Override 314 public void onAnimationUpdate(ValueAnimator animation) { 315 mActiveBar.setAlpha((Float) animation.getAnimatedValue()); 316 invalidate(); 317 } 318 }); 319 alphaAnimator.addListener(new AnimatorListenerAdapter() { 320 @Override 321 public void onAnimationEnd(Animator animation) { 322 setVisibility(View.INVISIBLE); 323 if (mActiveBar != mMainBar) { 324 mActiveBar.setAlpha(1.0f); 325 mActiveBar.setVisibility(View.INVISIBLE); 326 } 327 mMainBar.setAlpha(1.0f); 328 mMainBar.setVisibility(View.VISIBLE); 329 mActiveBar = mMainBar; 330 invalidate(); 331 } 332 }); 333 334 mHiddenAnimator = new AnimatorSet(); 335 mHiddenAnimator.setInterpolator(Gusterpolator.INSTANCE); 336 mHiddenAnimator.playTogether(radiusAnimator, alphaAnimator); 337 } 338 } 339 animateVisible()340 public void animateVisible() { 341 if (mIsHiddenOrHiding) { 342 if (mViewToShowHide != null) { 343 mViewToShowHide.setVisibility(View.INVISIBLE); 344 } 345 mHiddenAnimator.cancel(); 346 mVisibleAnimator.end(); 347 setVisibility(View.VISIBLE); 348 mVisibleAnimator.start(); 349 } 350 mIsHiddenOrHiding = false; 351 } 352 animateHidden()353 public void animateHidden() { 354 if (!mIsHiddenOrHiding) { 355 mVisibleAnimator.cancel(); 356 mHiddenAnimator.end(); 357 mHiddenAnimator.start(); 358 } 359 mIsHiddenOrHiding = true; 360 } 361 }