Home | History | Annotate | Download | only in keyguard
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.internal.policy.impl.keyguard;
     17 
     18 import android.animation.Animator;
     19 import android.animation.AnimatorListenerAdapter;
     20 import android.animation.AnimatorSet;
     21 import android.animation.ObjectAnimator;
     22 import android.animation.PropertyValuesHolder;
     23 import android.animation.TimeInterpolator;
     24 import android.appwidget.AppWidgetHostView;
     25 import android.appwidget.AppWidgetManager;
     26 import android.appwidget.AppWidgetProviderInfo;
     27 import android.content.Context;
     28 import android.os.Handler;
     29 import android.os.HandlerThread;
     30 import android.util.AttributeSet;
     31 import android.util.Slog;
     32 import android.view.Gravity;
     33 import android.view.MotionEvent;
     34 import android.view.View;
     35 import android.view.View.OnLongClickListener;
     36 import android.view.ViewGroup;
     37 import android.view.accessibility.AccessibilityEvent;
     38 import android.view.accessibility.AccessibilityManager;
     39 import android.view.animation.DecelerateInterpolator;
     40 import android.widget.FrameLayout;
     41 
     42 import com.android.internal.widget.LockPatternUtils;
     43 
     44 import java.util.ArrayList;
     45 
     46 public 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