KeyguardWidgetPager.java revision 838906b165e4d3cb2c512b2db344aa50cb5d4751
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.TimeInterpolator; 19import android.appwidget.AppWidgetHostView; 20import android.content.Context; 21import android.util.AttributeSet; 22import android.view.Gravity; 23import android.view.MotionEvent; 24import android.view.View; 25import android.view.ViewGroup; 26import android.view.animation.AccelerateInterpolator; 27import android.view.animation.DecelerateInterpolator; 28 29import android.widget.FrameLayout; 30 31import com.android.internal.R; 32 33public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener { 34 ZInterpolator mZInterpolator = new ZInterpolator(0.5f); 35 private static float CAMERA_DISTANCE = 10000; 36 private static float TRANSITION_SCALE_FACTOR = 0.74f; 37 private static float TRANSITION_PIVOT = 0.65f; 38 private static float TRANSITION_MAX_ROTATION = 30; 39 private static final boolean PERFORM_OVERSCROLL_ROTATION = true; 40 private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f); 41 private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4); 42 43 private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000; 44 private static final boolean CAFETERIA_TRAY = false; 45 46 private int mPage = 0; 47 private Callbacks mCallbacks; 48 49 public KeyguardWidgetPager(Context context, AttributeSet attrs) { 50 this(context, attrs, 0); 51 } 52 53 public KeyguardWidgetPager(Context context) { 54 this(null, null, 0); 55 } 56 57 public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) { 58 super(context, attrs, defStyle); 59 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 60 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 61 } 62 63 setPageSwitchListener(this); 64 } 65 66 @Override 67 public void onPageSwitch(View newPage, int newPageIndex) { 68 boolean showingStatusWidget = false; 69 if (newPage instanceof ViewGroup) { 70 ViewGroup vg = (ViewGroup) newPage; 71 if (vg.getChildAt(0) instanceof KeyguardStatusView) { 72 showingStatusWidget = true; 73 } 74 } 75 76 // Disable the status bar clock if we're showing the default status widget 77 if (showingStatusWidget) { 78 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK); 79 } else { 80 setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK); 81 } 82 83 // Extend the display timeout if the user switches pages 84 if (mPage != newPageIndex) { 85 mPage = newPageIndex; 86 if (mCallbacks != null) { 87 mCallbacks.onUserActivityTimeoutChanged(); 88 mCallbacks.userActivity(); 89 } 90 } 91 } 92 93 public void showPagingFeedback() { 94 // Nothing yet. 95 } 96 97 public long getUserActivityTimeout() { 98 View page = getPageAt(mPage); 99 if (page instanceof ViewGroup) { 100 ViewGroup vg = (ViewGroup) page; 101 View view = vg.getChildAt(0); 102 if (!(view instanceof KeyguardStatusView) 103 && !(view instanceof KeyguardMultiUserSelectorView)) { 104 return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT; 105 } 106 } 107 return -1; 108 } 109 110 public void setCallbacks(Callbacks callbacks) { 111 mCallbacks = callbacks; 112 } 113 114 public interface Callbacks { 115 public void userActivity(); 116 public void onUserActivityTimeoutChanged(); 117 } 118 119 /* 120 * We wrap widgets in a special frame which handles drawing the overscroll foreground. 121 */ 122 public void addWidget(AppWidgetHostView widget) { 123 KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext()); 124 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, 125 LayoutParams.MATCH_PARENT); 126 lp.gravity = Gravity.CENTER; 127 // The framework adds a default padding to AppWidgetHostView. We don't need this padding 128 // for the Keyguard, so we override it to be 0. 129 widget.setPadding(0, 0, 0, 0); 130 widget.setContentDescription(widget.getAppWidgetInfo().label); 131 frame.addView(widget, lp); 132 addView(frame); 133 } 134 135 protected void onUnhandledTap(MotionEvent ev) { 136 showPagingFeedback(); 137 } 138 139 @Override 140 protected void onPageBeginMoving() { 141 // Enable hardware layers while pages are moving 142 // TODO: We should only do this for the two views that are actually moving 143 int children = getChildCount(); 144 for (int i = 0; i < children; i++) { 145 getChildAt(i).setLayerType(LAYER_TYPE_HARDWARE, null); 146 } 147 } 148 149 @Override 150 protected void onPageEndMoving() { 151 // Disable hardware layers while pages are moving 152 int children = getChildCount(); 153 for (int i = 0; i < children; i++) { 154 getChildAt(i).setLayerType(LAYER_TYPE_NONE, null); 155 } 156 } 157 158 /* 159 * This interpolator emulates the rate at which the perceived scale of an object changes 160 * as its distance from a camera increases. When this interpolator is applied to a scale 161 * animation on a view, it evokes the sense that the object is shrinking due to moving away 162 * from the camera. 163 */ 164 static class ZInterpolator implements TimeInterpolator { 165 private float focalLength; 166 167 public ZInterpolator(float foc) { 168 focalLength = foc; 169 } 170 171 public float getInterpolation(float input) { 172 return (1.0f - focalLength / (focalLength + input)) / 173 (1.0f - focalLength / (focalLength + 1.0f)); 174 } 175 } 176 177 @Override 178 public String getCurrentPageDescription() { 179 final int nextPageIndex = getNextPage(); 180 if (nextPageIndex >= 0 && nextPageIndex < getChildCount()) { 181 KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(nextPageIndex); 182 CharSequence title = frame.getChildAt(0).getContentDescription(); 183 if (title == null) { 184 title = ""; 185 } 186 return mContext.getString( 187 com.android.internal.R.string.keyguard_accessibility_widget_changed, 188 title, nextPageIndex + 1, getChildCount()); 189 } 190 return super.getCurrentPageDescription(); 191 } 192 193 @Override 194 protected void overScroll(float amount) { 195 acceleratedOverScroll(amount); 196 } 197 198 // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack. 199 @Override 200 protected void screenScrolled(int screenCenter) { 201 super.screenScrolled(screenCenter); 202 203 for (int i = 0; i < getChildCount(); i++) { 204 View v = getPageAt(i); 205 if (v != null) { 206 float scrollProgress = getScrollProgress(screenCenter, v, i); 207 float interpolatedProgress = 208 mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0))); 209 float scale = 1.0f; 210 float translationX = 0; 211 float alpha = 1.0f; 212 213 if (CAFETERIA_TRAY) { 214 scale = (1 - interpolatedProgress) + 215 interpolatedProgress * TRANSITION_SCALE_FACTOR; 216 translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth(); 217 218 if (scrollProgress < 0) { 219 alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation( 220 1 - Math.abs(scrollProgress)) : 1.0f; 221 } else { 222 // On large screens we need to fade the page as it nears its leftmost position 223 alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress); 224 } 225 } 226 227 v.setCameraDistance(mDensity * CAMERA_DISTANCE); 228 int pageWidth = v.getMeasuredWidth(); 229 int pageHeight = v.getMeasuredHeight(); 230 231 if (PERFORM_OVERSCROLL_ROTATION) { 232 if (i == 0 && scrollProgress < 0) { 233 // Overscroll to the left 234 v.setPivotX(TRANSITION_PIVOT * pageWidth); 235 v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); 236 if (v instanceof KeyguardWidgetFrame) { 237 ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress), 238 true); 239 } 240 scale = 1.0f; 241 alpha = 1.0f; 242 // On the first page, we don't want the page to have any lateral motion 243 translationX = 0; 244 } else if (i == getChildCount() - 1 && scrollProgress > 0) { 245 // Overscroll to the right 246 v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth); 247 v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); 248 scale = 1.0f; 249 alpha = 1.0f; 250 if (v instanceof KeyguardWidgetFrame) { 251 ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress), 252 false); 253 } 254 // On the last page, we don't want the page to have any lateral motion. 255 translationX = 0; 256 } else { 257 v.setPivotY(pageHeight / 2.0f); 258 v.setPivotX(pageWidth / 2.0f); 259 v.setRotationY(0f); 260 if (v instanceof KeyguardWidgetFrame) { 261 ((KeyguardWidgetFrame) v).setOverScrollAmount(0, false); 262 } 263 } 264 } 265 266 v.setTranslationX(translationX); 267 v.setScaleX(scale); 268 v.setScaleY(scale); 269 v.setAlpha(alpha); 270 271 // If the view has 0 alpha, we set it to be invisible so as to prevent 272 // it from accepting touches 273 if (alpha == 0) { 274 v.setVisibility(INVISIBLE); 275 } else if (v.getVisibility() != VISIBLE) { 276 v.setVisibility(VISIBLE); 277 } 278 } 279 } 280 } 281} 282