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