Merge "Changing the overviewState to show appsearch and floating header" into ub-launcher3-master
This commit is contained in:
commit
2e38cf4825
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -428,7 +428,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
|||
}
|
||||
}
|
||||
|
||||
private void setTextAlpha(int alpha) {
|
||||
public void setTextAlpha(int alpha) {
|
||||
super.setTextColor(ColorUtils.setAlphaComponent(mTextColor, alpha));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
@ -952,7 +955,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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -187,8 +195,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);
|
||||
|
||||
|
@ -249,16 +258,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,12 +383,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);
|
||||
|
@ -388,6 +399,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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 canInterceptTouch(MotionEvent ev);
|
||||
|
||||
protected abstract boolean shouldInterceptTouch(MotionEvent ev);
|
||||
/**
|
||||
* 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 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;
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
// 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;
|
||||
mProgressMultiplier = initCurrentAnimation();
|
||||
|
||||
mCurrentAnimation.getTarget().addListener(this);
|
||||
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;
|
||||
} else {
|
||||
targetState = mBaseState;
|
||||
}
|
||||
animationDuration = SwipeDetector.calculateDuration(velocity,
|
||||
mToState == targetState ? (1 - progress) : progress);
|
||||
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 {
|
||||
if (progress > SUCCESS_TRANSITION_PROGRESS) {
|
||||
targetState = mToState;
|
||||
animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress);
|
||||
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 = mToState == mTargetState ? mBaseState : mTargetState;
|
||||
animationDuration = SwipeDetector.calculateDuration(velocity, progress);
|
||||
startProgress = Utilities.boundToRange(
|
||||
progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
|
||||
duration = SwipeDetector.calculateDuration(velocity,
|
||||
endProgress - Math.max(progress, 0));
|
||||
}
|
||||
} else {
|
||||
endProgress = 0;
|
||||
if (progress <= 0) {
|
||||
duration = 0;
|
||||
startProgress = 0;
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue