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