KeyguardWidgetPager.java revision caf24fc2c4bb9747eb80138bf3ce0be067851749
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.keyguard; 17 18import android.animation.Animator; 19import android.animation.AnimatorListenerAdapter; 20import android.animation.AnimatorSet; 21import android.animation.ObjectAnimator; 22import android.animation.PropertyValuesHolder; 23import android.animation.TimeInterpolator; 24import android.appwidget.AppWidgetHostView; 25import android.appwidget.AppWidgetManager; 26import android.appwidget.AppWidgetProviderInfo; 27import android.content.Context; 28import android.os.Handler; 29import android.os.HandlerThread; 30import android.text.format.DateFormat; 31import android.util.AttributeSet; 32import android.util.Slog; 33import android.view.Gravity; 34import android.view.MotionEvent; 35import android.view.View; 36import android.view.View.OnLongClickListener; 37import android.view.ViewGroup; 38import android.view.accessibility.AccessibilityEvent; 39import android.view.accessibility.AccessibilityManager; 40import android.view.animation.DecelerateInterpolator; 41import android.widget.FrameLayout; 42import android.widget.TextClock; 43 44import com.android.internal.widget.LockPatternUtils; 45 46import java.util.ArrayList; 47import java.util.TimeZone; 48 49public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener, 50 OnLongClickListener, ChallengeLayout.OnBouncerStateChangedListener { 51 52 ZInterpolator mZInterpolator = new ZInterpolator(0.5f); 53 private static float CAMERA_DISTANCE = 10000; 54 protected static float OVERSCROLL_MAX_ROTATION = 30; 55 private static final boolean PERFORM_OVERSCROLL_ROTATION = true; 56 57 private static final int FLAG_HAS_LOCAL_HOUR = 0x1; 58 private static final int FLAG_HAS_LOCAL_MINUTE = 0x2; 59 60 protected KeyguardViewStateManager mViewStateManager; 61 private LockPatternUtils mLockPatternUtils; 62 63 // Related to the fading in / out background outlines 64 public static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375; 65 public static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100; 66 protected AnimatorSet mChildrenOutlineFadeAnimation; 67 protected int mScreenCenter; 68 private boolean mHasMeasure = false; 69 boolean showHintsAfterLayout = false; 70 71 private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000; 72 private static final String TAG = "KeyguardWidgetPager"; 73 private boolean mCenterSmallWidgetsVertically; 74 75 private int mPage = 0; 76 private Callbacks mCallbacks; 77 78 private int mWidgetToResetAfterFadeOut; 79 protected boolean mShowingInitialHints = false; 80 81 // A temporary handle to the Add-Widget view 82 private View mAddWidgetView; 83 private int mLastWidthMeasureSpec; 84 private int mLastHeightMeasureSpec; 85 86 // Bouncer 87 private int mBouncerZoomInOutDuration = 250; 88 private float BOUNCER_SCALE_FACTOR = 0.67f; 89 90 // Background worker thread: used here for persistence, also made available to widget frames 91 private final HandlerThread mBackgroundWorkerThread; 92 private final Handler mBackgroundWorkerHandler; 93 94 public KeyguardWidgetPager(Context context, AttributeSet attrs) { 95 this(context, attrs, 0); 96 } 97 98 public KeyguardWidgetPager(Context context) { 99 this(null, null, 0); 100 } 101 102 public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) { 103 super(context, attrs, defStyle); 104 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 105 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 106 } 107 108 setPageSwitchListener(this); 109 110 mBackgroundWorkerThread = new HandlerThread("KeyguardWidgetPager Worker"); 111 mBackgroundWorkerThread.start(); 112 mBackgroundWorkerHandler = new Handler(mBackgroundWorkerThread.getLooper()); 113 } 114 115 @Override 116 protected void onDetachedFromWindow() { 117 super.onDetachedFromWindow(); 118 119 // Clean up the worker thread 120 mBackgroundWorkerThread.quit(); 121 } 122 123 public void setViewStateManager(KeyguardViewStateManager viewStateManager) { 124 mViewStateManager = viewStateManager; 125 } 126 127 public void setLockPatternUtils(LockPatternUtils l) { 128 mLockPatternUtils = l; 129 } 130 131 @Override 132 public void onPageSwitching(View newPage, int newPageIndex) { 133 if (mViewStateManager != null) { 134 mViewStateManager.onPageSwitching(newPage, newPageIndex); 135 } 136 } 137 138 @Override 139 public void onPageSwitched(View newPage, int newPageIndex) { 140 boolean showingClock = false; 141 if (newPage instanceof ViewGroup) { 142 ViewGroup vg = (ViewGroup) newPage; 143 if (vg.getChildAt(0) instanceof KeyguardStatusView) { 144 showingClock = true; 145 } 146 } 147 148 if (newPage != null && 149 findClockInHierarchy(newPage) == (FLAG_HAS_LOCAL_HOUR | FLAG_HAS_LOCAL_MINUTE)) { 150 showingClock = true; 151 } 152 153 // Disable the status bar clock if we're showing the default status widget 154 if (showingClock) { 155 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK); 156 } else { 157 setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK); 158 } 159 160 // Extend the display timeout if the user switches pages 161 if (mPage != newPageIndex) { 162 int oldPageIndex = mPage; 163 mPage = newPageIndex; 164 userActivity(); 165 KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex); 166 if (oldWidgetPage != null) { 167 oldWidgetPage.onActive(false); 168 } 169 KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex); 170 if (newWidgetPage != null) { 171 newWidgetPage.onActive(true); 172 newWidgetPage.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 173 newWidgetPage.requestAccessibilityFocus(); 174 } 175 if (mParent != null && AccessibilityManager.getInstance(mContext).isEnabled()) { 176 AccessibilityEvent event = AccessibilityEvent.obtain( 177 AccessibilityEvent.TYPE_VIEW_SCROLLED); 178 onInitializeAccessibilityEvent(event); 179 onPopulateAccessibilityEvent(event); 180 mParent.requestSendAccessibilityEvent(this, event); 181 } 182 } 183 if (mViewStateManager != null) { 184 mViewStateManager.onPageSwitched(newPage, newPageIndex); 185 } 186 } 187 188 @Override 189 public void onPageBeginWarp() { 190 mViewStateManager.onPageBeginWarp(); 191 } 192 193 @Override 194 public void onPageEndWarp() { 195 mViewStateManager.onPageEndWarp(); 196 } 197 198 @Override 199 public void sendAccessibilityEvent(int eventType) { 200 if (eventType != AccessibilityEvent.TYPE_VIEW_SCROLLED || isPageMoving()) { 201 super.sendAccessibilityEvent(eventType); 202 } 203 } 204 205 private void updateWidgetFramesImportantForAccessibility() { 206 final int pageCount = getPageCount(); 207 for (int i = 0; i < pageCount; i++) { 208 KeyguardWidgetFrame frame = getWidgetPageAt(i); 209 updateWidgetFrameImportantForAccessibility(frame); 210 } 211 } 212 213 private void updateWidgetFrameImportantForAccessibility(KeyguardWidgetFrame frame) { 214 if (frame.getContentAlpha() <= 0) { 215 frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); 216 } else { 217 frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 218 } 219 } 220 221 private void userActivity() { 222 if (mCallbacks != null) { 223 mCallbacks.onUserActivityTimeoutChanged(); 224 mCallbacks.userActivity(); 225 } 226 } 227 228 @Override 229 public boolean onTouchEvent(MotionEvent ev) { 230 return captureUserInteraction(ev) || super.onTouchEvent(ev); 231 } 232 233 @Override 234 public boolean onInterceptTouchEvent(MotionEvent ev) { 235 return captureUserInteraction(ev) || super.onInterceptTouchEvent(ev); 236 } 237 238 private boolean captureUserInteraction(MotionEvent ev) { 239 KeyguardWidgetFrame currentWidgetPage = getWidgetPageAt(getCurrentPage()); 240 return currentWidgetPage != null && currentWidgetPage.onUserInteraction(ev); 241 } 242 243 public void showPagingFeedback() { 244 // Nothing yet. 245 } 246 247 public long getUserActivityTimeout() { 248 View page = getPageAt(mPage); 249 if (page instanceof ViewGroup) { 250 ViewGroup vg = (ViewGroup) page; 251 View view = vg.getChildAt(0); 252 if (!(view instanceof KeyguardStatusView) 253 && !(view instanceof KeyguardMultiUserSelectorView)) { 254 return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT; 255 } 256 } 257 return -1; 258 } 259 260 public void setCallbacks(Callbacks callbacks) { 261 mCallbacks = callbacks; 262 } 263 264 public interface Callbacks { 265 public void userActivity(); 266 public void onUserActivityTimeoutChanged(); 267 public void onAddView(View v); 268 public void onRemoveView(View v, boolean deletePermanently); 269 public void onRemoveViewAnimationCompleted(); 270 } 271 272 public void addWidget(View widget) { 273 addWidget(widget, -1); 274 } 275 276 public void onRemoveView(View v, final boolean deletePermanently) { 277 final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); 278 if (mCallbacks != null) { 279 mCallbacks.onRemoveView(v, deletePermanently); 280 } 281 mBackgroundWorkerHandler.post(new Runnable() { 282 @Override 283 public void run() { 284 mLockPatternUtils.removeAppWidget(appWidgetId); 285 } 286 }); 287 } 288 289 @Override 290 public void onRemoveViewAnimationCompleted() { 291 if (mCallbacks != null) { 292 mCallbacks.onRemoveViewAnimationCompleted(); 293 } 294 } 295 296 public void onAddView(View v, final int index) { 297 final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); 298 final int[] pagesRange = new int[mTempVisiblePagesRange.length]; 299 getVisiblePages(pagesRange); 300 boundByReorderablePages(true, pagesRange); 301 if (mCallbacks != null) { 302 mCallbacks.onAddView(v); 303 } 304 // Subtract from the index to take into account pages before the reorderable 305 // pages (e.g. the "add widget" page) 306 mBackgroundWorkerHandler.post(new Runnable() { 307 @Override 308 public void run() { 309 mLockPatternUtils.addAppWidget(appWidgetId, index - pagesRange[0]); 310 } 311 }); 312 } 313 314 /* 315 * We wrap widgets in a special frame which handles drawing the over scroll foreground. 316 */ 317 public void addWidget(View widget, int pageIndex) { 318 KeyguardWidgetFrame frame; 319 // All views contained herein should be wrapped in a KeyguardWidgetFrame 320 if (!(widget instanceof KeyguardWidgetFrame)) { 321 frame = new KeyguardWidgetFrame(getContext()); 322 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, 323 LayoutParams.MATCH_PARENT); 324 lp.gravity = Gravity.TOP; 325 326 // The framework adds a default padding to AppWidgetHostView. We don't need this padding 327 // for the Keyguard, so we override it to be 0. 328 widget.setPadding(0, 0, 0, 0); 329 frame.addView(widget, lp); 330 331 // We set whether or not this widget supports vertical resizing. 332 if (widget instanceof AppWidgetHostView) { 333 AppWidgetHostView awhv = (AppWidgetHostView) widget; 334 AppWidgetProviderInfo info = awhv.getAppWidgetInfo(); 335 if ((info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) { 336 frame.setWidgetLockedSmall(false); 337 } else { 338 // Lock the widget to be small. 339 frame.setWidgetLockedSmall(true); 340 if (mCenterSmallWidgetsVertically) { 341 lp.gravity = Gravity.CENTER; 342 } 343 } 344 } 345 } else { 346 frame = (KeyguardWidgetFrame) widget; 347 } 348 349 ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams( 350 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 351 frame.setOnLongClickListener(this); 352 frame.setWorkerHandler(mBackgroundWorkerHandler); 353 354 if (pageIndex == -1) { 355 addView(frame, pageLp); 356 } else { 357 addView(frame, pageIndex, pageLp); 358 } 359 360 // Update the frame content description. 361 View content = (widget == frame) ? frame.getContent() : widget; 362 if (content != null) { 363 String contentDescription = mContext.getString( 364 R.string.keyguard_accessibility_widget, 365 content.getContentDescription()); 366 frame.setContentDescription(contentDescription); 367 } 368 updateWidgetFrameImportantForAccessibility(frame); 369 } 370 371 /** 372 * Use addWidget() instead. 373 * @deprecated 374 */ 375 @Override 376 public void addView(View child, int index) { 377 enforceKeyguardWidgetFrame(child); 378 super.addView(child, index); 379 } 380 381 /** 382 * Use addWidget() instead. 383 * @deprecated 384 */ 385 @Override 386 public void addView(View child, int width, int height) { 387 enforceKeyguardWidgetFrame(child); 388 super.addView(child, width, height); 389 } 390 391 /** 392 * Use addWidget() instead. 393 * @deprecated 394 */ 395 @Override 396 public void addView(View child, LayoutParams params) { 397 enforceKeyguardWidgetFrame(child); 398 super.addView(child, params); 399 } 400 401 /** 402 * Use addWidget() instead. 403 * @deprecated 404 */ 405 @Override 406 public void addView(View child, int index, LayoutParams params) { 407 enforceKeyguardWidgetFrame(child); 408 super.addView(child, index, params); 409 } 410 411 private void enforceKeyguardWidgetFrame(View child) { 412 if (!(child instanceof KeyguardWidgetFrame)) { 413 throw new IllegalArgumentException( 414 "KeyguardWidgetPager children must be KeyguardWidgetFrames"); 415 } 416 } 417 418 public KeyguardWidgetFrame getWidgetPageAt(int index) { 419 // This is always a valid cast as we've guarded the ability to 420 return (KeyguardWidgetFrame) getChildAt(index); 421 } 422 423 protected void onUnhandledTap(MotionEvent ev) { 424 showPagingFeedback(); 425 } 426 427 @Override 428 protected void onPageBeginMoving() { 429 if (mViewStateManager != null) { 430 mViewStateManager.onPageBeginMoving(); 431 } 432 if (!isReordering(false)) { 433 showOutlinesAndSidePages(); 434 } 435 userActivity(); 436 } 437 438 @Override 439 protected void onPageEndMoving() { 440 if (mViewStateManager != null) { 441 mViewStateManager.onPageEndMoving(); 442 } 443 444 // In the reordering case, the pages will be faded appropriately on completion 445 // of the zoom in animation. 446 if (!isReordering(false)) { 447 hideOutlinesAndSidePages(); 448 } 449 } 450 451 protected void enablePageContentLayers() { 452 int children = getChildCount(); 453 for (int i = 0; i < children; i++) { 454 getWidgetPageAt(i).enableHardwareLayersForContent(); 455 } 456 } 457 458 protected void disablePageContentLayers() { 459 int children = getChildCount(); 460 for (int i = 0; i < children; i++) { 461 getWidgetPageAt(i).disableHardwareLayersForContent(); 462 } 463 } 464 465 /* 466 * This interpolator emulates the rate at which the perceived scale of an object changes 467 * as its distance from a camera increases. When this interpolator is applied to a scale 468 * animation on a view, it evokes the sense that the object is shrinking due to moving away 469 * from the camera. 470 */ 471 static class ZInterpolator implements TimeInterpolator { 472 private float focalLength; 473 474 public ZInterpolator(float foc) { 475 focalLength = foc; 476 } 477 478 public float getInterpolation(float input) { 479 return (1.0f - focalLength / (focalLength + input)) / 480 (1.0f - focalLength / (focalLength + 1.0f)); 481 } 482 } 483 484 @Override 485 protected void overScroll(float amount) { 486 acceleratedOverScroll(amount); 487 } 488 489 float backgroundAlphaInterpolator(float r) { 490 return Math.min(1f, r); 491 } 492 493 private void updatePageAlphaValues(int screenCenter) { 494 } 495 496 public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) { 497 if (showSidePages) { 498 return 1f; 499 } else { 500 return index == mCurrentPage ? 1.0f : 0f; 501 } 502 } 503 504 public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) { 505 if (showSidePages) { 506 return getAlphaForPage(screenCenter, index, showSidePages) 507 * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER; 508 } else { 509 return 0f; 510 } 511 } 512 513 protected boolean isOverScrollChild(int index, float scrollProgress) { 514 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; 515 return (isInOverscroll && (index == 0 && scrollProgress < 0 || 516 index == getChildCount() - 1 && scrollProgress > 0)); 517 } 518 519 @Override 520 protected void screenScrolled(int screenCenter) { 521 mScreenCenter = screenCenter; 522 updatePageAlphaValues(screenCenter); 523 for (int i = 0; i < getChildCount(); i++) { 524 KeyguardWidgetFrame v = getWidgetPageAt(i); 525 if (v == mDragView) continue; 526 if (v != null) { 527 float scrollProgress = getScrollProgress(screenCenter, v, i); 528 529 v.setCameraDistance(mDensity * CAMERA_DISTANCE); 530 531 if (isOverScrollChild(i, scrollProgress) && PERFORM_OVERSCROLL_ROTATION) { 532 float pivotX = v.getMeasuredWidth() / 2; 533 float pivotY = v.getMeasuredHeight() / 2; 534 v.setPivotX(pivotX); 535 v.setPivotY(pivotY); 536 v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress); 537 v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0); 538 } else { 539 v.setRotationY(0f); 540 v.setOverScrollAmount(0, false); 541 } 542 543 float alpha = v.getAlpha(); 544 // If the view has 0 alpha, we set it to be invisible so as to prevent 545 // it from accepting touches 546 if (alpha == 0) { 547 v.setVisibility(INVISIBLE); 548 } else if (v.getVisibility() != VISIBLE) { 549 v.setVisibility(VISIBLE); 550 } 551 } 552 } 553 } 554 555 public boolean isWidgetPage(int pageIndex) { 556 if (pageIndex < 0 || pageIndex >= getChildCount()) { 557 return false; 558 } 559 View v = getChildAt(pageIndex); 560 if (v != null && v instanceof KeyguardWidgetFrame) { 561 KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v; 562 return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID; 563 } 564 return false; 565 } 566 567 /** 568 * Returns the bounded set of pages that are re-orderable. The range is fully inclusive. 569 */ 570 @Override 571 void boundByReorderablePages(boolean isReordering, int[] range) { 572 if (isReordering) { 573 // Remove non-widget pages from the range 574 while (range[1] >= range[0] && !isWidgetPage(range[1])) { 575 range[1]--; 576 } 577 while (range[0] <= range[1] && !isWidgetPage(range[0])) { 578 range[0]++; 579 } 580 } 581 } 582 583 protected void reorderStarting() { 584 showOutlinesAndSidePages(); 585 } 586 587 @Override 588 protected void onStartReordering() { 589 super.onStartReordering(); 590 enablePageContentLayers(); 591 reorderStarting(); 592 } 593 594 @Override 595 protected void onEndReordering() { 596 super.onEndReordering(); 597 hideOutlinesAndSidePages(); 598 } 599 600 void showOutlinesAndSidePages() { 601 animateOutlinesAndSidePages(true); 602 } 603 604 void hideOutlinesAndSidePages() { 605 animateOutlinesAndSidePages(false); 606 } 607 608 void updateChildrenContentAlpha(float sidePageAlpha) { 609 int count = getChildCount(); 610 for (int i = 0; i < count; i++) { 611 KeyguardWidgetFrame child = getWidgetPageAt(i); 612 if (i != mCurrentPage) { 613 child.setBackgroundAlpha(sidePageAlpha); 614 child.setContentAlpha(0f); 615 } else { 616 child.setBackgroundAlpha(0f); 617 child.setContentAlpha(1f); 618 } 619 } 620 } 621 622 public void showInitialPageHints() { 623 mShowingInitialHints = true; 624 updateChildrenContentAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); 625 } 626 627 @Override 628 void setCurrentPage(int currentPage) { 629 super.setCurrentPage(currentPage); 630 updateChildrenContentAlpha(0.0f); 631 updateWidgetFramesImportantForAccessibility(); 632 } 633 634 @Override 635 public void onAttachedToWindow() { 636 super.onAttachedToWindow(); 637 mHasMeasure = false; 638 } 639 640 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 641 mLastWidthMeasureSpec = widthMeasureSpec; 642 mLastHeightMeasureSpec = heightMeasureSpec; 643 644 int maxChallengeTop = -1; 645 View parent = (View) getParent(); 646 boolean challengeShowing = false; 647 // Widget pages need to know where the top of the sliding challenge is so that they 648 // now how big the widget should be when the challenge is up. We compute it here and 649 // then propagate it to each of our children. 650 if (parent.getParent() instanceof SlidingChallengeLayout) { 651 SlidingChallengeLayout scl = (SlidingChallengeLayout) parent.getParent(); 652 int top = scl.getMaxChallengeTop(); 653 654 // This is a bit evil, but we need to map a coordinate relative to the SCL into a 655 // coordinate relative to our children, hence we subtract the top padding.s 656 maxChallengeTop = top - getPaddingTop(); 657 challengeShowing = scl.isChallengeShowing(); 658 659 int count = getChildCount(); 660 for (int i = 0; i < count; i++) { 661 KeyguardWidgetFrame frame = getWidgetPageAt(i); 662 frame.setMaxChallengeTop(maxChallengeTop); 663 // On the very first measure pass, if the challenge is showing, we need to make sure 664 // that the widget on the current page is small. 665 if (challengeShowing && i == mCurrentPage && !mHasMeasure) { 666 frame.shrinkWidget(); 667 } 668 } 669 } 670 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 671 mHasMeasure = true; 672 } 673 674 void animateOutlinesAndSidePages(final boolean show) { 675 animateOutlinesAndSidePages(show, -1); 676 } 677 678 public void setWidgetToResetOnPageFadeOut(int widget) { 679 mWidgetToResetAfterFadeOut = widget; 680 } 681 682 public int getWidgetToResetOnPageFadeOut() { 683 return mWidgetToResetAfterFadeOut; 684 } 685 686 void animateOutlinesAndSidePages(final boolean show, int duration) { 687 if (mChildrenOutlineFadeAnimation != null) { 688 mChildrenOutlineFadeAnimation.cancel(); 689 mChildrenOutlineFadeAnimation = null; 690 } 691 int count = getChildCount(); 692 PropertyValuesHolder alpha; 693 ArrayList<Animator> anims = new ArrayList<Animator>(); 694 695 if (duration == -1) { 696 duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION : 697 CHILDREN_OUTLINE_FADE_OUT_DURATION; 698 } 699 700 int curPage = getNextPage(); 701 for (int i = 0; i < count; i++) { 702 float finalContentAlpha; 703 if (show) { 704 finalContentAlpha = getAlphaForPage(mScreenCenter, i, true); 705 } else if (!show && i == curPage) { 706 finalContentAlpha = 1f; 707 } else { 708 finalContentAlpha = 0f; 709 } 710 KeyguardWidgetFrame child = getWidgetPageAt(i); 711 712 alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalContentAlpha); 713 ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha); 714 anims.add(a); 715 716 float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i, true) : 0f; 717 child.fadeFrame(this, show, finalOutlineAlpha, duration); 718 } 719 720 mChildrenOutlineFadeAnimation = new AnimatorSet(); 721 mChildrenOutlineFadeAnimation.playTogether(anims); 722 723 mChildrenOutlineFadeAnimation.setDuration(duration); 724 mChildrenOutlineFadeAnimation.addListener(new AnimatorListenerAdapter() { 725 @Override 726 public void onAnimationStart(Animator animation) { 727 if (show) { 728 enablePageContentLayers(); 729 } 730 } 731 732 @Override 733 public void onAnimationEnd(Animator animation) { 734 if (!show) { 735 disablePageContentLayers(); 736 KeyguardWidgetFrame frame = getWidgetPageAt(mWidgetToResetAfterFadeOut); 737 if (frame != null && !(frame == getWidgetPageAt(mCurrentPage) && 738 mViewStateManager.isChallengeOverlapping())) { 739 frame.resetSize(); 740 } 741 mWidgetToResetAfterFadeOut = -1; 742 mShowingInitialHints = false; 743 } 744 updateWidgetFramesImportantForAccessibility(); 745 } 746 }); 747 mChildrenOutlineFadeAnimation.start(); 748 } 749 750 @Override 751 public boolean onLongClick(View v) { 752 // Disallow long pressing to reorder if the challenge is showing 753 boolean isChallengeOverlapping = mViewStateManager.isChallengeShowing() && 754 mViewStateManager.isChallengeOverlapping(); 755 if (!isChallengeOverlapping && startReordering()) { 756 return true; 757 } 758 return false; 759 } 760 761 public void removeWidget(View view) { 762 if (view instanceof KeyguardWidgetFrame) { 763 removeView(view); 764 } else { 765 // Assume view was wrapped by a KeyguardWidgetFrame in KeyguardWidgetPager#addWidget(). 766 // This supports legacy hard-coded "widgets" like KeyguardTransportControlView. 767 int pos = getWidgetPageIndex(view); 768 if (pos != -1) { 769 KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(pos); 770 frame.removeView(view); 771 removeView(frame); 772 } else { 773 Slog.w(TAG, "removeWidget() can't find:" + view); 774 } 775 } 776 } 777 778 public int getWidgetPageIndex(View view) { 779 if (view instanceof KeyguardWidgetFrame) { 780 return indexOfChild(view); 781 } else { 782 // View was wrapped by a KeyguardWidgetFrame by KeyguardWidgetPager#addWidget() 783 return indexOfChild((KeyguardWidgetFrame)view.getParent()); 784 } 785 } 786 787 @Override 788 protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) { 789 KeyguardWidgetFrame child = getWidgetPageAt(viewIndex); 790 child.setIsHoveringOverDeleteDropTarget(isHovering); 791 } 792 793 // ChallengeLayout.OnBouncerStateChangedListener 794 @Override 795 public void onBouncerStateChanged(boolean bouncerActive) { 796 if (bouncerActive) { 797 zoomOutToBouncer(); 798 } else { 799 zoomInFromBouncer(); 800 } 801 } 802 803 void setBouncerAnimationDuration(int duration) { 804 mBouncerZoomInOutDuration = duration; 805 } 806 807 // Zoom in after the bouncer is dismissed 808 void zoomInFromBouncer() { 809 if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) { 810 mZoomInOutAnim.cancel(); 811 } 812 final View currentPage = getPageAt(getCurrentPage()); 813 if (currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f) { 814 mZoomInOutAnim = new AnimatorSet(); 815 mZoomInOutAnim.playTogether( 816 ObjectAnimator.ofFloat(currentPage, "scaleX", 1f), 817 ObjectAnimator.ofFloat(currentPage , "scaleY", 1f)); 818 mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration); 819 mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f)); 820 mZoomInOutAnim.start(); 821 } 822 if (currentPage instanceof KeyguardWidgetFrame) { 823 ((KeyguardWidgetFrame)currentPage).onBouncerShowing(false); 824 } 825 } 826 827 // Zoom out after the bouncer is initiated 828 void zoomOutToBouncer() { 829 if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) { 830 mZoomInOutAnim.cancel(); 831 } 832 int curPage = getCurrentPage(); 833 View currentPage = getPageAt(curPage); 834 if (shouldSetTopAlignedPivotForWidget(curPage)) { 835 currentPage.setPivotY(0); 836 // Note: we are working around the issue that setting the x-pivot to the same value as it 837 // was does not actually work. 838 currentPage.setPivotX(0); 839 currentPage.setPivotX(currentPage.getMeasuredWidth() / 2); 840 } 841 if (!(currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f)) { 842 mZoomInOutAnim = new AnimatorSet(); 843 mZoomInOutAnim.playTogether( 844 ObjectAnimator.ofFloat(currentPage, "scaleX", BOUNCER_SCALE_FACTOR), 845 ObjectAnimator.ofFloat(currentPage, "scaleY", BOUNCER_SCALE_FACTOR)); 846 mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration); 847 mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f)); 848 mZoomInOutAnim.start(); 849 } 850 if (currentPage instanceof KeyguardWidgetFrame) { 851 ((KeyguardWidgetFrame)currentPage).onBouncerShowing(true); 852 } 853 } 854 855 void setAddWidgetEnabled(boolean enabled) { 856 if (mAddWidgetView != null && enabled) { 857 addView(mAddWidgetView, 0); 858 // We need to force measure the PagedView so that the calls to update the scroll 859 // position below work 860 measure(mLastWidthMeasureSpec, mLastHeightMeasureSpec); 861 // Bump up the current page to account for the addition of the new page 862 setCurrentPage(mCurrentPage + 1); 863 mAddWidgetView = null; 864 } else if (mAddWidgetView == null && !enabled) { 865 View addWidget = findViewById(R.id.keyguard_add_widget); 866 if (addWidget != null) { 867 mAddWidgetView = addWidget; 868 removeView(addWidget); 869 } 870 } 871 } 872 873 boolean isAddPage(int pageIndex) { 874 View v = getChildAt(pageIndex); 875 return v != null && v.getId() == R.id.keyguard_add_widget; 876 } 877 878 boolean isCameraPage(int pageIndex) { 879 View v = getChildAt(pageIndex); 880 return v != null && v instanceof CameraWidgetFrame; 881 } 882 883 @Override 884 protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) { 885 return !isCameraPage(childIndex) && super.shouldSetTopAlignedPivotForWidget(childIndex); 886 } 887 888 /** 889 * Search given {@link View} hierarchy for {@link TextClock} instances that 890 * show various time components. Returns combination of 891 * {@link #FLAG_HAS_LOCAL_HOUR} and {@link #FLAG_HAS_LOCAL_MINUTE}. 892 */ 893 private static int findClockInHierarchy(View view) { 894 if (view instanceof TextClock) { 895 return getClockFlags((TextClock) view); 896 } else if (view instanceof ViewGroup) { 897 int flags = 0; 898 final ViewGroup group = (ViewGroup) view; 899 final int size = group.getChildCount(); 900 for (int i = 0; i < size; i++) { 901 flags |= findClockInHierarchy(group.getChildAt(i)); 902 } 903 return flags; 904 } else { 905 return 0; 906 } 907 } 908 909 /** 910 * Return combination of {@link #FLAG_HAS_LOCAL_HOUR} and 911 * {@link #FLAG_HAS_LOCAL_MINUTE} describing the time represented described 912 * by the given {@link TextClock}. 913 */ 914 private static int getClockFlags(TextClock clock) { 915 int flags = 0; 916 917 final String timeZone = clock.getTimeZone(); 918 if (timeZone != null && !TimeZone.getDefault().equals(TimeZone.getTimeZone(timeZone))) { 919 // Ignore clocks showing another timezone 920 return 0; 921 } 922 923 final CharSequence format = clock.getFormat(); 924 final char hour = clock.is24HourModeEnabled() ? DateFormat.HOUR_OF_DAY 925 : DateFormat.HOUR; 926 927 if (DateFormat.hasDesignator(format, hour)) { 928 flags |= FLAG_HAS_LOCAL_HOUR; 929 } 930 if (DateFormat.hasDesignator(format, DateFormat.MINUTE)) { 931 flags |= FLAG_HAS_LOCAL_MINUTE; 932 } 933 934 return flags; 935 } 936 937 public void handleExternalCameraEvent(MotionEvent event) { 938 beginCameraEvent(); 939 int cameraPage = getPageCount() - 1; 940 boolean endWarp = false; 941 if (isCameraPage(cameraPage)) { 942 switch (event.getAction()) { 943 case MotionEvent.ACTION_DOWN: 944 userActivity(); 945 startWarp(cameraPage); 946 break; 947 case MotionEvent.ACTION_UP: 948 case MotionEvent.ACTION_CANCEL: 949 endWarp = true; 950 break; 951 } 952 dispatchTouchEvent(event); 953 // This has to happen after the event has been handled by the real widget pager 954 if (endWarp) endWarp(); 955 } 956 endCameraEvent(); 957 } 958 959} 960