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