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