Snap for 9257771 from 345ef9ffba to tm-qpr2-release

Change-Id: Ied2a41807a640fe6325f0dbce7e5362828f39018
This commit is contained in:
Android Build Coastguard Worker
2022-11-05 01:25:28 +00:00
40 changed files with 474 additions and 549 deletions
@@ -17,6 +17,7 @@
package com.android.launcher3.testing;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.app.Activity;
import android.app.Application;
@@ -248,6 +249,9 @@ public class DebugTestInformationHandler extends TestInformationHandler {
return response;
}
case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
default:
return super.call(method, arg, extras);
}
+2
View File
@@ -288,6 +288,8 @@
<dimen name="transient_taskbar_margin">24dp</dimen>
<dimen name="transient_taskbar_shadow_blur">40dp</dimen>
<dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
<dimen name="transient_taskbar_stashed_size">32dp</dimen>
<dimen name="transient_taskbar_icon_spacing">10dp</dimen>
<!-- Taskbar swipe up thresholds -->
<dimen name="taskbar_app_window_threshold">150dp</dimen>
<dimen name="taskbar_home_overview_threshold">225dp</dimen>
@@ -355,6 +355,11 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
mTaskbarLauncherStateController.applyState();
}
@Override
public boolean isIconAlignedWithHotseat() {
return mTaskbarLauncherStateController.isIconAlignedWithHotseat();
}
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
super.dumpLogs(prefix, pw);
@@ -38,9 +38,8 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
@@ -251,17 +250,7 @@ import java.util.StringJoiner;
private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
boolean goingToLauncher = isInLauncher();
final float toAlignment;
if (goingToLauncher) {
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
boolean willStashVisually = isInStashedState
&& mControllers.taskbarStashController.supportsVisualStashing();
boolean isTaskbarAlignedWithHotseat =
mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
toAlignment = isTaskbarAlignedWithHotseat && !willStashVisually ? 1 : 0;
} else {
toAlignment = 0;
}
final float toAlignment = isIconAlignedWithHotseat() ? 1 : 0;
if (DEBUG) {
Log.d(TAG, "onStateChangeApplied - mState: " + getStateString(mState)
+ ", changedFlags: " + getStateString(changedFlags)
@@ -357,6 +346,22 @@ import java.util.StringJoiner;
return mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
}
/**
* Returns if icons should be aligned to hotseat in the current transition
*/
public boolean isIconAlignedWithHotseat() {
if (isInLauncher()) {
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
boolean willStashVisually = isInStashedState
&& mControllers.taskbarStashController.supportsVisualStashing();
boolean isTaskbarAlignedWithHotseat =
mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
return isTaskbarAlignedWithHotseat && !willStashVisually;
} else {
return false;
}
}
private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
boolean committed) {
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
@@ -395,7 +400,7 @@ import java.util.StringJoiner;
|| (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0);
mControllers.taskbarViewController.setLauncherIconAlignment(
mIconAlignment.value, mIconAlignment.getEndValue(), mLauncher.getDeviceProfile());
mIconAlignment.value, mLauncher.getDeviceProfile());
mControllers.navbarButtonsViewController.updateTaskbarAlignment(mIconAlignment.value);
// Switch taskbar and hotseat in last frame
updateIconAlphaForHome(taskbarWillBeVisible ? 1 : 0);
@@ -190,8 +190,13 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
if (isPhoneMode()) {
// DeviceProfile's taskbar vars aren't initialized w/ the flag off
Resources resources = mActivity.getResources();
mUnstashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_size);
mStashedHeight = resources.getDimensionPixelOffset(R.dimen.taskbar_stashed_size);
boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
mUnstashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
? R.dimen.transient_taskbar_size
: R.dimen.taskbar_size);
mStashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
? R.dimen.transient_taskbar_stashed_size
: R.dimen.taskbar_stashed_size);
} else {
mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarSize;
@@ -344,7 +349,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
* @see WindowInsets.Type#systemBars()
*/
public int getContentHeightToReportToApps() {
if (isPhoneMode() && !mActivity.isThreeButtonNav()) {
if ((isPhoneMode() && !mActivity.isThreeButtonNav())
|| DisplayController.isTransientTaskbar(mActivity)) {
return getStashedHeight();
}
@@ -114,6 +114,13 @@ public class TaskbarUIController {
|| mControllers.navbarButtonsViewController.isEventOverAnyItem(ev);
}
/**
* Returns true if icons should be aligned to hotseat in the current transition.
*/
public boolean isIconAlignedWithHotseat() {
return false;
}
@CallSuper
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(String.format(
@@ -280,10 +280,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
* 0 => not aligned
* 1 => fully aligned
*/
public void setLauncherIconAlignment(float alignmentRatio, Float endAlignment,
DeviceProfile launcherDp) {
public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
if (mIconAlignControllerLazy == null) {
mIconAlignControllerLazy = createIconAlignmentController(launcherDp, endAlignment);
mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
}
mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
if (alignmentRatio <= 0 || alignmentRatio >= 1) {
@@ -295,8 +294,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
/**
* Creates an animation for aligning the taskbar icons with the provided Launcher device profile
*/
private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp,
Float endAlignment) {
private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
mOnControllerPreCreateCallback.run();
PendingAnimation setter = new PendingAnimation(100);
DeviceProfile taskbarDp = mActivity.getDeviceProfile();
@@ -322,7 +320,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
boolean isToHome = endAlignment != null && endAlignment == 1;
boolean isToHome = mControllers.uiController.isIconAlignedWithHotseat();
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
int positionInHotseat;
@@ -219,6 +219,8 @@ public class QuickstepLauncher extends Launcher {
mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get()
&& SystemProperties.getBoolean("ro.launcher.depth.widget", true);
getWorkspace().addOverlayCallback(progress ->
onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX));
}
@Override
@@ -594,17 +596,6 @@ public class QuickstepLauncher extends Launcher {
recentsView.finishRecentsAnimation(true /* toRecents */, null);
}
/**
* {@code LauncherOverlayCallbacks} scroll amount.
* Indicates transition progress to -1 screen.
* @param progress From 0 to 1.
*/
@Override
public void onScrollChanged(float progress) {
super.onScrollChanged(progress);
onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX);
}
@Override
public void onAllAppsTransition(float progress) {
super.onAllAppsTransition(progress);
@@ -48,7 +48,7 @@ import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELE
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -1704,10 +1704,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
private void resumeLastTask() {
if (mRecentsAnimationController != null) {
mRecentsAnimationController.finish(false /* toRecents */, null);
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "finishRecentsAnimation",
/* extras= */ false,
/* gestureEvent= */ FINISH_RECENTS_ANIMATION);
}
doLogGesture(LAST_TASK, null);
reset();
@@ -1916,10 +1912,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
mRecentsAnimationController.detachNavigationBarFromApp(true);
}
}
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "finishRecentsAnimation",
/* extras= */ true,
/* gestureEvent= */ FINISH_RECENTS_ANIMATION);
}
private void finishCurrentTransitionToHome() {
@@ -1931,10 +1923,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
finishRecentsControllerToHome(
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "finishRecentsAnimation",
/* extras= */ true,
/* gestureEvent= */ FINISH_RECENTS_ANIMATION);
doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
}
@@ -2045,6 +2033,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId);
if (!hasTaskPreviouslyAppeared) {
ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED);
}
nextTask.launchTask(success -> {
resultCallback.accept(success);
if (success) {
@@ -98,15 +98,6 @@ public class AnimatedFloat {
}
}
/**
* Starts the animation.
*/
public void startAnimation() {
if (mValueAnimator != null) {
mValueAnimator.start();
}
}
public void cancelAnimation() {
if (mValueAnimator != null) {
mValueAnimator.cancel();
@@ -119,10 +110,6 @@ public class AnimatedFloat {
}
}
public ObjectAnimator getCurrentAnimation() {
return mValueAnimator;
}
public boolean isAnimating() {
return mValueAnimator != null;
}
@@ -140,11 +127,4 @@ public class AnimatedFloat {
public boolean isSettledOnValue(float endValue) {
return !isAnimating() && value == endValue;
}
/**
* Returns the value we are animating to, or {@code null} if we are not currently animating.
*/
public Float getEndValue() {
return mEndValue;
}
}
@@ -21,7 +21,6 @@ import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERV
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_LAST_TASK;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
import android.annotation.Nullable;
@@ -341,8 +340,6 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK);
break;
case LAST_TASK:
ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_LAST_TASK);
break;
case RECENTS:
default:
// No-Op
@@ -23,6 +23,8 @@ import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
@@ -324,8 +326,14 @@ public class RecentTasksList {
writer.println(prefix + " mChangeId=" + mChangeId);
writer.println(prefix + " mResultsUi=[id=" + mResultsUi.mRequestId + ", tasks=");
for (GroupTask task : mResultsUi) {
writer.println(prefix + " t1=" + task.task1.key.id
+ " t2=" + (task.hasMultipleTasks() ? task.task2.key.id : "-1"));
Task task1 = task.task1;
Task task2 = task.task2;
ComponentName cn1 = task1.getTopComponent();
ComponentName cn2 = task2 != null ? task2.getTopComponent() : null;
writer.println(prefix + " t1: (id=" + task1.key.id
+ "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
+ " t2: (id=" + (task2 != null ? task2.key.id : "-1")
+ "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)"));
}
writer.println(prefix + " ]");
int currentUserId = Process.myUserHandle().getIdentifier();
@@ -333,8 +341,14 @@ public class RecentTasksList {
mSysUiProxy.getRecentTasks(Integer.MAX_VALUE, currentUserId);
writer.println(prefix + " rawTasks=[");
for (GroupedRecentTaskInfo task : rawTasks) {
writer.println(prefix + " t1=" + task.getTaskInfo1().taskId
+ " t2=" + (task.getTaskInfo2() != null ? task.getTaskInfo2().taskId : "-1"));
TaskInfo taskInfo1 = task.getTaskInfo1();
TaskInfo taskInfo2 = task.getTaskInfo2();
ComponentName cn1 = taskInfo1.topActivity;
ComponentName cn2 = taskInfo2 != null ? taskInfo2.topActivity : null;
writer.println(prefix + " t1: (id=" + taskInfo1.taskId
+ "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
+ " t2: (id=" + (taskInfo2 != null ? taskInfo2.taskId : "-1")
+ "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)"));
}
writer.println(prefix + " ]");
}
@@ -130,7 +130,7 @@ public class RecentsAnimationCallbacks implements
public final void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "onRecentsAnimationCancelled",
/* event= */ "RecentsAnimationCallbacks.onAnimationCanceled",
/* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationCanceled(thumbnailDatas);
@@ -142,7 +142,7 @@ public class RecentsAnimationCallbacks implements
@Override
public void onTasksAppeared(RemoteAnimationTarget[] apps) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
ActiveGestureLog.INSTANCE.addLog("onTasksAppeared",
ActiveGestureLog.INSTANCE.addLog("RecentsAnimationCallbacks.onTasksAppeared",
ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED);
for (RecentsAnimationListener listener : getListeners()) {
listener.onTasksAppeared(apps);
@@ -164,6 +164,8 @@ public class RecentsAnimationCallbacks implements
private final void onAnimationFinished(RecentsAnimationController controller) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "RecentsAnimationCallbacks.onAnimationFinished");
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationFinished(controller);
}
@@ -18,6 +18,7 @@ package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
import android.content.Context;
import android.os.RemoteException;
@@ -155,6 +156,10 @@ public class RecentsAnimationController {
mPendingFinishCallbacks.add(callback);
return;
}
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "finishRecentsAnimation",
/* extras= */ toRecents,
/* gestureEvent= */ FINISH_RECENTS_ANIMATION);
// Finish not yet requested
mFinishRequested = true;
@@ -21,6 +21,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
import static com.android.systemui.shared.system.RemoteTransitionCompat.newRemoteTransition;
import android.app.ActivityManager;
@@ -38,6 +39,7 @@ import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -101,6 +103,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
@UiThread
public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "startRecentsAnimation",
/* gestureEvent= */ START_RECENTS_ANIMATION);
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
@@ -250,6 +255,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
* Continues the existing running recents animation for a new gesture.
*/
public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
ActiveGestureLog.INSTANCE.addLog(/* event= */ "continueRecentsAnimation");
mCallbacks.removeListener(mLastGestureState);
mLastGestureState = gestureState;
mCallbacks.addListener(gestureState);
@@ -288,6 +294,8 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
*/
public void finishRunningRecentsAnimation(boolean toHome) {
if (mController != null) {
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "finishRunningRecentsAnimation", toHome);
mCallbacks.notifyAnimationCanceled();
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
? mController::finishAnimationToHome
@@ -320,6 +328,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
* Cleans up the recents animation entirely.
*/
private void cleanUpRecentsAnimation() {
ActiveGestureLog.INSTANCE.addLog(/* event= */ "cleanUpRecentsAnimation");
if (mLiveTileCleanUpHandler != null) {
mLiveTileCleanUpHandler.run();
mLiveTileCleanUpHandler = null;
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
@@ -22,6 +23,7 @@ import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -46,7 +48,6 @@ import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Icon;
import android.os.Build;
@@ -54,7 +55,6 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
import android.view.Choreographer;
import android.view.InputEvent;
@@ -86,7 +86,6 @@ import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
@@ -103,7 +102,6 @@ import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -139,21 +137,7 @@ public class TouchInteractionService extends Service
private static final String TAG = "TouchInteractionService";
private static final boolean BUBBLES_HOME_GESTURE_ENABLED =
SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true);
private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
/**
* System Action ID to show all apps.
* TODO: Use AccessibilityService's corresponding global action constant in S
*/
private static final int SYSTEM_ACTION_ID_ALL_APPS = 14;
private int mBackGestureNotificationCounter = -1;
private final TISBinder mTISBinder = new TISBinder();
@@ -264,12 +248,6 @@ public class TouchInteractionService extends Service
MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
}
@Override
public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) {
WindowBounds wb = new WindowBounds(bounds, insets);
MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
}
@BinderThread
@Override
public void onScreenTurnedOn() {
@@ -483,8 +461,6 @@ public class TouchInteractionService extends Service
// Temporarily disable model preload
// new ModelPreload().start(this);
mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
.getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
@@ -522,11 +498,11 @@ public class TouchInteractionService extends Service
Icon.createWithResource(this, R.drawable.ic_apps),
getString(R.string.all_apps_label),
getString(R.string.all_apps_label),
PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent,
PendingIntent.getActivity(this, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
am.registerSystemAction(allAppsAction, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
} else {
am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
}
StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface()
@@ -604,7 +580,7 @@ public class TouchInteractionService extends Service
ProtoTracer.INSTANCE.get(this).remove(this);
getSystemService(AccessibilityManager.class)
.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
mTaskbarManager.destroy();
sConnected = false;
@@ -829,27 +805,9 @@ public class TouchInteractionService extends Service
if (mDeviceState.isBubblesExpanded()) {
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("bubbles expanded");
if (BUBBLES_HOME_GESTURE_ENABLED) {
reasonString.append(SUBSTRING_PREFIX)
.append("bubbles can handle the home gesture")
.append(", trying to use default input consumer");
// Bubbles can handle home gesture itself.
base = getDefaultInputConsumer(reasonString);
} else {
// If Bubbles is expanded, use the overlay input consumer, which will close
// Bubbles instead of going all the way home when a swipe up is detected.
// Notification panel can be expanded on top of expanded bubbles. Bubbles remain
// expanded in the back. Make sure swipe up is not passed to bubbles in this
// case.
if (!mDeviceState.isNotificationPanelExpanded()) {
reasonString = newCompoundString(reasonPrefix)
.append(SUBSTRING_PREFIX)
.append("using SysUiOverlayInputConsumer");
base = new SysUiOverlayInputConsumer(
getBaseContext(), mDeviceState, mInputMonitorCompat);
}
}
.append("bubbles expanded, trying to use default input consumer");
// Bubbles can handle home gesture itself.
base = getDefaultInputConsumer(reasonString);
}
if (mDeviceState.isSystemUiDialogShowing()) {
@@ -927,6 +885,9 @@ public class TouchInteractionService extends Service
.append(consumer.getName())
.append(". reason(s):")
.append(reasonString));
if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) {
ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER);
}
}
private void handleOrientationSetup(InputConsumer baseInputConsumer) {
@@ -1251,12 +1212,22 @@ public class TouchInteractionService extends Service
}
private void onCommand(PrintWriter pw, LinkedList<String> args) {
switch (args.pollFirst()) {
String cmd = args.pollFirst();
if (cmd == null) {
pw.println("Command missing");
printAvailableCommands(pw);
return;
}
switch (cmd) {
case "clear-touch-log":
ActiveGestureLog.INSTANCE.clear();
break;
case "print-gesture-log":
ActiveGestureLog.INSTANCE.dump("", pw);
break;
default:
pw.println("Command does not exist: " + cmd);
printAvailableCommands(pw);
}
}
@@ -30,7 +30,6 @@ import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import android.annotation.TargetApi;
@@ -67,7 +66,6 @@ import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
@@ -387,10 +385,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "startRecentsAnimation",
/* gestureEvent= */ START_RECENTS_ANIMATION);
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
@@ -28,8 +28,6 @@ import android.content.Intent;
import android.graphics.Point;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -43,7 +41,6 @@ import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -102,8 +99,7 @@ public class ProgressDelegateInputConsumer implements InputConsumer,
mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
// Init states
mStateCallback = new MultiStateCallback(
STATE_NAMES, ProgressDelegateInputConsumer::getTrackedEventForState);
mStateCallback = new MultiStateCallback(STATE_NAMES);
mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
this::endRemoteAnimation);
mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED,
@@ -113,14 +109,6 @@ public class ProgressDelegateInputConsumer implements InputConsumer,
mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
}
@Nullable
private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
if (stateFlag == STATE_HANDLER_INVALIDATED) {
return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED;
}
return null;
}
@Override
public int getType() {
return TYPE_PROGRESS_DELEGATE;
@@ -135,7 +135,6 @@ public class AllSetActivity extends Activity {
} catch (URISyntaxException e) {
Log.e(LOG_TAG, "Failed to parse system nav settings intent", e);
}
finish();
});
findViewById(R.id.hint).setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
@@ -33,10 +33,11 @@ public class ActiveGestureErrorDetector {
* Enums associated to gesture navigation events.
*/
public enum GestureEvent {
MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_LAST_TASK,
SET_END_TARGET_NEW_TASK, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION,
FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK,
CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED,
MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_NEW_TASK,
ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION,
CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION,
CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER,
/**
* These GestureEvents are specifically associated to state flags that get set in
@@ -91,35 +92,40 @@ public class ActiveGestureErrorDetector {
case MOTION_UP:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_DOWN),
/* errorMessage= */ prefix + "\t\tMotion up detected before/without"
prefix,
/* errorMessage= */ "Motion up detected before/without"
+ " motion down.",
writer);
break;
case ON_SETTLED_ON_END_TARGET:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.SET_END_TARGET),
/* errorMessage= */ prefix + "\t\tonSettledOnEndTarget called "
prefix,
/* errorMessage= */ "onSettledOnEndTarget called "
+ "before/without setEndTarget.",
writer);
break;
case FINISH_RECENTS_ANIMATION:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
/* errorMessage= */ prefix + "\t\tfinishRecentsAnimation called "
prefix,
/* errorMessage= */ "finishRecentsAnimation called "
+ "before/without startRecentsAnimation.",
writer);
break;
case CANCEL_RECENTS_ANIMATION:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION),
/* errorMessage= */ prefix + "\t\tcancelRecentsAnimation called "
prefix,
/* errorMessage= */ "cancelRecentsAnimation called "
+ "before/without startRecentsAnimation.",
writer);
break;
case CLEANUP_SCREENSHOT:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
/* errorMessage= */ prefix + "\t\trecents activity screenshot was "
prefix,
/* errorMessage= */ "recents activity screenshot was "
+ "cleaned up before/without STATE_SCREENSHOT_CAPTURED "
+ "being set.",
writer);
@@ -129,48 +135,66 @@ public class ActiveGestureErrorDetector {
encounteredEvents.contains(GestureEvent.SET_END_TARGET_HOME)
&& !encounteredEvents.contains(
GestureEvent.ON_SETTLED_ON_END_TARGET),
/* errorMessage= */ prefix + "\t\trecents view scroller animation "
prefix,
/* errorMessage= */ "recents view scroller animation "
+ "aborted after setting end target HOME, but before"
+ " settling on end target.",
writer);
break;
case TASK_APPEARED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.SET_END_TARGET_LAST_TASK)
&& !encounteredEvents.contains(
GestureEvent.SET_END_TARGET_NEW_TASK),
/* errorMessage= */ prefix + "\t\tonTasksAppeared called "
+ "before/without setting end target to last or new task",
!encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
prefix,
/* errorMessage= */ "onTasksAppeared called "
+ "before/without setting end target to new task",
writer);
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.EXPECTING_TASK_APPEARED),
prefix,
/* errorMessage= */ "onTasksAppeared was not expected to be called",
writer);
break;
case EXPECTING_TASK_APPEARED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK),
prefix,
/* errorMessage= */ "expecting onTasksAppeared to be called "
+ "before/without setting end target to new task",
writer);
break;
case STATE_GESTURE_COMPLETED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_UP),
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set "
prefix,
/* errorMessage= */ "STATE_GESTURE_COMPLETED set "
+ "before/without motion up.",
writer);
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set "
prefix,
/* errorMessage= */ "STATE_GESTURE_COMPLETED set "
+ "before/without STATE_GESTURE_STARTED.",
writer);
break;
case STATE_GESTURE_CANCELLED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_UP),
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set "
prefix,
/* errorMessage= */ "STATE_GESTURE_CANCELLED set "
+ "before/without motion up.",
writer);
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED),
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set "
prefix,
/* errorMessage= */ "STATE_GESTURE_CANCELLED set "
+ "before/without STATE_GESTURE_STARTED.",
writer);
break;
case STATE_SCREENSHOT_CAPTURED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT),
/* errorMessage= */ prefix + "\t\tSTATE_SCREENSHOT_CAPTURED set "
prefix,
/* errorMessage= */ "STATE_SCREENSHOT_CAPTURED set "
+ "before/without STATE_CAPTURE_SCREENSHOT.",
writer);
break;
@@ -178,7 +202,8 @@ public class ActiveGestureErrorDetector {
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(
GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK),
/* errorMessage= */ prefix + "\t\tSTATE_RECENTS_SCROLLING_FINISHED "
prefix,
/* errorMessage= */ "STATE_RECENTS_SCROLLING_FINISHED "
+ "set before/without calling "
+ "setOnPageTransitionEndCallback.",
writer);
@@ -187,16 +212,19 @@ public class ActiveGestureErrorDetector {
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(
GestureEvent.START_RECENTS_ANIMATION),
/* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED "
prefix,
/* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED "
+ "set before/without startRecentsAnimation.",
writer);
break;
case MOTION_DOWN:
case SET_END_TARGET:
case SET_END_TARGET_HOME:
case SET_END_TARGET_NEW_TASK:
case START_RECENTS_ANIMATION:
case SET_ON_PAGE_TRANSITION_END_CALLBACK:
case CANCEL_CURRENT_ANIMATION:
case FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER:
case STATE_GESTURE_STARTED:
case STATE_END_TARGET_ANIMATION_FINISHED:
case STATE_CAPTURE_SCREENSHOT:
@@ -210,31 +238,36 @@ public class ActiveGestureErrorDetector {
// Check that all required events were found.
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_DOWN),
/* errorMessage= */ prefix + "\t\tMotion down never detected.",
prefix,
/* errorMessage= */ "Motion down never detected.",
writer);
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_UP),
/* errorMessage= */ prefix + "\t\tMotion up never detected.",
prefix,
/* errorMessage= */ "Motion up never detected.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
&& !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
/* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
prefix,
/* errorMessage= */ "setEndTarget was called, but "
+ "onSettledOnEndTarget wasn't.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
&& !encounteredEvents.contains(
GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED),
/* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
prefix,
/* errorMessage= */ "setEndTarget was called, but "
+ "STATE_END_TARGET_ANIMATION_FINISHED was never set.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET)
&& !encounteredEvents.contains(
GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
/* errorMessage= */ prefix + "\t\tsetEndTarget was called, but "
prefix,
/* errorMessage= */ "setEndTarget was called, but "
+ "STATE_RECENTS_SCROLLING_FINISHED was never set.",
writer);
errorDetected |= printErrorIfTrue(
@@ -243,7 +276,8 @@ public class ActiveGestureErrorDetector {
&& encounteredEvents.contains(
GestureEvent.STATE_RECENTS_SCROLLING_FINISHED)
&& !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET),
/* errorMessage= */ prefix + "\t\tSTATE_END_TARGET_ANIMATION_FINISHED and "
prefix,
/* errorMessage= */ "STATE_END_TARGET_ANIMATION_FINISHED and "
+ "STATE_RECENTS_SCROLLING_FINISHED were set, but onSettledOnEndTarget "
+ "wasn't called.",
writer);
@@ -253,7 +287,8 @@ public class ActiveGestureErrorDetector {
GestureEvent.START_RECENTS_ANIMATION)
&& !encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION)
&& !encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION),
/* errorMessage= */ prefix + "\t\tstartRecentsAnimation was called, but "
prefix,
/* errorMessage= */ "startRecentsAnimation was called, but "
+ "finishRecentsAnimation and cancelRecentsAnimation weren't.",
writer);
@@ -261,7 +296,8 @@ public class ActiveGestureErrorDetector {
/* condition= */ encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED)
&& !encounteredEvents.contains(GestureEvent.STATE_GESTURE_COMPLETED)
&& !encounteredEvents.contains(GestureEvent.STATE_GESTURE_CANCELLED),
/* errorMessage= */ prefix + "\t\tSTATE_GESTURE_STARTED was set, but "
prefix,
/* errorMessage= */ "STATE_GESTURE_STARTED was set, but "
+ "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.",
writer);
@@ -269,7 +305,8 @@ public class ActiveGestureErrorDetector {
/* condition= */ encounteredEvents.contains(
GestureEvent.STATE_CAPTURE_SCREENSHOT)
&& !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
/* errorMessage= */ prefix + "\t\tSTATE_CAPTURE_SCREENSHOT was set, but "
prefix,
/* errorMessage= */ "STATE_CAPTURE_SCREENSHOT was set, but "
+ "STATE_SCREENSHOT_CAPTURED wasn't.",
writer);
@@ -278,15 +315,18 @@ public class ActiveGestureErrorDetector {
GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK)
&& !encounteredEvents.contains(
GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
/* errorMessage= */ prefix + "\t\tsetOnPageTransitionEndCallback called, but "
prefix,
/* errorMessage= */ "setOnPageTransitionEndCallback called, but "
+ "STATE_RECENTS_SCROLLING_FINISHED wasn't set.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ !encounteredEvents.contains(
GestureEvent.CANCEL_CURRENT_ANIMATION)
/* condition= */ encounteredEvents.contains(
GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER)
&& !encounteredEvents.contains(GestureEvent.CANCEL_CURRENT_ANIMATION)
&& !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED),
/* errorMessage= */ prefix + "\t\tAbsSwipeUpHandler.cancelCurrentAnimation "
prefix,
/* errorMessage= */ "AbsSwipeUpHandler.cancelCurrentAnimation "
+ "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.",
writer);
@@ -294,23 +334,17 @@ public class ActiveGestureErrorDetector {
/* condition= */ encounteredEvents.contains(
GestureEvent.STATE_RECENTS_ANIMATION_CANCELED)
&& !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT),
/* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED was set but "
prefix,
/* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED was set but "
+ "the task screenshot wasn't cleaned up.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ encounteredEvents.contains(
GestureEvent.SET_END_TARGET_LAST_TASK)
GestureEvent.EXPECTING_TASK_APPEARED)
&& !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
/* errorMessage= */ prefix + "\t\tend target set to last task, but "
+ "onTaskAppeared wasn't called.",
writer);
errorDetected |= printErrorIfTrue(
/* condition= */ encounteredEvents.contains(
GestureEvent.SET_END_TARGET_NEW_TASK)
&& !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
/* errorMessage= */ prefix + "\t\tend target set to new task, but "
+ "onTaskAppeared wasn't called.",
prefix,
/* errorMessage= */ "onTaskAppeared was expected to be called but wasn't.",
writer);
if (!errorDetected) {
@@ -320,11 +354,11 @@ public class ActiveGestureErrorDetector {
}
private static boolean printErrorIfTrue(
boolean condition, String errorMessage, PrintWriter writer) {
boolean condition, String prefix, String errorMessage, PrintWriter writer) {
if (!condition) {
return false;
}
writer.println(errorMessage);
writer.println(prefix + "\t\t- " + errorMessage);
return true;
}
}
@@ -1,107 +0,0 @@
/*
* Copyright (C) 2020 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 static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.view.WindowManager;
import android.view.WindowMetrics;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.R;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.WindowBounds;
import java.util.ArrayList;
/**
* Utility class to hold the information abound a window bounds for split screen
*/
@TargetApi(Build.VERSION_CODES.R)
public class SplitScreenBounds {
public static final SplitScreenBounds INSTANCE = new SplitScreenBounds();
private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
@Nullable
private WindowBounds mBounds;
private SplitScreenBounds() { }
@UiThread
public void setSecondaryWindowBounds(@NonNull WindowBounds bounds) {
if (!bounds.equals(mBounds)) {
mBounds = bounds;
for (OnChangeListener listener : mListeners) {
listener.onSecondaryWindowBoundsChanged();
}
}
}
public @NonNull WindowBounds getSecondaryWindowBounds(Context context) {
if (mBounds == null) {
mBounds = createDefaultWindowBounds(context);
}
return mBounds;
}
/**
* Creates window bounds as 50% of device size
*/
private static WindowBounds createDefaultWindowBounds(Context context) {
WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
WindowBounds bounds = WindowBounds.fromWindowMetrics(wm);
int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
int halfDividerSize = context.getResources()
.getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
if (rotation == ROTATION_0 || rotation == ROTATION_180) {
bounds.bounds.top = bounds.insets.top + bounds.availableSize.y / 2 + halfDividerSize;
bounds.insets.top = 0;
} else {
bounds.bounds.left = bounds.insets.left + bounds.availableSize.x / 2 + halfDividerSize;
bounds.insets.left = 0;
}
return new WindowBounds(bounds.bounds, bounds.insets);
}
public void addOnChangeListener(OnChangeListener listener) {
mListeners.add(listener);
}
public void removeOnChangeListener(OnChangeListener listener) {
mListeners.remove(listener);
}
/**
* Interface to receive window bounds changes
*/
public interface OnChangeListener {
/**
* Called when window bounds for secondary window changes
*/
void onSecondaryWindowBoundsChanged();
}
}
@@ -178,7 +178,6 @@ import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.SurfaceTransactionApplier;
@@ -210,7 +209,7 @@ import java.util.function.Consumer;
public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener {
TaskVisualsChangeListener {
private static final String TAG = "RecentsView";
private static final boolean DEBUG = false;
@@ -948,7 +947,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(
mIPipAnimationListener);
mOrientationState.initListeners();
SplitScreenBounds.INSTANCE.addOnChangeListener(this);
mTaskOverlayFactory.initListeners();
}
@@ -965,7 +963,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
executeSideTaskLaunchCallback();
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(null);
SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
mIPipAnimationListener.setActivityAndRecentsView(null, null);
mOrientationState.destroyListeners();
mTaskOverlayFactory.removeListeners();
@@ -2900,6 +2897,19 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
if (mSplitHiddenTaskView == getRunningTaskView()) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
null /* onFinishComplete */);
} else {
switchToScreenshot(
() -> finishRecentsAnimation(true /* toRecents */,
false /* shouldPip */, null /* onFinishComplete */));
}
}
});
anim.addEndListener(success -> {
if (success) {
InteractionJankMonitorWrapper.end(
@@ -4190,8 +4200,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mSplitSelectStateController.setInitialTaskSelect(taskView.getTask(),
stagePosition, splitEvent, taskView.getItemInfo());
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
null /* onFinishComplete */);
}
/**
@@ -5198,12 +5206,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
return null;
}
@Override
public void onSecondaryWindowBoundsChanged() {
// Invalidate the task view size
setInsets(mInsets);
}
/** Enables or disables modal state for RecentsView */
public abstract void setModalStateEnabled(boolean isModalState, boolean animate);
+3 -1
View File
@@ -367,7 +367,9 @@
<dimen name="transient_taskbar_margin">0dp</dimen>
<dimen name="transient_taskbar_shadow_blur">0dp</dimen>
<dimen name="transient_taskbar_key_shadow_distance">0dp</dimen>
<dimen name="transient_taskbar_icon_spacing">10dp</dimen>
<dimen name="transient_taskbar_stashed_size">0dp</dimen>
<!-- Note that this applies to both sides of all icons, so visible space is double this. -->
<dimen name="transient_taskbar_icon_spacing">0dp</dimen>
<!-- Note that this applies to both sides of all icons, so visible space is double this. -->
<dimen name="taskbar_icon_spacing">8dp</dimen>
<dimen name="taskbar_nav_buttons_size">0dp</dimen>
+11 -34
View File
@@ -402,7 +402,6 @@ public class CellLayout extends ViewGroup {
mCountY = y;
mOccupied = new GridOccupancy(mCountX, mCountY);
mTmpOccupied = new GridOccupancy(mCountX, mCountY);
mTempRectStack.clear();
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
mBorderSpace);
requestLayout();
@@ -1247,21 +1246,6 @@ public class CellLayout extends ViewGroup {
result, resultSpan);
}
private final Stack<Rect> mTempRectStack = new Stack<>();
private void lazyInitTempRectStack() {
if (mTempRectStack.isEmpty()) {
for (int i = 0; i < mCountX * mCountY; i++) {
mTempRectStack.push(new Rect());
}
}
}
private void recycleTempRects(Stack<Rect> used) {
while (!used.isEmpty()) {
mTempRectStack.push(used.pop());
}
}
/**
* Find a vacant area that will fit the given bounds nearest the requested
* cell location. Uses Euclidean distance to score multiple vacant areas.
@@ -1281,8 +1265,6 @@ public class CellLayout extends ViewGroup {
*/
private int[] findNearestArea(int relativeXPos, int relativeYPos, int minSpanX, int minSpanY,
int spanX, int spanY, boolean ignoreOccupied, int[] result, int[] resultSpan) {
lazyInitTempRectStack();
// For items with a spanX / spanY > 1, the passed in point (relativeXPos, relativeYPos)
// corresponds to the center of the item, but we are searching based on the top-left cell,
// so we translate the point over to correspond to the top-left.
@@ -1352,9 +1334,6 @@ public class CellLayout extends ViewGroup {
hitMaxY |= ySize >= spanY;
incX = !incX;
}
incX = true;
hitMaxX = xSize >= spanX;
hitMaxY = ySize >= spanY;
}
final int[] cellXY = mTmpPoint;
cellToCenterPoint(x, y, cellXY);
@@ -1362,8 +1341,7 @@ public class CellLayout extends ViewGroup {
// We verify that the current rect is not a sub-rect of any of our previous
// candidates. In this case, the current rect is disqualified in favour of the
// containing rect.
Rect currentRect = mTempRectStack.pop();
currentRect.set(x, y, x + xSize, y + ySize);
Rect currentRect = new Rect(x, y, x + xSize, y + ySize);
boolean contained = false;
for (Rect r : validRegions) {
if (r.contains(currentRect)) {
@@ -1393,7 +1371,6 @@ public class CellLayout extends ViewGroup {
bestXY[0] = -1;
bestXY[1] = -1;
}
recycleTempRects(validRegions);
return bestXY;
}
@@ -2544,21 +2521,21 @@ public class CellLayout extends ViewGroup {
if (result == null) {
result = new int[]{-1, -1};
}
ItemConfiguration finalSolution;
// When we are checking drop validity or actually dropping, we don't recompute the
// direction vector, since we want the solution to match the preview, and it's possible
// that the exact position of the item has changed to result in a new reordering outcome.
if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP)
&& mPreviousSolution != null) {
ItemConfiguration finalSolution = null;
// We want the solution to match the animation of the preview and to match the drop so we
// only recalculate in mode MODE_SHOW_REORDER_HINT because that the first one to run in the
// reorder cycle.
if (mode == MODE_SHOW_REORDER_HINT || mPreviousSolution == null) {
finalSolution = calculateReorder(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
dragView);
mPreviousSolution = finalSolution;
} else {
finalSolution = mPreviousSolution;
// We reset this vector after drop
if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) {
mPreviousSolution = null;
}
} else {
finalSolution = calculateReorder(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
dragView);
mPreviousSolution = finalSolution;
}
if (finalSolution == null || !finalSolution.isSolution) {
+8 -4
View File
@@ -312,10 +312,14 @@ public class DeviceProfile {
}
if (isTaskbarPresent) {
taskbarSize = DisplayController.isTransientTaskbar(context)
? res.getDimensionPixelSize(R.dimen.transient_taskbar_size)
: res.getDimensionPixelSize(R.dimen.taskbar_size);
stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
if (DisplayController.isTransientTaskbar(context)) {
taskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_size);
stashedTaskbarSize =
res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_size);
} else {
taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
}
}
edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+13 -24
View File
@@ -53,6 +53,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
@@ -183,7 +185,6 @@ import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.ActivityTracker;
@@ -222,7 +223,6 @@ import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.shared.LauncherExterns;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -242,7 +242,7 @@ import java.util.stream.Stream;
*/
public class Launcher extends StatefulActivity<LauncherState>
implements LauncherExterns, Callbacks, InvariantDeviceProfile.OnIDPChangeListener,
PluginListener<LauncherOverlayPlugin>, LauncherOverlayCallbacks {
PluginListener<LauncherOverlayPlugin> {
public static final String TAG = "Launcher";
public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -696,17 +696,9 @@ public class Launcher extends StatefulActivity<LauncherState>
*/
@Override
public void setLauncherOverlay(LauncherOverlay overlay) {
if (overlay != null) {
overlay.setOverlayCallbacks(this);
}
mWorkspace.setLauncherOverlay(overlay);
}
@Override
public void runOnOverlayHidden(Runnable runnable) {
getWorkspace().runOnOverlayHidden(runnable);
}
public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
mLauncherCallbacks = callbacks;
return true;
@@ -1213,18 +1205,6 @@ public class Launcher extends StatefulActivity<LauncherState>
mAppWidgetHolder.setActivityResumed(false);
}
/**
* {@code LauncherOverlayCallbacks} scroll amount.
* Indicates transition progress to -1 screen.
* @param progress From 0 to 1.
*/
@Override
public void onScrollChanged(float progress) {
if (mWorkspace != null) {
mWorkspace.onOverlayScrollChanged(progress);
}
}
/**
* Restores the previous state, if it exists.
*
@@ -2895,7 +2875,16 @@ public class Launcher extends StatefulActivity<LauncherState>
/**
* Informs us that the overlay (-1 screen, typically), has either become visible or invisible.
*/
public void onOverlayVisibilityChanged(boolean visible) {}
public void onOverlayVisibilityChanged(boolean visible) {
getStatsLogManager().logger()
.withSrcState(LAUNCHER_STATE_HOME)
.withDstState(LAUNCHER_STATE_HOME)
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
.setWorkspace(WorkspaceContainer.newBuilder()
.setPageIndex(visible ? 0 : -1))
.build())
.log(visible ? LAUNCHER_SWIPELEFT : LAUNCHER_SWIPERIGHT);
}
/**
* Informs us that the page transition has ended, so that we can react to the newly selected
+43 -129
View File
@@ -26,7 +26,6 @@ import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
@@ -58,7 +57,6 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.Toast;
@@ -119,6 +117,7 @@ import com.android.launcher3.widget.WidgetManagerHelper;
import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
import com.android.launcher3.widget.util.WidgetSizes;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
import java.util.ArrayList;
import java.util.Iterator;
@@ -136,7 +135,7 @@ import java.util.stream.Collectors;
public class Workspace<T extends View & PageIndicator> extends PagedView<T>
implements DropTarget, DragSource, View.OnTouchListener,
DragController.DragListener, Insettable, StateHandler<LauncherState>,
WorkspaceLayoutManager, LauncherBindableItemsContainer {
WorkspaceLayoutManager, LauncherBindableItemsContainer, LauncherOverlayCallbacks {
/** The value that {@link #mTransitionProgress} must be greater than for
* {@link #transitionStateShouldAllowDrop()} to return true. */
@@ -254,14 +253,12 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
// State related to Launcher Overlay
private OverlayEdgeEffect mOverlayEdgeEffect;
boolean mOverlayShown = false;
private Runnable mOnOverlayHiddenCallback;
private boolean mOverlayShown = false;
private float mOverlayProgress; // 1 -> overlay completely visible, 0 -> home visible
private final List<LauncherOverlayCallbacks> mOverlayCallbacks = new ArrayList<>();
private boolean mForceDrawAdjacentPages = false;
// Total over scrollX in the overlay direction.
private float mOverlayTranslation;
// Handles workspace state transitions
private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
@@ -1151,9 +1148,15 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
}
public void setLauncherOverlay(LauncherOverlay overlay) {
mOverlayEdgeEffect = overlay == null ? null : new OverlayEdgeEffect(getContext(), overlay);
EdgeEffectCompat newEffect = overlay == null
? new EdgeEffectCompat(getContext()) : mOverlayEdgeEffect;
final EdgeEffectCompat newEffect;
if (overlay == null) {
newEffect = new EdgeEffectCompat(getContext());
mOverlayEdgeEffect = null;
} else {
newEffect = mOverlayEdgeEffect = new OverlayEdgeEffect(getContext(), overlay);
overlay.setOverlayCallbacks(this);
}
if (mIsRtl) {
mEdgeGlowRight = newEffect;
} else {
@@ -1203,132 +1206,46 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
@Override
protected boolean shouldFlingForVelocity(int velocityX) {
// When the overlay is moving, the fling or settle transition is controlled by the overlay.
return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 &&
super.shouldFlingForVelocity(velocityX);
return Float.compare(Math.abs(mOverlayProgress), 0) == 0
&& super.shouldFlingForVelocity(velocityX);
}
/**
* The overlay scroll is being controlled locally, just update our overlay effect
*/
@Override
public void onOverlayScrollChanged(float scroll) {
if (Float.compare(scroll, 1f) == 0) {
mOverlayProgress = Utilities.boundToRange(scroll, 0, 1);
if (Float.compare(mOverlayProgress, 1f) == 0) {
if (!mOverlayShown) {
mLauncher.getStatsLogManager().logger()
.withSrcState(LAUNCHER_STATE_HOME)
.withDstState(LAUNCHER_STATE_HOME)
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
.setWorkspace(
LauncherAtom.WorkspaceContainer.newBuilder()
.setPageIndex(0))
.build())
.log(LAUNCHER_SWIPELEFT);
mOverlayShown = true;
mLauncher.onOverlayVisibilityChanged(true);
}
mOverlayShown = true;
// Let the Launcher activity know that the overlay is now visible.
mLauncher.onOverlayVisibilityChanged(mOverlayShown);
// Not announcing the overlay page for accessibility since it announces itself.
} else if (Float.compare(scroll, 0f) == 0) {
} else if (Float.compare(mOverlayProgress, 0f) == 0) {
if (mOverlayShown) {
// TODO: this is logged unnecessarily on home gesture.
mLauncher.getStatsLogManager().logger()
.withSrcState(LAUNCHER_STATE_HOME)
.withDstState(LAUNCHER_STATE_HOME)
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
.setWorkspace(
LauncherAtom.WorkspaceContainer.newBuilder()
.setPageIndex(-1))
.build())
.log(LAUNCHER_SWIPERIGHT);
} else if (Float.compare(mOverlayTranslation, 0f) != 0) {
// When arriving to 0 overscroll from non-zero overscroll, announce page for
// accessibility since default announcements were disabled while in overscroll
// state.
// Not doing this if mOverlayShown because in that case the accessibility service
// will announce the launcher window description upon regaining focus after
// switching from the overlay screen.
announcePageForAccessibility();
mOverlayShown = false;
mLauncher.onOverlayVisibilityChanged(false);
}
mOverlayShown = false;
// Let the Launcher activity know that the overlay is no longer visible.
mLauncher.onOverlayVisibilityChanged(mOverlayShown);
tryRunOverlayCallback();
}
float offset = 0f;
scroll = Math.max(scroll - offset, 0);
scroll = Math.min(1, scroll / (1 - offset));
float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll);
float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
if (mIsRtl) {
transX = -transX;
int count = mOverlayCallbacks.size();
for (int i = 0; i < count; i++) {
mOverlayCallbacks.get(i).onOverlayScrollChanged(mOverlayProgress);
}
mOverlayTranslation = transX;
// TODO(adamcohen): figure out a final effect here. We may need to recommend
// different effects based on device performance. On at least one relatively high-end
// device I've tried, translating the launcher causes things to get quite laggy.
mLauncher.getDragLayer().setTranslationX(transX);
mLauncher.getDragLayer().getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
}
/**
* @return false if the callback is still pending
* Adds a callback for receiving overlay progress
*/
private boolean tryRunOverlayCallback() {
if (mOnOverlayHiddenCallback == null) {
// Return true as no callback is pending. This is used by OnWindowFocusChangeListener
// to remove itself if multiple focus handles were added.
return true;
}
if (mOverlayShown || !hasWindowFocus()) {
return false;
}
mOnOverlayHiddenCallback.run();
mOnOverlayHiddenCallback = null;
return true;
public void addOverlayCallback(LauncherOverlayCallbacks callback) {
mOverlayCallbacks.add(callback);
callback.onOverlayScrollChanged(mOverlayProgress);
}
/**
* Runs the given callback when the minus one overlay is hidden. Specifically, it is run
* when launcher's window has focus and the overlay is no longer being shown. If a callback
* is already present, the new callback will chain off it so both are run.
*
* @return Whether the callback was deferred.
* Removes a previously added overlay progress callback
*/
public boolean runOnOverlayHidden(Runnable callback) {
if (mOnOverlayHiddenCallback == null) {
mOnOverlayHiddenCallback = callback;
} else {
// Chain the new callback onto the previous callback(s).
Runnable oldCallback = mOnOverlayHiddenCallback;
mOnOverlayHiddenCallback = () -> {
oldCallback.run();
callback.run();
};
}
if (!tryRunOverlayCallback()) {
ViewTreeObserver observer = getViewTreeObserver();
if (observer != null && observer.isAlive()) {
observer.addOnWindowFocusChangeListener(
new ViewTreeObserver.OnWindowFocusChangeListener() {
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (tryRunOverlayCallback() && observer.isAlive()) {
observer.removeOnWindowFocusChangeListener(this);
}
}});
}
return true;
}
return false;
public void removeOverlayCallback(LauncherOverlayCallbacks callback) {
mOverlayCallbacks.remove(callback);
}
@Override
@@ -2467,23 +2384,20 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
final View child = (mDragInfo == null) ? null : mDragInfo.cell;
int reorderX = mTargetCell[0];
int reorderY = mTargetCell[1];
if (!nearestDropOccupied) {
mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
child, mTargetCell, new int[2], CellLayout.MODE_SHOW_REORDER_HINT);
mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
item.spanX, item.spanY, d);
} else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
&& !mReorderAlarm.alarmPending()
if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
&& (mLastReorderX != reorderX || mLastReorderY != reorderY)
&& targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell, item.spanX,
item.spanY)) {
int[] resultSpan = new int[2];
mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
(int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
child, mTargetCell, new int[2], CellLayout.MODE_SHOW_REORDER_HINT);
}
if (!nearestDropOccupied) {
mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
item.spanX, item.spanY, d);
} else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
&& !mReorderAlarm.alarmPending()) {
// Otherwise, if we aren't adding to or creating a folder and there's no pending
// reorder, then we schedule a reorder
ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
@@ -3470,7 +3384,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
protected boolean canAnnouncePageDescription() {
// Disable announcements while overscrolling potentially to overlay screen because if we end
// up on the overlay screen, it will take care of announcing itself.
return Float.compare(mOverlayTranslation, 0f) == 0;
return Float.compare(mOverlayProgress, 0f) == 0;
}
@Override
@@ -107,8 +107,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
updateHeaderScroll(
((AllAppsRecyclerView) recyclerView).computeVerticalScrollOffset());
updateHeaderScroll(recyclerView.computeVerticalScrollOffset());
}
};
@@ -192,7 +191,6 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
reset(true);
}
}
}
@Override
@@ -815,6 +813,10 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
}
}
protected void onInitializeRecyclerView(RecyclerView rv) {
rv.addOnScrollListener(mScrollListener);
}
/** Holds a {@link BaseAllAppsAdapter} and related fields. */
public class AdapterHolder {
public static final int MAIN = 0;
@@ -851,7 +853,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
mRecyclerView.setHasFixedSize(true);
// No animations will occur when changes occur to the items in this RecyclerView.
mRecyclerView.setItemAnimator(null);
mRecyclerView.addOnScrollListener(mScrollListener);
onInitializeRecyclerView(mRecyclerView);
FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mRecyclerView);
mRecyclerView.addItemDecoration(focusedItemDecorator);
mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
@@ -25,4 +25,11 @@ public class BaseSearchConfig {
public boolean isKeyboardSyncEnabled() {
return false;
}
/**
* Returns whether IME is enabled on swipe up.
*/
public boolean isImeEnabledOnSwipeUp() {
return false;
}
}
@@ -16,19 +16,50 @@
package com.android.launcher3.allapps;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.WindowInsets;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
/**
* AllAppsContainerView with launcher specific callbacks
*/
public class LauncherAllAppsContainerView extends ActivityAllAppsContainerView<Launcher> {
private final RecyclerView.OnScrollListener mActivityScrollListener =
new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
int scrolledOffset = recyclerView.computeVerticalScrollOffset();
ExtendedEditText input = mSearchUiManager.getEditText();
// Scroll up and scroll to top
if (dy < 0 && scrolledOffset == 0 && input != null) {
boolean isImeEnabledOnSwipeUp = Launcher.getLauncher(mActivityContext)
.getSearchConfig().isImeEnabledOnSwipeUp();
if (isImeEnabledOnSwipeUp || !TextUtils.isEmpty(input.getText())) {
input.showKeyboard();
}
}
}
};
@Override
protected void onInitializeRecyclerView(RecyclerView rv) {
super.onInitializeRecyclerView(rv);
if (FeatureFlags.SCROLL_TOP_TO_RESET.get()) {
rv.addOnScrollListener(mActivityScrollListener);
}
}
public LauncherAllAppsContainerView(Context context) {
this(context, null);
}
@@ -96,6 +96,10 @@ public final class FeatureFlags {
"ENABLE_QUICK_LAUNCH_V2", false, "Use quick launch v2 "
+ "behavior. Quick search and quick launch v1 would be unavailable if this is enabled");
public static final BooleanFlag GBOARD_UPDATE_ENTER_KEY = new DeviceFlag(
"GBOARD_UPDATE_ENTER_KEY", false, "Update gBoard enter key "
+ "icon dynamically based on top search content for Quick Launch V2");
public static final BooleanFlag ENABLE_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER",
true, "Hide header on keyboard before typing in all apps");
@@ -290,6 +294,10 @@ public final class FeatureFlags {
public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
"ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
public static final BooleanFlag SCROLL_TOP_TO_RESET = new DeviceFlag(
"SCROLL_TOP_TO_RESET", false, "Bring up IME and focus on "
+ "input when scroll to top if 'Always show keyboard' is enabled or in prefix state");
public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION = getDebugFlag(
"SHOW_DELIGHTFUL_PAGINATION", false,
"Enable showing the new 'delightful pagination' which is a brand"
@@ -47,7 +47,9 @@ import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
@@ -56,13 +58,14 @@ import com.android.launcher3.graphics.Scrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
import java.util.ArrayList;
/**
* A ViewGroup that coordinates dragging across its descendants
*/
public class DragLayer extends BaseDragLayer<Launcher> {
public class DragLayer extends BaseDragLayer<Launcher> implements LauncherOverlayCallbacks {
public static final int ALPHA_INDEX_OVERLAY = 0;
private static final int ALPHA_CHANNEL_COUNT = 1;
@@ -70,6 +73,8 @@ public class DragLayer extends BaseDragLayer<Launcher> {
public static final int ANIMATION_END_DISAPPEAR = 0;
public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
private final boolean mIsRtl;
private DragController mDragController;
// Variables relating to animation of views after drop
@@ -100,6 +105,7 @@ public class DragLayer extends BaseDragLayer<Launcher> {
setChildrenDrawingOrderEnabled(true);
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
mIsRtl = Utilities.isRtl(getResources());
}
/**
@@ -109,6 +115,7 @@ public class DragLayer extends BaseDragLayer<Launcher> {
mDragController = dragController;
recreateControllers();
mWorkspaceDragScrim = new Scrim(this);
workspace.addOverlayCallback(this);
}
@Override
@@ -476,4 +483,16 @@ public class DragLayer extends BaseDragLayer<Launcher> {
controller.onOneHandedModeStateChanged(activated);
}
}
@Override
public void onOverlayScrollChanged(float progress) {
float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(progress);
float transX = getMeasuredWidth() * progress;
if (mIsRtl) {
transX = -transX;
}
setTranslationX(transX);
getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
}
}
@@ -47,7 +47,9 @@ import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -214,8 +216,7 @@ public class TestInformationHandler implements ResourceBasedOverride {
}
case TestProtocol.REQUEST_HAS_TIS: {
response.putBoolean(
TestProtocol.REQUEST_HAS_TIS, false);
response.putBoolean(TestProtocol.REQUEST_HAS_TIS, false);
return response;
}
@@ -266,17 +267,24 @@ public class TestInformationHandler implements ResourceBasedOverride {
*/
private static <S, T> Bundle getUIProperty(
BundleSetter<T> bundleSetter, Function<S, T> provider, Supplier<S> targetSupplier) {
return getFromExecutorSync(MAIN_EXECUTOR, () -> {
S target = targetSupplier.get();
if (target == null) {
return null;
}
T value = provider.apply(target);
Bundle response = new Bundle();
bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value);
return response;
});
}
/**
* Executes the callback on the executor and waits for the result
*/
protected static <T> T getFromExecutorSync(ExecutorService executor, Callable<T> callback) {
try {
return MAIN_EXECUTOR.submit(() -> {
S target = targetSupplier.get();
if (target == null) {
return null;
}
T value = provider.apply(target);
Bundle response = new Bundle();
bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value);
return response;
}).get();
return executor.submit(callback).get();
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
@@ -128,6 +128,7 @@ public final class TestProtocol {
public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing";
public static final String REQUEST_ENABLE_ROTATION = "enable_rotation";
public static final String REQUEST_ENABLE_SUGGESTION = "enable-suggestion";
public static final String REQUEST_MODEL_QUEUE_CLEARED = "model-queue-cleared";
public static boolean sDebugTracing = false;
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
@@ -40,10 +40,4 @@ public interface LauncherExterns {
* Sets the overlay on the target activity
*/
void setLauncherOverlay(LauncherOverlay overlay);
/**
* Executes the command, next time the overlay is hidden
*/
void runOnOverlayHidden(Runnable runnable);
}
@@ -93,6 +93,6 @@ public interface LauncherOverlayManager extends Application.ActivityLifecycleCal
interface LauncherOverlayCallbacks {
void onScrollChanged(float progress);
void onOverlayScrollChanged(float progress);
}
}
+1
View File
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application android:debuggable="true" android:extractNativeLibs="true">
<uses-library android:name="android.test.runner"/>
@@ -29,7 +29,6 @@ import static org.junit.Assume.assumeTrue;
import android.content.Intent;
import android.graphics.Point;
import android.os.SystemClock;
import android.platform.test.annotations.IwTest;
import androidx.test.filters.LargeTest;
@@ -479,7 +478,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
@Test
@PortraitLandscape
public void testUninstallFromWorkspace() throws Exception {
TestUtil.installDummyApp();
installDummyAppAndWaitForUIUpdate();
try {
verifyAppUninstalledFromAllApps(
createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
@@ -492,10 +491,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
@PortraitLandscape
@ScreenRecord // (b/256659409)
public void testUninstallFromAllApps() throws Exception {
TestUtil.installDummyApp();
installDummyAppAndWaitForUIUpdate();
try {
// b/256659409
SystemClock.sleep(5000);
Workspace workspace = mLauncher.getWorkspace();
final HomeAllApps allApps = workspace.switchToAllApps();
allApps.freeze();
@@ -539,7 +536,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
Point[] gridPositions = getCornersAndCenterPositions();
createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]);
createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]);
TestUtil.installDummyApp();
installDummyAppAndWaitForUIUpdate();
try {
createShortcutIfNotExist(DUMMY_APP_NAME, gridPositions[2]);
Map<String, Point> initialPositions =
@@ -590,6 +587,17 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME));
}
private void installDummyAppAndWaitForUIUpdate() throws IOException {
TestUtil.installDummyApp();
// Wait for model thread completion as it may be processing
// the install event from the SystemService
mLauncher.waitForModelQueueCleared();
// Wait for Launcher UI thread completion, as it may be processing updating the UI in
// response to the model update. Not that `waitForLauncherInitialized` is just a proxy
// method, we can use any method which touches Launcher UI thread,
mLauncher.waitForLauncherInitialized();
}
/**
* @return List of workspace grid coordinates. Those are not pixels. See {@link
* Workspace#getIconGridDimensions()}
@@ -17,8 +17,13 @@ package com.android.launcher3.util;
import static androidx.test.InstrumentationRegistry.getContext;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static androidx.test.InstrumentationRegistry.getTargetContext;
import android.content.pm.LauncherApps;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import androidx.test.uiautomator.UiDevice;
@@ -27,6 +32,7 @@ import org.junit.Assert;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
public class TestUtil {
public static final String DUMMY_PACKAGE = "com.example.android.aardwolf";
@@ -40,24 +46,77 @@ public class TestUtil {
final String apkFilename = getInstrumentation().getTargetContext().
getFilesDir().getPath() + "/dummy_app.apk";
final FileOutputStream out = new FileOutputStream(apkFilename);
byte[] buff = new byte[1024];
int read;
try (PackageInstallCheck pic = new PackageInstallCheck()) {
final FileOutputStream out = new FileOutputStream(apkFilename);
byte[] buff = new byte[1024];
int read;
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
in.close();
out.close();
final String result = UiDevice.getInstance(getInstrumentation())
.executeShellCommand("pm install " + apkFilename);
Assert.assertTrue(
"Failed to install wellbeing test apk; make sure the device is rooted",
"Success".equals(result.replaceAll("\\s+", "")));
pic.mAddWait.await();
} catch (InterruptedException e) {
throw new IOException(e);
}
in.close();
out.close();
final String result = UiDevice.getInstance(getInstrumentation())
.executeShellCommand("pm install " + apkFilename);
Assert.assertTrue("Failed to install wellbeing test apk; make sure the device is rooted",
"Success".equals(result.replaceAll("\\s+", "")));
}
public static void uninstallDummyApp() throws IOException {
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
"pm uninstall " + DUMMY_PACKAGE);
}
private static class PackageInstallCheck extends LauncherApps.Callback
implements AutoCloseable {
final CountDownLatch mAddWait = new CountDownLatch(1);
final LauncherApps mLauncherApps;
PackageInstallCheck() {
mLauncherApps = getTargetContext().getSystemService(LauncherApps.class);
mLauncherApps.registerCallback(this, new Handler(Looper.getMainLooper()));
}
private void verifyPackage(String packageName) {
if (DUMMY_PACKAGE.equals(packageName)) {
mAddWait.countDown();
}
}
@Override
public void onPackageAdded(String packageName, UserHandle user) {
verifyPackage(packageName);
}
@Override
public void onPackageChanged(String packageName, UserHandle user) {
verifyPackage(packageName);
}
@Override
public void onPackageRemoved(String packageName, UserHandle user) { }
@Override
public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
for (String packageName : packageNames) {
verifyPackage(packageName);
}
}
@Override
public void onPackagesUnavailable(String[] packageNames, UserHandle user,
boolean replacing) { }
@Override
public void close() {
mLauncherApps.unregisterCallback(this);
}
}
}
@@ -848,6 +848,10 @@ public final class LauncherInstrumentation {
}
}
public void waitForModelQueueCleared() {
getTestInfo(TestProtocol.REQUEST_MODEL_QUEUE_CLEARED);
}
public void waitForLauncherInitialized() {
for (int i = 0; i < 100; ++i) {
if (getTestInfo(