diff options
35 files changed, 858 insertions, 1290 deletions
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java index efa83e4b6..1fb3584a2 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java @@ -85,8 +85,8 @@ public class AllAppsState extends LauncherState { } @Override - public float getHoseatAlpha(Launcher launcher) { - return 0; + public int getVisibleElements(Launcher launcher) { + return ALL_APPS_HEADER | ALL_APPS_CONTENT; } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java b/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java deleted file mode 100644 index 6df1aba01..000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2017 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.uioverrides; - -import com.android.launcher3.Alarm; -import com.android.launcher3.OnAlarmListener; - -/** - * Utility class to detect a pause during a drag. - */ -public class DragPauseDetector implements OnAlarmListener { - - private static final float MAX_VELOCITY_TO_PAUSE = 0.2f; - private static final long PAUSE_DURATION = 100; - - private final Alarm mAlarm; - private final Runnable mOnPauseCallback; - - private boolean mTriggered = false; - private int mDisabledFlags = 0; - - public DragPauseDetector(Runnable onPauseCallback) { - mOnPauseCallback = onPauseCallback; - - mAlarm = new Alarm(); - mAlarm.setOnAlarmListener(this); - mAlarm.setAlarm(PAUSE_DURATION); - } - - public void onDrag(float velocity) { - if (mTriggered || !isEnabled()) { - return; - } - - if (Math.abs(velocity) > MAX_VELOCITY_TO_PAUSE) { - // Cancel any previous alarm and set a new alarm - mAlarm.setAlarm(PAUSE_DURATION); - } - } - - @Override - public void onAlarm(Alarm alarm) { - if (!mTriggered && isEnabled()) { - mTriggered = true; - mOnPauseCallback.run(); - } - } - - public boolean isTriggered () { - return mTriggered; - } - - public boolean isEnabled() { - return mDisabledFlags == 0; - } - - public void addDisabledFlags(int flags) { - boolean wasEnabled = isEnabled(); - mDisabledFlags |= flags; - resetAlarm(wasEnabled); - } - - public void clearDisabledFlags(int flags) { - boolean wasEnabled = isEnabled(); - mDisabledFlags &= ~flags; - resetAlarm(wasEnabled); - } - - private void resetAlarm(boolean wasEnabled) { - boolean isEnabled = isEnabled(); - if (wasEnabled == isEnabled) { - // Nothing has changed - } if (isEnabled && !mTriggered) { - mAlarm.setAlarm(PAUSE_DURATION); - } else if (!isEnabled) { - mAlarm.cancelAlarm(); - } - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java deleted file mode 100644 index 97ac3e606..000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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.uioverrides; - -import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.touch.SwipeDetector.DIRECTION_NEGATIVE; -import static com.android.launcher3.touch.SwipeDetector.DIRECTION_POSITIVE; -import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL; -import static com.android.launcher3.touch.SwipeDetector.VERTICAL; -import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR; - -import android.graphics.Rect; -import android.metrics.LogMaker; -import android.view.MotionEvent; - -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; -import com.android.launcher3.dragndrop.DragLayer; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.util.VerticalSwipeController; -import com.android.quickstep.views.RecentsView; - -class EventLogTags { - private EventLogTags() { - } // don't instantiate - - /** 524292 sysui_multi_action (content|4) */ - public static final int SYSUI_MULTI_ACTION = 524292; - - public static void writeSysuiMultiAction(Object[] content) { - android.util.EventLog.writeEvent(SYSUI_MULTI_ACTION, content); - } -} - -class MetricsLogger { - private static MetricsLogger sMetricsLogger; - - private static MetricsLogger getLogger() { - if (sMetricsLogger == null) { - sMetricsLogger = new MetricsLogger(); - } - return sMetricsLogger; - } - - protected void saveLog(Object[] rep) { - EventLogTags.writeSysuiMultiAction(rep); - } - - public void write(LogMaker content) { - if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) { - content.setType(4/*MetricsEvent.TYPE_ACTION*/); - } - saveLog(content.serialize()); - } -} - -/** - * Extension of {@link VerticalSwipeController} to go from NORMAL to OVERVIEW. - */ -public class EdgeSwipeController extends VerticalSwipeController implements - OnDeviceProfileChangeListener { - - private static final Rect sTempRect = new Rect(); - - private final MetricsLogger mMetricsLogger = new MetricsLogger(); - - public EdgeSwipeController(Launcher l) { - super(l, NORMAL, OVERVIEW, l.getDeviceProfile().isVerticalBarLayout() - ? HORIZONTAL : VERTICAL); - l.addOnDeviceProfileChangeListener(this); - } - - @Override - public void onDeviceProfileChanged(DeviceProfile dp) { - mDetector.updateDirection(dp.isVerticalBarLayout() ? HORIZONTAL : VERTICAL); - } - - @Override - protected boolean shouldInterceptTouch(MotionEvent ev) { - return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0; - } - - @Override - protected int getSwipeDirection(MotionEvent ev) { - return isTransitionFlipped() ? DIRECTION_NEGATIVE : DIRECTION_POSITIVE; - } - - public EdgeSwipeController(Launcher l, LauncherState baseState) { - super(l, baseState); - } - - @Override - protected boolean isTransitionFlipped() { - return mLauncher.getDeviceProfile().isSeascape(); - } - - @Override - protected void onTransitionComplete(boolean wasFling, boolean stateChanged) { - if (stateChanged && mToState instanceof OverviewState) { - // Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for - // "Recents" activity for app transition tests. - final LogMaker builder = new LogMaker(761/*APP_TRANSITION*/); - builder.setPackageName("com.android.systemui"); - builder.addTaggedData(871/*FIELD_CLASS_NAME*/, - "com.android.systemui.recents.RecentsActivity"); - builder.addTaggedData(319/*APP_TRANSITION_DELAY_MS*/, - 0/* zero time */); - mMetricsLogger.write(builder); - - // Add user event logging for launcher pipeline - int direction = Direction.UP; - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - direction = Direction.LEFT; - if (mLauncher.getDeviceProfile().isSeascape()) { - direction = Direction.RIGHT; - } - } - mLauncher.getUserEventDispatcher().logStateChangeAction( - wasFling ? Touch.FLING : Touch.SWIPE, direction, - ContainerType.NAVBAR, ContainerType.WORKSPACE, // src target - ContainerType.TASKSWITCHER, // dst target - mLauncher.getWorkspace().getCurrentPage()); - } - } - - @Override - protected float getShiftRange() { - return getShiftRange(mLauncher); - } - - public static float getShiftRange(Launcher launcher) { - RecentsView.getPageRect(launcher.getDeviceProfile(), launcher, sTempRect); - DragLayer dl = launcher.getDragLayer(); - Rect insets = dl.getInsets(); - DeviceProfile dp = launcher.getDeviceProfile(); - - if (dp.isVerticalBarLayout()) { - if (dp.isSeascape()) { - return insets.left + sTempRect.left; - } else { - return dl.getWidth() - sTempRect.right + insets.right; - } - } else { - return dl.getHeight() - sTempRect.bottom + insets.bottom; - } - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java index 9541d0d05..9e82b2571 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java @@ -41,10 +41,10 @@ public class FastOverviewState extends OverviewState { } @Override - public float getHoseatAlpha(Launcher launcher) { + public int getVisibleElements(Launcher launcher) { if (DEBUG_DIFFERENT_UI) { - return 0; + return NONE; } - return super.getHoseatAlpha(launcher); + return super.getVisibleElements(launcher); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java new file mode 100644 index 000000000..23add9595 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java @@ -0,0 +1,71 @@ +package com.android.launcher3.uioverrides; + +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR; + +import android.view.MotionEvent; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.touch.AbstractStateChangeTouchController; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.quickstep.util.SysuiEventLogger; + +/** + * Touch controller for handling edge swipes in landscape/seascape UI + */ +public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchController { + + public LandscapeEdgeSwipeController(Launcher l) { + super(l, SwipeDetector.HORIZONTAL); + } + + @Override + protected 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 mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0; + } + + @Override + protected int getSwipeDirection(MotionEvent ev) { + mFromState = NORMAL; + mToState = OVERVIEW; + return SwipeDetector.DIRECTION_BOTH; + } + + @Override + protected float getShiftRange() { + return mLauncher.getDragLayer().getWidth(); + } + + @Override + protected float initCurrentAnimation() { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, maxAccuracy); + return (mLauncher.getDeviceProfile().isSeascape() ? 2 : -2) / range; + } + + @Override + protected int getDirectionForLog() { + return mLauncher.getDeviceProfile().isSeascape() ? Direction.RIGHT : Direction.LEFT; + } + + @Override + protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { + super.onSwipeInteractionCompleted(targetState, logAction); + if (mFromState == NORMAL && targetState == OVERVIEW) { + SysuiEventLogger.writeDummyRecentsTransition(0); + } + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java new file mode 100644 index 000000000..720b20ac1 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 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.uioverrides; + +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; + +import android.view.MotionEvent; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.quickstep.TouchInteractionService; +import com.android.quickstep.views.RecentsView; + +/** + * Touch controller from going from OVERVIEW to ALL_APPS + */ +public class LandscapeStatesTouchController extends PortraitStatesTouchController { + + public LandscapeStatesTouchController(Launcher l) { + super(l); + } + + @Override + protected 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; + } + if (mLauncher.isInState(ALL_APPS)) { + // In all-apps only listen if the container cannot scroll itself + return mLauncher.getAppsView().shouldContainerScroll(ev); + } else if (mLauncher.isInState(NORMAL)) { + return true; + } else if (mLauncher.isInState(OVERVIEW)) { + RecentsView rv = mLauncher.getOverviewPanel(); + return ev.getY() > (rv.getBottom() - rv.getPaddingBottom()); + } else { + return false; + } + } + + protected LauncherState getTargetState() { + if (mLauncher.isInState(ALL_APPS)) { + // Should swipe down go to OVERVIEW instead? + return TouchInteractionService.isConnected() ? + mLauncher.getStateManager().getLastState() : NORMAL; + } else { + return ALL_APPS; + } + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index 09acb1d2b..d123dce5a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -22,6 +22,7 @@ import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; import android.graphics.Rect; import android.view.View; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.Workspace; @@ -105,4 +106,29 @@ public class OverviewState extends LauncherState { return new float[] {scale, 0, translationY}; } + + @Override + public int getVisibleElements(Launcher launcher) { + if (launcher.getDeviceProfile().isVerticalBarLayout()) { + // TODO: Remove hotseat from overview + return HOTSEAT; + } else { + return launcher.getAppsView().getFloatingHeaderView().hasVisibleContent() + ? ALL_APPS_HEADER : HOTSEAT; + } + } + + @Override + public float getVerticalProgress(Launcher launcher) { + if (getVisibleElements(launcher) == HOTSEAT) { + return super.getVerticalProgress(launcher); + } + return 1 - (getDefaultSwipeHeight(launcher) + / launcher.getAllAppsController().getShiftRange()); + } + + public static float getDefaultSwipeHeight(Launcher launcher) { + DeviceProfile dp = launcher.getDeviceProfile(); + return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx; + } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java deleted file mode 100644 index 4fb388613..000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2017 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.uioverrides; - -import static com.android.launcher3.LauncherState.OVERVIEW; - -import android.view.MotionEvent; - -import com.android.launcher3.Launcher; -import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.util.VerticalSwipeController; - -/** - * Extension of {@link VerticalSwipeController} which allows swipe up from OVERVIEW to ALL_APPS - * Note that the swipe down is handled by {@link TwoStepSwipeController}. - */ -public class OverviewSwipeUpController extends VerticalSwipeController { - - public OverviewSwipeUpController(Launcher l) { - super(l, OVERVIEW); - } - - @Override - protected boolean shouldInterceptTouch(MotionEvent ev) { - if (!mLauncher.isInState(OVERVIEW)) { - return false; - } - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - return ev.getY() > - mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher); - } else { - return mLauncher.getDragLayer().isEventOverHotseat(ev); - } - } - - @Override - protected int getSwipeDirection(MotionEvent ev) { - return SwipeDetector.DIRECTION_POSITIVE; - } - - @Override - protected void onTransitionComplete(boolean wasFling, boolean stateChanged) { - if (stateChanged) { - // Transition complete. log the action - mLauncher.getUserEventDispatcher().logStateChangeAction( - wasFling ? Touch.FLING : Touch.SWIPE, - Direction.UP, - ContainerType.HOTSEAT, - ContainerType.TASKSWITCHER, - ContainerType.ALLAPPS, - mLauncher.getWorkspace().getCurrentPage()); - } - - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java new file mode 100644 index 000000000..9f648edd1 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java @@ -0,0 +1,128 @@ +/* + * 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.uioverrides; + +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; + +import android.view.MotionEvent; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.touch.AbstractStateChangeTouchController; +import com.android.quickstep.TouchInteractionService; +import com.android.quickstep.util.SysuiEventLogger; + +/** + * Touch controller for handling various state transitions in portrait UI. + */ +public class PortraitStatesTouchController extends AbstractStateChangeTouchController { + + public PortraitStatesTouchController(Launcher l) { + super(l, SwipeDetector.VERTICAL); + } + + @Override + protected boolean canInterceptTouch(MotionEvent ev) { + if (mCurrentAnimation != null) { + // If we are already animating from a previous state, we can intercept. + return true; + } + if (mLauncher.isInState(ALL_APPS)) { + // In all-apps only listen if the container cannot scroll itself + if (!mLauncher.getAppsView().shouldContainerScroll(ev)) { + return false; + } + } else { + // For all other states, only listen if the event originated below the hotseat height + DeviceProfile dp = mLauncher.getDeviceProfile(); + int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom; + if (ev.getY() < (mLauncher.getDragLayer().getHeight() - hotseatHeight)) { + return false; + } + } + if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { + return false; + } + return true; + } + + @Override + protected int getSwipeDirection(MotionEvent ev) { + final int directionsToDetectScroll; + if (mLauncher.isInState(ALL_APPS)) { + directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; + mStartContainerType = ContainerType.ALLAPPS; + } else if (mLauncher.isInState(NORMAL)) { + directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; + mStartContainerType = ContainerType.HOTSEAT; + } else if (mLauncher.isInState(OVERVIEW)) { + directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; + mStartContainerType = ContainerType.TASKSWITCHER; + } else { + return 0; + } + mFromState = mLauncher.getStateManager().getState(); + mToState = getTargetState(); + if (mFromState == mToState) { + return 0; + } + return directionsToDetectScroll; + } + + protected LauncherState getTargetState() { + if (mLauncher.isInState(ALL_APPS)) { + // Should swipe down go to OVERVIEW instead? + return TouchInteractionService.isConnected() ? + mLauncher.getStateManager().getLastState() : NORMAL; + } else if (mLauncher.isInState(OVERVIEW)) { + return ALL_APPS; + } else { + return TouchInteractionService.isConnected() ? OVERVIEW : ALL_APPS; + } + } + + @Override + protected float initCurrentAnimation() { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, maxAccuracy); + + float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range; + float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range; + + float totalShift = endVerticalShift - startVerticalShift; + if (totalShift == 0) { + totalShift = Math.signum(mFromState.ordinal - mToState.ordinal) + * OverviewState.getDefaultSwipeHeight(mLauncher); + } + return 1 / totalShift; + } + + @Override + protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { + super.onSwipeInteractionCompleted(targetState, logAction); + if (mFromState == NORMAL && targetState == OVERVIEW) { + SysuiEventLogger.writeDummyRecentsTransition(0); + } + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java b/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java deleted file mode 100644 index 651a75354..000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2017 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.uioverrides; - -import android.animation.Animator; -import android.util.SparseArray; - -import com.android.launcher3.anim.AnimatorSetBuilder; - -import java.util.Collections; -import java.util.List; - -public class TaggedAnimatorSetBuilder extends AnimatorSetBuilder { - - /** - * Map of the index in {@link #mAnims} to tag. All the animations in {@link #mAnims} starting - * from this index correspond to the tag (until a new tag is specified for an index) - */ - private final SparseArray<Object> mTags = new SparseArray<>(); - - @Override - public void startTag(Object obj) { - mTags.put(mAnims.size(), obj); - } - - public List<Animator> getAnimationsForTag(Object tag) { - int startIndex = mTags.indexOfValue(tag); - if (startIndex < 0) { - return Collections.emptyList(); - } - int startPos = mTags.keyAt(startIndex); - - int endIndex = startIndex + 1; - int endPos = endIndex >= mTags.size() ? mAnims.size() : mTags.keyAt(endIndex); - - return mAnims.subList(startPos, endPos); - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java index 8b738091a..d11547de8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java @@ -15,8 +15,6 @@ */ package com.android.launcher3.uioverrides; -import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; @@ -30,24 +28,21 @@ import android.view.View; 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.dragndrop.DragLayer; import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.TouchController; import com.android.quickstep.PendingAnimation; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; /** - * Touch controller for swipe interaction in Overview state + * Touch controller for handling task view card swipes */ -public class OverviewSwipeController extends AnimatorListenerAdapter +public class TaskViewTouchController extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener { private static final String TAG = "OverviewSwipeController"; @@ -68,16 +63,14 @@ public class OverviewSwipeController extends AnimatorListenerAdapter private boolean mCurrentAnimationIsGoingUp; private boolean mNoIntercept; - private boolean mSwipeDownEnabled; private float mDisplacementShift; private float mProgressMultiplier; private float mEndDisplacement; - private int mStartingTarget; private TaskView mTaskBeingDragged; - public OverviewSwipeController(Launcher launcher) { + public TaskViewTouchController(Launcher launcher) { mLauncher = launcher; mRecentsView = launcher.getOverviewPanel(); mDetector = new SwipeDetector(launcher, this, SwipeDetector.VERTICAL); @@ -94,15 +87,6 @@ public class OverviewSwipeController extends AnimatorListenerAdapter return mLauncher.isInState(OVERVIEW); } - private boolean isEventOverHotseat(MotionEvent ev) { - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - return ev.getY() > - mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher); - } else { - return mLauncher.getDragLayer().isEventOverHotseat(ev); - } - } - @Override public void onAnimationCancel(Animator animation) { if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) { @@ -129,22 +113,14 @@ public class OverviewSwipeController extends AnimatorListenerAdapter ignoreSlopWhenSettling = true; } else { mTaskBeingDragged = null; - mSwipeDownEnabled = true; View view = mRecentsView.getChildAt(mRecentsView.getCurrentPage()); if (view instanceof TaskView && mLauncher.getDragLayer().isEventOverView(view, ev)) { // The tile can be dragged down to open the task. mTaskBeingDragged = (TaskView) view; directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH; - mStartingTarget = LauncherLogProto.ItemType.TASK; - } else if (isEventOverHotseat(ev)) { - // The hotseat is being dragged - directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; - mSwipeDownEnabled = false; - mStartingTarget = ContainerType.HOTSEAT; } else { mNoIntercept = true; - mStartingTarget = ContainerType.WORKSPACE; return false; } } @@ -167,9 +143,6 @@ public class OverviewSwipeController extends AnimatorListenerAdapter } private void reInitAnimationController(boolean goingUp) { - if (!goingUp && !mSwipeDownEnabled) { - goingUp = true; - } if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) { // No need to init return; @@ -187,31 +160,20 @@ public class OverviewSwipeController extends AnimatorListenerAdapter long maxDuration = (long) (2 * range); DragLayer dl = mLauncher.getDragLayer(); - if (mTaskBeingDragged == null) { - // User is either going to all apps or home - mCurrentAnimation = mLauncher.getStateManager() - .createAnimationToNewWorkspace(goingUp ? ALL_APPS : NORMAL, maxDuration); - if (goingUp) { - mEndDisplacement = -range; - } else { - mEndDisplacement = EdgeSwipeController.getShiftRange(mLauncher); - } + if (goingUp) { + mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged, + true /* animateTaskView */, true /* removeTask */, maxDuration); + mCurrentAnimation = AnimatorPlaybackController + .wrap(mPendingAnimation.anim, maxDuration); + mEndDisplacement = -mTaskBeingDragged.getHeight(); } else { - if (goingUp) { - mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged, - true /* animateTaskView */, true /* removeTask */, maxDuration); - mCurrentAnimation = AnimatorPlaybackController - .wrap(mPendingAnimation.anim, maxDuration); - mEndDisplacement = -mTaskBeingDragged.getHeight(); - } else { - AnimatorSet anim = new AnimatorSet(); - // TODO: Setup a zoom animation - mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration); + AnimatorSet anim = new AnimatorSet(); + // TODO: Setup a zoom animation + mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration); - mTempCords[1] = mTaskBeingDragged.getHeight(); - dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords); - mEndDisplacement = dl.getHeight() - mTempCords[1]; - } + mTempCords[1] = mTaskBeingDragged.getHeight(); + dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords); + mEndDisplacement = dl.getHeight() - mTempCords[1]; } mCurrentAnimation.getTarget().addListener(this); @@ -249,9 +211,7 @@ public class OverviewSwipeController extends AnimatorListenerAdapter if (fling) { logAction = Touch.FLING; boolean goingUp = velocity < 0; - if (!goingUp && !mSwipeDownEnabled) { - goingToEnd = false; - } else if (goingUp != mCurrentAnimationIsGoingUp) { + if (goingUp != mCurrentAnimationIsGoingUp) { // In case the fling is in opposite direction, make sure if is close enough // from the start position if (mCurrentAnimation.getProgressFraction() @@ -277,7 +237,6 @@ public class OverviewSwipeController extends AnimatorListenerAdapter float nextFrameProgress = Utilities.boundToRange( progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f); - mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction)); ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); @@ -292,25 +251,13 @@ public class OverviewSwipeController extends AnimatorListenerAdapter mPendingAnimation.finish(wasSuccess); mPendingAnimation = null; } - if (mTaskBeingDragged == null) { - LauncherState state = wasSuccess ? - (mCurrentAnimationIsGoingUp ? ALL_APPS : NORMAL) : OVERVIEW; - mLauncher.getStateManager().goToState(state, false); - - } else if (wasSuccess) { + if (wasSuccess) { if (!mCurrentAnimationIsGoingUp) { mTaskBeingDragged.launchTask(false); mLauncher.getUserEventDispatcher().logTaskLaunch(logAction, Direction.DOWN, mTaskBeingDragged.getTask().getTopComponent()); } } - if (mTaskBeingDragged == null || (wasSuccess && mCurrentAnimationIsGoingUp)) { - mLauncher.getUserEventDispatcher().logStateChangeAction(logAction, - mCurrentAnimationIsGoingUp ? Direction.UP : Direction.DOWN, - mStartingTarget, ContainerType.TASKSWITCHER, - mLauncher.getStateManager().getState().containerType, - mRecentsView.getCurrentPage()); - } mDetector.finishedScrolling(); mTaskBeingDragged = null; mCurrentAnimation = null; diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java deleted file mode 100644 index c8d75dc6f..000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (C) 2017 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.uioverrides; - -import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; -import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -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.LauncherStateManager; -import com.android.launcher3.LauncherStateManager.AnimationConfig; -import com.android.launcher3.LauncherStateManager.StateHandler; -import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.util.FloatRange; -import com.android.launcher3.util.TouchController; -import com.android.quickstep.TouchInteractionService; - -/** - * Handles vertical touch gesture on the DragLayer - */ -public class TwoStepSwipeController extends AnimatorListenerAdapter - implements TouchController, SwipeDetector.Listener { - - private static final String TAG = "TwoStepSwipeController"; - - private static final float RECATCH_REJECTION_FRACTION = .0875f; - private static final int SINGLE_FRAME_MS = 16; - private static final long QUICK_SNAP_TO_OVERVIEW_DURATION = 250; - - // 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; - - /** - * Index of the vertical swipe handles in {@link LauncherStateManager#getStateHandlers()}. - */ - private static final int SWIPE_HANDLER_INDEX = 0; - - /** - * Index of various UI handlers in {@link LauncherStateManager#getStateHandlers()} not related - * to vertical swipe. - */ - private static final int OTHER_HANDLERS_START_INDEX = SWIPE_HANDLER_INDEX + 1; - - // Swipe progress range (when starting from NORMAL state) where OVERVIEW state is allowed - private static final float MIN_PROGRESS_TO_OVERVIEW = 0.1f; - private static final float MAX_PROGRESS_TO_OVERVIEW = 0.4f; - - private static final int FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE = 1 << 0; - private static final int FLAG_OVERVIEW_DISABLED_FLING = 1 << 1; - private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2; - private static final int FLAG_OVERVIEW_DISABLED = 1 << 4; - private static final int FLAG_DISABLED_TWO_TARGETS = 1 << 5; - private static final int FLAG_DISABLED_BACK_TARGET = 1 << 6; - - private final Launcher mLauncher; - private final SwipeDetector mDetector; - - private boolean mNoIntercept; - private int mStartContainerType; - - private DragPauseDetector mDragPauseDetector; - private FloatRange mOverviewProgressRange; - private TaggedAnimatorSetBuilder mTaggedAnimatorSetBuilder; - private AnimatorSet mQuickOverviewAnimation; - private boolean mAnimatingToOverview; - private CroppedAnimationController mCroppedAnimationController; - - private AnimatorPlaybackController mCurrentAnimation; - private LauncherState mFromState; - private LauncherState mToState; - - private float mStartProgress; - // Ratio of transition process [0, 1] to drag displacement (px) - private float mProgressMultiplier; - - public TwoStepSwipeController(Launcher l) { - mLauncher = l; - mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL); - } - - private boolean canInterceptTouch(MotionEvent ev) { - if (mCurrentAnimation != null) { - // If we are already animating from a previous state, we can intercept. - return true; - } - if (mLauncher.isInState(NORMAL)) { - if ((ev.getEdgeFlags() & EDGE_NAV_BAR) != 0 && - !mLauncher.getDeviceProfile().isVerticalBarLayout()) { - // On normal swipes ignore edge swipes - return false; - } - } else if (mLauncher.isInState(ALL_APPS)) { - if (!mLauncher.getAppsView().shouldContainerScroll(ev)) { - return false; - } - } else { - // Don't listen for the swipe gesture if we are already in some other state. - return false; - } - if (mAnimatingToOverview) { - return false; - } - if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { - return false; - } - - return true; - } - - @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(); - } - } - - @Override - public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mNoIntercept = !canInterceptTouch(ev); - if (mNoIntercept) { - return false; - } - - // Now figure out which direction scroll events the controller will start - // calling the callbacks. - final int directionsToDetectScroll; - boolean ignoreSlopWhenSettling = false; - - if (mCurrentAnimation != null) { - if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) { - directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; - } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) { - directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; - } else { - directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH; - ignoreSlopWhenSettling = true; - } - } else { - if (mLauncher.isInState(ALL_APPS)) { - directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE; - mStartContainerType = ContainerType.ALLAPPS; - } else { - directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; - mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ? - ContainerType.HOTSEAT : ContainerType.WORKSPACE; - } - } - - mDetector.setDetectableScrollConditions( - directionsToDetectScroll, ignoreSlopWhenSettling); - } - - if (mNoIntercept) { - return false; - } - - onControllerTouchEvent(ev); - return mDetector.isDraggingOrSettling(); - } - - @Override - public boolean onControllerTouchEvent(MotionEvent ev) { - return mDetector.onTouchEvent(ev); - } - - @Override - public void onDragStart(boolean start) { - if (mCurrentAnimation == null) { - float range = getShiftRange(); - long maxAccuracy = (long) (2 * range); - - mDragPauseDetector = new DragPauseDetector(this::onDragPauseDetected); - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE); - if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) { - mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_TWO_TARGETS); - } - - mOverviewProgressRange = new FloatRange(); - mOverviewProgressRange.start = mLauncher.isInState(NORMAL) - ? MIN_PROGRESS_TO_OVERVIEW - : 1 - MAX_PROGRESS_TO_OVERVIEW; - mOverviewProgressRange.end = mOverviewProgressRange.start - + MAX_PROGRESS_TO_OVERVIEW - MIN_PROGRESS_TO_OVERVIEW; - - // Build current animation - mFromState = mLauncher.getStateManager().getState(); - mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS; - - if (mToState == NORMAL && mLauncher.getStateManager().getLastState() == OVERVIEW) { - mToState = OVERVIEW; - mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_BACK_TARGET); - } - - mTaggedAnimatorSetBuilder = new TaggedAnimatorSetBuilder(); - mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace( - mToState, mTaggedAnimatorSetBuilder, maxAccuracy); - - if (!TouchInteractionService.isConnected()) { - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED); - } - - mCurrentAnimation.getTarget().addListener(this); - mStartProgress = 0; - mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range; - mCurrentAnimation.dispatchOnStart(); - } else { - mCurrentAnimation.pause(); - mStartProgress = mCurrentAnimation.getProgressFraction(); - - mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING); - updatePauseDetectorRangeFlag(); - } - } - - private float getShiftRange() { - return mLauncher.getAllAppsController().getShiftRange(); - } - - @Override - public boolean onDrag(float displacement, float velocity) { - float deltaProgress = mProgressMultiplier * displacement; - mCurrentAnimation.setPlayFraction(deltaProgress + mStartProgress); - - updatePauseDetectorRangeFlag(); - mDragPauseDetector.onDrag(velocity); - - return true; - } - - private void updatePauseDetectorRangeFlag() { - if (mOverviewProgressRange.contains(mCurrentAnimation.getProgressFraction())) { - mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE); - } else { - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE); - } - } - - @Override - public void onDragEnd(float velocity, boolean fling) { - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING); - - final int logAction; - LauncherState targetState; - final float progress = mCurrentAnimation.getProgressFraction(); - - if (fling) { - logAction = Touch.FLING; - targetState = velocity < 0 ? ALL_APPS : mLauncher.getStateManager().getLastState(); - // snap to top or bottom using the release velocity - } else { - logAction = Touch.SWIPE; - targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState; - } - - float endProgress; - - if (mDragPauseDetector.isTriggered() && targetState == NORMAL) { - targetState = OVERVIEW; - endProgress = OVERVIEW.getVerticalProgress(mLauncher); - if (mFromState == NORMAL) { - endProgress = 1 - endProgress; - } - } else if (targetState == mToState) { - endProgress = 1; - } else { - endProgress = 0; - } - - LauncherState targetStateFinal = targetState; - mCurrentAnimation.setEndAction(() -> - onSwipeInteractionCompleted(targetStateFinal, logAction)); - - float nextFrameProgress = Utilities.boundToRange( - progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f); - - ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); - anim.setFloatValues(nextFrameProgress, endProgress); - anim.setDuration( - SwipeDetector.calculateDuration(velocity, Math.abs(endProgress - progress))); - anim.setInterpolator(scrollInterpolatorForVelocity(velocity)); - anim.start(); - } - - private void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { - if (targetState != mFromState) { - // Transition complete. log the action - mLauncher.getUserEventDispatcher().logStateChangeAction(logAction, - mToState == ALL_APPS ? Direction.UP : Direction.DOWN, - mStartContainerType, - mFromState.containerType, - mToState.containerType, - mLauncher.getWorkspace().getCurrentPage()); - } - clearState(); - - // TODO: mQuickOverviewAnimation might still be running in which changing a state instantly - // may cause a jump. Animate the state change with a short duration in this case? - mLauncher.getStateManager().goToState(targetState, false /* animated */); - } - - private void onDragPauseDetected() { - final ValueAnimator twoStepAnimator = ValueAnimator.ofFloat(0, 1); - twoStepAnimator.setDuration(mCurrentAnimation.getDuration()); - StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers(); - - // Change the current animation to only play the vertical handle - AnimatorSet anim = new AnimatorSet(); - anim.playTogether(mTaggedAnimatorSetBuilder.getAnimationsForTag( - handlers[SWIPE_HANDLER_INDEX])); - anim.play(twoStepAnimator); - mCurrentAnimation = mCurrentAnimation.cloneFor(anim); - - AnimatorSetBuilder builder = new AnimatorSetBuilder(); - AnimationConfig config = new AnimationConfig(); - config.duration = QUICK_SNAP_TO_OVERVIEW_DURATION; - for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) { - handlers[i].setStateWithAnimation(OVERVIEW, builder, config); - } - mQuickOverviewAnimation = builder.build(); - mQuickOverviewAnimation.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - onQuickOverviewAnimationComplete(twoStepAnimator); - } - }); - mQuickOverviewAnimation.start(); - } - - private void onQuickOverviewAnimationComplete(ValueAnimator animator) { - if (mAnimatingToOverview) { - return; - } - - // For the remainder to the interaction, the user can either go to the ALL_APPS state or - // the OVERVIEW state. - // The remaining state handlers are on the OVERVIEW state. Create one animation towards the - // ALL_APPS state and only call it when the user moved above the current range. - AnimationConfig config = new AnimationConfig(); - config.duration = (long) (2 * getShiftRange()); - config.userControlled = true; - - AnimatorSetBuilder builderToAllAppsState = new AnimatorSetBuilder(); - StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers(); - for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) { - handlers[i].setStateWithAnimation(ALL_APPS, builderToAllAppsState, config); - } - - mCroppedAnimationController = new CroppedAnimationController( - AnimatorPlaybackController.wrap(builderToAllAppsState.build(), config.duration), - new FloatRange(animator.getAnimatedFraction(), mToState == ALL_APPS ? 1 : 0)); - animator.addUpdateListener(mCroppedAnimationController); - } - - private void clearState() { - mCurrentAnimation = null; - mTaggedAnimatorSetBuilder = null; - if (mDragPauseDetector != null) { - mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_CANCEL_STATE); - } - mDragPauseDetector = null; - - if (mQuickOverviewAnimation != null) { - mQuickOverviewAnimation.cancel(); - mQuickOverviewAnimation = null; - } - mCroppedAnimationController = null; - mAnimatingToOverview = false; - - mDetector.finishedScrolling(); - } - - /** - * {@link AnimatorUpdateListener} which controls another animation for a fraction of range - */ - private static class CroppedAnimationController implements AnimatorUpdateListener { - - private final AnimatorPlaybackController mTarget; - private final FloatRange mRange; - - CroppedAnimationController(AnimatorPlaybackController target, FloatRange range) { - mTarget = target; - mRange = range; - } - - - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - float fraction = valueAnimator.getAnimatedFraction(); - - if (mRange.start < mRange.end) { - if (fraction <= mRange.start) { - mTarget.setPlayFraction(0); - } else if (fraction >= mRange.end) { - mTarget.setPlayFraction(1); - } else { - mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start)); - } - } else if (mRange.start > mRange.end) { - if (fraction >= mRange.start) { - mTarget.setPlayFraction(0); - } else if (fraction <= mRange.end) { - mTarget.setPlayFraction(1); - } else { - mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start)); - } - } else { - // mRange.start == mRange.end - mTarget.setPlayFraction(0); - } - } - } -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index 637ce60a2..49792ac25 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -25,7 +25,6 @@ import android.view.View.AccessibilityDelegate; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherStateManager.StateHandler; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.util.TouchController; import com.android.quickstep.OverviewInteractionState; @@ -35,17 +34,17 @@ import com.android.quickstep.views.RecentsView; public class UiFactory { public static TouchController[] createTouchControllers(Launcher launcher) { - if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) { + if (launcher.getDeviceProfile().isVerticalBarLayout()) { return new TouchController[] { launcher.getDragController(), - new EdgeSwipeController(launcher), - new TwoStepSwipeController(launcher), - new OverviewSwipeController(launcher)}; + new LandscapeStatesTouchController(launcher), + new LandscapeEdgeSwipeController(launcher), + new TaskViewTouchController(launcher)}; } else { return new TouchController[] { launcher.getDragController(), - new TwoStepSwipeController(launcher), - new OverviewSwipeController(launcher)}; + new PortraitStatesTouchController(launcher), + new TaskViewTouchController(launcher)}; } } diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java index a9da4f916..b43352a04 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -143,10 +143,13 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { activity.getStateManager().setRestState(startState); if (!activityVisible) { + // Since the launcher is not visible, we can safely reset the scroll position. + // This ensures then the next swipe up to all-apps starts from scroll 0. + activity.getAppsView().reset(false /* animate */); activity.getStateManager().goToState(OVERVIEW, false); // Optimization, hide the all apps view to prevent layout while initializing - activity.getAppsView().setVisibility(View.GONE); + activity.getAppsView().getContentView().setVisibility(View.GONE); } } @@ -160,20 +163,21 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { @Override public AnimatorPlaybackController createControllerForHiddenActivity( Launcher activity, int transitionLength) { - float startProgress; AllAppsTransitionController controller = activity.getAllAppsController(); - + AnimatorSet anim = new AnimatorSet(); if (activity.getDeviceProfile().isVerticalBarLayout()) { - startProgress = 1; + // TODO: } else { float scrollRange = Math.max(controller.getShiftRange(), 1); - startProgress = (transitionLength / scrollRange) + 1; + float progressDelta = (transitionLength / scrollRange); + + float endProgress = OVERVIEW.getVerticalProgress(activity); + float startProgress = endProgress + progressDelta; + ObjectAnimator shiftAnim = ObjectAnimator.ofFloat( + controller, ALL_APPS_PROGRESS, startProgress, endProgress); + shiftAnim.setInterpolator(LINEAR); + anim.play(shiftAnim); } - AnimatorSet anim = new AnimatorSet(); - ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(controller, ALL_APPS_PROGRESS, - startProgress, OVERVIEW.getVerticalProgress(activity)); - shiftAnim.setInterpolator(LINEAR); - anim.play(shiftAnim); // TODO: Link this animation to state animation, so that it is cancelled // automatically on state change diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index 96cd4a024..2d2a483b5 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -35,7 +35,6 @@ import android.graphics.Matrix.ScaleToFit; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; -import android.metrics.LogMaker; import android.os.Build; import android.os.Handler; import android.os.Looper; @@ -64,6 +63,7 @@ import com.android.launcher3.util.TraceHelper; import com.android.quickstep.ActivityControlHelper.ActivityInitListener; import com.android.quickstep.ActivityControlHelper.LayoutListener; import com.android.quickstep.TouchConsumer.InteractionType; +import com.android.quickstep.util.SysuiEventLogger; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -77,40 +77,6 @@ import com.android.systemui.shared.system.WindowManagerWrapper; import java.util.StringJoiner; -class EventLogTags { - private EventLogTags() { - } // don't instantiate - - /** 524292 sysui_multi_action (content|4) */ - public static final int SYSUI_MULTI_ACTION = 524292; - - public static void writeSysuiMultiAction(Object[] content) { - android.util.EventLog.writeEvent(SYSUI_MULTI_ACTION, content); - } -} - -class MetricsLogger { - private static MetricsLogger sMetricsLogger; - - private static MetricsLogger getLogger() { - if (sMetricsLogger == null) { - sMetricsLogger = new MetricsLogger(); - } - return sMetricsLogger; - } - - protected void saveLog(Object[] rep) { - EventLogTags.writeSysuiMultiAction(rep); - } - - public void write(LogMaker content) { - if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) { - content.setType(4/*MetricsEvent.TYPE_ACTION*/); - } - saveLog(content.serialize()); - } -} - @TargetApi(Build.VERSION_CODES.O) public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> { private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName(); @@ -229,7 +195,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> { private Matrix mTmpMatrix = new Matrix(); private final long mTouchTimeMs; private long mLauncherFrameDrawnTime; - private final MetricsLogger mMetricsLogger = new MetricsLogger(); WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs, ActivityControlHelper<T> controller) { @@ -453,15 +418,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> { onLauncherLayoutChanged(); final long transitionDelay = mLauncherFrameDrawnTime - mTouchTimeMs; - // Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for - // "Recents" activity for app transition tests for the app-to-recents case. - final LogMaker builder = new LogMaker(761/*APP_TRANSITION*/); - builder.setPackageName("com.android.systemui"); - builder.addTaggedData(871/*FIELD_CLASS_NAME*/, - "com.android.systemui.recents.RecentsActivity"); - builder.addTaggedData(319/*APP_TRANSITION_DELAY_MS*/, - transitionDelay); - mMetricsLogger.write(builder); + SysuiEventLogger.writeDummyRecentsTransition(transitionDelay); + if (LatencyTrackerCompat.isEnabled(mContext)) { LatencyTrackerCompat.logToggleRecents((int) transitionDelay); } diff --git a/quickstep/src/com/android/quickstep/util/SysuiEventLogger.java b/quickstep/src/com/android/quickstep/util/SysuiEventLogger.java new file mode 100644 index 000000000..d474ded90 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/SysuiEventLogger.java @@ -0,0 +1,47 @@ +/* + * 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.quickstep.util; + +import android.metrics.LogMaker; +import android.util.EventLog; + +/** + * Utility class for writing logs on behalf of systemUI + */ +public class SysuiEventLogger { + + /** 524292 sysui_multi_action (content|4) */ + public static final int SYSUI_MULTI_ACTION = 524292; + + private static void write(LogMaker content) { + if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) { + content.setType(4/*MetricsEvent.TYPE_ACTION*/); + } + EventLog.writeEvent(SYSUI_MULTI_ACTION, content.serialize()); + } + + public static void writeDummyRecentsTransition(long transitionDelay) { + // Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for + // "Recents" activity for app transition tests for the app-to-recents case. + final LogMaker builder = new LogMaker(761/*APP_TRANSITION*/); + builder.setPackageName("com.android.systemui"); + builder.addTaggedData(871/*FIELD_CLASS_NAME*/, + "com.android.systemui.recents.RecentsActivity"); + builder.addTaggedData(319/*APP_TRANSITION_DELAY_MS*/, + transitionDelay); + write(builder); + } +} diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml index d07ff8107..4693917e5 100644 --- a/res/layout/launcher.xml +++ b/res/layout/launcher.xml @@ -44,13 +44,6 @@ layout="@layout/overview_panel" android:visibility="gone" /> - <!-- DO NOT CHANGE THE ID --> - <include - android:id="@+id/hotseat" - layout="@layout/hotseat" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - <!-- Keep these behind the workspace so that they are not visible when we go into AllApps --> <com.android.launcher3.pageindicators.WorkspacePageIndicator @@ -69,6 +62,13 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" /> + + <!-- DO NOT CHANGE THE ID --> + <include + android:id="@+id/hotseat" + layout="@layout/hotseat" + android:layout_width="match_parent" + android:layout_height="match_parent" /> </com.android.launcher3.dragndrop.DragLayer> </com.android.launcher3.LauncherRootView> 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(); diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java index 2acd29bc6..c05d30ccd 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java @@ -63,8 +63,8 @@ public class AllAppsState extends LauncherState { } @Override - public float getHoseatAlpha(Launcher launcher) { - return 0; + public int getVisibleElements(Launcher launcher) { + return ALL_APPS_HEADER | ALL_APPS_CONTENT; } @Override diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java index 76b7e0d81..e495477f5 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java @@ -1,19 +1,3 @@ -/* - * Copyright (C) 2017 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.uioverrides; import static com.android.launcher3.LauncherState.ALL_APPS; @@ -21,31 +5,34 @@ import static com.android.launcher3.LauncherState.NORMAL; import android.view.MotionEvent; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; +import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.util.VerticalSwipeController; /** - * Extension of {@link VerticalSwipeController} to switch between NORMAL and ALL_APPS state. + * TouchController to switch between NORMAL and ALL_APPS state. */ -public class AllAppsSwipeController extends VerticalSwipeController { - - private int mStartContainerType; +public class AllAppsSwipeController extends AbstractStateChangeTouchController { public AllAppsSwipeController(Launcher l) { - super(l, NORMAL); + super(l, SwipeDetector.VERTICAL); } @Override - protected boolean shouldInterceptTouch(MotionEvent ev) { + protected 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; + } if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) { // Don't listen for the swipe gesture if we are already in some other state. return false; } - if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) { return false; } @@ -56,8 +43,12 @@ public class AllAppsSwipeController extends VerticalSwipeController { protected int getSwipeDirection(MotionEvent ev) { if (mLauncher.isInState(ALL_APPS)) { mStartContainerType = ContainerType.ALLAPPS; + mFromState = ALL_APPS; + mToState = NORMAL; return SwipeDetector.DIRECTION_NEGATIVE; } else { + mFromState = NORMAL; + mToState = ALL_APPS; mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE; return SwipeDetector.DIRECTION_POSITIVE; @@ -65,14 +56,14 @@ public class AllAppsSwipeController extends VerticalSwipeController { } @Override - protected void onTransitionComplete(boolean wasFling, boolean stateChanged) { - if (stateChanged) { - // Transition complete. log the action - mLauncher.getUserEventDispatcher().logActionOnContainer( - wasFling ? Touch.FLING : Touch.SWIPE, - mLauncher.isInState(ALL_APPS) ? Direction.UP : Direction.DOWN, - mStartContainerType, - mLauncher.getWorkspace().getCurrentPage()); - } + protected float initCurrentAnimation() { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, maxAccuracy); + float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range; + float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range; + float totalShift = endVerticalShift - startVerticalShift; + return 1 / totalShift; } } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java index 3dfbc401d..8def0d3cc 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java @@ -16,16 +16,8 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; -import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; -import android.graphics.Rect; -import android.view.View; -import android.view.accessibility.AccessibilityNodeInfo; - -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.Workspace; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; /** |