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