Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2012 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 | */ |
Jim Miller | 5ecd811 | 2013-01-09 18:50:26 -0800 | [diff] [blame] | 16 | package com.android.keyguard; |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 17 | |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 18 | import android.animation.Animator; |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 19 | import android.animation.AnimatorSet; |
| 20 | import android.animation.ObjectAnimator; |
| 21 | import android.animation.PropertyValuesHolder; |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 22 | import android.content.Context; |
| 23 | import android.util.AttributeSet; |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 24 | import android.view.View; |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 25 | import android.view.animation.AccelerateInterpolator; |
| 26 | import android.view.animation.DecelerateInterpolator; |
| 27 | import android.view.animation.Interpolator; |
| 28 | |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 29 | import java.util.ArrayList; |
| 30 | |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 31 | public class KeyguardWidgetCarousel extends KeyguardWidgetPager { |
| 32 | |
| 33 | private float mAdjacentPagesAngle; |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 34 | private static float MAX_SCROLL_PROGRESS = 1.3f; |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 35 | private static float CAMERA_DISTANCE = 10000; |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 36 | protected AnimatorSet mChildrenTransformsAnimator; |
| 37 | float[] mTmpTransform = new float[3]; |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 38 | |
| 39 | public KeyguardWidgetCarousel(Context context, AttributeSet attrs) { |
| 40 | this(context, attrs, 0); |
| 41 | } |
| 42 | |
| 43 | public KeyguardWidgetCarousel(Context context) { |
| 44 | this(context, null, 0); |
| 45 | } |
| 46 | |
| 47 | public KeyguardWidgetCarousel(Context context, AttributeSet attrs, int defStyle) { |
| 48 | super(context, attrs, defStyle); |
| 49 | mAdjacentPagesAngle = context.getResources().getInteger(R.integer.kg_carousel_angle); |
| 50 | } |
| 51 | |
| 52 | protected float getMaxScrollProgress() { |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 53 | return MAX_SCROLL_PROGRESS; |
| 54 | } |
| 55 | |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 56 | public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) { |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 57 | View child = getChildAt(index); |
| 58 | if (child == null) return 0f; |
| 59 | |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 60 | boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1; |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 61 | float scrollProgress = getScrollProgress(screenCenter, child, index); |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 62 | |
| 63 | if (isOverScrollChild(index, scrollProgress)) { |
| 64 | return 1.0f; |
| 65 | } else if ((showSidePages && inVisibleRange) || index == getNextPage()) { |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 66 | scrollProgress = getBoundedScrollProgress(screenCenter, child, index); |
Adam Cohen | ab8635d | 2012-10-30 17:31:32 -0700 | [diff] [blame] | 67 | float alpha = 1.0f - 1.0f * Math.abs(scrollProgress / MAX_SCROLL_PROGRESS); |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 68 | return alpha; |
| 69 | } else { |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 70 | return 0f; |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) { |
| 75 | boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1; |
| 76 | if (inVisibleRange) { |
| 77 | return super.getOutlineAlphaForPage(screenCenter, index, showSidePages); |
| 78 | } else { |
| 79 | return 0f; |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 80 | } |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | private void updatePageAlphaValues(int screenCenter) { |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 84 | if (mChildrenOutlineFadeAnimation != null) { |
| 85 | mChildrenOutlineFadeAnimation.cancel(); |
| 86 | mChildrenOutlineFadeAnimation = null; |
| 87 | } |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 88 | boolean showSidePages = mShowingInitialHints || isPageMoving(); |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 89 | if (!isReordering(false)) { |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 90 | for (int i = 0; i < getChildCount(); i++) { |
| 91 | KeyguardWidgetFrame child = getWidgetPageAt(i); |
| 92 | if (child != null) { |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 93 | float outlineAlpha = getOutlineAlphaForPage(screenCenter, i, showSidePages); |
| 94 | float contentAlpha = getAlphaForPage(screenCenter, i,showSidePages); |
| 95 | child.setBackgroundAlpha(outlineAlpha); |
| 96 | child.setContentAlpha(contentAlpha); |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 97 | } |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | |
Adam Cohen | 934d083 | 2012-11-03 20:02:33 -0700 | [diff] [blame] | 102 | public void showInitialPageHints() { |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 103 | mShowingInitialHints = true; |
Adam Cohen | 934d083 | 2012-11-03 20:02:33 -0700 | [diff] [blame] | 104 | int count = getChildCount(); |
| 105 | for (int i = 0; i < count; i++) { |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 106 | boolean inVisibleRange = i >= getNextPage() - 1 && i <= getNextPage() + 1; |
Adam Cohen | 934d083 | 2012-11-03 20:02:33 -0700 | [diff] [blame] | 107 | KeyguardWidgetFrame child = getWidgetPageAt(i); |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 108 | if (inVisibleRange) { |
| 109 | child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); |
| 110 | child.setContentAlpha(1f); |
| 111 | } else { |
| 112 | child.setBackgroundAlpha(0f); |
| 113 | child.setContentAlpha(0f); |
Adam Cohen | 934d083 | 2012-11-03 20:02:33 -0700 | [diff] [blame] | 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 118 | @Override |
| 119 | protected void screenScrolled(int screenCenter) { |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 120 | mScreenCenter = screenCenter; |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 121 | updatePageAlphaValues(screenCenter); |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 122 | if (isReordering(false)) return; |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 123 | for (int i = 0; i < getChildCount(); i++) { |
| 124 | KeyguardWidgetFrame v = getWidgetPageAt(i); |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 125 | float scrollProgress = getScrollProgress(screenCenter, v, i); |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 126 | float boundedProgress = getBoundedScrollProgress(screenCenter, v, i); |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 127 | if (v == mDragView || v == null) continue; |
| 128 | v.setCameraDistance(CAMERA_DISTANCE); |
| 129 | |
| 130 | if (isOverScrollChild(i, scrollProgress)) { |
| 131 | v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress); |
| 132 | v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0); |
| 133 | } else { |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 134 | int width = v.getMeasuredWidth(); |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 135 | float pivotX = (width / 2f) + boundedProgress * (width / 2f); |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 136 | float pivotY = v.getMeasuredHeight() / 2; |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 137 | float rotationY = - mAdjacentPagesAngle * boundedProgress; |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 138 | v.setPivotX(pivotX); |
| 139 | v.setPivotY(pivotY); |
| 140 | v.setRotationY(rotationY); |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 141 | v.setOverScrollAmount(0f, false); |
| 142 | } |
Adam Cohen | f9048cd | 2012-10-27 16:36:10 -0700 | [diff] [blame] | 143 | float alpha = v.getAlpha(); |
| 144 | // If the view has 0 alpha, we set it to be invisible so as to prevent |
| 145 | // it from accepting touches |
| 146 | if (alpha == 0) { |
| 147 | v.setVisibility(INVISIBLE); |
| 148 | } else if (v.getVisibility() != VISIBLE) { |
| 149 | v.setVisibility(VISIBLE); |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 150 | } |
| 151 | } |
| 152 | } |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 153 | |
| 154 | void animatePagesToNeutral() { |
| 155 | if (mChildrenTransformsAnimator != null) { |
| 156 | mChildrenTransformsAnimator.cancel(); |
| 157 | mChildrenTransformsAnimator = null; |
| 158 | } |
| 159 | |
| 160 | int count = getChildCount(); |
| 161 | PropertyValuesHolder alpha; |
| 162 | PropertyValuesHolder outlineAlpha; |
| 163 | PropertyValuesHolder rotationY; |
| 164 | ArrayList<Animator> anims = new ArrayList<Animator>(); |
| 165 | |
| 166 | for (int i = 0; i < count; i++) { |
| 167 | KeyguardWidgetFrame child = getWidgetPageAt(i); |
| 168 | boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1); |
| 169 | if (!inVisibleRange) { |
| 170 | child.setRotationY(0f); |
| 171 | } |
| 172 | alpha = PropertyValuesHolder.ofFloat("contentAlpha", 1.0f); |
Adam Cohen | ab8635d | 2012-10-30 17:31:32 -0700 | [diff] [blame] | 173 | outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", |
| 174 | KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 175 | rotationY = PropertyValuesHolder.ofFloat("rotationY", 0f); |
| 176 | ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha, rotationY); |
| 177 | child.setVisibility(VISIBLE); |
| 178 | if (!inVisibleRange) { |
| 179 | a.setInterpolator(mSlowFadeInterpolator); |
| 180 | } |
| 181 | anims.add(a); |
| 182 | } |
| 183 | |
| 184 | int duration = REORDERING_ZOOM_IN_OUT_DURATION; |
| 185 | mChildrenTransformsAnimator = new AnimatorSet(); |
| 186 | mChildrenTransformsAnimator.playTogether(anims); |
| 187 | |
| 188 | mChildrenTransformsAnimator.setDuration(duration); |
| 189 | mChildrenTransformsAnimator.start(); |
| 190 | } |
| 191 | |
| 192 | private void getTransformForPage(int screenCenter, int index, float[] transform) { |
| 193 | View child = getChildAt(index); |
| 194 | float boundedProgress = getBoundedScrollProgress(screenCenter, child, index); |
| 195 | float rotationY = - mAdjacentPagesAngle * boundedProgress; |
| 196 | int width = child.getMeasuredWidth(); |
| 197 | float pivotX = (width / 2f) + boundedProgress * (width / 2f); |
| 198 | float pivotY = child.getMeasuredHeight() / 2; |
| 199 | |
| 200 | transform[0] = pivotX; |
| 201 | transform[1] = pivotY; |
| 202 | transform[2] = rotationY; |
| 203 | } |
| 204 | |
| 205 | Interpolator mFastFadeInterpolator = new Interpolator() { |
| 206 | Interpolator mInternal = new DecelerateInterpolator(1.5f); |
| 207 | float mFactor = 2.5f; |
| 208 | @Override |
| 209 | public float getInterpolation(float input) { |
| 210 | return mInternal.getInterpolation(Math.min(mFactor * input, 1f)); |
| 211 | } |
| 212 | }; |
| 213 | |
| 214 | Interpolator mSlowFadeInterpolator = new Interpolator() { |
| 215 | Interpolator mInternal = new AccelerateInterpolator(1.5f); |
| 216 | float mFactor = 1.3f; |
| 217 | @Override |
| 218 | public float getInterpolation(float input) { |
| 219 | input -= (1 - 1 / mFactor); |
| 220 | input = mFactor * Math.max(input, 0f); |
| 221 | return mInternal.getInterpolation(input); |
| 222 | } |
| 223 | }; |
| 224 | |
| 225 | void animatePagesToCarousel() { |
| 226 | if (mChildrenTransformsAnimator != null) { |
| 227 | mChildrenTransformsAnimator.cancel(); |
| 228 | mChildrenTransformsAnimator = null; |
| 229 | } |
| 230 | |
| 231 | int count = getChildCount(); |
| 232 | PropertyValuesHolder alpha; |
| 233 | PropertyValuesHolder outlineAlpha; |
| 234 | PropertyValuesHolder rotationY; |
| 235 | PropertyValuesHolder pivotX; |
| 236 | PropertyValuesHolder pivotY; |
| 237 | ArrayList<Animator> anims = new ArrayList<Animator>(); |
| 238 | |
| 239 | for (int i = 0; i < count; i++) { |
| 240 | KeyguardWidgetFrame child = getWidgetPageAt(i); |
Adam Cohen | 2b0501b | 2012-11-21 16:49:13 -0800 | [diff] [blame] | 241 | float finalAlpha = getAlphaForPage(mScreenCenter, i, true); |
| 242 | float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i, true); |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 243 | getTransformForPage(mScreenCenter, i, mTmpTransform); |
| 244 | |
| 245 | boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1); |
| 246 | |
| 247 | ObjectAnimator a; |
| 248 | alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalAlpha); |
Adam Cohen | ab8635d | 2012-10-30 17:31:32 -0700 | [diff] [blame] | 249 | outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", finalOutlineAlpha); |
Adam Cohen | 70009e4 | 2012-10-30 16:48:22 -0700 | [diff] [blame] | 250 | pivotX = PropertyValuesHolder.ofFloat("pivotX", mTmpTransform[0]); |
| 251 | pivotY = PropertyValuesHolder.ofFloat("pivotY", mTmpTransform[1]); |
| 252 | rotationY = PropertyValuesHolder.ofFloat("rotationY", mTmpTransform[2]); |
| 253 | |
| 254 | if (inVisibleRange) { |
| 255 | // for the central pages we animate into a rotated state |
| 256 | a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha, |
| 257 | pivotX, pivotY, rotationY); |
| 258 | } else { |
| 259 | a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha); |
| 260 | a.setInterpolator(mFastFadeInterpolator); |
| 261 | } |
| 262 | anims.add(a); |
| 263 | } |
| 264 | |
| 265 | int duration = REORDERING_ZOOM_IN_OUT_DURATION; |
| 266 | mChildrenTransformsAnimator = new AnimatorSet(); |
| 267 | mChildrenTransformsAnimator.playTogether(anims); |
| 268 | |
| 269 | mChildrenTransformsAnimator.setDuration(duration); |
| 270 | mChildrenTransformsAnimator.start(); |
| 271 | } |
| 272 | |
| 273 | protected void reorderStarting() { |
| 274 | mViewStateManager.fadeOutSecurity(REORDERING_ZOOM_IN_OUT_DURATION); |
| 275 | animatePagesToNeutral(); |
| 276 | } |
| 277 | |
| 278 | protected boolean zoomIn(final Runnable onCompleteRunnable) { |
| 279 | animatePagesToCarousel(); |
| 280 | return super.zoomIn(onCompleteRunnable); |
| 281 | } |
| 282 | |
| 283 | @Override |
| 284 | protected void onEndReordering() { |
| 285 | super.onEndReordering(); |
| 286 | mViewStateManager.fadeInSecurity(REORDERING_ZOOM_IN_OUT_DURATION); |
| 287 | } |
Adam Cohen | 9ec871d | 2012-10-24 19:25:44 -0700 | [diff] [blame] | 288 | } |