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