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 */ 16 package com.android.keyguard; 17 18 import android.animation.Animator; 19 import android.animation.AnimatorSet; 20 import android.animation.ObjectAnimator; 21 import android.animation.PropertyValuesHolder; 22 import android.content.Context; 23 import android.util.AttributeSet; 24 import android.view.View; 25 import android.view.animation.AccelerateInterpolator; 26 import android.view.animation.DecelerateInterpolator; 27 import android.view.animation.Interpolator; 28 29 import java.util.ArrayList; 30 31 public class KeyguardWidgetCarousel extends KeyguardWidgetPager { 32 33 private float mAdjacentPagesAngle; 34 private static float MAX_SCROLL_PROGRESS = 1.3f; 35 private static float CAMERA_DISTANCE = 10000; 36 protected AnimatorSet mChildrenTransformsAnimator; 37 float[] mTmpTransform = new float[3]; 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() { 53 return MAX_SCROLL_PROGRESS; 54 } 55 56 public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) { 57 View child = getChildAt(index); 58 if (child == null) return 0f; 59 60 boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1; 61 float scrollProgress = getScrollProgress(screenCenter, child, index); 62 63 if (isOverScrollChild(index, scrollProgress)) { 64 return 1.0f; 65 } else if ((showSidePages && inVisibleRange) || index == getNextPage()) { 66 scrollProgress = getBoundedScrollProgress(screenCenter, child, index); 67 float alpha = 1.0f - 1.0f * Math.abs(scrollProgress / MAX_SCROLL_PROGRESS); 68 return alpha; 69 } else { 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; 80 } 81 } 82 83 private void updatePageAlphaValues(int screenCenter) { 84 if (mChildrenOutlineFadeAnimation != null) { 85 mChildrenOutlineFadeAnimation.cancel(); 86 mChildrenOutlineFadeAnimation = null; 87 } 88 boolean showSidePages = mShowingInitialHints || isPageMoving(); 89 if (!isReordering(false)) { 90 for (int i = 0; i < getChildCount(); i++) { 91 KeyguardWidgetFrame child = getWidgetPageAt(i); 92 if (child != null) { 93 float outlineAlpha = getOutlineAlphaForPage(screenCenter, i, showSidePages); 94 float contentAlpha = getAlphaForPage(screenCenter, i,showSidePages); 95 child.setBackgroundAlpha(outlineAlpha); 96 child.setContentAlpha(contentAlpha); 97 } 98 } 99 } 100 } 101 102 public void showInitialPageHints() { 103 mShowingInitialHints = true; 104 int count = getChildCount(); 105 for (int i = 0; i < count; i++) { 106 boolean inVisibleRange = i >= getNextPage() - 1 && i <= getNextPage() + 1; 107 KeyguardWidgetFrame child = getWidgetPageAt(i); 108 if (inVisibleRange) { 109 child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); 110 child.setContentAlpha(1f); 111 } else { 112 child.setBackgroundAlpha(0f); 113 child.setContentAlpha(0f); 114 } 115 } 116 } 117 118 @Override 119 protected void screenScrolled(int screenCenter) { 120 mScreenCenter = screenCenter; 121 updatePageAlphaValues(screenCenter); 122 if (isReordering(false)) return; 123 for (int i = 0; i < getChildCount(); i++) { 124 KeyguardWidgetFrame v = getWidgetPageAt(i); 125 float scrollProgress = getScrollProgress(screenCenter, v, i); 126 float boundedProgress = getBoundedScrollProgress(screenCenter, v, i); 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 { 134 int width = v.getMeasuredWidth(); 135 float pivotX = (width / 2f) + boundedProgress * (width / 2f); 136 float pivotY = v.getMeasuredHeight() / 2; 137 float rotationY = - mAdjacentPagesAngle * boundedProgress; 138 v.setPivotX(pivotX); 139 v.setPivotY(pivotY); 140 v.setRotationY(rotationY); 141 v.setOverScrollAmount(0f, false); 142 } 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); 150 } 151 } 152 } 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); 173 outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 174 KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); 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); 241 float finalAlpha = getAlphaForPage(mScreenCenter, i, true); 242 float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i, true); 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); 249 outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", finalOutlineAlpha); 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 } 288 } 289