blob: 770fafcfa390a9034f6f795e30f9aafbdd3ace59 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.policy.impl.keyguard;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.Slog;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.TextClock;
import com.android.internal.widget.LockPatternUtils;
import java.util.ArrayList;
import java.util.TimeZone;
public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener,
OnLongClickListener, ChallengeLayout.OnBouncerStateChangedListener {
ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
private static float CAMERA_DISTANCE = 10000;
protected static float OVERSCROLL_MAX_ROTATION = 30;
private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
private static final int FLAG_HAS_LOCAL_HOUR = 0x1;
private static final int FLAG_HAS_LOCAL_MINUTE = 0x2;
protected KeyguardViewStateManager mViewStateManager;
private LockPatternUtils mLockPatternUtils;
// Related to the fading in / out background outlines
public static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
public static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
protected AnimatorSet mChildrenOutlineFadeAnimation;
protected int mScreenCenter;
private boolean mHasMeasure = false;
boolean showHintsAfterLayout = false;
private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
private static final String TAG = "KeyguardWidgetPager";
private boolean mCenterSmallWidgetsVertically;
private int mPage = 0;
private Callbacks mCallbacks;
private int mWidgetToResetAfterFadeOut;
protected boolean mShowingInitialHints = false;
// A temporary handle to the Add-Widget view
private View mAddWidgetView;
private int mLastWidthMeasureSpec;
private int mLastHeightMeasureSpec;
// Bouncer
private int mBouncerZoomInOutDuration = 250;
private float BOUNCER_SCALE_FACTOR = 0.67f;
// Background worker thread: used here for persistence, also made available to widget frames
private final HandlerThread mBackgroundWorkerThread;
private final Handler mBackgroundWorkerHandler;
public KeyguardWidgetPager(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public KeyguardWidgetPager(Context context) {
this(null, null, 0);
}
public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
setPageSwitchListener(this);
mBackgroundWorkerThread = new HandlerThread("KeyguardWidgetPager Worker");
mBackgroundWorkerThread.start();
mBackgroundWorkerHandler = new Handler(mBackgroundWorkerThread.getLooper());
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// Clean up the worker thread
mBackgroundWorkerThread.quit();
}
public void setViewStateManager(KeyguardViewStateManager viewStateManager) {
mViewStateManager = viewStateManager;
}
public void setLockPatternUtils(LockPatternUtils l) {
mLockPatternUtils = l;
}
@Override
public void onPageSwitching(View newPage, int newPageIndex) {
if (mViewStateManager != null) {
mViewStateManager.onPageSwitching(newPage, newPageIndex);
}
}
@Override
public void onPageSwitched(View newPage, int newPageIndex) {
boolean showingClock = false;
if (newPage instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) newPage;
if (vg.getChildAt(0) instanceof KeyguardStatusView) {
showingClock = true;
}
}
if (newPage != null &&
findClockInHierarchy(newPage) == (FLAG_HAS_LOCAL_HOUR | FLAG_HAS_LOCAL_MINUTE)) {
showingClock = true;
}
// Disable the status bar clock if we're showing the default status widget
if (showingClock) {
setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
} else {
setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
}
// Extend the display timeout if the user switches pages
if (mPage != newPageIndex) {
int oldPageIndex = mPage;
mPage = newPageIndex;
userActivity();
KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex);
if (oldWidgetPage != null) {
oldWidgetPage.onActive(false);
}
KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex);
if (newWidgetPage != null) {
newWidgetPage.onActive(true);
newWidgetPage.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
newWidgetPage.requestAccessibilityFocus();
}
if (mParent != null && AccessibilityManager.getInstance(mContext).isEnabled()) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_SCROLLED);
onInitializeAccessibilityEvent(event);
onPopulateAccessibilityEvent(event);
mParent.requestSendAccessibilityEvent(this, event);
}
}
if (mViewStateManager != null) {
mViewStateManager.onPageSwitched(newPage, newPageIndex);
}
}
@Override
public void sendAccessibilityEvent(int eventType) {
if (eventType != AccessibilityEvent.TYPE_VIEW_SCROLLED || isPageMoving()) {
super.sendAccessibilityEvent(eventType);
}
}
private void updateWidgetFramesImportantForAccessibility() {
final int pageCount = getPageCount();
for (int i = 0; i < pageCount; i++) {
KeyguardWidgetFrame frame = getWidgetPageAt(i);
updateWidgetFrameImportantForAccessibility(frame);
}
}
private void updateWidgetFrameImportantForAccessibility(KeyguardWidgetFrame frame) {
if (frame.getContentAlpha() <= 0) {
frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
} else {
frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
}
private void userActivity() {
if (mCallbacks != null) {
mCallbacks.onUserActivityTimeoutChanged();
mCallbacks.userActivity();
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return captureUserInteraction(ev) || super.onTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return captureUserInteraction(ev) || super.onInterceptTouchEvent(ev);
}
private boolean captureUserInteraction(MotionEvent ev) {
KeyguardWidgetFrame currentWidgetPage = getWidgetPageAt(getCurrentPage());
return currentWidgetPage != null && currentWidgetPage.onUserInteraction(ev);
}
public void showPagingFeedback() {
// Nothing yet.
}
public long getUserActivityTimeout() {
View page = getPageAt(mPage);
if (page instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) page;
View view = vg.getChildAt(0);
if (!(view instanceof KeyguardStatusView)
&& !(view instanceof KeyguardMultiUserSelectorView)) {
return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT;
}
}
return -1;
}
public void setCallbacks(Callbacks callbacks) {
mCallbacks = callbacks;
}
public interface Callbacks {
public void userActivity();
public void onUserActivityTimeoutChanged();
public void onAddView(View v);
public void onRemoveView(View v, boolean deletePermanently);
public void onRemoveViewAnimationCompleted();
}
public void addWidget(View widget) {
addWidget(widget, -1);
}
public void onRemoveView(View v, final boolean deletePermanently) {
final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
if (mCallbacks != null) {
mCallbacks.onRemoveView(v, deletePermanently);
}
mBackgroundWorkerHandler.post(new Runnable() {
@Override
public void run() {
mLockPatternUtils.removeAppWidget(appWidgetId);
}
});
}
@Override
public void onRemoveViewAnimationCompleted() {
if (mCallbacks != null) {
mCallbacks.onRemoveViewAnimationCompleted();
}
}
public void onAddView(View v, final int index) {
final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
final int[] pagesRange = new int[mTempVisiblePagesRange.length];
getVisiblePages(pagesRange);
boundByReorderablePages(true, pagesRange);
if (mCallbacks != null) {
mCallbacks.onAddView(v);
}
// Subtract from the index to take into account pages before the reorderable
// pages (e.g. the "add widget" page)
mBackgroundWorkerHandler.post(new Runnable() {
@Override
public void run() {
mLockPatternUtils.addAppWidget(appWidgetId, index - pagesRange[0]);
}
});
}
/*
* We wrap widgets in a special frame which handles drawing the over scroll foreground.
*/
public void addWidget(View widget, int pageIndex) {
KeyguardWidgetFrame frame;
// All views contained herein should be wrapped in a KeyguardWidgetFrame
if (!(widget instanceof KeyguardWidgetFrame)) {
frame = new KeyguardWidgetFrame(getContext());
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
lp.gravity = Gravity.TOP;
// The framework adds a default padding to AppWidgetHostView. We don't need this padding
// for the Keyguard, so we override it to be 0.
widget.setPadding(0, 0, 0, 0);
frame.addView(widget, lp);
// We set whether or not this widget supports vertical resizing.
if (widget instanceof AppWidgetHostView) {
AppWidgetHostView awhv = (AppWidgetHostView) widget;
AppWidgetProviderInfo info = awhv.getAppWidgetInfo();
if ((info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
frame.setWidgetLockedSmall(false);
} else {
// Lock the widget to be small.
frame.setWidgetLockedSmall(true);
if (mCenterSmallWidgetsVertically) {
lp.gravity = Gravity.CENTER;
}
}
}
} else {
frame = (KeyguardWidgetFrame) widget;
}
ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
frame.setOnLongClickListener(this);
frame.setWorkerHandler(mBackgroundWorkerHandler);
if (pageIndex == -1) {
addView(frame, pageLp);
} else {
addView(frame, pageIndex, pageLp);
}
// Update the frame content description.
View content = (widget == frame) ? frame.getContent() : widget;
if (content != null) {
String contentDescription = mContext.getString(
com.android.internal.R.string.keyguard_accessibility_widget,
content.getContentDescription());
frame.setContentDescription(contentDescription);
}
updateWidgetFrameImportantForAccessibility(frame);
}
/**
* Use addWidget() instead.
* @deprecated
*/
@Override
public void addView(View child, int index) {
enforceKeyguardWidgetFrame(child);
super.addView(child, index);
}
/**
* Use addWidget() instead.
* @deprecated
*/
@Override
public void addView(View child, int width, int height) {
enforceKeyguardWidgetFrame(child);
super.addView(child, width, height);
}
/**
* Use addWidget() instead.
* @deprecated
*/
@Override
public void addView(View child, LayoutParams params) {
enforceKeyguardWidgetFrame(child);
super.addView(child, params);
}
/**
* Use addWidget() instead.
* @deprecated
*/
@Override
public void addView(View child, int index, LayoutParams params) {
enforceKeyguardWidgetFrame(child);
super.addView(child, index, params);
}
private void enforceKeyguardWidgetFrame(View child) {
if (!(child instanceof KeyguardWidgetFrame)) {
throw new IllegalArgumentException(
"KeyguardWidgetPager children must be KeyguardWidgetFrames");
}
}
public KeyguardWidgetFrame getWidgetPageAt(int index) {
// This is always a valid cast as we've guarded the ability to
return (KeyguardWidgetFrame) getChildAt(index);
}
protected void onUnhandledTap(MotionEvent ev) {
showPagingFeedback();
}
@Override
protected void onPageBeginMoving() {
if (mViewStateManager != null) {
mViewStateManager.onPageBeginMoving();
}
if (!isReordering(false)) {
showOutlinesAndSidePages();
}
userActivity();
}
@Override
protected void onPageEndMoving() {
if (mViewStateManager != null) {
mViewStateManager.onPageEndMoving();
}
// In the reordering case, the pages will be faded appropriately on completion
// of the zoom in animation.
if (!isReordering(false)) {
hideOutlinesAndSidePages();
}
}
protected void enablePageContentLayers() {
int children = getChildCount();
for (int i = 0; i < children; i++) {
getWidgetPageAt(i).enableHardwareLayersForContent();
}
}
protected void disablePageContentLayers() {
int children = getChildCount();
for (int i = 0; i < children; i++) {
getWidgetPageAt(i).disableHardwareLayersForContent();
}
}
/*
* This interpolator emulates the rate at which the perceived scale of an object changes
* as its distance from a camera increases. When this interpolator is applied to a scale
* animation on a view, it evokes the sense that the object is shrinking due to moving away
* from the camera.
*/
static class ZInterpolator implements TimeInterpolator {
private float focalLength;
public ZInterpolator(float foc) {
focalLength = foc;
}
public float getInterpolation(float input) {
return (1.0f - focalLength / (focalLength + input)) /
(1.0f - focalLength / (focalLength + 1.0f));
}
}
@Override
protected void overScroll(float amount) {
acceleratedOverScroll(amount);
}
float backgroundAlphaInterpolator(float r) {
return Math.min(1f, r);
}
private void updatePageAlphaValues(int screenCenter) {
}
public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
if (showSidePages) {
return 1f;
} else {
return index == mCurrentPage ? 1.0f : 0f;
}
}
public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) {
if (showSidePages) {
return getAlphaForPage(screenCenter, index, showSidePages)
* KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER;
} else {
return 0f;
}
}
protected boolean isOverScrollChild(int index, float scrollProgress) {
boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
return (isInOverscroll && (index == 0 && scrollProgress < 0 ||
index == getChildCount() - 1 && scrollProgress > 0));
}
@Override
protected void screenScrolled(int screenCenter) {
mScreenCenter = screenCenter;
updatePageAlphaValues(screenCenter);
for (int i = 0; i < getChildCount(); i++) {
KeyguardWidgetFrame v = getWidgetPageAt(i);
if (v == mDragView) continue;
if (v != null) {
float scrollProgress = getScrollProgress(screenCenter, v, i);
v.setCameraDistance(mDensity * CAMERA_DISTANCE);
if (isOverScrollChild(i, scrollProgress) && PERFORM_OVERSCROLL_ROTATION) {
float pivotX = v.getMeasuredWidth() / 2;
float pivotY = v.getMeasuredHeight() / 2;
v.setPivotX(pivotX);
v.setPivotY(pivotY);
v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress);
v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0);
} else {
v.setRotationY(0f);
v.setOverScrollAmount(0, false);
}
float alpha = v.getAlpha();
// If the view has 0 alpha, we set it to be invisible so as to prevent
// it from accepting touches
if (alpha == 0) {
v.setVisibility(INVISIBLE);
} else if (v.getVisibility() != VISIBLE) {
v.setVisibility(VISIBLE);
}
}
}
}
public boolean isWidgetPage(int pageIndex) {
if (pageIndex < 0 || pageIndex >= getChildCount()) {
return false;
}
View v = getChildAt(pageIndex);
if (v != null && v instanceof KeyguardWidgetFrame) {
KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v;
return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID;
}
return false;
}
/**
* Returns the bounded set of pages that are re-orderable. The range is fully inclusive.
*/
@Override
void boundByReorderablePages(boolean isReordering, int[] range) {
if (isReordering) {
// Remove non-widget pages from the range
while (range[1] >= range[0] && !isWidgetPage(range[1])) {
range[1]--;
}
while (range[0] <= range[1] && !isWidgetPage(range[0])) {
range[0]++;
}
}
}
protected void reorderStarting() {
showOutlinesAndSidePages();
}
@Override
protected void onStartReordering() {
super.onStartReordering();
enablePageContentLayers();
reorderStarting();
}
@Override
protected void onEndReordering() {
super.onEndReordering();
hideOutlinesAndSidePages();
}
void showOutlinesAndSidePages() {
animateOutlinesAndSidePages(true);
}
void hideOutlinesAndSidePages() {
animateOutlinesAndSidePages(false);
}
void updateChildrenContentAlpha(float sidePageAlpha) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
KeyguardWidgetFrame child = getWidgetPageAt(i);
if (i != mCurrentPage) {
child.setBackgroundAlpha(sidePageAlpha);
child.setContentAlpha(0f);
} else {
child.setBackgroundAlpha(0f);
child.setContentAlpha(1f);
}
}
}
public void showInitialPageHints() {
mShowingInitialHints = true;
updateChildrenContentAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
}
@Override
void setCurrentPage(int currentPage) {
super.setCurrentPage(currentPage);
updateChildrenContentAlpha(0.0f);
updateWidgetFramesImportantForAccessibility();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mHasMeasure = false;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mLastWidthMeasureSpec = widthMeasureSpec;
mLastHeightMeasureSpec = heightMeasureSpec;
int maxChallengeTop = -1;
View parent = (View) getParent();
boolean challengeShowing = false;
// Widget pages need to know where the top of the sliding challenge is so that they
// now how big the widget should be when the challenge is up. We compute it here and
// then propagate it to each of our children.
if (parent.getParent() instanceof SlidingChallengeLayout) {
SlidingChallengeLayout scl = (SlidingChallengeLayout) parent.getParent();
int top = scl.getMaxChallengeTop();
// This is a bit evil, but we need to map a coordinate relative to the SCL into a
// coordinate relative to our children, hence we subtract the top padding.s
maxChallengeTop = top - getPaddingTop();
challengeShowing = scl.isChallengeShowing();
int count = getChildCount();
for (int i = 0; i < count; i++) {
KeyguardWidgetFrame frame = getWidgetPageAt(i);
frame.setMaxChallengeTop(maxChallengeTop);
// On the very first measure pass, if the challenge is showing, we need to make sure
// that the widget on the current page is small.
if (challengeShowing && i == mCurrentPage && !mHasMeasure) {
frame.shrinkWidget();
}
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mHasMeasure = true;
}
void animateOutlinesAndSidePages(final boolean show) {
animateOutlinesAndSidePages(show, -1);
}
public void setWidgetToResetOnPageFadeOut(int widget) {
mWidgetToResetAfterFadeOut = widget;
}
public int getWidgetToResetOnPageFadeOut() {
return mWidgetToResetAfterFadeOut;
}
void animateOutlinesAndSidePages(final boolean show, int duration) {
if (mChildrenOutlineFadeAnimation != null) {
mChildrenOutlineFadeAnimation.cancel();
mChildrenOutlineFadeAnimation = null;
}
int count = getChildCount();
PropertyValuesHolder alpha;
ArrayList<Animator> anims = new ArrayList<Animator>();
if (duration == -1) {
duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION :
CHILDREN_OUTLINE_FADE_OUT_DURATION;
}
int curPage = getNextPage();
for (int i = 0; i < count; i++) {
float finalContentAlpha;
if (show) {
finalContentAlpha = getAlphaForPage(mScreenCenter, i, true);
} else if (!show && i == curPage) {
finalContentAlpha = 1f;
} else {
finalContentAlpha = 0f;
}
KeyguardWidgetFrame child = getWidgetPageAt(i);
alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalContentAlpha);
ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha);
anims.add(a);
float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i, true) : 0f;
child.fadeFrame(this, show, finalOutlineAlpha, duration);
}
mChildrenOutlineFadeAnimation = new AnimatorSet();
mChildrenOutlineFadeAnimation.playTogether(anims);
mChildrenOutlineFadeAnimation.setDuration(duration);
mChildrenOutlineFadeAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
if (show) {
enablePageContentLayers();
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (!show) {
disablePageContentLayers();
KeyguardWidgetFrame frame = getWidgetPageAt(mWidgetToResetAfterFadeOut);
if (frame != null && !(frame == getWidgetPageAt(mCurrentPage) &&
mViewStateManager.isChallengeOverlapping())) {
frame.resetSize();
}
mWidgetToResetAfterFadeOut = -1;
mShowingInitialHints = false;
}
updateWidgetFramesImportantForAccessibility();
}
});
mChildrenOutlineFadeAnimation.start();
}
@Override
public boolean onLongClick(View v) {
// Disallow long pressing to reorder if the challenge is showing
boolean isChallengeOverlapping = mViewStateManager.isChallengeShowing() &&
mViewStateManager.isChallengeOverlapping();
if (!isChallengeOverlapping && startReordering()) {
return true;
}
return false;
}
public void removeWidget(View view) {
if (view instanceof KeyguardWidgetFrame) {
removeView(view);
} else {
// Assume view was wrapped by a KeyguardWidgetFrame in KeyguardWidgetPager#addWidget().
// This supports legacy hard-coded "widgets" like KeyguardTransportControlView.
int pos = getWidgetPageIndex(view);
if (pos != -1) {
KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(pos);
frame.removeView(view);
removeView(frame);
} else {
Slog.w(TAG, "removeWidget() can't find:" + view);
}
}
}
public int getWidgetPageIndex(View view) {
if (view instanceof KeyguardWidgetFrame) {
return indexOfChild(view);
} else {
// View was wrapped by a KeyguardWidgetFrame by KeyguardWidgetPager#addWidget()
return indexOfChild((KeyguardWidgetFrame)view.getParent());
}
}
@Override
protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {
KeyguardWidgetFrame child = getWidgetPageAt(viewIndex);
child.setIsHoveringOverDeleteDropTarget(isHovering);
}
// ChallengeLayout.OnBouncerStateChangedListener
@Override
public void onBouncerStateChanged(boolean bouncerActive) {
if (bouncerActive) {
zoomOutToBouncer();
} else {
zoomInFromBouncer();
}
}
void setBouncerAnimationDuration(int duration) {
mBouncerZoomInOutDuration = duration;
}
// Zoom in after the bouncer is dismissed
void zoomInFromBouncer() {
if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
mZoomInOutAnim.cancel();
}
final View currentPage = getPageAt(getCurrentPage());
if (currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f) {
mZoomInOutAnim = new AnimatorSet();
mZoomInOutAnim.playTogether(
ObjectAnimator.ofFloat(currentPage, "scaleX", 1f),
ObjectAnimator.ofFloat(currentPage , "scaleY", 1f));
mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
mZoomInOutAnim.start();
}
if (currentPage instanceof KeyguardWidgetFrame) {
((KeyguardWidgetFrame)currentPage).onBouncerShowing(false);
}
}
// Zoom out after the bouncer is initiated
void zoomOutToBouncer() {
if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
mZoomInOutAnim.cancel();
}
int curPage = getCurrentPage();
View currentPage = getPageAt(curPage);
if (shouldSetTopAlignedPivotForWidget(curPage)) {
currentPage.setPivotY(0);
// Note: we are working around the issue that setting the x-pivot to the same value as it
// was does not actually work.
currentPage.setPivotX(0);
currentPage.setPivotX(currentPage.getMeasuredWidth() / 2);
}
if (!(currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f)) {
mZoomInOutAnim = new AnimatorSet();
mZoomInOutAnim.playTogether(
ObjectAnimator.ofFloat(currentPage, "scaleX", BOUNCER_SCALE_FACTOR),
ObjectAnimator.ofFloat(currentPage, "scaleY", BOUNCER_SCALE_FACTOR));
mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
mZoomInOutAnim.start();
}
if (currentPage instanceof KeyguardWidgetFrame) {
((KeyguardWidgetFrame)currentPage).onBouncerShowing(true);
}
}
void setAddWidgetEnabled(boolean enabled) {
if (mAddWidgetView != null && enabled) {
addView(mAddWidgetView, 0);
// We need to force measure the PagedView so that the calls to update the scroll
// position below work
measure(mLastWidthMeasureSpec, mLastHeightMeasureSpec);
// Bump up the current page to account for the addition of the new page
setCurrentPage(mCurrentPage + 1);
mAddWidgetView = null;
} else if (mAddWidgetView == null && !enabled) {
View addWidget = findViewById(com.android.internal.R.id.keyguard_add_widget);
if (addWidget != null) {
mAddWidgetView = addWidget;
removeView(addWidget);
}
}
}
boolean isAddPage(int pageIndex) {
View v = getChildAt(pageIndex);
return v != null && v.getId() == com.android.internal.R.id.keyguard_add_widget;
}
boolean isCameraPage(int pageIndex) {
View v = getChildAt(pageIndex);
return v != null && v instanceof CameraWidgetFrame;
}
@Override
protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
return !isCameraPage(childIndex) && super.shouldSetTopAlignedPivotForWidget(childIndex);
}
/**
* Search given {@link View} hierarchy for {@link TextClock} instances that
* show various time components. Returns combination of
* {@link #FLAG_HAS_LOCAL_HOUR} and {@link #FLAG_HAS_LOCAL_MINUTE}.
*/
private static int findClockInHierarchy(View view) {
if (view instanceof TextClock) {
return getClockFlags((TextClock) view);
} else if (view instanceof ViewGroup) {
int flags = 0;
final ViewGroup group = (ViewGroup) view;
final int size = group.getChildCount();
for (int i = 0; i < size; i++) {
flags |= findClockInHierarchy(group.getChildAt(i));
}
return flags;
} else {
return 0;
}
}
/**
* Return combination of {@link #FLAG_HAS_LOCAL_HOUR} and
* {@link #FLAG_HAS_LOCAL_MINUTE} describing the time represented described
* by the given {@link TextClock}.
*/
private static int getClockFlags(TextClock clock) {
int flags = 0;
final String timeZone = clock.getTimeZone();
if (timeZone != null && !TimeZone.getDefault().equals(TimeZone.getTimeZone(timeZone))) {
// Ignore clocks showing another timezone
return 0;
}
final CharSequence format = clock.getFormat();
final char hour = clock.is24HourModeEnabled() ? DateFormat.HOUR_OF_DAY
: DateFormat.HOUR;
if (DateFormat.hasDesignator(format, hour)) {
flags |= FLAG_HAS_LOCAL_HOUR;
}
if (DateFormat.hasDesignator(format, DateFormat.MINUTE)) {
flags |= FLAG_HAS_LOCAL_MINUTE;
}
return flags;
}
}