Merge "Moving some notification binder calls to worker thread" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index 3d1d996..5def65f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -200,7 +200,7 @@
$(LOCAL_PATH)/quickstep/recents_ui_overrides/res
LOCAL_FULL_LIBS_MANIFEST_FILES := \
- $(LOCAL_PATH)/AndroidManifest.xml \
+ $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
@@ -247,7 +247,7 @@
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/go/AndroidManifest.xml \
- $(LOCAL_PATH)/AndroidManifest.xml \
+ $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
@@ -293,7 +293,7 @@
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/go/AndroidManifest.xml \
- $(LOCAL_PATH)/AndroidManifest.xml \
+ $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/go/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
new file mode 100644
index 0000000..0c60468
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -0,0 +1,46 @@
+/**
+ * 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.
+ * 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.BaseQuickstepLauncher;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.SysUINavigationMode;
+
+import java.util.ArrayList;
+
+public class QuickstepLauncher extends BaseQuickstepLauncher {
+
+ public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
+
+ @Override
+ public TouchController[] createTouchControllers() {
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(getDragController());
+
+ if (getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new LandscapeStatesTouchController(this));
+ list.add(new LandscapeEdgeSwipeController(this));
+ } else {
+ boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(this)
+ .getMode().hasGestures;
+ list.add(new PortraitStatesTouchController(this, allowDragToOverview));
+ }
+ return list.toArray(new TouchController[list.size()]);
+ }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
deleted file mode 100644
index d5ea1ec..0000000
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.
- * 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.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.graphics.RotationMode;
-import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
-import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
-import com.android.launcher3.util.TouchController;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.views.IconRecentsView;
-
-import java.util.ArrayList;
-
-/**
- * Provides recents-related {@link UiFactory} logic and classes.
- */
-public abstract class RecentsUiFactory {
-
- public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
-
- public static TouchController[] createTouchControllers(Launcher launcher) {
- ArrayList<TouchController> list = new ArrayList<>();
- list.add(launcher.getDragController());
-
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new LandscapeStatesTouchController(launcher));
- list.add(new LandscapeEdgeSwipeController(launcher));
- } else {
- boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(launcher)
- .getMode().hasGestures;
- list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));
- }
- if (Utilities.IS_DEBUG_DEVICE
- && !launcher.getDeviceProfile().isMultiWindowMode
- && !launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new StatusBarTouchController(launcher));
- }
- return list.toArray(new TouchController[list.size()]);
- }
-
- /**
- * Creates and returns the controller responsible for recents view state transitions.
- *
- * @param launcher the launcher activity
- * @return state handler for recents
- */
- public static StateHandler createRecentsViewStateController(Launcher launcher) {
- return new RecentsViewStateController(launcher);
- }
-
- /**
- * Clean-up logic that occurs when recents is no longer in use/visible.
- *
- * @param launcher the launcher activity
- */
- public static void resetOverview(Launcher launcher) {
- IconRecentsView recentsView = launcher.getOverviewPanel();
- recentsView.setTransitionedFromApp(false);
- }
-
- /**
- * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
- *
- * @param launcher the launcher activity
- */
- public static void onLauncherStateOrResumeChanged(Launcher launcher) {}
-
- public static RotationMode getRotationMode(DeviceProfile dp) {
- return RotationMode.NORMAL;
- }
-}
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
new file mode 100644
index 0000000..60afddb
--- /dev/null
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+** 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.
+*/
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.launcher3">
+ <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+ <!--
+ Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
+ Refer comments around specific entries on how to extend individual components.
+ -->
+
+ <application
+ android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+ android:fullBackupOnly="true"
+ android:fullBackupContent="@xml/backupscheme"
+ android:hardwareAccelerated="true"
+ android:icon="@drawable/ic_launcher_home"
+ android:label="@string/derived_app_name"
+ android:theme="@style/AppTheme"
+ android:largeHeap="@bool/config_largeHeap"
+ android:restoreAnyVersion="true"
+ android:supportsRtl="true" >
+
+ <!--
+ Main launcher activity. When extending only change the name, and keep all the
+ attributes and intent filters the same
+ -->
+ <activity
+ android:name="com.android.launcher3.uioverrides.QuickstepLauncher"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:stateNotNeeded="true"
+ android:windowSoftInputMode="adjustPan"
+ android:screenOrientation="unspecified"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:resizeableActivity="true"
+ android:resumeWhilePausing="true"
+ android:taskAffinity=""
+ android:enabled="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.HOME" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.MONKEY"/>
+ <category android:name="android.intent.category.LAUNCHER_APP" />
+ </intent-filter>
+ <meta-data
+ android:name="com.android.launcher3.grid.control"
+ android:value="${packageName}.grid_control" />
+ </activity>
+
+ </application>
+</manifest>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
similarity index 76%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 2a22e9d..43cdbdb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +13,6 @@
* 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;
@@ -21,13 +20,14 @@
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.view.Gravity;
+import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
@@ -49,21 +49,15 @@
import java.util.ArrayList;
-/**
- * Provides recents-related {@link UiFactory} logic and classes.
- */
-public abstract class RecentsUiFactory {
-
- private static final String TAG = RecentsUiFactory.class.getSimpleName();
+public class QuickstepLauncher extends BaseQuickstepLauncher {
public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
/**
* Reusable command for applying the shelf height on the background thread.
*/
- public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) -> {
- SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
- };
+ public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
+ SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
@Override
@@ -138,71 +132,78 @@
}
};
- public static RotationMode getRotationMode(DeviceProfile dp) {
+ @Override
+ protected RotationMode getFakeRotationMode(DeviceProfile dp) {
return !dp.isVerticalBarLayout() ? RotationMode.NORMAL
: (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE);
}
- public static TouchController[] createTouchControllers(Launcher launcher) {
- Mode mode = SysUINavigationMode.getMode(launcher);
-
- ArrayList<TouchController> list = new ArrayList<>();
- list.add(launcher.getDragController());
- if (mode == NO_BUTTON) {
- list.add(new QuickSwitchTouchController(launcher));
- list.add(new NavBarToHomeTouchController(launcher));
- list.add(new FlingAndHoldTouchController(launcher));
- } else {
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new OverviewToAllAppsTouchController(launcher));
- list.add(new LandscapeEdgeSwipeController(launcher));
- if (mode.hasGestures) {
- list.add(new TransposedQuickSwitchTouchController(launcher));
- }
- } else {
- list.add(new PortraitStatesTouchController(launcher,
- mode.hasGestures /* allowDragToOverview */));
- if (mode.hasGestures) {
- list.add(new QuickSwitchTouchController(launcher));
- }
- }
- }
-
- if (!launcher.getDeviceProfile().isMultiWindowMode) {
- list.add(new StatusBarTouchController(launcher));
- }
-
- list.add(new LauncherTaskViewController(launcher));
- return list.toArray(new TouchController[list.size()]);
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ onStateOrResumeChanged();
}
- /**
- * Creates and returns the controller responsible for recents view state transitions.
- *
- * @param launcher the launcher activity
- * @return state handler for recents
- */
- public static StateHandler createRecentsViewStateController(Launcher launcher) {
- return new RecentsViewStateController(launcher);
+ @Override
+ protected void onActivityFlagsChanged(int changeBits) {
+ super.onActivityFlagsChanged(changeBits);
+
+ if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
+ | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0
+ && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0) {
+ onStateOrResumeChanged();
+ }
}
/**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
- *
- * @param launcher the launcher activity
*/
- public static void onLauncherStateOrResumeChanged(Launcher launcher) {
- LauncherState state = launcher.getStateManager().getState();
- DeviceProfile profile = launcher.getDeviceProfile();
- boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+ private void onStateOrResumeChanged() {
+ LauncherState state = getStateManager().getState();
+ DeviceProfile profile = getDeviceProfile();
+ boolean visible = (state == NORMAL || state == OVERVIEW) && isUserActive()
&& !profile.isVerticalBarLayout();
- UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT, visible ? 1 : 0,
+ UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
profile.hotseatBarSizePx);
if (state == NORMAL) {
- launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
+ ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
}
}
+ @Override
+ public TouchController[] createTouchControllers() {
+ Mode mode = SysUINavigationMode.getMode(this);
+
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(getDragController());
+ if (mode == NO_BUTTON) {
+ list.add(new QuickSwitchTouchController(this));
+ list.add(new NavBarToHomeTouchController(this));
+ list.add(new FlingAndHoldTouchController(this));
+ } else {
+ if (getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new OverviewToAllAppsTouchController(this));
+ list.add(new LandscapeEdgeSwipeController(this));
+ if (mode.hasGestures) {
+ list.add(new TransposedQuickSwitchTouchController(this));
+ }
+ } else {
+ list.add(new PortraitStatesTouchController(this,
+ mode.hasGestures /* allowDragToOverview */));
+ if (mode.hasGestures) {
+ list.add(new QuickSwitchTouchController(this));
+ }
+ }
+ }
+
+ if (!getDeviceProfile().isMultiWindowMode) {
+ list.add(new StatusBarTouchController(this));
+ }
+
+ list.add(new LauncherTaskViewController(this));
+ return list.toArray(new TouchController[list.size()]);
+ }
+
private static final class LauncherTaskViewController extends
TaskViewTouchController<Launcher> {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index ee2e951..626292e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -35,12 +35,12 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -51,6 +51,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.quickstep.SystemUiProxy;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.views.RecentsView;
@@ -106,8 +107,7 @@
}
});
mPeekAnim.start();
- recentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1,
peekDuration, 0);
@@ -173,7 +173,7 @@
}
@Override
- public void onDragEnd(float velocity, boolean fling) {
+ public void onDragEnd(float velocity) {
if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
if (mPeekAnim != null) {
mPeekAnim.cancel();
@@ -196,7 +196,7 @@
});
overviewAnim.start();
} else {
- super.onDragEnd(velocity, fling);
+ super.onDragEnd(velocity);
}
View searchView = mLauncher.getAppsView().getSearchView();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index d66af1a..738436a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -43,7 +43,7 @@
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.TouchController;
@@ -52,12 +52,13 @@
/**
* Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.
*/
-public class NavBarToHomeTouchController implements TouchController, SwipeDetector.Listener {
+public class NavBarToHomeTouchController implements TouchController,
+ SingleAxisSwipeDetector.Listener {
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
private final Launcher mLauncher;
- private final SwipeDetector mSwipeDetector;
+ private final SingleAxisSwipeDetector mSwipeDetector;
private final float mPullbackDistance;
private boolean mNoIntercept;
@@ -67,7 +68,8 @@
public NavBarToHomeTouchController(Launcher launcher) {
mLauncher = launcher;
- mSwipeDetector = new SwipeDetector(mLauncher, this, SwipeDetector.VERTICAL);
+ mSwipeDetector = new SingleAxisSwipeDetector(mLauncher, this,
+ SingleAxisSwipeDetector.VERTICAL);
mPullbackDistance = mLauncher.getResources().getDimension(R.dimen.home_pullback_distance);
}
@@ -79,7 +81,8 @@
if (mNoIntercept) {
return false;
}
- mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+ mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_POSITIVE,
+ false /* ignoreSlop */);
}
if (mNoIntercept) {
@@ -173,7 +176,8 @@
}
@Override
- public void onDragEnd(float velocity, boolean fling) {
+ public void onDragEnd(float velocity) {
+ boolean fling = mSwipeDetector.isFling(velocity);
final int logAction = fling ? Touch.FLING : Touch.SWIPE;
float progress = mCurrentAnimation.getProgressFraction();
float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(progress);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 5c3b55d..a4ac1b0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -42,7 +42,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.quickstep.SysUINavigationMode;
@@ -59,10 +59,10 @@
private @Nullable TaskView mTaskToLaunch;
public QuickSwitchTouchController(Launcher launcher) {
- this(launcher, SwipeDetector.HORIZONTAL);
+ this(launcher, SingleAxisSwipeDetector.HORIZONTAL);
}
- protected QuickSwitchTouchController(Launcher l, SwipeDetector.Direction dir) {
+ protected QuickSwitchTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
super(l, dir);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 00e4f58..ad02de1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -19,6 +19,9 @@
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator;
@@ -32,7 +35,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.BaseSwipeDetector;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.launcher3.util.PendingAnimation;
@@ -46,15 +50,14 @@
* Touch controller for handling task view card swipes
*/
public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
- extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener {
-
- private static final String TAG = "OverviewSwipeController";
+ extends AnimatorListenerAdapter implements TouchController,
+ SingleAxisSwipeDetector.Listener {
// Progress after which the transition is assumed to be a success in case user does not fling
public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
protected final T mActivity;
- private final SwipeDetector mDetector;
+ private final SingleAxisSwipeDetector mDetector;
private final RecentsView mRecentsView;
private final int[] mTempCords = new int[2];
@@ -74,7 +77,7 @@
public TaskViewTouchController(T activity) {
mActivity = activity;
mRecentsView = activity.getOverviewPanel();
- mDetector = new SwipeDetector(activity, this, SwipeDetector.VERTICAL);
+ mDetector = new SingleAxisSwipeDetector(activity, this, SingleAxisSwipeDetector.VERTICAL);
}
private boolean canInterceptTouch() {
@@ -113,7 +116,7 @@
int directionsToDetectScroll = 0;
boolean ignoreSlopWhenSettling = false;
if (mCurrentAnimation != null) {
- directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ directionsToDetectScroll = DIRECTION_BOTH;
ignoreSlopWhenSettling = true;
} else {
mTaskBeingDragged = null;
@@ -126,12 +129,12 @@
if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
// Don't allow swipe down to open if we don't support swipe up
// to enter overview.
- directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+ directionsToDetectScroll = DIRECTION_POSITIVE;
} else {
// The task can be dragged up to dismiss it,
// and down to open if it's the current page.
directionsToDetectScroll = i == mRecentsView.getCurrentPage()
- ? SwipeDetector.DIRECTION_BOTH : SwipeDetector.DIRECTION_POSITIVE;
+ ? DIRECTION_BOTH : DIRECTION_POSITIVE;
}
break;
}
@@ -165,8 +168,8 @@
return;
}
int scrollDirections = mDetector.getScrollDirections();
- if (goingUp && ((scrollDirections & SwipeDetector.DIRECTION_POSITIVE) == 0)
- || !goingUp && ((scrollDirections & SwipeDetector.DIRECTION_NEGATIVE) == 0)) {
+ if (goingUp && ((scrollDirections & DIRECTION_POSITIVE) == 0)
+ || !goingUp && ((scrollDirections & DIRECTION_NEGATIVE) == 0)) {
// Trying to re-init in an unsupported direction.
return;
}
@@ -243,7 +246,8 @@
}
@Override
- public void onDragEnd(float velocity, boolean fling) {
+ public void onDragEnd(float velocity) {
+ boolean fling = mDetector.isFling(velocity);
final boolean goingToEnd;
final int logAction;
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
@@ -260,7 +264,7 @@
logAction = Touch.SWIPE;
goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
}
- long animationDuration = SwipeDetector.calculateDuration(
+ long animationDuration = BaseSwipeDetector.calculateDuration(
velocity, goingToEnd ? (1 - progress) : progress);
if (blockedFling && !goingToEnd) {
animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
index f1e4041..0ed5291 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
@@ -17,12 +17,12 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
public class TransposedQuickSwitchTouchController extends QuickSwitchTouchController {
public TransposedQuickSwitchTouchController(Launcher launcher) {
- super(launcher, SwipeDetector.VERTICAL);
+ super(launcher, SingleAxisSwipeDetector.VERTICAL);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 939656e..1d00825 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,15 +15,12 @@
*/
package com.android.quickstep;
-import static android.os.VibrationEffect.EFFECT_CLICK;
-import static android.os.VibrationEffect.createPredefined;
-
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.Animator;
@@ -38,9 +35,6 @@
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.provider.Settings;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -55,6 +49,7 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -96,6 +91,7 @@
protected float mDragLengthFactor = 1;
protected final Context mContext;
+ protected final RecentsAnimationDeviceState mDeviceState;
protected final GestureState mGestureState;
protected final OverviewComponentObserver mOverviewComponentObserver;
protected final BaseActivityInterface<T> mActivityInterface;
@@ -105,9 +101,6 @@
protected final AppWindowAnimationHelper mAppWindowAnimationHelper;
protected final TransformParams mTransformParams = new TransformParams();
- private final Vibrator mVibrator;
- protected final Mode mMode;
-
// Shift in the range of [0, 1].
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
@@ -135,10 +128,11 @@
protected boolean mCanceled;
protected int mFinishingRecentsAnimationForNewTaskId = -1;
- protected BaseSwipeUpHandler(Context context, GestureState gestureState,
- OverviewComponentObserver overviewComponentObserver,
+ protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
+ GestureState gestureState, OverviewComponentObserver overviewComponentObserver,
RecentsModel recentsModel, InputConsumerController inputConsumer, int runningTaskId) {
mContext = context;
+ mDeviceState = deviceState;
mGestureState = gestureState;
mOverviewComponentObserver = overviewComponentObserver;
mActivityInterface = gestureState.getActivityInterface();
@@ -147,29 +141,15 @@
mActivityInterface.createActivityInitListener(this::onActivityInit);
mRunningTaskId = runningTaskId;
mInputConsumer = inputConsumer;
- mMode = SysUINavigationMode.getMode(context);
mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
- mVibrator = context.getSystemService(Vibrator.class);
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext));
}
protected void performHapticFeedback() {
- if (!mVibrator.hasVibrator()) {
- return;
- }
- if (Settings.System.getInt(
- mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 0) {
- return;
- }
-
- VibrationEffect effect = createPredefined(EFFECT_CLICK);
- if (effect == null) {
- return;
- }
- UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(effect));
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
}
public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode rotationMode) {
@@ -348,7 +328,7 @@
mAppWindowAnimationHelper.updateHomeBounds(getStackBounds(dp));
}
mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
- if (mMode == Mode.NO_BUTTON) {
+ if (mDeviceState.isFullyGesturalNavMode()) {
// We can drag all the way to the top of the screen.
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index 3d1ecef..f889bc1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -75,6 +75,9 @@
@Override
public void onSwipeUpToRecentsComplete() {
RecentsActivity activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
RecentsView recentsView = activity.getOverviewPanel();
recentsView.getClearAllButton().setVisibilityAlpha(1);
recentsView.setDisallowScrollToClearAll(false);
@@ -236,12 +239,18 @@
public void onLaunchTaskFailed() {
// TODO: probably go back to overview instead.
RecentsActivity activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
activity.<RecentsView>getOverviewPanel().startHome();
}
@Override
public void onLaunchTaskSuccess() {
RecentsActivity activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
activity.onTaskLaunched();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 4406314..48b8fc6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -94,6 +94,9 @@
@Override
public void onTransitionCancelled(boolean activityVisible) {
Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
LauncherState startState = launcher.getStateManager().getRestState();
launcher.getStateManager().goToState(startState, activityVisible);
}
@@ -102,32 +105,40 @@
public void onSwipeUpToRecentsComplete() {
// Re apply state in case we did something funky during the transition.
Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
launcher.getStateManager().reapplyState();
DiscoveryBounce.showForOverviewIfNeeded(launcher);
}
@Override
public void onSwipeUpToHomeComplete() {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
// Ensure recents is at the correct position for NORMAL state. For example, when we detach
// recents, we assume the first task is invisible, making translation off by one task.
- Launcher launcher = getCreatedActivity();
launcher.getStateManager().reapplyState();
setLauncherHideBackArrow(false);
}
private void setLauncherHideBackArrow(boolean hideBackArrow) {
Launcher launcher = getCreatedActivity();
- if (launcher != null) {
- launcher.getRootView().setForceHideBackArrow(hideBackArrow);
+ if (launcher == null) {
+ return;
}
+ launcher.getRootView().setForceHideBackArrow(hideBackArrow);
}
@Override
public void onAssistantVisibilityChanged(float visibility) {
Launcher launcher = getCreatedActivity();
- if (launcher != null) {
- launcher.onAssistantVisibilityChanged(visibility);
+ if (launcher == null) {
+ return;
}
+ launcher.onAssistantVisibilityChanged(visibility);
}
@NonNull
@@ -476,12 +487,18 @@
@Override
public void onLaunchTaskFailed() {
Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
launcher.getStateManager().goToState(OVERVIEW);
}
@Override
public void onLaunchTaskSuccess() {
Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
launcher.getStateManager().moveToRestState();
}
@@ -503,6 +520,9 @@
public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
Runnable onFinishRunnable) {
Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
RecentsView recentsView = launcher.getOverviewPanel();
if (recentsView == null) {
if (onFinishRunnable != null) {
@@ -516,8 +536,9 @@
@Override
public void setOnDeferredActivityLaunchCallback(Runnable r) {
Launcher launcher = getCreatedActivity();
- if (launcher != null) {
- launcher.setOnDeferredActivityLaunchCallback(r);
+ if (launcher == null) {
+ return;
}
+ launcher.setOnDeferredActivityLaunchCallback(r);
}
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
index ce533a6..19c289d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -112,11 +112,6 @@
@Override
protected boolean isLauncherInitialized() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "isLauncherInitialized.TouchInteractionService.isInitialized=" +
- TouchInteractionService.isInitialized());
- }
return super.isLauncherInitialized() && TouchInteractionService.isInitialized();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index 17457aa..b5441df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -19,11 +19,11 @@
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.graphics.Matrix;
-import android.view.View;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.views.TaskThumbnailView;
@@ -40,30 +40,30 @@
public class TaskOverlayFactory implements ResourceBasedOverride {
/** Note that these will be shown in order from top to bottom, if available for the task. */
- private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
- new TaskSystemShortcut.AppInfo(),
- new TaskSystemShortcut.SplitScreen(),
- new TaskSystemShortcut.Pin(),
- new TaskSystemShortcut.Install(),
- new TaskSystemShortcut.Freeform()
+ private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
+ TaskShortcutFactory.APP_INFO,
+ TaskShortcutFactory.SPLIT_SCREEN,
+ TaskShortcutFactory.PIN,
+ TaskShortcutFactory.INSTALL,
+ TaskShortcutFactory.FREE_FORM,
+ TaskShortcutFactory.WELLBEING
};
- public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
- forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
-
- public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
- final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
+ public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
+ final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
- for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
- View.OnClickListener onClickListener =
- menuOption.getOnClickListener(activity, taskView);
- if (onClickListener != null) {
- shortcuts.add(menuOption);
+ for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
+ SystemShortcut shortcut = menuOption.getShortcut(activity, taskView);
+ if (shortcut != null) {
+ shortcuts.add(shortcut);
}
}
return shortcuts;
}
+ public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
+ forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
+
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
return new TaskOverlay();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
new file mode 100644
index 0000000..9ba2e5a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
@@ -0,0 +1,315 @@
+/*
+ * 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;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.view.View;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.WellbeingModel;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.popup.SystemShortcut.AppInfo;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.InstantAppResolver;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.ActivityCompat;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Represents a system shortcut that can be shown for a recent task.
+ */
+public interface TaskShortcutFactory {
+
+ SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
+
+ static WorkspaceItemInfo dummyInfo(TaskView view) {
+ Task task = view.getTask();
+
+ WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
+ dummyInfo.intent = new Intent();
+ ComponentName component = task.getTopComponent();
+ dummyInfo.intent.setComponent(component);
+ dummyInfo.user = UserHandle.of(task.key.userId);
+ dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
+ return dummyInfo;
+ }
+
+ TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, dummyInfo(view));
+
+ abstract class MultiWindowFactory implements TaskShortcutFactory {
+
+ private final int mIconRes;
+ private final int mTextRes;
+
+ MultiWindowFactory(int iconRes, int textRes) {
+ mIconRes = iconRes;
+ mTextRes = textRes;
+ }
+
+ protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
+ protected abstract ActivityOptions makeLaunchOptions(Activity activity);
+ protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
+
+ @Override
+ public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
+ final Task task = taskView.getTask();
+ if (!task.isDockable) {
+ return null;
+ }
+ if (!isAvailable(activity, task.key.displayId)) {
+ return null;
+ }
+ return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this);
+ }
+ }
+
+ class MultiWindowSystemShortcut extends SystemShortcut {
+
+ private Handler mHandler;
+
+ private final RecentsView mRecentsView;
+ private final TaskThumbnailView mThumbnailView;
+ private final TaskView mTaskView;
+ private final MultiWindowFactory mFactory;
+
+ public MultiWindowSystemShortcut(int iconRes, int textRes,
+ BaseDraggingActivity activity, TaskView taskView, MultiWindowFactory factory) {
+ super(iconRes, textRes, activity, dummyInfo(taskView));
+
+ mHandler = new Handler(Looper.getMainLooper());
+ mTaskView = taskView;
+ mRecentsView = activity.getOverviewPanel();
+ mThumbnailView = taskView.getThumbnail();
+ mFactory = factory;
+ }
+
+ @Override
+ public void onClick(View view) {
+ Task.TaskKey taskKey = mTaskView.getTask().key;
+ final int taskId = taskKey.id;
+
+ final View.OnLayoutChangeListener onLayoutChangeListener =
+ new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int l, int t, int r, int b,
+ int oldL, int oldT, int oldR, int oldB) {
+ mTaskView.getRootView().removeOnLayoutChangeListener(this);
+ mRecentsView.clearIgnoreResetTask(taskId);
+
+ // Start animating in the side pages once launcher has been resized
+ mRecentsView.dismissTask(mTaskView, false, false);
+ }
+ };
+
+ final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
+ new DeviceProfile.OnDeviceProfileChangeListener() {
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ mTarget.removeOnDeviceProfileChangeListener(this);
+ if (dp.isMultiWindowMode) {
+ mTaskView.getRootView().addOnLayoutChangeListener(
+ onLayoutChangeListener);
+ }
+ }
+ };
+
+ dismissTaskMenuView(mTarget);
+
+ ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
+ if (options != null
+ && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
+ options)) {
+ if (!mFactory.onActivityStarted(mTarget)) {
+ return;
+ }
+ // Add a device profile change listener to kick off animating the side tasks
+ // once we enter multiwindow mode and relayout
+ mTarget.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
+
+ final Runnable animStartedListener = () -> {
+ // Hide the task view and wait for the window to be resized
+ // TODO: Consider animating in launcher and do an in-place start activity
+ // afterwards
+ mRecentsView.setIgnoreResetTask(taskId);
+ mTaskView.setAlpha(0f);
+ };
+
+ final int[] position = new int[2];
+ mThumbnailView.getLocationOnScreen(position);
+ final int width = (int) (mThumbnailView.getWidth() * mTaskView.getScaleX());
+ final int height = (int) (mThumbnailView.getHeight() * mTaskView.getScaleY());
+ final Rect taskBounds = new Rect(position[0], position[1],
+ position[0] + width, position[1] + height);
+
+ // Take the thumbnail of the task without a scrim and apply it back after
+ float alpha = mThumbnailView.getDimAlpha();
+ mThumbnailView.setDimAlpha(0);
+ Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
+ taskBounds.width(), taskBounds.height(), mThumbnailView, 1f,
+ Color.BLACK);
+ mThumbnailView.setDimAlpha(alpha);
+
+ AppTransitionAnimationSpecsFuture future =
+ new AppTransitionAnimationSpecsFuture(mHandler) {
+ @Override
+ public List<AppTransitionAnimationSpecCompat> composeSpecs() {
+ return Collections.singletonList(new AppTransitionAnimationSpecCompat(
+ taskId, thumbnail, taskBounds));
+ }
+ };
+ WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
+ future, animStartedListener, mHandler, true /* scaleUp */,
+ taskKey.displayId);
+ }
+ }
+ }
+
+ TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(
+ R.drawable.ic_split_screen, R.string.recent_task_option_split_screen) {
+
+ @Override
+ protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
+ // Don't show menu-item if already in multi-window and the task is from
+ // the secondary display.
+ // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
+ // implementation is enabled
+ return !activity.getDeviceProfile().isMultiWindowMode
+ && (displayId == -1 || displayId == DEFAULT_DISPLAY);
+ }
+
+ @Override
+ protected ActivityOptions makeLaunchOptions(Activity activity) {
+ final ActivityCompat act = new ActivityCompat(activity);
+ final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
+ act.getDisplayId());
+ if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
+ return null;
+ }
+ boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
+ return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
+ }
+
+ @Override
+ protected boolean onActivityStarted(BaseDraggingActivity activity) {
+ SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
+ activity.getUserEventDispatcher().logActionOnControl(TAP,
+ LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
+ return true;
+ }
+ };
+
+ TaskShortcutFactory FREE_FORM = new MultiWindowFactory(
+ R.drawable.ic_split_screen, R.string.recent_task_option_freeform) {
+
+ @Override
+ protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
+ return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
+ }
+
+ @Override
+ protected ActivityOptions makeLaunchOptions(Activity activity) {
+ ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
+ // Arbitrary bounds only because freeform is in dev mode right now
+ Rect r = new Rect(50, 50, 200, 200);
+ activityOptions.setLaunchBounds(r);
+ return activityOptions;
+ }
+
+ @Override
+ protected boolean onActivityStarted(BaseDraggingActivity activity) {
+ activity.returnToHomescreen();
+ return true;
+ }
+ };
+
+ TaskShortcutFactory PIN = (activity, tv) -> {
+ if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
+ return null;
+ }
+ if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
+ return null;
+ }
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+ // We shouldn't be able to pin while an app is locked.
+ return null;
+ }
+ return new PinSystemShortcut(activity, tv);
+ };
+
+ class PinSystemShortcut extends SystemShortcut {
+
+ private static final String TAG = "PinSystemShortcut";
+
+ private final TaskView mTaskView;
+
+ public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
+ super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, dummyInfo(tv));
+ mTaskView = tv;
+ }
+
+ @Override
+ public void onClick(View view) {
+ Consumer<Boolean> resultCallback = success -> {
+ if (success) {
+ SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(
+ mTaskView.getTask().key.id);
+ } else {
+ mTaskView.notifyTaskLaunchFailed(TAG);
+ }
+ };
+ mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
+ dismissTaskMenuView(mTarget);
+ }
+ }
+
+ TaskShortcutFactory INSTALL = (activity, view) ->
+ InstantAppResolver.newInstance(activity).isInstantApp(activity,
+ view.getTask().getTopComponent().getPackageName())
+ ? new SystemShortcut.Install(activity, dummyInfo(view)) : null;
+
+ TaskShortcutFactory WELLBEING = (activity, view) ->
+ WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, dummyInfo(view));
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
deleted file mode 100644
index 5a2e3ff..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
+++ /dev/null
@@ -1,327 +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.quickstep;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.view.View;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.InstantAppResolver;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskThumbnailView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Represents a system shortcut that can be shown for a recent task.
- */
-public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
-
- private static final String TAG = "TaskSystemShortcut";
-
- protected T mSystemShortcut;
-
- public TaskSystemShortcut(T systemShortcut) {
- super(systemShortcut);
- mSystemShortcut = systemShortcut;
- }
-
- protected TaskSystemShortcut(int iconResId, int labelResId) {
- super(iconResId, labelResId);
- }
-
- @Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- return null;
- }
-
- public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
- Task task = view.getTask();
-
- WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
- dummyInfo.intent = new Intent();
- ComponentName component = task.getTopComponent();
- dummyInfo.intent.setComponent(component);
- dummyInfo.user = UserHandle.of(task.key.userId);
- dummyInfo.title = TaskUtils.getTitle(activity, task);
-
- return getOnClickListenerForTask(activity, task, dummyInfo);
- }
-
- protected View.OnClickListener getOnClickListenerForTask(
- BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) {
- return mSystemShortcut.getOnClickListener(activity, dummyInfo);
- }
-
- public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
- public AppInfo() {
- super(new SystemShortcut.AppInfo());
- }
- }
-
- public static abstract class MultiWindow extends TaskSystemShortcut {
-
- private Handler mHandler;
-
- public MultiWindow(int iconRes, int textRes) {
- super(iconRes, textRes);
- mHandler = new Handler(Looper.getMainLooper());
- }
-
- protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
- protected abstract ActivityOptions makeLaunchOptions(Activity activity);
- protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
-
- @Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, TaskView taskView) {
- final Task task = taskView.getTask();
- final int taskId = task.key.id;
- final int displayId = task.key.displayId;
- if (!task.isDockable) {
- return null;
- }
- if (!isAvailable(activity, displayId)) {
- return null;
- }
- final RecentsView recentsView = activity.getOverviewPanel();
-
- final TaskThumbnailView thumbnailView = taskView.getThumbnail();
- return (v -> {
- final View.OnLayoutChangeListener onLayoutChangeListener =
- new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int l, int t, int r, int b,
- int oldL, int oldT, int oldR, int oldB) {
- taskView.getRootView().removeOnLayoutChangeListener(this);
- recentsView.clearIgnoreResetTask(taskId);
-
- // Start animating in the side pages once launcher has been resized
- recentsView.dismissTask(taskView, false, false);
- }
- };
-
- final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
- new DeviceProfile.OnDeviceProfileChangeListener() {
- @Override
- public void onDeviceProfileChanged(DeviceProfile dp) {
- activity.removeOnDeviceProfileChangeListener(this);
- if (dp.isMultiWindowMode) {
- taskView.getRootView().addOnLayoutChangeListener(
- onLayoutChangeListener);
- }
- }
- };
-
- dismissTaskMenuView(activity);
-
- ActivityOptions options = makeLaunchOptions(activity);
- if (options != null
- && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
- options)) {
- if (!onActivityStarted(activity)) {
- return;
- }
- // Add a device profile change listener to kick off animating the side tasks
- // once we enter multiwindow mode and relayout
- activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
-
- final Runnable animStartedListener = () -> {
- // Hide the task view and wait for the window to be resized
- // TODO: Consider animating in launcher and do an in-place start activity
- // afterwards
- recentsView.setIgnoreResetTask(taskId);
- taskView.setAlpha(0f);
- };
-
- final int[] position = new int[2];
- thumbnailView.getLocationOnScreen(position);
- final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
- final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
- final Rect taskBounds = new Rect(position[0], position[1],
- position[0] + width, position[1] + height);
-
- // Take the thumbnail of the task without a scrim and apply it back after
- float alpha = thumbnailView.getDimAlpha();
- thumbnailView.setDimAlpha(0);
- Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
- taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
- Color.BLACK);
- thumbnailView.setDimAlpha(alpha);
-
- AppTransitionAnimationSpecsFuture future =
- new AppTransitionAnimationSpecsFuture(mHandler) {
- @Override
- public List<AppTransitionAnimationSpecCompat> composeSpecs() {
- return Collections.singletonList(new AppTransitionAnimationSpecCompat(
- taskId, thumbnail, taskBounds));
- }
- };
- WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
- future, animStartedListener, mHandler, true /* scaleUp */, displayId);
- }
- });
- }
- }
-
- public static class SplitScreen extends MultiWindow {
- public SplitScreen() {
- super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
- }
-
- @Override
- protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
- // Don't show menu-item if already in multi-window and the task is from
- // the secondary display.
- // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
- // implementation is enabled
- return !activity.getDeviceProfile().isMultiWindowMode
- && (displayId == -1 || displayId == DEFAULT_DISPLAY);
- }
-
- @Override
- protected ActivityOptions makeLaunchOptions(Activity activity) {
- final ActivityCompat act = new ActivityCompat(activity);
- final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
- act.getDisplayId());
- if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
- return null;
- }
- boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
- return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
- }
-
- @Override
- protected boolean onActivityStarted(BaseDraggingActivity activity) {
- SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
- activity.getUserEventDispatcher().logActionOnControl(TAP,
- LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
- return true;
- }
- }
-
- public static class Freeform extends MultiWindow {
- public Freeform() {
- super(R.drawable.ic_split_screen, R.string.recent_task_option_freeform);
- }
-
- @Override
- protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
- return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
- }
-
- @Override
- protected ActivityOptions makeLaunchOptions(Activity activity) {
- ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
- // Arbitrary bounds only because freeform is in dev mode right now
- Rect r = new Rect(50, 50, 200, 200);
- activityOptions.setLaunchBounds(r);
- return activityOptions;
- }
-
- @Override
- protected boolean onActivityStarted(BaseDraggingActivity activity) {
- activity.returnToHomescreen();
- return true;
- }
- }
-
- public static class Pin extends TaskSystemShortcut {
-
- private static final String TAG = Pin.class.getSimpleName();
-
- private Handler mHandler;
-
- public Pin() {
- super(R.drawable.ic_pin, R.string.recent_task_option_pin);
- mHandler = new Handler(Looper.getMainLooper());
- }
-
- @Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, TaskView taskView) {
- if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
- return null;
- }
- if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
- return null;
- }
- if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
- // We shouldn't be able to pin while an app is locked.
- return null;
- }
- return view -> {
- Consumer<Boolean> resultCallback = success -> {
- if (success) {
- SystemUiProxy.INSTANCE.get(activity).startScreenPinning(
- taskView.getTask().key.id);
- } else {
- taskView.notifyTaskLaunchFailed(TAG);
- }
- };
- taskView.launchTask(true, resultCallback, mHandler);
- dismissTaskMenuView(activity);
- };
- }
- }
-
- public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
- public Install() {
- super(new SystemShortcut.Install());
- }
-
- @Override
- protected View.OnClickListener getOnClickListenerForTask(
- BaseDraggingActivity activity, Task task, ItemInfo itemInfo) {
- if (InstantAppResolver.newInstance(activity).isInstantApp(activity,
- task.getTopComponent().getPackageName())) {
- return mSystemShortcut.createOnClickListener(activity, itemInfo);
- }
- return null;
- }
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index e3fcd2f..b2626e5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -35,6 +35,7 @@
import android.app.Service;
import android.app.TaskInfo;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
@@ -49,33 +50,32 @@
import android.view.MotionEvent;
import androidx.annotation.BinderThread;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
import com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer;
import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
+import com.android.quickstep.inputconsumers.OverscrollInputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
-import com.android.quickstep.inputconsumers.QuickCaptureInputConsumer;
import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.systemui.plugins.OverscrollPlugin;
+import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -112,8 +112,7 @@
* Service connected by system-UI for handling touch interaction.
*/
@TargetApi(Build.VERSION_CODES.Q)
-public class TouchInteractionService extends Service implements
- NavigationModeChangeListener {
+public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin> {
private static final String TAG = "TouchInteractionService";
@@ -122,6 +121,8 @@
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
private int mBackGestureNotificationCounter = -1;
+ @Nullable
+ private OverscrollPlugin mOverscrollPlugin;
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@@ -134,9 +135,6 @@
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
});
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "TIS initialized");
- }
sIsInitialized = true;
}
@@ -265,20 +263,18 @@
private InputMonitorCompat mInputMonitorCompat;
private InputEventReceiver mInputEventReceiver;
- private Mode mMode = Mode.THREE_BUTTONS;
@Override
public void onCreate() {
super.onCreate();
- mDeviceState = new RecentsAnimationDeviceState(this);
- mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
-
// Initialize anything here that is needed in direct boot mode.
// Everything else should be initialized in onUserUnlocked() below.
mMainChoreographer = Choreographer.getInstance();
mAM = ActivityManagerWrapper.getInstance();
+ mDeviceState = new RecentsAnimationDeviceState(this);
+ mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
+ mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
- onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
sConnected = true;
}
@@ -301,7 +297,7 @@
Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 1");
}
disposeEventHandlers();
- if (!mMode.hasGestures || !SystemUiProxy.INSTANCE.get(this).isActive()) {
+ if (mDeviceState.isButtonNavMode() || !SystemUiProxy.INSTANCE.get(this).isActive()) {
return;
}
if (TestProtocol.sDebugTracing) {
@@ -320,12 +316,10 @@
mDeviceState.updateGestureTouchRegions();
}
- @Override
- public void onNavigationModeChanged(Mode newMode) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onNavigationModeChanged " + newMode);
- }
- mMode = newMode;
+ /**
+ * Called when the navigation mode changes, guaranteed to be after the device state has updated.
+ */
+ private void onNavigationModeChanged(SysUINavigationMode.Mode mode) {
initInputMonitor();
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
}
@@ -348,6 +342,9 @@
mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
.getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
+
+ PluginManagerWrapper.INSTANCE.get(getBaseContext()).addPluginListener(this,
+ OverscrollPlugin.class, false /* allowMultiple */);
}
private void onDeferredActivityLaunch() {
@@ -362,7 +359,7 @@
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
- if (!mDeviceState.isUserUnlocked() || !mMode.hasGestures) {
+ if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
// mode doesn't have gestures
return;
@@ -397,9 +394,8 @@
@Override
public void onDestroy() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "TIS destroyed");
- }
+ PluginManagerWrapper.INSTANCE.get(getBaseContext()).removePluginListener(this);
+
sIsInitialized = false;
if (mDeviceState.isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
@@ -407,7 +403,6 @@
}
disposeEventHandlers();
mDeviceState.destroy();
- SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
SystemUiProxy.INSTANCE.get(this).setProxy(null);
sConnected = false;
@@ -428,6 +423,9 @@
Log.e(TAG, "Unknown event " + ev);
return;
}
+ if (!mDeviceState.isUserUnlocked()) {
+ return;
+ }
Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
@@ -443,7 +441,8 @@
ActiveGestureLog.INSTANCE.addLog("setInputConsumer", mConsumer.getType());
mUncheckedConsumer = mConsumer;
- } else if (mDeviceState.isUserUnlocked() && mMode == Mode.NO_BUTTON
+ } else if (mDeviceState.isUserUnlocked()
+ && mDeviceState.isFullyGesturalNavMode()
&& mDeviceState.canTriggerAssistantAction(event)) {
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we should
// not interrupt it. QuickSwitch assumes that interruption can only happen if the
@@ -484,15 +483,15 @@
|| previousGestureState.isRecentsAnimationRunning()
? newBaseConsumer(previousGestureState, newGestureState, event)
: mResetGestureInputConsumer;
- if (mMode == Mode.NO_BUTTON) {
+ if (mDeviceState.isFullyGesturalNavMode()) {
if (mDeviceState.canTriggerAssistantAction(event)) {
base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat);
}
- if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
- // Put the Compose gesture as higher priority than the Assistant or base gestures
- base = new QuickCaptureInputConsumer(this, newGestureState, base,
- mInputMonitorCompat);
+ if (mOverscrollPlugin != null) {
+ // Put the overscroll gesture as higher priority than the Assistant or base gestures
+ base = new OverscrollInputConsumer(this, newGestureState, base, mInputMonitorCompat,
+ mOverscrollPlugin);
}
if (mDeviceState.isScreenPinningActive()) {
@@ -531,9 +530,9 @@
() -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) {
final ComponentName homeComponent =
- mOverviewComponentObserver.getHomeIntent().getComponent();
+ mOverviewComponentObserver.getHomeIntent().getComponent();
forceOverviewInputConsumer =
- runningTaskInfo.baseIntent.getComponent(). equals(homeComponent);
+ runningTaskInfo.baseIntent.getComponent().equals(homeComponent);
}
}
@@ -574,7 +573,8 @@
final boolean shouldDefer;
final BaseSwipeUpHandler.Factory factory;
- if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+ if (mDeviceState.isFullyGesturalNavMode()
+ && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
shouldDefer = previousGestureState.getFinishingRecentsAnimationTaskId() < 0;
factory = mFallbackNoButtonFactory;
} else {
@@ -591,7 +591,7 @@
private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState,
RunningTaskInfo taskInfo) {
- if (mMode == Mode.NO_BUTTON && taskInfo != null) {
+ if (mDeviceState.isFullyGesturalNavMode() && taskInfo != null) {
return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
gestureState, mInputMonitorCompat, taskInfo.taskId);
} else {
@@ -631,7 +631,7 @@
if (!mDeviceState.isUserUnlocked()) {
return;
}
- if (!mMode.hasGestures && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+ if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
// Prevent the overview from being started before the real home on first boot.
return;
}
@@ -698,7 +698,6 @@
// Dump everything
mDeviceState.dump(pw);
pw.println("TouchState:");
- pw.println(" navMode=" + mMode);
boolean resumed = mOverviewComponentObserver != null
&& mOverviewComponentObserver.getActivityInterface().isResumed();
pw.println(" resumed=" + resumed);
@@ -738,9 +737,9 @@
private BaseSwipeUpHandler createFallbackNoButtonSwipeHandler(GestureState gestureState,
RunningTaskInfo runningTask, long touchTimeMs, boolean continuingLastGesture,
boolean isLikelyToStartNewTask) {
- return new FallbackNoButtonInputConsumer(this, gestureState, mOverviewComponentObserver,
- runningTask, mRecentsModel, mInputConsumer, isLikelyToStartNewTask,
- continuingLastGesture);
+ return new FallbackNoButtonInputConsumer(this, mDeviceState, gestureState,
+ mOverviewComponentObserver, runningTask, mRecentsModel, mInputConsumer,
+ isLikelyToStartNewTask, continuingLastGesture);
}
protected boolean shouldNotifyBackGesture() {
@@ -763,4 +762,14 @@
UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
.startRecentsActivity(intent, null, listener, null, null));
}
+
+ @Override
+ public void onPluginConnected(OverscrollPlugin overscrollPlugin, Context context) {
+ mOverscrollPlugin = overscrollPlugin;
+ }
+
+ @Override
+ public void onPluginDisconnected(OverscrollPlugin overscrollPlugin) {
+ mOverscrollPlugin = null;
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 22ad180..065f3eb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -72,7 +72,6 @@
import com.android.quickstep.BaseActivityInterface.AnimationFactory.ShelfAnimState;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
-import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AppWindowAnimationHelper.TargetAlphaProvider;
@@ -157,9 +156,7 @@
*/
private static final int LOG_NO_OP_PAGE_INDEX = -1;
- private final RecentsAnimationDeviceState mDeviceState;
private final TaskAnimationManager mTaskAnimationManager;
- private final GestureState mGestureState;
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim mRunningWindowAnim;
@@ -198,11 +195,9 @@
RunningTaskInfo runningTaskInfo, long touchTimeMs,
OverviewComponentObserver overviewComponentObserver, boolean continuingLastGesture,
InputConsumerController inputConsumer, RecentsModel recentsModel) {
- super(context, gestureState, overviewComponentObserver, recentsModel, inputConsumer,
- runningTaskInfo.id);
- mDeviceState = deviceState;
+ super(context, deviceState, gestureState, overviewComponentObserver, recentsModel,
+ inputConsumer, runningTaskInfo.id);
mTaskAnimationManager = taskAnimationManager;
- mGestureState = gestureState;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
initStateCallbacks();
@@ -444,7 +439,7 @@
* Note this method has no effect unless the navigation mode is NO_BUTTON.
*/
private void maybeUpdateRecentsAttachedState(boolean animate) {
- if (mMode != Mode.NO_BUTTON || mRecentsView == null) {
+ if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
return;
}
RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets == null
@@ -546,7 +541,7 @@
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed;
- if (mMode != Mode.NO_BUTTON) {
+ if (!mDeviceState.isFullyGesturalNavMode()) {
performHapticFeedback();
}
}
@@ -730,7 +725,7 @@
if (!isFling) {
if (isCancel) {
endTarget = LAST_TASK;
- } else if (mMode == Mode.NO_BUTTON) {
+ } else if (mDeviceState.isFullyGesturalNavMode()) {
if (mIsShelfPeeking) {
endTarget = RECENTS;
} else if (goingToNewTask) {
@@ -751,9 +746,9 @@
boolean willGoToNewTaskOnSwipeUp =
goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
- if (mMode == Mode.NO_BUTTON && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
+ if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
endTarget = HOME;
- } else if (mMode == Mode.NO_BUTTON && isSwipeUp && !mIsShelfPeeking) {
+ } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !mIsShelfPeeking) {
// If swiping at a diagonal, base end target on the faster velocity.
endTarget = NEW_TASK;
} else if (isSwipeUp) {
@@ -793,7 +788,7 @@
float minFlingVelocity = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_min_velocity);
if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
- if (endTarget == RECENTS && mMode != Mode.NO_BUTTON) {
+ if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) {
Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
startShift, endShift, endShift, endVelocity / 1000,
mTransitionDragLength, mContext);
@@ -839,7 +834,7 @@
}
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
- if (mMode == Mode.NO_BUTTON) {
+ if (mDeviceState.isFullyGesturalNavMode()) {
setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
}
} else if (endTarget == NEW_TASK || endTarget == LAST_TASK) {
@@ -1009,7 +1004,10 @@
@Override
public void onConsumerAboutToBeSwitched() {
- if (!mGestureState.isRunningAnimationToLauncher()) {
+ if (mActivity != null) {
+ mActivity.setOnStartCallback(null);
+ }
+ if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) {
cancelCurrentAnimation();
} else {
reset();
@@ -1088,7 +1086,9 @@
mRecentsView.onGestureAnimationEnd();
// Reset the callback for deferred activity launches
- mActivityInterface.setOnDeferredActivityLaunchCallback(null);
+ if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mActivityInterface.setOnDeferredActivityLaunchCallback(null);
+ }
mActivity.getRootView().setOnApplyWindowInsetsListener(null);
removeLiveTileOverlay();
}
@@ -1193,8 +1193,7 @@
}
mRecentsView.onSwipeUpAnimationSuccess();
- RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
-
+ SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
doLogGesture(RECENTS);
reset();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
index 152b9c9..7b24bd9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
@@ -50,6 +50,7 @@
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.RectFSpringAnim;
@@ -112,13 +113,13 @@
private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
private RunningWindowAnim mFinishAnimation;
- public FallbackNoButtonInputConsumer(Context context, GestureState gestureState,
- OverviewComponentObserver overviewComponentObserver,
+ public FallbackNoButtonInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
+ GestureState gestureState, OverviewComponentObserver overviewComponentObserver,
RunningTaskInfo runningTaskInfo, RecentsModel recentsModel,
InputConsumerController inputConsumer,
boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
- super(context, gestureState, overviewComponentObserver, recentsModel, inputConsumer,
- runningTaskInfo.id);
+ super(context, deviceState, gestureState, overviewComponentObserver, recentsModel,
+ inputConsumer, runningTaskInfo.id);
mLauncherAlpha.value = 1;
mRunningTaskInfo = runningTaskInfo;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index c479250..aeab4b5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -54,8 +54,6 @@
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationDeviceState;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
@@ -85,7 +83,6 @@
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
private final RunningTaskInfo mRunningTask;
private final InputMonitorCompat mInputMonitorCompat;
- private final SysUINavigationMode.Mode mMode;
private final BaseActivityInterface mActivityInterface;
private final BaseSwipeUpHandler.Factory mHandlerFactory;
@@ -137,7 +134,6 @@
mGestureState = gestureState;
mMainThreadHandler = new Handler(Looper.getMainLooper());
mRunningTask = runningTaskInfo;
- mMode = SysUINavigationMode.getMode(base);
mHandlerFactory = handlerFactory;
mActivityInterface = mGestureState.getActivityInterface();
@@ -293,7 +289,7 @@
mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
}
- if (mMode == Mode.NO_BUTTON) {
+ if (mDeviceState.isFullyGesturalNavMode()) {
mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/QuickCaptureInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
similarity index 74%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/QuickCaptureInputConsumer.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
index 9826b3a..e3da98b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/QuickCaptureInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
@@ -24,39 +24,28 @@
import static com.android.launcher3.Utilities.squaredHypot;
-import android.app.ActivityOptions;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
import android.graphics.PointF;
-import android.os.Bundle;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.R;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.views.RecentsView;
+import com.android.systemui.plugins.OverscrollPlugin;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
- * Input consumer for handling events to launch quick capture from launcher
+ * Input consumer for handling events to pass to an {@code OverscrollPlugin}.
+ *
* @param <T> Draggable activity subclass used by RecentsView
*/
-public class QuickCaptureInputConsumer<T extends BaseDraggingActivity>
- extends DelegateInputConsumer {
+public class OverscrollInputConsumer<T extends BaseDraggingActivity> extends DelegateInputConsumer {
- private static final String TAG = "QuickCaptureInputConsumer";
-
- private static final String QUICK_CAPTURE_PACKAGE = "com.google.auxe.compose";
- private static final String QUICK_CAPTURE_PACKAGE_DEV = "com.google.auxe.compose.debug";
-
- private static final String EXTRA_DEVICE_STATE = "deviceState";
- private static final String DEVICE_STATE_LOCKED = "Locked";
- private static final String DEVICE_STATE_LAUNCHER = "Launcher";
- private static final String DEVICE_STATE_APP = "App";
- private static final String DEVICE_STATE_UNKNOWN = "Unknown";
+ private static final String TAG = "OverscrollInputConsumer";
private static final int ANGLE_THRESHOLD = 35; // Degrees
@@ -71,14 +60,16 @@
private final Context mContext;
private final GestureState mGestureState;
+ @Nullable private final OverscrollPlugin mPlugin;
private RecentsView mRecentsView;
- public QuickCaptureInputConsumer(Context context, GestureState gestureState,
- InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ public OverscrollInputConsumer(Context context, GestureState gestureState,
+ InputConsumer delegate, InputMonitorCompat inputMonitor, OverscrollPlugin plugin) {
super(delegate, inputMonitor);
mContext = context;
mGestureState = gestureState;
+ mPlugin = plugin;
float slop = ViewConfiguration.get(context).getScaledTouchSlop();
mSquaredSlop = slop * slop;
@@ -89,7 +80,7 @@
@Override
public int getType() {
- return TYPE_QUICK_CAPTURE | mDelegate.getType();
+ return TYPE_OVERSCROLL | mDelegate.getType();
}
private boolean onActivityInit(Boolean alreadyOnHome) {
@@ -149,7 +140,7 @@
mPassedSlop = true;
mStartDragPos.set(mLastPos.x, mLastPos.y);
- if (isValidQuickCaptureGesture()) {
+ if (isOverscrolled()) {
setActive(ev);
} else {
mState = STATE_DELEGATE_ACTIVE;
@@ -161,8 +152,8 @@
}
case ACTION_CANCEL:
case ACTION_UP:
- if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop) {
- startQuickCapture();
+ if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop && mPlugin != null) {
+ mPlugin.onOverscroll(getDeviceState());
}
mPassedSlop = false;
@@ -175,7 +166,7 @@
}
}
- private boolean isValidQuickCaptureGesture() {
+ private boolean isOverscrolled() {
// Make sure there isn't an app to quick switch to on our right
boolean atRightMostApp = (mRecentsView == null || mRecentsView.getRunningTaskIndex() <= 0);
@@ -187,37 +178,19 @@
return atRightMostApp && angleInBounds;
}
- private void startQuickCapture() {
- // Inspect our delegate's type to figure out where the user invoked Compose
- String deviceState = DEVICE_STATE_UNKNOWN;
+ private String getDeviceState() {
+ String deviceState = OverscrollPlugin.DEVICE_STATE_UNKNOWN;
int consumerType = mDelegate.getType();
if (((consumerType & InputConsumer.TYPE_OVERVIEW) > 0)
|| ((consumerType & InputConsumer.TYPE_OVERVIEW_WITHOUT_FOCUS)) > 0) {
- deviceState = DEVICE_STATE_LAUNCHER;
+ deviceState = OverscrollPlugin.DEVICE_STATE_LAUNCHER;
} else if ((consumerType & InputConsumer.TYPE_OTHER_ACTIVITY) > 0) {
- deviceState = DEVICE_STATE_APP;
+ deviceState = OverscrollPlugin.DEVICE_STATE_APP;
} else if (((consumerType & InputConsumer.TYPE_RESET_GESTURE) > 0)
|| ((consumerType & InputConsumer.TYPE_DEVICE_LOCKED) > 0)) {
- deviceState = DEVICE_STATE_LOCKED;
+ deviceState = OverscrollPlugin.DEVICE_STATE_LOCKED;
}
- // Then launch the app
- PackageManager pm = mContext.getPackageManager();
-
- Intent qcIntent = pm.getLaunchIntentForPackage(QUICK_CAPTURE_PACKAGE);
-
- if (qcIntent == null) {
- // If we couldn't find the regular app, try the dev version
- qcIntent = pm.getLaunchIntentForPackage(QUICK_CAPTURE_PACKAGE_DEV);
- }
-
- if (qcIntent != null) {
- qcIntent.putExtra(EXTRA_DEVICE_STATE, deviceState);
-
- Bundle options = ActivityOptions.makeCustomAnimation(mContext, R.anim.slide_in_right,
- 0).toBundle();
-
- mContext.startActivity(qcIntent, options);
- }
+ return deviceState;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
index bbb318a..e2524b1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
@@ -15,8 +15,8 @@
*/
package com.android.quickstep.util;
-import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_LANDSCAPE;
-import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_SEASCAPE;
+import static com.android.launcher3.uioverrides.QuickstepLauncher.ROTATION_LANDSCAPE;
+import static com.android.launcher3.uioverrides.QuickstepLauncher.ROTATION_SEASCAPE;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 5a65c15..ca33605 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -378,10 +378,10 @@
}
@Override
- public void resetTaskVisuals() {
- super.resetTaskVisuals();
+ public void setContentAlpha(float alpha) {
+ super.setContentAlpha(alpha);
if (mRecentsExtraViewContainer != null) {
- mRecentsExtraViewContainer.setAlpha(mContentAlpha);
+ mRecentsExtraViewContainer.setAlpha(alpha);
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
index 07d0796..80022b4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,7 +16,6 @@
package com.android.quickstep.views;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -26,7 +25,6 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
@@ -41,16 +39,13 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.views.IconView.OnScaleUpdateListener;
-import java.util.List;
-
/**
* Contains options for a recent task when long-pressing its icon.
*/
@@ -197,22 +192,15 @@
params.topMargin = (int) -mThumbnailTopMargin;
mTaskIcon.setLayoutParams(params);
- final BaseDraggingActivity activity = BaseDraggingActivity.fromContext(getContext());
- final List<TaskSystemShortcut> shortcuts =
- TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(taskView);
- final int count = shortcuts.size();
- for (int i = 0; i < count; ++i) {
- final TaskSystemShortcut menuOption = shortcuts.get(i);
- addMenuOption(menuOption, menuOption.getOnClickListener(activity, taskView));
- }
+ TaskOverlayFactory.getEnabledShortcuts(taskView).forEach(this::addMenuOption);
}
- private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
+ private void addMenuOption(SystemShortcut menuOption) {
ViewGroup menuOptionView = (ViewGroup) mActivity.getLayoutInflater().inflate(
R.layout.task_view_menu_option, this, false);
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
- menuOptionView.setOnClickListener(onClickListener);
+ menuOptionView.setOnClickListener(menuOption);
mOptionLayout.addView(menuOptionView);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index bfb9613..0dfc39b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -53,6 +53,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.popup.SystemShortcut;
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;
@@ -61,7 +62,6 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.TaskCornerRadius;
@@ -713,15 +713,8 @@
getContext().getText(R.string.accessibility_close_task)));
final Context context = getContext();
- final List<TaskSystemShortcut> shortcuts =
- TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
- final int count = shortcuts.size();
- for (int i = 0; i < count; ++i) {
- final TaskSystemShortcut menuOption = shortcuts.get(i);
- OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
- if (onClickListener != null) {
- info.addAction(menuOption.createAccessibilityAction(context));
- }
+ for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
+ info.addAction(s.createAccessibilityAction(context));
}
if (mDigitalWellBeingToast.hasLimit()) {
@@ -752,16 +745,9 @@
return true;
}
- final List<TaskSystemShortcut> shortcuts =
- TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
- final int count = shortcuts.size();
- for (int i = 0; i < count; ++i) {
- final TaskSystemShortcut menuOption = shortcuts.get(i);
- if (menuOption.hasHandlerForAction(action)) {
- OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
- if (onClickListener != null) {
- onClickListener.onClick(this);
- }
+ for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
+ if (s.hasHandlerForAction(action)) {
+ s.onClick(this);
return true;
}
}
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 98aaceb..5d9a009 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -33,4 +33,6 @@
<!-- Assistant Gesture -->
<integer name="assistant_gesture_min_time_threshold">200</integer>
<integer name="assistant_gesture_corner_deg_threshold">20</integer>
+
+ <string name="wellbeing_provider_pkg" translatable="false"></string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
new file mode 100644
index 0000000..fc9cfcd
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ * 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;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
+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.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
+
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.model.WellbeingModel;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.proxy.ProxyActivityStarter;
+import com.android.launcher3.proxy.StartActivityParams;
+import com.android.launcher3.uioverrides.BackButtonAlphaHandler;
+import com.android.launcher3.uioverrides.RecentsViewStateController;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.RemoteFadeOutAnimationListener;
+
+import java.util.stream.Stream;
+
+/**
+ * Extension of Launcher activity to provide quickstep specific functionality
+ */
+public abstract class BaseQuickstepLauncher extends Launcher
+ implements NavigationModeChangeListener {
+
+ /**
+ * Reusable command for applying the back button alpha on the background thread.
+ */
+ public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
+ (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(
+ Float.intBitsToFloat(arg1), arg2 != 0);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ SysUINavigationMode.Mode mode = SysUINavigationMode.INSTANCE.get(this)
+ .addModeChangeListener(this);
+ getRotationHelper().setRotationHadDifferentUI(mode != Mode.NO_BUTTON);
+
+ if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
+ getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateTransitionStart(LauncherState toState) { }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
+ .get(BaseQuickstepLauncher.this).getMode().hasGestures;
+ LauncherState prevState = getStateManager().getLastState();
+
+ if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
+ && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT
+ <= getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
+ getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
+ getStateManager().removeStateListener(this);
+ }
+ }
+ });
+ }
+
+ if (!getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
+ getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateTransitionStart(LauncherState toState) { }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ LauncherState prevState = getStateManager().getLastState();
+
+ if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT
+ <= getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
+ getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
+ getStateManager().removeStateListener(this);
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onNavigationModeChanged(Mode newMode) {
+ getDragLayer().recreateControllers();
+ getRotationHelper().setRotationHadDifferentUI(newMode != Mode.NO_BUTTON);
+ }
+
+ @Override
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+ // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+ // overview
+ RecentsModel.INSTANCE.get(this).getThumbnailCache()
+ .getHighResLoadingState().setVisible(true);
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ RecentsModel.INSTANCE.get(this).onTrimMemory(level);
+ }
+
+ @Override
+ public void startIntentSenderForResult(IntentSender intent, int requestCode,
+ Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
+ if (requestCode != -1) {
+ mPendingActivityRequestCode = requestCode;
+ StartActivityParams params = new StartActivityParams(this, requestCode);
+ params.intentSender = intent;
+ params.fillInIntent = fillInIntent;
+ params.flagsMask = flagsMask;
+ params.flagsValues = flagsValues;
+ params.extraFlags = extraFlags;
+ params.options = options;
+ startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
+ } else {
+ super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
+ flagsValues, extraFlags, options);
+ }
+ }
+
+ @Override
+ public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
+ if (requestCode != -1) {
+ mPendingActivityRequestCode = -1;
+ StartActivityParams params = new StartActivityParams(this, requestCode);
+ params.intent = intent;
+ params.options = options;
+ startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
+ } else {
+ super.startActivityForResult(intent, requestCode, options);
+ }
+ }
+
+ @Override
+ protected void onDeferredResumed() {
+ if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
+ // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
+ onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
+ // ProxyActivityStarter is started with clear task to reset the task after which it
+ // removes the task itself.
+ startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
+ }
+ }
+
+ @Override
+ protected StateHandler[] createStateHandlers() {
+ return new StateHandler[] {
+ getAllAppsController(),
+ getWorkspace(),
+ new RecentsViewStateController(this),
+ new BackButtonAlphaHandler(this)};
+ }
+
+ @Override
+ protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
+ if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) {
+ float offscreenTranslationX = getDeviceProfile().widthPx
+ - getOverviewPanel().getPaddingStart();
+ return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
+ }
+ return super.getOverviewScaleAndTranslationForNormalState();
+ }
+
+ @Override
+ public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
+ QuickstepAppTransitionManagerImpl appTransitionManager =
+ (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
+ appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
+
+ // On the first call clear the reference.
+ signal.cancel();
+
+ ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
+ fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
+ wallpaperTargets));
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(fadeAnimation);
+ return anim;
+ }, signal);
+ }
+
+ @Override
+ public void onDragLayerHierarchyChanged() {
+ onLauncherStateOrFocusChanged();
+ }
+
+ @Override
+ protected void onActivityFlagsChanged(int changeBits) {
+ if ((changeBits
+ & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
+ onLauncherStateOrFocusChanged();
+ }
+
+ super.onActivityFlagsChanged(changeBits);
+ }
+
+ /**
+ * Sets the back button visibility based on the current state/window focus.
+ */
+ private void onLauncherStateOrFocusChanged() {
+ Mode mode = SysUINavigationMode.getMode(this);
+ boolean shouldBackButtonBeHidden = mode.hasGestures
+ && getStateManager().getState().hideBackButton
+ && hasWindowFocus()
+ && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
+ if (shouldBackButtonBeHidden) {
+ // Show the back button if there is a floating view visible.
+ shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
+ TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
+ }
+ UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
+ shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
+ if (getDragLayer() != null) {
+ getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
+ }
+ }
+
+ @Override
+ public void finishBindingItems(int pageBoundFirst) {
+ super.finishBindingItems(pageBoundFirst);
+ // Instantiate and initialize WellbeingModel now that its loading won't interfere with
+ // populating workspace.
+ // TODO: Find a better place for this
+ WellbeingModel.get(this);
+ }
+
+ @Override
+ public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+ return Stream.concat(super.getSupportedShortcuts(),
+ Stream.of(WellbeingModel.SHORTCUT_FACTORY));
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
new file mode 100644
index 0000000..852a08e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -0,0 +1,342 @@
+/*
+ * 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.model;
+
+import static android.content.ContentResolver.SCHEME_CONTENT;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.createAndStartNewLooper;
+
+import android.annotation.TargetApi;
+import android.app.RemoteAction;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherApps;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.popup.RemoteActionShortcut;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Data model for digital wellbeing status of apps.
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public final class WellbeingModel {
+ private static final String TAG = "WellbeingModel";
+ private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
+ private static final boolean DEBUG = false;
+
+ private static final int MSG_PACKAGE_ADDED = 1;
+ private static final int MSG_PACKAGE_REMOVED = 2;
+ private static final int MSG_FULL_REFRESH = 3;
+
+ // Welbeing contract
+ private static final String METHOD_GET_ACTIONS = "get_actions";
+ private static final String EXTRA_ACTIONS = "actions";
+ private static final String EXTRA_ACTION = "action";
+ private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown";
+ private static final String EXTRA_PACKAGES = "packages";
+
+ private static WellbeingModel sInstance;
+
+ private final Context mContext;
+ private final String mWellbeingProviderPkg;
+ private final Handler mWorkerHandler;
+
+ private final ContentObserver mContentObserver;
+
+ private final Object mModelLock = new Object();
+ // Maps the action Id to the corresponding RemoteAction
+ private final Map<String, RemoteAction> mActionIdMap = new ArrayMap<>();
+ private final Map<String, String> mPackageToActionId = new HashMap<>();
+
+ private boolean mIsInTest;
+
+ private WellbeingModel(final Context context) {
+ mContext = context;
+ mWorkerHandler =
+ new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
+
+ mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
+ mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ // Wellbeing reports that app actions have changed.
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "ContentObserver.onChange() called with: selfChange = [" + selfChange
+ + "], uri = [" + uri + "]");
+ }
+ Preconditions.assertUIThread();
+ updateWellbeingData();
+ }
+ };
+
+ if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
+ context.registerReceiver(
+ new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
+ PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
+ Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
+ Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
+ Intent.ACTION_PACKAGE_RESTARTED));
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
+ filter);
+
+ restartObserver();
+ }
+ }
+
+ public void setInTest(boolean inTest) {
+ mIsInTest = inTest;
+ }
+
+ protected void onWellbeingProviderChanged(Intent intent) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
+ }
+ restartObserver();
+ }
+
+ private void restartObserver() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.unregisterContentObserver(mContentObserver);
+ Uri actionsUri = apiBuilder().path("actions").build();
+ try {
+ resolver.registerContentObserver(
+ actionsUri, true /* notifyForDescendants */, mContentObserver);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
+ if (mIsInTest) throw new RuntimeException(e);
+ }
+ updateWellbeingData();
+ }
+
+ @MainThread
+ public static WellbeingModel get(@NonNull Context context) {
+ Preconditions.assertUIThread();
+ if (sInstance == null) {
+ sInstance = new WellbeingModel(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ @MainThread
+ private SystemShortcut getShortcutForApp(String packageName, int userId,
+ BaseDraggingActivity activity, ItemInfo info) {
+ Preconditions.assertUIThread();
+ // Work profile apps are not recognized by digital wellbeing.
+ if (userId != UserHandle.myUserId()) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "getShortcutForApp [" + packageName + "]: not current user");
+ }
+ return null;
+ }
+
+ synchronized (mModelLock) {
+ String actionId = mPackageToActionId.get(packageName);
+ final RemoteAction action = actionId != null ? mActionIdMap.get(actionId) : null;
+ if (action == null) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "getShortcutForApp [" + packageName + "]: no action");
+ }
+ return null;
+ }
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG,
+ "getShortcutForApp [" + packageName + "]: action: '" + action.getTitle()
+ + "'");
+ }
+ return new RemoteActionShortcut(action, activity, info);
+ }
+ }
+
+ private void updateWellbeingData() {
+ mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
+ }
+
+ private Uri.Builder apiBuilder() {
+ return new Uri.Builder()
+ .scheme(SCHEME_CONTENT)
+ .authority(mWellbeingProviderPkg + ".api");
+ }
+
+ private boolean updateActions(String... packageNames) {
+ if (packageNames.length == 0) {
+ return true;
+ }
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "retrieveActions() called with: packageNames = [" + String.join(", ",
+ packageNames) + "]");
+ }
+ Preconditions.assertNonUiThread();
+
+ Uri contentUri = apiBuilder().build();
+ final Bundle remoteActionBundle;
+ try (ContentProviderClient client = mContext.getContentResolver()
+ .acquireUnstableContentProviderClient(contentUri)) {
+ if (client == null) {
+ if (DEBUG || mIsInTest) Log.i(TAG, "retrieveActions(): null provider");
+ return false;
+ }
+
+ // Prepare wellbeing call parameters.
+ final Bundle params = new Bundle();
+ params.putStringArray(EXTRA_PACKAGES, packageNames);
+ params.putInt(EXTRA_MAX_NUM_ACTIONS_SHOWN, 1);
+ // Perform wellbeing call .
+ remoteActionBundle = client.call(METHOD_GET_ACTIONS, null, params);
+ } catch (DeadObjectException e) {
+ Log.i(TAG, "retrieveActions(): DeadObjectException");
+ return false;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
+ if (mIsInTest) throw new RuntimeException(e);
+ return true;
+ }
+
+ synchronized (mModelLock) {
+ // Remove the entries for requested packages, and then update the fist with what we
+ // got from service
+ Arrays.stream(packageNames).forEach(mPackageToActionId::remove);
+
+ // The result consists of sub-bundles, each one is per a remote action. Each sub-bundle
+ // has a RemoteAction and a list of packages to which the action applies.
+ for (String actionId :
+ remoteActionBundle.getStringArray(EXTRA_ACTIONS)) {
+ final Bundle actionBundle = remoteActionBundle.getBundle(actionId);
+ mActionIdMap.put(actionId,
+ actionBundle.getParcelable(EXTRA_ACTION));
+
+ final String[] packagesForAction =
+ actionBundle.getStringArray(EXTRA_PACKAGES);
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "....actionId: " + actionId + ", packages: " + String.join(", ",
+ packagesForAction));
+ }
+ for (String packageName : packagesForAction) {
+ mPackageToActionId.put(packageName, actionId);
+ }
+ }
+ }
+ if (DEBUG || mIsInTest) Log.i(TAG, "retrieveActions(): finished");
+ return true;
+ }
+
+ private boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PACKAGE_REMOVED: {
+ String packageName = (String) msg.obj;
+ mWorkerHandler.removeCallbacksAndMessages(packageName);
+ synchronized (mModelLock) {
+ mPackageToActionId.remove(packageName);
+ }
+ return true;
+ }
+ case MSG_PACKAGE_ADDED: {
+ String packageName = (String) msg.obj;
+ mWorkerHandler.removeCallbacksAndMessages(packageName);
+ if (!updateActions(packageName)) {
+ scheduleRefreshRetry(msg);
+ }
+ return true;
+ }
+
+ case MSG_FULL_REFRESH: {
+ // Remove all existing messages
+ mWorkerHandler.removeCallbacksAndMessages(null);
+ final String[] packageNames = mContext.getSystemService(LauncherApps.class)
+ .getActivityList(null, Process.myUserHandle()).stream()
+ .map(li -> li.getApplicationInfo().packageName).distinct()
+ .toArray(String[]::new);
+ if (!updateActions(packageNames)) {
+ scheduleRefreshRetry(msg);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void scheduleRefreshRetry(Message originalMsg) {
+ int retryCount = originalMsg.arg1;
+ if (retryCount >= RETRY_TIMES_MS.length) {
+ // To many retries, skip
+ return;
+ }
+
+ Message msg = Message.obtain(originalMsg);
+ msg.arg1 = retryCount + 1;
+ mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
+ }
+
+ private void onAppPackageChanged(Intent intent) {
+ if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
+ Preconditions.assertUIThread();
+
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName == null || packageName.length() == 0) {
+ // they sent us a bad intent
+ return;
+ }
+
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget();
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
+ }
+ }
+
+ /**
+ * Shortcut factory for generating wellbeing action
+ */
+ public static final SystemShortcut.Factory SHORTCUT_FACTORY = (activity, info) ->
+ WellbeingModel.get(activity).getShortcutForApp(
+ info.getTargetComponent().getPackageName(),
+ info.user.getIdentifier(),
+ activity, info);
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
new file mode 100644
index 0000000..965b5f0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * 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.app.Activity;
+import android.app.Person;
+import android.content.pm.ShortcutInfo;
+import android.util.Base64;
+
+import com.android.launcher3.Utilities;
+import com.android.systemui.shared.system.ActivityCompat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.zip.Deflater;
+
+public class ApiWrapper {
+
+ public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+ if (!Utilities.IS_DEBUG_DEVICE) {
+ return false;
+ }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
+ return false;
+ }
+
+ Deflater deflater = new Deflater();
+ deflater.setInput(out.toByteArray());
+ deflater.finish();
+
+ out.reset();
+ byte[] buffer = new byte[1024];
+ while (!deflater.finished()) {
+ int count = deflater.deflate(buffer); // returns the generated code... index
+ out.write(buffer, 0, count);
+ }
+
+ writer.println("--encoded-view-dump-v0--");
+ writer.println(Base64.encodeToString(
+ out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
+ return true;
+ }
+
+ public static Person[] getPersons(ShortcutInfo si) {
+ Person[] persons = si.getPersons();
+ return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
index aa0dfc3..43dc882 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
@@ -16,11 +16,9 @@
package com.android.launcher3.uioverrides;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.anim.AnimatorSetBuilder;
@@ -30,18 +28,14 @@
public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
- private static final String TAG = "BackButtonAlphaHandler";
+ private final BaseQuickstepLauncher mLauncher;
- private final Launcher mLauncher;
-
- public BackButtonAlphaHandler(Launcher launcher) {
+ public BackButtonAlphaHandler(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
}
@Override
- public void setState(LauncherState state) {
- UiFactory.onLauncherStateOrFocusChanged(mLauncher);
- }
+ public void setState(LauncherState state) { }
@Override
public void setStateWithAnimation(LauncherState toState,
@@ -52,8 +46,8 @@
if (!SysUINavigationMode.getMode(mLauncher).hasGestures) {
// If the nav mode is not gestural, then force back button alpha to be 1
- UiThreadHelper.setBackButtonAlphaAsync(mLauncher, UiFactory.SET_BACK_BUTTON_ALPHA, 1f,
- true /* animate */);
+ UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
+ BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, 1f, true /* animate */);
return;
}
@@ -64,15 +58,8 @@
anim.setDuration(config.duration);
anim.addUpdateListener(valueAnimator -> {
final float alpha = (float) valueAnimator.getAnimatedValue();
- UiThreadHelper.setBackButtonAlphaAsync(mLauncher, UiFactory.SET_BACK_BUTTON_ALPHA,
- alpha, false /* animate */);
- });
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Reapply the final alpha in case some state (e.g. window focus) changed.
- UiFactory.onLauncherStateOrFocusChanged(mLauncher);
- }
+ UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
+ BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, alpha, false /* animate */);
});
builder.play(anim);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
deleted file mode 100644
index 17c681b..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ /dev/null
@@ -1,267 +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 android.app.Activity.RESULT_CANCELED;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
-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.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
-
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.app.Activity;
-import android.app.Person;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.util.Base64;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.QuickstepAppTransitionManagerImpl;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.proxy.ProxyActivityStarter;
-import com.android.launcher3.proxy.StartActivityParams;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
-import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.RemoteFadeOutAnimationListener;
-import com.android.systemui.shared.system.ActivityCompat;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
-import java.util.zip.Deflater;
-
-public class UiFactory extends RecentsUiFactory {
-
- /**
- * Reusable command for applying the back button alpha on the background thread.
- */
- public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
- (context, arg1, arg2) -> {
- SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(Float.intBitsToFloat(arg1),
- arg2 != 0);
- };
-
- public static Runnable enableLiveUIChanges(Launcher launcher) {
- NavigationModeChangeListener listener = m -> {
- launcher.getDragLayer().recreateControllers();
- launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
- };
- SysUINavigationMode mode = SysUINavigationMode.INSTANCE.get(launcher);
- SysUINavigationMode.Mode m = mode.addModeChangeListener(listener);
- launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
- return () -> mode.removeModeChangeListener(listener);
- }
-
- public static StateHandler[] getStateHandler(Launcher launcher) {
- return new StateHandler[] {
- launcher.getAllAppsController(),
- launcher.getWorkspace(),
- createRecentsViewStateController(launcher),
- new BackButtonAlphaHandler(launcher)};
- }
-
- /**
- * Sets the back button visibility based on the current state/window focus.
- */
- public static void onLauncherStateOrFocusChanged(Launcher launcher) {
- Mode mode = SysUINavigationMode.getMode(launcher);
- boolean shouldBackButtonBeHidden = mode.hasGestures
- && launcher != null
- && launcher.getStateManager().getState().hideBackButton
- && launcher.hasWindowFocus();
- if (shouldBackButtonBeHidden) {
- // Show the back button if there is a floating view visible.
- shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher,
- TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
- }
- UiThreadHelper.setBackButtonAlphaAsync(launcher, UiFactory.SET_BACK_BUTTON_ALPHA,
- shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
- if (launcher != null && launcher.getDragLayer() != null) {
- launcher.getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
- }
- }
-
- public static void onCreate(Launcher launcher) {
- if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
- launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
- @Override
- public void onStateTransitionStart(LauncherState toState) {
- }
-
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- boolean swipeUpEnabled = SysUINavigationMode.INSTANCE.get(launcher).getMode()
- .hasGestures;
- LauncherState prevState = launcher.getStateManager().getLastState();
-
- if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
- && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT <=
- launcher.getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
- launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
- launcher.getStateManager().removeStateListener(this);
- }
- }
- });
- }
-
- if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
- launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
- @Override
- public void onStateTransitionStart(LauncherState toState) {
- }
-
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- LauncherState prevState = launcher.getStateManager().getLastState();
-
- if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT <=
- launcher.getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
- launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
- launcher.getStateManager().removeStateListener(this);
- }
- }
- });
- }
- }
-
- public static void onEnterAnimationComplete(Context context) {
- // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
- // as a part of quickstep, so that high-res thumbnails can load the next time we enter
- // overview
- RecentsModel.INSTANCE.get(context).getThumbnailCache()
- .getHighResLoadingState().setVisible(true);
- }
-
- public static void onTrimMemory(Context context, int level) {
- RecentsModel model = RecentsModel.INSTANCE.get(context);
- if (model != null) {
- model.onTrimMemory(level);
- }
- }
-
- public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
- CancellationSignal cancellationSignal) {
- QuickstepAppTransitionManagerImpl appTransitionManager =
- (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
- appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
-
- // On the first call clear the reference.
- cancellationSignal.cancel();
-
- ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
- fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
- wallpaperTargets));
- AnimatorSet anim = new AnimatorSet();
- anim.play(fadeAnimation);
- return anim;
- }, cancellationSignal);
- }
-
- public static boolean dumpActivity(Activity activity, PrintWriter writer) {
- if (!Utilities.IS_DEBUG_DEVICE) {
- return false;
- }
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
- return false;
- }
-
- Deflater deflater = new Deflater();
- deflater.setInput(out.toByteArray());
- deflater.finish();
-
- out.reset();
- byte[] buffer = new byte[1024];
- while (!deflater.finished()) {
- int count = deflater.deflate(buffer); // returns the generated code... index
- out.write(buffer, 0, count);
- }
-
- writer.println("--encoded-view-dump-v0--");
- writer.println(Base64.encodeToString(
- out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
- return true;
- }
-
- public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
- int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
- Bundle options) {
- StartActivityParams params = new StartActivityParams(activity, requestCode);
- params.intentSender = intent;
- params.fillInIntent = fillInIntent;
- params.flagsMask = flagsMask;
- params.flagsValues = flagsValues;
- params.extraFlags = extraFlags;
- params.options = options;
- ((Context) activity).startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
- return true;
- }
-
- public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
- Bundle options) {
- StartActivityParams params = new StartActivityParams(activity, requestCode);
- params.intent = intent;
- params.options = options;
- activity.startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
- return true;
- }
-
- /**
- * Removes any active ProxyActivityStarter task and sends RESULT_CANCELED to Launcher.
- *
- * ProxyActivityStarter is started with clear task to reset the task after which it removes the
- * task itself.
- */
- public static void resetPendingActivityResults(Launcher launcher, int requestCode) {
- launcher.onActivityResult(requestCode, RESULT_CANCELED, null);
- launcher.startActivity(ProxyActivityStarter.getLaunchIntent(launcher, null));
- }
-
- public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
- if (SysUINavigationMode.getMode(l) == Mode.NO_BUTTON) {
- float offscreenTranslationX = l.getDeviceProfile().widthPx
- - l.getOverviewPanel().getPaddingStart();
- return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
- }
- return new ScaleAndTranslation(1.1f, 0f, 0f);
- }
-
- public static Person[] getPersons(ShortcutInfo si) {
- Person[] persons = si.getPersons();
- return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
index bb72315..3cb0088 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
@@ -11,10 +11,10 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationComponents;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
/**
* Touch controller for handling edge swipes in landscape/seascape UI
@@ -24,7 +24,7 @@
private static final String TAG = "LandscapeEdgeSwipeCtrl";
public LandscapeEdgeSwipeController(Launcher l) {
- super(l, SwipeDetector.HORIZONTAL);
+ super(l, SingleAxisSwipeDetector.HORIZONTAL);
}
@Override
@@ -73,7 +73,7 @@
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
super.onSwipeInteractionCompleted(targetState, logAction);
if (mStartState == NORMAL && targetState == OVERVIEW) {
- RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+ SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index ef6a5e2..99b2a81 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -43,11 +43,10 @@
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.LayoutUtils;
@@ -79,7 +78,7 @@
private boolean mFinishFastOnSecondTouch;
public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {
- super(l, SwipeDetector.VERTICAL);
+ super(l, SingleAxisSwipeDetector.VERTICAL);
mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
mAllowDragToOverview = allowDragToOverview;
}
@@ -300,7 +299,7 @@
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
super.onSwipeInteractionCompleted(targetState, logAction);
if (mStartState == NORMAL && targetState == OVERVIEW) {
- RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+ SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
}
}
diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
index 71833ad..5fcdc19 100644
--- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
@@ -27,7 +27,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
@@ -122,13 +121,17 @@
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
- UiFactory.onEnterAnimationComplete(this);
+ // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+ // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+ // overview
+ RecentsModel.INSTANCE.get(this).getThumbnailCache()
+ .getHighResLoadingState().setVisible(true);
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
- UiFactory.onTrimMemory(this, level);
+ RecentsModel.INSTANCE.get(this).onTrimMemory(level);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 918645d..3e84e7d 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -33,7 +33,7 @@
int TYPE_SCREEN_PINNED = 1 << 6;
int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
int TYPE_RESET_GESTURE = 1 << 8;
- int TYPE_QUICK_CAPTURE = 1 << 9;
+ int TYPE_OVERSCROLL = 1 << 9;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -45,7 +45,7 @@
"TYPE_SCREEN_PINNED", // 6
"TYPE_OVERVIEW_WITHOUT_FOCUS", // 7
"TYPE_RESET_GESTURE", // 8
- "TYPE_QUICK_CAPTURE", // 9
+ "TYPE_OVERSCROLL", // 9
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
deleted file mode 100644
index bd6204a..0000000
--- a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
+++ /dev/null
@@ -1,99 +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.quickstep;
-
-import android.annotation.TargetApi;
-import android.app.ActivityManager.TaskDescription;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.LruCache;
-import android.util.SparseArray;
-
-import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.graphics.DrawableFactory;
-import com.android.launcher3.icons.LauncherIcons;
-import com.android.systemui.shared.recents.model.IconLoader;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache;
-
-/**
- * Extension of {@link IconLoader} with icon normalization support
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class NormalizedIconLoader extends IconLoader {
-
- private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
- private final DrawableFactory mDrawableFactory;
- private final boolean mDisableColorExtraction;
-
- public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
- LruCache<ComponentName, ActivityInfo> activityInfoCache,
- boolean disableColorExtraction) {
- super(context, iconCache, activityInfoCache);
- mDrawableFactory = DrawableFactory.INSTANCE.get(context);
- mDisableColorExtraction = disableColorExtraction;
- }
-
- @Override
- public Drawable getDefaultIcon(int userId) {
- synchronized (mDefaultIcons) {
- BitmapInfo info = mDefaultIcons.get(userId);
- if (info == null) {
- info = getBitmapInfo(Resources.getSystem()
- .getDrawable(android.R.drawable.sym_def_app_icon), userId, 0, false);
- mDefaultIcons.put(userId, info);
- }
-
- return new FastBitmapDrawable(info);
- }
- }
-
- @Override
- protected Drawable createBadgedDrawable(Drawable drawable, int userId, TaskDescription desc) {
- return new FastBitmapDrawable(getBitmapInfo(drawable, userId, desc.getPrimaryColor(),
- false));
- }
-
- private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
- int primaryColor, boolean isInstantApp) {
- try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
- if (mDisableColorExtraction) {
- la.disableColorExtraction();
- }
- la.setWrapperBackgroundColor(primaryColor);
-
- // User version code O, so that the icon is always wrapped in an adaptive icon container
- return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
- Build.VERSION_CODES.O, isInstantApp);
- }
- }
-
- @Override
- protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId,
- TaskDescription desc) {
- BitmapInfo bitmapInfo = getBitmapInfo(
- activityInfo.loadUnbadgedIcon(mContext.getPackageManager()),
- userId,
- desc.getPrimaryColor(),
- activityInfo.applicationInfo.isInstantApp());
- return mDrawableFactory.newIcon(mContext, bitmapInfo, activityInfo);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index e6e3297..1855e64 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
@@ -43,6 +44,7 @@
import android.graphics.Region;
import android.os.Process;
import android.text.TextUtils;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
@@ -52,7 +54,9 @@
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -65,7 +69,7 @@
* Manages the state of the system during a swipe up gesture.
*/
public class RecentsAnimationDeviceState implements
- SysUINavigationMode.NavigationModeChangeListener,
+ NavigationModeChangeListener,
DefaultDisplay.DisplayInfoChangeListener {
private Context mContext;
@@ -74,6 +78,8 @@
private DefaultDisplay mDefaultDisplay;
private int mDisplayId;
+ private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
+
private @SystemUiStateFlags int mSystemUiStateFlags;
private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
@@ -114,6 +120,7 @@
mContext.registerReceiver(mUserUnlockedReceiver,
new IntentFilter(ACTION_USER_UNLOCKED));
}
+ runOnDestroy(() -> Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver));
// Register for exclusion updates
mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
@@ -124,7 +131,11 @@
mExclusionRegion = region;
}
};
+ runOnDestroy(mExclusionListener::unregister);
+
+ // Register for navigation mode changes
onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
+ runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
// Add any blocked activities
String blockingActivity = context.getString(R.string.gesture_blocking_activity);
@@ -133,18 +144,34 @@
}
}
+ private void runOnDestroy(Runnable action) {
+ mOnDestroyActions.add(action);
+ }
+
/**
* Cleans up all the registered listeners and receivers.
*/
public void destroy() {
- Utilities.unregisterReceiverSafely(mContext, mUserUnlockedReceiver);
- mSysUiNavMode.removeModeChangeListener(this);
+ for (Runnable r : mOnDestroyActions) {
+ r.run();
+ }
mDefaultDisplay.removeChangeListener(this);
- mExclusionListener.unregister();
+ }
+
+ /**
+ * Adds a listener for the nav mode change, guaranteed to be called after the device state's
+ * mode has changed.
+ */
+ public void addNavigationModeChangedCallback(NavigationModeChangeListener listener) {
+ listener.onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(listener));
+ runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(listener));
}
@Override
public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onNavigationModeChanged " + newMode);
+ }
mDefaultDisplay.removeChangeListener(this);
if (newMode.hasGestures) {
mDefaultDisplay.addChangeListener(this);
@@ -168,6 +195,34 @@
}
/**
+ * @return the current navigation mode for the device.
+ */
+ public SysUINavigationMode.Mode getNavMode() {
+ return mMode;
+ }
+
+ /**
+ * @return whether the current nav mode is fully gestural.
+ */
+ public boolean isFullyGesturalNavMode() {
+ return mMode == NO_BUTTON;
+ }
+
+ /**
+ * @return whether the current nav mode has some gestures (either 2 or 0 button mode).
+ */
+ public boolean isGesturalNavMode() {
+ return mMode == TWO_BUTTONS || mMode == NO_BUTTON;
+ }
+
+ /**
+ * @return whether the current nav mode is button-based.
+ */
+ public boolean isButtonNavMode() {
+ return mMode == THREE_BUTTONS;
+ }
+
+ /**
* @return the display id for the display that Launcher is running on.
*/
public int getDisplayId() {
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 465d464..f248423 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -25,11 +25,9 @@
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
-import android.content.pm.LauncherApps;
import android.os.Build;
import android.os.Looper;
import android.os.Process;
-import android.os.UserHandle;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.model.Task;
@@ -48,8 +46,6 @@
@TargetApi(Build.VERSION_CODES.O)
public class RecentsModel extends TaskStackChangeListener {
- private static final String TAG = "RecentsModel";
-
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
new MainThreadInitializedObject<>(RecentsModel::new);
@@ -70,7 +66,6 @@
mIconCache = new TaskIconCache(context, looper);
mThumbnailCache = new TaskThumbnailCache(context, looper);
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
- setupPackageListener();
}
public TaskIconCache getIconCache() {
@@ -183,35 +178,6 @@
}
}
- public void onOverviewShown(boolean fromHome, String tag) {
- SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(fromHome, tag);
- }
-
- private void setupPackageListener() {
- mContext.getSystemService(LauncherApps.class).registerCallback(new LauncherApps.Callback() {
- @Override
- public void onPackageRemoved(String packageName, UserHandle user) {
- mIconCache.invalidatePackage(packageName);
- }
-
- @Override
- public void onPackageChanged(String packageName, UserHandle user) {
- mIconCache.invalidatePackage(packageName);
- }
-
- @Override
- public void onPackageAdded(String packageName, UserHandle user) { }
-
- @Override
- public void onPackagesAvailable(
- String[] packageNames, UserHandle user, boolean replacing) { }
-
- @Override
- public void onPackagesUnavailable(
- String[] packageNames, UserHandle user, boolean replacing) { }
- });
- }
-
public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.add(listener);
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 6873899..e3e8ace 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -36,7 +36,6 @@
private RecentsAnimationTargets mTargets;
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
- private ThumbnailData mCanceledThumbnail;
/**
* Preloads the recents animation.
@@ -81,15 +80,15 @@
if (thumbnailData != null) {
// If a screenshot is provided, switch to the screenshot before cleaning up
activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
- () -> cleanUpRecentsAnimation());
+ () -> cleanUpRecentsAnimation(thumbnailData));
} else {
- cleanUpRecentsAnimation();
+ cleanUpRecentsAnimation(null /* canceledThumbnail */);
}
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
- cleanUpRecentsAnimation();
+ cleanUpRecentsAnimation(null /* canceledThumbnail */);
}
});
mCallbacks.addListener(gestureState);
@@ -119,7 +118,7 @@
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
? mController::finishAnimationToHome
: mController::finishAnimationToApp);
- cleanUpRecentsAnimation();
+ cleanUpRecentsAnimation(null /* canceledThumbnail */);
}
}
@@ -146,9 +145,9 @@
/**
* Cleans up the recents animation entirely.
*/
- private void cleanUpRecentsAnimation() {
+ private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) {
// Clean up the screenshot if necessary
- if (mController != null && mCanceledThumbnail != null) {
+ if (mController != null && canceledThumbnail != null) {
mController.cleanupScreenshot();
}
@@ -165,7 +164,6 @@
mController = null;
mCallbacks = null;
mTargets = null;
- mCanceledThumbnail = null;
mLastGestureState = null;
}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 289a129..873f29c 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -15,67 +15,61 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED;
+import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import android.content.ComponentName;
+import android.app.ActivityManager.TaskDescription;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.util.LruCache;
+import android.os.UserHandle;
+import android.util.SparseArray;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
-import java.util.Map;
import java.util.function.Consumer;
/**
* Manages the caching of task icons and related data.
- * TODO(b/138944598): This class should later be merged into IconCache.
*/
public class TaskIconCache {
private final Handler mBackgroundHandler;
private final AccessibilityManager mAccessibilityManager;
- private final NormalizedIconLoader mIconLoader;
-
- private final TaskKeyLruCache<Drawable> mIconCache;
- private final TaskKeyLruCache<String> mContentDescriptionCache;
- private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
-
- private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction =
- new TaskKeyLruCache.EvictionCallback() {
- @Override
- public void onEntryEvicted(Task.TaskKey key) {
- if (key != null) {
- mActivityInfoCache.remove(key.getComponent());
- }
- }
- };
+ private final Context mContext;
+ private final TaskKeyLruCache<TaskCacheEntry> mIconCache;
+ private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
public TaskIconCache(Context context, Looper backgroundLooper) {
+ mContext = context;
mBackgroundHandler = new Handler(backgroundLooper);
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Resources res = context.getResources();
int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
- mIconCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
- mContentDescriptionCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
- mActivityInfoCache = new LruCache<>(cacheSize);
- mIconLoader = new NormalizedIconLoader(context, mIconCache, mActivityInfoCache,
- true /* disableColorExtraction */);
+ mIconCache = new TaskKeyLruCache<>(cacheSize);
}
/**
@@ -96,15 +90,14 @@
IconLoadRequest request = new IconLoadRequest(mBackgroundHandler) {
@Override
public void run() {
- Drawable icon = mIconLoader.getIcon(task);
- String contentDescription = loadContentDescriptionInBackground(task);
+ TaskCacheEntry entry = getCacheEntry(task);
if (isCanceled()) {
// We don't call back to the provided callback in this case
return;
}
MAIN_EXECUTOR.execute(() -> {
- task.icon = icon;
- task.titleDescription = contentDescription;
+ task.icon = entry.icon;
+ task.titleDescription = entry.contentDescription;
callback.accept(task);
onEnd();
});
@@ -116,51 +109,94 @@
public void clear() {
mIconCache.evictAll();
- mContentDescriptionCache.evictAll();
}
- /**
- * Loads the content description for the given {@param task}.
- */
- private String loadContentDescriptionInBackground(Task task) {
- // Return the cached content description if it exists
- String label = mContentDescriptionCache.getAndInvalidateIfModified(task.key);
- if (label != null) {
- return label;
- }
-
- // Skip loading content descriptions if accessibility is disabled unless low RAM recents
- // is enabled.
- if (!GO_LOW_RAM_RECENTS_ENABLED && !mAccessibilityManager.isEnabled()) {
- return "";
- }
-
- // Skip loading the content description if the activity no longer exists
- ActivityInfo activityInfo = mIconLoader.getAndUpdateActivityInfo(task.key);
- if (activityInfo == null) {
- return "";
- }
-
- // Load the label otherwise
- label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(activityInfo,
- task.key.userId, task.taskDescription);
- mContentDescriptionCache.put(task.key, label);
- return label;
- }
-
-
void onTaskRemoved(TaskKey taskKey) {
mIconCache.remove(taskKey);
}
- void invalidatePackage(String packageName) {
- // TODO(b/138944598): Merge this class into IconCache so we can do this at the base level
- Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
- for (ComponentName cn : activityInfoCache.keySet()) {
- if (cn.getPackageName().equals(packageName)) {
- mActivityInfoCache.remove(cn);
+ @WorkerThread
+ private TaskCacheEntry getCacheEntry(Task task) {
+ TaskCacheEntry entry = mIconCache.getAndInvalidateIfModified(task.key);
+ if (entry != null) {
+ return entry;
+ }
+
+ TaskDescription desc = task.taskDescription;
+ TaskKey key = task.key;
+ ActivityInfo activityInfo = null;
+
+ // Create new cache entry
+ entry = new TaskCacheEntry();
+
+ // Load icon
+ // TODO: Load icon resource (b/143363444)
+ Bitmap icon = desc.getIcon();
+ if (icon != null) {
+ entry.icon = new FastBitmapDrawable(getBitmapInfo(
+ new BitmapDrawable(mContext.getResources(), icon),
+ key.userId,
+ desc.getPrimaryColor(),
+ false /* isInstantApp */));
+ } else {
+ activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
+ key.getComponent(), key.userId);
+ if (activityInfo != null) {
+ BitmapInfo bitmapInfo = getBitmapInfo(
+ activityInfo.loadUnbadgedIcon(mContext.getPackageManager()),
+ key.userId,
+ desc.getPrimaryColor(),
+ activityInfo.applicationInfo.isInstantApp());
+ entry.icon = DrawableFactory.INSTANCE.get(mContext).newIcon(
+ mContext, bitmapInfo, activityInfo);
+ } else {
+ entry.icon = getDefaultIcon(key.userId);
}
}
+
+ // Loading content descriptions if accessibility or low RAM recents is enabled.
+ if (GO_LOW_RAM_RECENTS_ENABLED || mAccessibilityManager.isEnabled()) {
+ // Skip loading the content description if the activity no longer exists
+ if (activityInfo == null) {
+ activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
+ key.getComponent(), key.userId);
+ }
+ if (activityInfo != null) {
+ entry.contentDescription = ActivityManagerWrapper.getInstance()
+ .getBadgedContentDescription(activityInfo, task.key.userId,
+ task.taskDescription);
+ }
+ }
+
+ mIconCache.put(task.key, entry);
+ return entry;
+ }
+
+ @WorkerThread
+ private Drawable getDefaultIcon(int userId) {
+ synchronized (mDefaultIcons) {
+ BitmapInfo info = mDefaultIcons.get(userId);
+ if (info == null) {
+ try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
+ info = la.makeDefaultIcon(UserHandle.of(userId));
+ }
+ mDefaultIcons.put(userId, info);
+ }
+ return new FastBitmapDrawable(info);
+ }
+ }
+
+ @WorkerThread
+ private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
+ int primaryColor, boolean isInstantApp) {
+ try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
+ la.disableColorExtraction();
+ la.setWrapperBackgroundColor(primaryColor);
+
+ // User version code O, so that the icon is always wrapped in an adaptive icon container
+ return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
+ Build.VERSION_CODES.O, isInstantApp);
+ }
}
public static abstract class IconLoadRequest extends HandlerRunnable {
@@ -168,4 +204,9 @@
super(handler, null);
}
}
+
+ private static class TaskCacheEntry {
+ public Drawable icon;
+ public String contentDescription = "";
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index b17ed4c..13731b6 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -31,6 +31,9 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.rule.ShellCommandRule.disableHeadsUpNotification;
import static com.android.launcher3.util.rule.ShellCommandRule.getLauncherCommand;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
+import static com.android.launcher3.util.rule.TestStabilityRule.RUN_FLAFOR;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_PRESUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -99,7 +102,7 @@
}
mOrderSensitiveRules = RuleChain.outerRule(new NavigationModeSwitchRule(mLauncher))
- .around(new FailureWatcher(mDevice));
+ .around(new FailureWatcher(mDevice));
mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
getHomeIntentInPackage(context),
@@ -131,7 +134,8 @@
@Test
public void goToOverviewFromHome() {
// b/142828227
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess()) {
+ if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
+ (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
return;
}
mDevice.pressHome();
@@ -145,7 +149,8 @@
@Test
public void goToOverviewFromApp() {
// b/142828227
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess()) {
+ if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
+ (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
return;
}
startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
@@ -183,7 +188,8 @@
@Test
public void testOverview() {
// b/142828227
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess()) {
+ if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
+ (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
return;
}
startAppFastAndWaitForRecentTask(getAppPackageName());
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 25224b0..50cab74 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -18,6 +18,9 @@
import static com.android.launcher3.util.RaceConditionReproducer.enterEvt;
import static com.android.launcher3.util.RaceConditionReproducer.exitEvt;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
+import static com.android.launcher3.util.rule.TestStabilityRule.RUN_FLAFOR;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_PRESUBMIT;
import android.content.Intent;
@@ -93,7 +96,8 @@
@NavigationModeSwitch
public void testStressSwipeToOverview() {
// b/142828227
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess()) {
+ if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
+ (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
return;
}
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index d270d76..8cd3bb6 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -248,33 +248,32 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
- @Ignore("Temporarily disabled b/140252765")
public void testQuickSwitchFromApp() throws Exception {
- startAppFast(getAppPackageName());
startTestActivity(2);
- String calculatorPackage = resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
- startAppFast(calculatorPackage);
+ startTestActivity(3);
+ startTestActivity(4);
Background background = getAndAssertBackground();
background.quickSwitchToPreviousApp();
assertTrue("The first app we should have quick switched to is not running",
- isTestActivityRunning("TestActivity2"));
+ isTestActivityRunning(3));
background = getAndAssertBackground();
background.quickSwitchToPreviousApp();
if (mLauncher.getNavigationModel() == NavigationModel.THREE_BUTTON) {
// 3-button mode toggles between 2 apps, rather than going back further.
assertTrue("Second quick switch should have returned to the first app.",
- mDevice.wait(Until.hasObject(By.pkg(calculatorPackage)), DEFAULT_UI_TIMEOUT));
+ isTestActivityRunning(4));
} else {
assertTrue("The second app we should have quick switched to is not running",
- isTestActivityRunning("Test Pin Item"));
+ isTestActivityRunning(2));
}
getAndAssertBackground();
}
- private boolean isTestActivityRunning(String activityLabel) {
- return mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text(activityLabel)),
+ private boolean isTestActivityRunning(int activityNumber) {
+ return mDevice.wait(Until.hasObject(By.pkg(getAppPackageName())
+ .text("TestActivity" + activityNumber)),
DEFAULT_UI_TIMEOUT);
}
@@ -285,7 +284,7 @@
startTestActivity(2);
mLauncher.pressHome().quickSwitchToPreviousApp();
assertTrue("The most recent task is not running after quick switching from home",
- isTestActivityRunning("TestActivity2"));
+ isTestActivityRunning(2));
getAndAssertBackground();
}
}
diff --git a/res/anim/slide_in_right.xml b/res/anim/slide_in_right.xml
deleted file mode 100644
index 55d3e54..0000000
--- a/res/anim/slide_in_right.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false" >
- <translate
- android:duration="@android:integer/config_shortAnimTime"
- android:fromXDelta="100%"
- android:toXDelta="0%"
- />
-</set>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index 0387184..10671c5 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -69,7 +69,6 @@
<string name="app_transition_manager_class" translatable="false"></string>
<string name="instant_app_resolver_class" translatable="false"></string>
<string name="main_process_initializer_class" translatable="false"></string>
- <string name="system_shortcut_factory_class" translatable="false"></string>
<string name="app_launch_tracker_class" translatable="false"></string>
<string name="test_information_handler_class" translatable="false"></string>
<string name="launcher_activity_logic_class" translatable="false"></string>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index b28077f..382bfdf 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -35,7 +35,7 @@
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
-import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
@@ -81,19 +81,39 @@
protected StatsLogManager mStatsLogManager;
protected SystemUiController mSystemUiController;
- private static final int ACTIVITY_STATE_STARTED = 1 << 0;
- private static final int ACTIVITY_STATE_RESUMED = 1 << 1;
+
+ public static final int ACTIVITY_STATE_STARTED = 1 << 0;
+ public static final int ACTIVITY_STATE_RESUMED = 1 << 1;
+
/**
- * State flag indicating if the user is active or the actitvity when to background as a result
+ * State flags indicating that the activity has received one frame after resume, and was
+ * not immediately paused.
+ */
+ public static final int ACTIVITY_STATE_DEFERRED_RESUMED = 1 << 2;
+
+ public static final int ACTIVITY_STATE_WINDOW_FOCUSED = 1 << 3;
+
+ /**
+ * State flag indicating if the user is active or the activity when to background as a result
* of user action.
* @see #isUserActive()
*/
- private static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 2;
+ public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
+
+ /**
+ * State flag indicating that a state transition is in progress
+ */
+ public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 5;
@Retention(SOURCE)
@IntDef(
flag = true,
- value = {ACTIVITY_STATE_STARTED, ACTIVITY_STATE_RESUMED, ACTIVITY_STATE_USER_ACTIVE})
+ value = {ACTIVITY_STATE_STARTED,
+ ACTIVITY_STATE_RESUMED,
+ ACTIVITY_STATE_DEFERRED_RESUMED,
+ ACTIVITY_STATE_WINDOW_FOCUSED,
+ ACTIVITY_STATE_USER_ACTIVE,
+ ACTIVITY_STATE_TRANSITION_ACTIVE})
public @interface ActivityFlags{}
@ActivityFlags
@@ -146,19 +166,19 @@
@Override
protected void onStart() {
- mActivityFlags |= ACTIVITY_STATE_STARTED;
+ addActivityFlags(ACTIVITY_STATE_STARTED);
super.onStart();
}
@Override
protected void onResume() {
- mActivityFlags |= ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE;
+ addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
super.onResume();
}
@Override
protected void onUserLeaveHint() {
- mActivityFlags &= ~ACTIVITY_STATE_USER_ACTIVE;
+ removeActivityFlags(ACTIVITY_STATE_USER_ACTIVE);
super.onUserLeaveHint();
}
@@ -172,7 +192,7 @@
@Override
protected void onStop() {
- mActivityFlags &= ~ACTIVITY_STATE_STARTED & ~ACTIVITY_STATE_USER_ACTIVE;
+ removeActivityFlags(ACTIVITY_STATE_STARTED | ACTIVITY_STATE_USER_ACTIVE);
mForceInvisible = 0;
super.onStop();
@@ -183,7 +203,7 @@
@Override
protected void onPause() {
- mActivityFlags &= ~ACTIVITY_STATE_RESUMED;
+ removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
super.onPause();
// Reset the overridden sysui flags used for the task-swipe launch animation, we do this
@@ -193,6 +213,17 @@
getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
}
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (hasFocus) {
+ addActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
+ } else {
+ removeActivityFlags(ACTIVITY_STATE_WINDOW_FOCUSED);
+ }
+
+ }
+
public boolean isStarted() {
return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
}
@@ -208,6 +239,22 @@
return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
}
+ public int getActivityFlags() {
+ return mActivityFlags;
+ }
+
+ protected void addActivityFlags(int flags) {
+ mActivityFlags |= flags;
+ onActivityFlagsChanged(flags);
+ }
+
+ protected void removeActivityFlags(int flags) {
+ mActivityFlags &= ~flags;
+ onActivityFlagsChanged(flags);
+ }
+
+ protected void onActivityFlagsChanged(int changeBits) { }
+
public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
mDPChangeListeners.add(listener);
}
@@ -233,7 +280,7 @@
/**
* Used to set the override visibility state, used only to handle the transition home with the
* recents animation.
- * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner()
+ * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner
*/
public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
mForceInvisible |= flag;
@@ -260,7 +307,7 @@
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- if (!UiFactory.dumpActivity(this, writer)) {
+ if (!ApiWrapper.dumpActivity(this, writer)) {
super.dump(prefix, fd, writer, args);
}
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 8ebf464..93def50 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -454,6 +454,8 @@
.object()
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
.key(NAME_KEY).value(name)
+ .key(USER_HANDLE_KEY).value(
+ UserManagerCompat.getInstance(mContext).getSerialNumberForUser(user))
.key(APP_SHORTCUT_TYPE_KEY).value(isActivity);
if (icon != null) {
byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon);
@@ -475,7 +477,7 @@
public Pair<ItemInfo, Object> getItemInfo() {
if (isActivity) {
- WorkspaceItemInfo si = createWorkspaceItemInfo(data,
+ WorkspaceItemInfo si = createWorkspaceItemInfo(data, user,
LauncherAppState.getInstance(mContext));
si.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
@@ -500,7 +502,7 @@
return Pair.create(widgetInfo, providerInfo);
} else {
WorkspaceItemInfo itemInfo =
- createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
+ createWorkspaceItemInfo(data, user, LauncherAppState.getInstance(mContext));
return Pair.create(itemInfo, null);
}
}
@@ -618,7 +620,8 @@
return new PendingInstallShortcutInfo(info, original.mContext);
}
- private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, LauncherAppState app) {
+ private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, UserHandle user,
+ LauncherAppState app) {
if (data == null) {
Log.e(TAG, "Can't construct WorkspaceItemInfo with null data");
return null;
@@ -635,10 +638,7 @@
}
final WorkspaceItemInfo info = new WorkspaceItemInfo();
-
- // Only support intents for current user for now. Intents sent from other
- // users wouldn't get here without intent forwarding anyway.
- info.user = Process.myUserHandle();
+ info.user = user;
BitmapInfo iconInfo = null;
LauncherIcons li = LauncherIcons.obtain(app.getContext());
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 67c1a04..3dce9fc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -31,6 +31,10 @@
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.LoggerUtils.newTarget;
+import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
+import static com.android.launcher3.popup.SystemShortcut.DISMISS_PREDICTION;
+import static com.android.launcher3.popup.SystemShortcut.INSTALL;
+import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.testing.TestProtocol.CRASH_ADD_CUSTOM_SHORTCUT;
@@ -58,6 +62,7 @@
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Parcelable;
import android.os.Process;
@@ -82,6 +87,8 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore;
@@ -111,11 +118,12 @@
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -135,6 +143,7 @@
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.ViewOnDrawExecutor;
@@ -166,6 +175,7 @@
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Stream;
/**
* Default launcher application.
@@ -294,12 +304,11 @@
*/
private PendingRequestArgs mPendingRequestArgs;
// Request id for any pending activity result
- private int mPendingActivityRequestCode = -1;
+ protected int mPendingActivityRequestCode = -1;
public ViewGroupFocusHelper mFocusHandler;
private RotationHelper mRotationHelper;
- private Runnable mCancelTouchController;
final Handler mHandler = new Handler();
private final Runnable mHandleDeferredResume = this::handleDeferredResume;
@@ -350,7 +359,6 @@
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new LauncherStateManager(this);
- UiFactory.onCreate(this);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this,
@@ -474,7 +482,6 @@
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
- UiFactory.onEnterAnimationComplete(this);
mAllAppsController.highlightWorkTabIfNecessary();
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
}
@@ -488,7 +495,6 @@
}
mOldConfig.setTo(newConfig);
- UiFactory.onLauncherStateOrResumeChanged(this);
super.onConfigurationChanged(newConfig);
}
@@ -504,7 +510,7 @@
public void reapplyUi() {
if (supportsFakeLandscapeUI()) {
mRotationMode = mStableDeviceProfile == null
- ? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile);
+ ? RotationMode.NORMAL : getFakeRotationMode(mDeviceProfile);
}
getRootView().dispatchInsets();
getStateManager().reapplyState(true /* cancelCurrentAnimation */);
@@ -567,7 +573,7 @@
if (supportsFakeLandscapeUI() && mDeviceProfile.isVerticalBarLayout()) {
mStableDeviceProfile = mDeviceProfile.inv.portraitProfile;
- mRotationMode = UiFactory.getRotationMode(mDeviceProfile);
+ mRotationMode = getFakeRotationMode(mDeviceProfile);
} else {
mStableDeviceProfile = null;
mRotationMode = RotationMode.NORMAL;
@@ -933,8 +939,6 @@
NotificationListener.removeNotificationsChangedListener();
getStateManager().moveToRestState();
- UiFactory.onLauncherStateOrResumeChanged(this);
-
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
}
@@ -957,7 +961,6 @@
logStopAndResume(Action.Command.RESUME);
getUserEventDispatcher().startSession();
- UiFactory.onLauncherStateOrResumeChanged(this);
AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
// Process any items that were added while Launcher was away.
@@ -972,15 +975,17 @@
DiscoveryBounce.showForHomeIfNeeded(this);
- if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
- UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode);
- }
+ onDeferredResumed();
+ addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
+
mDeferredResumePending = false;
} else {
mDeferredResumePending = true;
}
}
+ protected void onDeferredResumed() { }
+
private void logStopAndResume(int command) {
int containerType = mStateManager.getState().containerType;
if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
@@ -1034,12 +1039,14 @@
if (mDeferOverlayCallbacks) {
scheduleDeferredCheck();
}
+ addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
}
public void onStateSetEnd(LauncherState state) {
getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
getWorkspace().setClipChildren(!state.disablePageClipping);
finishAutoCancelActionMode();
+ removeActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
}
@Override
@@ -1084,18 +1091,6 @@
}
}
- @Override
- protected void onUserLeaveHint() {
- super.onUserLeaveHint();
- UiFactory.onLauncherStateOrResumeChanged(this);
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- mStateManager.onWindowFocusChanged();
- }
-
class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
public void onScrollChanged(float progress) {
@@ -1159,7 +1154,6 @@
// Setup the drag layer
mDragLayer.setup(mDragController, mWorkspace);
- mCancelTouchController = UiFactory.enableLiveUIChanges(this);
mWorkspace.setup(mDragController);
// Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
@@ -1537,11 +1531,6 @@
mWorkspace.removeFolderListeners();
PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this);
- if (mCancelTouchController != null) {
- mCancelTouchController.run();
- mCancelTouchController = null;
- }
-
// Stop callbacks from LauncherModel
// It's possible to receive onDestroy after a new Launcher activity has
// been created. In this case, don't interfere with the new Launcher.
@@ -1577,10 +1566,7 @@
if (requestCode != -1) {
mPendingActivityRequestCode = requestCode;
}
- if (requestCode == -1
- || !UiFactory.startActivityForResult(this, intent, requestCode, options)) {
- super.startActivityForResult(intent, requestCode, options);
- }
+ super.startActivityForResult(intent, requestCode, options);
}
@Override
@@ -1589,14 +1575,11 @@
if (requestCode != -1) {
mPendingActivityRequestCode = requestCode;
}
- if (requestCode == -1 || !UiFactory.startIntentSenderForResult(this, intent, requestCode,
- fillInIntent, flagsMask, flagsValues, extraFlags, options)) {
- try {
- super.startIntentSenderForResult(intent, requestCode,
- fillInIntent, flagsMask, flagsValues, extraFlags, options);
- } catch (IntentSender.SendIntentException e) {
- throw new ActivityNotFoundException();
- }
+ try {
+ super.startIntentSenderForResult(intent, requestCode,
+ fillInIntent, flagsMask, flagsValues, extraFlags, options);
+ } catch (IntentSender.SendIntentException e) {
+ throw new ActivityNotFoundException();
}
}
@@ -1934,7 +1917,6 @@
// This clears all widget bitmaps from the widget tray
// TODO(hyunyoungs)
}
- UiFactory.onTrimMemory(this, level);
}
@Override
@@ -2653,16 +2635,40 @@
return super.onKeyUp(keyCode, event);
}
- public static Launcher getLauncher(Context context) {
- return (Launcher) fromContext(context);
+ protected StateHandler[] createStateHandlers() {
+ return new StateHandler[] { getAllAppsController(), getWorkspace() };
}
+ public TouchController[] createTouchControllers() {
+ return new TouchController[] {getDragController(), new AllAppsSwipeController(this)};
+ }
+
+ protected RotationMode getFakeRotationMode(DeviceProfile deviceProfile) {
+ return RotationMode.NORMAL;
+ }
+
+ protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
+ return new ScaleAndTranslation(1.1f, 0f, 0f);
+ }
+
+ public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { }
+
+ public void onDragLayerHierarchyChanged() { }
+
@Override
public void returnToHomescreen() {
super.returnToHomescreen();
getStateManager().goToState(LauncherState.NORMAL);
}
+ public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+ return Stream.of(APP_INFO, WIDGETS, INSTALL, DISMISS_PREDICTION);
+ }
+
+ public static Launcher getLauncher(Context context) {
+ return (Launcher) fromContext(context);
+ }
+
/**
* Just a wrapper around the type cast to allow easier tracking of calls.
*/
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index fc2e953..b0b213c 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -93,10 +93,6 @@
private boolean mModelLoaded;
public boolean isModelLoaded() {
synchronized (mLock) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "isModelLoaded: " + mModelLoaded + ", " + mLoaderTask);
- }
return mModelLoaded && mLoaderTask == null;
}
}
@@ -326,9 +322,6 @@
public void stopLoader() {
synchronized (mLock) {
LoaderTask oldTask = mLoaderTask;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "LauncherModel.stopLoader");
- }
mLoaderTask = null;
if (oldTask != null) {
oldTask.stopLocked();
@@ -340,10 +333,6 @@
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LauncherModel.startLoaderForResults " + mLoaderTask);
- }
// Always post the loader task, instead of running directly (even on same thread) so
// that we exit any nested synchronized blocks
@@ -445,10 +434,6 @@
public void close() {
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LauncherModel.close " + mLoaderTask + ", " + mTask);
- }
if (mLoaderTask == mTask) {
mLoaderTask = null;
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 6e2626b..d2b447b 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -26,9 +26,11 @@
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -36,14 +38,11 @@
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.view.animation.Interpolator;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.states.SpringLoadedState;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.uioverrides.states.AllAppsState;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -210,7 +209,7 @@
}
public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
- return UiFactory.getOverviewScaleAndTranslationForNormalState(launcher);
+ return launcher.getOverviewScaleAndTranslationForNormalState();
}
public float getOverviewFullscreenProgress() {
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 848e19f..daf270b 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -24,7 +24,8 @@
import android.animation.AnimatorSet;
import android.os.Handler;
import android.os.Looper;
-import android.util.Log;
+
+import androidx.annotation.IntDef;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -32,16 +33,12 @@
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.uioverrides.UiFactory;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import androidx.annotation.IntDef;
-
/**
* TODO: figure out what kind of tests we can write for this
*
@@ -146,7 +143,7 @@
public StateHandler[] getStateHandlers() {
if (mStateHandlers == null) {
- mStateHandlers = UiFactory.getStateHandler(mLauncher);
+ mStateHandlers = mLauncher.createStateHandlers();
}
return mStateHandlers;
}
@@ -414,7 +411,6 @@
// Only disable clipping if needed, otherwise leave it as previous value.
mLauncher.getWorkspace().setClipChildren(false);
}
- UiFactory.onLauncherStateOrResumeChanged(mLauncher);
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onStateTransitionStart(state);
@@ -435,8 +431,6 @@
setRestState(null);
}
- UiFactory.onLauncherStateOrResumeChanged(mLauncher);
-
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onStateTransitionComplete(state);
}
@@ -444,10 +438,6 @@
AccessibilityManagerCompat.sendStateEventToTest(mLauncher, state.ordinal);
}
- public void onWindowFocusChanged() {
- UiFactory.onLauncherStateOrFocusChanged(mLauncher);
- }
-
public LauncherState getLastState() {
return mLastStableState;
}
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index 23795c5..71bd92f 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -28,7 +28,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.ContentWriter;
import java.util.Arrays;
@@ -192,7 +192,7 @@
}
disabledMessage = shortcutInfo.getDisabledMessage();
- Person[] persons = UiFactory.getPersons(shortcutInfo);
+ Person[] persons = ApiWrapper.getPersons(shortcutInfo);
personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
: Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index cdc7061..8823bde 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -57,7 +57,6 @@
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.Transposable;
@@ -121,7 +120,7 @@
}
public void recreateControllers() {
- mControllers = UiFactory.createTouchControllers(mActivity);
+ mControllers = mActivity.createTouchControllers();
}
public ViewGroupFocusHelper getFocusIndicatorHelper() {
@@ -477,14 +476,14 @@
public void onViewAdded(View child) {
super.onViewAdded(child);
updateChildIndices();
- UiFactory.onLauncherStateOrFocusChanged(mActivity);
+ mActivity.onDragLayerHierarchyChanged();
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
updateChildIndices();
- UiFactory.onLauncherStateOrFocusChanged(mActivity);
+ mActivity.onDragLayerHierarchyChanged();
}
@Override
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 07eb0d6..869dd94 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -32,7 +32,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.PendingAddItemInfo;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -68,7 +67,7 @@
public boolean init(Launcher launcher, boolean alreadyOnHome) {
super.init(launcher, alreadyOnHome);
if (!alreadyOnHome) {
- UiFactory.useFadeOutAnimationForLauncherStart(launcher, mCancelSignal);
+ launcher.useFadeOutAnimationForLauncherStart(mCancelSignal);
}
return false;
}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 227bb22..2d62c9e 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -117,25 +117,30 @@
}
SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user,
packageName);
+ List<LauncherActivityInfo> activities = launcherApps
+ .getActivityList(packageName, item.user);
+ boolean hasActivity = activities != null && !activities.isEmpty();
+
if (sessionInfo == null) {
- List<LauncherActivityInfo> activities = launcherApps
- .getActivityList(packageName, item.user);
- if (activities != null && !activities.isEmpty()) {
- // App was installed while launcher was in the background.
- itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
- .makeWorkspaceItem();
- WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
- wii.title = "";
- wii.applyFrom(app.getIconCache().getDefaultIcon(item.user));
- app.getIconCache().getTitleAndIcon(wii,
- ((WorkspaceItemInfo) itemInfo).usingLowResIcon());
- } else {
+ if (!hasActivity) {
// Session was cancelled, do not add.
continue;
}
} else {
workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
}
+
+ if (hasActivity) {
+ // App was installed while launcher was in the background,
+ // or app was already installed for another user.
+ itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
+ .makeWorkspaceItem();
+ WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
+ wii.title = "";
+ wii.applyFrom(app.getIconCache().getDefaultIcon(item.user));
+ app.getIconCache().getTitleAndIcon(wii,
+ ((WorkspaceItemInfo) itemInfo).usingLowResIcon());
+ }
}
// Add the shortcut to the db
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 0a4f005..a00a6bd 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -300,8 +300,8 @@
public LooperIdleLock newIdleLock(Object lock) {
LooperIdleLock idleLock = new LooperIdleLock(lock, Looper.getMainLooper());
- // If we are not binding, there is no reason to wait for idle.
- if (mCallbacks.get() == null) {
+ // If we are not binding or if the main looper is already idle, there is no reason to wait
+ if (mCallbacks.get() == null || Looper.getMainLooper().getQueue().isIdle()) {
idleLock.queueIdle();
}
return idleLock;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 04f15fc..353a0d2 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -164,32 +164,15 @@
}
public void run() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LoaderTask1 " + this);
- }
synchronized (this) {
// Skip fast if we are already stopped.
if (mStopped) {
return;
}
}
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LoaderTask2 " + this);
- }
Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
- TimingLogger logger = TestProtocol.sDebugTracing ?
- new TimingLogger(TAG, "run") {
- @Override
- public void addSplit(String splitLabel) {
- super.addSplit(splitLabel);
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LoaderTask.addSplit " + splitLabel);
- }
- }
- : new TimingLogger(TAG, "run");
+ TimingLogger logger = new TimingLogger(TAG, "run");
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
loadWorkspace();
logger.addSplit("loadWorkspace");
@@ -258,10 +241,6 @@
updateHandler.finish();
logger.addSplit("finish icon update");
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LoaderTask3 " + this);
- }
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 717a7e9..021fb30 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -16,7 +16,7 @@
package com.android.launcher3.notification;
-import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
import android.app.Notification;
import android.content.Context;
@@ -30,7 +30,7 @@
import com.android.launcher3.R;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.Themes;
import java.util.List;
@@ -49,7 +49,7 @@
private final TextView mHeaderCount;
private final NotificationMainView mMainView;
private final NotificationFooterLayout mFooter;
- private final SwipeDetector mSwipeDetector;
+ private final SingleAxisSwipeDetector mSwipeDetector;
private final View mIconView;
private final View mHeader;
@@ -74,8 +74,8 @@
mHeader = container.findViewById(R.id.header);
mDivider = container.findViewById(R.id.divider);
- mSwipeDetector = new SwipeDetector(mContext, mMainView, HORIZONTAL);
- mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
+ mSwipeDetector = new SingleAxisSwipeDetector(mContext, mMainView, HORIZONTAL);
+ mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_BOTH, false);
mMainView.setSwipeDetector(mSwipeDetector);
mFooter.setContainer(this);
}
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 78627ec..b67adbb 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -38,8 +38,9 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.OverScroll;
-import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Themes;
@@ -48,7 +49,7 @@
* e.g. icon + title + text.
*/
@TargetApi(Build.VERSION_CODES.N)
-public class NotificationMainView extends FrameLayout implements SwipeDetector.Listener {
+public class NotificationMainView extends FrameLayout implements SingleAxisSwipeDetector.Listener {
private static FloatProperty<NotificationMainView> CONTENT_TRANSLATION =
new FloatProperty<NotificationMainView>("contentTranslation") {
@@ -75,7 +76,7 @@
private TextView mTextView;
private View mIconView;
- private SwipeDetector mSwipeDetector;
+ private SingleAxisSwipeDetector mSwipeDetector;
public NotificationMainView(Context context) {
this(context, null, 0);
@@ -107,7 +108,7 @@
mIconView = findViewById(R.id.popup_item_icon);
}
- public void setSwipeDetector(SwipeDetector swipeDetector) {
+ public void setSwipeDetector(SingleAxisSwipeDetector swipeDetector) {
mSwipeDetector = swipeDetector;
}
@@ -173,7 +174,7 @@
LauncherLogProto.ItemType.NOTIFICATION);
}
- // SwipeDetector.Listener's
+ // SingleAxisSwipeDetector.Listener's
@Override
public void onDragStart(boolean start) { }
@@ -187,7 +188,7 @@
}
@Override
- public void onDragEnd(float velocity, boolean fling) {
+ public void onDragEnd(float velocity) {
final boolean willExit;
final float endTranslation;
final float startTranslation = mTextAndBackground.getTranslationX();
@@ -195,7 +196,7 @@
if (!canChildBeDismissed()) {
willExit = false;
endTranslation = 0;
- } else if (fling) {
+ } else if (mSwipeDetector.isFling(velocity)) {
willExit = true;
endTranslation = velocity < 0 ? - getWidth() : getWidth();
} else if (Math.abs(startTranslation) > getWidth() / 2) {
@@ -206,7 +207,7 @@
endTranslation = 0;
}
- long duration = SwipeDetector.calculateDuration(velocity,
+ long duration = BaseSwipeDetector.calculateDuration(velocity,
(endTranslation - startTranslation) / getWidth());
mContentTranslateAnimator.removeAllListeners();
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 4833c26..2034926 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -77,6 +77,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* A container for shortcuts to deep links and notifications associated with an app.
@@ -213,7 +214,7 @@
final PopupContainerWithArrow container =
(PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
- container.populateAndShow(icon, itemInfo, SystemShortcutFactory.INSTANCE.get(launcher));
+ container.populateAndShow(icon, itemInfo);
return container;
}
@@ -238,8 +239,7 @@
}
}
- protected void populateAndShow(
- BubbleTextView icon, ItemInfo item, SystemShortcutFactory factory) {
+ protected void populateAndShow(BubbleTextView icon, ItemInfo item) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_CONTEXT_MENU, "populateAndShow");
}
@@ -247,7 +247,10 @@
populateAndShow(icon,
popupDataProvider.getShortcutCountForItem(item),
popupDataProvider.getNotificationKeysForItem(item),
- factory.getEnabledShortcuts(mLauncher, item));
+ mLauncher.getSupportedShortcuts()
+ .map(s -> s.getShortcut(mLauncher, item))
+ .filter(s -> s != null)
+ .collect(Collectors.toList()));
}
public ViewGroup getSystemShortcutContainerForTesting() {
@@ -382,8 +385,7 @@
@Override
public void onWidgetsBound() {
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
- SystemShortcut widgetInfo = new SystemShortcut.Widgets();
- View.OnClickListener onClickListener = widgetInfo.getOnClickListener(mLauncher, itemInfo);
+ SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
View widgetsView = null;
int count = mSystemShortcutContainer.getChildCount();
for (int i = 0; i < count; i++) {
@@ -394,7 +396,7 @@
}
}
- if (onClickListener != null && widgetsView == null) {
+ if (widgetInfo != null && widgetsView == null) {
// We didn't have any widgets cached but now there are some, so enable the shortcut.
if (mSystemShortcutContainer != this) {
initializeSystemShortcut(
@@ -407,7 +409,7 @@
close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
- } else if (onClickListener == null && widgetsView != null) {
+ } else if (widgetInfo == null && widgetsView != null) {
// No widgets exist, but we previously added the shortcut so remove it.
if (mSystemShortcutContainer != this) {
mSystemShortcutContainer.removeView(widgetsView);
@@ -430,8 +432,7 @@
info.setIconAndContentDescriptionFor((ImageView) view);
}
view.setTag(info);
- view.setOnClickListener(info.getOnClickListener(mLauncher,
- (ItemInfo) mOriginalIcon.getTag()));
+ view.setOnClickListener(info);
}
/**
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 5a5fbab..8751202 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -16,13 +16,19 @@
package com.android.launcher3.popup;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.RemoteAction;
+import android.content.Context;
import android.content.Intent;
-import android.os.Handler;
-import android.os.Looper;
+import android.os.Build;
import android.util.Log;
import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.ImageView;
+import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.AbstractFloatingView;
@@ -32,55 +38,75 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+@TargetApi(Build.VERSION_CODES.Q)
public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
private static final String TAG = "RemoteActionShortcut";
private static final boolean DEBUG = Utilities.IS_DEBUG_DEVICE;
private final RemoteAction mAction;
- public RemoteActionShortcut(RemoteAction action) {
- super(action.getIcon(), action.getTitle(), action.getContentDescription(),
- R.id.action_remote_action_shortcut);
+ public RemoteActionShortcut(RemoteAction action,
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
+ super(0, R.id.action_remote_action_shortcut, activity, itemInfo);
mAction = action;
}
@Override
- public View.OnClickListener getOnClickListener(
- final BaseDraggingActivity activity, final ItemInfo itemInfo) {
- return view -> {
- AbstractFloatingView.closeAllOpenViews(activity);
+ public void setIconAndLabelFor(View iconView, TextView labelView) {
+ mAction.getIcon().loadDrawableAsync(iconView.getContext(),
+ iconView::setBackground,
+ MAIN_EXECUTOR.getHandler());
+ labelView.setText(mAction.getTitle());
+ }
- final String actionIdentity = mAction.getTitle() + ", " +
- itemInfo.getTargetComponent().getPackageName();
- try {
- if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
- mAction.getActionIntent().send(
- activity,
- 0,
- new Intent().putExtra(
- Intent.EXTRA_PACKAGE_NAME,
- itemInfo.getTargetComponent().getPackageName()),
- (pendingIntent, intent, resultCode, resultData, resultExtras) -> {
- if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
- if (resultData != null && !resultData.isEmpty()) {
- Log.e(TAG, "Remote action returned result: " + actionIdentity
- + " : " + resultData);
- Toast.makeText(activity, resultData, Toast.LENGTH_SHORT).show();
- }
- },
- new Handler(Looper.getMainLooper()));
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
- Toast.makeText(activity, activity.getString(
- R.string.remote_action_failed,
- mAction.getTitle()),
- Toast.LENGTH_SHORT)
- .show();
- }
+ @Override
+ public void setIconAndContentDescriptionFor(ImageView view) {
+ mAction.getIcon().loadDrawableAsync(view.getContext(),
+ view::setImageDrawable,
+ MAIN_EXECUTOR.getHandler());
+ view.setContentDescription(mAction.getContentDescription());
+ }
- activity.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
- LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
- };
+ @Override
+ public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
+ return new AccessibilityNodeInfo.AccessibilityAction(
+ R.id.action_remote_action_shortcut, mAction.getContentDescription());
+ }
+
+ @Override
+ public void onClick(View view) {
+ AbstractFloatingView.closeAllOpenViews(mTarget);
+
+ final String actionIdentity = mAction.getTitle() + ", "
+ + mItemInfo.getTargetComponent().getPackageName();
+ try {
+ if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
+ mAction.getActionIntent().send(
+ mTarget,
+ 0,
+ new Intent().putExtra(
+ Intent.EXTRA_PACKAGE_NAME,
+ mItemInfo.getTargetComponent().getPackageName()),
+ (pendingIntent, intent, resultCode, resultData, resultExtras) -> {
+ if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
+ if (resultData != null && !resultData.isEmpty()) {
+ Log.e(TAG, "Remote action returned result: " + actionIdentity
+ + " : " + resultData);
+ Toast.makeText(mTarget, resultData, Toast.LENGTH_SHORT).show();
+ }
+ },
+ MAIN_EXECUTOR.getHandler());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
+ Toast.makeText(mTarget, mTarget.getString(
+ R.string.remote_action_failed,
+ mAction.getTitle()),
+ Toast.LENGTH_SHORT)
+ .show();
+ }
+
+ mTarget.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
+ LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
}
@Override
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index a87b7b8..222c6c9 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -5,14 +5,13 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
-import android.graphics.drawable.Icon;
-import android.os.Handler;
-import android.os.Looper;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
@@ -39,41 +38,30 @@
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
* @param <T>
*/
-public abstract class SystemShortcut<T extends BaseDraggingActivity>
- extends ItemInfo {
+public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo
+ implements View.OnClickListener {
+
private final int mIconResId;
private final int mLabelResId;
- private final Icon mIcon;
- private final CharSequence mLabel;
- private final CharSequence mContentDescription;
private final int mAccessibilityActionId;
- public SystemShortcut(int iconResId, int labelResId) {
+ protected final T mTarget;
+ protected final ItemInfo mItemInfo;
+
+ public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) {
mIconResId = iconResId;
mLabelResId = labelResId;
mAccessibilityActionId = labelResId;
- mIcon = null;
- mLabel = null;
- mContentDescription = null;
+ mTarget = target;
+ mItemInfo = itemInfo;
}
- public SystemShortcut(Icon icon, CharSequence label, CharSequence contentDescription,
- int accessibilityActionId) {
- mIcon = icon;
- mLabel = label;
- mContentDescription = contentDescription;
- mAccessibilityActionId = accessibilityActionId;
- mIconResId = 0;
- mLabelResId = 0;
- }
-
- public SystemShortcut(SystemShortcut other) {
+ public SystemShortcut(SystemShortcut<T> other) {
mIconResId = other.mIconResId;
mLabelResId = other.mLabelResId;
- mIcon = other.mIcon;
- mLabel = other.mLabel;
- mContentDescription = other.mContentDescription;
mAccessibilityActionId = other.mAccessibilityActionId;
+ mTarget = other.mTarget;
+ mItemInfo = other.mItemInfo;
}
/**
@@ -84,150 +72,135 @@
}
public void setIconAndLabelFor(View iconView, TextView labelView) {
- if (mIcon != null) {
- mIcon.loadDrawableAsync(iconView.getContext(),
- iconView::setBackground,
- new Handler(Looper.getMainLooper()));
- } else {
- iconView.setBackgroundResource(mIconResId);
- }
-
- if (mLabel != null) {
- labelView.setText(mLabel);
- } else {
- labelView.setText(mLabelResId);
- }
+ iconView.setBackgroundResource(mIconResId);
+ labelView.setText(mLabelResId);
}
public void setIconAndContentDescriptionFor(ImageView view) {
- if (mIcon != null) {
- mIcon.loadDrawableAsync(view.getContext(),
- view::setImageDrawable,
- new Handler(Looper.getMainLooper()));
- } else {
- view.setImageResource(mIconResId);
- }
-
- view.setContentDescription(getContentDescription(view.getContext()));
- }
-
- private CharSequence getContentDescription(Context context) {
- return mContentDescription != null ? mContentDescription : context.getText(mLabelResId);
+ view.setImageResource(mIconResId);
+ view.setContentDescription(view.getContext().getText(mLabelResId));
}
public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
- return new AccessibilityNodeInfo.AccessibilityAction(mAccessibilityActionId,
- getContentDescription(context));
+ return new AccessibilityNodeInfo.AccessibilityAction(
+ mAccessibilityActionId, context.getText(mLabelResId));
}
public boolean hasHandlerForAction(int action) {
return mAccessibilityActionId == action;
}
- public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo);
+ public interface Factory<T extends BaseDraggingActivity> {
+
+ @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo);
+ }
+
+ public static final Factory<Launcher> WIDGETS = (launcher, itemInfo) -> {
+ if (itemInfo.getTargetComponent() == null) return null;
+ final List<WidgetItem> widgets =
+ launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
+ itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
+ if (widgets == null) {
+ return null;
+ }
+ return new Widgets(launcher, itemInfo);
+ };
public static class Widgets extends SystemShortcut<Launcher> {
- public Widgets() {
- super(R.drawable.ic_widget, R.string.widget_button_text);
+ public Widgets(Launcher target, ItemInfo itemInfo) {
+ super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo);
}
@Override
- public View.OnClickListener getOnClickListener(final Launcher launcher,
- final ItemInfo itemInfo) {
- if (itemInfo.getTargetComponent() == null) return null;
- final List<WidgetItem> widgets =
- launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
- itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
- if (widgets == null) {
- return null;
- }
- return (view) -> {
- AbstractFloatingView.closeAllOpenViews(launcher);
- WidgetsBottomSheet widgetsBottomSheet =
- (WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
- R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
- widgetsBottomSheet.populateAndShow(itemInfo);
- launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.WIDGETS_BUTTON, view);
- };
+ public void onClick(View view) {
+ AbstractFloatingView.closeAllOpenViews(mTarget);
+ WidgetsBottomSheet widgetsBottomSheet =
+ (WidgetsBottomSheet) mTarget.getLayoutInflater().inflate(
+ R.layout.widgets_bottom_sheet, mTarget.getDragLayer(), false);
+ widgetsBottomSheet.populateAndShow(mItemInfo);
+ mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.WIDGETS_BUTTON, view);
}
}
+ public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
+
public static class AppInfo extends SystemShortcut {
- public AppInfo() {
- super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label);
+
+ public AppInfo(BaseDraggingActivity target, ItemInfo itemInfo) {
+ super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
+ itemInfo);
}
@Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- return (view) -> {
- dismissTaskMenuView(activity);
- Rect sourceBounds = activity.getViewBounds(view);
- new PackageManagerHelper(activity).startDetailsActivityForInfo(
- itemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
- activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.APPINFO_TARGET, view);
- };
+ public void onClick(View view) {
+ dismissTaskMenuView(mTarget);
+ Rect sourceBounds = mTarget.getViewBounds(view);
+ new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
+ mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
+ mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.APPINFO_TARGET, view);
}
}
+ public static Factory<BaseDraggingActivity> INSTALL = (activity, itemInfo) -> {
+ boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
+ && ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
+ WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
+ boolean isInstantApp = false;
+ if (itemInfo instanceof com.android.launcher3.AppInfo) {
+ com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
+ isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
+ }
+ boolean enabled = supportsWebUI || isInstantApp;
+ if (!enabled) {
+ return null;
+ }
+ return new Install(activity, itemInfo);
+ };
+
public static class Install extends SystemShortcut {
- public Install() {
- super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label);
+
+ public Install(BaseDraggingActivity target, ItemInfo itemInfo) {
+ super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
+ target, itemInfo);
}
@Override
- public View.OnClickListener getOnClickListener(
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo) &&
- ((WorkspaceItemInfo) itemInfo).hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
- boolean isInstantApp = false;
- if (itemInfo instanceof com.android.launcher3.AppInfo) {
- com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
- isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
- }
- boolean enabled = supportsWebUI || isInstantApp;
- if (!enabled) {
- return null;
- }
- return createOnClickListener(activity, itemInfo);
- }
-
- public View.OnClickListener createOnClickListener(
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- return view -> {
- Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
- itemInfo.getTargetComponent().getPackageName());
- activity.startActivitySafely(view, intent, itemInfo, null);
- AbstractFloatingView.closeAllOpenViews(activity);
- };
+ public void onClick(View view) {
+ Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
+ mItemInfo.getTargetComponent().getPackageName());
+ mTarget.startActivitySafely(view, intent, mItemInfo, null);
+ AbstractFloatingView.closeAllOpenViews(mTarget);
}
}
+ public static Factory<Launcher> DISMISS_PREDICTION = (launcher, itemInfo) -> {
+ if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
+ if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null;
+ return new DismissPrediction(launcher, itemInfo);
+ };
+
public static class DismissPrediction extends SystemShortcut<Launcher> {
- public DismissPrediction() {
- super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label);
+ public DismissPrediction(Launcher launcher, ItemInfo itemInfo) {
+ super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label, launcher,
+ itemInfo);
}
@Override
- public View.OnClickListener getOnClickListener(Launcher activity, ItemInfo itemInfo) {
- if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
- if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null;
- return (view) -> {
- PopupContainerWithArrow.closeAllOpenViews(activity);
- activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
- AppLaunchTracker.INSTANCE.get(view.getContext())
- .onDismissApp(itemInfo.getTargetComponent(),
- itemInfo.user,
- AppLaunchTracker.CONTAINER_PREDICTIONS);
- };
+ public void onClick(View view) {
+ PopupContainerWithArrow.closeAllOpenViews(mTarget);
+ mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
+ AppLaunchTracker.INSTANCE.get(view.getContext()).onDismissApp(
+ mItemInfo.getTargetComponent(),
+ mItemInfo.user,
+ AppLaunchTracker.CONTAINER_PREDICTIONS);
}
}
- protected static void dismissTaskMenuView(BaseDraggingActivity activity) {
+ public static void dismissTaskMenuView(BaseDraggingActivity activity) {
AbstractFloatingView.closeOpenViews(activity, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
}
diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java
deleted file mode 100644
index dfcc2f8..0000000
--- a/src/com/android/launcher3/popup/SystemShortcutFactory.java
+++ /dev/null
@@ -1,61 +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.popup;
-
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SystemShortcutFactory implements ResourceBasedOverride {
-
- public static final MainThreadInitializedObject<SystemShortcutFactory> INSTANCE =
- forOverride(SystemShortcutFactory.class, R.string.system_shortcut_factory_class);
-
- /** Note that these are in order of priority. */
- private final SystemShortcut[] mAllShortcuts;
-
- @SuppressWarnings("unused")
- public SystemShortcutFactory() {
- this(new SystemShortcut.AppInfo(),
- new SystemShortcut.Widgets(),
- new SystemShortcut.Install(),
- new SystemShortcut.DismissPrediction());
- }
-
- protected SystemShortcutFactory(SystemShortcut... shortcuts) {
- mAllShortcuts = shortcuts;
- }
-
- public @NonNull List<SystemShortcut> getEnabledShortcuts(Launcher launcher, ItemInfo info) {
- List<SystemShortcut> systemShortcuts = new ArrayList<>();
- for (SystemShortcut systemShortcut : mAllShortcuts) {
- if (systemShortcut.getOnClickListener(launcher, info) != null) {
- systemShortcuts.add(systemShortcut);
- }
- }
-
- return systemShortcuts;
- }
-}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index d0e648f..140a06a 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -177,11 +177,6 @@
}
protected boolean isLauncherInitialized() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "isLauncherInitialized " + Launcher.ACTIVITY_TRACKER.getCreatedActivity() + ", "
- + LauncherAppState.getInstance(mContext).getModel().isModelLoaded());
- }
return Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null
|| LauncherAppState.getInstance(mContext).getModel().isModelLoaded();
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 923c466..1766814 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -85,6 +85,5 @@
public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
public static final String APP_NOT_DISABLED = "b/139891609";
public static final String NO_CONTEXT_MENU = "b/141770616";
- public static final String LAUNCHER_DIDNT_INITIALIZE = "b/142514365";
public static final String CRASH_ADD_CUSTOM_SHORTCUT = "b/141568904";
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index c5ba5ba..60f6ee9 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -53,7 +53,7 @@
* TouchController for handling state changes
*/
public abstract class AbstractStateChangeTouchController
- implements TouchController, SwipeDetector.Listener {
+ implements TouchController, SingleAxisSwipeDetector.Listener {
// Progress after which the transition is assumed to be a success in case user does not fling
public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
@@ -65,8 +65,8 @@
protected final long ATOMIC_DURATION = getAtomicDuration();
protected final Launcher mLauncher;
- protected final SwipeDetector mDetector;
- protected final SwipeDetector.Direction mSwipeDirection;
+ protected final SingleAxisSwipeDetector mDetector;
+ protected final SingleAxisSwipeDetector.Direction mSwipeDirection;
private boolean mNoIntercept;
private boolean mIsLogContainerSet;
@@ -101,9 +101,9 @@
private float mAtomicComponentsStartProgress;
- public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
+ public AbstractStateChangeTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
mLauncher = l;
- mDetector = new SwipeDetector(l, this, dir);
+ mDetector = new SingleAxisSwipeDetector(l, this, dir);
mSwipeDirection = dir;
}
@@ -127,7 +127,7 @@
boolean ignoreSlopWhenSettling = false;
if (mCurrentAnimation != null) {
- directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ directionsToDetectScroll = SingleAxisSwipeDetector.DIRECTION_BOTH;
ignoreSlopWhenSettling = true;
} else {
directionsToDetectScroll = getSwipeDirection();
@@ -152,10 +152,10 @@
LauncherState fromState = mLauncher.getStateManager().getState();
int swipeDirection = 0;
if (getTargetState(fromState, true /* isDragTowardPositive */) != fromState) {
- swipeDirection |= SwipeDetector.DIRECTION_POSITIVE;
+ swipeDirection |= SingleAxisSwipeDetector.DIRECTION_POSITIVE;
}
if (getTargetState(fromState, false /* isDragTowardPositive */) != fromState) {
- swipeDirection |= SwipeDetector.DIRECTION_NEGATIVE;
+ swipeDirection |= SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
}
return swipeDirection;
}
@@ -369,7 +369,8 @@
}
@Override
- public void onDragEnd(float velocity, boolean fling) {
+ public void onDragEnd(float velocity) {
+ boolean fling = mDetector.isFling(velocity);
final int logAction = fling ? Touch.FLING : Touch.SWIPE;
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
@@ -406,7 +407,7 @@
} else {
startProgress = Utilities.boundToRange(progress
+ velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
- duration = SwipeDetector.calculateDuration(velocity,
+ duration = BaseSwipeDetector.calculateDuration(velocity,
endProgress - Math.max(progress, 0)) * durationMultiplier;
}
} else {
@@ -424,7 +425,7 @@
} else {
startProgress = Utilities.boundToRange(progress
+ velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
- duration = SwipeDetector.calculateDuration(velocity,
+ duration = BaseSwipeDetector.calculateDuration(velocity,
Math.min(progress, 1) - endProgress) * durationMultiplier;
}
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
similarity index 75%
rename from src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
rename to src/com/android/launcher3/touch/AllAppsSwipeController.java
index bd6ea50..31a5d79 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -1,4 +1,19 @@
-package com.android.launcher3.uioverrides;
+/**
+ * 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.
+ * 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.touch;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -9,8 +24,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationComponents;
-import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
@@ -21,7 +34,7 @@
private MotionEvent mTouchDownEvent;
public AllAppsSwipeController(Launcher l) {
- super(l, SwipeDetector.VERTICAL);
+ super(l, SingleAxisSwipeDetector.VERTICAL);
}
@Override
@@ -58,8 +71,8 @@
@Override
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
- return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent) ?
- ContainerType.HOTSEAT : ContainerType.WORKSPACE;
+ return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent)
+ ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
}
@Override
diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java
new file mode 100644
index 0000000..08d73d0
--- /dev/null
+++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java
@@ -0,0 +1,267 @@
+/*
+ * 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.touch;
+
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import android.graphics.PointF;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Scroll/drag/swipe gesture detector.
+ *
+ * Definition of swipe is different from android system in that this detector handles
+ * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
+ * swipe action happens.
+ *
+ * @see SingleAxisSwipeDetector
+ */
+public abstract class BaseSwipeDetector {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "BaseSwipeDetector";
+ private static final float ANIMATION_DURATION = 1200;
+ /** The minimum release velocity in pixels per millisecond that triggers fling.*/
+ private static final float RELEASE_VELOCITY_PX_MS = 1.0f;
+ private static final PointF sTempPoint = new PointF();
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ protected final boolean mIsRtl;
+ protected final float mTouchSlop;
+ protected final float mMaxVelocity;
+
+ private int mActivePointerId = INVALID_POINTER_ID;
+ private VelocityTracker mVelocityTracker;
+ private PointF mLastDisplacement = new PointF();
+ private PointF mDisplacement = new PointF();
+ protected PointF mSubtractDisplacement = new PointF();
+ private ScrollState mState = ScrollState.IDLE;
+
+ protected boolean mIgnoreSlopWhenSettling;
+
+ private enum ScrollState {
+ IDLE,
+ DRAGGING, // onDragStart, onDrag
+ SETTLING // onDragEnd
+ }
+
+ protected BaseSwipeDetector(@NonNull ViewConfiguration config, boolean isRtl) {
+ mTouchSlop = config.getScaledTouchSlop();
+ mMaxVelocity = config.getScaledMaximumFlingVelocity();
+ mIsRtl = isRtl;
+ }
+
+ public static long calculateDuration(float velocity, float progressNeeded) {
+ // TODO: make these values constants after tuning.
+ float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
+ float travelDistance = Math.max(0.2f, progressNeeded);
+ long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
+ if (DBG) {
+ Log.d(TAG, String.format(
+ "calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
+ }
+ return duration;
+ }
+
+ public int getDownX() {
+ return (int) mDownPos.x;
+ }
+
+ public int getDownY() {
+ return (int) mDownPos.y;
+ }
+ /**
+ * There's no touch and there's no animation.
+ */
+ public boolean isIdleState() {
+ return mState == ScrollState.IDLE;
+ }
+
+ public boolean isSettlingState() {
+ return mState == ScrollState.SETTLING;
+ }
+
+ public boolean isDraggingState() {
+ return mState == ScrollState.DRAGGING;
+ }
+
+ public boolean isDraggingOrSettling() {
+ return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
+ }
+
+ public void finishedScrolling() {
+ setState(ScrollState.IDLE);
+ }
+
+ public boolean isFling(float velocity) {
+ return Math.abs(velocity) > RELEASE_VELOCITY_PX_MS;
+ }
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ int actionMasked = ev.getActionMasked();
+ if (actionMasked == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ mLastDisplacement.set(0, 0);
+ mDisplacement.set(0, 0);
+
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ setState(ScrollState.DRAGGING);
+ }
+ break;
+ //case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mDisplacement.set(ev.getX(pointerIndex) - mDownPos.x,
+ ev.getY(pointerIndex) - mDownPos.y);
+ if (mIsRtl) {
+ mDisplacement.x = -mDisplacement.x;
+ }
+
+ // handle state and listener calls.
+ if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
+ setState(ScrollState.DRAGGING);
+ }
+ if (mState == ScrollState.DRAGGING) {
+ reportDragging(ev);
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // These are synthetic events and there is no need to update internal values.
+ if (mState == ScrollState.DRAGGING) {
+ setState(ScrollState.SETTLING);
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ //------------------- ScrollState transition diagram -----------------------------------
+ //
+ // IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
+ // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
+ // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
+ // SETTLING -> (View settled) -> IDLE
+
+ private void setState(ScrollState newState) {
+ if (DBG) {
+ Log.d(TAG, "setState:" + mState + "->" + newState);
+ }
+ // onDragStart and onDragEnd is reported ONLY on state transition
+ if (newState == ScrollState.DRAGGING) {
+ initializeDragging();
+ if (mState == ScrollState.IDLE) {
+ reportDragStart(false /* recatch */);
+ } else if (mState == ScrollState.SETTLING) {
+ reportDragStart(true /* recatch */);
+ }
+ }
+ if (newState == ScrollState.SETTLING) {
+ reportDragEnd();
+ }
+
+ mState = newState;
+ }
+
+ private void initializeDragging() {
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ mSubtractDisplacement.set(0, 0);
+ } else {
+ mSubtractDisplacement.x = mDisplacement.x > 0 ? mTouchSlop : -mTouchSlop;
+ mSubtractDisplacement.y = mDisplacement.y > 0 ? mTouchSlop : -mTouchSlop;
+ }
+ }
+
+ protected abstract boolean shouldScrollStart(PointF displacement);
+
+ private void reportDragStart(boolean recatch) {
+ reportDragStartInternal(recatch);
+ if (DBG) {
+ Log.d(TAG, "onDragStart recatch:" + recatch);
+ }
+ }
+
+ protected abstract void reportDragStartInternal(boolean recatch);
+
+ private void reportDragging(MotionEvent event) {
+ if (mDisplacement != mLastDisplacement) {
+ if (DBG) {
+ Log.d(TAG, String.format("onDrag disp=%s", mDisplacement));
+ }
+
+ mLastDisplacement.set(mDisplacement);
+ sTempPoint.set(mDisplacement.x - mSubtractDisplacement.x,
+ mDisplacement.y - mSubtractDisplacement.y);
+ reportDraggingInternal(sTempPoint, event);
+ }
+ }
+
+ protected abstract void reportDraggingInternal(PointF displacement, MotionEvent event);
+
+ private void reportDragEnd() {
+ mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
+ PointF velocity = new PointF(mVelocityTracker.getXVelocity() / 1000,
+ mVelocityTracker.getYVelocity() / 1000);
+ if (mIsRtl) {
+ velocity.x = -velocity.x;
+ }
+ if (DBG) {
+ Log.d(TAG, String.format("onScrollEnd disp=%.1s, velocity=%.1s",
+ mDisplacement, velocity));
+ }
+
+ reportDragEndInternal(velocity);
+ }
+
+ protected abstract void reportDragEndInternal(PointF velocity);
+}
diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
new file mode 100644
index 0000000..0bf2ff6
--- /dev/null
+++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ * 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.touch;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * One dimensional scroll/drag/swipe gesture detector (either HORIZONTAL or VERTICAL).
+ */
+public class SingleAxisSwipeDetector extends BaseSwipeDetector {
+
+ public static final int DIRECTION_POSITIVE = 1 << 0;
+ public static final int DIRECTION_NEGATIVE = 1 << 1;
+ public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
+
+ public static final Direction VERTICAL = new Direction() {
+
+ @Override
+ boolean isPositive(float displacement) {
+ // Up
+ return displacement < 0;
+ }
+
+ @Override
+ boolean isNegative(float displacement) {
+ // Down
+ return displacement > 0;
+ }
+
+ @Override
+ float extractDirection(PointF direction) {
+ return direction.y;
+ }
+
+ @Override
+ boolean canScrollStart(PointF displacement, float touchSlop) {
+ return Math.abs(displacement.y) >= Math.max(Math.abs(displacement.x), touchSlop);
+ }
+
+ };
+
+ public static final Direction HORIZONTAL = new Direction() {
+
+ @Override
+ boolean isPositive(float displacement) {
+ // Right
+ return displacement > 0;
+ }
+
+ @Override
+ boolean isNegative(float displacement) {
+ // Left
+ return displacement < 0;
+ }
+
+ @Override
+ float extractDirection(PointF direction) {
+ return direction.x;
+ }
+
+ @Override
+ boolean canScrollStart(PointF displacement, float touchSlop) {
+ return Math.abs(displacement.x) >= Math.max(Math.abs(displacement.y), touchSlop);
+ }
+ };
+
+ private final Direction mDir;
+ /* Client of this gesture detector can register a callback. */
+ private final Listener mListener;
+
+ private int mScrollDirections;
+
+ public SingleAxisSwipeDetector(@NonNull Context context, @NonNull Listener l,
+ @NonNull Direction dir) {
+ this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
+ }
+
+ @VisibleForTesting
+ protected SingleAxisSwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
+ @NonNull Direction dir, boolean isRtl) {
+ super(config, isRtl);
+ mListener = l;
+ mDir = dir;
+ }
+
+ public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
+ mScrollDirections = scrollDirectionFlags;
+ mIgnoreSlopWhenSettling = ignoreSlop;
+ }
+
+ public int getScrollDirections() {
+ return mScrollDirections;
+ }
+
+ /**
+ * Returns if the start drag was towards the positive direction or negative.
+ *
+ * @see #setDetectableScrollConditions(int, boolean)
+ * @see #DIRECTION_BOTH
+ */
+ public boolean wasInitialTouchPositive() {
+ return mDir.isPositive(mDir.extractDirection(mSubtractDisplacement));
+ }
+
+ @Override
+ protected boolean shouldScrollStart(PointF displacement) {
+ // Reject cases where the angle or slop condition is not met.
+ if (!mDir.canScrollStart(displacement, mTouchSlop)) {
+ return false;
+ }
+
+ // Check if the client is interested in scroll in current direction.
+ float displacementComponent = mDir.extractDirection(displacement);
+ return canScrollNegative(displacementComponent) || canScrollPositive(displacementComponent);
+ }
+
+ private boolean canScrollNegative(float displacement) {
+ return (mScrollDirections & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(displacement);
+ }
+
+ private boolean canScrollPositive(float displacement) {
+ return (mScrollDirections & DIRECTION_POSITIVE) > 0 && mDir.isPositive(displacement);
+ }
+
+ @Override
+ protected void reportDragStartInternal(boolean recatch) {
+ mListener.onDragStart(!recatch);
+ }
+
+ @Override
+ protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
+ mListener.onDrag(mDir.extractDirection(displacement), event);
+ }
+
+ @Override
+ protected void reportDragEndInternal(PointF velocity) {
+ float velocityComponent = mDir.extractDirection(velocity);
+ mListener.onDragEnd(velocityComponent);
+ }
+
+ /** Listener to receive updates on the swipe. */
+ public interface Listener {
+ void onDragStart(boolean start);
+
+ // TODO remove
+ boolean onDrag(float displacement);
+
+ default boolean onDrag(float displacement, MotionEvent event) {
+ return onDrag(displacement);
+ }
+
+ void onDragEnd(float velocity);
+ }
+
+ public abstract static class Direction {
+
+ abstract boolean isPositive(float displacement);
+
+ abstract boolean isNegative(float displacement);
+
+ /** Returns the part of the given {@link PointF} that is relevant to this direction. */
+ abstract float extractDirection(PointF point);
+
+ /** Reject cases where the angle or slop condition is not met. */
+ abstract boolean canScrollStart(PointF displacement, float touchSlop);
+
+ }
+}
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
deleted file mode 100644
index c38ca24..0000000
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ /dev/null
@@ -1,391 +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.touch;
-
-import static android.view.MotionEvent.INVALID_POINTER_ID;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.launcher3.Utilities;
-
-/**
- * One dimensional scroll/drag/swipe gesture detector.
- *
- * Definition of swipe is different from android system in that this detector handles
- * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
- * swipe action happens
- */
-public class SwipeDetector {
-
- private static final boolean DBG = false;
- private static final String TAG = "SwipeDetector";
- private static final float ANIMATION_DURATION = 1200;
- /** The minimum release velocity in pixels per millisecond that triggers fling.*/
- private static final float RELEASE_VELOCITY_PX_MS = 1.0f;
-
- public static final int DIRECTION_POSITIVE = 1 << 0;
- public static final int DIRECTION_NEGATIVE = 1 << 1;
- public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
-
- public static final Direction VERTICAL = new Direction() {
-
- @Override
- float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
- return ev.getY(pointerIndex) - refPoint.y;
- }
-
- @Override
- float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
- return Math.abs(ev.getX(pointerIndex) - downPos.x);
- }
-
- @Override
- float getVelocity(VelocityTracker tracker, boolean isRtl) {
- return tracker.getYVelocity();
- }
-
- @Override
- boolean isPositive(float displacement) {
- // Up
- return displacement < 0;
- }
-
- @Override
- boolean isNegative(float displacement) {
- // Down
- return displacement > 0;
- }
- };
-
- public static final Direction HORIZONTAL = new Direction() {
-
- @Override
- float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
- float displacement = ev.getX(pointerIndex) - refPoint.x;
- if (isRtl) {
- displacement = -displacement;
- }
- return displacement;
- }
-
- @Override
- float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
- return Math.abs(ev.getY(pointerIndex) - downPos.y);
- }
-
- @Override
- float getVelocity(VelocityTracker tracker, boolean isRtl) {
- float velocity = tracker.getXVelocity();
- if (isRtl) {
- velocity = -velocity;
- }
- return velocity;
- }
-
- @Override
- boolean isPositive(float displacement) {
- // Right
- return displacement > 0;
- }
-
- @Override
- boolean isNegative(float displacement) {
- // Left
- return displacement < 0;
- }
- };
-
- private final PointF mDownPos = new PointF();
- private final PointF mLastPos = new PointF();
- private final Direction mDir;
- private final boolean mIsRtl;
- private final float mTouchSlop;
- private final float mMaxVelocity;
- /* Client of this gesture detector can register a callback. */
- private final Listener mListener;
-
- private int mActivePointerId = INVALID_POINTER_ID;
- private VelocityTracker mVelocityTracker;
- private float mLastDisplacement;
- private float mDisplacement;
- private float mSubtractDisplacement;
- private boolean mIgnoreSlopWhenSettling;
- private int mScrollDirections;
- private ScrollState mState = ScrollState.IDLE;
-
- private enum ScrollState {
- IDLE,
- DRAGGING, // onDragStart, onDrag
- SETTLING // onDragEnd
- }
-
- public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
- this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
- }
-
- @VisibleForTesting
- protected SwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
- @NonNull Direction dir, boolean isRtl) {
- mListener = l;
- mDir = dir;
- mIsRtl = isRtl;
- mTouchSlop = config.getScaledTouchSlop();
- mMaxVelocity = config.getScaledMaximumFlingVelocity();
- }
-
- public static long calculateDuration(float velocity, float progressNeeded) {
- // TODO: make these values constants after tuning.
- float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
- float travelDistance = Math.max(0.2f, progressNeeded);
- long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
- if (DBG) {
- Log.d(TAG, String.format(
- "calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
- }
- return duration;
- }
-
- public int getDownX() {
- return (int) mDownPos.x;
- }
-
- public int getDownY() {
- return (int) mDownPos.y;
- }
- /**
- * There's no touch and there's no animation.
- */
- public boolean isIdleState() {
- return mState == ScrollState.IDLE;
- }
-
- public boolean isSettlingState() {
- return mState == ScrollState.SETTLING;
- }
-
- public boolean isDraggingState() {
- return mState == ScrollState.DRAGGING;
- }
-
- public boolean isDraggingOrSettling() {
- return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
- }
-
- public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
- mScrollDirections = scrollDirectionFlags;
- mIgnoreSlopWhenSettling = ignoreSlop;
- }
-
- public int getScrollDirections() {
- return mScrollDirections;
- }
-
- public void finishedScrolling() {
- setState(ScrollState.IDLE);
- }
-
- /**
- * Returns if the start drag was towards the positive direction or negative.
- *
- * @see #setDetectableScrollConditions(int, boolean)
- * @see #DIRECTION_BOTH
- */
- public boolean wasInitialTouchPositive() {
- return mDir.isPositive(mSubtractDisplacement);
- }
-
- public boolean onTouchEvent(MotionEvent ev) {
- int actionMasked = ev.getActionMasked();
- if (actionMasked == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
-
- switch (actionMasked) {
- case MotionEvent.ACTION_DOWN:
- mActivePointerId = ev.getPointerId(0);
- mDownPos.set(ev.getX(), ev.getY());
- mLastPos.set(mDownPos);
- mLastDisplacement = 0;
- mDisplacement = 0;
-
- if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
- setState(ScrollState.DRAGGING);
- }
- break;
- //case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_POINTER_UP:
- int ptrIdx = ev.getActionIndex();
- int ptrId = ev.getPointerId(ptrIdx);
- if (ptrId == mActivePointerId) {
- final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
- mDownPos.set(
- ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
- ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
- mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
- mActivePointerId = ev.getPointerId(newPointerIdx);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == INVALID_POINTER_ID) {
- break;
- }
- mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos, mIsRtl);
-
- // handle state and listener calls.
- if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
- setState(ScrollState.DRAGGING);
- }
- if (mState == ScrollState.DRAGGING) {
- reportDragging(ev);
- }
- mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- // These are synthetic events and there is no need to update internal values.
- if (mState == ScrollState.DRAGGING) {
- setState(ScrollState.SETTLING);
- }
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- break;
- default:
- break;
- }
- return true;
- }
-
- //------------------- ScrollState transition diagram -----------------------------------
- //
- // IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
- // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
- // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
- // SETTLING -> (View settled) -> IDLE
-
- private void setState(ScrollState newState) {
- if (DBG) {
- Log.d(TAG, "setState:" + mState + "->" + newState);
- }
- // onDragStart and onDragEnd is reported ONLY on state transition
- if (newState == ScrollState.DRAGGING) {
- initializeDragging();
- if (mState == ScrollState.IDLE) {
- reportDragStart(false /* recatch */);
- } else if (mState == ScrollState.SETTLING) {
- reportDragStart(true /* recatch */);
- }
- }
- if (newState == ScrollState.SETTLING) {
- reportDragEnd();
- }
-
- mState = newState;
- }
-
- private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
- // reject cases where the angle or slop condition is not met.
- if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)
- > Math.abs(mDisplacement)) {
- return false;
- }
-
- // Check if the client is interested in scroll in current direction.
- return ((mScrollDirections & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(mDisplacement))
- || ((mScrollDirections & DIRECTION_POSITIVE) > 0 && mDir.isPositive(mDisplacement));
- }
-
- private void reportDragStart(boolean recatch) {
- mListener.onDragStart(!recatch);
- if (DBG) {
- Log.d(TAG, "onDragStart recatch:" + recatch);
- }
- }
-
- private void initializeDragging() {
- if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
- mSubtractDisplacement = 0;
- } else if (mDisplacement > 0) {
- mSubtractDisplacement = mTouchSlop;
- } else {
- mSubtractDisplacement = -mTouchSlop;
- }
- }
-
- private void reportDragging(MotionEvent event) {
- if (mDisplacement != mLastDisplacement) {
- if (DBG) {
- Log.d(TAG, String.format("onDrag disp=%.1f", mDisplacement));
- }
-
- mLastDisplacement = mDisplacement;
- mListener.onDrag(mDisplacement - mSubtractDisplacement, event);
- }
- }
-
- private void reportDragEnd() {
- mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
- float velocity = mDir.getVelocity(mVelocityTracker, mIsRtl) / 1000;
- if (DBG) {
- Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
- mDisplacement, velocity));
- }
-
- mListener.onDragEnd(velocity, Math.abs(velocity) > RELEASE_VELOCITY_PX_MS);
- }
-
- /** Listener to receive updates on the swipe. */
- public interface Listener {
- void onDragStart(boolean start);
-
- boolean onDrag(float displacement);
-
- default boolean onDrag(float displacement, MotionEvent event) {
- return onDrag(displacement);
- }
-
- void onDragEnd(float velocity, boolean fling);
- }
-
- public abstract static class Direction {
-
- abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint,
- boolean isRtl);
-
- /**
- * Distance in pixels a touch can wander before we think the user is scrolling.
- */
- abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
-
- abstract float getVelocity(VelocityTracker tracker, boolean isRtl);
-
- abstract boolean isPositive(float displacement);
-
- abstract boolean isNegative(float displacement);
- }
-}
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
index b4f361f..4b931f4 100644
--- a/src/com/android/launcher3/util/ActivityTracker.java
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -45,13 +45,7 @@
}
public void onActivityDestroyed(T activity) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "onActivityDestroyed");
- }
if (mCurrentActivity.get() == activity) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "onActivityDestroyed: clear");
- }
mCurrentActivity.clear();
}
}
@@ -97,10 +91,6 @@
}
public boolean handleCreate(T activity) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "ActivityTracker.handleCreate " + mCurrentActivity.get() + " => " + activity);
- }
mCurrentActivity = new WeakReference<>(activity);
return handleIntent(activity, activity.getIntent(), false, false);
}
diff --git a/src/com/android/launcher3/util/LooperIdleLock.java b/src/com/android/launcher3/util/LooperIdleLock.java
index 2896535..f4ccf42 100644
--- a/src/com/android/launcher3/util/LooperIdleLock.java
+++ b/src/com/android/launcher3/util/LooperIdleLock.java
@@ -22,29 +22,30 @@
/**
* Utility class to block execution until the UI looper is idle.
*/
-public class LooperIdleLock implements MessageQueue.IdleHandler, Runnable {
+public class LooperIdleLock implements MessageQueue.IdleHandler {
private final Object mLock;
private boolean mIsLocked;
+ private Looper mLooper;
public LooperIdleLock(Object lock, Looper looper) {
mLock = lock;
+ mLooper = looper;
mIsLocked = true;
looper.getQueue().addIdleHandler(this);
}
@Override
- public void run() {
- Looper.myQueue().addIdleHandler(this);
- }
-
- @Override
public boolean queueIdle() {
synchronized (mLock) {
mIsLocked = false;
mLock.notify();
}
+ // Manually remove from the list in case we're calling this outside of the idle callbacks
+ // (this is Ok in the normal flow as well because MessageQueue makes a copy of all handlers
+ // before calling back)
+ mLooper.getQueue().removeIdleHandler(this);
return false;
}
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index a133f01..ec87e79 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -23,7 +23,6 @@
import android.os.IBinder;
import android.os.Message;
import android.view.inputmethod.InputMethodManager;
-import com.android.launcher3.uioverrides.UiFactory;
/**
* Utility class for offloading some class from UI thread
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
new file mode 100644
index 0000000..04741a1
--- /dev/null
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ * 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.util;
+
+import static android.os.VibrationEffect.createPredefined;
+import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Build;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+/**
+ * Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public class VibratorWrapper {
+
+ public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE =
+ new MainThreadInitializedObject<>(VibratorWrapper::new);
+
+ private static final VibrationEffect EFFECT_CLICK =
+ createPredefined(VibrationEffect.EFFECT_CLICK);
+
+ /**
+ * Haptic when entering overview.
+ */
+ public static final VibrationEffect OVERVIEW_HAPTIC = EFFECT_CLICK;
+
+ private final Vibrator mVibrator;
+ private final boolean mHasVibrator;
+
+ private boolean mIsHapticFeedbackEnabled;
+
+ public VibratorWrapper(Context context) {
+ mVibrator = context.getSystemService(Vibrator.class);
+ mHasVibrator = mVibrator.hasVibrator();
+ if (mHasVibrator) {
+ final ContentResolver resolver = context.getContentResolver();
+ mIsHapticFeedbackEnabled = isHapticFeedbackEnabled(resolver);
+ final ContentObserver observer = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mIsHapticFeedbackEnabled = isHapticFeedbackEnabled(resolver);
+ }
+ };
+ resolver.registerContentObserver(Settings.System.getUriFor(HAPTIC_FEEDBACK_ENABLED),
+ false /* notifyForDescendents */, observer);
+ } else {
+ mIsHapticFeedbackEnabled = false;
+ }
+ }
+
+ private boolean isHapticFeedbackEnabled(ContentResolver resolver) {
+ return Settings.System.getInt(resolver, HAPTIC_FEEDBACK_ENABLED, 0) == 1;
+ }
+
+ /** Vibrates with the given effect if haptic feedback is available and enabled. */
+ public void vibrate(VibrationEffect vibrationEffect) {
+ if (mHasVibrator && mIsHapticFeedbackEnabled) {
+ UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(vibrationEffect));
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index 61ba4e5..5a131c8 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -55,7 +55,9 @@
mLoadAnimationCompleted = true;
}
- attachObserver();
+ if (mAttachedView.isAttachedToWindow()) {
+ attachObserver();
+ }
}
private void attachObserver() {
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index a4518ba..195a77a 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -32,13 +32,14 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.BaseSwipeDetector;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
/**
* Extension of AbstractFloatingView with common methods for sliding in from bottom
*/
public abstract class AbstractSlideInView extends AbstractFloatingView
- implements SwipeDetector.Listener {
+ implements SingleAxisSwipeDetector.Listener {
protected static Property<AbstractSlideInView, Float> TRANSLATION_SHIFT =
new Property<AbstractSlideInView, Float>(Float.class, "translationShift") {
@@ -57,7 +58,7 @@
protected static final float TRANSLATION_SHIFT_OPENED = 0f;
protected final Launcher mLauncher;
- protected final SwipeDetector mSwipeDetector;
+ protected final SingleAxisSwipeDetector mSwipeDetector;
protected final ObjectAnimator mOpenCloseAnimator;
protected View mContent;
@@ -73,7 +74,8 @@
mLauncher = Launcher.getLauncher(context);
mScrollInterpolator = Interpolators.SCROLL_CUBIC;
- mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
+ mSwipeDetector = new SingleAxisSwipeDetector(context, this,
+ SingleAxisSwipeDetector.VERTICAL);
mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@@ -97,7 +99,7 @@
}
int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
- SwipeDetector.DIRECTION_NEGATIVE : 0;
+ SingleAxisSwipeDetector.DIRECTION_NEGATIVE : 0;
mSwipeDetector.setDetectableScrollConditions(
directionsToDetectScroll, false);
mSwipeDetector.onTouchEvent(ev);
@@ -122,7 +124,7 @@
return mIsOpen && mOpenCloseAnimator.isRunning();
}
- /* SwipeDetector.Listener */
+ /* SingleAxisSwipeDetector.Listener */
@Override
public void onDragStart(boolean start) { }
@@ -136,17 +138,17 @@
}
@Override
- public void onDragEnd(float velocity, boolean fling) {
- if ((fling && velocity > 0) || mTranslationShift > 0.5f) {
+ public void onDragEnd(float velocity) {
+ if ((mSwipeDetector.isFling(velocity) && velocity > 0) || mTranslationShift > 0.5f) {
mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
- mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(
+ mOpenCloseAnimator.setDuration(BaseSwipeDetector.calculateDuration(
velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift));
close(true);
} else {
mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(
TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
mOpenCloseAnimator.setDuration(
- SwipeDetector.calculateDuration(velocity, mTranslationShift))
+ BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
.setInterpolator(Interpolators.DEACCEL);
mOpenCloseAnimator.start();
}
diff --git a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
new file mode 100644
index 0000000..60eb304
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ * 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.systemui.plugins;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this interface to receive a callback when the user swipes right
+ * to left on the gesture area. It won't fire if the user has quick switched to a previous app
+ * (swiped right) and the current app isn't yet the active one (i.e., if swiping left would take
+ * the user to a more recent app).
+ */
+@ProvidesInterface(action = com.android.systemui.plugins.OverscrollPlugin.ACTION,
+ version = com.android.systemui.plugins.OverlayPlugin.VERSION)
+public interface OverscrollPlugin extends Plugin {
+
+ String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERSCROLL";
+ int VERSION = 1;
+
+ String DEVICE_STATE_LOCKED = "Locked";
+ String DEVICE_STATE_LAUNCHER = "Launcher";
+ String DEVICE_STATE_APP = "App";
+ String DEVICE_STATE_UNKNOWN = "Unknown";
+
+ /**
+ * Called when the user completed a right to left swipe in the gesture area.
+ *
+ * @param deviceState One of the DEVICE_STATE_* constants.
+ */
+ void onOverscroll(String deviceState);
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
new file mode 100644
index 0000000..5407ea3
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.app.Activity;
+import android.app.Person;
+import android.content.pm.ShortcutInfo;
+
+import com.android.launcher3.Utilities;
+
+import java.io.PrintWriter;
+
+public class ApiWrapper {
+
+ public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+ return false;
+ }
+
+ public static Person[] getPersons(ShortcutInfo si) {
+ return Utilities.EMPTY_PERSON_ARRAY;
+ }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
deleted file mode 100644
index 606c990..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ /dev/null
@@ -1,102 +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.app.Activity;
-import android.app.Person;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.graphics.RotationMode;
-import com.android.launcher3.util.TouchController;
-
-import java.io.PrintWriter;
-
-public class UiFactory {
-
- public static TouchController[] createTouchControllers(Launcher launcher) {
- return new TouchController[] {
- launcher.getDragController(), new AllAppsSwipeController(launcher)};
- }
-
- public static Runnable enableLiveUIChanges(Launcher l) {
- return null;
- }
-
- public static StateHandler[] getStateHandler(Launcher launcher) {
- return new StateHandler[] {
- launcher.getAllAppsController(), launcher.getWorkspace() };
- }
-
- public static void resetOverview(Launcher launcher) { }
-
- public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
-
- public static void onCreate(Launcher launcher) { }
-
- public static void onStart(Launcher launcher) { }
-
- public static void onEnterAnimationComplete(Context context) {}
-
- public static void onLauncherStateOrResumeChanged(Launcher launcher) { }
-
- public static void onTrimMemory(Launcher launcher, int level) { }
-
- public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
- CancellationSignal cancellationSignal) { }
-
- public static boolean dumpActivity(Activity activity, PrintWriter writer) {
- return false;
- }
-
- public static void setBackButtonAlpha(Launcher launcher, float alpha, boolean animate) { }
-
-
- public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
- return new ScaleAndTranslation(1.1f, 0f, 0f);
- }
-
- public static RotationMode getRotationMode(DeviceProfile dp) {
- return RotationMode.NORMAL;
- }
-
- public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
- int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
- Bundle options) {
- return false;
- }
-
- public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
- Bundle options) {
- return false;
- }
-
- public static void resetPendingActivityResults(Launcher launcher, int requestCode) { }
-
- public static Person[] getPersons(ShortcutInfo si) {
- return Utilities.EMPTY_PERSON_ARRAY;
- }
-}
diff --git a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
similarity index 72%
rename from tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
rename to tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
index f209fae..5174e4d 100644
--- a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
@@ -15,6 +15,12 @@
*/
package com.android.launcher3.touch;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
+
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyObject;
@@ -39,19 +45,19 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SwipeDetectorTest {
+public class SingleAxisSwipeDetectorTest {
- private static final String TAG = SwipeDetectorTest.class.getSimpleName();
+ private static final String TAG = SingleAxisSwipeDetectorTest.class.getSimpleName();
public static void L(String s, Object... parts) {
Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
}
private TouchEventGenerator mGenerator;
- private SwipeDetector mDetector;
+ private SingleAxisSwipeDetector mDetector;
private int mTouchSlop;
@Mock
- private SwipeDetector.Listener mMockListener;
+ private SingleAxisSwipeDetector.Listener mMockListener;
@Mock
private ViewConfiguration mMockConfig;
@@ -65,8 +71,8 @@
doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
.getScaledMaximumFlingVelocity();
- mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
- mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
+ mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
+ mDetector.setDetectableScrollConditions(DIRECTION_BOTH, false);
mTouchSlop = orgConfig.getScaledTouchSlop();
doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
@@ -75,8 +81,8 @@
@Test
public void testDragStart_verticalPositive() {
- mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
- mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+ mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
+ mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 - mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
@@ -85,8 +91,8 @@
@Test
public void testDragStart_verticalNegative() {
- mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
- mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
+ mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
+ mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
@@ -103,8 +109,8 @@
@Test
public void testDragStart_horizontalPositive() {
- mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
- mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+ mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
+ mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 + mTouchSlop, 100);
@@ -114,8 +120,8 @@
@Test
public void testDragStart_horizontalNegative() {
- mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
- mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
+ mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
+ mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 - mTouchSlop, 100);
@@ -125,8 +131,8 @@
@Test
public void testDragStart_horizontalRtlPositive() {
- mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
- mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+ mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
+ mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 - mTouchSlop, 100);
@@ -136,8 +142,8 @@
@Test
public void testDragStart_horizontalRtlNegative() {
- mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
- mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
+ mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
+ mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100 + mTouchSlop, 100);
@@ -160,6 +166,6 @@
mGenerator.move(0, 100, 100 + mTouchSlop * 2);
mGenerator.lift(0);
// TODO: actually calculate the following parameters and do exact value checks.
- verify(mMockListener).onDragEnd(anyFloat(), anyBoolean());
+ verify(mMockListener).onDragEnd(anyFloat());
}
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 62989a3..76b5d28 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -26,6 +26,8 @@
import static java.lang.System.exit;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -373,7 +375,8 @@
startIntent(
getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
packageName),
- By.pkg(packageName).depth(0));
+ By.pkg(packageName).depth(0),
+ true /* newTask */);
}
public static void startTestActivity(int activityNumber) {
@@ -382,12 +385,17 @@
getLaunchIntentForPackage(packageName);
intent.setComponent(new ComponentName(packageName,
"com.android.launcher3.tests.Activity" + activityNumber));
- startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber));
+ startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber),
+ false /* newTask */);
}
- private static void startIntent(Intent intent, BySelector selector) {
+ private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ if (newTask) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ } else {
+ intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+ }
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
UiDevice.getInstance(getInstrumentation())
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 465cee2..29da0fa 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -18,6 +18,10 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
+import static com.android.launcher3.util.rule.TestStabilityRule.RUN_FLAFOR;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_PRESUBMIT;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -174,7 +178,8 @@
@Test
public void testWorkspace() throws Exception {
// b/142828227
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess()) {
+ if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
+ (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
return;
}
final Workspace workspace = mLauncher.getWorkspace();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 1c17e34..259f9ed 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -48,7 +48,6 @@
@Test
@PortraitLandscape
- @Stability(flavors = UNBUNDLED_POSTSUBMIT) // b/142514365
public void testDragIcon() throws Throwable {
clearHomescreen();
mDevice.pressHome();
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index 69bf01d..08fa098 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -57,7 +57,7 @@
public static final int PLATFORM_PRESUBMIT = 0x8;
public static final int PLATFORM_POSTSUBMIT = 0x10;
- private static final int RUN_FLAFOR = getRunFlavor();
+ public static final int RUN_FLAFOR = getRunFlavor();
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index e1e9b8d..cc92327 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -163,7 +163,7 @@
"Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
++attempts <= MAX_SCROLL_ATTEMPTS);
- mLauncher.scroll(allAppsContainer, Direction.UP, margins, 50);
+ mLauncher.scroll(allAppsContainer, Direction.UP, margins, 12);
}
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 41a4bdb..3713c14 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -424,11 +424,7 @@
// b/136278866
for (int i = 0; i != 100; ++i) {
if (getNavigationModeMismatchError() == null) break;
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ sleep(100);
}
final String error = getNavigationModeMismatchError();
@@ -497,9 +493,7 @@
}
public void waitForLauncherInitialized() {
- // b/136278866
- final int attempts = android.os.Build.MODEL.contains("Cuttlefish") ? 600 : 100;
- for (int i = 0; i < attempts; ++i) {
+ for (int i = 0; i < 100; ++i) {
if (getTestInfo(
TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED).
getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD)) {
@@ -797,6 +791,8 @@
final int distance = gestureStart - container.getVisibleBounds().top - topPadding;
final int bottomMargin = container.getVisibleBounds().height() - distance;
+ // TODO: Make the gesture steps dependent on the distance so that it can run for various
+ // screen sizes
scroll(
container,
Direction.DOWN,
@@ -805,7 +801,7 @@
0,
0,
Math.max(bottomMargin, getBottomGestureMargin(container))),
- 150);
+ 80);
}
void scroll(UiObject2 container, Direction direction, Rect margins, int steps) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index 4f8aeb1..16a64a7 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -58,7 +58,7 @@
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD),
mLauncher.getDevice().getDisplayWidth() / 2,
0,
- 50,
+ 12,
ALL_APPS_STATE_ORDINAL);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index d1261e0..db3314e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -38,7 +38,7 @@
* Operations on the workspace screen.
*/
public final class Workspace extends Home {
- private static final int DRAG_DURACTION = 2000;
+ private static final int DRAG_DURATION = 500;
private static final int FLING_STEPS = 10;
private final UiObject2 mHotseat;
@@ -72,7 +72,7 @@
start.y,
start.x,
start.y - swipeHeight - mLauncher.getTouchSlop(),
- 60,
+ 12,
ALL_APPS_STATE_ORDINAL);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
@@ -166,7 +166,7 @@
launcher.waitForLauncherObject(longPressIndicator);
LauncherInstrumentation.log("dragIconToWorkspace: indicator");
launcher.movePointer(
- downTime, SystemClock.uptimeMillis(), DRAG_DURACTION, launchableCenter, dest);
+ downTime, SystemClock.uptimeMillis(), DRAG_DURATION, launchableCenter, dest);
LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
launcher.sendPointer(
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);