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.Animator.AnimatorListener;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.os.Handler;
     22 import android.os.Looper;
     23 import android.view.View;
     24 
     25 public class KeyguardViewStateManager implements
     26         SlidingChallengeLayout.OnChallengeScrolledListener,
     27         ChallengeLayout.OnBouncerStateChangedListener {
     28 
     29     private static final String TAG = "KeyguardViewStateManager";
     30     private KeyguardWidgetPager mKeyguardWidgetPager;
     31     private ChallengeLayout mChallengeLayout;
     32     private KeyguardHostView mKeyguardHostView;
     33     private int[] mTmpPoint = new int[2];
     34     private int[] mTmpLoc = new int[2];
     35 
     36     private KeyguardSecurityView mKeyguardSecurityContainer;
     37     private static final int SCREEN_ON_HINT_DURATION = 1000;
     38     private static final int SCREEN_ON_RING_HINT_DELAY = 300;
     39     private static final boolean SHOW_INITIAL_PAGE_HINTS = false;
     40     Handler mMainQueue = new Handler(Looper.myLooper());
     41 
     42     int mLastScrollState = SlidingChallengeLayout.SCROLL_STATE_IDLE;
     43 
     44     // Paged view state
     45     private int mPageListeningToSlider = -1;
     46     private int mCurrentPage = -1;
     47     private int mPageIndexOnPageBeginMoving = -1;
     48 
     49     int mChallengeTop = 0;
     50 
     51     private final AnimatorListener mPauseListener = new AnimatorListenerAdapter() {
     52         public void onAnimationEnd(Animator animation) {
     53             mKeyguardSecurityContainer.onPause();
     54         }
     55     };
     56 
     57     private final AnimatorListener mResumeListener = new AnimatorListenerAdapter() {
     58         public void onAnimationEnd(Animator animation) {
     59             if (((View)mKeyguardSecurityContainer).isShown()) {
     60                 mKeyguardSecurityContainer.onResume(0);
     61             }
     62         }
     63     };
     64 
     65     public KeyguardViewStateManager(KeyguardHostView hostView) {
     66         mKeyguardHostView = hostView;
     67     }
     68 
     69     public void setPagedView(KeyguardWidgetPager pagedView) {
     70         mKeyguardWidgetPager = pagedView;
     71         updateEdgeSwiping();
     72     }
     73 
     74     public void setChallengeLayout(ChallengeLayout layout) {
     75         mChallengeLayout = layout;
     76         updateEdgeSwiping();
     77     }
     78 
     79     private void updateEdgeSwiping() {
     80         if (mChallengeLayout != null && mKeyguardWidgetPager != null) {
     81             if (mChallengeLayout.isChallengeOverlapping()) {
     82                 mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(true);
     83             } else {
     84                 mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(false);
     85             }
     86         }
     87     }
     88 
     89     public boolean isChallengeShowing() {
     90         if (mChallengeLayout != null) {
     91             return mChallengeLayout.isChallengeShowing();
     92         }
     93         return false;
     94     }
     95 
     96     public boolean isChallengeOverlapping() {
     97         if (mChallengeLayout != null) {
     98             return mChallengeLayout.isChallengeOverlapping();
     99         }
    100         return false;
    101     }
    102 
    103     public void setSecurityViewContainer(KeyguardSecurityView container) {
    104         mKeyguardSecurityContainer = container;
    105     }
    106 
    107     public void showBouncer(boolean show) {
    108         mChallengeLayout.showBouncer();
    109     }
    110 
    111     public boolean isBouncing() {
    112         return mChallengeLayout.isBouncing();
    113     }
    114 
    115     public void fadeOutSecurity(int duration) {
    116         ((View) mKeyguardSecurityContainer).animate().alpha(0f).setDuration(duration)
    117                 .setListener(mPauseListener);
    118     }
    119 
    120     public void fadeInSecurity(int duration) {
    121         ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration)
    122                 .setListener(mResumeListener);
    123     }
    124 
    125     public void onPageBeginMoving() {
    126         if (mChallengeLayout.isChallengeOverlapping() &&
    127                 mChallengeLayout instanceof SlidingChallengeLayout) {
    128             SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
    129             scl.fadeOutChallenge();
    130             mPageIndexOnPageBeginMoving = mKeyguardWidgetPager.getCurrentPage();
    131         }
    132         // We use mAppWidgetToShow to show a particular widget after you add it--
    133         // once the user swipes a page we clear that behavior
    134         if (mKeyguardHostView != null) {
    135             mKeyguardHostView.clearAppWidgetToShow();
    136             mKeyguardHostView.setOnDismissAction(null);
    137         }
    138         if (mHideHintsRunnable != null) {
    139             mMainQueue.removeCallbacks(mHideHintsRunnable);
    140             mHideHintsRunnable = null;
    141         }
    142     }
    143 
    144     public void onPageEndMoving() {
    145         mPageIndexOnPageBeginMoving = -1;
    146     }
    147 
    148     public void onPageSwitching(View newPage, int newPageIndex) {
    149         if (mKeyguardWidgetPager != null && mChallengeLayout instanceof SlidingChallengeLayout) {
    150             boolean isCameraPage = newPage instanceof CameraWidgetFrame;
    151             if (isCameraPage) {
    152                 CameraWidgetFrame camera = (CameraWidgetFrame) newPage;
    153                 camera.setUseFastTransition(mKeyguardWidgetPager.isWarping());
    154             }
    155             SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
    156             scl.setChallengeInteractive(!isCameraPage);
    157             final int currentFlags = mKeyguardWidgetPager.getSystemUiVisibility();
    158             final int newFlags = isCameraPage ? (currentFlags | View.STATUS_BAR_DISABLE_SEARCH)
    159                     : (currentFlags & ~View.STATUS_BAR_DISABLE_SEARCH);
    160             mKeyguardWidgetPager.setSystemUiVisibility(newFlags);
    161         }
    162 
    163         // If the page we're settling to is the same as we started on, and the action of
    164         // moving the page hid the security, we restore it immediately.
    165         if (mPageIndexOnPageBeginMoving == mKeyguardWidgetPager.getNextPage() &&
    166                 mChallengeLayout instanceof SlidingChallengeLayout) {
    167             SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout;
    168             scl.fadeInChallenge();
    169             mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(-1);
    170         }
    171         mPageIndexOnPageBeginMoving = -1;
    172     }
    173 
    174     public void onPageSwitched(View newPage, int newPageIndex) {
    175         // Reset the previous page size and ensure the current page is sized appropriately.
    176         // We only modify the page state if it is not currently under control by the slider.
    177         // This prevents conflicts.
    178 
    179         // If the page hasn't switched, don't bother with any of this
    180         if (mCurrentPage == newPageIndex) return;
    181 
    182         if (mKeyguardWidgetPager != null && mChallengeLayout != null) {
    183             KeyguardWidgetFrame prevPage = mKeyguardWidgetPager.getWidgetPageAt(mCurrentPage);
    184             if (prevPage != null && mCurrentPage != mPageListeningToSlider && mCurrentPage
    185                     != mKeyguardWidgetPager.getWidgetToResetOnPageFadeOut()) {
    186                 prevPage.resetSize();
    187             }
    188 
    189             KeyguardWidgetFrame newCurPage = mKeyguardWidgetPager.getWidgetPageAt(newPageIndex);
    190             boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
    191             if (challengeOverlapping && !newCurPage.isSmall()
    192                     && mPageListeningToSlider != newPageIndex) {
    193                 newCurPage.shrinkWidget(true);
    194             }
    195         }
    196 
    197         mCurrentPage = newPageIndex;
    198     }
    199 
    200     public void onPageBeginWarp() {
    201         fadeOutSecurity(SlidingChallengeLayout.CHALLENGE_FADE_OUT_DURATION);
    202         View frame = mKeyguardWidgetPager.getPageAt(mKeyguardWidgetPager.getPageWarpIndex());
    203         ((KeyguardWidgetFrame)frame).showFrame(this);
    204     }
    205 
    206     public void onPageEndWarp() {
    207         fadeInSecurity(SlidingChallengeLayout.CHALLENGE_FADE_IN_DURATION);
    208         View frame = mKeyguardWidgetPager.getPageAt(mKeyguardWidgetPager.getPageWarpIndex());
    209         ((KeyguardWidgetFrame)frame).hideFrame(this);
    210     }
    211 
    212     private int getChallengeTopRelativeToFrame(KeyguardWidgetFrame frame, int top) {
    213         mTmpPoint[0] = 0;
    214         mTmpPoint[1] = top;
    215         mapPoint((View) mChallengeLayout, frame, mTmpPoint);
    216         return mTmpPoint[1];
    217     }
    218 
    219     /**
    220      * Simple method to map a point from one view's coordinates to another's. Note: this method
    221      * doesn't account for transforms, so if the views will be transformed, this should not be used.
    222      *
    223      * @param fromView The view to which the point is relative
    224      * @param toView The view into which the point should be mapped
    225      * @param pt The point
    226      */
    227     private void mapPoint(View fromView, View toView, int pt[]) {
    228         fromView.getLocationInWindow(mTmpLoc);
    229 
    230         int x = mTmpLoc[0];
    231         int y = mTmpLoc[1];
    232 
    233         toView.getLocationInWindow(mTmpLoc);
    234         int vX = mTmpLoc[0];
    235         int vY = mTmpLoc[1];
    236 
    237         pt[0] += x - vX;
    238         pt[1] += y - vY;
    239     }
    240 
    241     private void userActivity() {
    242         if (mKeyguardHostView != null) {
    243             mKeyguardHostView.onUserActivityTimeoutChanged();
    244             mKeyguardHostView.userActivity();
    245         }
    246     }
    247 
    248     @Override
    249     public void onScrollStateChanged(int scrollState) {
    250         if (mKeyguardWidgetPager == null || mChallengeLayout == null) return;
    251 
    252         boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
    253 
    254         if (scrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
    255             KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
    256             if (frame == null) return;
    257 
    258             if (!challengeOverlapping) {
    259                 if (!mKeyguardWidgetPager.isPageMoving()) {
    260                     frame.resetSize();
    261                     userActivity();
    262                 } else {
    263                     mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(mPageListeningToSlider);
    264                 }
    265             }
    266             if (frame.isSmall()) {
    267                 // This is to make sure that if the scroller animation gets cut off midway
    268                 // that the frame doesn't stay in a partial down position.
    269                 frame.setFrameHeight(frame.getSmallFrameHeight());
    270             }
    271             if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
    272                 frame.hideFrame(this);
    273             }
    274             updateEdgeSwiping();
    275 
    276             if (mChallengeLayout.isChallengeShowing()) {
    277                 mKeyguardSecurityContainer.onResume(KeyguardSecurityView.VIEW_REVEALED);
    278             } else {
    279                 mKeyguardSecurityContainer.onPause();
    280             }
    281             mPageListeningToSlider = -1;
    282         } else if (mLastScrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
    283             // Whether dragging or settling, if the last state was idle, we use this signal
    284             // to update the current page who will receive events from the sliding challenge.
    285             // We resize the frame as appropriate.
    286             mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
    287             KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
    288             if (frame == null) return;
    289 
    290             // Skip showing the frame and shrinking the widget if we are
    291             if (!mChallengeLayout.isBouncing()) {
    292                 if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
    293                     frame.showFrame(this);
    294                 }
    295 
    296                 // As soon as the security begins sliding, the widget becomes small (if it wasn't
    297                 // small to begin with).
    298                 if (!frame.isSmall()) {
    299                     // We need to fetch the final page, in case the pages are in motion.
    300                     mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
    301                     frame.shrinkWidget(false);
    302                 }
    303             } else {
    304                 if (!frame.isSmall()) {
    305                     // We need to fetch the final page, in case the pages are in motion.
    306                     mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
    307                 }
    308             }
    309 
    310             // View is on the move.  Pause the security view until it completes.
    311             mKeyguardSecurityContainer.onPause();
    312         }
    313         mLastScrollState = scrollState;
    314     }
    315 
    316     @Override
    317     public void onScrollPositionChanged(float scrollPosition, int challengeTop) {
    318         mChallengeTop = challengeTop;
    319         KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
    320         if (frame != null && mLastScrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
    321             frame.adjustFrame(getChallengeTopRelativeToFrame(frame, mChallengeTop));
    322         }
    323     }
    324 
    325     private Runnable mHideHintsRunnable = new Runnable() {
    326         @Override
    327         public void run() {
    328             if (mKeyguardWidgetPager != null) {
    329                 mKeyguardWidgetPager.hideOutlinesAndSidePages();
    330             }
    331         }
    332     };
    333 
    334     public void showUsabilityHints() {
    335         mMainQueue.postDelayed( new Runnable() {
    336             @Override
    337             public void run() {
    338                 mKeyguardSecurityContainer.showUsabilityHint();
    339             }
    340         } , SCREEN_ON_RING_HINT_DELAY);
    341         if (SHOW_INITIAL_PAGE_HINTS) {
    342             mKeyguardWidgetPager.showInitialPageHints();
    343         }
    344         if (mHideHintsRunnable != null) {
    345             mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION);
    346         }
    347     }
    348 
    349     // ChallengeLayout.OnBouncerStateChangedListener
    350     @Override
    351     public void onBouncerStateChanged(boolean bouncerActive) {
    352         if (bouncerActive) {
    353             mKeyguardWidgetPager.zoomOutToBouncer();
    354         } else {
    355             mKeyguardWidgetPager.zoomInFromBouncer();
    356             if (mKeyguardHostView != null) {
    357                 mKeyguardHostView.setOnDismissAction(null);
    358             }
    359         }
    360     }
    361 }
    362