KeyguardWidgetPager.java revision e3643138c89d49a01ba6a622ffaf71c9a95d5cdc
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.animation.Animator; 19import android.animation.AnimatorListenerAdapter; 20import android.animation.AnimatorSet; 21import android.animation.ObjectAnimator; 22import android.animation.PropertyValuesHolder; 23import android.animation.TimeInterpolator; 24import android.appwidget.AppWidgetHostView; 25import android.content.Context; 26import android.content.res.Resources; 27import android.util.AttributeSet; 28import android.view.Gravity; 29import android.view.MotionEvent; 30import android.view.View; 31import android.view.View.OnLongClickListener; 32import android.view.ViewGroup; 33import android.widget.FrameLayout; 34 35import com.android.internal.R; 36 37import com.android.internal.widget.LockPatternUtils; 38 39import java.util.ArrayList; 40 41public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener, 42 OnLongClickListener { 43 44 ZInterpolator mZInterpolator = new ZInterpolator(0.5f); 45 private static float CAMERA_DISTANCE = 10000; 46 protected static float OVERSCROLL_MAX_ROTATION = 30; 47 private static final boolean PERFORM_OVERSCROLL_ROTATION = true; 48 49 private KeyguardViewStateManager mViewStateManager; 50 private LockPatternUtils mLockPatternUtils; 51 52 // Related to the fading in / out background outlines 53 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375; 54 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 75; 55 protected AnimatorSet mChildrenOutlineFadeAnimation; 56 private float mChildrenOutlineAlpha = 0; 57 private float mSidePagesAlpha = 1f; 58 protected int mScreenCenter; 59 60 private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000; 61 62 private int mPage = 0; 63 private Callbacks mCallbacks; 64 65 private boolean mCameraWidgetEnabled; 66 67 public KeyguardWidgetPager(Context context, AttributeSet attrs) { 68 this(context, attrs, 0); 69 } 70 71 public KeyguardWidgetPager(Context context) { 72 this(null, null, 0); 73 } 74 75 public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) { 76 super(context, attrs, defStyle); 77 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 78 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 79 } 80 81 setPageSwitchListener(this); 82 83 Resources r = getResources(); 84 mCameraWidgetEnabled = r.getBoolean(R.bool.kg_enable_camera_default_widget); 85 } 86 87 public void setViewStateManager(KeyguardViewStateManager viewStateManager) { 88 mViewStateManager = viewStateManager; 89 } 90 91 public void setLockPatternUtils(LockPatternUtils l) { 92 mLockPatternUtils = l; 93 } 94 95 @Override 96 public void onPageSwitch(View newPage, int newPageIndex) { 97 boolean showingStatusWidget = false; 98 if (newPage instanceof ViewGroup) { 99 ViewGroup vg = (ViewGroup) newPage; 100 if (vg.getChildAt(0) instanceof KeyguardStatusView) { 101 showingStatusWidget = true; 102 } 103 } 104 105 // Disable the status bar clock if we're showing the default status widget 106 if (showingStatusWidget) { 107 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK); 108 } else { 109 setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK); 110 } 111 112 // Extend the display timeout if the user switches pages 113 if (mPage != newPageIndex) { 114 int oldPageIndex = mPage; 115 mPage = newPageIndex; 116 if (mCallbacks != null) { 117 mCallbacks.onUserActivityTimeoutChanged(); 118 mCallbacks.userActivity(); 119 } 120 KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex); 121 if (oldWidgetPage != null) { 122 oldWidgetPage.onActive(false); 123 } 124 KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex); 125 if (newWidgetPage != null) { 126 newWidgetPage.onActive(true); 127 } 128 } 129 if (mViewStateManager != null) { 130 mViewStateManager.onPageSwitch(newPage, newPageIndex); 131 } 132 } 133 134 public void showPagingFeedback() { 135 // Nothing yet. 136 } 137 138 public long getUserActivityTimeout() { 139 View page = getPageAt(mPage); 140 if (page instanceof ViewGroup) { 141 ViewGroup vg = (ViewGroup) page; 142 View view = vg.getChildAt(0); 143 if (!(view instanceof KeyguardStatusView) 144 && !(view instanceof KeyguardMultiUserSelectorView)) { 145 return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT; 146 } 147 } 148 return -1; 149 } 150 151 public void setCallbacks(Callbacks callbacks) { 152 mCallbacks = callbacks; 153 } 154 155 public interface Callbacks { 156 public void userActivity(); 157 public void onUserActivityTimeoutChanged(); 158 } 159 160 public void addWidget(View widget) { 161 addWidget(widget, -1); 162 } 163 164 165 public void onRemoveView(View v) { 166 int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); 167 mLockPatternUtils.removeAppWidget(appWidgetId); 168 } 169 170 public void onAddView(View v, int index) { 171 int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); 172 getVisiblePages(mTempVisiblePagesRange); 173 boundByReorderablePages(true, mTempVisiblePagesRange); 174 // Subtract from the index to take into account pages before the reorderable 175 // pages (e.g. the "add widget" page) 176 mLockPatternUtils.addAppWidget(appWidgetId, index - mTempVisiblePagesRange[0]); 177 } 178 179 /* 180 * We wrap widgets in a special frame which handles drawing the over scroll foreground. 181 */ 182 public void addWidget(View widget, int pageIndex) { 183 KeyguardWidgetFrame frame; 184 // All views contained herein should be wrapped in a KeyguardWidgetFrame 185 if (!(widget instanceof KeyguardWidgetFrame)) { 186 frame = new KeyguardWidgetFrame(getContext()); 187 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, 188 LayoutParams.MATCH_PARENT); 189 lp.gravity = Gravity.TOP; 190 // The framework adds a default padding to AppWidgetHostView. We don't need this padding 191 // for the Keyguard, so we override it to be 0. 192 widget.setPadding(0, 0, 0, 0); 193 if (widget instanceof AppWidgetHostView) { 194 AppWidgetHostView awhv = (AppWidgetHostView) widget; 195 widget.setContentDescription(awhv.getAppWidgetInfo().label); 196 } 197 frame.addView(widget, lp); 198 } else { 199 frame = (KeyguardWidgetFrame) widget; 200 } 201 202 ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams( 203 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 204 frame.setOnLongClickListener(this); 205 206 if (pageIndex == -1) { 207 addView(frame, pageLp); 208 } else { 209 addView(frame, pageIndex, pageLp); 210 } 211 } 212 213 // We enforce that all children are KeyguardWidgetFrames 214 @Override 215 public void addView(View child, int index) { 216 enforceKeyguardWidgetFrame(child); 217 super.addView(child, index); 218 } 219 220 @Override 221 public void addView(View child, int width, int height) { 222 enforceKeyguardWidgetFrame(child); 223 super.addView(child, width, height); 224 } 225 226 @Override 227 public void addView(View child, LayoutParams params) { 228 enforceKeyguardWidgetFrame(child); 229 super.addView(child, params); 230 } 231 232 @Override 233 public void addView(View child, int index, LayoutParams params) { 234 enforceKeyguardWidgetFrame(child); 235 super.addView(child, index, params); 236 } 237 238 private void enforceKeyguardWidgetFrame(View child) { 239 if (!(child instanceof KeyguardWidgetFrame)) { 240 throw new IllegalArgumentException( 241 "KeyguardWidgetPager children must be KeyguardWidgetFrames"); 242 } 243 } 244 245 public KeyguardWidgetFrame getWidgetPageAt(int index) { 246 // This is always a valid cast as we've guarded the ability to 247 return (KeyguardWidgetFrame) getChildAt(index); 248 } 249 250 protected void onUnhandledTap(MotionEvent ev) { 251 showPagingFeedback(); 252 } 253 254 @Override 255 protected void onPageBeginMoving() { 256 if (mViewStateManager != null) { 257 mViewStateManager.onPageBeginMoving(); 258 } 259 showOutlinesAndSidePages(); 260 } 261 262 @Override 263 protected void onPageEndMoving() { 264 if (mViewStateManager != null) { 265 mViewStateManager.onPageEndMoving(); 266 } 267 hideOutlinesAndSidePages(); 268 } 269 270 private void enablePageLayers() { 271 int children = getChildCount(); 272 for (int i = 0; i < children; i++) { 273 getWidgetPageAt(i).enableHardwareLayersForContent(); 274 } 275 } 276 277 private void disablePageLayers() { 278 int children = getChildCount(); 279 for (int i = 0; i < children; i++) { 280 getWidgetPageAt(i).disableHardwareLayersForContent(); 281 } 282 } 283 284 /* 285 * This interpolator emulates the rate at which the perceived scale of an object changes 286 * as its distance from a camera increases. When this interpolator is applied to a scale 287 * animation on a view, it evokes the sense that the object is shrinking due to moving away 288 * from the camera. 289 */ 290 static class ZInterpolator implements TimeInterpolator { 291 private float focalLength; 292 293 public ZInterpolator(float foc) { 294 focalLength = foc; 295 } 296 297 public float getInterpolation(float input) { 298 return (1.0f - focalLength / (focalLength + input)) / 299 (1.0f - focalLength / (focalLength + 1.0f)); 300 } 301 } 302 303 @Override 304 public String getCurrentPageDescription() { 305 final int nextPageIndex = getNextPage(); 306 if (nextPageIndex >= 0 && nextPageIndex < getChildCount()) { 307 KeyguardWidgetFrame frame = getWidgetPageAt(nextPageIndex); 308 CharSequence title = frame.getChildAt(0).getContentDescription(); 309 if (title == null) { 310 title = ""; 311 } 312 return mContext.getString( 313 com.android.internal.R.string.keyguard_accessibility_widget_changed, 314 title, nextPageIndex + 1, getChildCount()); 315 } 316 return super.getCurrentPageDescription(); 317 } 318 319 @Override 320 protected void overScroll(float amount) { 321 acceleratedOverScroll(amount); 322 } 323 324 float backgroundAlphaInterpolator(float r) { 325 return Math.min(1f, r); 326 } 327 328 private void updatePageAlphaValues(int screenCenter) { 329 } 330 331 public float getAlphaForPage(int screenCenter, int index) { 332 return 1f; 333 } 334 335 protected boolean isOverScrollChild(int index, float scrollProgress) { 336 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; 337 return (isInOverscroll && (index == 0 && scrollProgress < 0 || 338 index == getChildCount() - 1 && scrollProgress > 0)); 339 } 340 341 @Override 342 protected void screenScrolled(int screenCenter) { 343 mScreenCenter = screenCenter; 344 updatePageAlphaValues(screenCenter); 345 for (int i = 0; i < getChildCount(); i++) { 346 KeyguardWidgetFrame v = getWidgetPageAt(i); 347 if (v == mDragView) continue; 348 if (v != null) { 349 float scrollProgress = getScrollProgress(screenCenter, v, i); 350 351 v.setCameraDistance(mDensity * CAMERA_DISTANCE); 352 353 if (isOverScrollChild(i, scrollProgress) && PERFORM_OVERSCROLL_ROTATION) { 354 v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress); 355 v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0); 356 } else { 357 v.setRotationY(0f); 358 v.setOverScrollAmount(0, false); 359 } 360 361 float alpha = v.getAlpha(); 362 // If the view has 0 alpha, we set it to be invisible so as to prevent 363 // it from accepting touches 364 if (alpha == 0) { 365 v.setVisibility(INVISIBLE); 366 } else if (v.getVisibility() != VISIBLE) { 367 v.setVisibility(VISIBLE); 368 } 369 } 370 } 371 } 372 373 @Override 374 void boundByReorderablePages(boolean isReordering, int[] range) { 375 if (isReordering) { 376 if (isAddWidgetPageVisible()) { 377 range[0]++; 378 } 379 if (isMusicWidgetVisible()) { 380 range[1]--; 381 } 382 if (isCameraWidgetVisible()) { 383 range[1]--; 384 } 385 } 386 } 387 388 /* 389 * Special widgets 390 */ 391 boolean isAddWidgetPageVisible() { 392 // TODO: Make proper test once we decide whether the add-page is always showing 393 return true; 394 } 395 boolean isMusicWidgetVisible() { 396 // TODO: Make proper test once we have music in the list 397 return false; 398 } 399 boolean isCameraWidgetVisible() { 400 return mCameraWidgetEnabled; 401 } 402 403 @Override 404 protected void onStartReordering() { 405 super.onStartReordering(); 406 showOutlinesAndSidePages(); 407 } 408 409 @Override 410 protected void onEndReordering() { 411 super.onEndReordering(); 412 hideOutlinesAndSidePages(); 413 } 414 415 void showOutlinesAndSidePages() { 416 enablePageLayers(); 417 animateOutlinesAndSidePages(true); 418 } 419 420 void hideOutlinesAndSidePages() { 421 animateOutlinesAndSidePages(false); 422 } 423 424 public void showInitialPageHints() { 425 showOutlinesAndSidePages(); 426 } 427 428 void animateOutlinesAndSidePages(final boolean show) { 429 if (mChildrenOutlineFadeAnimation != null) { 430 mChildrenOutlineFadeAnimation.cancel(); 431 mChildrenOutlineFadeAnimation = null; 432 } 433 434 int count = getChildCount(); 435 PropertyValuesHolder alpha; 436 ArrayList<Animator> anims = new ArrayList<Animator>(); 437 438 int duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION : 439 CHILDREN_OUTLINE_FADE_OUT_DURATION; 440 441 int curPage = getNextPage(); 442 for (int i = 0; i < count; i++) { 443 float finalContentAlpha; 444 if (show) { 445 finalContentAlpha = getAlphaForPage(mScreenCenter, i); 446 } else if (!show && i == curPage) { 447 finalContentAlpha = 1f; 448 } else { 449 finalContentAlpha = 0f; 450 } 451 KeyguardWidgetFrame child = getWidgetPageAt(i); 452 alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalContentAlpha); 453 ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha); 454 anims.add(a); 455 456 float finalOutlineAlpha = show ? getAlphaForPage(mScreenCenter, i) : 0f; 457 child.fadeFrame(this, show, finalOutlineAlpha, duration); 458 } 459 460 mChildrenOutlineFadeAnimation = new AnimatorSet(); 461 mChildrenOutlineFadeAnimation.playTogether(anims); 462 463 mChildrenOutlineFadeAnimation.setDuration(duration); 464 mChildrenOutlineFadeAnimation.addListener(new AnimatorListenerAdapter() { 465 @Override 466 public void onAnimationEnd(Animator animation) { 467 if (!show) { 468 disablePageLayers(); 469 } 470 } 471 }); 472 mChildrenOutlineFadeAnimation.start(); 473 } 474 475 public void setChildrenOutlineAlpha(float alpha) { 476 mChildrenOutlineAlpha = alpha; 477 for (int i = 0; i < getChildCount(); i++) { 478 getWidgetPageAt(i).setBackgroundAlpha(alpha); 479 } 480 } 481 482 public void setSidePagesAlpha(float alpha) { 483 // This gives the current page, or the destination page if in transit. 484 int curPage = getNextPage(); 485 mSidePagesAlpha = alpha; 486 for (int i = 0; i < getChildCount(); i++) { 487 if (curPage != i) { 488 getWidgetPageAt(i).setContentAlpha(alpha); 489 } else { 490 // We lock the current page alpha to 1. 491 getWidgetPageAt(i).setContentAlpha(1.0f); 492 } 493 } 494 } 495 496 public void setChildrenOutlineMultiplier(float alpha) { 497 mChildrenOutlineAlpha = alpha; 498 for (int i = 0; i < getChildCount(); i++) { 499 getWidgetPageAt(i).setBackgroundAlphaMultiplier(alpha); 500 } 501 } 502 503 public float getSidePagesAlpha() { 504 return mSidePagesAlpha; 505 } 506 507 public float getChildrenOutlineAlpha() { 508 return mChildrenOutlineAlpha; 509 } 510 511 @Override 512 public boolean onLongClick(View v) { 513 if (startReordering()) { 514 return true; 515 } 516 return false; 517 } 518} 519