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