diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2018-03-14 17:51:49 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2018-03-19 20:02:34 -0700 |
commit | 7185dd63eb8942dec65c2babeb39ee6ec64b4533 (patch) | |
tree | bec53b0f31970f001716a95876de2306d3cc9f1a /src/com/android/launcher3 | |
parent | 9d69c8da9a4f933cc700ef1672b4e60d34a2fb10 (diff) | |
download | android_packages_apps_Trebuchet-7185dd63eb8942dec65c2babeb39ee6ec64b4533.tar.gz android_packages_apps_Trebuchet-7185dd63eb8942dec65c2babeb39ee6ec64b4533.tar.bz2 android_packages_apps_Trebuchet-7185dd63eb8942dec65c2babeb39ee6ec64b4533.zip |
Changing the overviewState to show appsearch and floating header
Change-Id: I2cfd61cfc9978e4c8e4520f0f7217e49e7344c79
Diffstat (limited to 'src/com/android/launcher3')
15 files changed, 436 insertions, 275 deletions
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index fc61155d3..8ae307306 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -419,7 +419,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } - private void setTextAlpha(int alpha) { + public void setTextAlpha(int alpha) { super.setTextColor(ColorUtils.setAlphaComponent(mTextColor, alpha)); } diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index a3fe89a5f..dec6cb452 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -16,10 +16,10 @@ package com.android.launcher3; -import static com.android.launcher3.AlphaUpdateListener.updateVisibility; import static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT; import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT; import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT; +import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility; import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; import android.animation.TimeInterpolator; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ee6dd591c..82be592b2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -364,6 +364,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, L getRootView().dispatchInsets(); getStateManager().reapplyState(); + // Recreate touch controllers + mDragLayer.setup(mDragController); + // TODO: We can probably avoid rebind when only screen size changed. rebindModel(); } @@ -956,7 +959,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, L mDragController.setMoveTarget(mWorkspace); mDropTargetBar.setup(mDragController); - mAllAppsController.setupViews(mAppsView, mHotseat); + mAllAppsController.setupViews(mAppsView); } /** @@ -1258,7 +1261,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, L // Reset the apps view if (!alreadyOnHome && mAppsView != null) { - mAppsView.reset(); + mAppsView.reset(isStarted() /* animate */); } if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()) { diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java index fc4de2d32..b1273b64c 100644 --- a/src/com/android/launcher3/LauncherRootView.java +++ b/src/com/android/launcher3/LauncherRootView.java @@ -29,6 +29,7 @@ public class LauncherRootView extends InsettableFrameLayout { private int mRightInsetBarWidth; private View mAlignedView; + private WindowStateListener mWindowStateListener; public LauncherRootView(Context context, AttributeSet attrs) { super(context, attrs); @@ -117,4 +118,31 @@ public class LauncherRootView extends InsettableFrameLayout { } } } + + public void setWindowStateListener(WindowStateListener listener) { + mWindowStateListener = listener; + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + if (mWindowStateListener != null) { + mWindowStateListener.onWindowFocusChanged(hasWindowFocus); + } + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + if (mWindowStateListener != null) { + mWindowStateListener.onWindowVisibilityChanged(visibility); + } + } + + public interface WindowStateListener { + + void onWindowFocusChanged(boolean hasFocus); + + void onWindowVisibilityChanged(int visibility); + } }
\ No newline at end of file diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index e5d8f47c3..9fef64ae1 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -40,6 +40,16 @@ import java.util.Arrays; */ public class LauncherState { + + /** + * Set of elements indicating various workspace elements which change visibility across states + * Note that workspace is not included here as in that case, we animate individual pages + */ + public static final int NONE = 0; + public static final int HOTSEAT = 1 << 0; + public static final int ALL_APPS_HEADER = 1 << 1; + public static final int ALL_APPS_CONTENT = 1 << 2; + protected static final int FLAG_SHOW_SCRIM = 1 << 0; protected static final int FLAG_MULTI_PAGE = 1 << 1; protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 2; @@ -51,7 +61,6 @@ public class LauncherState { protected static final int FLAG_DISABLE_INTERACTION = 1 << 8; protected static final int FLAG_OVERVIEW_UI = 1 << 9; - protected static final PageAlphaProvider DEFAULT_ALPHA_PROVIDER = new PageAlphaProvider(ACCEL_2) { @Override @@ -68,13 +77,13 @@ public class LauncherState { public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE, 0, FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED); - public static final LauncherState ALL_APPS = new AllAppsState(1); - - public static final LauncherState SPRING_LOADED = new SpringLoadedState(2); - - public static final LauncherState OVERVIEW = new OverviewState(3); - - public static final LauncherState FAST_OVERVIEW = new FastOverviewState(4); + /** + * Various Launcher states arranged in the increasing order of UI layers + */ + public static final LauncherState SPRING_LOADED = new SpringLoadedState(1); + public static final LauncherState OVERVIEW = new OverviewState(2); + public static final LauncherState FAST_OVERVIEW = new FastOverviewState(3); + public static final LauncherState ALL_APPS = new AllAppsState(4); public final int ordinal; @@ -161,10 +170,6 @@ public class LauncherState { return new float[] {1, 0, 0}; } - public float getHoseatAlpha(Launcher launcher) { - return 1f; - } - public float getOverviewTranslationX(Launcher launcher) { return launcher.getDragLayer().getMeasuredWidth(); } @@ -179,6 +184,10 @@ public class LauncherState { return launcher.getWorkspace(); } + public int getVisibleElements(Launcher launcher) { + return HOTSEAT; + } + /** * Fraction shift in the vertical translation UI and related properties * diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 950a8ac20..3c4303573 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -17,6 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -28,8 +29,12 @@ import android.view.View; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.anim.PropertySetter; +import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter; import com.android.launcher3.uioverrides.UiFactory; +import java.util.ArrayList; + /** * TODO: figure out what kind of tests we can write for this * @@ -78,6 +83,7 @@ public class LauncherStateManager { private final AnimationConfig mConfig = new AnimationConfig(); private final Handler mUiHandler; private final Launcher mLauncher; + private final ArrayList<StateListener> mListeners = new ArrayList<>(); private StateHandler[] mStateHandlers; private LauncherState mState = NORMAL; @@ -87,8 +93,6 @@ public class LauncherStateManager { private LauncherState mRestState; - private StateListener mStateListener; - public LauncherStateManager(Launcher l) { mUiHandler = new Handler(Looper.getMainLooper()); mLauncher = l; @@ -105,8 +109,12 @@ public class LauncherStateManager { return mStateHandlers; } - public void setStateListener(StateListener stateListener) { - mStateListener = stateListener; + public void addStateListener(StateListener listener) { + mListeners.add(listener); + } + + public void removeStateListener(StateListener listener) { + mListeners.remove(listener); } /** @@ -188,8 +196,9 @@ public class LauncherStateManager { for (StateHandler handler : getStateHandlers()) { handler.setState(state); } - if (mStateListener != null) { - mStateListener.onStateSetImmediately(state); + + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).onStateSetImmediately(state); } onStateTransitionEnd(state); @@ -251,16 +260,16 @@ public class LauncherStateManager { public void onAnimationStart(Animator animation) { // Change the internal state only when the transition actually starts onStateTransitionStart(state); - if (mStateListener != null) { - mStateListener.onStateTransitionStart(state); + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).onStateTransitionStart(state); } } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - if (mStateListener != null) { - mStateListener.onStateTransitionComplete(mState); + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).onStateTransitionComplete(state); } } @@ -376,12 +385,14 @@ public class LauncherStateManager { public static class AnimationConfig extends AnimatorListenerAdapter { public long duration; public boolean userControlled; + private PropertySetter mProperSetter; private AnimatorSet mCurrentAnimation; public void reset() { duration = 0; userControlled = false; + mProperSetter = null; if (mCurrentAnimation != null) { mCurrentAnimation.setDuration(0); @@ -390,6 +401,14 @@ public class LauncherStateManager { } } + public PropertySetter getProperSetter(AnimatorSetBuilder builder) { + if (mProperSetter == null) { + mProperSetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER + : new AnimatedPropertySetter(duration, builder); + } + return mProperSetter; + } + @Override public void onAnimationEnd(Animator animation) { if (mCurrentAnimation == animation) { diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index f6d02482b..63c118125 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -18,6 +18,8 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.LauncherState.HOTSEAT; +import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; import android.animation.Animator; @@ -32,65 +34,14 @@ import com.android.launcher3.LauncherState.PageAlphaProvider; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.graphics.ViewScrim; /** - * A convenience class to update a view's visibility state after an alpha animation. - */ -class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimator.AnimatorUpdateListener { - private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; - - private View mView; - private boolean mAccessibilityEnabled; - private boolean mCanceled = false; - - public AlphaUpdateListener(View v, boolean accessibilityEnabled) { - mView = v; - mAccessibilityEnabled = accessibilityEnabled; - } - - @Override - public void onAnimationUpdate(ValueAnimator arg0) { - updateVisibility(mView, mAccessibilityEnabled); - } - - public static void updateVisibility(View view, boolean accessibilityEnabled) { - // 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 = accessibilityEnabled ? View.GONE : View.INVISIBLE; - if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { - view.setVisibility(invisibleState); - } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD - && view.getVisibility() != View.VISIBLE) { - view.setVisibility(View.VISIBLE); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - mCanceled = true; - } - - @Override - public void onAnimationEnd(Animator arg0) { - if (mCanceled) return; - updateVisibility(mView, mAccessibilityEnabled); - } - - @Override - public void onAnimationStart(Animator arg0) { - // We want the views to be visible for animation, so fade-in/out is visible - mView.setVisibility(View.VISIBLE); - } -} - -/** * Manages the animations between each of the workspace states. */ public class WorkspaceStateTransitionAnimation { - public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter(); - private final Launcher mLauncher; private final Workspace mWorkspace; @@ -107,9 +58,7 @@ public class WorkspaceStateTransitionAnimation { public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder, AnimationConfig config) { - AnimatedPropertySetter propertySetter = - new AnimatedPropertySetter(config.duration, builder); - setWorkspaceProperty(toState, propertySetter); + setWorkspaceProperty(toState, config.getProperSetter(builder)); } public float getFinalScale() { @@ -135,10 +84,12 @@ public class WorkspaceStateTransitionAnimation { propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y, scaleAndTranslation[2], Interpolators.ZOOM_IN); - propertySetter.setViewAlpha(mLauncher.getHotseat(), state.getHoseatAlpha(mLauncher), + int elements = state.getVisibleElements(mLauncher); + float hotseatAlpha = (elements & HOTSEAT) != 0 ? 1 : 0; + propertySetter.setViewAlpha(mLauncher.getHotseat(), hotseatAlpha, pageAlphaProvider.interpolator); propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(), - state.getHoseatAlpha(mLauncher), pageAlphaProvider.interpolator); + hotseatAlpha, pageAlphaProvider.interpolator); // Set scrim propertySetter.setFloat(ViewScrim.get(mWorkspace), ViewScrim.PROGRESS, @@ -162,71 +113,4 @@ public class WorkspaceStateTransitionAnimation { propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA, pageAlpha, pageAlphaProvider.interpolator); } - - public static class PropertySetter { - - public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { - view.setAlpha(alpha); - AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view.getContext())); - } - - public <T> void setFloat(T target, Property<T, Float> property, float value, - TimeInterpolator interpolator) { - property.set(target, value); - } - - public <T> void setInt(T target, Property<T, Integer> property, int value, - TimeInterpolator interpolator) { - property.set(target, value); - } - } - - public static class AnimatedPropertySetter extends PropertySetter { - - private final long mDuration; - private final AnimatorSetBuilder mStateAnimator; - - public AnimatedPropertySetter(long duration, AnimatorSetBuilder builder) { - mDuration = duration; - mStateAnimator = builder; - } - - @Override - public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { - if (view.getAlpha() == alpha) { - return; - } - ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha); - anim.addListener(new AlphaUpdateListener( - view, isAccessibilityEnabled(view.getContext()))); - anim.setDuration(mDuration).setInterpolator(interpolator); - mStateAnimator.play(anim); - } - - @Override - public <T> void setFloat(T target, Property<T, Float> property, float value, - TimeInterpolator interpolator) { - if (property.get(target) == value) { - return; - } - Animator anim = ObjectAnimator.ofFloat(target, property, value); - anim.setDuration(mDuration).setInterpolator(interpolator); - mStateAnimator.play(anim); - } - - @Override - public <T> void setInt(T target, Property<T, Integer> property, int value, - TimeInterpolator interpolator) { - if (property.get(target) == value) { - return; - } - Animator anim = ObjectAnimator.ofInt(target, property, value); - anim.setDuration(mDuration).setInterpolator(interpolator); - mStateAnimator.play(anim); - } - - private TimeInterpolator getFadeInterpolator(float finalAlpha) { - return finalAlpha == 0 ? Interpolators.DEACCEL_2 : null; - } - } }
\ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 39a8df391..8f5fcf532 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -217,14 +217,14 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo /** * Resets the state of AllApps. */ - public void reset() { + public void reset(boolean animate) { for (int i = 0; i < mAH.length; i++) { if (mAH[i].recyclerView != null) { mAH[i].recyclerView.scrollToTop(); } } if (isHeaderVisible()) { - mHeader.reset(); + mHeader.reset(animate); } // Reset the search bar and base recycler view after transitioning home mSearchUiManager.resetSearch(); @@ -360,7 +360,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo public void onTabChanged(int pos) { mHeader.setMainActive(pos == 0); - reset(); + reset(true /* animate */); if (mAH[pos].recyclerView != null) { mAH[pos].recyclerView.bindFastScrollbar(); @@ -383,6 +383,18 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return mHeader; } + public View getSearchView() { + return mSearchContainer; + } + + public View getContentView() { + return mViewPager == null ? getActiveRecyclerView() : mViewPager; + } + + public RecyclerViewFastScroller getScrollBar() { + return getActiveRecyclerView().getScrollbar(); + } + public void setupHeader() { mHeader.setVisibility(View.VISIBLE); mHeader.setup(mAH, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null); diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 13a42f1d3..bf8f531cc 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -1,7 +1,10 @@ package com.android.launcher3.allapps; +import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; +import static com.android.launcher3.LauncherState.ALL_APPS_HEADER; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS; import android.animation.Animator; @@ -13,16 +16,15 @@ import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; -import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.allapps.SearchUiManager.OnScrollRangeChangeListener; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.util.Themes; /** @@ -55,7 +57,6 @@ public class AllAppsTransitionController public static final float PARALLAX_COEFFICIENT = .125f; private AllAppsContainerView mAppsView; - private Hotseat mHotseat; private final Launcher mLauncher; private final boolean mIsDarkTheme; @@ -88,7 +89,6 @@ public class AllAppsTransitionController private void onProgressAnimationStart() { // Initialize values that should not change until #onDragEnd - mHotseat.setVisibility(View.VISIBLE); mAppsView.setVisibility(View.VISIBLE); } @@ -116,14 +116,10 @@ public class AllAppsTransitionController mProgress = progress; float shiftCurrent = progress * mShiftRange; - float workspaceHotseatAlpha = Utilities.boundToRange(progress, 0f, 1f); - float alpha = 1 - workspaceHotseatAlpha; - mAppsView.setTranslationY(shiftCurrent); float hotseatTranslation = -mShiftRange + shiftCurrent; if (!mIsVerticalLayout) { - mAppsView.setAlpha(alpha); mLauncher.getHotseat().setTranslationY(hotseatTranslation); mLauncher.getWorkspace().getPageIndicator().setTranslationY(hotseatTranslation); } @@ -149,6 +145,7 @@ public class AllAppsTransitionController @Override public void setState(LauncherState state) { setProgress(state.getVerticalProgress(mLauncher)); + setAlphas(state, NO_ANIM_PROPERTY_SETTER); onProgressAnimationEnd(); } @@ -161,6 +158,7 @@ public class AllAppsTransitionController AnimatorSetBuilder builder, AnimationConfig config) { float targetProgress = toState.getVerticalProgress(mLauncher); if (Float.compare(mProgress, targetProgress) == 0) { + setAlphas(toState, config.getProperSetter(builder)); // Fail fast onProgressAnimationEnd(); return; @@ -174,6 +172,19 @@ public class AllAppsTransitionController anim.addListener(getProgressAnimatorListener()); builder.play(anim); + + setAlphas(toState, config.getProperSetter(builder)); + } + + private void setAlphas(LauncherState toState, PropertySetter setter) { + int visibleElements = toState.getVisibleElements(mLauncher); + boolean hasHeader = (visibleElements & ALL_APPS_HEADER) != 0; + boolean hasContent = (visibleElements & ALL_APPS_CONTENT) != 0; + + setter.setViewAlpha(mAppsView.getSearchView(), hasHeader ? 1 : 0, LINEAR); + setter.setViewAlpha(mAppsView.getContentView(), hasContent ? 1 : 0, LINEAR); + setter.setViewAlpha(mAppsView.getScrollBar(), hasContent ? 1 : 0, LINEAR); + mAppsView.getFloatingHeaderView().setContentVisibility(hasHeader, hasContent, setter); } public AnimatorListenerAdapter getProgressAnimatorListener() { @@ -190,10 +201,8 @@ public class AllAppsTransitionController }; } - public void setupViews(AllAppsContainerView appsView, Hotseat hotseat) { + public void setupViews(AllAppsContainerView appsView) { mAppsView = appsView; - mHotseat = hotseat; - mHotseat.bringToFront(); mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this); } @@ -210,15 +219,12 @@ public class AllAppsTransitionController private void onProgressAnimationEnd() { if (Float.compare(mProgress, 1f) == 0) { mAppsView.setVisibility(View.INVISIBLE); - mHotseat.setVisibility(View.VISIBLE); - mAppsView.reset(); + mAppsView.reset(false /* animate */); } else if (Float.compare(mProgress, 0f) == 0) { - mHotseat.setVisibility(View.INVISIBLE); mAppsView.setVisibility(View.VISIBLE); mAppsView.onScrollUpEnd(); } else { mAppsView.setVisibility(View.VISIBLE); - mHotseat.setVisibility(View.VISIBLE); } } } diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java index a0dc5a382..461f5b5ba 100644 --- a/src/com/android/launcher3/allapps/FloatingHeaderView.java +++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.anim.Interpolators.LINEAR; + import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Point; @@ -29,6 +31,7 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.launcher3.R; +import com.android.launcher3.anim.PropertySetter; public class FloatingHeaderView extends LinearLayout implements ValueAnimator.AnimatorUpdateListener { @@ -57,7 +60,7 @@ public class FloatingHeaderView extends LinearLayout implements } }; - private ViewGroup mTabLayout; + protected ViewGroup mTabLayout; private AllAppsRecyclerView mMainRV; private AllAppsRecyclerView mWorkRV; private AllAppsRecyclerView mCurrentRV; @@ -65,6 +68,8 @@ public class FloatingHeaderView extends LinearLayout implements private boolean mHeaderCollapsed; private int mSnappedScrolledY; private int mTranslationY; + + private boolean mAllowTouchForwarding; private boolean mForwardToRecyclerView; protected boolean mTabsHidden; @@ -91,7 +96,7 @@ public class FloatingHeaderView extends LinearLayout implements mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView); mParent = (ViewGroup) mMainRV.getParent(); setMainActive(true); - reset(); + reset(false); } private AllAppsRecyclerView setupRV(AllAppsRecyclerView old, AllAppsRecyclerView updated) { @@ -158,12 +163,19 @@ public class FloatingHeaderView extends LinearLayout implements } } - public void reset() { - int translateTo = 0; - mAnimator.setIntValues(mTranslationY, translateTo); - mAnimator.addUpdateListener(this); - mAnimator.setDuration(150); - mAnimator.start(); + public void reset(boolean animate) { + if (mAnimator.isStarted()) { + mAnimator.cancel(); + } + if (animate) { + mAnimator.setIntValues(mTranslationY, 0); + mAnimator.addUpdateListener(this); + mAnimator.setDuration(150); + mAnimator.start(); + } else { + mTranslationY = 0; + apply(); + } mHeaderCollapsed = false; mSnappedScrolledY = -mMaxTranslation; mCurrentRV.scrollToTop(); @@ -181,6 +193,10 @@ public class FloatingHeaderView extends LinearLayout implements @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + if (!mAllowTouchForwarding) { + mForwardToRecyclerView = false; + return super.onInterceptTouchEvent(ev); + } calcOffset(mTempOffset); ev.offsetLocation(mTempOffset.x, mTempOffset.y); mForwardToRecyclerView = mCurrentRV.onInterceptTouchEvent(ev); @@ -208,6 +224,19 @@ public class FloatingHeaderView extends LinearLayout implements p.x = getLeft() - mCurrentRV.getLeft() - mParent.getLeft(); p.y = getTop() - mCurrentRV.getTop() - mParent.getTop(); } + + public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter) { + setter.setViewAlpha(this, hasContent ? 1 : 0, LINEAR); + allowTouchForwarding(hasContent); + } + + protected void allowTouchForwarding(boolean allow) { + mAllowTouchForwarding = allow; + } + + public boolean hasVisibleContent() { + return false; + } } diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java new file mode 100644 index 000000000..04d97a728 --- /dev/null +++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2018 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.launcher3.anim; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.view.View; + +/** + * A convenience class to update a view's visibility state after an alpha animation. + */ +public class AlphaUpdateListener extends AnimatorListenerAdapter implements AnimatorUpdateListener { + private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; + + private View mView; + private boolean mAccessibilityEnabled; + private boolean mCanceled = false; + + public AlphaUpdateListener(View v, boolean accessibilityEnabled) { + mView = v; + mAccessibilityEnabled = accessibilityEnabled; + } + + @Override + public void onAnimationUpdate(ValueAnimator arg0) { + updateVisibility(mView, mAccessibilityEnabled); + } + + public static void updateVisibility(View view, boolean accessibilityEnabled) { + // 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 = accessibilityEnabled ? View.GONE : View.INVISIBLE; + if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) { + view.setVisibility(invisibleState); + } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD + && view.getVisibility() != View.VISIBLE) { + view.setVisibility(View.VISIBLE); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + mCanceled = true; + } + + @Override + public void onAnimationEnd(Animator arg0) { + if (mCanceled) return; + updateVisibility(mView, mAccessibilityEnabled); + } + + @Override + public void onAnimationStart(Animator arg0) { + // We want the views to be visible for animation, so fade-in/out is visible + mView.setVisibility(View.VISIBLE); + } +}
\ No newline at end of file diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java new file mode 100644 index 000000000..51580b1ea --- /dev/null +++ b/src/com/android/launcher3/anim/PropertySetter.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 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.launcher3.anim; + +import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.util.Property; +import android.view.View; + +/** + * Utility class for setting a property with or without animation + */ +public class PropertySetter { + + public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter(); + + public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { + view.setAlpha(alpha); + AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view.getContext())); + } + + public <T> void setFloat(T target, Property<T, Float> property, float value, + TimeInterpolator interpolator) { + property.set(target, value); + } + + public <T> void setInt(T target, Property<T, Integer> property, int value, + TimeInterpolator interpolator) { + property.set(target, value); + } + + public static class AnimatedPropertySetter extends PropertySetter { + + private final long mDuration; + private final AnimatorSetBuilder mStateAnimator; + + public AnimatedPropertySetter(long duration, AnimatorSetBuilder builder) { + mDuration = duration; + mStateAnimator = builder; + } + + @Override + public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) { + if (view.getAlpha() == alpha) { + return; + } + ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha); + anim.addListener(new AlphaUpdateListener( + view, isAccessibilityEnabled(view.getContext()))); + anim.setDuration(mDuration).setInterpolator(interpolator); + mStateAnimator.play(anim); + } + + @Override + public <T> void setFloat(T target, Property<T, Float> property, float value, + TimeInterpolator interpolator) { + if (property.get(target) == value) { + return; + } + Animator anim = ObjectAnimator.ofFloat(target, property, value); + anim.setDuration(mDuration).setInterpolator(interpolator); + mStateAnimator.play(anim); + } + + @Override + public <T> void setInt(T target, Property<T, Integer> property, int value, + TimeInterpolator interpolator) { + if (property.get(target) == value) { + return; + } + Animator anim = ObjectAnimator.ofInt(target, property, value); + anim.setDuration(mDuration).setInterpolator(interpolator); + mStateAnimator.play(anim); + } + } +} diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 28645dc73..78ea419b8 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -51,6 +51,4 @@ abstract class BaseFlags { // When enabled shows a work profile tab in all apps public static final boolean ALL_APPS_TABS_ENABLED = true; - - public static final boolean ENABLE_TWO_SWIPE_TARGETS = true; } diff --git a/src/com/android/launcher3/util/VerticalSwipeController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index ae5bfd581..db5363436 100644 --- a/src/com/android/launcher3/util/VerticalSwipeController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2019 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. @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.launcher3.touch; -package com.android.launcher3.util; - -import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import android.animation.Animator; @@ -25,80 +23,56 @@ import android.animation.ValueAnimator; import android.util.Log; import android.view.MotionEvent; -import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.touch.SwipeDetector.Direction; - +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.launcher3.util.TouchController; /** - * Handles vertical touch gesture on the DragLayer allowing transitioning from - * {@link #mBaseState} to {@link LauncherState#ALL_APPS} and vice-versa. + * TouchController for handling state changes */ -public abstract class VerticalSwipeController extends AnimatorListenerAdapter +public abstract class AbstractStateChangeTouchController extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener { - private static final String TAG = "VerticalSwipeController"; - - private static final float RECATCH_REJECTION_FRACTION = .0875f; - private static final int SINGLE_FRAME_MS = 16; + private static final String TAG = "ASCTouchController"; + public static final float RECATCH_REJECTION_FRACTION = .0875f; + public static final int SINGLE_FRAME_MS = 16; // Progress after which the transition is assumed to be a success in case user does not fling - private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; + public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; protected final Launcher mLauncher; protected final SwipeDetector mDetector; - private final LauncherState mBaseState; - private final LauncherState mTargetState; private boolean mNoIntercept; + protected int mStartContainerType; - private AnimatorPlaybackController mCurrentAnimation; + protected LauncherState mFromState; protected LauncherState mToState; + protected AnimatorPlaybackController mCurrentAnimation; private float mStartProgress; // Ratio of transition process [0, 1] to drag displacement (px) private float mProgressMultiplier; - public VerticalSwipeController(Launcher l, LauncherState baseState) { - this(l, baseState, ALL_APPS, SwipeDetector.VERTICAL); - } - - public VerticalSwipeController( - Launcher l, LauncherState baseState, LauncherState targetState, Direction dir) { + public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) { mLauncher = l; mDetector = new SwipeDetector(l, this, dir); - mBaseState = baseState; - mTargetState = targetState; - } - - private boolean canInterceptTouch(MotionEvent ev) { - if (mCurrentAnimation != null) { - // If we are already animating from a previous state, we can intercept. - return true; - } - if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { - return false; - } - return shouldInterceptTouch(ev); } - protected abstract boolean shouldInterceptTouch(MotionEvent ev); + protected abstract boolean canInterceptTouch(MotionEvent ev); - @Override - public void onAnimationCancel(Animator animation) { - if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) { - Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception()); - mDetector.finishedScrolling(); - mCurrentAnimation = null; - } - } + /** + * Initializes the {@code mFromState} and {@code mToState} and swipe direction to use for + * the detector. In can of disabling swipe, return 0. + */ + protected abstract int getSwipeDirection(MotionEvent ev); @Override - public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + public final boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mNoIntercept = !canInterceptTouch(ev); if (mNoIntercept) { @@ -121,8 +95,11 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter } } else { directionsToDetectScroll = getSwipeDirection(ev); + if (directionsToDetectScroll == 0) { + mNoIntercept = true; + return false; + } } - mDetector.setDetectableScrollConditions( directionsToDetectScroll, ignoreSlopWhenSettling); } @@ -135,27 +112,24 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter return mDetector.isDraggingOrSettling(); } - protected abstract int getSwipeDirection(MotionEvent ev); - @Override - public boolean onControllerTouchEvent(MotionEvent ev) { + public final boolean onControllerTouchEvent(MotionEvent ev) { return mDetector.onTouchEvent(ev); } + protected float getShiftRange() { + return mLauncher.getAllAppsController().getShiftRange(); + } + + protected abstract float initCurrentAnimation(); + @Override public void onDragStart(boolean start) { if (mCurrentAnimation == null) { - float range = getShiftRange(); - long maxAccuracy = (long) (2 * range); + mStartProgress = 0; + mProgressMultiplier = initCurrentAnimation(); - // Build current animation - mToState = mLauncher.isInState(mTargetState) ? mBaseState : mTargetState; - mCurrentAnimation = mLauncher.getStateManager() - .createAnimationToNewWorkspace(mToState, maxAccuracy); mCurrentAnimation.getTarget().addListener(this); - mStartProgress = 0; - mProgressMultiplier = - (mLauncher.isInState(mTargetState) ^ isTransitionFlipped() ? 1 : -1) / range; mCurrentAnimation.dispatchOnStart(); } else { mCurrentAnimation.pause(); @@ -163,14 +137,6 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter } } - protected boolean isTransitionFlipped() { - return false; - } - - protected float getShiftRange() { - return mLauncher.getAllAppsController().getShiftRange(); - } - @Override public boolean onDrag(float displacement, float velocity) { float deltaProgress = mProgressMultiplier * displacement; @@ -180,45 +146,85 @@ public abstract class VerticalSwipeController extends AnimatorListenerAdapter @Override public void onDragEnd(float velocity, boolean fling) { - final long animationDuration; + final int logAction; final LauncherState targetState; final float progress = mCurrentAnimation.getProgressFraction(); if (fling) { - if (velocity < 0 ^ isTransitionFlipped()) { - targetState = mTargetState; + logAction = Touch.FLING; + targetState = + Float.compare(Math.signum(velocity), Math.signum(mProgressMultiplier)) == 0 + ? mToState : mFromState; + // snap to top or bottom using the release velocity + } else { + logAction = Touch.SWIPE; + targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState; + } + + + final float endProgress; + final float startProgress; + final long duration; + + if (targetState == mToState) { + endProgress = 1; + if (progress >= 1) { + duration = 0; + startProgress = 1; } else { - targetState = mBaseState; + startProgress = Utilities.boundToRange( + progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f); + duration = SwipeDetector.calculateDuration(velocity, + endProgress - Math.max(progress, 0)); } - animationDuration = SwipeDetector.calculateDuration(velocity, - mToState == targetState ? (1 - progress) : progress); - // snap to top or bottom using the release velocity } else { - if (progress > SUCCESS_TRANSITION_PROGRESS) { - targetState = mToState; - animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress); + endProgress = 0; + if (progress <= 0) { + duration = 0; + startProgress = 0; } else { - targetState = mToState == mTargetState ? mBaseState : mTargetState; - animationDuration = SwipeDetector.calculateDuration(velocity, progress); + startProgress = Utilities.boundToRange( + progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f); + duration = SwipeDetector.calculateDuration(velocity, + Math.min(progress, 1) - endProgress); } } - mCurrentAnimation.setEndAction(() -> { - mLauncher.getStateManager().goToState(targetState, false); - onTransitionComplete(fling, targetState == mToState); - mDetector.finishedScrolling(); - mCurrentAnimation = null; - }); - - float nextFrameProgress = Utilities.boundToRange( - progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f); - + mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction)); ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); - anim.setFloatValues(nextFrameProgress, targetState == mToState ? 1f : 0f); - anim.setDuration(animationDuration); - anim.setInterpolator(scrollInterpolatorForVelocity(velocity)); + anim.setFloatValues(startProgress, endProgress); + anim.setDuration(duration).setInterpolator(scrollInterpolatorForVelocity(velocity)); anim.start(); } - protected abstract void onTransitionComplete(boolean wasFling, boolean stateChanged); + protected int getDirectionForLog() { + return mToState.ordinal > mFromState.ordinal ? Direction.UP : Direction.DOWN; + } + + protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { + if (targetState != mFromState) { + // Transition complete. log the action + mLauncher.getUserEventDispatcher().logStateChangeAction(logAction, + getDirectionForLog(), + mStartContainerType, + mFromState.containerType, + mToState.containerType, + mLauncher.getWorkspace().getCurrentPage()); + } + clearState(); + mLauncher.getStateManager().goToState(targetState, false /* animated */); + } + + protected void clearState() { + mCurrentAnimation = null; + mDetector.finishedScrolling(); + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mCurrentAnimation != null && animation == mCurrentAnimation.getOriginalTarget()) { + Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception()); + clearState(); + } + } } diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java index f10a695c4..6f012f69a 100644 --- a/src/com/android/launcher3/touch/ItemLongClickListener.java +++ b/src/com/android/launcher3/touch/ItemLongClickListener.java @@ -18,6 +18,7 @@ package com.android.launcher3.touch; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; +import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; @@ -78,8 +79,8 @@ public class ItemLongClickListener { Launcher launcher = Launcher.getLauncher(v.getContext()); if (!canStartDrag(launcher)) return false; // When we have exited all apps or are in transition, disregard long clicks - if (!launcher.isInState(LauncherState.ALL_APPS) || - launcher.getWorkspace().isSwitchingState()) return false; + if (!launcher.isInState(ALL_APPS) && !launcher.isInState(OVERVIEW)) return false; + if (launcher.getWorkspace().isSwitchingState()) return false; // Start the drag final DragController dragController = launcher.getDragController(); |