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