KeyguardWidgetPager.java revision 1254f2f42f7173ef51d0034975ab5cb7d44f8209
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 */ 16package com.android.internal.policy.impl.keyguard; 17 18import android.animation.ObjectAnimator; 19import android.animation.PropertyValuesHolder; 20import android.animation.TimeInterpolator; 21import android.appwidget.AppWidgetHostView; 22import android.content.Context; 23import android.util.AttributeSet; 24import android.view.Gravity; 25import android.view.MotionEvent; 26import android.view.View; 27import android.view.View.OnLongClickListener; 28import android.view.ViewGroup; 29import android.view.animation.AccelerateInterpolator; 30import android.view.animation.DecelerateInterpolator; 31import android.widget.FrameLayout; 32 33import com.android.internal.widget.LockPatternUtils; 34 35public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener, 36 OnLongClickListener { 37 38 ZInterpolator mZInterpolator = new ZInterpolator(0.5f); 39 private static float CAMERA_DISTANCE = 10000; 40 private static float TRANSITION_SCALE_FACTOR = 0.74f; 41 private static float TRANSITION_PIVOT = 0.65f; 42 private static float TRANSITION_MAX_ROTATION = 30; 43 private static final boolean PERFORM_OVERSCROLL_ROTATION = true; 44 private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f); 45 private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4); 46 private KeyguardViewStateManager mViewStateManager; 47 private LockPatternUtils mLockPatternUtils; 48 49 // Related to the fading in / out background outlines 50 private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0; 51 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375; 52 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100; 53 private ObjectAnimator mChildrenOutlineFadeInAnimation; 54 private ObjectAnimator mChildrenOutlineFadeOutAnimation; 55 private float mChildrenOutlineAlpha = 0; 56 private float mSidePagesAlpha = 1f; 57 58 private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000; 59 private static final boolean CAFETERIA_TRAY = false; 60 61 private int mPage = 0; 62 private Callbacks mCallbacks; 63 64 public KeyguardWidgetPager(Context context, AttributeSet attrs) { 65 this(context, attrs, 0); 66 } 67 68 public KeyguardWidgetPager(Context context) { 69 this(null, null, 0); 70 } 71 72 public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) { 73 super(context, attrs, defStyle); 74 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 75 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 76 } 77 78 setPageSwitchListener(this); 79 } 80 81 public void setViewStateManager(KeyguardViewStateManager viewStateManager) { 82 mViewStateManager = viewStateManager; 83 } 84 85 public void setLockPatternUtils(LockPatternUtils l) { 86 mLockPatternUtils = l; 87 } 88 89 @Override 90 public void onPageSwitch(View newPage, int newPageIndex) { 91 boolean showingStatusWidget = false; 92 if (newPage instanceof ViewGroup) { 93 ViewGroup vg = (ViewGroup) newPage; 94 if (vg.getChildAt(0) instanceof KeyguardStatusView) { 95 showingStatusWidget = true; 96 } 97 } 98 99 // Disable the status bar clock if we're showing the default status widget 100 if (showingStatusWidget) { 101 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK); 102 } else { 103 setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK); 104 } 105 106 // Extend the display timeout if the user switches pages 107 if (mPage != newPageIndex) { 108 int oldPageIndex = mPage; 109 mPage = newPageIndex; 110 if (mCallbacks != null) { 111 mCallbacks.onUserActivityTimeoutChanged(); 112 mCallbacks.userActivity(); 113 } 114 KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex); 115 if (oldWidgetPage != null) { 116 oldWidgetPage.onActive(false); 117 } 118 KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex); 119 if (newWidgetPage != null) { 120 newWidgetPage.onActive(true); 121 } 122 } 123 if (mViewStateManager != null) { 124 mViewStateManager.onPageSwitch(newPage, newPageIndex); 125 } 126 } 127 128 public void showPagingFeedback() { 129 // Nothing yet. 130 } 131 132 public long getUserActivityTimeout() { 133 View page = getPageAt(mPage); 134 if (page instanceof ViewGroup) { 135 ViewGroup vg = (ViewGroup) page; 136 View view = vg.getChildAt(0); 137 if (!(view instanceof KeyguardStatusView) 138 && !(view instanceof KeyguardMultiUserSelectorView)) { 139 return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT; 140 } 141 } 142 return -1; 143 } 144 145 public void setCallbacks(Callbacks callbacks) { 146 mCallbacks = callbacks; 147 } 148 149 public interface Callbacks { 150 public void userActivity(); 151 public void onUserActivityTimeoutChanged(); 152 } 153 154 public void addWidget(View widget) { 155 addWidget(widget, -1); 156 } 157 158 159 public void onRemoveView(View v) { 160 int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); 161 mLockPatternUtils.removeAppWidget(appWidgetId); 162 } 163 164 public void onAddView(View v, int index) { 165 int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); 166 getVisiblePages(mTempVisiblePagesRange); 167 boundByReorderablePages(true, mTempVisiblePagesRange); 168 // Subtract from the index to take into account pages before the reorderable 169 // pages (e.g. the "add widget" page) 170 mLockPatternUtils.addAppWidget(appWidgetId, index - mTempVisiblePagesRange[0]); 171 } 172 173 /* 174 * We wrap widgets in a special frame which handles drawing the over scroll foreground. 175 */ 176 public void addWidget(View widget, int pageIndex) { 177 KeyguardWidgetFrame frame; 178 // All views contained herein should be wrapped in a KeyguardWidgetFrame 179 if (!(widget instanceof KeyguardWidgetFrame)) { 180 frame = new KeyguardWidgetFrame(getContext()); 181 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, 182 LayoutParams.MATCH_PARENT); 183 lp.gravity = Gravity.TOP; 184 // The framework adds a default padding to AppWidgetHostView. We don't need this padding 185 // for the Keyguard, so we override it to be 0. 186 widget.setPadding(0, 0, 0, 0); 187 if (widget instanceof AppWidgetHostView) { 188 AppWidgetHostView awhv = (AppWidgetHostView) widget; 189 widget.setContentDescription(awhv.getAppWidgetInfo().label); 190 } 191 frame.addView(widget, lp); 192 } else { 193 frame = (KeyguardWidgetFrame) widget; 194 } 195 196 ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams( 197 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 198 frame.setOnLongClickListener(this); 199 200 if (pageIndex == -1) { 201 addView(frame, pageLp); 202 } else { 203 addView(frame, pageIndex, pageLp); 204 } 205 } 206 207 // We enforce that all children are KeyguardWidgetFrames 208 @Override 209 public void addView(View child, int index) { 210 enforceKeyguardWidgetFrame(child); 211 super.addView(child, index); 212 } 213 214 @Override 215 public void addView(View child, int width, int height) { 216 enforceKeyguardWidgetFrame(child); 217 super.addView(child, width, height); 218 } 219 220 @Override 221 public void addView(View child, LayoutParams params) { 222 enforceKeyguardWidgetFrame(child); 223 super.addView(child, params); 224 } 225 226 @Override 227 public void addView(View child, int index, LayoutParams params) { 228 enforceKeyguardWidgetFrame(child); 229 super.addView(child, index, params); 230 } 231 232 private void enforceKeyguardWidgetFrame(View child) { 233 if (!(child instanceof KeyguardWidgetFrame)) { 234 throw new IllegalArgumentException( 235 "KeyguardWidgetPager children must be KeyguardWidgetFrames"); 236 } 237 } 238 239 public KeyguardWidgetFrame getWidgetPageAt(int index) { 240 // This is always a valid cast as we've guarded the ability to 241 return (KeyguardWidgetFrame) getChildAt(index); 242 } 243 244 protected void onUnhandledTap(MotionEvent ev) { 245 showPagingFeedback(); 246 } 247 248 @Override 249 protected void onPageBeginMoving() { 250 // Enable hardware layers while pages are moving 251 // TODO: We should only do this for the two views that are actually moving 252 int children = getChildCount(); 253 for (int i = 0; i < children; i++) { 254 getWidgetPageAt(i).enableHardwareLayersForContent(); 255 } 256 257 if (mViewStateManager != null) { 258 mViewStateManager.onPageBeginMoving(); 259 } 260 showOutlinesAndSidePages(); 261 } 262 263 @Override 264 protected void onPageEndMoving() { 265 // Disable hardware layers while pages are moving 266 int children = getChildCount(); 267 for (int i = 0; i < children; i++) { 268 getWidgetPageAt(i).disableHardwareLayersForContent(); 269 } 270 271 if (mViewStateManager != null) { 272 mViewStateManager.onPageEndMoving(); 273 } 274 hideOutlinesAndSidePages(); 275 } 276 277 /* 278 * This interpolator emulates the rate at which the perceived scale of an object changes 279 * as its distance from a camera increases. When this interpolator is applied to a scale 280 * animation on a view, it evokes the sense that the object is shrinking due to moving away 281 * from the camera. 282 */ 283 static class ZInterpolator implements TimeInterpolator { 284 private float focalLength; 285 286 public ZInterpolator(float foc) { 287 focalLength = foc; 288 } 289 290 public float getInterpolation(float input) { 291 return (1.0f - focalLength / (focalLength + input)) / 292 (1.0f - focalLength / (focalLength + 1.0f)); 293 } 294 } 295 296 @Override 297 public String getCurrentPageDescription() { 298 final int nextPageIndex = getNextPage(); 299 if (nextPageIndex >= 0 && nextPageIndex < getChildCount()) { 300 KeyguardWidgetFrame frame = getWidgetPageAt(nextPageIndex); 301 CharSequence title = frame.getChildAt(0).getContentDescription(); 302 if (title == null) { 303 title = ""; 304 } 305 return mContext.getString( 306 com.android.internal.R.string.keyguard_accessibility_widget_changed, 307 title, nextPageIndex + 1, getChildCount()); 308 } 309 return super.getCurrentPageDescription(); 310 } 311 312 @Override 313 protected void overScroll(float amount) { 314 acceleratedOverScroll(amount); 315 } 316 317 float backgroundAlphaInterpolator(float r) { 318 return r; 319 } 320 321 private void updatePageAlphaValues(int screenCenter) { 322 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; 323 if (!isInOverscroll) { 324 for (int i = 0; i < getChildCount(); i++) { 325 KeyguardWidgetFrame child = getWidgetPageAt(i); 326 if (child != null) { 327 float scrollProgress = getScrollProgress(screenCenter, child, i); 328 // TODO: Set content alpha 329 if (!isReordering(false)) { 330 child.setBackgroundAlphaMultiplier( 331 backgroundAlphaInterpolator(Math.abs(scrollProgress))); 332 } else { 333 child.setBackgroundAlphaMultiplier(1f); 334 } 335 } 336 } 337 } 338 } 339 340 // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack. 341 @Override 342 protected void screenScrolled(int screenCenter) { 343 super.screenScrolled(screenCenter); 344 updatePageAlphaValues(screenCenter); 345 for (int i = 0; i < getChildCount(); i++) { 346 KeyguardWidgetFrame v = getWidgetPageAt(i); 347 if (v == mDragView) continue; 348 if (v != null) { 349 float scrollProgress = getScrollProgress(screenCenter, v, i); 350 float interpolatedProgress = 351 mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0))); 352 353 float scale = 1.0f; 354 float translationX = 0; 355 float alpha = 1.0f; 356 357 if (CAFETERIA_TRAY) { 358 scale = (1 - interpolatedProgress) + 359 interpolatedProgress * TRANSITION_SCALE_FACTOR; 360 translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth(); 361 362 if (scrollProgress < 0) { 363 alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation( 364 1 - Math.abs(scrollProgress)) : 1.0f; 365 } else { 366 // On large screens we need to fade the page as it nears its leftmost position 367 alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress); 368 } 369 } 370 371 v.setCameraDistance(mDensity * CAMERA_DISTANCE); 372 int pageWidth = v.getMeasuredWidth(); 373 int pageHeight = v.getMeasuredHeight(); 374 375 if (PERFORM_OVERSCROLL_ROTATION) { 376 if (i == 0 && scrollProgress < 0) { 377 // Overscroll to the left 378 v.setPivotX(TRANSITION_PIVOT * pageWidth); 379 v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); 380 v.setOverScrollAmount(Math.abs(scrollProgress), true); 381 scale = 1.0f; 382 alpha = 1.0f; 383 // On the first page, we don't want the page to have any lateral motion 384 translationX = 0; 385 } else if (i == getChildCount() - 1 && scrollProgress > 0) { 386 // Overscroll to the right 387 v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth); 388 v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); 389 scale = 1.0f; 390 alpha = 1.0f; 391 v.setOverScrollAmount(Math.abs(scrollProgress), false); 392 // On the last page, we don't want the page to have any lateral motion. 393 translationX = 0; 394 } else { 395 v.setPivotY(pageHeight / 2.0f); 396 v.setPivotX(pageWidth / 2.0f); 397 v.setRotationY(0f); 398 v.setOverScrollAmount(0, false); 399 } 400 } 401 402 if (CAFETERIA_TRAY) { 403 v.setTranslationX(translationX); 404 v.setScaleX(scale); 405 v.setScaleY(scale); 406 } 407 v.setAlpha(alpha); 408 409 // If the view has 0 alpha, we set it to be invisible so as to prevent 410 // it from accepting touches 411 if (alpha == 0) { 412 v.setVisibility(INVISIBLE); 413 } else if (v.getVisibility() != VISIBLE) { 414 v.setVisibility(VISIBLE); 415 } 416 } 417 } 418 } 419 420 @Override 421 protected void onStartReordering() { 422 super.onStartReordering(); 423 setChildrenOutlineMultiplier(1.0f); 424 showOutlinesAndSidePages(); 425 } 426 427 @Override 428 protected void onEndReordering() { 429 super.onEndReordering(); 430 hideOutlinesAndSidePages(); 431 } 432 433 void showOutlinesAndSidePages() { 434 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); 435 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); 436 437 PropertyValuesHolder outlinesAlpha = 438 PropertyValuesHolder.ofFloat("childrenOutlineAlpha", 1.0f); 439 PropertyValuesHolder sidePagesAlpha = PropertyValuesHolder.ofFloat("sidePagesAlpha", 1.0f); 440 mChildrenOutlineFadeInAnimation = 441 ObjectAnimator.ofPropertyValuesHolder(this, outlinesAlpha, sidePagesAlpha); 442 443 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION); 444 mChildrenOutlineFadeInAnimation.start(); 445 } 446 447 public void showInitialPageHints() { 448 // We start with everything showing 449 setChildrenOutlineAlpha(1.0f); 450 setSidePagesAlpha(1.0f); 451 setChildrenOutlineMultiplier(1.0f); 452 453 int currPage = getCurrentPage(); 454 KeyguardWidgetFrame frame = getWidgetPageAt(currPage); 455 frame.setBackgroundAlphaMultiplier(0f); 456 } 457 458 void hideOutlinesAndSidePages() { 459 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); 460 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); 461 462 PropertyValuesHolder outlinesAlpha = 463 PropertyValuesHolder.ofFloat("childrenOutlineAlpha", 0f); 464 PropertyValuesHolder sidePagesAlpha = PropertyValuesHolder.ofFloat("sidePagesAlpha", 0f); 465 mChildrenOutlineFadeOutAnimation = 466 ObjectAnimator.ofPropertyValuesHolder(this, outlinesAlpha, sidePagesAlpha); 467 468 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION); 469 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY); 470 mChildrenOutlineFadeOutAnimation.start(); 471 } 472 473 public void setChildrenOutlineAlpha(float alpha) { 474 mChildrenOutlineAlpha = alpha; 475 for (int i = 0; i < getChildCount(); i++) { 476 getWidgetPageAt(i).setBackgroundAlpha(alpha); 477 } 478 } 479 480 public void setSidePagesAlpha(float alpha) { 481 // This gives the current page, or the destination page if in transit. 482 int curPage = getNextPage(); 483 mSidePagesAlpha = alpha; 484 for (int i = 0; i < getChildCount(); i++) { 485 if (curPage != i) { 486 getWidgetPageAt(i).setContentAlpha(alpha); 487 } else { 488 // We lock the current page alpha to 1. 489 getWidgetPageAt(i).setContentAlpha(1.0f); 490 } 491 } 492 } 493 494 public void setChildrenOutlineMultiplier(float alpha) { 495 mChildrenOutlineAlpha = alpha; 496 for (int i = 0; i < getChildCount(); i++) { 497 getWidgetPageAt(i).setBackgroundAlphaMultiplier(alpha); 498 } 499 } 500 501 public float getSidePagesAlpha() { 502 return mSidePagesAlpha; 503 } 504 505 public float getChildrenOutlineAlpha() { 506 return mChildrenOutlineAlpha; 507 } 508 509 @Override 510 public boolean onLongClick(View v) { 511 if (startReordering()) { 512 return true; 513 } 514 return false; 515 } 516} 517