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