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