blob: c5664571df6e8aba77ac7f4ad02c2feefca0e69c [file] [log] [blame]
Jim Millerdcb3d842012-08-23 19:18:12 -07001/*
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 */
Jim Miller5ecd8112013-01-09 18:50:26 -080016package com.android.keyguard;
Jim Millerdcb3d842012-08-23 19:18:12 -070017
Adam Cohenf9048cd2012-10-27 16:36:10 -070018import android.animation.Animator;
19import android.animation.AnimatorListenerAdapter;
20import android.animation.AnimatorSet;
Jim Millerd6523da2012-10-21 16:47:02 -070021import android.animation.ObjectAnimator;
Adam Cohen6fb841f2012-10-24 13:15:38 -070022import android.animation.PropertyValuesHolder;
Jim Millerdcb3d842012-08-23 19:18:12 -070023import android.animation.TimeInterpolator;
Adam Cohen4ddcd572012-11-01 17:36:32 -070024import android.appwidget.AppWidgetHostView;
Jim Miller51117262012-11-04 17:58:09 -080025import android.appwidget.AppWidgetManager;
Adam Cohen4ddcd572012-11-01 17:36:32 -070026import android.appwidget.AppWidgetProviderInfo;
Jim Millerdcb3d842012-08-23 19:18:12 -070027import android.content.Context;
Winson Chungf8f59662012-10-30 12:03:18 -070028import android.os.Handler;
29import android.os.HandlerThread;
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -080030import android.text.format.DateFormat;
Jim Millerdcb3d842012-08-23 19:18:12 -070031import android.util.AttributeSet;
Jim Millerbdca3c02012-10-29 19:11:50 -070032import android.util.Slog;
Jim Millerf2dfc352012-08-29 18:42:21 -070033import android.view.Gravity;
Jim Millerc1aa6a52012-09-28 19:37:26 -070034import android.view.MotionEvent;
Jim Millerdcb3d842012-08-23 19:18:12 -070035import android.view.View;
Jim Millerd6523da2012-10-21 16:47:02 -070036import android.view.View.OnLongClickListener;
Adam Cohen4ddcd572012-11-01 17:36:32 -070037import android.view.ViewGroup;
Svetoslav Ganovc4842c12012-10-31 14:33:32 -070038import android.view.accessibility.AccessibilityEvent;
39import android.view.accessibility.AccessibilityManager;
Winson Chung70c2f872012-11-07 21:47:12 -080040import android.view.animation.DecelerateInterpolator;
Jim Millerf2dfc352012-08-29 18:42:21 -070041import android.widget.FrameLayout;
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -080042import android.widget.TextClock;
Jim Millerdcb3d842012-08-23 19:18:12 -070043
Michael Jurka1254f2f2012-10-25 11:44:31 -070044import com.android.internal.widget.LockPatternUtils;
Svetoslav Ganov6526fdd2012-10-02 16:21:20 -070045
Adam Cohenf9048cd2012-10-27 16:36:10 -070046import java.util.ArrayList;
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -080047import java.util.TimeZone;
Adam Cohenf9048cd2012-10-27 16:36:10 -070048
Jim Millerd6523da2012-10-21 16:47:02 -070049public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener,
Winson Chung48275d22012-11-05 10:56:31 -080050 OnLongClickListener, ChallengeLayout.OnBouncerStateChangedListener {
Jim Millerd6523da2012-10-21 16:47:02 -070051
Jim Millerdcb3d842012-08-23 19:18:12 -070052 ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
Jim Millerf2dfc352012-08-29 18:42:21 -070053 private static float CAMERA_DISTANCE = 10000;
Adam Cohenf9048cd2012-10-27 16:36:10 -070054 protected static float OVERSCROLL_MAX_ROTATION = 30;
Jim Millerdcb3d842012-08-23 19:18:12 -070055 private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
Adam Cohen9ec871d2012-10-24 19:25:44 -070056
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -080057 private static final int FLAG_HAS_LOCAL_HOUR = 0x1;
58 private static final int FLAG_HAS_LOCAL_MINUTE = 0x2;
59
Adam Cohen70009e42012-10-30 16:48:22 -070060 protected KeyguardViewStateManager mViewStateManager;
Michael Jurka1254f2f2012-10-25 11:44:31 -070061 private LockPatternUtils mLockPatternUtils;
Jim Millerd6523da2012-10-21 16:47:02 -070062
63 // Related to the fading in / out background outlines
Adam Cohendb1c5d52012-11-03 17:10:07 -070064 public static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
65 public static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
Adam Cohenf9048cd2012-10-27 16:36:10 -070066 protected AnimatorSet mChildrenOutlineFadeAnimation;
Adam Cohenf9048cd2012-10-27 16:36:10 -070067 protected int mScreenCenter;
Adam Cohen8a7785c2012-10-29 22:01:33 -070068 private boolean mHasMeasure = false;
Adam Cohen70009e42012-10-30 16:48:22 -070069 boolean showHintsAfterLayout = false;
Jim Millerf2dfc352012-08-29 18:42:21 -070070
Jim Miller838906b2012-10-19 18:41:25 -070071 private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
Jim Millerbdca3c02012-10-29 19:11:50 -070072 private static final String TAG = "KeyguardWidgetPager";
Adam Cohen6f72b1c2012-11-05 15:20:30 -080073 private boolean mCenterSmallWidgetsVertically;
Jim Miller838906b2012-10-19 18:41:25 -070074
75 private int mPage = 0;
76 private Callbacks mCallbacks;
77
Adam Cohen8caabad2012-11-04 17:23:25 -080078 private int mWidgetToResetAfterFadeOut;
Adam Cohen2b0501b2012-11-21 16:49:13 -080079 protected boolean mShowingInitialHints = false;
Adam Cohendb1c5d52012-11-03 17:10:07 -070080
Winson Chung4752e7d2012-11-20 17:06:04 -080081 // A temporary handle to the Add-Widget view
82 private View mAddWidgetView;
83 private int mLastWidthMeasureSpec;
84 private int mLastHeightMeasureSpec;
85
Winson Chung48275d22012-11-05 10:56:31 -080086 // Bouncer
Winson Chung70c2f872012-11-07 21:47:12 -080087 private int mBouncerZoomInOutDuration = 250;
Winson Chung48275d22012-11-05 10:56:31 -080088 private float BOUNCER_SCALE_FACTOR = 0.67f;
89
John Spurlock37d84ae2012-11-04 11:11:47 -050090 // Background worker thread: used here for persistence, also made available to widget frames
91 private final HandlerThread mBackgroundWorkerThread;
92 private final Handler mBackgroundWorkerHandler;
Winson Chungf8f59662012-10-30 12:03:18 -070093
Jim Millerf2dfc352012-08-29 18:42:21 -070094 public KeyguardWidgetPager(Context context, AttributeSet attrs) {
95 this(context, attrs, 0);
96 }
97
98 public KeyguardWidgetPager(Context context) {
99 this(null, null, 0);
100 }
101
102 public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
103 super(context, attrs, defStyle);
Svetoslav Ganov6526fdd2012-10-02 16:21:20 -0700104 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
105 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
106 }
Jim Miller838906b2012-10-19 18:41:25 -0700107
108 setPageSwitchListener(this);
Winson Chungefc49252012-10-26 15:41:27 -0700109
John Spurlock37d84ae2012-11-04 11:11:47 -0500110 mBackgroundWorkerThread = new HandlerThread("KeyguardWidgetPager Worker");
111 mBackgroundWorkerThread.start();
112 mBackgroundWorkerHandler = new Handler(mBackgroundWorkerThread.getLooper());
Jim Millerf2dfc352012-08-29 18:42:21 -0700113 }
114
Winson Chunge72855e2012-11-01 18:05:04 -0700115 @Override
116 protected void onDetachedFromWindow() {
117 super.onDetachedFromWindow();
118
John Spurlock37d84ae2012-11-04 11:11:47 -0500119 // Clean up the worker thread
120 mBackgroundWorkerThread.quit();
Winson Chunge72855e2012-11-01 18:05:04 -0700121 }
122
Jim Millerd6523da2012-10-21 16:47:02 -0700123 public void setViewStateManager(KeyguardViewStateManager viewStateManager) {
124 mViewStateManager = viewStateManager;
125 }
126
Michael Jurka1254f2f2012-10-25 11:44:31 -0700127 public void setLockPatternUtils(LockPatternUtils l) {
128 mLockPatternUtils = l;
129 }
130
Jim Miller838906b2012-10-19 18:41:25 -0700131 @Override
John Spurlockbb5c9412012-10-31 09:46:15 -0400132 public void onPageSwitching(View newPage, int newPageIndex) {
133 if (mViewStateManager != null) {
134 mViewStateManager.onPageSwitching(newPage, newPageIndex);
135 }
136 }
137
138 @Override
139 public void onPageSwitched(View newPage, int newPageIndex) {
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -0800140 boolean showingClock = false;
Jim Miller838906b2012-10-19 18:41:25 -0700141 if (newPage instanceof ViewGroup) {
142 ViewGroup vg = (ViewGroup) newPage;
143 if (vg.getChildAt(0) instanceof KeyguardStatusView) {
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -0800144 showingClock = true;
Jim Miller838906b2012-10-19 18:41:25 -0700145 }
146 }
147
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -0800148 if (newPage != null &&
149 findClockInHierarchy(newPage) == (FLAG_HAS_LOCAL_HOUR | FLAG_HAS_LOCAL_MINUTE)) {
150 showingClock = true;
151 }
152
Jim Miller838906b2012-10-19 18:41:25 -0700153 // Disable the status bar clock if we're showing the default status widget
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -0800154 if (showingClock) {
Jim Miller838906b2012-10-19 18:41:25 -0700155 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
156 } else {
157 setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
158 }
159
160 // Extend the display timeout if the user switches pages
161 if (mPage != newPageIndex) {
John Spurlock86b63572012-10-24 11:24:25 -0400162 int oldPageIndex = mPage;
Jim Miller838906b2012-10-19 18:41:25 -0700163 mPage = newPageIndex;
Chris Wren32c557c2012-10-29 11:16:42 -0400164 userActivity();
John Spurlock86b63572012-10-24 11:24:25 -0400165 KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex);
166 if (oldWidgetPage != null) {
167 oldWidgetPage.onActive(false);
168 }
169 KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex);
170 if (newWidgetPage != null) {
171 newWidgetPage.onActive(true);
Svetoslav4293e232013-04-25 13:25:06 -0700172 newWidgetPage.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
Svetoslav Ganovc4842c12012-10-31 14:33:32 -0700173 newWidgetPage.requestAccessibilityFocus();
John Spurlock86b63572012-10-24 11:24:25 -0400174 }
Svetoslav Ganov45942ca2012-10-31 19:46:24 -0700175 if (mParent != null && AccessibilityManager.getInstance(mContext).isEnabled()) {
176 AccessibilityEvent event = AccessibilityEvent.obtain(
177 AccessibilityEvent.TYPE_VIEW_SCROLLED);
178 onInitializeAccessibilityEvent(event);
179 onPopulateAccessibilityEvent(event);
180 mParent.requestSendAccessibilityEvent(this, event);
181 }
Jim Miller838906b2012-10-19 18:41:25 -0700182 }
Jim Millerd6523da2012-10-21 16:47:02 -0700183 if (mViewStateManager != null) {
John Spurlockbb5c9412012-10-31 09:46:15 -0400184 mViewStateManager.onPageSwitched(newPage, newPageIndex);
Jim Millerd6523da2012-10-21 16:47:02 -0700185 }
Svetoslav Ganovc4842c12012-10-31 14:33:32 -0700186 }
187
188 @Override
189 public void sendAccessibilityEvent(int eventType) {
190 if (eventType != AccessibilityEvent.TYPE_VIEW_SCROLLED || isPageMoving()) {
191 super.sendAccessibilityEvent(eventType);
192 }
Jim Miller838906b2012-10-19 18:41:25 -0700193 }
194
Svetoslav Ganova90a18d2012-11-06 15:22:11 -0800195 private void updateWidgetFramesImportantForAccessibility() {
196 final int pageCount = getPageCount();
197 for (int i = 0; i < pageCount; i++) {
198 KeyguardWidgetFrame frame = getWidgetPageAt(i);
199 updateWidgetFrameImportantForAccessibility(frame);
200 }
201 }
202
203 private void updateWidgetFrameImportantForAccessibility(KeyguardWidgetFrame frame) {
204 if (frame.getContentAlpha() <= 0) {
205 frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
206 } else {
207 frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
208 }
209 }
210
Chris Wren32c557c2012-10-29 11:16:42 -0400211 private void userActivity() {
212 if (mCallbacks != null) {
213 mCallbacks.onUserActivityTimeoutChanged();
214 mCallbacks.userActivity();
215 }
216 }
217
John Spurlock4b976ea2012-10-28 12:34:11 -0400218 @Override
219 public boolean onTouchEvent(MotionEvent ev) {
John Spurlockf70239a2012-11-04 09:42:42 -0500220 return captureUserInteraction(ev) || super.onTouchEvent(ev);
221 }
222
223 @Override
224 public boolean onInterceptTouchEvent(MotionEvent ev) {
225 return captureUserInteraction(ev) || super.onInterceptTouchEvent(ev);
226 }
227
228 private boolean captureUserInteraction(MotionEvent ev) {
John Spurlock4b976ea2012-10-28 12:34:11 -0400229 KeyguardWidgetFrame currentWidgetPage = getWidgetPageAt(getCurrentPage());
John Spurlockf70239a2012-11-04 09:42:42 -0500230 return currentWidgetPage != null && currentWidgetPage.onUserInteraction(ev);
John Spurlock4b976ea2012-10-28 12:34:11 -0400231 }
232
Jim Miller838906b2012-10-19 18:41:25 -0700233 public void showPagingFeedback() {
234 // Nothing yet.
235 }
Jim Millerd6523da2012-10-21 16:47:02 -0700236
Jim Miller838906b2012-10-19 18:41:25 -0700237 public long getUserActivityTimeout() {
238 View page = getPageAt(mPage);
239 if (page instanceof ViewGroup) {
240 ViewGroup vg = (ViewGroup) page;
241 View view = vg.getChildAt(0);
242 if (!(view instanceof KeyguardStatusView)
243 && !(view instanceof KeyguardMultiUserSelectorView)) {
244 return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT;
245 }
246 }
247 return -1;
248 }
249
250 public void setCallbacks(Callbacks callbacks) {
251 mCallbacks = callbacks;
252 }
253
254 public interface Callbacks {
255 public void userActivity();
256 public void onUserActivityTimeoutChanged();
Winson Chung40f82112012-11-08 11:54:55 -0800257 public void onAddView(View v);
Michael Jurka75b5cfb2012-11-15 18:22:47 -0800258 public void onRemoveView(View v, boolean deletePermanently);
Winson Chung4752e7d2012-11-20 17:06:04 -0800259 public void onRemoveViewAnimationCompleted();
Jim Miller838906b2012-10-19 18:41:25 -0700260 }
Jim Millerd6523da2012-10-21 16:47:02 -0700261
262 public void addWidget(View widget) {
263 addWidget(widget, -1);
264 }
265
Michael Jurka75b5cfb2012-11-15 18:22:47 -0800266 public void onRemoveView(View v, final boolean deletePermanently) {
Winson Chungf8f59662012-10-30 12:03:18 -0700267 final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
Michael Jurkaeb963f02012-11-08 00:32:29 -0800268 if (mCallbacks != null) {
Michael Jurka75b5cfb2012-11-15 18:22:47 -0800269 mCallbacks.onRemoveView(v, deletePermanently);
Michael Jurkaeb963f02012-11-08 00:32:29 -0800270 }
John Spurlock37d84ae2012-11-04 11:11:47 -0500271 mBackgroundWorkerHandler.post(new Runnable() {
Winson Chungf8f59662012-10-30 12:03:18 -0700272 @Override
273 public void run() {
274 mLockPatternUtils.removeAppWidget(appWidgetId);
275 }
276 });
Michael Jurka1254f2f2012-10-25 11:44:31 -0700277 }
278
Winson Chung4752e7d2012-11-20 17:06:04 -0800279 @Override
280 public void onRemoveViewAnimationCompleted() {
281 if (mCallbacks != null) {
282 mCallbacks.onRemoveViewAnimationCompleted();
283 }
284 }
285
Winson Chungf8f59662012-10-30 12:03:18 -0700286 public void onAddView(View v, final int index) {
287 final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
288 final int[] pagesRange = new int[mTempVisiblePagesRange.length];
289 getVisiblePages(pagesRange);
290 boundByReorderablePages(true, pagesRange);
Winson Chung40f82112012-11-08 11:54:55 -0800291 if (mCallbacks != null) {
292 mCallbacks.onAddView(v);
293 }
Michael Jurka1254f2f2012-10-25 11:44:31 -0700294 // Subtract from the index to take into account pages before the reorderable
295 // pages (e.g. the "add widget" page)
John Spurlock37d84ae2012-11-04 11:11:47 -0500296 mBackgroundWorkerHandler.post(new Runnable() {
Winson Chungf8f59662012-10-30 12:03:18 -0700297 @Override
298 public void run() {
299 mLockPatternUtils.addAppWidget(appWidgetId, index - pagesRange[0]);
300 }
301 });
Michael Jurka1254f2f2012-10-25 11:44:31 -0700302 }
303
Jim Millerf2dfc352012-08-29 18:42:21 -0700304 /*
Jim Millerd6523da2012-10-21 16:47:02 -0700305 * We wrap widgets in a special frame which handles drawing the over scroll foreground.
Jim Millerf2dfc352012-08-29 18:42:21 -0700306 */
Jim Millerd6523da2012-10-21 16:47:02 -0700307 public void addWidget(View widget, int pageIndex) {
308 KeyguardWidgetFrame frame;
309 // All views contained herein should be wrapped in a KeyguardWidgetFrame
310 if (!(widget instanceof KeyguardWidgetFrame)) {
311 frame = new KeyguardWidgetFrame(getContext());
312 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
313 LayoutParams.MATCH_PARENT);
314 lp.gravity = Gravity.TOP;
Adam Cohen4ddcd572012-11-01 17:36:32 -0700315
Jim Millerd6523da2012-10-21 16:47:02 -0700316 // The framework adds a default padding to AppWidgetHostView. We don't need this padding
317 // for the Keyguard, so we override it to be 0.
318 widget.setPadding(0, 0, 0, 0);
Jim Millerd6523da2012-10-21 16:47:02 -0700319 frame.addView(widget, lp);
Adam Cohen4ddcd572012-11-01 17:36:32 -0700320
321 // We set whether or not this widget supports vertical resizing.
322 if (widget instanceof AppWidgetHostView) {
323 AppWidgetHostView awhv = (AppWidgetHostView) widget;
324 AppWidgetProviderInfo info = awhv.getAppWidgetInfo();
325 if ((info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
326 frame.setWidgetLockedSmall(false);
327 } else {
328 // Lock the widget to be small.
329 frame.setWidgetLockedSmall(true);
Adam Cohen6f72b1c2012-11-05 15:20:30 -0800330 if (mCenterSmallWidgetsVertically) {
331 lp.gravity = Gravity.CENTER;
332 }
Adam Cohen4ddcd572012-11-01 17:36:32 -0700333 }
334 }
Jim Millerd6523da2012-10-21 16:47:02 -0700335 } else {
336 frame = (KeyguardWidgetFrame) widget;
337 }
338
339 ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams(
340 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
341 frame.setOnLongClickListener(this);
John Spurlock37d84ae2012-11-04 11:11:47 -0500342 frame.setWorkerHandler(mBackgroundWorkerHandler);
Jim Millerd6523da2012-10-21 16:47:02 -0700343
344 if (pageIndex == -1) {
345 addView(frame, pageLp);
346 } else {
347 addView(frame, pageIndex, pageLp);
348 }
Svetoslav Ganovc4842c12012-10-31 14:33:32 -0700349
350 // Update the frame content description.
351 View content = (widget == frame) ? frame.getContent() : widget;
352 if (content != null) {
353 String contentDescription = mContext.getString(
Jim Miller5ecd8112013-01-09 18:50:26 -0800354 R.string.keyguard_accessibility_widget,
Svetoslav Ganovc4842c12012-10-31 14:33:32 -0700355 content.getContentDescription());
356 frame.setContentDescription(contentDescription);
357 }
Svetoslav Ganova90a18d2012-11-06 15:22:11 -0800358 updateWidgetFrameImportantForAccessibility(frame);
Jim Millerd6523da2012-10-21 16:47:02 -0700359 }
360
Jim Millerbdca3c02012-10-29 19:11:50 -0700361 /**
362 * Use addWidget() instead.
363 * @deprecated
364 */
Jim Millerd6523da2012-10-21 16:47:02 -0700365 @Override
366 public void addView(View child, int index) {
367 enforceKeyguardWidgetFrame(child);
368 super.addView(child, index);
369 }
370
Jim Millerbdca3c02012-10-29 19:11:50 -0700371 /**
372 * Use addWidget() instead.
373 * @deprecated
374 */
Jim Millerd6523da2012-10-21 16:47:02 -0700375 @Override
376 public void addView(View child, int width, int height) {
377 enforceKeyguardWidgetFrame(child);
378 super.addView(child, width, height);
379 }
380
Jim Millerbdca3c02012-10-29 19:11:50 -0700381 /**
382 * Use addWidget() instead.
383 * @deprecated
384 */
Jim Millerd6523da2012-10-21 16:47:02 -0700385 @Override
386 public void addView(View child, LayoutParams params) {
387 enforceKeyguardWidgetFrame(child);
388 super.addView(child, params);
389 }
390
Jim Millerbdca3c02012-10-29 19:11:50 -0700391 /**
392 * Use addWidget() instead.
393 * @deprecated
394 */
Jim Millerd6523da2012-10-21 16:47:02 -0700395 @Override
396 public void addView(View child, int index, LayoutParams params) {
397 enforceKeyguardWidgetFrame(child);
398 super.addView(child, index, params);
399 }
400
401 private void enforceKeyguardWidgetFrame(View child) {
402 if (!(child instanceof KeyguardWidgetFrame)) {
403 throw new IllegalArgumentException(
404 "KeyguardWidgetPager children must be KeyguardWidgetFrames");
405 }
406 }
407
408 public KeyguardWidgetFrame getWidgetPageAt(int index) {
409 // This is always a valid cast as we've guarded the ability to
410 return (KeyguardWidgetFrame) getChildAt(index);
Jim Millerf2dfc352012-08-29 18:42:21 -0700411 }
412
Jim Millerc1aa6a52012-09-28 19:37:26 -0700413 protected void onUnhandledTap(MotionEvent ev) {
Jim Miller838906b2012-10-19 18:41:25 -0700414 showPagingFeedback();
Jim Millerc1aa6a52012-09-28 19:37:26 -0700415 }
416
Jim Millereeadda52012-10-02 17:32:38 -0700417 @Override
418 protected void onPageBeginMoving() {
Jim Millerd6523da2012-10-21 16:47:02 -0700419 if (mViewStateManager != null) {
420 mViewStateManager.onPageBeginMoving();
421 }
Adam Cohen70009e42012-10-30 16:48:22 -0700422 if (!isReordering(false)) {
423 showOutlinesAndSidePages();
424 }
Chris Wren32c557c2012-10-29 11:16:42 -0400425 userActivity();
Jim Millereeadda52012-10-02 17:32:38 -0700426 }
427
428 @Override
429 protected void onPageEndMoving() {
Jim Millerd6523da2012-10-21 16:47:02 -0700430 if (mViewStateManager != null) {
431 mViewStateManager.onPageEndMoving();
432 }
Adam Cohen70009e42012-10-30 16:48:22 -0700433
434 // In the reordering case, the pages will be faded appropriately on completion
435 // of the zoom in animation.
436 if (!isReordering(false)) {
437 hideOutlinesAndSidePages();
438 }
Jim Millereeadda52012-10-02 17:32:38 -0700439 }
440
Adam Cohen5d47a8d2012-11-03 19:10:56 -0700441 protected void enablePageContentLayers() {
Adam Cohenf9048cd2012-10-27 16:36:10 -0700442 int children = getChildCount();
443 for (int i = 0; i < children; i++) {
444 getWidgetPageAt(i).enableHardwareLayersForContent();
445 }
446 }
447
Adam Cohen5d47a8d2012-11-03 19:10:56 -0700448 protected void disablePageContentLayers() {
Adam Cohenf9048cd2012-10-27 16:36:10 -0700449 int children = getChildCount();
450 for (int i = 0; i < children; i++) {
451 getWidgetPageAt(i).disableHardwareLayersForContent();
452 }
453 }
454
Jim Millerdcb3d842012-08-23 19:18:12 -0700455 /*
456 * This interpolator emulates the rate at which the perceived scale of an object changes
457 * as its distance from a camera increases. When this interpolator is applied to a scale
458 * animation on a view, it evokes the sense that the object is shrinking due to moving away
459 * from the camera.
460 */
461 static class ZInterpolator implements TimeInterpolator {
462 private float focalLength;
463
464 public ZInterpolator(float foc) {
465 focalLength = foc;
466 }
467
468 public float getInterpolation(float input) {
469 return (1.0f - focalLength / (focalLength + input)) /
470 (1.0f - focalLength / (focalLength + 1.0f));
471 }
472 }
473
Jim Millerf2dfc352012-08-29 18:42:21 -0700474 @Override
475 protected void overScroll(float amount) {
476 acceleratedOverScroll(amount);
477 }
478
Jim Millerd6523da2012-10-21 16:47:02 -0700479 float backgroundAlphaInterpolator(float r) {
Adam Cohen9ec871d2012-10-24 19:25:44 -0700480 return Math.min(1f, r);
Jim Millerd6523da2012-10-21 16:47:02 -0700481 }
482
483 private void updatePageAlphaValues(int screenCenter) {
Adam Cohenf9048cd2012-10-27 16:36:10 -0700484 }
485
Adam Cohen2b0501b2012-11-21 16:49:13 -0800486 public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
487 if (showSidePages) {
488 return 1f;
489 } else {
490 return index == mCurrentPage ? 1.0f : 0f;
491 }
Adam Cohenf9048cd2012-10-27 16:36:10 -0700492 }
493
Adam Cohen2b0501b2012-11-21 16:49:13 -0800494 public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) {
495 if (showSidePages) {
496 return getAlphaForPage(screenCenter, index, showSidePages)
497 * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER;
498 } else {
499 return 0f;
500 }
Adam Powellcfc30862012-10-29 18:21:31 -0700501 }
502
Adam Cohenf9048cd2012-10-27 16:36:10 -0700503 protected boolean isOverScrollChild(int index, float scrollProgress) {
Jim Millerd6523da2012-10-21 16:47:02 -0700504 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
Adam Cohenf9048cd2012-10-27 16:36:10 -0700505 return (isInOverscroll && (index == 0 && scrollProgress < 0 ||
506 index == getChildCount() - 1 && scrollProgress > 0));
Jim Millerd6523da2012-10-21 16:47:02 -0700507 }
508
Jim Millerdcb3d842012-08-23 19:18:12 -0700509 @Override
510 protected void screenScrolled(int screenCenter) {
Adam Cohenf9048cd2012-10-27 16:36:10 -0700511 mScreenCenter = screenCenter;
Jim Millerd6523da2012-10-21 16:47:02 -0700512 updatePageAlphaValues(screenCenter);
Jim Millerdcb3d842012-08-23 19:18:12 -0700513 for (int i = 0; i < getChildCount(); i++) {
Jim Millerd6523da2012-10-21 16:47:02 -0700514 KeyguardWidgetFrame v = getWidgetPageAt(i);
515 if (v == mDragView) continue;
Jim Millerdcb3d842012-08-23 19:18:12 -0700516 if (v != null) {
517 float scrollProgress = getScrollProgress(screenCenter, v, i);
Jim Millerd6523da2012-10-21 16:47:02 -0700518
Jim Millerdcb3d842012-08-23 19:18:12 -0700519 v.setCameraDistance(mDensity * CAMERA_DISTANCE);
Jim Millerdcb3d842012-08-23 19:18:12 -0700520
Adam Cohenf9048cd2012-10-27 16:36:10 -0700521 if (isOverScrollChild(i, scrollProgress) && PERFORM_OVERSCROLL_ROTATION) {
Adam Cohend51700b32012-11-07 16:26:46 -0800522 float pivotX = v.getMeasuredWidth() / 2;
523 float pivotY = v.getMeasuredHeight() / 2;
524 v.setPivotX(pivotX);
525 v.setPivotY(pivotY);
Adam Cohenf9048cd2012-10-27 16:36:10 -0700526 v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress);
527 v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0);
528 } else {
529 v.setRotationY(0f);
530 v.setOverScrollAmount(0, false);
Jim Millerdcb3d842012-08-23 19:18:12 -0700531 }
Jim Millerdcb3d842012-08-23 19:18:12 -0700532
Adam Cohenf9048cd2012-10-27 16:36:10 -0700533 float alpha = v.getAlpha();
Jim Millerdcb3d842012-08-23 19:18:12 -0700534 // If the view has 0 alpha, we set it to be invisible so as to prevent
535 // it from accepting touches
536 if (alpha == 0) {
537 v.setVisibility(INVISIBLE);
538 } else if (v.getVisibility() != VISIBLE) {
539 v.setVisibility(VISIBLE);
540 }
541 }
542 }
543 }
Adam Cohenf9048cd2012-10-27 16:36:10 -0700544
Jim Miller51117262012-11-04 17:58:09 -0800545 public boolean isWidgetPage(int pageIndex) {
546 if (pageIndex < 0 || pageIndex >= getChildCount()) {
547 return false;
548 }
549 View v = getChildAt(pageIndex);
550 if (v != null && v instanceof KeyguardWidgetFrame) {
551 KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v;
552 return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID;
553 }
554 return false;
555 }
556
Winson Chungc065a5d2012-11-07 17:17:33 -0800557 /**
558 * Returns the bounded set of pages that are re-orderable. The range is fully inclusive.
559 */
Jim Millerd6523da2012-10-21 16:47:02 -0700560 @Override
Winson Chungefc49252012-10-26 15:41:27 -0700561 void boundByReorderablePages(boolean isReordering, int[] range) {
562 if (isReordering) {
Jim Miller51117262012-11-04 17:58:09 -0800563 // Remove non-widget pages from the range
Winson Chungc065a5d2012-11-07 17:17:33 -0800564 while (range[1] >= range[0] && !isWidgetPage(range[1])) {
Jim Miller51117262012-11-04 17:58:09 -0800565 range[1]--;
566 }
Winson Chungc065a5d2012-11-07 17:17:33 -0800567 while (range[0] <= range[1] && !isWidgetPage(range[0])) {
Winson Chungefc49252012-10-26 15:41:27 -0700568 range[0]++;
569 }
Winson Chungefc49252012-10-26 15:41:27 -0700570 }
571 }
572
Adam Cohen70009e42012-10-30 16:48:22 -0700573 protected void reorderStarting() {
574 showOutlinesAndSidePages();
575 }
576
Winson Chungefc49252012-10-26 15:41:27 -0700577 @Override
Jim Millerd6523da2012-10-21 16:47:02 -0700578 protected void onStartReordering() {
579 super.onStartReordering();
Adam Cohen5d47a8d2012-11-03 19:10:56 -0700580 enablePageContentLayers();
Adam Cohen70009e42012-10-30 16:48:22 -0700581 reorderStarting();
Jim Millerd6523da2012-10-21 16:47:02 -0700582 }
583
584 @Override
585 protected void onEndReordering() {
586 super.onEndReordering();
Adam Cohen6fb841f2012-10-24 13:15:38 -0700587 hideOutlinesAndSidePages();
Jim Millerd6523da2012-10-21 16:47:02 -0700588 }
589
Adam Cohen6fb841f2012-10-24 13:15:38 -0700590 void showOutlinesAndSidePages() {
Adam Cohenf9048cd2012-10-27 16:36:10 -0700591 animateOutlinesAndSidePages(true);
592 }
Adam Cohen6fb841f2012-10-24 13:15:38 -0700593
Adam Cohenf9048cd2012-10-27 16:36:10 -0700594 void hideOutlinesAndSidePages() {
595 animateOutlinesAndSidePages(false);
Jim Millerd6523da2012-10-21 16:47:02 -0700596 }
597
Jim Millere5fb5e42013-04-10 16:10:06 -0700598 void updateChildrenContentAlpha(float sidePageAlpha) {
Adam Cohen45adaa72012-11-02 22:00:57 -0700599 int count = getChildCount();
600 for (int i = 0; i < count; i++) {
601 KeyguardWidgetFrame child = getWidgetPageAt(i);
602 if (i != mCurrentPage) {
Jim Millere5fb5e42013-04-10 16:10:06 -0700603 child.setBackgroundAlpha(sidePageAlpha);
Adam Cohen45adaa72012-11-02 22:00:57 -0700604 child.setContentAlpha(0f);
605 } else {
606 child.setBackgroundAlpha(0f);
607 child.setContentAlpha(1f);
608 }
Adam Cohen8a7785c2012-10-29 22:01:33 -0700609 }
610 }
611
Jim Millere5fb5e42013-04-10 16:10:06 -0700612 public void showInitialPageHints() {
613 mShowingInitialHints = true;
614 updateChildrenContentAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
615 }
616
Adam Cohen8a7785c2012-10-29 22:01:33 -0700617 @Override
Svetoslav Ganova90a18d2012-11-06 15:22:11 -0800618 void setCurrentPage(int currentPage) {
619 super.setCurrentPage(currentPage);
Jim Millere5fb5e42013-04-10 16:10:06 -0700620 updateChildrenContentAlpha(0.0f);
Svetoslav Ganova90a18d2012-11-06 15:22:11 -0800621 updateWidgetFramesImportantForAccessibility();
622 }
623
624 @Override
Adam Cohen8a7785c2012-10-29 22:01:33 -0700625 public void onAttachedToWindow() {
626 super.onAttachedToWindow();
627 mHasMeasure = false;
Adam Cohen8a7785c2012-10-29 22:01:33 -0700628 }
629
Adam Cohen8a7785c2012-10-29 22:01:33 -0700630 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Winson Chung4752e7d2012-11-20 17:06:04 -0800631 mLastWidthMeasureSpec = widthMeasureSpec;
632 mLastHeightMeasureSpec = heightMeasureSpec;
633
Adam Cohen8a7785c2012-10-29 22:01:33 -0700634 int maxChallengeTop = -1;
635 View parent = (View) getParent();
636 boolean challengeShowing = false;
637 // Widget pages need to know where the top of the sliding challenge is so that they
638 // now how big the widget should be when the challenge is up. We compute it here and
639 // then propagate it to each of our children.
640 if (parent.getParent() instanceof SlidingChallengeLayout) {
641 SlidingChallengeLayout scl = (SlidingChallengeLayout) parent.getParent();
642 int top = scl.getMaxChallengeTop();
643
644 // This is a bit evil, but we need to map a coordinate relative to the SCL into a
645 // coordinate relative to our children, hence we subtract the top padding.s
646 maxChallengeTop = top - getPaddingTop();
647 challengeShowing = scl.isChallengeShowing();
Adam Cohen8a7785c2012-10-29 22:01:33 -0700648
Adam Cohen4ddcd572012-11-01 17:36:32 -0700649 int count = getChildCount();
650 for (int i = 0; i < count; i++) {
651 KeyguardWidgetFrame frame = getWidgetPageAt(i);
652 frame.setMaxChallengeTop(maxChallengeTop);
653 // On the very first measure pass, if the challenge is showing, we need to make sure
654 // that the widget on the current page is small.
655 if (challengeShowing && i == mCurrentPage && !mHasMeasure) {
656 frame.shrinkWidget();
657 }
Adam Cohen8a7785c2012-10-29 22:01:33 -0700658 }
659 }
660 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Adam Cohen4ddcd572012-11-01 17:36:32 -0700661 mHasMeasure = true;
Adam Cohen6fb841f2012-10-24 13:15:38 -0700662 }
663
Adam Cohenf9048cd2012-10-27 16:36:10 -0700664 void animateOutlinesAndSidePages(final boolean show) {
Adam Cohen70009e42012-10-30 16:48:22 -0700665 animateOutlinesAndSidePages(show, -1);
666 }
667
Adam Cohen8caabad2012-11-04 17:23:25 -0800668 public void setWidgetToResetOnPageFadeOut(int widget) {
Adam Cohendb1c5d52012-11-03 17:10:07 -0700669 mWidgetToResetAfterFadeOut = widget;
670 }
671
Adam Cohen8caabad2012-11-04 17:23:25 -0800672 public int getWidgetToResetOnPageFadeOut() {
673 return mWidgetToResetAfterFadeOut;
674 }
675
Adam Cohen70009e42012-10-30 16:48:22 -0700676 void animateOutlinesAndSidePages(final boolean show, int duration) {
Adam Cohenf9048cd2012-10-27 16:36:10 -0700677 if (mChildrenOutlineFadeAnimation != null) {
678 mChildrenOutlineFadeAnimation.cancel();
679 mChildrenOutlineFadeAnimation = null;
680 }
Adam Cohenf9048cd2012-10-27 16:36:10 -0700681 int count = getChildCount();
682 PropertyValuesHolder alpha;
Adam Cohenf9048cd2012-10-27 16:36:10 -0700683 ArrayList<Animator> anims = new ArrayList<Animator>();
Adam Cohen6fb841f2012-10-24 13:15:38 -0700684
Adam Cohen70009e42012-10-30 16:48:22 -0700685 if (duration == -1) {
686 duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION :
687 CHILDREN_OUTLINE_FADE_OUT_DURATION;
688 }
Adam Cohene3643132012-10-28 18:29:17 -0700689
Adam Cohenf9048cd2012-10-27 16:36:10 -0700690 int curPage = getNextPage();
691 for (int i = 0; i < count; i++) {
692 float finalContentAlpha;
693 if (show) {
Adam Cohen2b0501b2012-11-21 16:49:13 -0800694 finalContentAlpha = getAlphaForPage(mScreenCenter, i, true);
Adam Cohenf9048cd2012-10-27 16:36:10 -0700695 } else if (!show && i == curPage) {
696 finalContentAlpha = 1f;
697 } else {
698 finalContentAlpha = 0f;
699 }
Adam Cohenf9048cd2012-10-27 16:36:10 -0700700 KeyguardWidgetFrame child = getWidgetPageAt(i);
Adam Cohen45adaa72012-11-02 22:00:57 -0700701
Adam Cohenf9048cd2012-10-27 16:36:10 -0700702 alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalContentAlpha);
Adam Cohene3643132012-10-28 18:29:17 -0700703 ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha);
Adam Cohenf9048cd2012-10-27 16:36:10 -0700704 anims.add(a);
Adam Cohene3643132012-10-28 18:29:17 -0700705
Adam Cohen2b0501b2012-11-21 16:49:13 -0800706 float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i, true) : 0f;
Adam Cohene3643132012-10-28 18:29:17 -0700707 child.fadeFrame(this, show, finalOutlineAlpha, duration);
Adam Cohenf9048cd2012-10-27 16:36:10 -0700708 }
709
Adam Cohenf9048cd2012-10-27 16:36:10 -0700710 mChildrenOutlineFadeAnimation = new AnimatorSet();
711 mChildrenOutlineFadeAnimation.playTogether(anims);
712
713 mChildrenOutlineFadeAnimation.setDuration(duration);
714 mChildrenOutlineFadeAnimation.addListener(new AnimatorListenerAdapter() {
715 @Override
Adam Cohen70009e42012-10-30 16:48:22 -0700716 public void onAnimationStart(Animator animation) {
717 if (show) {
Adam Cohen5d47a8d2012-11-03 19:10:56 -0700718 enablePageContentLayers();
Adam Cohen70009e42012-10-30 16:48:22 -0700719 }
720 }
Adam Cohendb1c5d52012-11-03 17:10:07 -0700721
Adam Cohen70009e42012-10-30 16:48:22 -0700722 @Override
Adam Cohenf9048cd2012-10-27 16:36:10 -0700723 public void onAnimationEnd(Animator animation) {
724 if (!show) {
Adam Cohen5d47a8d2012-11-03 19:10:56 -0700725 disablePageContentLayers();
Adam Cohen8caabad2012-11-04 17:23:25 -0800726 KeyguardWidgetFrame frame = getWidgetPageAt(mWidgetToResetAfterFadeOut);
727 if (frame != null && !(frame == getWidgetPageAt(mCurrentPage) &&
728 mViewStateManager.isChallengeOverlapping())) {
729 frame.resetSize();
Adam Cohendb1c5d52012-11-03 17:10:07 -0700730 }
Adam Cohen8caabad2012-11-04 17:23:25 -0800731 mWidgetToResetAfterFadeOut = -1;
Adam Cohen2b0501b2012-11-21 16:49:13 -0800732 mShowingInitialHints = false;
Adam Cohenf9048cd2012-10-27 16:36:10 -0700733 }
Svetoslav Ganova90a18d2012-11-06 15:22:11 -0800734 updateWidgetFramesImportantForAccessibility();
Adam Cohenf9048cd2012-10-27 16:36:10 -0700735 }
736 });
737 mChildrenOutlineFadeAnimation.start();
Jim Millerd6523da2012-10-21 16:47:02 -0700738 }
739
Jim Millerd6523da2012-10-21 16:47:02 -0700740 @Override
741 public boolean onLongClick(View v) {
Winson Chung9dc99232012-10-29 17:43:18 -0700742 // Disallow long pressing to reorder if the challenge is showing
Winson Chung70aa5282012-10-30 10:56:37 -0700743 boolean isChallengeOverlapping = mViewStateManager.isChallengeShowing() &&
744 mViewStateManager.isChallengeOverlapping();
745 if (!isChallengeOverlapping && startReordering()) {
Jim Miller19a52672012-10-23 19:52:04 -0700746 return true;
747 }
748 return false;
Jim Millerd6523da2012-10-21 16:47:02 -0700749 }
Jim Millerbdca3c02012-10-29 19:11:50 -0700750
751 public void removeWidget(View view) {
752 if (view instanceof KeyguardWidgetFrame) {
753 removeView(view);
754 } else {
755 // Assume view was wrapped by a KeyguardWidgetFrame in KeyguardWidgetPager#addWidget().
756 // This supports legacy hard-coded "widgets" like KeyguardTransportControlView.
757 int pos = getWidgetPageIndex(view);
758 if (pos != -1) {
759 KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(pos);
760 frame.removeView(view);
761 removeView(frame);
762 } else {
763 Slog.w(TAG, "removeWidget() can't find:" + view);
764 }
765 }
766 }
767
768 public int getWidgetPageIndex(View view) {
769 if (view instanceof KeyguardWidgetFrame) {
770 return indexOfChild(view);
771 } else {
772 // View was wrapped by a KeyguardWidgetFrame by KeyguardWidgetPager#addWidget()
773 return indexOfChild((KeyguardWidgetFrame)view.getParent());
774 }
775 }
Winson Chungf3b9ec82012-11-01 14:48:51 -0700776
777 @Override
778 protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {
779 KeyguardWidgetFrame child = getWidgetPageAt(viewIndex);
780 child.setIsHoveringOverDeleteDropTarget(isHovering);
781 }
Winson Chung48275d22012-11-05 10:56:31 -0800782
783 // ChallengeLayout.OnBouncerStateChangedListener
784 @Override
785 public void onBouncerStateChanged(boolean bouncerActive) {
786 if (bouncerActive) {
787 zoomOutToBouncer();
788 } else {
789 zoomInFromBouncer();
790 }
791 }
792
Winson Chung70c2f872012-11-07 21:47:12 -0800793 void setBouncerAnimationDuration(int duration) {
794 mBouncerZoomInOutDuration = duration;
795 }
796
Winson Chung48275d22012-11-05 10:56:31 -0800797 // Zoom in after the bouncer is dismissed
798 void zoomInFromBouncer() {
799 if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
800 mZoomInOutAnim.cancel();
801 }
802 final View currentPage = getPageAt(getCurrentPage());
803 if (currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f) {
804 mZoomInOutAnim = new AnimatorSet();
Winson Chung48275d22012-11-05 10:56:31 -0800805 mZoomInOutAnim.playTogether(
806 ObjectAnimator.ofFloat(currentPage, "scaleX", 1f),
807 ObjectAnimator.ofFloat(currentPage , "scaleY", 1f));
Winson Chung70c2f872012-11-07 21:47:12 -0800808 mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
809 mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
Winson Chung48275d22012-11-05 10:56:31 -0800810 mZoomInOutAnim.start();
811 }
John Spurlock0552c5d2012-11-15 08:04:45 -0500812 if (currentPage instanceof KeyguardWidgetFrame) {
813 ((KeyguardWidgetFrame)currentPage).onBouncerShowing(false);
814 }
Winson Chung48275d22012-11-05 10:56:31 -0800815 }
816
817 // Zoom out after the bouncer is initiated
818 void zoomOutToBouncer() {
819 if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
820 mZoomInOutAnim.cancel();
821 }
Winson Chung70c2f872012-11-07 21:47:12 -0800822 int curPage = getCurrentPage();
823 View currentPage = getPageAt(curPage);
824 if (shouldSetTopAlignedPivotForWidget(curPage)) {
825 currentPage.setPivotY(0);
826 // Note: we are working around the issue that setting the x-pivot to the same value as it
827 // was does not actually work.
828 currentPage.setPivotX(0);
829 currentPage.setPivotX(currentPage.getMeasuredWidth() / 2);
830 }
Winson Chung48275d22012-11-05 10:56:31 -0800831 if (!(currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f)) {
832 mZoomInOutAnim = new AnimatorSet();
Winson Chung48275d22012-11-05 10:56:31 -0800833 mZoomInOutAnim.playTogether(
834 ObjectAnimator.ofFloat(currentPage, "scaleX", BOUNCER_SCALE_FACTOR),
835 ObjectAnimator.ofFloat(currentPage, "scaleY", BOUNCER_SCALE_FACTOR));
Winson Chung70c2f872012-11-07 21:47:12 -0800836 mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
837 mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
Winson Chung48275d22012-11-05 10:56:31 -0800838 mZoomInOutAnim.start();
839 }
John Spurlock0552c5d2012-11-15 08:04:45 -0500840 if (currentPage instanceof KeyguardWidgetFrame) {
841 ((KeyguardWidgetFrame)currentPage).onBouncerShowing(true);
842 }
Winson Chung48275d22012-11-05 10:56:31 -0800843 }
Winson Chung6cf53bb2012-11-05 17:55:42 -0800844
Winson Chung4752e7d2012-11-20 17:06:04 -0800845 void setAddWidgetEnabled(boolean enabled) {
846 if (mAddWidgetView != null && enabled) {
847 addView(mAddWidgetView, 0);
848 // We need to force measure the PagedView so that the calls to update the scroll
849 // position below work
850 measure(mLastWidthMeasureSpec, mLastHeightMeasureSpec);
851 // Bump up the current page to account for the addition of the new page
852 setCurrentPage(mCurrentPage + 1);
853 mAddWidgetView = null;
854 } else if (mAddWidgetView == null && !enabled) {
Jim Miller5ecd8112013-01-09 18:50:26 -0800855 View addWidget = findViewById(R.id.keyguard_add_widget);
Winson Chung4752e7d2012-11-20 17:06:04 -0800856 if (addWidget != null) {
857 mAddWidgetView = addWidget;
858 removeView(addWidget);
859 }
860 }
861 }
862
Winson Chung6cf53bb2012-11-05 17:55:42 -0800863 boolean isAddPage(int pageIndex) {
864 View v = getChildAt(pageIndex);
Jim Miller5ecd8112013-01-09 18:50:26 -0800865 return v != null && v.getId() == R.id.keyguard_add_widget;
Winson Chung6cf53bb2012-11-05 17:55:42 -0800866 }
867
868 boolean isCameraPage(int pageIndex) {
869 View v = getChildAt(pageIndex);
870 return v != null && v instanceof CameraWidgetFrame;
871 }
872
873 @Override
874 protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
875 return !isCameraPage(childIndex) && super.shouldSetTopAlignedPivotForWidget(childIndex);
876 }
Jeff Sharkey06c5f8a2012-12-04 09:53:44 -0800877
878 /**
879 * Search given {@link View} hierarchy for {@link TextClock} instances that
880 * show various time components. Returns combination of
881 * {@link #FLAG_HAS_LOCAL_HOUR} and {@link #FLAG_HAS_LOCAL_MINUTE}.
882 */
883 private static int findClockInHierarchy(View view) {
884 if (view instanceof TextClock) {
885 return getClockFlags((TextClock) view);
886 } else if (view instanceof ViewGroup) {
887 int flags = 0;
888 final ViewGroup group = (ViewGroup) view;
889 final int size = group.getChildCount();
890 for (int i = 0; i < size; i++) {
891 flags |= findClockInHierarchy(group.getChildAt(i));
892 }
893 return flags;
894 } else {
895 return 0;
896 }
897 }
898
899 /**
900 * Return combination of {@link #FLAG_HAS_LOCAL_HOUR} and
901 * {@link #FLAG_HAS_LOCAL_MINUTE} describing the time represented described
902 * by the given {@link TextClock}.
903 */
904 private static int getClockFlags(TextClock clock) {
905 int flags = 0;
906
907 final String timeZone = clock.getTimeZone();
908 if (timeZone != null && !TimeZone.getDefault().equals(TimeZone.getTimeZone(timeZone))) {
909 // Ignore clocks showing another timezone
910 return 0;
911 }
912
913 final CharSequence format = clock.getFormat();
914 final char hour = clock.is24HourModeEnabled() ? DateFormat.HOUR_OF_DAY
915 : DateFormat.HOUR;
916
917 if (DateFormat.hasDesignator(format, hour)) {
918 flags |= FLAG_HAS_LOCAL_HOUR;
919 }
920 if (DateFormat.hasDesignator(format, DateFormat.MINUTE)) {
921 flags |= FLAG_HAS_LOCAL_MINUTE;
922 }
923
924 return flags;
925 }
Jim Millerdcb3d842012-08-23 19:18:12 -0700926}