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 */ 16package com.android.keyguard; 17 18import android.animation.Animator; 19import android.animation.Animator.AnimatorListener; 20import android.animation.AnimatorListenerAdapter; 21import android.os.Handler; 22import android.os.Looper; 23import android.view.View; 24 25public 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