diff options
author | Nebojsa Cvetkovic <nebkat@gmail.com> | 2013-12-15 19:46:56 +0000 |
---|---|---|
committer | Danesh M <daneshm90@gmail.com> | 2014-01-24 16:25:19 -0800 |
commit | 7fc14e45774e3094ef3afdf391a13ad54df62a3e (patch) | |
tree | a50303eadf4dab290f99ef0a3c52446da2271827 /src/com/android/launcher3/AppsCustomizePagedView.java | |
parent | 04d59a5a3bda757080acf34a974474317e7a6ab3 (diff) | |
download | android_packages_apps_Trebuchet-7fc14e45774e3094ef3afdf391a13ad54df62a3e.tar.gz android_packages_apps_Trebuchet-7fc14e45774e3094ef3afdf391a13ad54df62a3e.tar.bz2 android_packages_apps_Trebuchet-7fc14e45774e3094ef3afdf391a13ad54df62a3e.zip |
AppsCustomize: Overview panel
Change-Id: I5cfd1183194ee15d8bb18dcd2536a644c9599978
Diffstat (limited to 'src/com/android/launcher3/AppsCustomizePagedView.java')
-rw-r--r-- | src/com/android/launcher3/AppsCustomizePagedView.java | 451 |
1 files changed, 436 insertions, 15 deletions
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index ace83f77e..34dd9294d 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -16,7 +16,10 @@ package com.android.launcher3; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; @@ -36,13 +39,16 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Process; +import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.GridLayout; @@ -53,6 +59,7 @@ import com.android.launcher3.DropTarget.DragObject; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; @@ -157,6 +164,16 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } private ContentType mContentType = ContentType.Applications; + /** + * The different sort modes than can be used to order items. + */ + public enum SortMode { + Title, + LaunchCount, + InstallTime + } + private SortMode mSortMode = SortMode.Title; + // Refs private Launcher mLauncher; private DragController mDragController; @@ -188,12 +205,33 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private int mNumAppsPages; private int mNumWidgetPages; + // Animation states + enum State { NORMAL, OVERVIEW}; + private State mState = State.NORMAL; + private boolean mIsSwitchingState = false; + + // Animation values + private float mNewScale; + private float[] mOldBackgroundAlphas; + private float[] mOldTranslationXs; + private float[] mOldTranslationYs; + private float[] mOldScaleXs; + private float[] mOldScaleYs; + private float[] mOldAlphas; + private float[] mNewBackgroundAlphas; + private float[] mNewTranslationXs; + private float[] mNewTranslationYs; + private float[] mNewScaleXs; + private float[] mNewScaleYs; + private float[] mNewAlphas; + // Relating to the scroll and overscroll effects Workspace.ZInterpolator mZInterpolator = new Workspace.ZInterpolator(0.5f); private static float CAMERA_DISTANCE = 6500; private static float TRANSITION_SCALE_FACTOR = 0.74f; private static float TRANSITION_PIVOT = 0.65f; private static float TRANSITION_MAX_ROTATION = 22; + private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; private static final boolean PERFORM_OVERSCROLL_ROTATION = true; private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f); private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4); @@ -226,6 +264,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private Rect mTmpRect = new Rect(); + private float mOverviewModeShrinkFactor; + private int mOverviewModePageOffset; + // Used for drawing shortcut previews BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache(); PaintCache mCachedShortcutPreviewPaint = new PaintCache(); @@ -242,6 +283,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private boolean mInBulkBind; private boolean mNeedToUpdatePageCountsAndInvalidateData; + private static boolean sAccessibilityEnabled; + public AppsCustomizePagedView(Context context, AttributeSet attrs) { super(context, attrs); mLayoutInflater = LayoutInflater.from(context); @@ -282,6 +325,11 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen Context context = getContext(); Resources r = context.getResources(); setDragSlopeThreshold(r.getInteger(R.integer.config_appsCustomizeDragSlopeThreshold)/100f); + mOverviewModeShrinkFactor = + r.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100.0f; + mOverviewModePageOffset = r.getDimensionPixelSize(R.dimen.overview_mode_page_offset); + + setMinScale(mOverviewModeShrinkFactor - 0.2f); } public void onFinishInflate() { @@ -527,10 +575,22 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } + /** + * Called directly from a CellLayout (not by the framework), after we've been added as a + * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout + * that it should intercept touch events, which is not something that is normally supported. + */ + @Override + public boolean onTouch(View v, MotionEvent event) { + return isInOverviewMode() || mIsSwitchingState; + } + public boolean onKey(View v, int keyCode, KeyEvent event) { return FocusHelper.handleAppsCustomizeKeyEvent(v, keyCode, event); } + private final Workspace.ZoomInInterpolator mZoomInInterpolator = new Workspace.ZoomInInterpolator(); + /* * PagedViewWithDraggableItems implementation */ @@ -903,6 +963,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen cancelAllTasks(); } + protected void onResume() { + AccessibilityManager am = (AccessibilityManager) + getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); + sAccessibilityEnabled = am.isEnabled(); + } + public void clearAllWidgetPages() { cancelAllTasks(); int count = getChildCount(); @@ -976,6 +1042,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen private void setupPage(AppsCustomizeCellLayout layout) { layout.setGridSize(mCellCountX, mCellCountY); + layout.setOnClickListener(mLauncher); + // Note: We force a measure here to get around the fact that when we do layout calculations // immediately after syncing, we don't have a proper width. That said, we already know the // expected page width, so we can actually optimize by hiding all the TextView-based @@ -988,6 +1056,18 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen setVisibilityOnChildren(layout, View.VISIBLE); } + @Override + public void onChildViewAdded(View parent, View child) { + // For overview mode + if (child instanceof CellLayout) { + CellLayout cl = ((CellLayout) child); + cl.setOnInterceptTouchListener(this); + cl.setClickable(true); + cl.setImportantForAccessibility(ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO); + } + super.onChildViewAdded(parent, child); + } + public void syncAppsPageItems(int page, boolean immediate) { // ensure that we have the right number of items on the pages final boolean isRtl = isLayoutRtl(); @@ -1354,6 +1434,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen final boolean isRtl = isLayoutRtl(); super.screenScrolled(screenCenter); + enableHwLayersOnVisiblePages(); + + if (isInOverviewMode()) { + return; + } + for (int i = 0; i < getChildCount(); i++) { View v = getPageAt(i); if (v != null) { @@ -1419,7 +1505,11 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen v.setTranslationX(translationX); v.setScaleX(scale); v.setScaleY(scale); - v.setAlpha(alpha); + if (v instanceof CellLayout) { + ((CellLayout) v).getShortcutsAndWidgets().setAlpha(alpha); + } else { + v.setAlpha(alpha); + } // If the view has 0 alpha, we set it to be invisible so as to prevent // it from accepting touches @@ -1430,8 +1520,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } } - - enableHwLayersOnVisiblePages(); } private void enableHwLayersOnVisiblePages() { @@ -1456,26 +1544,316 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen for (int i = 0; i < screenCount; i++) { final View layout = (View) getPageAt(i); - if (!(leftScreen <= i && i <= rightScreen && - (i == forceDrawScreen || shouldDrawChild(layout)))) { + boolean enableLayer = leftScreen <= i && i <= rightScreen && + (i == forceDrawScreen || shouldDrawChild(layout)); + if (layout instanceof CellLayout) { + ((CellLayout) layout).enableHardwareLayer(enableLayer); + } else if (enableLayer) { + if (layout.getLayerType() != LAYER_TYPE_HARDWARE) { + layout.setLayerType(LAYER_TYPE_HARDWARE, null); + } + } else { layout.setLayerType(LAYER_TYPE_NONE, null); } } + } - for (int i = 0; i < screenCount; i++) { - final View layout = (View) getPageAt(i); + protected void overScroll(float amount) { + acceleratedOverScroll(amount); + } - if (leftScreen <= i && i <= rightScreen && - (i == forceDrawScreen || shouldDrawChild(layout))) { - if (layout.getLayerType() != LAYER_TYPE_HARDWARE) { - layout.setLayerType(LAYER_TYPE_HARDWARE, null); + public boolean isInOverviewMode() { + return mState == State.OVERVIEW; + } + + public boolean enterOverviewMode() { + if (mTouchState != TOUCH_STATE_REST || !mIsDataReady || mContentType != ContentType.Applications) { + return false; + } + enableOverviewMode(true, -1, true); + return true; + } + + public void exitOverviewMode(boolean animated) { + exitOverviewMode(-1, animated); + } + + public void exitOverviewMode(int snapPage, boolean animated) { + enableOverviewMode(false, snapPage, animated); + } + + private void enableOverviewMode(boolean enable, int snapPage, boolean animated) { + State finalState = AppsCustomizePagedView.State.OVERVIEW; + if (!enable) { + finalState = AppsCustomizePagedView.State.NORMAL; + } + + mLauncher.updateOverviewPanel(); + + Animator appsCustomizeAnim = getChangeStateAnimation(finalState, animated, 0, snapPage); + if (appsCustomizeAnim != null) { + onTransitionPrepare(); + appsCustomizeAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator arg0) { + onTransitionEnd(); + } + }); + appsCustomizeAnim.start(); + } + } + + int getOverviewModeTranslationY() { + int childHeight = getNormalChildHeight(); + int viewPortHeight = getViewportHeight(); + int scaledChildHeight = (int) (mOverviewModeShrinkFactor * childHeight); + + int offset = (viewPortHeight - scaledChildHeight) / 2; + int offsetDelta = mOverviewModePageOffset - offset + mInsets.top; + + return offsetDelta; + } + + private void setState(State state) { + mState = state; + updateAccessibilityFlags(); + } + + private void updateAccessibilityFlags() { + int accessible = mState == State.NORMAL ? + ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES : + ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; + setImportantForAccessibility(accessible); + } + + private void initAnimationArrays() { + final int childCount = getChildCount(); + if (mOldBackgroundAlphas != null) return; + mOldBackgroundAlphas = new float[childCount]; + mOldTranslationXs = new float[childCount]; + mOldTranslationYs = new float[childCount]; + mOldScaleXs = new float[childCount]; + mOldScaleYs = new float[childCount]; + mOldAlphas = new float[childCount]; + mNewBackgroundAlphas = new float[childCount]; + mNewTranslationXs = new float[childCount]; + mNewTranslationYs = new float[childCount]; + mNewScaleXs = new float[childCount]; + mNewScaleYs = new float[childCount]; + mNewAlphas = new float[childCount]; + } + + Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) { + if (mState == state) { + return null; + } + + // Initialize animation arrays for the first time if necessary + initAnimationArrays(); + + AnimatorSet anim = animated ? LauncherAnimUtils.createAnimatorSet() : null; + + final State oldState = mState; + final boolean oldStateIsNormal = (oldState == State.NORMAL); + final boolean oldStateIsOverview = (oldState == State.OVERVIEW); + setState(state); + final boolean stateIsNormal = (state == State.NORMAL); + final boolean stateIsOverview = (state == State.OVERVIEW); + float finalBackgroundAlpha = stateIsOverview ? 1.0f : 0f; + float finalPageIndicatorAlpha = stateIsOverview ? 0f : 1f; + float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f; + float finalWorkspaceTranslationY = stateIsOverview ? getOverviewModeTranslationY() : 0; + + boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview); + boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal); + + mNewScale = 1.0f; + + if (state != State.NORMAL) { + if (stateIsOverview) { + mNewScale = mOverviewModeShrinkFactor; + } + } + + final int duration = getResources().getInteger(R.integer.config_overviewTransitionTime); + + for (int i = 0; i < getChildCount(); i++) { + final CellLayout cl = (CellLayout) getChildAt(i); + float translationX = 0f; + float translationY = 0f; + float scaleX = 1f; + float scaleY = 1f; + float finalAlpha = 1f; + + if (stateIsOverview) { + cl.setVisibility(VISIBLE); + cl.setTranslationX(0f); + cl.setScaleX(1f); + cl.setScaleY(1f); + cl.setShortcutAndWidgetAlpha(1f); + } + + mOldAlphas[i] = cl.getShortcutsAndWidgets().getAlpha(); + mNewAlphas[i] = finalAlpha; + if (animated) { + mOldBackgroundAlphas[i] = cl.getBackgroundAlpha(); + mOldTranslationXs[i] = cl.getTranslationX(); + mOldTranslationYs[i] = cl.getTranslationY(); + mOldScaleXs[i] = cl.getScaleX(); + mNewScaleXs[i] = cl.getScaleY(); + + mNewBackgroundAlphas[i] = finalBackgroundAlpha; + mNewTranslationXs[i] = translationX; + mNewTranslationYs[i] = translationY; + mNewScaleXs[i] = scaleX; + mNewScaleYs[i] = scaleY; + } else { + cl.setBackgroundAlpha(finalBackgroundAlpha); + cl.setTranslationX(translationX); + cl.setTranslationY(translationY); + cl.setScaleX(scaleX); + cl.setScaleY(scaleY); + cl.setShortcutAndWidgetAlpha(finalAlpha); + } + } + + final View overviewPanel = mLauncher.getOverviewPanel(); + if (animated) { + anim.setDuration(duration); + LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this); + scale.scaleX(mNewScale) + .scaleY(mNewScale) + .translationY(finalWorkspaceTranslationY) + .setInterpolator(mZoomInInterpolator); + anim.play(scale); + ValueAnimator invalidate = ValueAnimator.ofFloat(0f, 1f); + invalidate.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + invalidate(); + } + }); + anim.play(invalidate); + ObjectAnimator pageIndicatorAlpha = null; + if (getPageIndicator() != null) { + pageIndicatorAlpha = ObjectAnimator.ofFloat(getPageIndicator(), "alpha", + finalPageIndicatorAlpha); + } + ObjectAnimator overviewPanelAlpha = ObjectAnimator.ofFloat(overviewPanel, + "alpha", finalOverviewPanelAlpha); + + overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel)); + + if (overviewToWorkspace) { + overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); + } + + if (getPageIndicator() != null) { + pageIndicatorAlpha.addListener(new AlphaUpdateListener(getPageIndicator())); + } + + anim.play(overviewPanelAlpha); + anim.play(pageIndicatorAlpha); + + for (int index = 0; index < getChildCount(); index++) { + final int i = index; + + final CellLayout cl = (CellLayout) getChildAt(i); + if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) { + cl.setBackgroundAlpha(mNewBackgroundAlphas[i]); + cl.setTranslationX(mNewTranslationXs[i]); + cl.setTranslationY(mNewTranslationYs[i]); + cl.setScaleX(mNewScaleXs[i]); + cl.setScaleY(mNewScaleYs[i]); + cl.getShortcutsAndWidgets().setAlpha(mNewAlphas[i]); + } else { + LauncherViewPropertyAnimator a = new LauncherViewPropertyAnimator(cl); + a.translationX(mNewTranslationXs[i]) + .translationY(mNewTranslationYs[i]) + .scaleX(mNewScaleXs[i]) + .scaleY(mNewScaleYs[i]) + .setDuration(duration) + .setInterpolator(mZoomInInterpolator); + anim.play(a); + a = new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets()); + a.translationX(mNewTranslationXs[i]) + .alpha(mNewAlphas[i]) + .setDuration(duration) + .setInterpolator(mZoomInInterpolator); + anim.play(a); + if (mOldBackgroundAlphas[i] != 0 || + mNewBackgroundAlphas[i] != 0) { + ValueAnimator bgAnim = + LauncherAnimUtils.ofFloat(cl, 0f, 1f); + bgAnim.setInterpolator(mZoomInInterpolator); + bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() { + public void onAnimationUpdate(float a, float b) { + cl.setBackgroundAlpha( + a * mOldBackgroundAlphas[i] + + b * mNewBackgroundAlphas[i]); + } + }); + anim.play(bgAnim); + } } } + + anim.setStartDelay(delay); + } else { + overviewPanel.setAlpha(finalOverviewPanelAlpha); + AlphaUpdateListener.updateVisibility(overviewPanel); + if (getPageIndicator() != null) { + getPageIndicator().setAlpha(finalPageIndicatorAlpha); + AlphaUpdateListener.updateVisibility(getPageIndicator()); + } + setScaleX(mNewScale); + setScaleY(mNewScale); + setTranslationY(finalWorkspaceTranslationY); } + return anim; } - protected void overScroll(float amount) { - acceleratedOverScroll(amount); + static class AlphaUpdateListener implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { + View view; + public AlphaUpdateListener(View v) { + view = v; + } + + @Override + public void onAnimationUpdate(ValueAnimator arg0) { + updateVisibility(view); + } + + public static void updateVisibility(View view) { + // We want to avoid the extra layout pass by setting the views to GONE unless + // accessibility is on, in which case not setting them to GONE causes a glitch. + int invisibleState = sAccessibilityEnabled ? GONE : INVISIBLE; + if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { + view.setVisibility(invisibleState); + } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD + && view.getVisibility() != VISIBLE) { + view.setVisibility(VISIBLE); + } + } + + @Override + public void onAnimationCancel(Animator arg0) { + } + + @Override + public void onAnimationEnd(Animator arg0) { + updateVisibility(view); + } + + @Override + public void onAnimationRepeat(Animator arg0) { + } + + @Override + public void onAnimationStart(Animator arg0) { + // We want the views to be visible for animation, so fade-in/out is visible + view.setVisibility(VISIBLE); + } } /** @@ -1495,6 +1873,49 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mSaveInstanceStateItemIndex = -1; } + private void onTransitionPrepare() { + mIsSwitchingState = true; + + // Invalidate here to ensure that the pages are rendered during the state change transition. + invalidate(); + + enableHwLayersOnVisiblePages(); + } + + private void onTransitionEnd() { + mIsSwitchingState = false; + } + + public Comparator<AppInfo> getComparatorForSortMode() { + switch (mSortMode) { + case Title: + return LauncherModel.getAppNameComparator(); + case LaunchCount: + return LauncherModel.getAppLaunchCountComparator(mLauncher.getStats()); + case InstallTime: + return LauncherModel.getAppInstallTimeComparator(); + } + return LauncherModel.getAppNameComparator(); + } + + public void setSortMode(SortMode sortMode) { + if (mSortMode == sortMode) return; + + mSortMode = sortMode; + + Collections.sort(mApps, getComparatorForSortMode()); + + if (mContentType == ContentType.Applications) { + for (int i = 0; i < getChildCount(); i++) { + syncAppsPageItems(i, true); + } + } + } + + public SortMode getSortMode() { + return mSortMode; + } + /* * AllAppsView implementation */ @@ -1523,7 +1944,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen public void setApps(ArrayList<AppInfo> list) { if (!DISABLE_ALL_APPS) { mApps = list; - Collections.sort(mApps, LauncherModel.getAppNameComparator()); + Collections.sort(mApps, getComparatorForSortMode()); updatePageCountsAndInvalidateData(); } } @@ -1532,7 +1953,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int count = list.size(); for (int i = 0; i < count; ++i) { AppInfo info = list.get(i); - int index = Collections.binarySearch(mApps, info, LauncherModel.getAppNameComparator()); + int index = Collections.binarySearch(mApps, info, getComparatorForSortMode()); if (index < 0) { mApps.add(-(index + 1), info); } |