diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index ff5bf0d1e5..19a16e341a 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -45,9 +45,6 @@
-
-
-
+ android:name="com.android.launcher3.SessionCommitReceiver"
+ android:exported="true">
-
+
@@ -117,6 +117,7 @@
android:name="com.android.launcher3.notification.NotificationListener"
android:label="@string/notification_dots_service_title"
android:enabled="@bool/notification_dots_enabled"
+ android:exported="true"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
@@ -130,6 +131,7 @@
android:theme="@style/AppItemActivityTheme"
android:excludeFromRecents="true"
android:autoRemoveFromRecents="true"
+ android:exported="true"
android:label="@string/action_add_to_workspace" >
@@ -165,6 +167,7 @@
android:name="com.android.launcher3.settings.SettingsActivity"
android:label="@string/settings_button_text"
android:theme="@style/HomeSettingsTheme"
+ android:exported="true"
android:autoRemoveFromRecents="true">
@@ -187,6 +190,7 @@
android:name="com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher"
android:theme="@style/AppTheme"
android:launchMode="singleTop"
+ android:exported="true"
android:enabled="true">
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b031ffbee4..8e01f82018 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -53,6 +53,7 @@
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
+ android:exported="true"
android:enabled="true">
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index e649ce1de4..aa3710b6d9 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -164,6 +164,12 @@ public class DebugTestInformationHandler extends TestInformationHandler {
}
case TestProtocol.REQUEST_GET_TEST_EVENTS: {
+ if (sEvents == null) {
+ // sEvents can be null if Launcher died and restarted after
+ // REQUEST_START_EVENT_LOGGING.
+ return response;
+ }
+
synchronized (sEvents) {
response.putStringArrayList(
TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 561196941c..cd229ae015 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -131,6 +131,7 @@ message Application {
// Legacy shortcuts and shortcuts handled by ShortcutManager
message Shortcut {
optional string shortcut_name = 1;
+ optional string shortcut_id = 2;
}
// AppWidgets handled by AppWidgetManager
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 60afddb0a8..53910e344b 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -53,6 +53,7 @@
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
+ android:exported="true"
android:enabled="true">
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index e49f2ecdc0..4e7c3fa75b 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -17,97 +17,91 @@
** limitations under the License.
*/
-->
-
-
+
+
-
-
-
+
+
+
-
+
-
+
-
+
+ android:excludeFromRecents="true"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:stateNotNeeded="true"
+ android:theme="@style/LauncherTheme"
+ android:screenOrientation="unspecified"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:resizeableActivity="true"
+ android:resumeWhilePausing="true"
+ android:taskAffinity=""/>
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
-
+
+
+ android:permission="${packageName}.permission.HOTSEAT_EDU"
+ android:exported="true">
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
index 4c7943b10b..8cabe3d80b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
@@ -16,6 +16,12 @@
package com.android.launcher3.appprediction;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.annotation.TargetApi;
@@ -31,29 +37,38 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.FolderContainer;
+import com.android.launcher3.logger.LauncherAtom.HotseatContainer;
+import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer;
+import com.android.launcher3.logging.StatsLogManager.EventEnum;
import com.android.launcher3.model.AppLaunchTracker;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.systemui.plugins.AppLaunchEventsPlugin;
-import com.android.systemui.plugins.PluginListener;
+import com.android.launcher3.pm.UserCache;
+import com.android.quickstep.logging.StatsLogCompatManager;
+import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Locale;
+import java.util.function.Predicate;
/**
* Subclass of app tracker which publishes the data to the prediction engine and gets back results.
*/
@TargetApi(Build.VERSION_CODES.Q)
-public class PredictionAppTracker extends AppLaunchTracker
- implements PluginListener {
+public class PredictionAppTracker extends AppLaunchTracker implements StatsLogConsumer {
private static final String TAG = "PredictionAppTracker";
private static final boolean DBG = false;
@@ -65,11 +80,9 @@ public class PredictionAppTracker extends AppLaunchTracker
protected final Context mContext;
private final Handler mMessageHandler;
- private final List mAppLaunchEventsPluginsList;
// Accessed only on worker thread
private AppPredictor mHomeAppPredictor;
- private AppPredictor mRecentsOverviewPredictor;
public PredictionAppTracker(Context context) {
mContext = context;
@@ -77,10 +90,6 @@ public class PredictionAppTracker extends AppLaunchTracker
InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);
mMessageHandler.sendEmptyMessage(MSG_INIT);
-
- mAppLaunchEventsPluginsList = new ArrayList<>();
- PluginManagerWrapper.INSTANCE.get(context)
- .addPluginListener(this, AppLaunchEventsPlugin.class, true);
}
@UiThread
@@ -97,10 +106,7 @@ public class PredictionAppTracker extends AppLaunchTracker
mHomeAppPredictor.destroy();
mHomeAppPredictor = null;
}
- if (mRecentsOverviewPredictor != null) {
- mRecentsOverviewPredictor.destroy();
- mRecentsOverviewPredictor = null;
- }
+ StatsLogCompatManager.LOGS_CONSUMER.remove(this);
}
@WorkerThread
@@ -142,7 +148,7 @@ public class PredictionAppTracker extends AppLaunchTracker
// Initialize the clients
int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns;
mHomeAppPredictor = createPredictor(Client.HOME, count);
- mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count);
+ StatsLogCompatManager.LOGS_CONSUMER.add(this);
return true;
}
case MSG_DESTROY: {
@@ -157,12 +163,7 @@ public class PredictionAppTracker extends AppLaunchTracker
}
case MSG_PREDICT: {
if (mHomeAppPredictor != null) {
- String client = (String) msg.obj;
- if (Client.HOME.id.equals(client)) {
- mHomeAppPredictor.requestPredictionUpdate();
- } else {
- mRecentsOverviewPredictor.requestPredictionUpdate();
- }
+ mHomeAppPredictor.requestPredictionUpdate();
}
return true;
}
@@ -179,98 +180,142 @@ public class PredictionAppTracker extends AppLaunchTracker
if (DBG) {
Log.d(TAG, String.format("Sent immediate message to update %s", client));
}
-
- // Relay onReturnedToHome to every plugin.
- mAppLaunchEventsPluginsList.forEach(AppLaunchEventsPlugin::onReturnedToHome);
}
- @Override
- @UiThread
- public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
- String container) {
- // TODO: Use the full shortcut info
- AppTarget target = new AppTarget.Builder(
- new AppTargetId("shortcut:" + shortcutId), packageName, user)
- .setClassName(shortcutId)
- .build();
-
- sendLaunch(target, container);
-
- // Relay onStartShortcut info to every connected plugin.
- mAppLaunchEventsPluginsList
- .forEach(plugin -> plugin.onStartShortcut(
- packageName,
- shortcutId,
- user,
- container != null ? container : CONTAINER_DEFAULT)
- );
-
- }
-
- @Override
- @UiThread
- public void onStartApp(ComponentName cn, UserHandle user, String container) {
- if (cn != null) {
- AppTarget target = new AppTarget.Builder(
- new AppTargetId("app:" + cn), cn.getPackageName(), user)
- .setClassName(cn.getClassName())
+ @AnyThread
+ private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId) {
+ AppTarget target = toAppTarget(atomInfo);
+ if (target != null) {
+ AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
+ .setLaunchLocation(getContainer(atomInfo))
.build();
- sendLaunch(target, container);
-
- // Relay onStartApp to every connected plugin.
- mAppLaunchEventsPluginsList
- .forEach(plugin -> plugin.onStartApp(
- cn,
- user,
- container != null ? container : CONTAINER_DEFAULT)
- );
+ Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
}
}
@Override
- @UiThread
- public void onDismissApp(ComponentName cn, UserHandle user, String container) {
- if (cn == null) return;
- AppTarget target = new AppTarget.Builder(
- new AppTargetId("app: " + cn), cn.getPackageName(), user)
- .setClassName(cn.getClassName())
- .build();
- sendDismiss(target, container);
-
- // Relay onDismissApp to every connected plugin.
- mAppLaunchEventsPluginsList
- .forEach(plugin -> plugin.onDismissApp(
- cn,
- user,
- container != null ? container : CONTAINER_DEFAULT)
- );
+ public void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
+ if (event == LAUNCHER_APP_LAUNCH_TAP
+ || event == LAUNCHER_TASK_LAUNCH_SWIPE_DOWN
+ || event == LAUNCHER_TASK_LAUNCH_TAP
+ || event == LAUNCHER_QUICKSWITCH_RIGHT
+ || event == LAUNCHER_QUICKSWITCH_LEFT) {
+ sendEvent(atomInfo, AppTargetEvent.ACTION_LAUNCH);
+ } else if (event == LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST) {
+ sendEvent(atomInfo, AppTargetEvent.ACTION_DISMISS);
+ }
}
- @UiThread
- private void sendEvent(AppTarget target, String container, int eventId) {
- AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
- .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
- .build();
- Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
+ @Nullable
+ private AppTarget toAppTarget(LauncherAtom.ItemInfo info) {
+ UserHandle userHandle = Process.myUserHandle();
+ if (info.getIsWork()) {
+ userHandle = UserCache.INSTANCE.get(mContext).getUserProfiles().stream()
+ .filter(((Predicate) userHandle::equals).negate())
+ .findAny()
+ .orElse(null);
+ }
+ if (userHandle == null) {
+ return null;
+ }
+ ComponentName cn = null;
+ String id = null;
+
+ switch (info.getItemCase()) {
+ case APPLICATION: {
+ LauncherAtom.Application app = info.getApplication();
+ if ((cn = parseNullable(app.getComponentName())) != null) {
+ id = "app:" + cn.getPackageName();
+ }
+ break;
+ }
+ case SHORTCUT: {
+ LauncherAtom.Shortcut si = info.getShortcut();
+ if (!TextUtils.isEmpty(si.getShortcutId())
+ && (cn = parseNullable(si.getShortcutName())) != null) {
+ id = "shortcut:" + si.getShortcutId();
+ }
+ break;
+ }
+ case WIDGET: {
+ LauncherAtom.Widget widget = info.getWidget();
+ if ((cn = parseNullable(widget.getComponentName())) != null) {
+ id = "widget:" + cn.getPackageName();
+ }
+ break;
+ }
+ case TASK: {
+ LauncherAtom.Task task = info.getTask();
+ if ((cn = parseNullable(task.getComponentName())) != null) {
+ id = "app:" + cn.getPackageName();
+ }
+ break;
+ }
+ case FOLDER_ICON: {
+ id = "folder:" + SystemClock.uptimeMillis();
+ cn = new ComponentName(mContext.getPackageName(), "#folder");
+ }
+ }
+ if (id != null && cn != null) {
+ return new AppTarget.Builder(new AppTargetId(id), cn.getPackageName(), userHandle)
+ .setClassName(cn.getClassName())
+ .build();
+ }
+ return null;
}
- @UiThread
- private void sendLaunch(AppTarget target, String container) {
- sendEvent(target, container, AppTargetEvent.ACTION_LAUNCH);
+ private String getContainer(LauncherAtom.ItemInfo info) {
+ ContainerInfo ci = info.getContainerInfo();
+ switch (ci.getContainerCase()) {
+ case WORKSPACE: {
+ // In case the item type is not widgets, the spaceX and spanY default to 1.
+ int spanX = info.getWidget().getSpanX();
+ int spanY = info.getWidget().getSpanY();
+ return getWorkspaceContainerString(ci.getWorkspace(), spanX, spanY);
+ }
+ case HOTSEAT: {
+ return getHotseatContainerString(ci.getHotseat());
+ }
+ case TASK_SWITCHER_CONTAINER: {
+ return "task-switcher";
+ }
+ case ALL_APPS_CONTAINER: {
+ return "all-apps";
+ }
+ case SEARCH_RESULT_CONTAINER: {
+ return "search-results";
+ }
+ case PREDICTED_HOTSEAT_CONTAINER: {
+ return "predictions/hotseat";
+ }
+ case PREDICTION_CONTAINER: {
+ return "predictions";
+ }
+ case FOLDER: {
+ FolderContainer fc = ci.getFolder();
+ switch (fc.getParentContainerCase()) {
+ case WORKSPACE:
+ return "folder/" + getWorkspaceContainerString(fc.getWorkspace(), 1, 1);
+ case HOTSEAT:
+ return "folder/" + getHotseatContainerString(fc.getHotseat());
+ }
+ return "folder";
+ }
+ }
+ return "";
}
- @UiThread
- private void sendDismiss(AppTarget target, String container) {
- sendEvent(target, container, AppTargetEvent.ACTION_DISMISS);
+ private static String getWorkspaceContainerString(WorkspaceContainer wc, int spanX, int spanY) {
+ return String.format(Locale.ENGLISH, "workspace/%d/[%d,%d]/[%d,%d]",
+ wc.getPageIndex(), wc.getGridX(), wc.getGridY(), spanX, spanY);
}
- @Override
- public void onPluginConnected(AppLaunchEventsPlugin appLaunchEventsPlugin, Context context) {
- mAppLaunchEventsPluginsList.add(appLaunchEventsPlugin);
+ private static String getHotseatContainerString(HotseatContainer hc) {
+ return String.format(Locale.ENGLISH, "hotseat/%d", hc.getIndex());
}
- @Override
- public void onPluginDisconnected(AppLaunchEventsPlugin appLaunchEventsPlugin) {
- mAppLaunchEventsPluginsList.remove(appLaunchEventsPlugin);
+ private static ComponentName parseNullable(String componentNameString) {
+ return TextUtils.isEmpty(componentNameString)
+ ? null : ComponentName.unflattenFromString(componentNameString);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index 44691d3bb0..e11c701640 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -56,7 +56,6 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusIndicatorHelper;
import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -93,9 +92,6 @@ public class PredictionRowView extends LinearLayout implements
private static final Interpolator ALPHA_FACTOR_INTERPOLATOR =
(t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f;
- private static final OnClickListener PREDICTION_CLICK_LISTENER =
- ItemClickHandler.getInstance(AppLaunchTracker.CONTAINER_PREDICTIONS);
-
private final Launcher mLauncher;
private final PredictionUiStateManager mPredictionUiStateManager;
private int mNumPredictedAppsPerRow;
@@ -246,7 +242,7 @@ public class PredictionRowView extends LinearLayout implements
while (getChildCount() < mNumPredictedAppsPerRow) {
BubbleTextView icon = (BubbleTextView) inflater.inflate(
R.layout.all_apps_icon, this, false);
- icon.setOnClickListener(PREDICTION_CLICK_LISTENER);
+ icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mFocusHelper);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index a0f6b044ba..830c1032ba 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -75,8 +75,7 @@ public class PredictionUiStateManager implements StateListener,
// TODO (b/129421797): Update the client constants
public enum Client {
- HOME("home"),
- OVERVIEW("overview");
+ HOME("home");
public final String id;
@@ -91,10 +90,9 @@ public class PredictionUiStateManager implements StateListener,
private final Context mContext;
private final DynamicItemCache mDynamicItemCache;
- private final List[] mPredictionServicePredictions;
+ private List mPredictionServicePredictions = Collections.emptyList();
private int mMaxIconsPerRow;
- private Client mActiveClient;
private AllAppsContainerView mAppsView;
@@ -108,16 +106,10 @@ public class PredictionUiStateManager implements StateListener,
mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated);
- mActiveClient = Client.HOME;
-
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
mMaxIconsPerRow = idp.numColumns;
idp.addOnChangeListener(this);
- mPredictionServicePredictions = new List[Client.values().length];
- for (int i = 0; i < mPredictionServicePredictions.length; i++) {
- mPredictionServicePredictions[i] = Collections.emptyList();
- }
mGettingValidPredictionResults = Utilities.getDevicePrefs(context)
.getBoolean(LAST_PREDICTION_ENABLED_STATE, true);
@@ -130,18 +122,6 @@ public class PredictionUiStateManager implements StateListener,
mMaxIconsPerRow = profile.numColumns;
}
- public Client getClient() {
- return mActiveClient;
- }
-
- public void switchClient(Client client) {
- if (client == mActiveClient) {
- return;
- }
- mActiveClient = client;
- dispatchOnChange(true);
- }
-
public void setTargetAppsView(AllAppsContainerView appsView) {
if (mAppsView != null) {
mAppsView.getAppsStore().removeUpdateListener(this);
@@ -195,10 +175,8 @@ public class PredictionUiStateManager implements StateListener,
}
private void updatePredictionStateAfterCallback() {
- boolean validResults = false;
- for (List l : mPredictionServicePredictions) {
- validResults |= l != null && !l.isEmpty();
- }
+ boolean validResults = mPredictionServicePredictions != null
+ && !mPredictionServicePredictions.isEmpty();
if (validResults != mGettingValidPredictionResults) {
mGettingValidPredictionResults = validResults;
Utilities.getDevicePrefs(mContext).edit()
@@ -210,7 +188,7 @@ public class PredictionUiStateManager implements StateListener,
public AppPredictor.Callback appPredictorCallback(Client client) {
return targets -> {
- mPredictionServicePredictions[client.ordinal()] = targets;
+ mPredictionServicePredictions = targets;
updatePredictionStateAfterCallback();
};
}
@@ -238,7 +216,7 @@ public class PredictionUiStateManager implements StateListener,
state.apps = new ArrayList<>();
- List appTargets = mPredictionServicePredictions[mActiveClient.ordinal()];
+ List appTargets = mPredictionServicePredictions;
if (!appTargets.isEmpty()) {
for (AppTarget appTarget : appTargets) {
ComponentKey key;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 9a03d92e48..bdbd1e955d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -34,8 +34,6 @@ import android.os.Bundle;
import android.util.Log;
import android.view.View;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -129,12 +127,11 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
}
@Override
- public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
- @Nullable String sourceContainer) {
+ public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
if (mHotseatPredictionController != null) {
mHotseatPredictionController.setPauseUIUpdate(true);
}
- return super.startActivitySafely(v, intent, item, sourceContainer);
+ return super.startActivitySafely(v, intent, item);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index de83caf162..9310685e30 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -24,6 +24,7 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD
import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.app.ActivityManager.RunningTaskInfo;
import android.util.Log;
import android.view.animation.Interpolator;
@@ -52,17 +53,17 @@ final class AppToOverviewAnimationProvider> extend
private final BaseActivityInterface, T> mActivityInterface;
// The id of the currently running task that is transitioning to overview.
- private final int mTargetTaskId;
+ private final RunningTaskInfo mTargetTask;
private final RecentsAnimationDeviceState mDeviceState;
private T mActivity;
private RecentsView mRecentsView;
AppToOverviewAnimationProvider(
- BaseActivityInterface, T> activityInterface, int targetTaskId,
+ BaseActivityInterface, T> activityInterface, RunningTaskInfo targetTask,
RecentsAnimationDeviceState deviceState) {
mActivityInterface = activityInterface;
- mTargetTaskId = targetTaskId;
+ mTargetTask = targetTask;
mDeviceState = deviceState;
}
@@ -73,7 +74,7 @@ final class AppToOverviewAnimationProvider> extend
* @param wasVisible true if it was visible before
*/
boolean onActivityReady(T activity, Boolean wasVisible) {
- activity.getOverviewPanel().showCurrentTask(mTargetTaskId);
+ activity.getOverviewPanel().showCurrentTask(mTargetTask);
AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
mDeviceState,
@@ -122,7 +123,8 @@ final class AppToOverviewAnimationProvider> extend
wallpaperTargets, MODE_CLOSING);
// Use the top closing app to determine the insets for the animation
- RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
+ RemoteAnimationTargetCompat runningTaskTarget = mTargetTask == null ? null
+ : targets.findTask(mTargetTask.taskId);
if (runningTaskTarget == null) {
Log.e(TAG, "No closing app");
return pa.buildAnim();
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 a63f3a802f..b49299d2fe 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static android.widget.Toast.LENGTH_SHORT;
+
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.VibratorWrapper.OVERVIEW_HAPTIC;
@@ -27,11 +29,13 @@ import android.graphics.Rect;
import android.os.Build;
import android.util.Log;
import android.view.MotionEvent;
+import android.widget.Toast;
import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.VibratorWrapper;
@@ -138,10 +142,10 @@ public abstract class BaseSwipeUpHandler, Q extend
mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
true /* freezeTaskList */);
} else {
- int taskId = mRecentsView.getNextPageTaskView().getTask().key.id;
if (!mCanceled) {
- TaskView nextTask = mRecentsView.getTaskView(taskId);
+ TaskView nextTask = mRecentsView.getNextPageTaskView();
if (nextTask != null) {
+ int taskId = nextTask.getTask().key.id;
mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId);
@@ -158,6 +162,10 @@ public abstract class BaseSwipeUpHandler, Q extend
mRecentsAnimationController.finish(true /* toRecents */, null);
}
}, MAIN_EXECUTOR.getHandler());
+ } else {
+ mActivityInterface.onLaunchTaskFailed();
+ Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
+ mRecentsAnimationController.finish(true /* toRecents */, null);
}
}
mCanceled = false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
index 6c4c5d3f4d..5905695b4a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
@@ -38,7 +38,6 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINI
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -60,6 +59,7 @@ import android.view.ViewTreeObserver.OnDrawListener;
import android.view.WindowInsets;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.AbstractFloatingView;
@@ -70,6 +70,7 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -266,10 +267,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
this::notifyTransitionCancelled);
- mGestureState.runOnceAtState(STATE_END_TARGET_SET,
- () -> mDeviceState.onEndTargetCalculated(mGestureState.getEndTarget(),
- mActivityInterface));
-
if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
| STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
@@ -313,12 +310,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
}
setupRecentsViewUi();
-
- if (mDeviceState.getNavMode() == TWO_BUTTONS) {
- // If the device is in two button mode, swiping up will show overview with predictions
- // so we need to kick off switching to the overview predictions as soon as possible
- mActivityInterface.updateOverviewPredictionState();
- }
linkRecentsViewScroll();
return true;
@@ -400,6 +391,11 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback(
mOnDeferredActivityLaunch);
+ mGestureState.runOnceAtState(STATE_END_TARGET_SET,
+ () -> mDeviceState.getRotationTouchHelper().
+ onEndTargetCalculated(mGestureState.getEndTarget(),
+ mActivityInterface));
+
notifyGestureStartedAsync();
}
@@ -423,7 +419,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
}
protected void notifyGestureAnimationStartToRecents() {
- mRecentsView.onGestureAnimationStart(mGestureState.getRunningTaskId());
+ mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
}
private void launcherFrameDrawn() {
@@ -451,12 +447,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
@Override
public void onMotionPauseChanged(boolean isPaused) {
setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
-
- if (mDeviceState.isFullyGesturalNavMode() && isPaused) {
- // In fully gestural nav mode, switch to overview predictions once the user has paused
- // (this is a no-op if the predictions are already in that state)
- mActivityInterface.updateOverviewPredictionState();
- }
}
public void maybeUpdateRecentsAttachedState() {
@@ -555,14 +545,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
@Override
public void updateFinalShift() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (mRecentsAnimationTargets != null) {
- LiveTileOverlay.INSTANCE.update(
- mTaskViewSimulator.getCurrentCropRect(),
- mTaskViewSimulator.getCurrentCornerRadius());
- }
- }
-
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed;
@@ -573,6 +555,14 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
updateSysUiFlags(mCurrentShift.value);
applyWindowTransform();
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (mRecentsAnimationTargets != null) {
+ LiveTileOverlay.INSTANCE.update(
+ mTaskViewSimulator.getCurrentRect(),
+ mTaskViewSimulator.getCurrentCornerRadius());
+ }
+ }
+
updateLauncherTransitionProgress();
}
@@ -889,22 +879,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
}
- private void doLogGesture(GestureEndTarget endTarget) {
- DeviceProfile dp = mDp;
- if (dp == null || mDownPos == null) {
- // We probably never received an animation controller, skip logging.
- return;
- }
-
- int pageIndex = endTarget == LAST_TASK
- ? LOG_NO_OP_PAGE_INDEX
- : mRecentsView.getNextPage();
- UserEventDispatcher.newInstance(mContext).logStateChangeAction(
- mLogAction, mLogDirection,
- (int) mDownPos.x, (int) mDownPos.y,
- ContainerType.NAVBAR, ContainerType.APP,
- endTarget.containerType,
- pageIndex);
+ private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
StatsLogManager.EventEnum event;
switch (endTarget) {
case HOME:
@@ -922,10 +897,29 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
default:
event = IGNORE;
}
- StatsLogManager.newInstance(mContext).logger()
+ StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
.withSrcState(LAUNCHER_STATE_BACKGROUND)
- .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType))
- .log(event);
+ .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType));
+ if (targetTask != null) {
+ logger.withItemInfo(targetTask.getItemInfo());
+ }
+ logger.log(event);
+
+
+ DeviceProfile dp = mDp;
+ if (dp == null || mDownPos == null) {
+ // We probably never received an animation controller, skip logging.
+ return;
+ }
+ int pageIndex = endTarget == LAST_TASK
+ ? LOG_NO_OP_PAGE_INDEX
+ : mRecentsView.getNextPage();
+ UserEventDispatcher.newInstance(mContext).logStateChangeAction(
+ mLogAction, mLogDirection,
+ (int) mDownPos.x, (int) mDownPos.y,
+ ContainerType.NAVBAR, ContainerType.APP,
+ endTarget.containerType,
+ pageIndex);
}
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
@@ -1130,7 +1124,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
private void resumeLastTask() {
mRecentsAnimationController.finish(false /* toRecents */, null);
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
- doLogGesture(LAST_TASK);
+ doLogGesture(LAST_TASK, null);
reset();
}
@@ -1145,6 +1139,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
@UiThread
private void startNewTaskInternal() {
+ TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getNextPageTaskView();
startNewTask(success -> {
if (!success) {
reset();
@@ -1153,7 +1148,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
endLauncherTransitionController();
updateSysUiFlags(1 /* windowProgress == overview */);
}
- doLogGesture(NEW_TASK);
+ doLogGesture(NEW_TASK, taskToLaunch);
});
}
@@ -1298,7 +1293,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
- doLogGesture(HOME);
+ doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
}
protected abstract void finishRecentsControllerToHome(Runnable callback);
@@ -1313,7 +1308,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte
mRecentsView.onSwipeUpAnimationSuccess();
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
- doLogGesture(RECENTS);
+ doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
reset();
}
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 33b9cdee08..d1da0c1c10 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -140,7 +140,7 @@ public final class FallbackActivityInterface extends
}
@Override
- public void onExitOverview(RecentsAnimationDeviceState deviceState, Runnable exitRunnable) {
+ public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
// no-op, fake landscape not supported for 3P
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index fc7a119f21..1909f4772e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -140,7 +140,6 @@ public class FallbackSwipeHandler extends
private final long mDuration;
FallbackHomeAnimationFactory(long duration) {
- super(null);
mDuration = duration;
if (mRunningOverHome) {
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 edefbe1252..b02035516d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -105,7 +105,7 @@ public final class LauncherActivityInterface extends
// recents, we assume the first task is invisible, making translation off by one task.
launcher.getStateManager().reapplyState();
launcher.getRootView().setForceHideBackArrow(false);
- notifyRecentsOfOrientation(deviceState);
+ notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
}
@Override
@@ -120,7 +120,7 @@ public final class LauncherActivityInterface extends
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer callback) {
- notifyRecentsOfOrientation(deviceState);
+ notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
@@ -228,7 +228,7 @@ public final class LauncherActivityInterface extends
@Override
- public void onExitOverview(RecentsAnimationDeviceState deviceState, Runnable exitRunnable) {
+ public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
final StateManager stateManager = getCreatedActivity().getStateManager();
stateManager.addStateListener(
new StateManager.StateListener() {
@@ -244,11 +244,11 @@ public final class LauncherActivityInterface extends
});
}
- private void notifyRecentsOfOrientation(RecentsAnimationDeviceState deviceState) {
+ private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
// reset layout on swipe to home
RecentsView recentsView = getCreatedActivity().getOverviewPanel();
- recentsView.setLayoutRotation(deviceState.getCurrentActiveRotation(),
- deviceState.getDisplayRotation());
+ recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
+ rotationTouchHelper.getDisplayRotation());
}
@Override
@@ -261,16 +261,6 @@ public final class LauncherActivityInterface extends
return true;
}
- @Override
- public void updateOverviewPredictionState() {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
- PredictionUiStateManager.Client.OVERVIEW);
- }
-
@Override
public int getContainerType() {
final Launcher launcher = getVisibleLauncher();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index fa7d2689f3..052d0a68a6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.AnimatorSet;
import android.content.Context;
@@ -28,6 +29,7 @@ import androidx.annotation.NonNull;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.views.FloatingIconView;
+import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -72,36 +74,39 @@ public class LauncherSwipeHandlerV2 extends
mActivity.getRootView().setForceHideBackArrow(true);
mActivity.setHintUserWillBeActive();
- homeAnimFactory = new HomeAnimationFactory(floatingIconView) {
-
- @Override
- public RectF getWindowTargetRect() {
- if (canUseWorkspaceView) {
+ if (canUseWorkspaceView) {
+ // We want the window alpha to be 0 once this threshold is met, so that the
+ // FolderIconView can be seen morphing into the icon shape.
+ float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
+ homeAnimFactory = new LauncherHomeAnimationFactory() {
+ @Override
+ public RectF getWindowTargetRect() {
return iconLocation;
- } else {
- return super.getWindowTargetRect();
}
- }
- @NonNull
- @Override
- public AnimatorPlaybackController createActivityAnimationToHome() {
- // Return an empty APC here since we have an non-user controlled animation
- // to home.
- long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
- return mActivity.getStateManager().createAnimationToNewWorkspace(
- NORMAL, accuracy, 0 /* animComponents */);
- }
+ @Override
+ public void setAnimation(RectFSpringAnim anim) {
+ anim.addAnimatorListener(floatingIconView);
+ floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
+ floatingIconView.setFastFinishRunnable(anim::end);
+ }
- @Override
- public void playAtomicAnimation(float velocity) {
- new StaggeredWorkspaceAnim(mActivity, velocity,
- true /* animateOverviewScrim */).start();
- }
- };
+ @Override
+ public void update(RectF currentRect, float progress, float radius) {
+ floatingIconView.update(currentRect, 1f, progress, windowAlphaThreshold,
+ radius, false);
+ }
+ @Override
+ public void onCancel() {
+ floatingIconView.fastFinish();
+ }
+ };
+ } else {
+ homeAnimFactory = new LauncherHomeAnimationFactory();
+ }
} else {
- homeAnimFactory = new HomeAnimationFactory(null) {
+ homeAnimFactory = new HomeAnimationFactory() {
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
@@ -118,4 +123,22 @@ public class LauncherSwipeHandlerV2 extends
mRecentsAnimationController.finish(
true /* toRecents */, callback, true /* sendUserLeaveHint */);
}
+
+ private class LauncherHomeAnimationFactory extends HomeAnimationFactory {
+ @NonNull
+ @Override
+ public AnimatorPlaybackController createActivityAnimationToHome() {
+ // Return an empty APC here since we have an non-user controlled animation
+ // to home.
+ long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+ return mActivity.getStateManager().createAnimationToNewWorkspace(
+ NORMAL, accuracy, 0 /* animComponents */);
+ }
+
+ @Override
+ public void playAtomicAnimation(float velocity) {
+ new StaggeredWorkspaceAnim(mActivity, velocity,
+ true /* animateOverviewScrim */).start();
+ }
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index 434a929b5c..dca33789e5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -29,7 +29,6 @@ import android.view.ViewConfiguration;
import androidx.annotation.BinderThread;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -165,7 +164,7 @@ public class OverviewCommandHelper {
mActivityInterface = mOverviewComponentObserver.getActivityInterface();
mCreateTime = SystemClock.elapsedRealtime();
mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,
- RecentsModel.getRunningTaskId(), mDeviceState);
+ ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
// Preload the plan
mRecentsModel.getTasks(null);
@@ -227,10 +226,6 @@ public class OverviewCommandHelper {
LauncherLogProto.ContainerType.TASKSWITCHER);
mUserEventLogged = true;
}
-
- // Switch prediction client to overview
- PredictionUiStateManager.INSTANCE.get(activity).switchClient(
- PredictionUiStateManager.Client.OVERVIEW);
return mAnimationProvider.onActivityReady(activity, wasVisible);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
index dc8f1c54c8..e54a21c073 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -17,7 +17,6 @@ package com.android.quickstep;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.Animator;
import android.content.Context;
@@ -28,7 +27,6 @@ import android.graphics.RectF;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
@@ -37,7 +35,6 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -85,7 +82,8 @@ public abstract class SwipeUpAnimationLogic {
mTransformParams = transformParams;
mTaskViewSimulator.setLayoutRotation(
- mDeviceState.getCurrentActiveRotation(), mDeviceState.getDisplayRotation());
+ mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
+ mDeviceState.getRotationTouchHelper().getDisplayRotation());
}
protected void initTransitionEndpoints(DeviceProfile dp) {
@@ -148,12 +146,6 @@ public abstract class SwipeUpAnimationLogic {
protected abstract class HomeAnimationFactory {
- public FloatingIconView mIconView;
-
- public HomeAnimationFactory(@Nullable FloatingIconView iconView) {
- mIconView = iconView;
- }
-
public @NonNull RectF getWindowTargetRect() {
PagedOrientationHandler orientationHandler = getOrientationHandler();
DeviceProfile dp = mDp;
@@ -174,6 +166,12 @@ public abstract class SwipeUpAnimationLogic {
public void playAtomicAnimation(float velocity) {
// No-op
}
+
+ public void setAnimation(RectFSpringAnim anim) { }
+
+ public void update(RectF currentRect, float progress, float radius) { }
+
+ public void onCancel() { }
}
/**
@@ -184,8 +182,6 @@ public abstract class SwipeUpAnimationLogic {
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
- final FloatingIconView fiv = homeAnimationFactory.mIconView;
- final boolean isFloatingIconView = fiv != null;
mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor);
mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
@@ -203,11 +199,7 @@ public abstract class SwipeUpAnimationLogic {
windowToHomePositionMap.mapRect(startRect);
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
- if (isFloatingIconView) {
- anim.addAnimatorListener(fiv);
- fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
- fiv.setFastFinishRunnable(anim::end);
- }
+ homeAnimationFactory.setAnimation(anim);
SpringAnimationRunner runner = new SpringAnimationRunner(
homeAnimationFactory, cropRectF, homeToWindowPositionMap);
@@ -242,32 +234,27 @@ public abstract class SwipeUpAnimationLogic {
final RectF mWindowCurrentRect = new RectF();
final Matrix mHomeToWindowPositionMap;
+ final HomeAnimationFactory mAnimationFactory;
- final FloatingIconView mFIV;
final AnimatorPlaybackController mHomeAnim;
final RectF mCropRectF;
final float mStartRadius;
final float mEndRadius;
- final float mWindowAlphaThreshold;
SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
Matrix homeToWindowPositionMap) {
+ mAnimationFactory = factory;
mHomeAnim = factory.createActivityAnimationToHome();
mCropRectF = cropRectF;
mHomeToWindowPositionMap = homeToWindowPositionMap;
cropRectF.roundOut(mCropRect);
- mFIV = factory.mIconView;
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
// rounding at the end of the animation.
mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
mEndRadius = cropRectF.width() / 2f;
-
- // We want the window alpha to be 0 once this threshold is met, so that the
- // FolderIconView can be seen morphing into the icon shape.
- mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f;
}
@Override
@@ -282,10 +269,7 @@ public abstract class SwipeUpAnimationLogic {
.setCornerRadius(cornerRadius);
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
- if (mFIV != null) {
- mFIV.update(currentRect, 1f, progress,
- mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false);
- }
+ mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
}
@Override
@@ -298,9 +282,7 @@ public abstract class SwipeUpAnimationLogic {
@Override
public void onCancel() {
- if (mFIV != null) {
- mFIV.fastFinish();
- }
+ mAnimationFactory.onCancel();
}
@Override
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 e9614d1351..db512fa0f9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -19,6 +19,7 @@ package com.android.quickstep;
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
import android.annotation.SuppressLint;
@@ -146,26 +147,29 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
*/
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
boolean rotated) {
- final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+ getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
- getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
+ if (thumbnail != null) {
+ getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
+ final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
- getActionsView().setCallbacks(new OverlayUICallbacks() {
- @Override
- public void onShare() {
- if (isAllowedByPolicy) {
- mImageApi.startShareActivity();
- } else {
- showBlockedByPolicyMessage();
+ getActionsView().setCallbacks(new OverlayUICallbacks() {
+ @Override
+ public void onShare() {
+ if (isAllowedByPolicy) {
+ mImageApi.startShareActivity();
+ } else {
+ showBlockedByPolicyMessage();
+ }
}
- }
- @SuppressLint("NewApi")
- @Override
- public void onScreenshot() {
- saveScreenshot(task);
- }
- });
+ @SuppressLint("NewApi")
+ @Override
+ public void onScreenshot() {
+ saveScreenshot(task);
+ }
+ });
+ }
}
/**
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 6e0b517b25..8837c0ec75 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -61,7 +61,6 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
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.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
@@ -258,6 +257,7 @@ public class TouchInteractionService extends Service implements PluginListener mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
}
return gestureState;
@@ -660,7 +661,7 @@ public class TouchInteractionService extends Service implements PluginListener mAM.getRunningTask(true /* filterOnlyVisibleRecents */)));
ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent();
ComponentName runningComponent =
@@ -771,13 +772,7 @@ public class TouchInteractionService extends Service implements PluginListener {
- AppLaunchTracker.INSTANCE.get(TouchInteractionService.this);
- return false;
- }).register(overviewIntent);
- } else if (fromInit) {
+ if (activityInterface.getCreatedActivity() != null && fromInit) {
// The activity has been created before the initialization of overview service. It is
// usually happens when booting or launcher is the top activity, so we should already
// have the latest state.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index d20bbe9780..a946304779 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -63,12 +63,6 @@ public class FallbackRecentsView extends RecentsView
mActivity.startHome();
}
- @Override
- public boolean shouldUseMultiWindowTaskSizeStrategy() {
- // Just use the activity task size for multi-window as well.
- return false;
- }
-
/**
* When starting gesture interaction from home, we add a temporary invisible tile corresponding
* to the home task. This allows us to handle quick-switch similarly to a quick-switching
@@ -76,7 +70,7 @@ public class FallbackRecentsView extends RecentsView
*/
public void onGestureAnimationStartOnHome(RunningTaskInfo homeTaskInfo) {
mHomeTaskInfo = homeTaskInfo;
- onGestureAnimationStart(homeTaskInfo == null ? -1 : homeTaskInfo.taskId);
+ onGestureAnimationStart(homeTaskInfo);
}
/**
@@ -107,14 +101,15 @@ public class FallbackRecentsView extends RecentsView
}
@Override
- protected boolean shouldAddDummyTaskView(int runningTaskId) {
- if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId
+ protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
+ if (mHomeTaskInfo != null && runningTaskInfo != null &&
+ mHomeTaskInfo.taskId == runningTaskInfo.taskId
&& getTaskViewCount() == 0) {
// Do not add a dummy task if we are running over home with empty recents, so that we
// show the empty recents message instead of showing a dummy task and later removing it.
return false;
}
- return super.shouldAddDummyTaskView(runningTaskId);
+ return super.shouldAddDummyTaskView(runningTaskInfo);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index 5ad48ebabc..0c2c92cfb0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -99,7 +99,8 @@ public class AccessibilityInputConsumer extends DelegateInputConsumer {
case ACTION_POINTER_DOWN: {
if (mState == STATE_INACTIVE) {
int pointerIndex = ev.getActionIndex();
- if (mDeviceState.isInSwipeUpTouchRegion(ev, pointerIndex)
+ if (mDeviceState.getRotationTouchHelper()
+ .isInSwipeUpTouchRegion(ev, pointerIndex)
&& mDelegate.allowInterceptByParent()) {
setActive(ev);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 3a97216035..a676390ce3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -147,7 +147,7 @@ public class DeviceLockedInputConsumer implements InputConsumer,
if (!mThresholdCrossed) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) {
+ if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx)) {
int action = ev.getAction();
ev.setAction(ACTION_CANCEL);
finishTouchTracking(ev);
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 26df9c7b93..6259f1f9fe 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
@@ -59,6 +59,7 @@ import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
@@ -86,6 +87,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
private final NavBarPosition mNavBarPosition;
private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
+ private final RotationTouchHelper mRotationTouchHelper;
private RecentsAnimationCallbacks mActiveCallbacks;
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
private final InputMonitorCompat mInputMonitorCompat;
@@ -163,6 +165,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
+ mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
}
@Override
@@ -230,7 +233,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
if (!mPassedPilferInputSlop) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mDeviceState.isInSwipeUpTouchRegion(ev, ptrIdx)) {
+ if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx)) {
forceCancelGesture(ev);
}
}
@@ -424,7 +427,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
@Override
public void notifyOrientationSetup() {
- mDeviceState.onStartGesture();
+ mRotationTouchHelper.onStartGesture();
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 3cafd423ca..41203319b1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -205,13 +205,15 @@ public class StaggeredWorkspaceAnim {
ResourceProvider rp = DynamicResource.provider(v.getContext());
float stiffness = rp.getFloat(R.dimen.staggered_stiffness);
float damping = rp.getFloat(R.dimen.staggered_damping_ratio);
+ float endTransY = 0;
+ float springVelocity = Math.abs(mVelocity) * Math.signum(endTransY - mSpringTransY);
ValueAnimator springTransY = new SpringAnimationBuilder(v.getContext())
.setStiffness(stiffness)
.setDampingRatio(damping)
.setMinimumVisibleChange(1f)
.setStartValue(mSpringTransY)
- .setEndValue(0)
- .setStartVelocity(mVelocity)
+ .setEndValue(endTransY)
+ .setStartVelocity(springVelocity)
.build(v, VIEW_TRANSLATE_Y);
springTransY.setStartDelay(startDelay);
springTransY.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index c9ed498af1..64ae1e0479 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
@@ -197,6 +198,15 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
return mTempRectF;
}
+ /**
+ * Returns the current task bounds in the Launcher coordinate space.
+ */
+ public RectF getCurrentRect() {
+ RectF result = getCurrentCropRect();
+ mMatrix.mapRect(result);
+ return result;
+ }
+
public RecentsOrientedState getOrientationState() {
return mOrientationState;
}
@@ -295,6 +305,10 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
builder.withMatrix(mMatrix)
.withWindowCrop(mTmpCropRect)
.withCornerRadius(getCurrentCornerRadius());
+
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
+ builder.withRelativeLayerTo(params.getRecentsSurface(), Integer.MAX_VALUE);
+ }
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
index 0135f7472c..756331dcdf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
import android.util.FloatProperty;
+import android.view.SurfaceControl;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -58,6 +59,7 @@ public class TransformParams {
private float mCornerRadius;
private RemoteAnimationTargets mTargetSet;
private SurfaceTransactionApplier mSyncTransactionApplier;
+ private SurfaceControl mRecentsSurface;
private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
@@ -138,6 +140,8 @@ public class TransformParams {
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet;
SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length];
+ mRecentsSurface = getRecentsSurface(targets);
+
for (int i = 0; i < targets.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
@@ -165,6 +169,20 @@ public class TransformParams {
return surfaceParams;
}
+ private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
+ for (int i = 0; i < targets.unfilteredApps.length; i++) {
+ RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
+ if (app.mode == targets.targetMode) {
+ if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) {
+ return app.leash.getSurfaceControl();
+ }
+ } else {
+ return app.leash.getSurfaceControl();
+ }
+ }
+ return null;
+ }
+
// Pubic getters so outside packages can read the values.
public float getProgress() {
@@ -179,6 +197,10 @@ public class TransformParams {
return mCornerRadius;
}
+ public SurfaceControl getRecentsSurface() {
+ return mRecentsSurface;
+ }
+
public RemoteAnimationTargets getTargetSet() {
return mTargetSet;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
index 0979c071bb..c06dd9c0bd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -42,8 +43,10 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.Themes;
@@ -51,6 +54,7 @@ import com.android.quickstep.util.MultiValueUpdateListener;
/**
* View used to educate the user on how to access All Apps when in No Nav Button navigation mode.
+ * Consumes all touches until after the animation is completed and the view is removed.
*/
public class AllAppsEduView extends AbstractFloatingView {
@@ -110,9 +114,19 @@ public class AllAppsEduView extends AbstractFloatingView {
return (type & TYPE_ALL_APPS_EDU) != 0;
}
+ @Override
+ public boolean onBackPressed() {
+ return true;
+ }
+
+ @Override
+ public boolean canInterceptEventsInSystemGestureRegion() {
+ return true;
+ }
+
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- return mAnimation != null && mAnimation.isRunning();
+ return true;
}
private void playAnimation() {
@@ -139,7 +153,12 @@ public class AllAppsEduView extends AbstractFloatingView {
config.userControlled = false;
AnimatorPlaybackController stateAnimationController =
mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, config);
- float maxAllAppsProgress = 0.15f;
+ float maxAllAppsProgress = mLauncher.getDeviceProfile().isLandscape ? 0.35f : 0.15f;
+
+ AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
+ PendingAnimation allAppsAlpha = new PendingAnimation(config.duration);
+ allAppsController.setAlphas(ALL_APPS, config, allAppsAlpha);
+ mAnimation.play(allAppsAlpha.buildAnim());
ValueAnimator intro = ValueAnimator.ofFloat(0, 1f);
intro.setInterpolator(LINEAR);
@@ -191,7 +210,8 @@ public class AllAppsEduView extends AbstractFloatingView {
@Override
public void onAnimationEnd(Animator animation) {
mAnimation = null;
- stateAnimationController.dispatchOnCancel();
+ // Handles cancelling the animation used to hint towards All Apps.
+ mLauncher.getStateManager().goToState(NORMAL, false);
handleClose(false);
}
});
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 846b94463f..1034234138 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
@@ -31,7 +31,6 @@ import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
-import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.Surface;
@@ -41,20 +40,15 @@ import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.RecentsExtraCard;
-import com.android.systemui.shared.recents.model.Task;
/**
* {@link RecentsView} used in Launcher activity
@@ -180,18 +174,6 @@ public class LauncherRecentsView extends RecentsView
super.onTaskLaunchAnimationEnd(success);
}
- @Override
- public void onTaskLaunched(Task task) {
- UserHandle user = UserHandle.of(task.key.userId);
- AppLaunchTracker.INSTANCE.get(getContext()).onStartApp(task.getTopComponent(), user,
- AppLaunchTracker.CONTAINER_OVERVIEW);
- }
-
- @Override
- public boolean shouldUseMultiWindowTaskSizeStrategy() {
- return TraceHelper.whitelistIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode);
- }
-
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
@@ -237,9 +219,6 @@ public class LauncherRecentsView extends RecentsView
super.reset();
setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
- // We are moving to home or some other UI with no recents. Switch back to the home client,
- // the home predictions should have been updated when the activity was resumed.
- PredictionUiStateManager.INSTANCE.get(getContext()).switchClient(Client.HOME);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
index a2da39855c..79d57c55a2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
@@ -70,12 +70,14 @@ public class OverviewActionsView extends FrameLayo
@IntDef(flag = true, value = {
DISABLED_SCROLLING,
- DISABLED_ROTATED})
+ DISABLED_ROTATED,
+ DISABLED_NO_THUMBNAIL})
@Retention(RetentionPolicy.SOURCE)
public @interface ActionsDisabledFlags { }
public static final int DISABLED_SCROLLING = 1 << 0;
public static final int DISABLED_ROTATED = 1 << 1;
+ public static final int DISABLED_NO_THUMBNAIL = 1 << 2;
private static final int INDEX_CONTENT_ALPHA = 0;
private static final int INDEX_VISIBILITY_ALPHA = 1;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 7b24b03f2b..1ad02a963d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -55,10 +55,8 @@ import android.animation.LayoutTransition.TransitionListener;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.app.ActivityManager;
-import android.content.ComponentName;
+import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Point;
@@ -135,6 +133,7 @@ import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.LauncherEventUtil;
@@ -147,7 +146,7 @@ import java.util.function.Consumer;
/**
* A list of recent tasks.
*/
-@TargetApi(Build.VERSION_CODES.P)
+@TargetApi(Build.VERSION_CODES.R)
public abstract class RecentsView extends PagedView implements
Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
@@ -377,7 +376,7 @@ public abstract class RecentsView extends PagedView
mOrientationState.setMultiWindowMode(inMultiWindowMode);
setLayoutRotation(mOrientationState.getTouchRotation(),
mOrientationState.getDisplayRotation());
- rotateAllChildTasks();
+ updateChildTaskOrientations();
}
if (!inMultiWindowMode && mOverviewStateEnabled) {
// TODO: Re-enable layout transitions for addition of the unpinned task
@@ -1041,13 +1040,13 @@ public abstract class RecentsView extends PagedView
/**
* Called when a gesture from an app is starting.
*/
- public void onGestureAnimationStart(int runningTaskId) {
+ public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
updateOrientationHandler();
}
- showCurrentTask(runningTaskId);
+ showCurrentTask(runningTaskInfo);
setEnableFreeScroll(false);
setEnableDrawingLiveTile(false);
setRunningTaskHidden(true);
@@ -1078,7 +1077,7 @@ public abstract class RecentsView extends PagedView
pa.addListener(AnimationSuccessListener.forRunnable(() -> {
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
mActivity.getDragLayer().recreateControllers();
- rotateAllChildTasks();
+ updateChildTaskOrientations();
setRecentsChangedOrientation(false).start();
}));
pa.start();
@@ -1099,7 +1098,7 @@ public abstract class RecentsView extends PagedView
}
- private void rotateAllChildTasks() {
+ private void updateChildTaskOrientations() {
for (int i = 0; i < getTaskViewCount(); i++) {
getTaskViewAt(i).setOrientationState(mOrientationState);
}
@@ -1127,8 +1126,8 @@ public abstract class RecentsView extends PagedView
/**
* Returns true if we should add a dummy taskView for the running task id
*/
- protected boolean shouldAddDummyTaskView(int runningTaskId) {
- return getTaskView(runningTaskId) == null;
+ protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
+ return runningTaskInfo != null && getTaskView(runningTaskInfo.taskId) == null;
}
/**
@@ -1137,8 +1136,8 @@ public abstract class RecentsView extends PagedView
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
* is called. Also scrolls the view to this task.
*/
- public void showCurrentTask(int runningTaskId) {
- if (shouldAddDummyTaskView(runningTaskId)) {
+ public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
+ if (shouldAddDummyTaskView(runningTaskInfo)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = mTaskViewPool.getView();
@@ -1148,10 +1147,7 @@ public abstract class RecentsView extends PagedView
}
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
- mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(),
- new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
- false, true, false, false, new ActivityManager.TaskDescription(), 0,
- new ComponentName("", ""), false);
+ mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false);
taskView.bind(mTmpRunningTask, mOrientationState);
// Measure and layout immediately so that the scroll values is updated instantly
@@ -1162,7 +1158,7 @@ public abstract class RecentsView extends PagedView
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
- setCurrentTask(runningTaskId);
+ setCurrentTask(runningTaskInfo == null ? -1 : runningTaskInfo.taskId);
setCurrentPage(getRunningTaskIndex());
setRunningTaskViewShowScreenshot(false);
setRunningTaskHidden(runningTaskTileHidden);
@@ -1652,6 +1648,9 @@ public abstract class RecentsView extends PagedView
super.setVisibility(visibility);
if (mActionsView != null) {
mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE);
+ if (visibility != VISIBLE) {
+ mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
+ }
}
}
@@ -1680,10 +1679,11 @@ public abstract class RecentsView extends PagedView
: View.LAYOUT_DIRECTION_RTL);
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
mActivity.getDragLayer().recreateControllers();
- boolean isInLandscape = mOrientationState.getTouchRotation() != 0
+ boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
|| mOrientationState.getRecentsActivityRotation() != ROTATION_0;
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
!mOrientationState.canRecentsActivityRotate() && isInLandscape);
+ updateChildTaskOrientations();
resetPaddingFromTaskSize();
requestLayout();
// Reapply the current page to update page scrolls.
@@ -1998,19 +1998,12 @@ public abstract class RecentsView extends PagedView
protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
}
- public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
-
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
resetTaskVisuals();
}
}
- /**
- * Called when task activity is launched
- */
- public void onTaskLaunched(Task task){ }
-
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
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 ef66b7a396..8b49f2c212 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
@@ -169,7 +169,9 @@ public class TaskMenuView extends AbstractFloatingView {
}
if (mIsOpen) {
mOptionLayout.removeAllViews();
- populateAndLayoutMenu();
+ if (!populateAndLayoutMenu()) {
+ close(false);
+ }
}
}
@@ -186,14 +188,22 @@ public class TaskMenuView extends AbstractFloatingView {
}
mActivity.getDragLayer().addView(this);
mTaskView = taskView;
- populateAndLayoutMenu();
+ if (!populateAndLayoutMenu()) {
+ return false;
+ }
post(this::animateOpen);
return true;
}
- private void populateAndLayoutMenu() {
+ /** @return true if successfully able to populate task view menu, false otherwise */
+ private boolean populateAndLayoutMenu() {
+ if (mTaskView.getTask().icon == null) {
+ // Icon may not be loaded
+ return false;
+ }
addMenuOptions(mTaskView);
orientAroundTaskView(mTaskView);
+ return true;
}
private void addMenuOptions(TaskView taskView) {
@@ -240,8 +250,10 @@ public class TaskMenuView extends AbstractFloatingView {
setLayoutParams(params);
setScaleX(taskView.getScaleX());
setScaleY(taskView.getScaleY());
+ boolean canActivityRotate = taskView.getRecentsView()
+ .mOrientationState.canRecentsActivityRotate();
mOptionLayout.setOrientation(orientationHandler
- .getTaskMenuLayoutOrientation(mOptionLayout));
+ .getTaskMenuLayoutOrientation(canActivityRotate, mOptionLayout));
setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top,
taskView.getPagedOrientationHandler());
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index b2f937f8c2..37f6faf523 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -357,7 +357,7 @@ public class TaskThumbnailView extends View implements PluginListener
+
+
+
+
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 459d65faf7..43bf0ea966 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -24,6 +24,14 @@
android:layout_height="match_parent"
android:background="@drawable/gesture_tutorial_ripple"/>
+
+
+ android:padding="18dp"
+ android:src="@drawable/gesture_tutorial_close_button"
+ android:tint="?android:attr/textColorPrimary"/>
+ android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end"/>
+ android:layout_marginTop="10dp"
+ android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end"/>
+ android:layout_marginBottom="10dp"/>
+ android:stateListAnimator="@null"/>
+ android:stateListAnimator="@null"/>
\ No newline at end of file
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
index 93b64e666c..c148a4b6fe 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
@@ -17,6 +17,7 @@ package com.android.quickstep;
import static com.android.launcher3.util.LauncherUIHelper.doLayout;
+import android.app.ActivityManager.RunningTaskInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -50,7 +51,7 @@ public class RecentsActivityTest {
}
@Test
- public void testRecets_showCurrentTask() {
+ public void testRecents_showCurrentTask() {
ActivityController controller =
Robolectric.buildActivity(RecentsActivity.class);
@@ -58,7 +59,10 @@ public class RecentsActivityTest {
doLayout(activity);
FallbackRecentsView frv = activity.getOverviewPanel();
- frv.showCurrentTask(22);
+
+ RunningTaskInfo dummyTask = new RunningTaskInfo();
+ dummyTask.taskId = 22;
+ frv.showCurrentTask(dummyTask);
doLayout(activity);
ThumbnailData thumbnailData = new ThumbnailData();
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 47ce320331..6b941be471 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -157,6 +157,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
protected void onDeferredResumed() {
super.onDeferredResumed();
+ handlePendingActivityRequest();
+ }
+
+ @Override
+ protected void handlePendingActivityRequest() {
+ super.handlePendingActivityRequest();
if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
// Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 10f789d428..2d096d1dfe 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -16,8 +16,6 @@
package com.android.launcher3;
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
@@ -62,7 +60,6 @@ import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.util.Pair;
-import android.util.TypedValue;
import android.view.View;
import androidx.annotation.NonNull;
@@ -879,10 +876,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
}
});
} else {
- float velocityDpPerS = DynamicResource.provider(mLauncher)
+ float velocityPxPerS = DynamicResource.provider(mLauncher)
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
- float velocityPxPerS = TypedValue.applyDimension(COMPLEX_UNIT_DIP,
- velocityDpPerS, mLauncher.getResources().getDisplayMetrics());
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocityPxPerS, false)
.getAnimators());
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 2b698bdd5b..f4a394a4f6 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -150,16 +150,9 @@ public abstract class BaseActivityInterface mOnDestroyActions = new ArrayList<>();
@@ -107,76 +101,10 @@ public class RecentsAnimationDeviceState implements
}
};
- private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
- @Override
- public void onRecentTaskListFrozenChanged(boolean frozen) {
- mTaskListFrozen = frozen;
- if (frozen || mInOverview) {
- return;
- }
- enableMultipleRegions(false);
- }
-
- @Override
- public void onActivityRotation(int displayId) {
- super.onActivityRotation(displayId);
- // This always gets called before onDisplayInfoChanged() so we know how to process
- // the rotation in that method. This is done to avoid having a race condition between
- // the sensor readings and onDisplayInfoChanged() call
- if (displayId != mDisplayId) {
- return;
- }
-
- mPrioritizeDeviceRotation = true;
- if (mInOverview) {
- // reset, launcher must be rotating
- mExitOverviewRunnable.run();
- }
- }
- };
-
- private Runnable mExitOverviewRunnable = new Runnable() {
- @Override
- public void run() {
- mInOverview = false;
- enableMultipleRegions(false);
- }
- };
-
- private OrientationTouchTransformer mOrientationTouchTransformer;
- /**
- * Used to listen for when the device rotates into the orientation of the current
- * foreground app. For example, if a user quickswitches from a portrait to a fixed landscape
- * app and then rotates rotates the device to match that orientation, this triggers calls to
- * sysui to adjust the navbar.
- */
- private OrientationEventListener mOrientationListener;
- private int mSensorRotation = ROTATION_0;
- /**
- * This is the configuration of the foreground app or the app that will be in the foreground
- * once a quickstep gesture finishes.
- */
- private int mCurrentAppRotation = -1;
- /**
- * This flag is set to true when the device physically changes orientations. When true,
- * we will always report the current rotation of the foreground app whenever the display
- * changes, as it would indicate the user's intention to rotate the foreground app.
- */
- private boolean mPrioritizeDeviceRotation = false;
-
private Region mExclusionRegion;
private SystemGestureExclusionListenerCompat mExclusionListener;
private final List mGestureBlockedActivities;
- private Runnable mOnDestroyFrozenTaskRunnable;
- /**
- * Set to true when user swipes to recents. In recents, we ignore the state of the recents
- * task list being frozen or not to allow the user to keep interacting with nav bar rotation
- * they went into recents with as opposed to defaulting to the default display rotation.
- * TODO: (b/156984037) For when user rotates after entering overview
- */
- private boolean mInOverview;
- private boolean mTaskListFrozen;
private boolean mIsUserSetupComplete;
@@ -186,6 +114,8 @@ public class RecentsAnimationDeviceState implements
mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
mDisplayId = mDefaultDisplay.getInfo().id;
runOnDestroy(() -> mDefaultDisplay.removeChangeListener(this));
+ mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
+ runOnDestroy(mRotationTouchHelper::destroy);
// Register for user unlocked if necessary
mIsUserUnlocked = context.getSystemService(UserManager.class)
@@ -207,10 +137,6 @@ public class RecentsAnimationDeviceState implements
};
runOnDestroy(mExclusionListener::unregister);
- Resources resources = mContext.getResources();
- mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
- () -> QuickStepContract.getWindowCornerRadius(resources));
-
// Register for navigation mode changes
onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
@@ -241,38 +167,6 @@ public class RecentsAnimationDeviceState implements
userSetupObserver.register();
runOnDestroy(userSetupObserver::unregister);
}
-
- mOrientationListener = new OrientationEventListener(context) {
- @Override
- public void onOrientationChanged(int degrees) {
- int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
- mSensorRotation);
- if (newRotation == mSensorRotation) {
- return;
- }
-
- mSensorRotation = newRotation;
- mPrioritizeDeviceRotation = true;
-
- if (newRotation == mCurrentAppRotation) {
- // When user rotates device to the orientation of the foreground app after
- // quickstepping
- toggleSecondaryNavBarsForRotation();
- }
- }
- };
- }
-
- private void setupOrientationSwipeHandler() {
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
- mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
- .unregisterTaskStackListener(mFrozenTaskListener);
- runOnDestroy(mOnDestroyFrozenTaskRunnable);
- }
-
- private void destroyOrientationSwipeHandlerCallback() {
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
- mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
}
private void runOnDestroy(Runnable action) {
@@ -311,13 +205,6 @@ public class RecentsAnimationDeviceState implements
mNavBarPosition = new NavBarPosition(newMode, mDefaultDisplay.getInfo());
- mOrientationTouchTransformer.setNavigationMode(newMode, mDefaultDisplay.getInfo());
- if (!mMode.hasGestures && newMode.hasGestures) {
- setupOrientationSwipeHandler();
- } else if (mMode.hasGestures && !newMode.hasGestures){
- destroyOrientationSwipeHandlerCallback();
- }
-
mMode = newMode;
}
@@ -328,28 +215,10 @@ public class RecentsAnimationDeviceState implements
return;
}
- mDisplayRotation = info.rotation;
-
if (!mMode.hasGestures) {
return;
}
mNavBarPosition = new NavBarPosition(mMode, info);
- updateGestureTouchRegions();
- mOrientationTouchTransformer.createOrAddTouchRegion(info);
- mCurrentAppRotation = mDisplayRotation;
-
- /* Update nav bars on the following:
- * a) if this is coming from an activity rotation OR
- * aa) we launch an app in the orientation that user is already in
- * b) We're not in overview, since overview will always be portrait (w/o home rotation)
- * c) We're actively in quickswitch mode
- */
- if ((mPrioritizeDeviceRotation
- || mCurrentAppRotation == mSensorRotation) // switch to an app of orientation user is in
- && !mInOverview
- && mTaskListFrozen) {
- toggleSecondaryNavBarsForRotation();
- }
}
/**
@@ -464,7 +333,7 @@ public class RecentsAnimationDeviceState implements
*/
public boolean canStartSystemGesture() {
boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
- || mTaskListFrozen;
+ || mRotationTouchHelper.isTaskListFrozen();
return canStartWithNavHidden
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
@@ -536,33 +405,6 @@ public class RecentsAnimationDeviceState implements
return (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0;
}
- /**
- * Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
- */
- public void updateGestureTouchRegions() {
- if (!mMode.hasGestures) {
- return;
- }
-
- mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo());
- }
-
- /**
- * @return whether the coordinates of the {@param event} is in the swipe up gesture region.
- */
- public boolean isInSwipeUpTouchRegion(MotionEvent event) {
- return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY());
- }
-
- /**
- * @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
- * is in the swipe up gesture region.
- */
- public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
- return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
- event.getY(pointerIndex));
- }
-
/**
* Sets the region in screen space where the gestures should be deferred (ie. due to specific
* nav bar ui).
@@ -620,101 +462,13 @@ public class RecentsAnimationDeviceState implements
public boolean canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task) {
return mAssistantAvailable
&& !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
- && mOrientationTouchTransformer.touchInAssistantRegion(ev)
+ && mRotationTouchHelper.touchInAssistantRegion(ev)
&& !isLockToAppActive()
&& !isGestureBlockedActivity(task);
}
- /**
- * *May* apply a transform on the motion event if it lies in the nav bar region for another
- * orientation that is currently being tracked as a part of quickstep
- */
- void setOrientationTransformIfNeeded(MotionEvent event) {
- // negative coordinates bug b/143901881
- if (event.getX() < 0 || event.getY() < 0) {
- event.setLocation(Math.max(0, event.getX()), Math.max(0, event.getY()));
- }
- mOrientationTouchTransformer.transform(event);
- }
-
- private void enableMultipleRegions(boolean enable) {
- mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
- notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
- if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
- // Clear any previous state from sensor manager
- mSensorRotation = mCurrentAppRotation;
- mOrientationListener.enable();
- } else {
- mOrientationListener.disable();
- }
- }
-
- public void onStartGesture() {
- if (mTaskListFrozen) {
- // Prioritize whatever nav bar user touches once in quickstep
- // This case is specifically when user changes what nav bar they are using mid
- // quickswitch session before tasks list is unfrozen
- notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
- }
- }
-
- void onEndTargetCalculated(GestureState.GestureEndTarget endTarget,
- BaseActivityInterface activityInterface) {
- if (endTarget == GestureState.GestureEndTarget.RECENTS) {
- mInOverview = true;
- if (!mTaskListFrozen) {
- // If we're in landscape w/o ever quickswitching, show the navbar in landscape
- enableMultipleRegions(true);
- }
- activityInterface.onExitOverview(this, mExitOverviewRunnable);
- } else if (endTarget == GestureState.GestureEndTarget.HOME) {
- enableMultipleRegions(false);
- } else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) {
- if (mOrientationTouchTransformer.getQuickStepStartingRotation() == -1) {
- // First gesture to start quickswitch
- enableMultipleRegions(true);
- } else {
- notifySysuiOfCurrentRotation(
- mOrientationTouchTransformer.getCurrentActiveRotation());
- }
-
- // A new gesture is starting, reset the current device rotation
- // This is done under the assumption that the user won't rotate the phone and then
- // quickswitch in the old orientation.
- mPrioritizeDeviceRotation = false;
- } else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) {
- if (!mTaskListFrozen) {
- // touched nav bar but didn't go anywhere and not quickswitching, do nothing
- return;
- }
- notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
- }
- }
-
- private void notifySysuiOfCurrentRotation(int rotation) {
- UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
- .onQuickSwitchToNewTask(rotation));
- }
-
- /**
- * Disables/Enables multiple nav bars on {@link OrientationTouchTransformer} and then
- * notifies system UI of the primary rotation the user is interacting with
- */
- private void toggleSecondaryNavBarsForRotation() {
- mOrientationTouchTransformer.setSingleActiveRegion(mDefaultDisplay.getInfo());
- notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
- }
-
- public int getCurrentActiveRotation() {
- if (!mMode.hasGestures) {
- // touch rotation should always match that of display for 3 button
- return mDisplayRotation;
- }
- return mOrientationTouchTransformer.getCurrentActiveRotation();
- }
-
- public int getDisplayRotation() {
- return mDisplayRotation;
+ public RotationTouchHelper getRotationTouchHelper() {
+ return mRotationTouchHelper;
}
public void dump(PrintWriter pw) {
@@ -726,9 +480,7 @@ public class RecentsAnimationDeviceState implements
pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
- pw.println(" currentActiveRotation=" + getCurrentActiveRotation());
- pw.println(" displayRotation=" + getDisplayRotation());
pw.println(" isUserUnlocked=" + mIsUserUnlocked);
- mOrientationTouchTransformer.dump(pw);
+ mRotationTouchHelper.dump(pw);
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 517501ad9c..6f54ba2935 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -92,15 +92,6 @@ public class RecentsModel extends TaskStackChangeListener {
return mTaskList.getTasks(false /* loadKeysOnly */, callback);
}
- /**
- * @return The task id of the running task, or -1 if there is no current running task.
- */
- public static int getRunningTaskId() {
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- return runningTask != null ? runningTask.id : -1;
- }
-
/**
* @return Whether the provided {@param changeId} is the latest recent tasks list id.
*/
@@ -140,7 +131,9 @@ public class RecentsModel extends TaskStackChangeListener {
}
// Keep the cache up to date with the latest thumbnails
- int runningTaskId = RecentsModel.getRunningTaskId();
+ ActivityManager.RunningTaskInfo runningTask =
+ ActivityManagerWrapper.getInstance().getRunningTask();
+ int runningTaskId = runningTask != null ? runningTask.id : -1;
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
for (Task task : tasks) {
if (task.key.id == runningTaskId) {
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
new file mode 100644
index 0000000000..d89ca10b69
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static android.view.Surface.ROTATION_0;
+
+import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
+import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.MotionEvent;
+import android.view.OrientationEventListener;
+
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class RotationTouchHelper implements
+ SysUINavigationMode.NavigationModeChangeListener,
+ DefaultDisplay.DisplayInfoChangeListener {
+ public static final MainThreadInitializedObject INSTANCE =
+ new MainThreadInitializedObject<>(RotationTouchHelper::new);
+
+ private final OrientationTouchTransformer mOrientationTouchTransformer;
+ private final DefaultDisplay mDefaultDisplay;
+ private final SysUINavigationMode mSysUiNavMode;
+ private final int mDisplayId;
+ private int mDisplayRotation;
+
+ private final ArrayList mOnDestroyActions = new ArrayList<>();
+
+ private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
+
+ private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() {
+ @Override
+ public void onRecentTaskListFrozenChanged(boolean frozen) {
+ mTaskListFrozen = frozen;
+ if (frozen || mInOverview) {
+ return;
+ }
+ enableMultipleRegions(false);
+ }
+
+ @Override
+ public void onActivityRotation(int displayId) {
+ super.onActivityRotation(displayId);
+ // This always gets called before onDisplayInfoChanged() so we know how to process
+ // the rotation in that method. This is done to avoid having a race condition between
+ // the sensor readings and onDisplayInfoChanged() call
+ if (displayId != mDisplayId) {
+ return;
+ }
+
+ mPrioritizeDeviceRotation = true;
+ if (mInOverview) {
+ // reset, launcher must be rotating
+ mExitOverviewRunnable.run();
+ }
+ }
+ };
+
+ private Runnable mExitOverviewRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mInOverview = false;
+ enableMultipleRegions(false);
+ }
+ };
+
+ /**
+ * Used to listen for when the device rotates into the orientation of the current foreground
+ * app. For example, if a user quickswitches from a portrait to a fixed landscape app and then
+ * rotates rotates the device to match that orientation, this triggers calls to sysui to adjust
+ * the navbar.
+ */
+ private OrientationEventListener mOrientationListener;
+ private int mSensorRotation = ROTATION_0;
+ /**
+ * This is the configuration of the foreground app or the app that will be in the foreground
+ * once a quickstep gesture finishes.
+ */
+ private int mCurrentAppRotation = -1;
+ /**
+ * This flag is set to true when the device physically changes orientations. When true, we will
+ * always report the current rotation of the foreground app whenever the display changes, as it
+ * would indicate the user's intention to rotate the foreground app.
+ */
+ private boolean mPrioritizeDeviceRotation = false;
+ private Runnable mOnDestroyFrozenTaskRunnable;
+ /**
+ * Set to true when user swipes to recents. In recents, we ignore the state of the recents
+ * task list being frozen or not to allow the user to keep interacting with nav bar rotation
+ * they went into recents with as opposed to defaulting to the default display rotation.
+ * TODO: (b/156984037) For when user rotates after entering overview
+ */
+ private boolean mInOverview;
+ private boolean mTaskListFrozen;
+
+
+ private final Context mContext;
+
+ private RotationTouchHelper(Context context) {
+ mContext = context;
+ Resources resources = mContext.getResources();
+ mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
+ mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
+ mDisplayId = mDefaultDisplay.getInfo().id;
+
+ mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
+ () -> QuickStepContract.getWindowCornerRadius(resources));
+
+ // Register for navigation mode changes
+ onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
+ runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
+
+ mOrientationListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int degrees) {
+ int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
+ mSensorRotation);
+ if (newRotation == mSensorRotation) {
+ return;
+ }
+
+ mSensorRotation = newRotation;
+ mPrioritizeDeviceRotation = true;
+
+ if (newRotation == mCurrentAppRotation) {
+ // When user rotates device to the orientation of the foreground app after
+ // quickstepping
+ toggleSecondaryNavBarsForRotation();
+ }
+ }
+ };
+ }
+
+ private void setupOrientationSwipeHandler() {
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
+ mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
+ .unregisterTaskStackListener(mFrozenTaskListener);
+ runOnDestroy(mOnDestroyFrozenTaskRunnable);
+ }
+
+ private void destroyOrientationSwipeHandlerCallback() {
+ ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
+ mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
+ }
+
+ private void runOnDestroy(Runnable action) {
+ mOnDestroyActions.add(action);
+ }
+
+ /**
+ * Cleans up all the registered listeners and receivers.
+ */
+ public void destroy() {
+ for (Runnable r : mOnDestroyActions) {
+ r.run();
+ }
+ }
+
+ public boolean isTaskListFrozen() {
+ return mTaskListFrozen;
+ }
+
+ public boolean touchInAssistantRegion(MotionEvent ev) {
+ return mOrientationTouchTransformer.touchInAssistantRegion(ev);
+ }
+
+ /**
+ * Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
+ */
+ public void updateGestureTouchRegions() {
+ if (!mMode.hasGestures) {
+ return;
+ }
+
+ mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo());
+ }
+
+ /**
+ * @return whether the coordinates of the {@param event} is in the swipe up gesture region.
+ */
+ public boolean isInSwipeUpTouchRegion(MotionEvent event) {
+ return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY());
+ }
+
+ /**
+ * @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
+ * is in the swipe up gesture region.
+ */
+ public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
+ return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex),
+ event.getY(pointerIndex));
+ }
+
+
+ @Override
+ public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
+ mDefaultDisplay.removeChangeListener(this);
+ mDefaultDisplay.addChangeListener(this);
+ onDisplayInfoChanged(mDefaultDisplay.getInfo(), CHANGE_ALL);
+
+ mOrientationTouchTransformer.setNavigationMode(newMode, mDefaultDisplay.getInfo());
+ if (!mMode.hasGestures && newMode.hasGestures) {
+ setupOrientationSwipeHandler();
+ } else if (mMode.hasGestures && !newMode.hasGestures){
+ destroyOrientationSwipeHandlerCallback();
+ }
+
+ mMode = newMode;
+ }
+
+ public int getDisplayRotation() {
+ return mDisplayRotation;
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
+ if (info.id != mDisplayId|| flags == CHANGE_FRAME_DELAY) {
+ // ignore displays that aren't running launcher and frame refresh rate changes
+ return;
+ }
+
+ mDisplayRotation = info.rotation;
+
+ if (!mMode.hasGestures) {
+ return;
+ }
+ updateGestureTouchRegions();
+ mOrientationTouchTransformer.createOrAddTouchRegion(info);
+ mCurrentAppRotation = mDisplayRotation;
+
+ /* Update nav bars on the following:
+ * a) if this is coming from an activity rotation OR
+ * aa) we launch an app in the orientation that user is already in
+ * b) We're not in overview, since overview will always be portrait (w/o home rotation)
+ * c) We're actively in quickswitch mode
+ */
+ if ((mPrioritizeDeviceRotation
+ || mCurrentAppRotation == mSensorRotation) // switch to an app of orientation user is in
+ && !mInOverview
+ && mTaskListFrozen) {
+ toggleSecondaryNavBarsForRotation();
+ }
+ }
+
+ /**
+ * *May* apply a transform on the motion event if it lies in the nav bar region for another
+ * orientation that is currently being tracked as a part of quickstep
+ */
+ void setOrientationTransformIfNeeded(MotionEvent event) {
+ // negative coordinates bug b/143901881
+ if (event.getX() < 0 || event.getY() < 0) {
+ event.setLocation(Math.max(0, event.getX()), Math.max(0, event.getY()));
+ }
+ mOrientationTouchTransformer.transform(event);
+ }
+
+ private void enableMultipleRegions(boolean enable) {
+ mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
+ notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
+ if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
+ // Clear any previous state from sensor manager
+ mSensorRotation = mCurrentAppRotation;
+ mOrientationListener.enable();
+ } else {
+ mOrientationListener.disable();
+ }
+ }
+
+ public void onStartGesture() {
+ if (mTaskListFrozen) {
+ // Prioritize whatever nav bar user touches once in quickstep
+ // This case is specifically when user changes what nav bar they are using mid
+ // quickswitch session before tasks list is unfrozen
+ notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
+ }
+ }
+
+ void onEndTargetCalculated(GestureState.GestureEndTarget endTarget,
+ BaseActivityInterface activityInterface) {
+ if (endTarget == GestureState.GestureEndTarget.RECENTS) {
+ mInOverview = true;
+ if (!mTaskListFrozen) {
+ // If we're in landscape w/o ever quickswitching, show the navbar in landscape
+ enableMultipleRegions(true);
+ }
+ activityInterface.onExitOverview(this, mExitOverviewRunnable);
+ } else if (endTarget == GestureState.GestureEndTarget.HOME) {
+ enableMultipleRegions(false);
+ } else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) {
+ if (mOrientationTouchTransformer.getQuickStepStartingRotation() == -1) {
+ // First gesture to start quickswitch
+ enableMultipleRegions(true);
+ } else {
+ notifySysuiOfCurrentRotation(
+ mOrientationTouchTransformer.getCurrentActiveRotation());
+ }
+
+ // A new gesture is starting, reset the current device rotation
+ // This is done under the assumption that the user won't rotate the phone and then
+ // quickswitch in the old orientation.
+ mPrioritizeDeviceRotation = false;
+ } else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) {
+ if (!mTaskListFrozen) {
+ // touched nav bar but didn't go anywhere and not quickswitching, do nothing
+ return;
+ }
+ notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
+ }
+ }
+
+ private void notifySysuiOfCurrentRotation(int rotation) {
+ UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
+ .onQuickSwitchToNewTask(rotation));
+ }
+
+ /**
+ * Disables/Enables multiple nav bars on {@link OrientationTouchTransformer} and then
+ * notifies system UI of the primary rotation the user is interacting with
+ */
+ private void toggleSecondaryNavBarsForRotation() {
+ mOrientationTouchTransformer.setSingleActiveRegion(mDefaultDisplay.getInfo());
+ notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
+ }
+
+ public int getCurrentActiveRotation() {
+ if (!mMode.hasGestures) {
+ // touch rotation should always match that of display for 3 button
+ return mDisplayRotation;
+ }
+ return mOrientationTouchTransformer.getCurrentActiveRotation();
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("RotationTouchHelper:");
+ pw.println(" currentActiveRotation=" + getCurrentActiveRotation());
+ pw.println(" displayRotation=" + getDisplayRotation());
+ mOrientationTouchTransformer.dump(pw);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 14e00dce7a..4110b33d24 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -17,6 +17,7 @@ package com.android.quickstep.interaction;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION;
import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
@@ -110,6 +111,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
+ mFakeIconView.setVisibility(View.INVISIBLE);
mFakeTaskView.setVisibility(View.INVISIBLE);
mFakeTaskView.setAlpha(1);
mRunningWindowAnim = null;
@@ -131,6 +133,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
});
} else {
anim.setViewAlpha(mFakeTaskView, 0, ACCEL);
+ anim.setViewAlpha(mFakeIconView, 0, ACCEL);
anim.addListener(resetTaskView);
}
if (onEndRunnable != null) {
@@ -202,7 +205,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
// derivative of the scroll interpolator at zero, ie. 2.
long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y));
long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
- HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) {
+ HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory() {
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
@@ -218,6 +221,24 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
fakeHomeIconLeft + fakeHomeIconSizePx,
fakeHomeIconTop + fakeHomeIconSizePx);
}
+
+ @Override
+ public void update(RectF rect, float progress, float radius) {
+ mFakeIconView.setVisibility(View.VISIBLE);
+ mFakeIconView.update(rect, progress,
+ 1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */,
+ radius,
+ false, /* isOpening */
+ mFakeIconView, mDp,
+ false /* isVerticalBarLayout */);
+ mFakeIconView.setAlpha(1);
+ mFakeTaskView.setAlpha(getWindowAlpha(progress));
+ }
+
+ @Override
+ public void onCancel() {
+ mFakeIconView.setVisibility(View.INVISIBLE);
+ }
};
RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory);
windowAnim.start(mContext, velocityPxPerMs);
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index c1918c2326..73f1f8cb8c 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -28,6 +28,7 @@ import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
@@ -46,6 +47,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
final TextView mTitleTextView;
final TextView mSubtitleTextView;
final TextView mFeedbackView;
+ final ClipIconView mFakeIconView;
final View mFakeTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
@@ -66,6 +68,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
+ mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index eac45e9907..ecbe73447d 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -27,7 +27,7 @@ import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGE
import android.content.Context;
import android.util.Log;
-import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
@@ -54,6 +54,7 @@ import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.ArrayList;
import java.util.Optional;
import java.util.OptionalInt;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* This class calls StatsLog compile time generated methods.
@@ -68,8 +69,6 @@ public class StatsLogCompatManager extends StatsLogManager {
private static final String TAG = "StatsLog";
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
- private static Context sContext;
-
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
// from nano to lite, bake constant to prevent robo test failure.
@@ -77,8 +76,13 @@ public class StatsLogCompatManager extends StatsLogManager {
private static final int FOLDER_HIERARCHY_OFFSET = 100;
private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
+ public static final CopyOnWriteArrayList LOGS_CONSUMER =
+ new CopyOnWriteArrayList<>();
+
+ private final Context mContext;
+
public StatsLogCompatManager(Context context) {
- sContext = context;
+ mContext = context;
}
@Override
@@ -86,25 +90,12 @@ public class StatsLogCompatManager extends StatsLogManager {
return new StatsCompatLogger();
}
- /**
- * Logs a ranking event and accompanying {@link InstanceId} and package name.
- */
- @Override
- public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
- int position) {
- SysUiStatsLog.write(SysUiStatsLog.RANKING_SELECTED,
- rankingEvent.getId() /* event_id = 1; */,
- packageName /* package_name = 2; */,
- instanceId.getId() /* instance_id = 3; */,
- position /* position_picked = 4; */);
- }
-
/**
* Logs the workspace layout information on the model thread.
*/
@Override
public void logSnapshot() {
- LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
+ LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
new SnapshotWorker());
}
@@ -175,6 +166,7 @@ public class StatsLogCompatManager extends StatsLogManager {
private static class StatsCompatLogger implements StatsLogger {
private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
+
private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private OptionalInt mRank = OptionalInt.empty();
@@ -253,36 +245,35 @@ public class StatsLogCompatManager extends StatsLogManager {
return;
}
- if (mItemInfo.container < 0) {
- // Item is not within a folder. Write to StatsLog in same thread.
- write(event, mInstanceId, applyOverwrites(mItemInfo.buildProto()), mSrcState,
- mDstState);
+ LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
+ if (mItemInfo.container < 0 || appState == null) {
+ // Write log on the model thread so that logs do not go out of order
+ // (for eg: drop comes after drag)
+ Executors.MODEL_EXECUTOR.execute(
+ () -> write(event, applyOverwrites(mItemInfo.buildProto())));
} else {
// Item is inside the folder, fetch folder info in a BG thread
// and then write to StatsLog.
- LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
+ appState.getModel().enqueueModelUpdateTask(
new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel,
AllAppsList apps) {
FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
- write(event, mInstanceId,
- applyOverwrites(mItemInfo.buildProto(folderInfo)),
- mSrcState, mDstState);
+ write(event, applyOverwrites(mItemInfo.buildProto(folderInfo)));
}
});
}
}
private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
- LauncherAtom.ItemInfo.Builder itemInfoBuilder =
- (LauncherAtom.ItemInfo.Builder) atomInfo.toBuilder();
+ LauncherAtom.ItemInfo.Builder itemInfoBuilder = atomInfo.toBuilder();
mRank.ifPresent(itemInfoBuilder::setRank);
mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
- FolderIcon.Builder folderIconBuilder = (FolderIcon.Builder) itemInfoBuilder
+ FolderIcon.Builder folderIconBuilder = itemInfoBuilder
.getFolderIcon()
.toBuilder();
mFromState.ifPresent(folderIconBuilder::setFromLabelState);
@@ -293,8 +284,11 @@ public class StatsLogCompatManager extends StatsLogManager {
return itemInfoBuilder.build();
}
- private void write(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo,
- int srcState, int dstState) {
+ @WorkerThread
+ private void write(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
+ InstanceId instanceId = mInstanceId;
+ int srcState = mSrcState;
+ int dstState = mDstState;
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + "";
@@ -307,6 +301,10 @@ public class StatsLogCompatManager extends StatsLogManager {
atomInfo));
}
+ for (StatsLogConsumer consumer : LOGS_CONSUMER) {
+ consumer.consume(event, atomInfo);
+ }
+
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT,
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
@@ -446,7 +444,16 @@ public class StatsLogCompatManager extends StatsLogManager {
return "ALLAPPS";
default:
return "INVALID";
-
}
}
+
+
+ /**
+ * Interface to get stats log while it is dispatched to the system
+ */
+ public interface StatsLogConsumer {
+
+ @WorkerThread
+ void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index ae19d73719..f7bd1e29d8 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -49,7 +49,7 @@ public class LayoutUtils {
Rect taskSize = new Rect();
LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize,
orientationHandler);
- return (dp.heightPx - taskSize.height()) / 2;
+ return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
}
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
diff --git a/res/layout/launcher_preview_layout.xml b/res/layout/launcher_preview_layout.xml
index 3fd02e3fda..4a20c705b6 100644
--- a/res/layout/launcher_preview_layout.xml
+++ b/res/layout/launcher_preview_layout.xml
@@ -17,7 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:focusable="false">
0.7
- 150
- 3dp
+ 4dp
- 0.7
- 200
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 9cb8cf2c51..112126b988 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -45,7 +45,6 @@ import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
@@ -82,7 +81,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
super.onCreate(savedInstanceState);
- mIsSafeModeEnabled = TraceHelper.whitelistIpcs("isSafeMode",
+ mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
DefaultDisplay.INSTANCE.get(this).addChangeListener(this);
@@ -154,8 +153,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
public abstract ActivityOptions getActivityLaunchOptions(View v);
- public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
- @Nullable String sourceContainer) {
+ public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item) {
if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
@@ -176,17 +174,13 @@ public abstract class BaseDraggingActivity extends BaseActivity
&& !((WorkspaceItemInfo) item).isPromise();
if (isShortcut) {
// Shortcuts need some special checks due to legacy reasons.
- startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
+ startShortcutIntentSafely(intent, optsBundle, item);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
- AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
- Process.myUserHandle(), sourceContainer);
} else {
getSystemService(LauncherApps.class).startMainActivity(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
- AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
- sourceContainer);
}
getUserEventDispatcher().logAppLaunch(v, intent, user);
if (item != null) {
@@ -206,8 +200,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
.log(LAUNCHER_APP_LAUNCH_TAP);
}
- private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
- @Nullable String sourceContainer) {
+ private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
try {
StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
try {
@@ -221,8 +214,6 @@ public abstract class BaseDraggingActivity extends BaseActivity
String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
String packageName = intent.getPackage();
startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
- AppLaunchTracker.INSTANCE.get(this).onStartShortcut(packageName, id, info.user,
- sourceContainer);
} else {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 8eceec04ec..41eeb78bc7 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -183,6 +183,10 @@ public abstract class BaseRecyclerView extends RecyclerView {
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onScrollStateChanged: " + state);
+ }
+
if (state == SCROLL_STATE_IDLE) {
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
}
@@ -192,6 +196,10 @@ public abstract class BaseRecyclerView extends RecyclerView {
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (isLayoutSuppressed()) info.setScrollable(false);
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS,
+ "onInitializeAccessibilityNodeInfo, scrollable: " + info.isScrollable());
+ }
}
@Override
@@ -199,8 +207,12 @@ public abstract class BaseRecyclerView extends RecyclerView {
final boolean changing = frozen != isLayoutSuppressed();
super.setLayoutFrozen(frozen);
if (changing) {
- ActivityContext.lookupContext(getContext()).getDragLayer()
- .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "setLayoutFrozen " + frozen
+ + " @ " + Log.getStackTraceString(new Throwable()));
+ ActivityContext.lookupContext(getContext()).getDragLayer()
+ .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
+ }
}
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 60b6da67f9..48819cbfa0 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -331,10 +331,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
public boolean onTouchEvent(MotionEvent event) {
// ignore events if they happen in padding area
if (event.getAction() == MotionEvent.ACTION_DOWN
- && (event.getY() < getPaddingTop()
- || event.getX() < getPaddingLeft()
- || event.getY() > getHeight() - getPaddingBottom()
- || event.getX() > getWidth() - getPaddingRight())) {
+ && shouldIgnoreTouchDown(event.getX(), event.getY())) {
return false;
}
if (isLongClickable()) {
@@ -347,6 +344,16 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
}
}
+ /**
+ * Returns true if the touch down at the provided position be ignored
+ */
+ protected boolean shouldIgnoreTouchDown(float x, float y) {
+ return y < getPaddingTop()
+ || x < getPaddingLeft()
+ || y > getHeight() - getPaddingBottom()
+ || x > getWidth() - getPaddingRight();
+ }
+
void setStayPressed(boolean stayPressed) {
mStayPressed = stayPressed;
refreshDrawableState();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0970dae479..2bf47fbec6 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -814,7 +814,7 @@ public class Launcher extends StatefulActivity implements Launche
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- startActivitySafely(v, intent, null, null);
+ startActivitySafely(v, intent, null);
} else {
// TODO: Show a snack bar with link to settings
Toast.makeText(this, getString(R.string.msg_no_phone_permission,
@@ -923,6 +923,7 @@ public class Launcher extends StatefulActivity implements Launche
DiscoveryBounce.showForHomeIfNeeded(this);
}
+ protected void handlePendingActivityRequest() { }
private void logStopAndResume(int command) {
int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
@@ -1423,7 +1424,8 @@ public class Launcher extends StatefulActivity implements Launche
if (!isInState(NORMAL)) {
// Only change state, if not already the same. This prevents cancelling any
// animations running as part of resume
- mStateManager.goToState(NORMAL);
+ mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange(),
+ this::handlePendingActivityRequest);
}
// Reset the apps view
@@ -1860,13 +1862,12 @@ public class Launcher extends StatefulActivity implements Launche
}
@Override
- public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
- @Nullable String sourceContainer) {
+ public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
if (!hasBeenResumed()) {
// Workaround an issue where the WM launch animation is clobbered when finishing the
// recents animation into launcher. Defer launching the activity until Launcher is
// next resumed.
- addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
+ addOnResumeCallback(() -> startActivitySafely(v, intent, item));
if (mOnDeferredActivityLaunchCallback != null) {
mOnDeferredActivityLaunchCallback.run();
mOnDeferredActivityLaunchCallback = null;
@@ -1874,7 +1875,7 @@ public class Launcher extends StatefulActivity implements Launche
return true;
}
- boolean success = super.startActivitySafely(v, intent, item, sourceContainer);
+ boolean success = super.startActivitySafely(v, intent, item);
if (success && v instanceof BubbleTextView) {
// This is set to the view that launched the activity that navigated the user away
// from launcher. Since there is no callback for when the activity has finished
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f434c9180a..ff4b545a06 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -58,7 +58,7 @@ import com.android.launcher3.pm.InstallSessionTracker;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageUserKey;
@@ -410,7 +410,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- final IntSparseArrayMap removedIds = new IntSparseArrayMap<>();
+ final IntSet removedIds = new IntSet();
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo
@@ -418,13 +418,13 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
&& user.equals(info.user)
&& info.getIntent() != null
&& TextUtils.equals(packageName, info.getIntent().getPackage())) {
- removedIds.put(info.id, true /* remove */);
+ removedIds.add(info.id);
}
}
}
if (!removedIds.isEmpty()) {
- deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
+ deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds));
}
}
});
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index c78df62991..39b0f2ffb9 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -250,7 +250,8 @@ public abstract class LauncherState implements BaseState {
}
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
- if (this != NORMAL || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
+ if ((this != NORMAL && this != HINT_STATE)
+ || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
return DEFAULT_ALPHA_PROVIDER;
}
final int centerPage = launcher.getWorkspace().getNextPage();
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 499b54f6d2..56875bb337 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -39,7 +39,7 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -218,13 +218,16 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
public void onDrop(DragObject d, DragOptions options) {
// Defer onComplete
d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
+
super.onDrop(d, options);
+ StatsLogger logger = mStatsLogManager.logger().withInstanceId(d.logInstanceId);
+ if (d.originalDragInfo != null) {
+ logger.withItemInfo(d.originalDragInfo);
+ }
if (mCurrentAccessibilityAction == UNINSTALL) {
- mStatsLogManager.logger().withInstanceId(d.logInstanceId)
- .log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
+ logger.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
} else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
- mStatsLogManager.logger().withInstanceId(d.logInstanceId)
- .log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
+ logger.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
}
}
@@ -283,8 +286,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
return null;
}
if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
- AppLaunchTracker.INSTANCE.get(getContext()).onDismissApp(info.getTargetComponent(),
- info.user, AppLaunchTracker.CONTAINER_PREDICTIONS);
+ // We sent the log event, nothing else left to do
return null;
}
// else: mCurrentAccessibilityAction == UNINSTALL
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 1441e0b93b..a6283ff253 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -309,7 +309,9 @@ public class Workspace extends PagedView
// In portrait, we want the pages spaced such that there is no
// overhang of the previous / next page into the current page viewport.
// We assume symmetrical padding in portrait mode.
- setPageSpacing(Math.max(grid.edgeMarginPx, padding.left + 1));
+ int maxInsets = Math.max(insets.left, insets.right);
+ int maxPadding = Math.max(grid.edgeMarginPx, padding.left + 1);
+ setPageSpacing(Math.max(maxInsets, maxPadding));
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index c989e7bf64..77b8a32c7a 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -83,7 +83,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
protected final BaseDraggingActivity mLauncher;
protected final AdapterHolder[] mAH;
private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
- private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
+ private final ItemInfoMatcher mWorkMatcher = mPersonalMatcher.negate();
private final AllAppsStore mAllAppsStore = new AllAppsStore();
private final Paint mNavBarScrimPaint;
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 8ec4d273f8..d65316090c 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -41,7 +41,6 @@ import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.PackageManagerHelper;
@@ -270,7 +269,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter mLauncher.startActivitySafely(
- v, mMarketSearchIntent, null, AppLaunchTracker.CONTAINER_SEARCH));
+ v, mMarketSearchIntent, null));
return new ViewHolder(searchMarketView);
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index ed45749b1e..df1cd26da4 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -28,7 +28,6 @@ import android.widget.TextView.OnEditorActionListener;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Utilities;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
@@ -112,8 +111,8 @@ public class AllAppsSearchBarController
return false;
}
return mLauncher.startActivitySafely(v,
- PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null,
- AppLaunchTracker.CONTAINER_SEARCH);
+ PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null
+ );
}
@Override
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 1d32d1dfa2..d86bb17698 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -75,6 +75,9 @@ public class AccessibilityManagerCompat {
}
public static void sendScrollFinishedEventToTest(Context context) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "sendScrollFinishedEventToTest");
+ }
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return;
@@ -94,6 +97,9 @@ public class AccessibilityManagerCompat {
AccessibilityEvent.TYPE_ANNOUNCEMENT);
e.setClassName(eventTag);
e.setParcelableData(data);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "sendEventToTest " + e);
+ }
accessibilityManager.sendAccessibilityEvent(e);
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index d01e189f7e..de2b5da6ad 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -241,9 +241,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
mFolderName.setSelectAllOnFocus(true);
mFolderName.setInputType(mFolderName.getInputType()
& ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
- & ~InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
+ | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
| InputType.TYPE_TEXT_FLAG_CAP_WORDS);
- mFolderName.forceDisableSuggestions(!FeatureFlags.FOLDER_NAME_SUGGEST.get());
+ mFolderName.forceDisableSuggestions(true);
mFooter = findViewById(R.id.folder_footer);
@@ -741,7 +741,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
@Override
protected View getAccessibilityInitialFocusView() {
- return mContent.getFirstItem();
+ View firstItem = mContent.getFirstItem();
+ return firstItem != null ? firstItem : super.getAccessibilityInitialFocusView();
}
private void closeComplete(boolean wasAnimated) {
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 458ffa368d..a424f8458a 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -126,7 +126,7 @@ public class LauncherPreviewRenderer {
*/
public static class PreviewContext extends ContextWrapper {
- private static final Set WHITELIST = new HashSet<>(
+ private final Set mAllowedObjects = new HashSet<>(
Arrays.asList(UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE));
@@ -160,7 +160,7 @@ public class LauncherPreviewRenderer {
*/
public T getObject(MainThreadInitializedObject mainThreadInitializedObject,
MainThreadInitializedObject.ObjectProvider provider) {
- if (!WHITELIST.contains(mainThreadInitializedObject)) {
+ if (!mAllowedObjects.contains(mainThreadInitializedObject)) {
throw new IllegalStateException("Leaking unknown objects");
}
if (mainThreadInitializedObject == LauncherAppState.INSTANCE) {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 8e23b65d5f..acf44824dc 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.logging;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_CLOSE_DOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_OPEN_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
@@ -23,8 +22,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import android.content.Context;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.R;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logger.LauncherAtom.FromState;
@@ -403,19 +400,6 @@ public class StatsLogManager implements ResourceBasedOverride {
return mgr;
}
- /**
- * Log an event with ranked-choice information along with package. Does nothing if event.getId()
- * <= 0.
- *
- * @param rankingEvent an enum implementing EventEnum interface.
- * @param instanceId An identifier obtained from an InstanceIdSequence.
- * @param packageName the package name of the relevant app, if known (null otherwise).
- * @param position the position picked.
- */
- public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
- int position) {
- }
-
/**
* Logs snapshot, or impression of the current workspace.
*/
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
index 629a0ee496..a93c0dd77c 100644
--- a/src/com/android/launcher3/model/AppLaunchTracker.java
+++ b/src/com/android/launcher3/model/AppLaunchTracker.java
@@ -17,13 +17,7 @@ package com.android.launcher3.model;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-import android.content.ComponentName;
-import android.os.UserHandle;
-
-import androidx.annotation.Nullable;
-
import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -32,28 +26,8 @@ import com.android.launcher3.util.ResourceBasedOverride;
*/
public class AppLaunchTracker implements ResourceBasedOverride {
- /**
- * Derived from LauncherEvent proto.
- * TODO: Use proper descriptive constants
- */
- public static final String CONTAINER_DEFAULT = Integer.toString(ContainerType.WORKSPACE);
- public static final String CONTAINER_ALL_APPS = Integer.toString(ContainerType.ALLAPPS);
- public static final String CONTAINER_PREDICTIONS = Integer.toString(ContainerType.PREDICTION);
- public static final String CONTAINER_SEARCH = Integer.toString(ContainerType.SEARCHRESULT);
- public static final String CONTAINER_OVERVIEW = Integer.toString(ContainerType.OVERVIEW);
-
-
public static final MainThreadInitializedObject INSTANCE =
forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class);
- public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
- @Nullable String container) { }
-
- public void onStartApp(ComponentName componentName, UserHandle user,
- @Nullable String container) { }
-
- public void onDismissApp(ComponentName componentName, UserHandle user,
- @Nullable String container){}
-
public void onReturnedToHome() { }
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 9bef84718b..7524920ce8 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -56,6 +56,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
@@ -348,6 +349,19 @@ public class BgDataModel {
}
}
+ /**
+ * Calls the provided {@code op} for all workspaceItems in the in-memory model (both persisted
+ * items and dynamic/predicted items for the provided {@code userHandle}.
+ * Note the call is not synchronized over the model, that should be handled by the called.
+ */
+ public void forAllWorkspaceItemInfos(UserHandle userHandle, Consumer op) {
+ for (ItemInfo info : itemsIdMap) {
+ if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
+ op.accept((WorkspaceItemInfo) info);
+ }
+ }
+ }
+
public interface Callbacks {
// If the launcher has permission to access deep shortcuts.
int FLAG_HAS_SHORTCUT_PERMISSION = 1 << 0;
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 8e6b064498..f644d49276 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -21,7 +21,6 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import java.util.ArrayList;
@@ -48,23 +47,18 @@ public class CacheDataUpdatedTask extends BaseModelUpdateTask {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IconCache iconCache = app.getIconCache();
-
-
ArrayList updatedShortcuts = new ArrayList<>();
synchronized (dataModel) {
- for (ItemInfo info : dataModel.itemsIdMap) {
- if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) info;
- ComponentName cn = si.getTargetComponent();
- if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && isValidShortcut(si) && cn != null
- && mPackages.contains(cn.getPackageName())) {
- iconCache.getTitleAndIcon(si, si.usingLowResIcon());
- updatedShortcuts.add(si);
- }
+ dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+ ComponentName cn = si.getTargetComponent();
+ if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+ && isValidShortcut(si) && cn != null
+ && mPackages.contains(cn.getPackageName())) {
+ iconCache.getTitleAndIcon(si, si.usingLowResIcon());
+ updatedShortcuts.add(si);
}
- }
+ });
apps.updateIconsAndLabels(mPackages, mUser);
}
bindUpdatedWorkspaceItems(updatedShortcuts);
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 203f1ca8e3..8369c48e5c 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -20,8 +20,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.PromiseAppInfo;
@@ -70,21 +68,18 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
synchronized (dataModel) {
final HashSet updates = new HashSet<>();
- for (ItemInfo info : dataModel.itemsIdMap) {
- if (info instanceof WorkspaceItemInfo) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) info;
- ComponentName cn = si.getTargetComponent();
- if (si.hasPromiseIconUi() && (cn != null)
- && mInstallInfo.packageName.equals(cn.getPackageName())) {
- si.setInstallProgress(mInstallInfo.progress);
- if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
- // Mark this info as broken.
- si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
- }
- updates.add(si);
+ dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
+ ComponentName cn = si.getTargetComponent();
+ if (si.hasPromiseIconUi() && (cn != null)
+ && mInstallInfo.packageName.equals(cn.getPackageName())) {
+ si.setInstallProgress(mInstallInfo.progress);
+ if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
+ // Mark this info as broken.
+ si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
}
+ updates.add(si);
}
- }
+ });
for (LauncherAppWidgetInfo widget : dataModel.appWidgets) {
if (widget.providerName.getPackageName().equals(mInstallInfo.packageName)) {
@@ -94,12 +89,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
}
if (!updates.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindRestoreItemsChange(updates);
- }
- });
+ scheduleCallbackTask(callbacks -> callbacks.bindRestoreItemsChange(updates));
}
}
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 7cd467efd6..dca4ec0dcc 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -45,7 +45,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.FlagOp;
-import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
@@ -92,9 +92,11 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
final String[] packages = mPackages;
final int N = packages.length;
- FlagOp flagOp = FlagOp.NO_OP;
+ final FlagOp flagOp;
final HashSet packageSet = new HashSet<>(Arrays.asList(packages));
- ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
+ final ItemInfoMatcher matcher = mOp == OP_USER_AVAILABILITY_CHANGE
+ ? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user
+ : ItemInfoMatcher.ofPackages(packageSet, mUser);
final HashSet removedComponents = new HashSet<>();
switch (mOp) {
@@ -158,19 +160,22 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
flagOp = ums.isUserQuiet(mUser)
? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
: FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
- // We want to update all packages for this user.
- matcher = ItemInfoMatcher.ofUser(mUser);
appsList.updateDisabledFlags(matcher, flagOp);
// We are not synchronizing here, as int operations are atomic
appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled());
break;
}
+ default:
+ flagOp = FlagOp.NO_OP;
+ break;
}
bindApplicationsIfNeeded();
- final IntSparseArrayMap removedShortcuts = new IntSparseArrayMap<>();
+ final IntSet removedShortcuts = new IntSet();
+ // Shortcuts to keep even if the corresponding app was removed
+ final IntSet forceKeepShortcuts = new IntSet();
// Update shortcut infos
if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
@@ -180,118 +185,118 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
// For system apps, package manager send OP_UPDATE when an app is enabled.
final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
synchronized (dataModel) {
- for (ItemInfo info : dataModel.itemsIdMap) {
- if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) info;
- boolean infoUpdated = false;
- boolean shortcutUpdated = false;
+ dataModel.forAllWorkspaceItemInfos(mUser, si -> {
- // Update shortcuts which use iconResource.
- if ((si.iconResource != null)
- && packageSet.contains(si.iconResource.packageName)) {
- LauncherIcons li = LauncherIcons.obtain(context);
- BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
- li.recycle();
- if (iconInfo != null) {
- si.bitmap = iconInfo;
- infoUpdated = true;
+ boolean infoUpdated = false;
+ boolean shortcutUpdated = false;
+
+ // Update shortcuts which use iconResource.
+ if ((si.iconResource != null)
+ && packageSet.contains(si.iconResource.packageName)) {
+ LauncherIcons li = LauncherIcons.obtain(context);
+ BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
+ li.recycle();
+ if (iconInfo != null) {
+ si.bitmap = iconInfo;
+ infoUpdated = true;
+ }
+ }
+
+ ComponentName cn = si.getTargetComponent();
+ if (cn != null && matcher.matches(si, cn)) {
+ String packageName = cn.getPackageName();
+
+ if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
+ forceKeepShortcuts.add(si.id);
+ if (mOp == OP_REMOVE) {
+ return;
}
}
- ComponentName cn = si.getTargetComponent();
- if (cn != null && matcher.matches(si, cn)) {
- String packageName = cn.getPackageName();
-
- if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
- removedShortcuts.put(si.id, false);
- if (mOp == OP_REMOVE) {
- continue;
- }
- }
-
- if (si.isPromise() && isNewApkAvailable) {
- boolean isTargetValid = true;
- if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- List shortcut =
- new ShortcutRequest(context, mUser)
- .forPackage(cn.getPackageName(),
- si.getDeepShortcutId())
- .query(ShortcutRequest.PINNED);
- if (shortcut.isEmpty()) {
- isTargetValid = false;
- } else {
- si.updateFromDeepShortcutInfo(shortcut.get(0), context);
- infoUpdated = true;
- }
- } else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
- isTargetValid = context.getSystemService(LauncherApps.class)
- .isActivityEnabled(cn, mUser);
- }
- if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
- if (updateWorkspaceItemIntent(context, si, packageName)) {
- infoUpdated = true;
- } else if (si.hasPromiseIconUi()) {
- removedShortcuts.put(si.id, true);
- continue;
- }
- } else if (!isTargetValid) {
- removedShortcuts.put(si.id, true);
- FileLog.e(TAG, "Restored shortcut no longer valid "
- + si.getIntent());
- continue;
+ if (si.isPromise() && isNewApkAvailable) {
+ boolean isTargetValid = true;
+ if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ List shortcut =
+ new ShortcutRequest(context, mUser)
+ .forPackage(cn.getPackageName(),
+ si.getDeepShortcutId())
+ .query(ShortcutRequest.PINNED);
+ if (shortcut.isEmpty()) {
+ isTargetValid = false;
} else {
- si.status = WorkspaceItemInfo.DEFAULT;
+ si.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true;
}
- } else if (isNewApkAvailable && removedComponents.contains(cn)) {
+ } else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
+ isTargetValid = context.getSystemService(LauncherApps.class)
+ .isActivityEnabled(cn, mUser);
+ }
+ if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
+ } else if (si.hasPromiseIconUi()) {
+ removedShortcuts.add(si.id);
+ return;
}
- }
-
- if (isNewApkAvailable &&
- si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
- iconCache.getTitleAndIcon(si, si.usingLowResIcon());
+ } else if (!isTargetValid) {
+ removedShortcuts.add(si.id);
+ FileLog.e(TAG, "Restored shortcut no longer valid "
+ + si.getIntent());
+ return;
+ } else {
+ si.status = WorkspaceItemInfo.DEFAULT;
infoUpdated = true;
}
-
- int oldRuntimeFlags = si.runtimeStatusFlags;
- si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
- if (si.runtimeStatusFlags != oldRuntimeFlags) {
- shortcutUpdated = true;
+ } else if (isNewApkAvailable && removedComponents.contains(cn)) {
+ if (updateWorkspaceItemIntent(context, si, packageName)) {
+ infoUpdated = true;
}
}
- if (infoUpdated || shortcutUpdated) {
- updatedWorkspaceItems.add(si);
+ if (isNewApkAvailable
+ && si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
+ iconCache.getTitleAndIcon(si, si.usingLowResIcon());
+ infoUpdated = true;
}
- if (infoUpdated) {
- getModelWriter().updateItemInDatabase(si);
- }
- } else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
- LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
- if (mUser.equals(widgetInfo.user)
- && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
- && packageSet.contains(widgetInfo.providerName.getPackageName())) {
- widgetInfo.restoreStatus &=
- ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
- ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
- // adding this flag ensures that launcher shows 'click to setup'
- // if the widget has a config activity. In case there is no config
- // activity, it will be marked as 'restored' during bind.
- widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
-
- widgets.add(widgetInfo);
- getModelWriter().updateItemInDatabase(widgetInfo);
+ int oldRuntimeFlags = si.runtimeStatusFlags;
+ si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
+ if (si.runtimeStatusFlags != oldRuntimeFlags) {
+ shortcutUpdated = true;
}
}
+
+ if (infoUpdated || shortcutUpdated) {
+ updatedWorkspaceItems.add(si);
+ }
+ if (infoUpdated && si.id != ItemInfo.NO_ID) {
+ getModelWriter().updateItemInDatabase(si);
+ }
+ });
+
+ for (LauncherAppWidgetInfo widgetInfo : dataModel.appWidgets) {
+ if (mUser.equals(widgetInfo.user)
+ && widgetInfo.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+ && packageSet.contains(widgetInfo.providerName.getPackageName())) {
+ widgetInfo.restoreStatus &=
+ ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY
+ & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+
+ // adding this flag ensures that launcher shows 'click to setup'
+ // if the widget has a config activity. In case there is no config
+ // activity, it will be marked as 'restored' during bind.
+ widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+
+ widgets.add(widgetInfo);
+ getModelWriter().updateItemInDatabase(widgetInfo);
+ }
}
}
bindUpdatedWorkspaceItems(updatedWorkspaceItems);
if (!removedShortcuts.isEmpty()) {
- deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
+ deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts));
}
if (!widgets.isEmpty()) {
@@ -319,7 +324,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
.or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
- .and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));
+ .and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate());
deleteAndBindComponentsRemoved(removeMatch);
// Remove any queued items from the install queue
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 1cbe5c2f1b..88006ba6fb 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -21,7 +21,6 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -58,14 +57,14 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask {
MultiHashMap keyToShortcutInfo = new MultiHashMap<>();
HashSet allIds = new HashSet<>();
- for (ItemInfo itemInfo : dataModel.itemsIdMap) {
- if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
- if (mPackageName.equals(si.getIntent().getPackage()) && si.user.equals(mUser)) {
+ synchronized (dataModel) {
+ dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+ if ((si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ && mPackageName.equals(si.getIntent().getPackage())) {
keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
allIds.add(si.getDeepShortcutId());
}
- }
+ });
}
final ArrayList updatedWorkspaceItemInfos = new ArrayList<>();
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 7ec884f424..5048e13e3e 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -23,7 +23,6 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -73,27 +72,27 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask {
ArrayList updatedWorkspaceItemInfos = new ArrayList<>();
HashSet removedKeys = new HashSet<>();
- for (ItemInfo itemInfo : dataModel.itemsIdMap) {
- if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
- && mUser.equals(itemInfo.user)) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
- if (mIsUserUnlocked) {
- ShortcutKey key = ShortcutKey.fromItemInfo(si);
- ShortcutInfo shortcut = pinnedShortcuts.get(key);
- // We couldn't verify the shortcut during loader. If its no longer available
- // (probably due to clear data), delete the workspace item as well
- if (shortcut == null) {
- removedKeys.add(key);
- continue;
+ synchronized (dataModel) {
+ dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+ if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ if (mIsUserUnlocked) {
+ ShortcutKey key = ShortcutKey.fromItemInfo(si);
+ ShortcutInfo shortcut = pinnedShortcuts.get(key);
+ // We couldn't verify the shortcut during loader. If its no longer available
+ // (probably due to clear data), delete the workspace item as well
+ if (shortcut == null) {
+ removedKeys.add(key);
+ return;
+ }
+ si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
+ si.updateFromDeepShortcutInfo(shortcut, context);
+ app.getIconCache().getShortcutIcon(si, shortcut);
+ } else {
+ si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
- si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
- si.updateFromDeepShortcutInfo(shortcut, context);
- app.getIconCache().getShortcutIcon(si, shortcut);
- } else {
- si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
+ updatedWorkspaceItemInfos.add(si);
}
- updatedWorkspaceItemInfos.add(si);
- }
+ });
}
bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
if (!removedKeys.isEmpty()) {
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 0d3ddad183..59233cd4d8 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -53,6 +53,7 @@ import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ContentWriter;
import java.util.Optional;
@@ -285,6 +286,13 @@ public class ItemInfo {
.orElse(LauncherAtom.Application.newBuilder()));
break;
case ITEM_TYPE_DEEP_SHORTCUT:
+ itemBuilder
+ .setShortcut(nullableComponent
+ .map(component -> LauncherAtom.Shortcut.newBuilder()
+ .setShortcutName(component.flattenToShortString())
+ .setShortcutId(ShortcutKey.fromItemInfo(this).getId()))
+ .orElse(LauncherAtom.Shortcut.newBuilder()));
+ break;
case ITEM_TYPE_SHORTCUT:
itemBuilder
.setShortcut(nullableComponent
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index fd292a3714..79984889ef 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -174,7 +174,7 @@ public abstract class SystemShortcut extends Ite
public void onClick(View view) {
Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
mItemInfo.getTargetComponent().getPackageName());
- mTarget.startActivitySafely(view, intent, mItemInfo, null);
+ mTarget.startActivitySafely(view, intent, mItemInfo);
AbstractFloatingView.closeAllOpenViews(mTarget);
}
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 21ad2756fb..a013312860 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.secondarydisplay;
-import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityOptions;
@@ -327,7 +325,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
- startActivitySafely(v, intent, item, CONTAINER_ALL_APPS);
+ startActivitySafely(v, intent, item);
}
}
}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index 2daa2fe78e..eb685921d9 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -23,7 +23,6 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.view.MotionEvent;
import android.widget.Toast;
import com.android.launcher3.BubbleTextView;
@@ -106,12 +105,12 @@ public class DeepShortcutTextView extends BubbleTextView {
}
@Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- // Show toast if user touches the drag handle (long clicks still start the drag).
- mShowInstructionToast = mDragHandleBounds.contains((int) ev.getX(), (int) ev.getY());
- }
- return super.onTouchEvent(ev);
+ protected boolean shouldIgnoreTouchDown(float x, float y) {
+ // Show toast if user touches the drag handle (long clicks still start the drag).
+ mShowInstructionToast = mDragHandleBounds.contains((int) x, (int) y);
+
+ // assume the whole view as clickable
+ return false;
}
@Override
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 38b3712f6e..d4a132e5ba 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -33,6 +33,7 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.widget.WidgetsFullSheet;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
@@ -92,6 +93,11 @@ public class TestInformationHandler implements ResourceBasedOverride {
l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
}
+ case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
+ return getLauncherUIProperty(Bundle::putInt,
+ l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
+ }
+
case TestProtocol.REQUEST_WINDOW_INSETS: {
return getUIProperty(Bundle::putParcelable, a -> {
WindowInsets insets = a.getWindow()
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 3ca08fd8ec..2644db8f3c 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -81,6 +81,7 @@ public final class TestProtocol {
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
+ public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
public static final String REQUEST_WINDOW_INSETS = "window-insets";
public static final String REQUEST_PID = "pid";
public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
@@ -106,4 +107,5 @@ public final class TestProtocol {
public static final String PAUSE_NOT_DETECTED = "b/139891609";
public static final String OVERIEW_NOT_ALLAPPS = "b/156095088";
public static final String NO_SWIPE_TO_HOME = "b/158017601";
+ public static final String NO_SCROLL_END_WIDGETS = "b/160238801";
}
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 848666674d..61d6f7d804 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -18,7 +18,6 @@ package com.android.launcher3.touch;
import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
-import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
@@ -37,8 +36,6 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -72,13 +69,9 @@ public class ItemClickHandler {
/**
* Instance used for click handling on items
*/
- public static final OnClickListener INSTANCE = getInstance(null);
+ public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
- public static final OnClickListener getInstance(String sourceContainer) {
- return v -> onClick(v, sourceContainer);
- }
-
- private static void onClick(View v, String sourceContainer) {
+ private static void onClick(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) return;
@@ -88,14 +81,14 @@ public class ItemClickHandler {
Object tag = v.getTag();
if (tag instanceof WorkspaceItemInfo) {
- onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher, sourceContainer);
+ onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
onClickFolderIcon(v);
}
} else if (tag instanceof AppInfo) {
- startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher,
- sourceContainer == null ? CONTAINER_ALL_APPS: sourceContainer);
+ startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher
+ );
} else if (tag instanceof LauncherAppWidgetInfo) {
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
@@ -191,7 +184,7 @@ public class ItemClickHandler {
// Fallback to using custom market intent.
Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
- launcher.startActivitySafely(v, intent, item, null);
+ launcher.startActivitySafely(v, intent, item);
}
/**
@@ -199,8 +192,7 @@ public class ItemClickHandler {
*
* @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
*/
- public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher,
- @Nullable String sourceContainer) {
+ public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
if (shortcut.isDisabled()) {
final int disabledFlags = shortcut.runtimeStatusFlags
& WorkspaceItemInfo.FLAG_DISABLED_MASK;
@@ -241,11 +233,10 @@ public class ItemClickHandler {
}
// Start activities
- startAppShortcutOrInfoActivity(v, shortcut, launcher, sourceContainer);
+ startAppShortcutOrInfoActivity(v, shortcut, launcher);
}
- private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher,
- @Nullable String sourceContainer) {
+ private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
Intent intent;
@@ -274,6 +265,6 @@ public class ItemClickHandler {
// Preload the icon to reduce latency b/w swapping the floating view with the original.
FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
}
- launcher.startActivitySafely(v, intent, item, sourceContainer);
+ launcher.startActivitySafely(v, intent, item);
}
}
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 48c773413f..ac1ade260d 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -17,6 +17,7 @@
package com.android.launcher3.touch;
import static android.widget.ListPopupWindow.WRAP_CONTENT;
+
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
@@ -33,6 +34,7 @@ import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller;
@@ -237,7 +239,8 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
}
@Override
- public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
+ public int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate,
+ LinearLayout taskMenuLayout) {
return LinearLayout.HORIZONTAL;
}
@@ -260,4 +263,10 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
}
return new ChildBounds(childHeight, childWidth, childBottom, childLeft);
}
+
+ @SuppressWarnings("SuspiciousNameCombination")
+ @Override
+ public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
+ return rect.left;
+ }
}
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 65b1a7a77e..d4f5cba40e 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -94,8 +94,9 @@ public interface PagedOrientationHandler {
float getTaskMenuX(float x, View thumbnailView);
float getTaskMenuY(float y, View thumbnailView);
int getTaskMenuWidth(View view);
- int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout);
+ int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate, LinearLayout taskMenuLayout);
void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp);
+ int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
/**
* Maps the velocity from the coordinate plane of the foreground app to that
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 79e5c87785..3341996383 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -32,6 +32,7 @@ import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.PagedView;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.OverScroller;
@@ -236,8 +237,9 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
}
@Override
- public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
- return taskMenuLayout.getOrientation();
+ public int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate,
+ LinearLayout taskMenuLayout) {
+ return canRecentsActivityRotate ? taskMenuLayout.getOrientation() : LinearLayout.VERTICAL;
}
@Override
@@ -257,4 +259,9 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
}
return new ChildBounds(childWidth, childHeight, childRight, childTop);
}
+
+ @Override
+ public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
+ return dp.heightPx - rect.bottom;
+ }
}
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index d5ae2dcb16..e91f16d71a 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -18,9 +18,11 @@ package com.android.launcher3.touch;
import android.content.res.Resources;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.view.Surface;
import android.view.View;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
@@ -77,4 +79,9 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
view.setTranslationX(0);
view.setTranslationY(translation);
}
+
+ @Override
+ public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
+ return dp.widthPx - rect.right;
+ }
}
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 4d5405d102..e98af35563 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -81,11 +81,10 @@ public interface ItemInfoMatcher {
}
/**
- * Returns a new matcher which returns the opposite boolean value of the provided
- * {@param matcher}.
+ * Returns a new matcher with returns the opposite value of this.
*/
- static ItemInfoMatcher not(ItemInfoMatcher matcher) {
- return (info, cn) -> !matcher.matches(info, cn);
+ default ItemInfoMatcher negate() {
+ return (info, cn) -> !matches(info, cn);
}
static ItemInfoMatcher ofUser(UserHandle user) {
@@ -105,7 +104,10 @@ public interface ItemInfoMatcher {
keys.contains(ShortcutKey.fromItemInfo(info));
}
- static ItemInfoMatcher ofItemIds(IntSparseArrayMap ids, Boolean matchDefault) {
- return (info, cn) -> ids.get(info.id, matchDefault);
+ /**
+ * Returns a matcher for items with provided ids
+ */
+ static ItemInfoMatcher ofItemIds(IntSet ids) {
+ return (info, cn) -> ids.contains(info.id);
}
}
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index fc9f8f7ec4..f6003dd7bf 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -46,7 +46,7 @@ public class MainThreadInitializedObject {
if (mValue == null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
- mValue = TraceHelper.whitelistIpcs("main.thread.object",
+ mValue = TraceHelper.allowIpcs("main.thread.object",
() -> mProvider.get(context.getApplicationContext()));
} else {
try {
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index d4e074c897..26313e5a21 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -64,7 +64,7 @@ public class OnboardingPrefs {
private static final Map MAX_COUNTS;
static {
- Map maxCounts = new ArrayMap<>(3);
+ Map maxCounts = new ArrayMap<>(4);
maxCounts.put(HOME_BOUNCE_COUNT, 3);
maxCounts.put(SHELF_BOUNCE_COUNT, 3);
maxCounts.put(ALL_APPS_COUNT, 5);
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index 168227db6e..c23df77db1 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -78,7 +78,7 @@ public class TraceHelper {
* Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
*/
@MainThread
- public static T whitelistIpcs(String rpcName, Supplier supplier) {
+ public static T allowIpcs(String rpcName, Supplier supplier) {
Object traceToken = INSTANCE.beginSection(rpcName, FLAG_IGNORE_BINDERS);
try {
return supplier.get();
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index b010b4b94b..f60c65593a 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -292,6 +292,9 @@ public abstract class BaseDragLayer
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "BaseDragLayer: " + ev);
+ }
switch (ev.getAction()) {
case ACTION_DOWN: {
if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) {
@@ -602,13 +605,13 @@ public abstract class BaseDragLayer
*/
private boolean computeAllowSysuiScrims(@Nullable WallpaperInfo newWallpaperInfo) {
if (newWallpaperInfo == null) {
- // New wallpaper is static, not live. Thus, blacklist isn't applicable.
+ // Static wallpapers need scrim unless determined otherwise by wallpaperColors.
return true;
}
ComponentName newWallpaper = newWallpaperInfo.getComponent();
for (String wallpaperWithoutScrim : mWallpapersWithoutSysuiScrims) {
if (newWallpaper.equals(ComponentName.unflattenFromString(wallpaperWithoutScrim))) {
- // New wallpaper is blacklisted from showing a scrim.
+ // New wallpaper does not need a scrim.
return false;
}
}
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index 1a8e11b400..fab0bd4a0a 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -36,6 +36,7 @@ import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewOutlineProvider;
import androidx.annotation.Nullable;
@@ -44,8 +45,6 @@ import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InsettableFrameLayout.LayoutParams;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
@@ -94,7 +93,6 @@ public class ClipIconView extends View implements ClipPathView {
}
};
- private final Launcher mLauncher;
private final int mBlurSizeOutline;
private final boolean mIsRtl;
@@ -128,7 +126,6 @@ public class ClipIconView extends View implements ClipPathView {
public ClipIconView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mLauncher = Launcher.getLauncher(context);
mBlurSizeOutline = getResources().getDimensionPixelSize(
R.dimen.blur_size_medium_outline);
mIsRtl = Utilities.isRtl(getResources());
@@ -143,10 +140,41 @@ public class ClipIconView extends View implements ClipPathView {
.setStiffness(SpringForce.STIFFNESS_LOW));
}
- void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
- boolean isOpening, float scale, float minSize, LayoutParams parentLp,
- boolean isVerticalBarLayout) {
- DeviceProfile dp = mLauncher.getDeviceProfile();
+ /**
+ * Update the icon UI to match the provided parameters during an animation frame
+ */
+ public void update(RectF rect, float progress, float shapeProgressStart,
+ float cornerRadius, boolean isOpening, View container,
+ DeviceProfile dp, boolean isVerticalBarLayout) {
+
+ MarginLayoutParams lp = (MarginLayoutParams) container.getLayoutParams();
+
+ float dX = mIsRtl
+ ? rect.left - (dp.widthPx - lp.getMarginStart() - lp.width)
+ : rect.left - lp.getMarginStart();
+ float dY = rect.top - lp.topMargin;
+ container.setTranslationX(dX);
+ container.setTranslationY(dY);
+
+ float minSize = Math.min(lp.width, lp.height);
+ float scaleX = rect.width() / minSize;
+ float scaleY = rect.height() / minSize;
+ float scale = Math.max(1f, Math.min(scaleX, scaleY));
+
+ update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
+ minSize, lp, isVerticalBarLayout, dp);
+
+ container.setPivotX(0);
+ container.setPivotY(0);
+ container.setScaleX(scale);
+ container.setScaleY(scale);
+
+ container.invalidate();
+ }
+
+ private void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
+ boolean isOpening, float scale, float minSize, MarginLayoutParams parentLp,
+ boolean isVerticalBarLayout, DeviceProfile dp) {
float dX = mIsRtl
? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
: rect.left - parentLp.getMarginStart();
@@ -228,8 +256,11 @@ public class ClipIconView extends View implements ClipPathView {
}
}
- void setIcon(@Nullable Drawable drawable, int iconOffset, LayoutParams lp, boolean isOpening,
- boolean isVerticalBarLayout) {
+ /**
+ * Sets the icon for this view as part of initial setup
+ */
+ public void setIcon(@Nullable Drawable drawable, int iconOffset, MarginLayoutParams lp,
+ boolean isOpening, boolean isVerticalBarLayout, DeviceProfile dp) {
mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
if (mIsAdaptiveIcon) {
boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
@@ -264,15 +295,14 @@ public class ClipIconView extends View implements ClipPathView {
Utilities.scaleRectAboutCenter(mStartRevealRect, IconShape.getNormalizationScale());
}
- float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
if (isVerticalBarLayout) {
- lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
+ lp.width = (int) Math.max(lp.width, lp.height * dp.aspectRatio);
} else {
- lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
+ lp.height = (int) Math.max(lp.height, lp.width * dp.aspectRatio);
}
int left = mIsRtl
- ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
+ ? dp.widthPx - lp.getMarginStart() - lp.width
: lp.leftMargin;
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 177aff4a89..7cdde2e84b 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -20,6 +20,7 @@ import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -47,7 +48,6 @@ import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -144,32 +144,8 @@ public class FloatingIconView extends FrameLayout implements
public void update(RectF rect, float alpha, float progress, float shapeProgressStart,
float cornerRadius, boolean isOpening) {
setAlpha(alpha);
-
- InsettableFrameLayout.LayoutParams lp =
- (InsettableFrameLayout.LayoutParams) getLayoutParams();
-
- DeviceProfile dp = mLauncher.getDeviceProfile();
- float dX = mIsRtl
- ? rect.left - (dp.widthPx - lp.getMarginStart() - lp.width)
- : rect.left - lp.getMarginStart();
- float dY = rect.top - lp.topMargin;
- setTranslationX(dX);
- setTranslationY(dY);
-
- float minSize = Math.min(lp.width, lp.height);
- float scaleX = rect.width() / minSize;
- float scaleY = rect.height() / minSize;
- float scale = Math.max(1f, Math.min(scaleX, scaleY));
-
- mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
- minSize, lp, mIsVerticalBarLayout);
-
- setPivotX(0);
- setPivotY(0);
- setScaleX(scale);
- setScaleY(scale);
-
- invalidate();
+ mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening,
+ this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout);
}
@Override
@@ -336,7 +312,8 @@ public class FloatingIconView extends FrameLayout implements
final InsettableFrameLayout.LayoutParams lp =
(InsettableFrameLayout.LayoutParams) getLayoutParams();
mBadge = badge;
- mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout);
+ mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout,
+ mLauncher.getDeviceProfile());
if (drawable instanceof AdaptiveIconDrawable) {
final int originalHeight = lp.height;
final int originalWidth = lp.width;
@@ -381,7 +358,7 @@ public class FloatingIconView extends FrameLayout implements
if (mIconLoadResult.isIconLoaded) {
setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
mIconLoadResult.iconOffset);
- hideOriginalView(originalView);
+ setIconAndDotVisible(originalView, false);
} else {
mIconLoadResult.onIconLoaded = () -> {
if (cancellationSignal.isCanceled()) {
@@ -392,22 +369,13 @@ public class FloatingIconView extends FrameLayout implements
mIconLoadResult.iconOffset);
setVisibility(VISIBLE);
- hideOriginalView(originalView);
+ setIconAndDotVisible(originalView, false);
};
mLoadIconSignal = cancellationSignal;
}
}
}
- private void hideOriginalView(View originalView) {
- if (originalView instanceof IconLabelDotView) {
- ((IconLabelDotView) originalView).setIconVisible(false);
- ((IconLabelDotView) originalView).setForceHideDot(true);
- } else {
- originalView.setVisibility(INVISIBLE);
- }
- }
-
@WorkerThread
@SuppressWarnings("WrongThread")
private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) {
@@ -477,7 +445,7 @@ public class FloatingIconView extends FrameLayout implements
}
if (!mIsOpening) {
// When closing an app, we want the item on the workspace to be invisible immediately
- hideOriginalView(mOriginalIcon);
+ setIconAndDotVisible(mOriginalIcon, false);
}
}
@@ -573,12 +541,7 @@ public class FloatingIconView extends FrameLayout implements
if (hideOriginal) {
if (isOpening) {
- if (originalView instanceof BubbleTextView) {
- ((BubbleTextView) originalView).setIconVisible(true);
- ((BubbleTextView) originalView).setForceHideDot(false);
- } else {
- originalView.setVisibility(VISIBLE);
- }
+ setIconAndDotVisible(originalView, true);
view.finish(dragLayer);
} else {
view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
@@ -615,12 +578,10 @@ public class FloatingIconView extends FrameLayout implements
});
if (originalView instanceof IconLabelDotView) {
- IconLabelDotView view = (IconLabelDotView) originalView;
fade.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- view.setIconVisible(true);
- view.setForceHideDot(false);
+ setIconAndDotVisible(originalView, true);
}
});
}
diff --git a/src/com/android/launcher3/views/IconLabelDotView.java b/src/com/android/launcher3/views/IconLabelDotView.java
index 057caafe78..e9113cf874 100644
--- a/src/com/android/launcher3/views/IconLabelDotView.java
+++ b/src/com/android/launcher3/views/IconLabelDotView.java
@@ -15,10 +15,24 @@
*/
package com.android.launcher3.views;
+import android.view.View;
+
/**
* A view that has an icon, label, and notification dot.
*/
public interface IconLabelDotView {
void setIconVisible(boolean visible);
void setForceHideDot(boolean hide);
+
+ /**
+ * Sets the visibility of icon and dot of the view
+ */
+ static void setIconAndDotVisible(View view, boolean visible) {
+ if (view instanceof IconLabelDotView) {
+ ((IconLabelDotView) view).setIconVisible(visible);
+ ((IconLabelDotView) view).setForceHideDot(!visible);
+ } else {
+ view.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index d558781be8..049a1acbf3 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -220,7 +220,7 @@ public class OptionsPopupView extends ArrowPopup
if (!TextUtils.isEmpty(pickerPackage)) {
intent.setPackage(pickerPackage);
}
- return launcher.startActivitySafely(v, intent, dummyInfo(intent), null);
+ return launcher.startActivitySafely(v, intent, dummyInfo(intent));
}
static WorkspaceItemInfo dummyInfo(Intent intent) {
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
index df6e2c3023..c9e80dc848 100644
--- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java
+++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
@@ -20,8 +20,10 @@ import android.util.Log;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
import java.util.ArrayList;
@@ -32,8 +34,8 @@ import java.util.Iterator;
* methods accordingly.
*/
public class WidgetsDiffReporter {
- private static final boolean DEBUG = false;
- private static final String TAG = "WidgetsDiffReporter";
+ private static final boolean DEBUG = Utilities.IS_RUNNING_IN_TEST_HARNESS; // b/160238801
+ private static final String TAG = TestProtocol.NO_SCROLL_END_WIDGETS;
private final IconCache mIconCache;
private final RecyclerView.Adapter mListener;
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index 68a3ec51ee..ba55f5adee 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -24,6 +24,7 @@ import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -38,8 +39,10 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.TopRoundedCornerView;
@@ -68,6 +71,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet
}
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsFullSheet: " + ev);
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
public WidgetsFullSheet(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 82d4110e85..8f81977fc2 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -120,6 +120,9 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
public int getCurrentScrollY() {
// Skip early if widgets are not bound.
if (isModelNotReady() || getChildCount() == 0) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "getCurrentScrollY: -1");
+ }
return -1;
}
@@ -128,6 +131,10 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
int y = (child.getMeasuredHeight() * rowIndex);
int offset = getLayoutManager().getDecoratedTop(child);
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS,
+ "getCurrentScrollY: " + (getPaddingTop() + y - offset));
+ }
return getPaddingTop() + y - offset;
}
@@ -158,13 +165,23 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
}
if (mTouchDownOnScroller) {
- return mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+ final boolean result = mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 1 " + result);
+ }
+ return result;
+ }
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 2 false");
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView.onTouchEvent");
+ }
if (mTouchDownOnScroller) {
mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
}
@@ -172,5 +189,31 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onRequestDisallowInterceptTouchEvent "
+ + disallowIntercept);
+ }
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ final boolean result = super.dispatchTouchEvent(ev);
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView: state: "
+ + getScrollState()
+ + " can scroll: " + getLayoutManager().canScrollVertically()
+ + " result: " + result
+ + " layout suppressed: " + isLayoutSuppressed()
+ + " event: " + ev);
+ }
+ return result;
+ }
+
+ @Override
+ public void stopNestedScroll() {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "stopNestedScroll");
+ }
+ super.stopNestedScroll();
}
}
\ No newline at end of file
diff --git a/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java b/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java
deleted file mode 100644
index 15a0ffaa0c..0000000000
--- a/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java
+++ /dev/null
@@ -1,54 +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.systemui.plugins;
-
-import android.content.ComponentName;
-import android.os.UserHandle;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Plugin interface which sends app launch events.
- */
-@ProvidesInterface(action = AppLaunchEventsPlugin.ACTION, version = AppLaunchEventsPlugin.VERSION)
-public interface AppLaunchEventsPlugin extends Plugin {
- String ACTION = "com.android.systemui.action.PLUGIN_APP_EVENTS";
- int VERSION = 1;
-
- /**
- * Receives onStartShortcut event from
- * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
- */
- void onStartShortcut(String packageName, String shortcutId, UserHandle user, String container);
-
- /**
- * Receives onStartApp event from
- * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
- */
- void onStartApp(ComponentName componentName, UserHandle user, String container);
-
- /**
- * Receives onDismissApp event from
- * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
- */
- void onDismissApp(ComponentName componentName, UserHandle user, String container);
-
- /**
- * Receives onReturnedToHome event from
- * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
- */
- void onReturnedToHome();
-}
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 1c8f095810..f243f279eb 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -29,6 +29,7 @@
@@ -39,6 +40,7 @@
@@ -49,6 +51,7 @@
@@ -58,12 +61,14 @@
+ android:name="com.android.launcher3.testcomponent.WidgetConfigActivity"
+ android:exported="true">
-
+
@@ -72,6 +77,7 @@
@@ -102,6 +108,7 @@
android:stateNotNeeded="true"
android:taskAffinity=""
android:theme="@android:style/Theme.DeviceDefault.Light"
+ android:exported="true"
android:windowSoftInputMode="adjustPan">
@@ -114,6 +121,7 @@
@@ -128,6 +136,7 @@
@@ -136,6 +145,7 @@
@@ -144,6 +154,7 @@
@@ -152,6 +163,7 @@
@@ -160,6 +172,7 @@
@@ -168,6 +181,7 @@
@@ -176,6 +190,7 @@
@@ -184,6 +199,7 @@
@@ -192,6 +208,7 @@
@@ -200,6 +217,7 @@
diff --git a/tests/dummy_app/AndroidManifest.xml b/tests/dummy_app/AndroidManifest.xml
index f00138c78e..d5e2320b70 100644
--- a/tests/dummy_app/AndroidManifest.xml
+++ b/tests/dummy_app/AndroidManifest.xml
@@ -26,6 +26,7 @@
diff --git a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
index 202dcb181f..dd216c7816 100644
--- a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
+++ b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
@@ -73,20 +73,12 @@ public class ActivityLeakTracker implements Application.ActivityLifecycleCallbac
}
public boolean noLeakedActivities() {
- int liveActivities = 0;
- int destroyedActivities = 0;
-
for (Activity activity : mActivities.keySet()) {
if (activity.isDestroyed()) {
- ++destroyedActivities;
- } else {
- ++liveActivities;
+ return false;
}
}
- if (liveActivities > 2) return false;
-
- // It's OK to have 1 leaked activity if no active activities exist.
- return liveActivities == 0 ? destroyedActivities <= 1 : destroyedActivities == 0;
+ return mActivities.size() <= 2;
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index f4274a86dc..53d60a0013 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -152,6 +152,7 @@ public final class LauncherInstrumentation {
private static final String WIDGETS_RES_ID = "widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container";
public static final int WAIT_TIME_MS = 10000;
+ public static final int LONG_WAIT_TIME_MS = 60000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static WeakReference sActiveContainer = new WeakReference<>(null);
@@ -635,9 +636,11 @@ public final class LauncherInstrumentation {
Parcelable executeAndWaitForEvent(Runnable command,
UiAutomation.AccessibilityEventFilter eventFilter, Supplier message) {
try {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "executeAndWaitForEvent: before");
final AccessibilityEvent event =
mInstrumentation.getUiAutomation().executeAndWaitForEvent(
- command, eventFilter, WAIT_TIME_MS);
+ command, eventFilter, LONG_WAIT_TIME_MS);
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "executeAndWaitForEvent: after");
assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event);
final Parcelable parcelableData = event.getParcelableData();
event.recycle();
@@ -1094,7 +1097,10 @@ public final class LauncherInstrumentation {
executeAndWaitForEvent(
() -> linearGesture(
startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE),
- event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
+ event -> {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "scroll: received event: " + event);
+ return TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName());
+ },
() -> "Didn't receive a scroll end message: " + startX + ", " + startY
+ ", " + endX + ", " + endY);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 4440b82976..ab6465c572 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -57,6 +57,8 @@ public class LogEventChecker {
while (true) {
rawEvents = mLauncher.getTestInfo(TestProtocol.REQUEST_GET_TEST_EVENTS)
.getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ if (rawEvents == null) return null;
+
final int expectedCount = mExpectedEvents.entrySet()
.stream().mapToInt(e -> e.getValue().size()).sum();
if (rawEvents.size() >= expectedCount
@@ -83,6 +85,7 @@ public class LogEventChecker {
String verify(long waitForExpectedCountMs, boolean successfulGesture) {
final ListMap actualEvents = finishSync(waitForExpectedCountMs);
+ if (actualEvents == null) return "null event sequences because launcher likely died";
final StringBuilder sb = new StringBuilder();
boolean hasMismatches = false;
@@ -91,8 +94,7 @@ public class LogEventChecker {
List actual = new ArrayList<>(actualEvents.getNonNull(sequence));
final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
- hasMismatches = hasMismatches
- || mismatchPosition != -1 && !ignoreMistatch(successfulGesture, sequence);
+ hasMismatches = hasMismatches || mismatchPosition != -1;
formatSequenceWithMismatch(
sb,
sequence,
@@ -103,8 +105,7 @@ public class LogEventChecker {
// Check for unexpected event sequences in the actual data.
for (String actualNamedSequence : actualEvents.keySet()) {
if (!mExpectedEvents.containsKey(actualNamedSequence)) {
- hasMismatches = hasMismatches
- || !ignoreMistatch(successfulGesture, actualNamedSequence);
+ hasMismatches = true;
formatSequenceWithMismatch(
sb,
actualNamedSequence,
@@ -117,13 +118,6 @@ public class LogEventChecker {
return hasMismatches ? "mismatching events: " + sb.toString() : null;
}
- // Workaround for b/154157191
- private static boolean ignoreMistatch(boolean successfulGesture, String sequence) {
- // b/156287114
- return false;
-// return TestProtocol.SEQUENCE_TIS.equals(sequence) && successfulGesture;
- }
-
// If the list of actual events matches the list of expected events, returns -1, otherwise
// the position of the mismatch.
private static int getMismatchPosition(List expected, List actual) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 39ac6454b0..49af616b85 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -28,6 +28,7 @@ import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
import com.android.launcher3.tapl.LauncherInstrumentation.GestureScope;
+import com.android.launcher3.testing.TestProtocol;
import java.util.Collection;
@@ -90,6 +91,12 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
return LauncherInstrumentation.ContainerType.WIDGETS;
}
+ private int getWidgetsScroll() {
+ return mLauncher.getTestInfo(
+ TestProtocol.REQUEST_WIDGETS_SCROLL_Y)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
public Widget getWidget(String labelText) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
@@ -136,7 +143,13 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
}
mLauncher.assertTrue("Too many attempts", ++i <= 40);
+ final int scroll = getWidgetsScroll();
mLauncher.scrollToLastVisibleRow(widgetsContainer, cells, 0);
+ final int newScroll = getWidgetsScroll();
+ mLauncher.assertTrue(
+ "Scrolled in a wrong direction in Widgets: from " + scroll + " to "
+ + newScroll, newScroll >= scroll);
+ mLauncher.assertTrue("Unable to scroll to the widget", newScroll != scroll);
}
}
}