mThumbnailChangeListeners = new ArrayList<>();
private final Context mContext;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 2f411efc7b..bf3cd8afe2 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,18 +16,19 @@
package com.android.quickstep.logging;
-import android.content.Context;
-import android.content.Intent;
-import android.stats.launcher.nano.LauncherExtension;
-import android.stats.launcher.nano.LauncherTarget;
-
import static android.stats.launcher.nano.Launcher.ALLAPPS;
import static android.stats.launcher.nano.Launcher.HOME;
import static android.stats.launcher.nano.Launcher.LAUNCH_APP;
import static android.stats.launcher.nano.Launcher.LAUNCH_TASK;
+import static android.stats.launcher.nano.Launcher.DISMISS_TASK;
import static android.stats.launcher.nano.Launcher.BACKGROUND;
import static android.stats.launcher.nano.Launcher.OVERVIEW;
+import android.content.Context;
+import android.content.Intent;
+import android.stats.launcher.nano.Launcher;
+import android.stats.launcher.nano.LauncherExtension;
+import android.stats.launcher.nano.LauncherTarget;
import android.view.View;
import com.android.launcher3.ItemInfo;
@@ -38,8 +39,6 @@ import com.android.launcher3.util.ComponentKey;
import com.android.systemui.shared.system.StatsLogCompat;
import com.google.protobuf.nano.MessageNano;
-import androidx.annotation.Nullable;
-
/**
* This method calls the StatsLog hidden method until they are made available public.
*
@@ -74,6 +73,27 @@ public class StatsLogCompatManager extends StatsLogManager {
MessageNano.toByteArray(ext), true);
}
+ @Override
+ public void logTaskDismiss(View v, ComponentKey componentKey) {
+ LauncherExtension ext = new LauncherExtension();
+ ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
+ int srcState = OVERVIEW;
+ fillInLauncherExtension(v, ext);
+ StatsLogCompat.write(DISMISS_TASK, srcState, BACKGROUND /* dstState */,
+ MessageNano.toByteArray(ext), true);
+ }
+
+ @Override
+ public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) {
+ LauncherExtension ext = new LauncherExtension();
+ ext.srcTarget = new LauncherTarget[1];
+ int srcState = mStateProvider.getCurrentState();
+ fillInLauncherExtensionWithPageId(ext, pageId);
+ int launcherAction = isSwipingToLeft ? Launcher.SWIPE_LEFT : Launcher.SWIPE_RIGHT;
+ StatsLogCompat.write(launcherAction, srcState, srcState,
+ MessageNano.toByteArray(ext), true);
+ }
+
public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
@@ -88,6 +108,13 @@ public class StatsLogCompatManager extends StatsLogManager {
return true;
}
+ public static boolean fillInLauncherExtensionWithPageId(LauncherExtension ext, int pageId) {
+ Target target = new Target();
+ target.pageIndex = pageId;
+ copy(target, ext.srcTarget[0]);
+ return true;
+ }
+
private static void copy(Target src, LauncherTarget dst) {
// fill in
}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 3747f9a8b1..dc6b56eecf 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -156,12 +156,14 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
mDragHandleProgress = 1;
mMidAlpha = 0;
} else {
- mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
+ Context context = getContext();
+ mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
mMidProgress = OVERVIEW.getVerticalProgress(mLauncher);
Rect hotseatPadding = dp.getHotseatLayoutPadding();
int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
- hotseatPadding.bottom - hotseatPadding.top;
- float dragHandleTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
+ float dragHandleTop =
+ Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(context, dp));
mDragHandleProgress = 1 - (dragHandleTop / mShiftRange);
}
mTopOffset = dp.getInsets().top - mShelfOffset;
diff --git a/quickstep/tests/OWNERS b/quickstep/tests/OWNERS
index 046d871163..02e8ebcaba 100644
--- a/quickstep/tests/OWNERS
+++ b/quickstep/tests/OWNERS
@@ -1 +1,4 @@
vadimt@google.com
+sunnygoyal@google.com
+winsonc@google.com
+hyunyoungs@google.com
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index ec3d49afad..a7c33a9549 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -69,10 +69,9 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest {
private DigitalWellBeingToast getToast() {
executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW));
waitForState("Launcher internal state didn't switch to Overview", OVERVIEW);
- waitForLauncherCondition("No latest task", launcher -> getLatestTask(launcher) != null);
+ final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
return getFromLauncher(launcher -> {
- final TaskView task = getLatestTask(launcher);
assertTrue("Latest task is not Calculator",
CALCULATOR_PACKAGE.equals(task.getTask().getTopComponent().getPackageName()));
return task.getDigitalWellBeingToast();
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index f27f400884..e29552713d 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -34,9 +34,9 @@ import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.util.rule.FailureWatcher;
import com.android.systemui.shared.system.QuickStepContract;
-import org.junit.Assert;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -79,6 +79,14 @@ public class NavigationModeSwitchRule implements TestRule {
description.getAnnotation(NavigationModeSwitch.class) != null) {
Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
return new Statement() {
+ private void assertTrue(String message, boolean condition) {
+ if(!condition) {
+ final AssertionError assertionError = new AssertionError(message);
+ FailureWatcher.onError(mLauncher.getDevice(), description, assertionError);
+ throw assertionError;
+ }
+ }
+
@Override
public void evaluate() throws Throwable {
mLauncher.enableDebugTracing();
@@ -107,9 +115,9 @@ public class NavigationModeSwitchRule implements TestRule {
Log.e(TAG, "Exception", e);
throw e;
} finally {
- Assert.assertTrue(setActiveOverlay(prevOverlayPkg, originalMode));
+ assertTrue("Couldn't set overlay",
+ setActiveOverlay(prevOverlayPkg, originalMode));
}
- mLauncher.disableDebugTracing();
}
private void evaluateWithThreeButtons() throws Throwable {
@@ -176,7 +184,7 @@ public class NavigationModeSwitchRule implements TestRule {
latch.await(10, TimeUnit.SECONDS);
targetContext.getMainExecutor().execute(() ->
sysUINavigationMode.removeModeChangeListener(listener));
- Assert.assertTrue("Navigation mode didn't change to " + expectedMode,
+ assertTrue("Navigation mode didn't change to " + expectedMode,
currentSysUiNavigationMode() == expectedMode);
}
@@ -184,7 +192,7 @@ public class NavigationModeSwitchRule implements TestRule {
if (mLauncher.getNavigationModel() == expectedMode) break;
Thread.sleep(100);
}
- Assert.assertTrue("Couldn't switch to " + overlayPackage,
+ assertTrue("Couldn't switch to " + overlayPackage,
mLauncher.getNavigationModel() == expectedMode);
for (int i = 0; i != 100; ++i) {
@@ -192,7 +200,7 @@ public class NavigationModeSwitchRule implements TestRule {
Thread.sleep(100);
}
final String error = mLauncher.getNavigationModeMismatchError();
- Assert.assertTrue("Switching nav mode: " + error, error == null);
+ assertTrue("Switching nav mode: " + error, error == null);
Thread.sleep(5000);
return true;
diff --git a/res/values/config.xml b/res/values/config.xml
index 638a411be6..038718473b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -72,6 +72,7 @@
+
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
index 92bcc64348..a3d121676f 100644
--- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
@@ -1,6 +1,8 @@
package com.android.launcher3.config;
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+import com.android.launcher3.uioverrides.TogglableFlag;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -70,7 +72,7 @@ public final class FlagOverrideRule implements TestRule {
};
}
- private void override(BaseFlags.TogglableFlag flag, boolean newValue) {
+ private void override(BaseTogglableFlag flag, boolean newValue) {
if (!ruleInProgress) {
throw new IllegalStateException(
"Rule isn't in progress. Did you remember to mark it with @Rule?");
@@ -93,7 +95,7 @@ public final class FlagOverrideRule implements TestRule {
private void applyAnnotation(FlagOverride flagOverride) {
boolean found = false;
- for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+ for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
if (flag.getKey().equals(flagOverride.key())) {
override(flag, flagOverride.value());
found = true;
@@ -109,7 +111,7 @@ public final class FlagOverrideRule implements TestRule {
* Resets all flags to their default values.
*/
private void clearOverrides() {
- for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+ for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
flag.setForTests(flag.getDefaultValue());
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index e9324f9ce2..42a4f5cc99 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -29,7 +29,8 @@ public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestC
private PackageInstallStateChangedTask newTask(String pkg, int progress) {
int state = PackageInstallerCompat.STATUS_INSTALLING;
- PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress);
+ PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress,
+ android.os.Process.myUserHandle());
return new PackageInstallStateChangedTask(installInfo);
}
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 733f29540f..8b49c06389 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -89,7 +89,7 @@ public class AllAppsList {
public void addPromiseApp(Context context,
PackageInstallerCompat.PackageInstallInfo installInfo) {
ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context)
- .getApplicationInfo(installInfo.packageName, 0, Process.myUserHandle());
+ .getApplicationInfo(installInfo.packageName, 0, installInfo.user);
// only if not yet installed
if (applicationInfo == null) {
PromiseAppInfo info = new PromiseAppInfo(installInfo);
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index c84be4dacd..864fa6e8a6 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -22,6 +22,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.views.RecyclerViewFastScroller;
import androidx.recyclerview.widget.RecyclerView;
@@ -171,4 +172,13 @@ public abstract class BaseRecyclerView extends RecyclerView {
* Override in each subclass of this base class.
*/
public void onFastScrollCompleted() {}
+
+ @Override
+ public void onScrollStateChanged(int state) {
+ super.onScrollStateChanged(state);
+
+ if (state == SCROLL_STATE_IDLE) {
+ AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
+ }
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 22c69f59a5..b1132494a2 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -32,7 +32,6 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Property;
import android.util.TypedValue;
import android.view.KeyEvent;
@@ -54,8 +53,8 @@ import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconCache.IconLoadRequest;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.IconLabelDotView;
import java.text.NumberFormat;
@@ -64,7 +63,8 @@ import java.text.NumberFormat;
* because we want to make the bubble taller than the text and TextView's clip is
* too aggressive.
*/
-public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback {
+public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
+ IconLabelDotView {
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
@@ -413,7 +413,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
}
}
- public void forceHideDot(boolean forceHideDot) {
+ @Override
+ public void setForceHideDot(boolean forceHideDot) {
if (mForceHideDot == forceHideDot) {
return;
}
@@ -602,6 +603,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
}
}
+ @Override
public void setIconVisible(boolean visible) {
mIsIconVisible = visible;
Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 7ab88a0083..a90025e97f 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -142,8 +142,11 @@ public class FastBitmapDrawable extends Drawable {
@Override
public void setAlpha(int alpha) {
- mAlpha = alpha;
- mPaint.setAlpha(alpha);
+ if (mAlpha != alpha) {
+ mAlpha = alpha;
+ mPaint.setAlpha(alpha);
+ invalidateSelf();
+ }
}
@Override
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index e1ef9548cd..0f006f7c19 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -1,16 +1,17 @@
package com.android.launcher3;
-import android.content.Context;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
import android.content.pm.LauncherActivityInfo;
import android.graphics.drawable.Drawable;
+import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
public class IconProvider implements ResourceBasedOverride {
- public static IconProvider newInstance(Context context) {
- return Overrides.getObject(IconProvider.class, context, R.string.icon_provider_class);
- }
+ public static MainThreadInitializedObject INSTANCE =
+ forOverride(IconProvider.class, R.string.icon_provider_class);
public IconProvider() { }
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index e9b932aaf5..f19c602685 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -19,6 +19,7 @@ package com.android.launcher3;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -29,7 +30,6 @@ import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.os.Process;
@@ -141,7 +141,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
String pkg = getIntentPackage(info.launchIntent);
if (!TextUtils.isEmpty(pkg)
- && !launcherApps.isPackageEnabledForProfile(pkg, info.user)) {
+ && !launcherApps.isPackageEnabledForProfile(pkg, info.user)
+ && !info.isActivity) {
if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
+ info.launchIntent);
continue;
@@ -250,7 +251,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
public static WorkspaceItemInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
- return (WorkspaceItemInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
+ return (WorkspaceItemInfo)
+ new PendingInstallShortcutInfo(info, context).getItemInfo().first;
}
public static void queueShortcut(ShortcutInfo info, Context context) {
@@ -261,8 +263,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context);
}
- public static void queueActivityInfo(LauncherActivityInfo activity, Context context) {
- queuePendingShortcutInfo(new PendingInstallShortcutInfo(activity, context), context);
+ public static void queueApplication(Intent data, UserHandle user, Context context) {
+ queuePendingShortcutInfo(new PendingInstallShortcutInfo(data, context, user),
+ context);
}
public static HashSet getPendingShortcuts(Context context) {
@@ -326,7 +329,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
private static class PendingInstallShortcutInfo {
- final LauncherActivityInfo activityInfo;
+ final boolean isActivity;
final ShortcutInfo shortcutInfo;
final AppWidgetProviderInfo providerInfo;
@@ -340,7 +343,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
* Initializes a PendingInstallShortcutInfo received from a different app.
*/
public PendingInstallShortcutInfo(Intent data, UserHandle user, Context context) {
- activityInfo = null;
+ isActivity = false;
shortcutInfo = null;
providerInfo = null;
@@ -350,14 +353,13 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-
}
/**
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
*/
public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) {
- activityInfo = info;
+ isActivity = true;
shortcutInfo = null;
providerInfo = null;
@@ -369,11 +371,27 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
label = info.getLabel().toString();
}
+ /**
+ * Initializes a PendingInstallShortcutInfo to represent a launcher target.
+ */
+ public PendingInstallShortcutInfo(Intent data, Context context, UserHandle user) {
+ isActivity = true;
+ shortcutInfo = null;
+ providerInfo = null;
+
+ this.data = data;
+ this.user = user;
+ mContext = context;
+
+ launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+ label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
+ }
+
/**
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
*/
public PendingInstallShortcutInfo(ShortcutInfo info, Context context) {
- activityInfo = null;
+ isActivity = false;
shortcutInfo = info;
providerInfo = null;
@@ -390,7 +408,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
*/
public PendingInstallShortcutInfo(
AppWidgetProviderInfo info, int widgetId, Context context) {
- activityInfo = null;
+ isActivity = false;
shortcutInfo = null;
providerInfo = info;
@@ -405,17 +423,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
public String encodeToString() {
try {
- if (activityInfo != null) {
- // If it a launcher target, we only need component name, and user to
- // recreate this.
- return new JSONStringer()
- .object()
- .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
- .key(APP_SHORTCUT_TYPE_KEY).value(true)
- .key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext)
- .getSerialNumberForUser(user))
- .endObject().toString();
- } else if (shortcutInfo != null) {
+ if (shortcutInfo != null) {
// If it a launcher target, we only need component name, and user to
// recreate this.
return new JSONStringer()
@@ -457,7 +465,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
JSONStringer json = new JSONStringer()
.object()
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
- .key(NAME_KEY).value(name);
+ .key(NAME_KEY).value(name)
+ .key(APP_SHORTCUT_TYPE_KEY).value(isActivity);
if (icon != null) {
byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon);
json = json.key(ICON_KEY).value(
@@ -477,29 +486,18 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
public Pair getItemInfo() {
- if (activityInfo != null) {
- AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
- final LauncherAppState app = LauncherAppState.getInstance(mContext);
- // Set default values until proper values is loaded.
- appInfo.title = "";
- appInfo.applyFrom(app.getIconCache().getDefaultIcon(user));
- final WorkspaceItemInfo si = appInfo.makeWorkspaceItem();
- if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
- app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
- } else {
- app.getModel().updateAndBindWorkspaceItem(() -> {
- app.getIconCache().getTitleAndIcon(
- si, activityInfo, false /* useLowResIcon */);
- return si;
- });
- }
- return Pair.create((ItemInfo) si, (Object) activityInfo);
+ if (isActivity) {
+ WorkspaceItemInfo si = createWorkspaceItemInfo(data,
+ LauncherAppState.getInstance(mContext));
+ si.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+ return Pair.create(si, null);
} else if (shortcutInfo != null) {
- WorkspaceItemInfo si = new WorkspaceItemInfo(shortcutInfo, mContext);
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
LauncherIcons li = LauncherIcons.obtain(mContext);
- si.applyFrom(li.createShortcutIcon(shortcutInfo));
+ itemInfo.applyFrom(li.createShortcutIcon(shortcutInfo));
li.recycle();
- return Pair.create((ItemInfo) si, (Object) shortcutInfo);
+ return Pair.create(itemInfo, shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
.fromProviderInfo(mContext, providerInfo);
@@ -511,15 +509,16 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
widgetInfo.minSpanY = info.minSpanY;
widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
- return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo);
+ return Pair.create(widgetInfo, providerInfo);
} else {
- WorkspaceItemInfo si = createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
- return Pair.create((ItemInfo) si, null);
+ WorkspaceItemInfo itemInfo =
+ createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
+ return Pair.create(itemInfo, null);
}
}
public boolean isLauncherActivity() {
- return activityInfo != null;
+ return isActivity;
}
}
@@ -534,7 +533,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
.resolveActivity(decoder.launcherIntent, decoder.user);
- return info == null ? null : new PendingInstallShortcutInfo(info, context);
+ if (info != null) {
+ return new PendingInstallShortcutInfo(info, context);
+ }
} else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
DeepShortcutManager sm = DeepShortcutManager.getInstance(context);
List si = sm.queryForFullDetails(
@@ -578,7 +579,11 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
}
- return new PendingInstallShortcutInfo(data, decoder.user, context);
+ if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
+ return new PendingInstallShortcutInfo(data, context, decoder.user);
+ } else {
+ return new PendingInstallShortcutInfo(data, decoder.user, context);
+ }
} catch (JSONException | URISyntaxException e) {
Log.d(TAG, "Exception reading shortcut to add: " + e);
}
@@ -626,6 +631,11 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, LauncherAppState app) {
+ if (data == null) {
+ Log.e(TAG, "Can't construct WorkspaceItemInfo with null data");
+ return null;
+ }
+
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 134e116063..3f723d17f1 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -22,6 +22,8 @@ import android.content.Intent;
import android.os.Process;
import android.os.UserHandle;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.util.ContentWriter;
/**
@@ -134,6 +136,7 @@ public class ItemInfo {
return null;
}
+ @Nullable
public ComponentName getTargetComponent() {
Intent intent = getIntent();
if (intent != null) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5b38261a4e..257f0dfcf3 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -127,6 +127,7 @@ import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.RaceConditionTracker;
+import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
@@ -156,6 +157,7 @@ import java.util.List;
import java.util.function.Predicate;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
/**
* Default launcher application.
@@ -208,9 +210,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
// How long to wait before the new-shortcut animation automatically pans the workspace
- private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
+ @VisibleForTesting public static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
- @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
+ @Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500;
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
@@ -873,9 +875,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onStop();
}
-
- getUserEventDispatcher().logActionCommand(Action.Command.STOP,
- mStateManager.getState().containerType, -1);
+ logStopAndResume(Action.Command.STOP);
mAppWidgetHost.setListenIfResumed(false);
@@ -901,8 +901,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
private void handleDeferredResume() {
if (hasBeenResumed() && !mStateManager.getState().disableInteraction) {
- getUserEventDispatcher().logActionCommand(Action.Command.RESUME,
- mStateManager.getState().containerType, -1);
+ logStopAndResume(Action.Command.RESUME);
getUserEventDispatcher().startSession();
UiFactory.onLauncherStateOrResumeChanged(this);
@@ -929,6 +928,17 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
}
+ private void logStopAndResume(int command) {
+ int containerType = mStateManager.getState().containerType;
+ if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
+ getUserEventDispatcher().logActionCommand(command,
+ containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0);
+ } else {
+ getUserEventDispatcher().logActionCommand(command, containerType, -1);
+ }
+
+ }
+
protected void onStateSet(LauncherState state) {
getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
if (mDeferredResumePending) {
@@ -2494,7 +2504,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON));
}
if (currentFocus.getTag() instanceof ItemInfo
- && DeepShortcutManager.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
+ && ShortcutUtil.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
shortcutInfos.add(new KeyboardShortcutInfo(
getString(R.string.shortcuts_menu_with_notifications_description),
KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON));
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index d07638a5c1..b4a2216c57 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -19,14 +19,12 @@ package com.android.launcher3;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
-import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
-import android.os.Process;
import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -46,7 +44,7 @@ public class LauncherAppState {
// We do not need any synchronization for this variable as its only written on UI thread.
private static final MainThreadInitializedObject INSTANCE =
- new MainThreadInitializedObject<>((c) -> new LauncherAppState(c));
+ new MainThreadInitializedObject<>(LauncherAppState::new);
private final Context mContext;
private final LauncherModel mModel;
@@ -96,6 +94,7 @@ public class LauncherAppState {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
filter.addAction(ACTION_FORCE_ROLOAD);
}
+ FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d79f5d5a94..a0414894b1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
@@ -51,6 +52,7 @@ import com.android.launcher3.model.UserLockStateChangedTask;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
@@ -211,6 +213,30 @@ public class LauncherModel extends BroadcastReceiver
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
+ public void onSessionFailure(String packageName, UserHandle user) {
+ enqueueModelUpdateTask(new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ final IntSparseArrayMap removedIds = new IntSparseArrayMap<>();
+ synchronized (dataModel) {
+ for (ItemInfo info : dataModel.itemsIdMap) {
+ if (info instanceof WorkspaceItemInfo
+ && ((WorkspaceItemInfo) info).hasPromiseIconUi()
+ && user.equals(info.user)
+ && info.getIntent() != null
+ && TextUtils.equals(packageName, info.getIntent().getPackage())) {
+ removedIds.put(info.id, true /* remove */);
+ }
+ }
+ }
+
+ if (!removedIds.isEmpty()) {
+ deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
+ }
+ }
+ });
+ }
+
@Override
public void onPackageRemoved(String packageName, UserHandle user) {
onPackagesRemoved(user, packageName);
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index d66e5813b9..63914b0eb5 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -227,6 +227,11 @@ public class LauncherStateManager {
private void goToState(LauncherState state, boolean animated, long delay,
final Runnable onCompleteRunnable) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " +
+ state.getClass().getSimpleName() +
+ " @ " + Log.getStackTraceString(new Throwable()));
+ }
animated &= Utilities.areAnimationsEnabled(mLauncher);
if (mLauncher.isInState(state)) {
if (mConfig.mCurrentAnimation == null) {
@@ -407,6 +412,11 @@ public class LauncherStateManager {
mState.onStateDisabled(mLauncher);
}
mState = state;
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.STABLE_STATE_MISMATCH, "onStateTransitionStart: " +
+ state.getClass().getSimpleName() +
+ " @ " + Log.getStackTraceString(new Throwable()));
+ }
mState.onStateEnabled(mLauncher);
mLauncher.onStateSet(mState);
@@ -426,6 +436,11 @@ public class LauncherStateManager {
if (state != mCurrentStableState) {
mLastStableState = state.getHistoryForState(mCurrentStableState);
mCurrentStableState = state;
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " +
+ state.getClass().getSimpleName() +
+ " @ " + Log.getStackTraceString(new Throwable()));
+ }
}
state.onStateTransitionEnd(mLauncher);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index d2b8d4e300..79d6ca7934 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -890,23 +890,7 @@ public abstract class PagedView extends ViewGrou
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
- /*
- * If being flinged and user touches the screen, initiate drag;
- * otherwise don't. mScroller.isFinished should be false when
- * being flinged.
- */
- final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos());
- final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3);
-
- if (finishedScrolling) {
- mIsBeingDragged = false;
- if (!mScroller.isFinished() && !mFreeScroll) {
- setCurrentPage(getNextPage());
- pageEndTransition();
- }
- } else {
- mIsBeingDragged = true;
- }
+ updateIsBeingDraggedOnTouchDown();
break;
}
@@ -929,6 +913,25 @@ public abstract class PagedView extends ViewGrou
return mIsBeingDragged;
}
+ /**
+ * If being flinged and user touches the screen, initiate drag; otherwise don't.
+ */
+ private void updateIsBeingDraggedOnTouchDown() {
+ // mScroller.isFinished should be false when being flinged.
+ final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos());
+ final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3);
+
+ if (finishedScrolling) {
+ mIsBeingDragged = false;
+ if (!mScroller.isFinished() && !mFreeScroll) {
+ setCurrentPage(getNextPage());
+ pageEndTransition();
+ }
+ } else {
+ mIsBeingDragged = true;
+ }
+ }
+
public boolean isHandlingTouch() {
return mIsBeingDragged;
}
@@ -1085,7 +1088,9 @@ public abstract class PagedView extends ViewGrou
if (mFreeScroll) {
setCurrentPage(getNextPage());
} else if (wasFreeScroll) {
- snapToPage(getNextPage());
+ if (getScrollForPage(getNextPage()) != getScrollX()) {
+ snapToPage(getNextPage());
+ }
}
}
@@ -1104,6 +1109,8 @@ public abstract class PagedView extends ViewGrou
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
+ updateIsBeingDraggedOnTouchDown();
+
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
@@ -1562,12 +1569,20 @@ public abstract class PagedView extends ViewGrou
final boolean pagesFlipped = isPageOrderFlipped();
info.setScrollable(getPageCount() > 1);
if (getCurrentPage() < getPageCount() - 1) {
- info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
- : AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ info.addAction(pagesFlipped ?
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD
+ : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+ info.addAction(mIsRtl ?
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT
+ : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);
}
if (getCurrentPage() > 0) {
- info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
- : AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ info.addAction(pagesFlipped ?
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD
+ : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+ info.addAction(mIsRtl ?
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT
+ : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);
}
// Accessibility-wise, PagedView doesn't support long click, so disabling it.
@@ -1607,8 +1622,21 @@ public abstract class PagedView extends ViewGrou
if (pagesFlipped ? scrollRight() : scrollLeft()) {
return true;
}
+ } break;
+ case android.R.id.accessibilityActionPageRight: {
+ if (!mIsRtl) {
+ return scrollRight();
+ } else {
+ return scrollLeft();
+ }
+ }
+ case android.R.id.accessibilityActionPageLeft: {
+ if (!mIsRtl) {
+ return scrollLeft();
+ } else {
+ return scrollRight();
+ }
}
- break;
}
return false;
}
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index b0da6b9cd9..b4078ee0ea 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -18,28 +18,33 @@ package com.android.launcher3;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
-import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
import java.util.List;
+import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle;
+
/**
* BroadcastReceiver to handle session commit intent.
*/
@@ -66,15 +71,29 @@ public class SessionCommitReceiver extends BroadcastReceiver {
SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ PackageInstallerCompat packageInstallerCompat = PackageInstallerCompat.getInstance(context);
- if (TextUtils.isEmpty(info.getAppPackageName()) ||
- info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
+ if (TextUtils.isEmpty(info.getAppPackageName())
+ || info.getInstallReason() != PackageManager.INSTALL_REASON_USER
+ || packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) {
+ packageInstallerCompat.removePromiseIconId(info.getSessionId());
return;
}
queueAppIconAddition(context, info.getAppPackageName(), user);
}
+ public static void queuePromiseAppIconAddition(Context context, SessionInfo sessionInfo) {
+ String packageName = sessionInfo.getAppPackageName();
+ List activities = LauncherAppsCompat.getInstance(context)
+ .getActivityList(packageName, getUserHandle(sessionInfo));
+ if (activities == null || activities.isEmpty()) {
+ // Ensure application isn't already installed.
+ queueAppIconAddition(context, packageName, sessionInfo.getAppLabel(),
+ sessionInfo.getAppIcon(), getUserHandle(sessionInfo));
+ }
+ }
+
public static void queueAppIconAddition(Context context, String packageName, UserHandle user) {
List activities = LauncherAppsCompat.getInstance(context)
.getActivityList(packageName, user);
@@ -82,7 +101,18 @@ public class SessionCommitReceiver extends BroadcastReceiver {
// no activity found
return;
}
- InstallShortcutReceiver.queueActivityInfo(activities.get(0), context);
+ queueAppIconAddition(context, packageName, activities.get(0).getLabel(), null, user);
+ }
+
+ private static void queueAppIconAddition(Context context, String packageName,
+ CharSequence label, Bitmap icon, UserHandle user) {
+ Intent data = new Intent();
+ data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent(
+ new ComponentName(packageName, "")).setPackage(packageName));
+ data.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
+ data.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
+
+ InstallShortcutReceiver.queueApplication(data, user, context);
}
public static boolean isEnabled(Context context) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index fc5cd8a888..ba122f9441 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -21,6 +21,7 @@ import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
+import android.app.Person;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -109,6 +110,9 @@ public final class Utilities {
private static final Matrix sMatrix = new Matrix();
private static final Matrix sInverseMatrix = new Matrix();
+ public static final String[] EMPTY_STRING_ARRAY = new String[0];
+ public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
+
public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
public static final boolean ATLEAST_P =
@@ -728,7 +732,7 @@ public final class Utilities {
int[] array = new int[tokenizer.countTokens()];
int count = 0;
while (tokenizer.hasMoreTokens()) {
- array[count] = Integer.parseInt(tokenizer.nextToken());
+ array[count] = Integer.parseInt(tokenizer.nextToken().trim());
count++;
}
return array;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 269a591115..6612662ea5 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -85,6 +85,7 @@ import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.WorkspaceTouchListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -420,6 +421,9 @@ public class Workspace extends PagedView
}
// Always enter the spring loaded mode
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Switching to SPRING_LOADED");
+ }
mLauncher.getStateManager().goToState(SPRING_LOADED);
}
@@ -1048,6 +1052,7 @@ public class Workspace extends PagedView
if (!mOverlayShown) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
+ mLauncher.getStatsLogManager().logSwipeOnContainer(true, 0);
}
mOverlayShown = true;
// Not announcing the overlay page for accessibility since it announces itself.
@@ -1057,6 +1062,7 @@ public class Workspace extends PagedView
if (!ued.isPreviousHomeGesture()) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+ mLauncher.getStatsLogManager().logSwipeOnContainer(false, -1);
}
} else if (Float.compare(mOverlayTranslation, 0f) != 0) {
// When arriving to 0 overscroll from non-zero overscroll, announce page for
@@ -1741,6 +1747,9 @@ public class Workspace extends PagedView
public void prepareAccessibilityDrop() { }
public void onDrop(final DragObject d, DragOptions options) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Workspace.onDrop");
+ }
mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
CellLayout dropTargetLayout = mDropToLayout;
@@ -2418,6 +2427,9 @@ public class Workspace extends PagedView
* to add an item to one of the workspace screens.
*/
private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Workspace.onDropExternal");
+ }
if (d.dragInfo instanceof PendingAddShortcutInfo) {
WorkspaceItemInfo si = ((PendingAddShortcutInfo) d.dragInfo)
.activityInfo.createWorkspaceItemInfo();
@@ -3251,6 +3263,10 @@ public class Workspace extends PagedView
}
}
+ public boolean isOverlayShown() {
+ return mOverlayShown;
+ }
+
void moveToDefaultScreen() {
int page = DEFAULT_PAGE;
if (!workspaceInModalState() && getNextPage() != page) {
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index 5a2373b996..050a8bef75 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -16,17 +16,23 @@
package com.android.launcher3;
+import android.app.Person;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.ContentWriter;
+import java.util.Arrays;
+
/**
* Represents a launchable icon on the workspaces and in folders.
*/
@@ -44,24 +50,26 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
* The icon was added as an auto-install app, and is not ready to be used. This flag can't
* be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
* parsing.
+ *
+ * OR this icon was added due to it being an active install session created by the user.
*/
- public static final int FLAG_AUTOINSTALL_ICON = 2; //0B10;
+ public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
/**
* The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
* is set, then the icon is either being installed or is in a broken state.
*/
- public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100;
+ public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2;
/**
* Indicates that the widget restore has started.
*/
- public static final int FLAG_RESTORE_STARTED = 8; //0B1000;
+ public static final int FLAG_RESTORE_STARTED = 1 << 3;
/**
* Web UI supported.
*/
- public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
+ public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4;
/**
* The intent used to start the application.
@@ -82,11 +90,18 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
public int status;
+ /**
+ * A set of person's Id associated with the WorkspaceItemInfo, this is only used if the item
+ * represents a deep shortcut.
+ */
+ @NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
+
/**
* The installation progress [0-100] of the package that this shortcut represents.
*/
private int mInstallProgress;
+
public WorkspaceItemInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
}
@@ -98,6 +113,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
iconResource = info.iconResource;
status = info.status;
mInstallProgress = info.mInstallProgress;
+ personKeys = info.personKeys.clone();
}
/** TODO: Remove this. It's only called by ApplicationInfo.makeWorkspaceItem. */
@@ -175,6 +191,10 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
}
disabledMessage = shortcutInfo.getDisabledMessage();
+
+ Person[] persons = UiFactory.getPersons(shortcutInfo);
+ personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
+ : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
}
/** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
@@ -183,11 +203,16 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
}
+ @NonNull
+ public String[] getPersonKeys() {
+ return personKeys;
+ }
+
@Override
public ComponentName getTargetComponent() {
ComponentName cn = super.getTargetComponent();
if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT
- || hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
+ || hasStatusFlag(FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON))) {
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
// a packageName. In that case create a dummy componentName instead of adding additional
// check everywhere.
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index fd4df5247e..0c12c60a08 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -1,5 +1,7 @@
package com.android.launcher3.accessibility;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
+
import static com.android.launcher3.LauncherState.NORMAL;
import android.app.AlertDialog;
@@ -30,16 +32,17 @@ import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Workspace;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
+import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -115,7 +118,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
// If the request came from keyboard, do not add custom shortcuts as that is already
// exposed as a direct shortcut
- if (!fromKeyboard && DeepShortcutManager.supportsShortcuts(item)) {
+ if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) {
info.addAction(mActions.get(NotificationListener.getInstanceIfConnected() != null
? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
}
@@ -163,6 +166,13 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
}
public boolean performAction(final View host, final ItemInfo item, int action) {
+ if (action == ACTION_LONG_CLICK && ShortcutUtil.isDeepShortcut(item)) {
+ CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host);
+ if (popup.canShow()) {
+ popup.show();
+ return true;
+ }
+ }
if (action == MOVE) {
beginAccessibleDrag(host, item);
} else if (action == ADD_TO_WORKSPACE) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 4a2109e588..293b86722d 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -628,20 +628,4 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
return super.performAccessibilityAction(action, arguments);
}
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- final boolean result = super.dispatchTouchEvent(ev);
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- if (result) mAllAppsStore.enableDeferUpdates(
- AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mAllAppsStore.disableDeferUpdates(AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
- break;
- }
- return result;
- }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index a0e9dc5d8e..f82e380f88 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -26,13 +26,15 @@ import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -41,8 +43,6 @@ import com.android.launcher3.views.RecyclerViewFastScroller;
import java.util.List;
-import androidx.recyclerview.widget.RecyclerView;
-
/**
* A RecyclerView with custom fast scroll support for the all apps view.
*/
@@ -114,6 +114,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
if (mScrollbar != null) {
mScrollbar.reattachThumbToScroll();
}
+ if (getLayoutManager() instanceof AppsGridLayoutManager) {
+ AppsGridLayoutManager layoutManager = (AppsGridLayoutManager) getLayoutManager();
+ if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
+ // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
+ return;
+ }
+ }
scrollToPosition(0);
}
@@ -420,13 +427,4 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
public boolean hasOverlappingRendering() {
return false;
}
-
- @Override
- public void onScrollStateChanged(int state) {
- super.onScrollStateChanged(state);
-
- if (state == SCROLL_STATE_IDLE) {
- AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
- }
- }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 267363fa7d..ca8dbebfc2 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -39,10 +39,8 @@ public class AllAppsStore {
// Defer updates flag used to defer all apps updates to the next draw.
public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0;
- // Defer updates flag used to defer all apps updates while the user interacts with all apps.
- public static final int DEFER_UPDATES_USER_INTERACTION = 1 << 1;
// Defer updates flag used to defer all apps updates by a test's request.
- public static final int DEFER_UPDATES_TEST = 1 << 2;
+ public static final int DEFER_UPDATES_TEST = 1 << 1;
private PackageUserKey mTempKey = new PackageUserKey(null, null);
private final HashMap mComponentToAppMap = new HashMap<>();
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 5b3beeca7f..3836c9fdb4 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -18,7 +18,6 @@ import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.util.FloatProperty;
-import android.util.Log;
import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
@@ -32,7 +31,6 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringObjectAnimator;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
new file mode 100644
index 0000000000..0f34c1e97e
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.anim;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.util.FloatProperty;
+
+import com.android.launcher3.util.DefaultDisplay;
+
+import androidx.annotation.FloatRange;
+import androidx.dynamicanimation.animation.SpringForce;
+
+/**
+ * Utility class to build an object animator which follows the same path as a spring animation for
+ * an underdamped spring.
+ */
+public class SpringAnimationBuilder extends FloatProperty {
+
+ private final T mTarget;
+ private final FloatProperty mProperty;
+
+ private float mStartValue;
+ private float mEndValue;
+ private float mVelocity = 0;
+
+ private float mStiffness = SpringForce.STIFFNESS_MEDIUM;
+ private float mDampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+ private float mMinVisibleChange = 1;
+
+ // Multiplier to the min visible change value for value threshold
+ private static final float THRESHOLD_MULTIPLIER = 0.65f;
+
+ /**
+ * The spring equation is given as
+ * x = e^(-beta*t/2) * (a cos(gamma * t) + b sin(gamma * t)
+ * v = e^(-beta*t/2) * ((2 * a * gamma + beta * b) * sin(gamma * t)
+ * + (a * beta - 2 * b * gamma) * cos(gamma * t)) / 2
+ *
+ * a = x(0)
+ * b = beta * x(0) / (2 * gamma) + v(0) / gamma
+ */
+ private double beta;
+ private double gamma;
+
+ private double a, b;
+ private double va, vb;
+
+ // Threshold for velocity and value to determine when it's reasonable to assume that the spring
+ // is approximately at rest.
+ private double mValueThreshold;
+ private double mVelocityThreshold;
+
+ private float mCurrentTime = 0;
+
+ public SpringAnimationBuilder(T target, FloatProperty property) {
+ super("dynamic-spring-property");
+ mTarget = target;
+ mProperty = property;
+
+ mStartValue = mProperty.get(target);
+ }
+
+ public SpringAnimationBuilder setEndValue(float value) {
+ mEndValue = value;
+ return this;
+ }
+
+ public SpringAnimationBuilder setStartValue(float value) {
+ mStartValue = value;
+ return this;
+ }
+
+ public SpringAnimationBuilder setValues(float... values) {
+ if (values.length > 1) {
+ mStartValue = values[0];
+ mEndValue = values[values.length - 1];
+ } else {
+ mEndValue = values[0];
+ }
+ return this;
+ }
+
+ public SpringAnimationBuilder setStiffness(
+ @FloatRange(from = 0.0, fromInclusive = false) float stiffness) {
+ if (stiffness <= 0) {
+ throw new IllegalArgumentException("Spring stiffness constant must be positive.");
+ }
+ mStiffness = stiffness;
+ return this;
+ }
+
+ public SpringAnimationBuilder setDampingRatio(
+ @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
+ float dampingRatio) {
+ if (dampingRatio <= 0 || dampingRatio >= 1) {
+ throw new IllegalArgumentException("Damping ratio must be between 0 and 1");
+ }
+ mDampingRatio = dampingRatio;
+ return this;
+ }
+
+ public SpringAnimationBuilder setMinimumVisibleChange(
+ @FloatRange(from = 0.0, fromInclusive = false) float minimumVisibleChange) {
+ if (minimumVisibleChange <= 0) {
+ throw new IllegalArgumentException("Minimum visible change must be positive.");
+ }
+ mMinVisibleChange = minimumVisibleChange;
+ return this;
+ }
+
+ public SpringAnimationBuilder setStartVelocity(float startVelocity) {
+ mVelocity = startVelocity;
+ return this;
+ }
+
+ @Override
+ public void setValue(T object, float time) {
+ mCurrentTime = time;
+ mProperty.setValue(
+ object, (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue);
+ }
+
+ @Override
+ public Float get(T t) {
+ return mCurrentTime;
+ }
+
+ public ObjectAnimator build(Context context) {
+ int singleFrameMs = DefaultDisplay.getSingleFrameMs(context);
+ double naturalFreq = Math.sqrt(mStiffness);
+ double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
+
+ // All the calculations assume the stable position to be 0, shift the values accordingly.
+ beta = 2 * mDampingRatio * naturalFreq;
+ gamma = dampedFreq;
+ a = mStartValue - mEndValue;
+ b = beta * a / (2 * gamma) + mVelocity / gamma;
+
+ va = a * beta / 2 - b * gamma;
+ vb = a * gamma + beta * b / 2;
+
+ mValueThreshold = mMinVisibleChange * THRESHOLD_MULTIPLIER;
+
+ // This multiplier is used to calculate the velocity threshold given a certain value
+ // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount,
+ // then the velocity is a reasonable threshold.
+ mVelocityThreshold = mValueThreshold * 1000.0 / singleFrameMs;
+
+ // Find the duration (in seconds) for the spring to reach equilibrium.
+ // equilibrium is reached when x = 0
+ double duration = Math.atan2(-a, b) / gamma;
+
+ // Keep moving ahead until the velocity reaches equilibrium.
+ double piByG = Math.PI / gamma;
+ while (duration < 0 || Math.abs(exponentialComponent(duration) * cosSinV(duration))
+ >= mVelocityThreshold) {
+ duration += piByG;
+ }
+
+ // Find the shortest time
+ double edgeTime = Math.max(0, duration - piByG / 2);
+ double minDiff = singleFrameMs / 2000.0; // Half frame time in seconds
+
+ do {
+ if ((duration - edgeTime) < minDiff) {
+ break;
+ }
+ double mid = (edgeTime + duration) / 2;
+ if (isAtEquilibrium(mid)) {
+ duration = mid;
+ } else {
+ edgeTime = mid;
+ }
+ } while (true);
+
+
+ long durationMs = (long) (1000.0 * duration);
+ ObjectAnimator animator = ObjectAnimator.ofFloat(mTarget, this, 0, (float) duration);
+ animator.setDuration(durationMs).setInterpolator(Interpolators.LINEAR);
+ animator.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ mProperty.setValue(mTarget, mEndValue);
+ }
+ });
+ return animator;
+ }
+
+ private boolean isAtEquilibrium(double t) {
+ double ec = exponentialComponent(t);
+
+ if (Math.abs(ec * cosSinX(t)) >= mValueThreshold) {
+ return false;
+ }
+ return Math.abs(ec * cosSinV(t)) < mVelocityThreshold;
+ }
+
+ private double exponentialComponent(double t) {
+ return Math.pow(Math.E, - beta * t / 2);
+ }
+
+ private double cosSinX(double t) {
+ return cosSin(t, a, b);
+ }
+
+ private double cosSinV(double t) {
+ return cosSin(t, va, vb);
+ }
+
+ private double cosSin(double t, double cosFactor, double sinFactor) {
+ double angle = t * gamma;
+ return cosFactor * Math.cos(angle) + sinFactor * Math.sin(angle);
+ }
+}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 1d19b533a0..1885d8f03d 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -31,11 +31,14 @@ import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.Log;
import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import androidx.annotation.NonNull;
@@ -167,6 +170,10 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat {
@Override
public void onPackagesSuspended(String[] packageNames, UserHandle user) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.APP_NOT_DISABLED, "onPackagesSuspended: " +
+ Arrays.toString(packageNames));
+ }
mCallback.onPackagesSuspended(packageNames, user);
}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 4f4d64161a..55df98b48f 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -19,15 +19,25 @@ package com.android.launcher3.compat;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.os.Process;
import android.os.UserHandle;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import androidx.annotation.NonNull;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.PackageUserKey;
+
public abstract class PackageInstallerCompat {
+ // Set of session ids of promise icons that have been added to the home screen
+ // as FLAG_PROMISE_NEW_INSTALLS.
+ protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
+
public static final int STATUS_INSTALLED = 0;
public static final int STATUS_INSTALLING = 1;
public static final int STATUS_FAILED = 2;
@@ -44,15 +54,19 @@ public abstract class PackageInstallerCompat {
}
}
+ public static UserHandle getUserHandle(SessionInfo info) {
+ return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
+ }
+
/**
* @return a map of active installs to their progress
*/
- public abstract HashMap updateAndGetActiveSessionCache();
+ public abstract HashMap updateAndGetActiveSessionCache();
/**
* @return an active SessionInfo for {@param pkg} or null if none exists.
*/
- public abstract PackageInstaller.SessionInfo getActiveSessionInfo(UserHandle user, String pkg);
+ public abstract SessionInfo getActiveSessionInfo(UserHandle user, String pkg);
public abstract void onStop();
@@ -61,30 +75,44 @@ public abstract class PackageInstallerCompat {
public final String packageName;
public final int state;
public final int progress;
+ public final UserHandle user;
- private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) {
+ private PackageInstallInfo(@NonNull SessionInfo info) {
this.state = STATUS_INSTALLING;
this.packageName = info.getAppPackageName();
this.componentName = new ComponentName(packageName, "");
this.progress = (int) (info.getProgress() * 100f);
+ this.user = getUserHandle(info);
}
- public PackageInstallInfo(String packageName, int state, int progress) {
+ public PackageInstallInfo(String packageName, int state, int progress, UserHandle user) {
this.state = state;
this.packageName = packageName;
this.componentName = new ComponentName(packageName, "");
this.progress = progress;
+ this.user = user;
}
- public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) {
+ public static PackageInstallInfo fromInstallingState(SessionInfo info) {
return new PackageInstallInfo(info);
}
- public static PackageInstallInfo fromState(int state, String packageName) {
- return new PackageInstallInfo(packageName, state, 0 /* progress */);
+ public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
+ return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
}
}
- public abstract List getAllVerifiedSessions();
+ public abstract List getAllVerifiedSessions();
+
+ /**
+ * Returns true if a promise icon was already added to the home screen for {@param sessionId}.
+ * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
+ */
+ public abstract boolean promiseIconAddedForId(int sessionId);
+
+ /**
+ * Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
+ */
+ public abstract void removePromiseIconId(int sessionId);
}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 8a5eabca99..879d963c7c 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -21,17 +21,21 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionCallback;
import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
import android.os.Handler;
-import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.SparseArray;
+import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -39,11 +43,13 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import static com.android.launcher3.Utilities.getPrefs;
+
public class PackageInstallerCompatVL extends PackageInstallerCompat {
private static final boolean DEBUG = false;
- @Thunk final SparseArray mActiveSessions = new SparseArray<>();
+ @Thunk final SparseArray mActiveSessions = new SparseArray<>();
@Thunk final PackageInstaller mInstaller;
private final IconCache mCache;
@@ -51,6 +57,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
private final Context mAppContext;
private final HashMap mSessionVerifiedMap = new HashMap<>();
private final LauncherAppsCompat mLauncherApps;
+ private final IntSet mPromiseIconIds;
PackageInstallerCompatVL(Context context) {
mAppContext = context.getApplicationContext();
@@ -59,17 +66,39 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
mWorker = new Handler(LauncherModel.getWorkerLooper());
mInstaller.registerSessionCallback(mCallback, mWorker);
mLauncherApps = LauncherAppsCompat.getInstance(context);
+ mPromiseIconIds = IntSet.wrap(IntArray.wrap(Utilities.getIntArrayFromString(
+ getPrefs(context).getString(PROMISE_ICON_IDS, ""))));
+
+ cleanUpPromiseIconIds();
+ }
+
+ private void cleanUpPromiseIconIds() {
+ IntArray existingIds = new IntArray();
+ for (SessionInfo info : updateAndGetActiveSessionCache().values()) {
+ existingIds.add(info.getSessionId());
+ }
+ IntArray idsToRemove = new IntArray();
+
+ for (int i = mPromiseIconIds.size() - 1; i >= 0; --i) {
+ if (!existingIds.contains(mPromiseIconIds.getArray().get(i))) {
+ idsToRemove.add(mPromiseIconIds.getArray().get(i));
+ }
+ }
+ for (int i = idsToRemove.size() - 1; i >= 0; --i) {
+ mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
+ }
}
@Override
- public HashMap updateAndGetActiveSessionCache() {
- HashMap activePackages = new HashMap<>();
- UserHandle primaryUser = Process.myUserHandle();
+ public HashMap updateAndGetActiveSessionCache() {
+ HashMap activePackages = new HashMap<>();
for (SessionInfo info : getAllVerifiedSessions()) {
- addSessionInfoToCache(info, Utilities.ATLEAST_Q ? info.getUser() : primaryUser);
+ addSessionInfoToCache(info, getUserHandle(info));
if (info.getAppPackageName() != null) {
- activePackages.put(info.getAppPackageName(), info);
- mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
+ activePackages.put(new PackageUserKey(info.getAppPackageName(),
+ getUserHandle(info)), info);
+ mActiveSessions.put(info.getSessionId(),
+ new PackageUserKey(info.getAppPackageName(), getUserHandle(info)));
}
}
return activePackages;
@@ -78,7 +107,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
for (SessionInfo info : getAllVerifiedSessions()) {
boolean match = pkg.equals(info.getAppPackageName());
- if (Utilities.ATLEAST_Q && !user.equals(info.getUser())) {
+ if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) {
match = false;
}
if (match) {
@@ -108,6 +137,30 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
}
}
+ /**
+ * Add a promise app icon to the workspace iff:
+ * - The settings for it are enabled
+ * - The user installed the app
+ * - There is an app icon and label (For apps with no launching activity, no icon is provided).
+ * - The app is not already installed
+ * - A promise icon for the session has not already been created
+ */
+ private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) {
+ if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+ && SessionCommitReceiver.isEnabled(mAppContext)
+ && verify(sessionInfo) != null
+ && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
+ && sessionInfo.getAppIcon() != null
+ && !TextUtils.isEmpty(sessionInfo.getAppLabel())
+ && !mPromiseIconIds.contains(sessionInfo.getSessionId())
+ && mLauncherApps.getApplicationInfo(sessionInfo.getAppPackageName(), 0,
+ getUserHandle(sessionInfo)) == null) {
+ SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
+ mPromiseIconIds.add(sessionInfo.getSessionId());
+ updatePromiseIconPrefs();
+ }
+ }
+
private final SessionCallback mCallback = new SessionCallback() {
@Override
@@ -120,19 +173,31 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
PackageInstallInfo.fromInstallingState(sessionInfo));
}
}
+
+ tryQueuePromiseAppIcon(sessionInfo);
}
@Override
public void onFinished(int sessionId, boolean success) {
// For a finished session, we can't get the session info. So use the
// packageName from our local cache.
- String packageName = mActiveSessions.get(sessionId);
+ PackageUserKey key = mActiveSessions.get(sessionId);
mActiveSessions.remove(sessionId);
- if (packageName != null) {
- sendUpdate(PackageInstallInfo.fromState(
- success ? STATUS_INSTALLED : STATUS_FAILED,
- packageName));
+ if (key != null && key.mPackageName != null) {
+ String packageName = key.mPackageName;
+ sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
+ packageName, key.mUser));
+
+ if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+ && mPromiseIconIds.contains(sessionId)) {
+ LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
+ if (appState != null) {
+ appState.getModel().onSessionFailure(packageName, key.mUser);
+ }
+ // If it is successful, the id is removed in the the package added flow.
+ removePromiseIconId(sessionId);
+ }
}
}
@@ -149,14 +214,18 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
@Override
public void onBadgingChanged(int sessionId) {
- pushSessionDisplayToLauncher(sessionId);
+ SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+ if (sessionInfo != null) {
+ tryQueuePromiseAppIcon(sessionInfo);
+ }
}
private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
if (session != null && session.getAppPackageName() != null) {
- mActiveSessions.put(sessionId, session.getAppPackageName());
- addSessionInfoToCache(session, Process.myUserHandle());
+ mActiveSessions.put(session.getSessionId(),
+ new PackageUserKey(session.getAppPackageName(), getUserHandle(session)));
+ addSessionInfoToCache(session, getUserHandle(session));
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
if (app != null) {
app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
@@ -178,7 +247,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
if (!mSessionVerifiedMap.containsKey(pkg)) {
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
- ApplicationInfo.FLAG_SYSTEM, Process.myUserHandle()) != null;
+ ApplicationInfo.FLAG_SYSTEM, getUserHandle(sessionInfo)) != null;
mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
}
}
@@ -198,4 +267,23 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
}
return list;
}
+
+ @Override
+ public boolean promiseIconAddedForId(int sessionId) {
+ return mPromiseIconIds.contains(sessionId);
+ }
+
+ @Override
+ public void removePromiseIconId(int sessionId) {
+ if (mPromiseIconIds.contains(sessionId)) {
+ mPromiseIconIds.getArray().removeValue(sessionId);
+ updatePromiseIconPrefs();
+ }
+ }
+
+ private void updatePromiseIconPrefs() {
+ getPrefs(mAppContext).edit()
+ .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
+ .apply();
+ }
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 45639e0a43..025087b9d8 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -18,20 +18,16 @@ package com.android.launcher3.config;
import static androidx.core.util.Preconditions.checkNotNull;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
import androidx.annotation.GuardedBy;
import androidx.annotation.Keep;
-import androidx.annotation.VisibleForTesting;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Utilities;
+import com.android.launcher3.uioverrides.TogglableFlag;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
@@ -41,11 +37,9 @@ import java.util.TreeMap;
* Defines a set of flags used to control various launcher behaviors.
*
* All the flags should be defined here with appropriate default values.
- *
- *
This class is kept package-private to prevent direct access.
*/
@Keep
-abstract class BaseFlags {
+public abstract class BaseFlags {
private static final Object sLock = new Object();
@GuardedBy("sLock")
@@ -66,6 +60,11 @@ abstract class BaseFlags {
// When enabled the promise icon is visible in all apps while installation an app.
public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
+ // When enabled a promise icon is added to the home screen when install session is active.
+ public static final TogglableFlag PROMISE_APPS_NEW_INSTALLS =
+ new TogglableFlag("PROMISE_APPS_NEW_INSTALLS", true,
+ "Adds a promise icon to the home screen for new install sessions.");
+
// Enable moving the QSB on the 0th screen of the workspace
public static final boolean QSB_ON_FIRST_SCREEN = true;
@@ -105,18 +104,22 @@ abstract class BaseFlags {
"ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag(
- "ENABLE_HINTS_IN_OVERVIEW", false,
+ "ENABLE_HINTS_IN_OVERVIEW", true,
"Show chip hints and gleams on the overview screen");
public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
"FAKE_LANDSCAPE_UI", false,
"Rotate launcher UI instead of using transposed layout");
+ public static final TogglableFlag APP_SEARCH_IMPROVEMENTS = new TogglableFlag(
+ "APP_SEARCH_IMPROVEMENTS", false,
+ "Adds localized title and keyword search and ranking");
+
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
synchronized (sLock) {
- for (TogglableFlag flag : sFlags) {
+ for (BaseTogglableFlag flag : sFlags) {
flag.initialize(context);
}
}
@@ -132,27 +135,30 @@ abstract class BaseFlags {
SortedMap flagsByKey = new TreeMap<>();
synchronized (sLock) {
for (TogglableFlag flag : sFlags) {
- flagsByKey.put(flag.key, flag);
+ flagsByKey.put(((BaseTogglableFlag) flag).getKey(), flag);
}
}
return new ArrayList<>(flagsByKey.values());
}
- public static class TogglableFlag {
+ public static abstract class BaseTogglableFlag {
private final String key;
+ // should be value that is hardcoded in client side.
+ // Comparatively, getDefaultValue() can be overridden.
private final boolean defaultValue;
private final String description;
private boolean currentValue;
- TogglableFlag(
+ public BaseTogglableFlag(
String key,
boolean defaultValue,
String description) {
this.key = checkNotNull(key);
this.currentValue = this.defaultValue = defaultValue;
this.description = checkNotNull(description);
+
synchronized (sLock) {
- sFlags.add(this);
+ sFlags.add((TogglableFlag)this);
}
}
@@ -162,18 +168,22 @@ abstract class BaseFlags {
currentValue = value;
}
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public String getKey() {
return key;
}
- void initialize(Context context) {
- currentValue = getFromStorage(context, defaultValue);
+
+ protected void initialize(Context context) {
+ currentValue = getFromStorage(context, getDefaultValue());
}
+ protected abstract boolean getOverridenDefaultValue(boolean value);
+
+ protected abstract void addChangeListener(Context context, Runnable r);
+
public void updateStorage(Context context, boolean value) {
SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
Context.MODE_PRIVATE).edit();
- if (value == defaultValue) {
+ if (value == getDefaultValue()) {
editor.remove(key).apply();
} else {
editor.putBoolean(key, value).apply();
@@ -182,11 +192,11 @@ abstract class BaseFlags {
boolean getFromStorage(Context context, boolean defaultValue) {
return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
- .getBoolean(key, defaultValue);
+ .getBoolean(key, getDefaultValue());
}
boolean getDefaultValue() {
- return defaultValue;
+ return getOverridenDefaultValue(defaultValue);
}
/** Returns the value of the flag at process start, including any overrides present. */
@@ -203,6 +213,8 @@ abstract class BaseFlags {
return "TogglableFlag{"
+ "key=" + key + ", "
+ "defaultValue=" + defaultValue + ", "
+ + "overriddenDefaultValue=" + getOverridenDefaultValue(defaultValue) + ", "
+ + "currentValue=" + currentValue + ", "
+ "description=" + description
+ "}";
}
@@ -213,9 +225,9 @@ abstract class BaseFlags {
return true;
}
if (o instanceof TogglableFlag) {
- TogglableFlag that = (TogglableFlag) o;
+ BaseTogglableFlag that = (BaseTogglableFlag) o;
return (this.key.equals(that.getKey()))
- && (this.defaultValue == that.getDefaultValue())
+ && (this.getDefaultValue() == that.getDefaultValue())
&& (this.description.equals(that.getDescription()));
}
return false;
@@ -227,54 +239,10 @@ abstract class BaseFlags {
h$ *= 1000003;
h$ ^= key.hashCode();
h$ *= 1000003;
- h$ ^= defaultValue ? 1231 : 1237;
+ h$ ^= getDefaultValue() ? 1231 : 1237;
h$ *= 1000003;
h$ ^= description.hashCode();
return h$;
}
}
-
- /**
- * Stores the FeatureFlag's value in Settings.Global instead of our SharedPrefs.
- * This is useful if we want to be able to control this flag from another process.
- */
- public static final class ToggleableGlobalSettingsFlag extends TogglableFlag {
- private ContentResolver contentResolver;
-
- ToggleableGlobalSettingsFlag(String key, boolean defaultValue, String description) {
- super(key, defaultValue, description);
- }
-
- @Override
- public void initialize(Context context) {
- contentResolver = context.getContentResolver();
- contentResolver.registerContentObserver(Settings.Global.getUriFor(getKey()), true,
- new ContentObserver(new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange) {
- superInitialize(context);
- }});
- superInitialize(context);
- }
-
- private void superInitialize(Context context) {
- super.initialize(context);
- }
-
- @Override
- public void updateStorage(Context context, boolean value) {
- if (contentResolver == null) {
- return;
- }
- Settings.Global.putInt(contentResolver, getKey(), value ? 1 : 0);
- }
-
- @Override
- boolean getFromStorage(Context context, boolean defaultValue) {
- if (contentResolver == null) {
- return defaultValue;
- }
- return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
- }
- }
}
diff --git a/src/com/android/launcher3/config/FlagTogglerPrefUi.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
index 5ecb186500..54e5322bd5 100644
--- a/src/com/android/launcher3/config/FlagTogglerPrefUi.java
+++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
@@ -26,12 +26,13 @@ import android.view.MenuItem;
import android.widget.Toast;
import com.android.launcher3.R;
-import com.android.launcher3.config.BaseFlags.TogglableFlag;
import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceFragment;
import androidx.preference.PreferenceGroup;
import androidx.preference.SwitchPreference;
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+import com.android.launcher3.uioverrides.TogglableFlag;
/**
* Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
@@ -62,7 +63,7 @@ public final class FlagTogglerPrefUi {
@Override
public boolean getBoolean(String key, boolean defaultValue) {
- for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+ for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
if (flag.getKey().equals(key)) {
return flag.getFromStorage(mContext, defaultValue);
}
@@ -83,7 +84,7 @@ public final class FlagTogglerPrefUi {
// flag with a different value than the default. That way, when we flip flags in
// future, engineers will pick up the new value immediately. To accomplish this, we use a
// custom preference data store.
- for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+ for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
SwitchPreference switchPreference = new SwitchPreference(mContext);
switchPreference.setKey(flag.getKey());
switchPreference.setDefaultValue(flag.getDefaultValue());
@@ -99,7 +100,7 @@ public final class FlagTogglerPrefUi {
/**
* Updates the summary to show the description and whether the flag overrides the default value.
*/
- private void updateSummary(SwitchPreference switchPreference, TogglableFlag flag) {
+ private void updateSummary(SwitchPreference switchPreference, BaseTogglableFlag flag) {
String onWarning = flag.getDefaultValue() ? "" : "OVERRIDDEN
";
String offWarning = flag.getDefaultValue() ? "OVERRIDDEN
" : "";
switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.getDescription()));
@@ -134,7 +135,7 @@ public final class FlagTogglerPrefUi {
}
}
- private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) {
+ private boolean getFlagStateFromSharedPrefs(BaseTogglableFlag flag) {
return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue());
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index d32dd2eb98..b72fd988a3 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -579,6 +579,9 @@ public class DragController implements DragDriver.EventListener, TouchController
}
private void drop(DropTarget dropTarget, Runnable flingAnimation) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragController.drop");
+ }
final int[] coordinates = mCoordinatesTemp;
mDragObject.x = coordinates[0];
mDragObject.y = coordinates[1];
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index 84fc94dd25..01e0f923c1 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -17,10 +17,12 @@
package com.android.launcher3.dragndrop;
import android.content.Context;
+import android.util.Log;
import android.view.DragEvent;
import android.view.MotionEvent;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.testing.TestProtocol;
/**
* Base class for driving a drag/drop operation.
@@ -52,10 +54,16 @@ public abstract class DragDriver {
mEventListener.onDriverDragMove(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_UP:
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragDriver.ACTION_UP");
+ }
mEventListener.onDriverDragMove(ev.getX(), ev.getY());
mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_CANCEL:
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragDriver.ACTION_CANCEL");
+ }
mEventListener.onDriverDragCancel();
break;
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2ef6d707e3..f22b533380 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -516,7 +516,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- mFolderIcon.setBackgroundVisible(false);
+ mFolderIcon.setIconVisible(false);
mFolderIcon.drawLeaveBehindIfExists();
}
@Override
@@ -646,7 +646,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
clearFocus();
if (mFolderIcon != null) {
mFolderIcon.setVisibility(View.VISIBLE);
- mFolderIcon.setBackgroundVisible(true);
+ mFolderIcon.setIconVisible(true);
mFolderIcon.mFolderName.setTextVisibility(true);
if (wasAnimated) {
mFolderIcon.animateBgShadowAndStroke();
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 250169cdb2..0e2d4673e4 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -65,6 +65,7 @@ import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.IconLabelDotView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
@@ -73,7 +74,7 @@ import java.util.List;
/**
* An icon that can appear on in the workspace representing an {@link Folder}.
*/
-public class FolderIcon extends FrameLayout implements FolderListener {
+public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView {
@Thunk Launcher mLauncher;
@Thunk Folder mFolder;
@@ -107,6 +108,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
private Alarm mOpenAlarm = new Alarm();
+ private boolean mForceHideDot;
@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
private FolderDotInfo mDotInfo = new FolderDotInfo();
private DotRenderer mDotRenderer;
@@ -409,6 +411,20 @@ public class FolderIcon extends FrameLayout implements FolderListener {
return mPreviewLayoutRule;
}
+ @Override
+ public void setForceHideDot(boolean forceHideDot) {
+ if (mForceHideDot == forceHideDot) {
+ return;
+ }
+ mForceHideDot = forceHideDot;
+
+ if (forceHideDot) {
+ invalidate();
+ } else if (hasDot()) {
+ animateDotScale(0, 1);
+ }
+ }
+
/**
* Sets mDotScale to 1 or 0, animating if wasDotted or isDotted is false
* (the dot is being added or removed).
@@ -468,7 +484,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
mBackground.setInvalidateDelegate(this);
}
- public void setBackgroundVisible(boolean visible) {
+ @Override
+ public void setIconVisible(boolean visible) {
mBackgroundIsVisible = visible;
invalidate();
}
@@ -509,7 +526,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
public void drawDot(Canvas canvas) {
- if ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0) {
+ if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
Rect iconBounds = mDotParams.iconBounds;
BubbleTextView.getIconBounds(this, iconBounds,
mLauncher.getWallpaperDeviceProfile().iconSizePx);
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index c9566cb145..288749fa7e 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -17,6 +17,7 @@
package com.android.launcher3.graphics;
import static com.android.launcher3.graphics.IconShape.getShapePath;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -31,6 +32,8 @@ import android.os.Process;
import android.os.UserHandle;
import android.util.ArrayMap;
+import androidx.annotation.UiThread;
+
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
@@ -38,16 +41,13 @@ import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
-import androidx.annotation.UiThread;
-
/**
* Factory for creating new drawables.
*/
public class DrawableFactory implements ResourceBasedOverride {
public static final MainThreadInitializedObject INSTANCE =
- new MainThreadInitializedObject<>(c -> Overrides.getObject(DrawableFactory.class,
- c.getApplicationContext(), R.string.drawable_factory_class));
+ forOverride(DrawableFactory.class, R.string.drawable_factory_class);
protected final UserHandle mMyUser = Process.myUserHandle();
protected final ArrayMap mUserBadges = new ArrayMap<>();
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 648445e40c..abff237e8b 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -29,6 +29,8 @@ import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.AppInfo;
import com.android.launcher3.IconProvider;
import com.android.launcher3.InvariantDeviceProfile;
@@ -36,10 +38,11 @@ import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.cache.BaseIconCache;
import com.android.launcher3.icons.cache.CachingLogic;
@@ -50,8 +53,6 @@ import com.android.launcher3.util.Preconditions;
import java.util.function.Supplier;
-import androidx.annotation.NonNull;
-
/**
* Cache of application icons. Icons can be made from any thread.
*/
@@ -75,11 +76,11 @@ public class IconCache extends BaseIconCache {
super(context, LauncherFiles.APP_ICONS_DB, LauncherModel.getWorkerLooper(),
inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */);
mComponentWithLabelCachingLogic = new ComponentCachingLogic(context);
- mLauncherActivityInfoCachingLogic = new LauncherActivtiyCachingLogic(this);
+ mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context);
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
mUserManager = UserManagerCompat.getInstance(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
- mIconProvider = IconProvider.newInstance(context);
+ mIconProvider = IconProvider.INSTANCE.get(context);
}
@Override
@@ -237,7 +238,8 @@ public class IconCache extends BaseIconCache {
@Override
protected String getIconSystemState(String packageName) {
- return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
+ return mIconProvider.getSystemStateForPackage(mSystemState, packageName)
+ + ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
}
public static abstract class IconLoadRequest extends HandlerRunnable {
diff --git a/src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
similarity index 67%
rename from src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java
rename to src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index 7c996339bd..f9a94daf53 100644
--- a/src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -20,14 +20,23 @@ import android.content.Context;
import android.content.pm.LauncherActivityInfo;
import android.os.UserHandle;
+import com.android.launcher3.IconProvider;
+import com.android.launcher3.R;
import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.util.ResourceBasedOverride;
-public class LauncherActivtiyCachingLogic implements CachingLogic {
+/**
+ * Caching logic for LauncherActivityInfo.
+ */
+public class LauncherActivityCachingLogic
+ implements CachingLogic, ResourceBasedOverride {
- private final IconCache mCache;
-
- public LauncherActivtiyCachingLogic(IconCache cache) {
- mCache = cache;
+ /**
+ * Creates and returns a new instance
+ */
+ public static LauncherActivityCachingLogic newInstance(Context context) {
+ return Overrides.getObject(LauncherActivityCachingLogic.class, context,
+ R.string.launcher_activity_logic_class);
}
@Override
@@ -49,8 +58,10 @@ public class LauncherActivtiyCachingLogic implements CachingLogic=0 ? newContainerTarget(dstContainerType) : null);
}
+ public void logActionCommand(int command, int srcContainerType, int dstContainerType,
+ int pageIndex) {
+ Target srcTarget = newContainerTarget(srcContainerType);
+ srcTarget.pageIndex = pageIndex;
+ logActionCommand(command, srcTarget,
+ dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
+ }
+
public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
if (command == Action.Command.STOP) {
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index ed0d470801..7d4f2f7225 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -16,6 +16,8 @@
package com.android.launcher3.model;
import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInstaller.SessionInfo;
import android.os.UserHandle;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -32,6 +34,8 @@ import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
@@ -85,6 +89,10 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
}
}
+ PackageInstallerCompat packageInstaller =
+ PackageInstallerCompat.getInstance(app.getContext());
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(app.getContext());
+
for (ItemInfo item : filteredItems) {
// Find appropriate space for the item.
int[] coords = findSpaceForItem(app, dataModel, workspaceScreens,
@@ -101,6 +109,36 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
throw new RuntimeException("Unexpected info type");
}
+ if (item instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) item).isPromise()) {
+ WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) item;
+ String packageName = item.getTargetComponent() != null
+ ? item.getTargetComponent().getPackageName() : null;
+ if (packageName == null) {
+ continue;
+ }
+ SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user,
+ packageName);
+ if (sessionInfo == null) {
+ List activities = launcherApps
+ .getActivityList(packageName, item.user);
+ if (activities != null && !activities.isEmpty()) {
+ // App was installed while launcher was in the background.
+ itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
+ .makeWorkspaceItem();
+ WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
+ wii.title = "";
+ wii.applyFrom(app.getIconCache().getDefaultIcon(item.user));
+ app.getIconCache().getTitleAndIcon(wii,
+ ((WorkspaceItemInfo) itemInfo).usingLowResIcon());
+ } else {
+ // Session was cancelled, do not add.
+ continue;
+ }
+ } else {
+ workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
+ }
+ }
+
// Add the shortcut to the db
getModelWriter().addItemToDatabase(itemInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
index 1613d47b9f..29a46cfa5c 100644
--- a/src/com/android/launcher3/model/AppLaunchTracker.java
+++ b/src/com/android/launcher3/model/AppLaunchTracker.java
@@ -15,18 +15,18 @@
*/
package com.android.launcher3.model;
-import static com.android.launcher3.util.ResourceBasedOverride.Overrides.getObject;
+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;
-import androidx.annotation.Nullable;
-
/**
* Callback for receiving various app launch events
*/
@@ -43,8 +43,7 @@ public class AppLaunchTracker implements ResourceBasedOverride {
public static final MainThreadInitializedObject INSTANCE =
- new MainThreadInitializedObject<>(c ->
- getObject(AppLaunchTracker.class, c, R.string.app_launch_tracker_class));
+ forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class);
public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
@Nullable String container) { }
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index 1149b553f6..a0b7177630 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -26,6 +26,7 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.HashMap;
@@ -34,6 +35,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import static android.os.Process.myUserHandle;
+
/**
* Helper class to send broadcasts to package installers that have:
* - Items on the first screen
@@ -60,7 +63,7 @@ public class FirstScreenBroadcast {
private final MultiHashMap mPackagesForInstaller;
- public FirstScreenBroadcast(HashMap sessionInfoForPackage) {
+ public FirstScreenBroadcast(HashMap sessionInfoForPackage) {
mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage);
}
@@ -69,11 +72,13 @@ public class FirstScreenBroadcast {
* of packages with active sessions for that installer.
*/
private MultiHashMap getPackagesForInstaller(
- HashMap sessionInfoForPackage) {
+ HashMap sessionInfoForPackage) {
MultiHashMap packagesForInstaller = new MultiHashMap<>();
- for (Map.Entry entry : sessionInfoForPackage.entrySet()) {
- packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
- entry.getKey());
+ for (Map.Entry entry : sessionInfoForPackage.entrySet()) {
+ if (myUserHandle().equals(entry.getKey().mUser)) {
+ packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
+ entry.getKey().mPackageName);
+ }
}
return packagesForInstaller;
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index faecc067e4..783e908e30 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -34,10 +34,12 @@ import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.function.Consumer;
import androidx.annotation.VisibleForTesting;
@@ -970,8 +972,9 @@ public class GridSizeMigrationTask {
.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
validPackages.add(info.packageName);
}
- validPackages.addAll(PackageInstallerCompat.getInstance(context)
- .updateAndGetActiveSessionCache().keySet());
+ PackageInstallerCompat.getInstance(context)
+ .updateAndGetActiveSessionCache().keySet()
+ .forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName));
return validPackages;
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 1a03b77714..1c39d1f23c 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -227,7 +227,7 @@ public class LoaderCursor extends CursorWrapper {
if (!TextUtils.isEmpty(title)) {
info.title = Utilities.trim(title);
}
- } else if (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
+ } else if (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
if (TextUtils.isEmpty(info.title)) {
info.title = getTitle();
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 0138572d0c..7593a3371a 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -19,6 +19,7 @@ package com.android.launcher3.model;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle;
import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
import android.appwidget.AppWidgetProviderInfo;
@@ -49,8 +50,8 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
@@ -61,7 +62,7 @@ import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherActivtiyCachingLogic;
+import com.android.launcher3.icons.LauncherActivityCachingLogic;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.logging.FileLog;
@@ -72,6 +73,7 @@ import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LooperIdleLock;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.TraceHelper;
import java.util.ArrayList;
@@ -196,7 +198,7 @@ public class LoaderTask implements Runnable {
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
- new LauncherActivtiyCachingLogic(mApp.getIconCache()),
+ LauncherActivityCachingLogic.newInstance(mApp.getContext()),
mApp.getModel()::onPackageIconsUpdated);
// Take a break
@@ -281,8 +283,9 @@ public class LoaderTask implements Runnable {
synchronized (mBgDataModel) {
mBgDataModel.clear();
- final HashMap installingPkgs =
+ final HashMap installingPkgs =
mPackageInstaller.updateAndGetActiveSessionCache();
+ final PackageUserKey tempPackageKey = new PackageUserKey(null, null);
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
Map shortcutKeyToPinnedShortcuts = new HashMap<>();
@@ -419,9 +422,10 @@ public class LoaderTask implements Runnable {
// installed later.
FileLog.d(TAG, "package not yet restored: " + targetPkg);
+ tempPackageKey.update(targetPkg, c.user);
if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) {
// Restore has started once.
- } else if (installingPkgs.containsKey(targetPkg)) {
+ } else if (installingPkgs.containsKey(tempPackageKey)) {
// App restore has started. Update the flag
c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
c.updater().put(LauncherSettings.Favorites.RESTORED,
@@ -536,7 +540,8 @@ public class LoaderTask implements Runnable {
}
if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
- SessionInfo si = installingPkgs.get(targetPkg);
+ tempPackageKey.update(targetPkg, c.user);
+ SessionInfo si = installingPkgs.get(tempPackageKey);
if (si == null) {
info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
} else {
@@ -630,8 +635,10 @@ public class LoaderTask implements Runnable {
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
component);
appWidgetInfo.restoreStatus = c.restoreFlag;
+
+ tempPackageKey.update(component.getPackageName(), c.user);
SessionInfo si =
- installingPkgs.get(component.getPackageName());
+ installingPkgs.get(tempPackageKey);
Integer installProgress = si == null
? null
: (int) (si.getProgress() * 100);
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 5f6d1281bd..9fcab38870 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -18,7 +18,6 @@ package com.android.launcher3.model;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.os.Process;
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
@@ -56,7 +55,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
ApplicationInfo ai = app.getContext()
.getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
- app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle());
+ app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user);
}
} catch (PackageManager.NameNotFoundException e) {
// Ignore
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index c37ed99522..4428c8e6df 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -43,6 +43,7 @@ import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -55,6 +56,8 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+
/**
* Handles updates due to changes in package manager (app installed/updated/removed)
* or when a user availability changes.
@@ -85,6 +88,10 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.APP_NOT_DISABLED, "PackageUpdatedTask: " + mOp + ", " +
+ Arrays.toString(mPackages));
+ }
final Context context = app.getContext();
final IconCache iconCache = app.getIconCache();
@@ -99,7 +106,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
- appsList.removePackage(packages[i], Process.myUserHandle());
+ appsList.removePackage(packages[i], mUser);
}
appsList.addPackage(context, packages[i], mUser);
@@ -227,8 +234,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
isTargetValid = LauncherAppsCompat.getInstance(context)
.isActivityEnabledForProfile(cn, mUser);
}
- if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)
- && !isTargetValid) {
+ if (si.hasStatusFlag(FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
} else if (si.hasPromiseIconUi()) {
diff --git a/src/com/android/launcher3/notification/NotificationKeyData.java b/src/com/android/launcher3/notification/NotificationKeyData.java
index 5050457b9b..a1917ecb00 100644
--- a/src/com/android/launcher3/notification/NotificationKeyData.java
+++ b/src/com/android/launcher3/notification/NotificationKeyData.java
@@ -17,13 +17,17 @@
package com.android.launcher3.notification;
import android.app.Notification;
+import android.app.Person;
import android.service.notification.StatusBarNotification;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Utilities;
+
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.NonNull;
-
/**
* The key data associated with the notification, used to determine what to include
* in dots and dummy popup views before they are populated.
@@ -33,20 +37,27 @@ import androidx.annotation.NonNull;
public class NotificationKeyData {
public final String notificationKey;
public final String shortcutId;
+ @NonNull
+ public final String[] personKeysFromNotification;
public int count;
- private NotificationKeyData(String notificationKey, String shortcutId, int count) {
+ private NotificationKeyData(String notificationKey, String shortcutId, int count,
+ String[] personKeysFromNotification) {
this.notificationKey = notificationKey;
this.shortcutId = shortcutId;
this.count = Math.max(1, count);
+ this.personKeysFromNotification = personKeysFromNotification;
}
public static NotificationKeyData fromNotification(StatusBarNotification sbn) {
Notification notif = sbn.getNotification();
- return new NotificationKeyData(sbn.getKey(), notif.getShortcutId(), notif.number);
+ return new NotificationKeyData(sbn.getKey(), notif.getShortcutId(), notif.number,
+ extractPersonKeyOnly(notif.extras.getParcelableArrayList(
+ Notification.EXTRA_PEOPLE_LIST)));
}
- public static List extractKeysOnly(@NonNull List notificationKeys) {
+ public static List extractKeysOnly(
+ @NonNull List notificationKeys) {
List keysOnly = new ArrayList<>(notificationKeys.size());
for (NotificationKeyData notificationKeyData : notificationKeys) {
keysOnly.add(notificationKeyData.notificationKey);
@@ -54,6 +65,14 @@ public class NotificationKeyData {
return keysOnly;
}
+ private static String[] extractPersonKeyOnly(@Nullable ArrayList people) {
+ if (people == null || people.isEmpty()) {
+ return Utilities.EMPTY_STRING_ARRAY;
+ }
+ return people.stream().filter(person -> person.getKey() != null)
+ .map(Person::getKey).sorted().toArray(String[]::new);
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof NotificationKeyData)) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 25d9f7976a..15fb4cea6f 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -36,7 +36,6 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
@@ -53,7 +52,6 @@ import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
import com.android.launcher3.dot.DotInfo;
@@ -65,13 +63,12 @@ import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationItemView;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
@@ -201,7 +198,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
return null;
}
ItemInfo itemInfo = (ItemInfo) icon.getTag();
- if (!DeepShortcutManager.supportsShortcuts(itemInfo)) {
+ if (!ShortcutUtil.supportsShortcuts(itemInfo)) {
return null;
}
@@ -300,7 +297,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
}
mLauncher.getDragController().addDragListener(this);
- mOriginalIcon.forceHideDot(true);
+ mOriginalIcon.setForceHideDot(true);
// All views are added. Animate layout from now on.
setLayoutTransition(new LayoutTransition());
@@ -563,14 +560,14 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
protected void onCreateCloseAnimation(AnimatorSet anim) {
// Animate original icon's text back in.
anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
- mOriginalIcon.forceHideDot(false);
+ mOriginalIcon.setForceHideDot(false);
}
@Override
protected void closeComplete() {
super.closeComplete();
mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
- mOriginalIcon.forceHideDot(false);
+ mOriginalIcon.setForceHideDot(false);
}
@Override
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 2d301ac008..4612b2a474 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -29,17 +29,22 @@ import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.widget.WidgetListRowEntry;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
* Provides data for the popup menu that appears after long-clicking on apps.
@@ -129,7 +134,8 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
for (PackageUserKey packageUserKey : mPackageUserToDotInfos.keySet()) {
DotInfo prevDot = updatedDots.get(packageUserKey);
DotInfo newDot = mPackageUserToDotInfos.get(packageUserKey);
- if (prevDot == null) {
+ if (prevDot == null
+ || prevDot.getNotificationCount() != newDot.getNotificationCount()) {
updatedDots.put(packageUserKey, newDot);
} else {
// No need to update the dot if it already existed (no visual change).
@@ -155,7 +161,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
}
public int getShortcutCountForItem(ItemInfo info) {
- if (!DeepShortcutManager.supportsShortcuts(info)) {
+ if (!ShortcutUtil.supportsDeepShortcuts(info)) {
return 0;
}
ComponentName component = info.getTargetComponent();
@@ -167,17 +173,26 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
return count == null ? 0 : count;
}
- public DotInfo getDotInfoForItem(ItemInfo info) {
- if (!DeepShortcutManager.supportsShortcuts(info)) {
+ public @Nullable DotInfo getDotInfoForItem(@NonNull ItemInfo info) {
+ if (!ShortcutUtil.supportsShortcuts(info)) {
return null;
}
-
- return mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
+ DotInfo dotInfo = mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
+ if (dotInfo == null) {
+ return null;
+ }
+ List notifications = getNotificationsForItem(
+ info, dotInfo.getNotificationKeys());
+ if (notifications.isEmpty()) {
+ return null;
+ }
+ return dotInfo;
}
public @NonNull List getNotificationKeysForItem(ItemInfo info) {
DotInfo dotInfo = getDotInfoForItem(info);
- return dotInfo == null ? Collections.EMPTY_LIST : dotInfo.getNotificationKeys();
+ return dotInfo == null ? Collections.EMPTY_LIST
+ : getNotificationsForItem(info, dotInfo.getNotificationKeys());
}
/** This makes a potentially expensive binder call and should be run on a background thread. */
@@ -226,6 +241,27 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
return null;
}
+ /**
+ * Returns a list of notifications that are relevant to given ItemInfo.
+ */
+ public static @NonNull List getNotificationsForItem(
+ @NonNull ItemInfo info, @NonNull List notifications) {
+ String shortcutId = ShortcutUtil.getShortcutIdIfPinnedShortcut(info);
+ if (shortcutId == null) {
+ return notifications;
+ }
+ String[] personKeys = ShortcutUtil.getPersonKeysIfPinnedShortcut(info);
+ return notifications.stream().filter((NotificationKeyData notification) -> {
+ if (notification.shortcutId != null) {
+ return notification.shortcutId.equals(shortcutId);
+ }
+ if (notification.personKeysFromNotification.length != 0) {
+ return Arrays.equals(notification.personKeysFromNotification, personKeys);
+ }
+ return false;
+ }).collect(Collectors.toList());
+ }
+
public interface PopupDataChangeListener {
PopupDataChangeListener INSTANCE = new PopupDataChangeListener() { };
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 41ab4df7bf..5a5fbabacf 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -29,11 +29,12 @@ import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
public class RemoteActionShortcut extends SystemShortcut {
private static final String TAG = "RemoteActionShortcut";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Utilities.IS_DEBUG_DEVICE;
private final RemoteAction mAction;
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 563f3b3c65..78bd81b464 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -131,6 +131,7 @@ public abstract class SystemShortcut extends Ite
@Override
public View.OnClickListener getOnClickListener(final Launcher launcher,
final ItemInfo itemInfo) {
+ if (itemInfo.getTargetComponent() == null) return null;
final List widgets =
launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java
index 516fafad54..37a209289e 100644
--- a/src/com/android/launcher3/popup/SystemShortcutFactory.java
+++ b/src/com/android/launcher3/popup/SystemShortcutFactory.java
@@ -15,6 +15,10 @@
*/
package com.android.launcher3.popup;
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
+import androidx.annotation.NonNull;
+
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -24,13 +28,10 @@ import com.android.launcher3.util.ResourceBasedOverride;
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.NonNull;
-
public class SystemShortcutFactory implements ResourceBasedOverride {
public static final MainThreadInitializedObject INSTANCE =
- new MainThreadInitializedObject<>(c -> Overrides.getObject(
- SystemShortcutFactory.class, c, R.string.system_shortcut_factory_class));
+ forOverride(SystemShortcutFactory.class, R.string.system_shortcut_factory_class);
/** Note that these are in order of priority. */
private final SystemShortcut[] mAllShortcuts;
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 3c0c5fddea..d643a0b493 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -173,12 +173,6 @@ public class RestoreDbTask {
values.put(Favorites.PROFILE_ID, newProfileId);
db.update(Favorites.TABLE_NAME, values, "profileId = ?",
new String[]{Long.toString(oldProfileId)});
-
- // Change default value of the column.
- db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
- Favorites.addTableToDb(db, newProfileId, false);
- db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
- dropTable(db, "favorites_old");
}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index bab454f070..790a2e8448 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -15,8 +15,13 @@
*/
package com.android.launcher3.testing;
+import static android.graphics.Bitmap.Config.ARGB_8888;
+
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
import android.os.Bundle;
+import android.os.Debug;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
@@ -28,6 +33,7 @@ import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.util.ResourceBasedOverride;
+import java.util.LinkedList;
import java.util.concurrent.ExecutionException;
public class TestInformationHandler implements ResourceBasedOverride {
@@ -41,6 +47,7 @@ public class TestInformationHandler implements ResourceBasedOverride {
protected DeviceProfile mDeviceProfile;
protected LauncherAppState mLauncherAppState;
protected Launcher mLauncher;
+ private static LinkedList mLeaks;
public void init(Context context) {
mContext = context;
@@ -112,7 +119,37 @@ public class TestInformationHandler implements ResourceBasedOverride {
}
break;
}
+
+ case TestProtocol.REQUEST_TOTAL_PSS_KB: {
+ Debug.MemoryInfo mem = new Debug.MemoryInfo();
+ Debug.getMemoryInfo(mem);
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss());
+ break;
+ }
+
+ case TestProtocol.REQUEST_JAVA_LEAK: {
+ if (mLeaks == null) mLeaks = new LinkedList();
+
+ // Allocate and dirty the memory.
+ final int leakSize = 1024 * 1024;
+ final byte[] bytes = new byte[leakSize];
+ for (int i = 0; i < leakSize; i += 239) {
+ bytes[i] = (byte) (i % 256);
+ }
+ mLeaks.add(bytes);
+ break;
+ }
+
+ case TestProtocol.REQUEST_NATIVE_LEAK: {
+ if (mLeaks == null) mLeaks = new LinkedList();
+
+ // Allocate and dirty a bitmap.
+ final Bitmap bitmap = Bitmap.createBitmap(512, 512, ARGB_8888);
+ bitmap.eraseColor(Color.RED);
+ mLeaks.add(bitmap);
+ break;
+ }
}
return response;
}
-}
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 9846a04271..232a764e01 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -73,10 +73,17 @@ public final class TestProtocol {
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin";
public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin";
+ public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
+ public static final String REQUEST_JAVA_LEAK = "java-leak";
+ public static final String REQUEST_NATIVE_LEAK = "native-leak";
public static boolean sDebugTracing = false;
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
+ public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
+ public static final String APP_NOT_DISABLED = "b/139891609";
+ public static final String ALL_APPS_UPON_RECENTS = "b/139941530";
+ public static final String STABLE_STATE_MISMATCH = "b/140311911";
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 0545344170..c5ba5bab6a 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -69,6 +69,7 @@ public abstract class AbstractStateChangeTouchController
protected final SwipeDetector.Direction mSwipeDirection;
private boolean mNoIntercept;
+ private boolean mIsLogContainerSet;
protected int mStartContainerType;
protected LauncherState mStartState;
@@ -180,7 +181,7 @@ public abstract class AbstractStateChangeTouchController
/**
* Returns the container that the touch started from when leaving NORMAL state.
*/
- protected abstract int getLogContainerTypeForNormalState();
+ protected abstract int getLogContainerTypeForNormalState(MotionEvent ev);
private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
@@ -231,13 +232,7 @@ public abstract class AbstractStateChangeTouchController
@Override
public void onDragStart(boolean start) {
mStartState = mLauncher.getStateManager().getState();
- if (mStartState == ALL_APPS) {
- mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
- } else if (mStartState == NORMAL) {
- mStartContainerType = getLogContainerTypeForNormalState();
- } else if (mStartState == OVERVIEW){
- mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
- }
+ mIsLogContainerSet = false;
if (mCurrentAnimation == null) {
mFromState = mStartState;
mToState = null;
@@ -285,6 +280,21 @@ public abstract class AbstractStateChangeTouchController
return true;
}
+ @Override
+ public boolean onDrag(float displacement, MotionEvent ev) {
+ if (!mIsLogContainerSet) {
+ if (mStartState == ALL_APPS) {
+ mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
+ } else if (mStartState == NORMAL) {
+ mStartContainerType = getLogContainerTypeForNormalState(ev);
+ } else if (mStartState == OVERVIEW) {
+ mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
+ }
+ mIsLogContainerSet = true;
+ }
+ return onDrag(displacement);
+ }
+
protected void updateProgress(float fraction) {
mCurrentAnimation.setPlayFraction(fraction);
if (mAtomicComponentsController != null) {
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 2ee0328597..e185a31990 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -18,12 +18,13 @@ package com.android.launcher3.util;
import android.content.Context;
import android.os.Looper;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import java.util.concurrent.ExecutionException;
-import androidx.annotation.VisibleForTesting;
-
/**
* Utility class for defining singletons which are initiated on main thread.
*/
@@ -60,6 +61,14 @@ public class MainThreadInitializedObject {
mValue = value;
}
+ /**
+ * Initializes a provider based on resource overrides
+ */
+ public static MainThreadInitializedObject forOverride(
+ Class clazz, int resourceId) {
+ return new MainThreadInitializedObject<>(c -> Overrides.getObject(clazz, c, resourceId));
+ }
+
public interface ObjectProvider {
T get(Context context);
diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java
index 1ce2822109..aa11968e41 100644
--- a/src/com/android/launcher3/util/PackageUserKey.java
+++ b/src/com/android/launcher3/util/PackageUserKey.java
@@ -3,6 +3,8 @@ package com.android.launcher3.util;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.ItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -15,7 +17,9 @@ public class PackageUserKey {
public UserHandle mUser;
private int mHashCode;
+ @Nullable
public static PackageUserKey fromItemInfo(ItemInfo info) {
+ if (info.getTargetComponent() == null) return null;
return new PackageUserKey(info.getTargetComponent().getPackageName(), info.user);
}
@@ -27,7 +31,7 @@ public class PackageUserKey {
update(packageName, user);
}
- private void update(String packageName, UserHandle user) {
+ public void update(String packageName, UserHandle user) {
mPackageName = packageName;
mUser = user;
mHashCode = Arrays.hashCode(new Object[] {packageName, user});
@@ -38,7 +42,8 @@ public class PackageUserKey {
* @return Whether this PackageUserKey was successfully updated - it shouldn't be used if not.
*/
public boolean updateFromItemInfo(ItemInfo info) {
- if (DeepShortcutManager.supportsShortcuts(info)) {
+ if (info.getTargetComponent() == null) return false;
+ if (ShortcutUtil.supportsShortcuts(info)) {
update(info.getTargetComponent().getPackageName(), info.user);
return true;
}
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
new file mode 100644
index 0000000000..af99713a1a
--- /dev/null
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.shortcuts.ShortcutKey;
+
+public class ShortcutUtil {
+ /**
+ * Returns true when we should show shortcut menu for the item.
+ */
+ public static boolean supportsShortcuts(ItemInfo info) {
+ return isActive(info) && (isApp(info) || isPinnedShortcut(info));
+ }
+
+ /**
+ * Returns true when we should show depp shortcuts in shortcut menu for the item.
+ */
+ public static boolean supportsDeepShortcuts(ItemInfo info) {
+ return isActive(info) && isApp(info);
+ }
+
+ /**
+ * Returns the shortcut id if the item is a pinned shortcut.
+ */
+ public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
+ return isActive(info) && isPinnedShortcut(info)
+ ? ShortcutKey.fromItemInfo(info).getId() : null;
+ }
+
+ /**
+ * Returns the person keys associated with the item. (Has no function right now.)
+ */
+ public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
+ return isActive(info) && isPinnedShortcut(info)
+ ? ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
+ }
+
+ /**
+ * Returns true if the item is a deep shortcut.
+ */
+ public static boolean isDeepShortcut(ItemInfo info) {
+ return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && info instanceof WorkspaceItemInfo;
+ }
+
+ private static boolean isActive(ItemInfo info) {
+ boolean isLoading = info instanceof WorkspaceItemInfo
+ && ((WorkspaceItemInfo) info).hasPromiseIconUi();
+ return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
+ }
+
+ private static boolean isApp(ItemInfo info) {
+ return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ }
+
+ private static boolean isPinnedShortcut(ItemInfo info) {
+ return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && info.container != ItemInfo.NO_ID
+ && info instanceof WorkspaceItemInfo;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index f948beb8d2..a4518bae3c 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -153,15 +153,15 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
}
protected void handleClose(boolean animate, long defaultDuration) {
- if (mIsOpen && !animate) {
+ if (!mIsOpen) {
+ return;
+ }
+ if (!animate) {
mOpenCloseAnimator.cancel();
setTranslationShift(TRANSLATION_SHIFT_CLOSED);
onCloseComplete();
return;
}
- if (!mIsOpen) {
- return;
- }
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED));
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 799762d8fe..c08b659313 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -29,6 +29,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
@@ -41,6 +42,7 @@ import android.widget.FrameLayout;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.TouchController;
@@ -261,6 +263,10 @@ public abstract class BaseDragLayer
}
case ACTION_CANCEL:
case ACTION_UP:
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE,
+ "BaseDragLayer.ACTION_UP/CANCEL " + ev);
+ }
mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
break;
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index e09a9e8bfc..f728a67764 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -18,7 +18,6 @@ package com.android.launcher3.views;
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
-import static com.android.launcher3.Utilities.isRtl;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -564,11 +563,6 @@ public class FloatingIconView extends View implements
*/
private void checkIconResult(View originalView, boolean isOpening) {
CancellationSignal cancellationSignal = new CancellationSignal();
- if (!isOpening) {
- // Hide immediately since the floating view starts at a different location.
- originalView.setVisibility(INVISIBLE);
- cancellationSignal.setOnCancelListener(() -> originalView.setVisibility(VISIBLE));
- }
if (mIconLoadResult == null) {
Log.w(TAG, "No icon load result found in checkIconResult");
@@ -580,7 +574,7 @@ public class FloatingIconView extends View implements
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
mIconLoadResult.iconOffset);
if (isOpening) {
- originalView.setVisibility(INVISIBLE);
+ hideOriginalView(originalView);
}
} else {
mIconLoadResult.onIconLoaded = () -> {
@@ -591,15 +585,26 @@ public class FloatingIconView extends View implements
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
mIconLoadResult.iconOffset);
- // Delay swapping views until the icon is loaded to prevent a flash.
setVisibility(VISIBLE);
- originalView.setVisibility(INVISIBLE);
+ if (isOpening) {
+ // Delay swapping views until the icon is loaded to prevent a flash.
+ hideOriginalView(originalView);
+ }
};
mLoadIconSignal = cancellationSignal;
}
}
}
+ private void hideOriginalView(View originalView) {
+ if (originalView instanceof BubbleTextView) {
+ ((BubbleTextView) originalView).setIconVisible(false);
+ ((BubbleTextView) originalView).setForceHideDot(true);
+ } else {
+ originalView.setVisibility(INVISIBLE);
+ }
+ }
+
private void setBackgroundDrawableBounds(float scale) {
sTmpRect.set(mFinalDrawableBounds);
Utilities.scaleRectAboutCenter(sTmpRect, scale);
@@ -716,7 +721,7 @@ public class FloatingIconView extends View implements
*/
@UiThread
public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) {
- IconLoadResult result = new IconLoadResult();
+ IconLoadResult result = new IconLoadResult(info);
new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
RectF position = new RectF();
getLocationBoundsForView(l, v, isOpening, position);
@@ -745,10 +750,13 @@ public class FloatingIconView extends View implements
// Get the drawable on the background thread
boolean shouldLoadIcon = originalView.getTag() instanceof ItemInfo && hideOriginal;
- view.mIconLoadResult = sIconLoadResult;
- if (shouldLoadIcon && view.mIconLoadResult == null) {
- view.mIconLoadResult = fetchIcon(launcher, originalView,
- (ItemInfo) originalView.getTag(), isOpening);
+ if (shouldLoadIcon) {
+ if (sIconLoadResult != null && sIconLoadResult.itemInfo == originalView.getTag()) {
+ view.mIconLoadResult = sIconLoadResult;
+ } else {
+ view.mIconLoadResult = fetchIcon(launcher, originalView,
+ (ItemInfo) originalView.getTag(), isOpening);
+ }
}
sIconLoadResult = null;
@@ -776,7 +784,12 @@ public class FloatingIconView extends View implements
if (hideOriginal) {
if (isOpening) {
- originalView.setVisibility(VISIBLE);
+ if (originalView instanceof BubbleTextView) {
+ ((BubbleTextView) originalView).setIconVisible(true);
+ ((BubbleTextView) originalView).setForceHideDot(false);
+ } else {
+ originalView.setVisibility(VISIBLE);
+ }
view.finish(dragLayer);
} else {
view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
@@ -804,38 +817,33 @@ public class FloatingIconView extends View implements
}
});
- if (mBadge != null && !(mOriginalIcon instanceof FolderIcon)) {
+ if (mBadge != null) {
ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255);
badgeFade.addUpdateListener(valueAnimator -> invalidate());
fade.play(badgeFade);
}
- if (originalView instanceof BubbleTextView) {
- BubbleTextView btv = (BubbleTextView) originalView;
- btv.forceHideDot(true);
+ if (originalView instanceof IconLabelDotView) {
+ IconLabelDotView view = (IconLabelDotView) originalView;
fade.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- btv.forceHideDot(false);
+ view.setIconVisible(true);
+ view.setForceHideDot(false);
}
});
}
- if (originalView instanceof FolderIcon) {
- FolderIcon folderIcon = (FolderIcon) originalView;
- folderIcon.setBackgroundVisible(false);
- folderIcon.getFolderName().setTextVisibility(false);
- fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true));
+ if (originalView instanceof BubbleTextView) {
+ BubbleTextView btv = (BubbleTextView) originalView;
fade.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationEnd(Animator animation) {
- folderIcon.setBackgroundVisible(true);
- if (folderIcon.hasDot()) {
- folderIcon.animateDotScale(0, 1f);
- }
+ public void onAnimationStart(Animator animation) {
+ btv.setIconVisible(true);
}
});
- } else {
+ fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255));
+ } else if (!(originalView instanceof FolderIcon)) {
fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
}
@@ -890,10 +898,15 @@ public class FloatingIconView extends View implements
}
private static class IconLoadResult {
+ final ItemInfo itemInfo;
Drawable drawable;
Drawable badge;
int iconOffset;
Runnable onIconLoaded;
boolean isIconLoaded;
+
+ public IconLoadResult(ItemInfo itemInfo) {
+ this.itemInfo = itemInfo;
+ }
}
}
diff --git a/src/com/android/launcher3/views/IconLabelDotView.java b/src/com/android/launcher3/views/IconLabelDotView.java
new file mode 100644
index 0000000000..057caafe78
--- /dev/null
+++ b/src/com/android/launcher3/views/IconLabelDotView.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+/**
+ * A view that has an icon, label, and notification dot.
+ */
+public interface IconLabelDotView {
+ void setIconVisible(boolean visible);
+ void setForceHideDot(boolean hide);
+}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 63f742768f..465df448e3 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -18,10 +18,8 @@ package com.android.launcher3.views;
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextUtils;
@@ -33,6 +31,9 @@ import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -46,7 +47,6 @@ import com.android.launcher3.widget.WidgetsFullSheet;
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.VisibleForTesting;
/**
* Popup shown on long pressing an empty space in launcher
@@ -169,16 +169,17 @@ public class OptionsPopupView extends ArrowPopup
}
public static boolean onWidgetsClicked(View view) {
- return openWidgets(Launcher.getLauncher(view.getContext()));
+ return openWidgets(Launcher.getLauncher(view.getContext())) != null;
}
- public static boolean openWidgets(Launcher launcher) {
+ /** Returns WidgetsFullSheet that was opened, or null if nothing was opened. */
+ @Nullable
+ public static WidgetsFullSheet openWidgets(Launcher launcher) {
if (launcher.getPackageManager().isSafeMode()) {
Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
- return false;
+ return null;
} else {
- WidgetsFullSheet.show(launcher, true /* animated */);
- return true;
+ return WidgetsFullSheet.show(launcher, true /* animated */);
}
}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index c36011745c..da1df3f899 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -18,14 +18,14 @@ package com.android.launcher3.views;
import static android.content.Context.ACCESSIBILITY_SERVICE;
import static android.view.MotionEvent.ACTION_DOWN;
+import static androidx.core.graphics.ColorUtils.compositeColors;
+
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.DEACCEL;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
-import static androidx.core.graphics.ColorUtils.compositeColors;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.Keyframe;
@@ -47,6 +47,13 @@ import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
+import androidx.customview.widget.ExploreByTouchHelper;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
@@ -62,15 +69,10 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.widget.WidgetsFullSheet;
import java.util.List;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
-import androidx.customview.widget.ExploreByTouchHelper;
/**
* Simple scrim which draws a flat color
@@ -325,7 +327,7 @@ public class ScrimView extends View implements Insettable, OnChangeListener,
if (enabled) {
stateManager.addStateListener(this);
- handleStateChangedComplete(mLauncher.getStateManager().getState());
+ handleStateChangedComplete(stateManager.getState());
} else {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
}
@@ -437,7 +439,24 @@ public class ScrimView extends View implements Insettable, OnChangeListener,
} else if (action == WALLPAPERS) {
return OptionsPopupView.startWallpaperPicker(ScrimView.this);
} else if (action == WIDGETS) {
- return OptionsPopupView.onWidgetsClicked(ScrimView.this);
+ int originalImportanceForAccessibility = getImportantForAccessibility();
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ WidgetsFullSheet widgetsFullSheet = OptionsPopupView.openWidgets(mLauncher);
+ if (widgetsFullSheet == null) {
+ setImportantForAccessibility(originalImportanceForAccessibility);
+ return false;
+ }
+ widgetsFullSheet.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {}
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ setImportantForAccessibility(originalImportanceForAccessibility);
+ widgetsFullSheet.removeOnAttachStateChangeListener(this);
+ }
+ });
+ return true;
} else if (action == SETTINGS) {
return OptionsPopupView.startSettings(ScrimView.this);
}
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 6b6f70d7b6..09b1890491 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -27,9 +27,7 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
+import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
@@ -63,13 +61,6 @@ public class DeepShortcutManager {
mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
}
- public static boolean supportsShortcuts(ItemInfo info) {
- boolean isItemPromise = info instanceof WorkspaceItemInfo
- && ((WorkspaceItemInfo) info).hasPromiseIconUi();
- return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && !info.isDisabled() && !isItemPromise;
- }
-
public boolean wasLastCallSuccess() {
return mWasLastCallSuccess;
}
@@ -89,8 +80,9 @@ public class DeepShortcutManager {
* Gets all the manifest and dynamic shortcuts associated with the given package and user,
* to be displayed in the shortcuts container on long press.
*/
- public List queryForShortcutsContainer(ComponentName activity,
+ public List queryForShortcutsContainer(@Nullable ComponentName activity,
UserHandle user) {
+ if (activity == null) return Collections.EMPTY_LIST;
return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
activity.getPackageName(), activity, null, user);
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
index e9dc800e3c..bd6ea502e4 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
@@ -57,7 +57,7 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
}
@Override
- protected int getLogContainerTypeForNormalState() {
+ protected int getLogContainerTypeForNormalState(MotionEvent ev) {
return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent) ?
ContainerType.HOTSEAT : ContainerType.WORKSPACE;
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
new file mode 100644
index 0000000000..60f12d82a7
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import android.content.Context;
+import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
+
+public class TogglableFlag extends BaseTogglableFlag {
+
+ public TogglableFlag(String key, boolean defaultValue, String description) {
+ super(key, defaultValue, description);
+ }
+
+ @Override
+ public boolean getOverridenDefaultValue(boolean value) {
+ return value;
+ }
+
+ @Override
+ public void addChangeListener(Context context, Runnable r) { }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 5cc64dc9aa..467ae02d5c 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -17,9 +17,11 @@
package com.android.launcher3.uioverrides;
import android.app.Activity;
+import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -27,6 +29,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.util.TouchController;
@@ -95,4 +98,7 @@ public class UiFactory {
public static void clearSwipeSharedState(boolean finishAnimation) {}
+ public static Person[] getPersons(ShortcutInfo si) {
+ return Utilities.EMPTY_PERSON_ARRAY;
+ }
}
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 61c7306e57..c6f55a7178 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -20,6 +20,9 @@
+
+
+
diff --git a/tests/OWNERS b/tests/OWNERS
index 046d871163..02e8ebcaba 100644
--- a/tests/OWNERS
+++ b/tests/OWNERS
@@ -1 +1,4 @@
vadimt@google.com
+sunnygoyal@google.com
+winsonc@google.com
+hyunyoungs@google.com
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
new file mode 100644
index 0000000000..efbd9c99c3
--- /dev/null
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.compat;
+
+import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.UUID;
+
+
+/**
+ * Test to verify promise icon flow.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class PromiseIconUiTest extends AbstractLauncherUiTest {
+
+ private int mSessionId = -1;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mDevice.pressHome();
+ waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
+ waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+ mSessionId = -1;
+ }
+
+ @After
+ public void tearDown() {
+ if (mSessionId > -1) {
+ mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+ }
+ }
+
+ /**
+ * Create a session and return the id.
+ */
+ private int createSession(String label, Bitmap icon) throws Throwable {
+ SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+ params.setAppPackageName("test.promise.app");
+ params.setAppLabel(label);
+ params.setAppIcon(icon);
+ params.setInstallReason(PackageManager.INSTALL_REASON_USER);
+ return mTargetContext.getPackageManager().getPackageInstaller().createSession(params);
+ }
+
+ @Test
+ public void testPromiseIcon_addedFromEligibleSession() throws Throwable {
+ final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
+ final Workspace.ItemOperator findPromiseApp = (info, view) ->
+ info != null && TextUtils.equals(info.title, appLabel);
+
+ // Create and add test session
+ mSessionId = createSession(appLabel, Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8));
+
+ // Verify promise icon is added
+ waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
+ launcher.getWorkspace().getFirstMatch(findPromiseApp) != null);
+
+ // Remove session
+ mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+ mSessionId = -1;
+
+ // Verify promise icon is removed
+ waitForLauncherCondition("Test Promise App not removed from workspace", launcher ->
+ launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+ }
+
+ @Test
+ public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
+ final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
+ final Workspace.ItemOperator findPromiseApp = (info, view) ->
+ info != null && TextUtils.equals(info.title, appLabel);
+
+ // Create and add test session without icon or label
+ mSessionId = createSession(null, null);
+
+ // Sleep for duration of animation if a view was to be added + some buffer time.
+ Thread.sleep(Launcher.NEW_APPS_PAGE_MOVE_DELAY + Launcher.NEW_APPS_ANIMATION_DELAY + 500);
+
+ // Verify promise icon is not added
+ waitForLauncherCondition("Test Promise App not found on workspace", launcher ->
+ launcher.getWorkspace().getFirstMatch(findPromiseApp) == null);
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 8dc8cea40d..dc890bb005 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -24,7 +24,6 @@ import static org.junit.Assert.assertTrue;
import static java.lang.System.exit;
-import android.app.Instrumentation;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -38,6 +37,7 @@ import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
@@ -111,6 +111,7 @@ public abstract class AbstractLauncherUiTest {
launcher ->
checkLauncherIntegrity(launcher, containerType)));
}
+ mLauncher.enableDebugTracing();
}
protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
@@ -186,14 +187,6 @@ public abstract class AbstractLauncherUiTest {
}
}
- protected void lockRotation(boolean naturalOrientation) throws RemoteException {
- if (naturalOrientation) {
- mDevice.setOrientationNatural();
- } else {
- mDevice.setOrientationRight();
- }
- }
-
protected void clearLauncherData() throws IOException, InterruptedException {
if (TestHelpers.isInLauncherProcess()) {
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
@@ -201,6 +194,7 @@ public abstract class AbstractLauncherUiTest {
resetLoaderState();
} else {
clearPackageData(mDevice.getLauncherPackageName());
+ mLauncher.enableDebugTracing();
}
}
@@ -272,6 +266,12 @@ public abstract class AbstractLauncherUiTest {
waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
}
+ // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
+ // flakiness.
+ protected T getOnceNotNull(String message, Function f) {
+ return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT);
+ }
+
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
protected void waitForLauncherCondition(
@@ -280,6 +280,20 @@ public abstract class AbstractLauncherUiTest {
Wait.atMost(message, () -> getFromLauncher(condition), timeout);
}
+ // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
+ // flakiness.
+ protected T getOnceNotNull(String message, Function f, long timeout) {
+ if (!TestHelpers.isInLauncherProcess()) return null;
+
+ final Object[] output = new Object[1];
+ Wait.atMost(message, () -> {
+ final Object fromLauncher = getFromLauncher(f);
+ output[0] = fromLauncher;
+ return fromLauncher != null;
+ }, timeout);
+ return (T) output[0];
+ }
+
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
protected void waitForLauncherCondition(
@@ -330,30 +344,27 @@ public abstract class AbstractLauncherUiTest {
}
protected void startAppFast(String packageName) {
- final Instrumentation instrumentation = getInstrumentation();
- final Intent intent = instrumentation.getContext().getPackageManager().
- getLaunchIntentForPackage(packageName);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- instrumentation.getTargetContext().startActivity(intent);
- assertTrue(packageName + " didn't start",
- mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), DEFAULT_UI_TIMEOUT));
+ startIntent(
+ getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
+ packageName),
+ By.pkg(packageName).depth(0));
}
protected void startTestActivity(int activityNumber) {
final String packageName = getAppPackageName();
- final Instrumentation instrumentation = getInstrumentation();
- final Intent intent = instrumentation.getContext().getPackageManager().
+ final Intent intent = getInstrumentation().getContext().getPackageManager().
getLaunchIntentForPackage(packageName);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(packageName,
"com.android.launcher3.tests.Activity" + activityNumber));
- instrumentation.getTargetContext().startActivity(intent);
- assertTrue(packageName + " didn't start",
- mDevice.wait(
- Until.hasObject(By.pkg(packageName).text("TestActivity" + activityNumber)),
- DEFAULT_UI_TIMEOUT));
+ startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber));
+ }
+
+ private void startIntent(Intent intent, BySelector selector) {
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ getInstrumentation().getTargetContext().startActivity(intent);
+ assertTrue("App didn't start: " + selector,
+ mDevice.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
}
public static String resolveSystemApp(String category) {
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index ddcb4da883..80bb3edddd 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -44,8 +44,11 @@ class PortraitLandscapeRunner implements TestRule {
} finally {
mTest.mDevice.setOrientationNatural();
mTest.executeOnLauncher(launcher ->
- launcher.getRotationHelper().forceAllowRotationForTesting(
- false));
+ {
+ if (launcher != null) {
+ launcher.getRotationHelper().forceAllowRotationForTesting(false);
+ }
+ });
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
}
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 4dab44fadd..c2a3c1c524 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -190,7 +190,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
isWorkspaceScrollable(launcher)));
assertNotNull("ensureScrollable didn't add Chrome app",
- workspace.tryGetWorkspaceAppIcon("Chrome"));
+ workspace.getWorkspaceAppIcon("Chrome"));
// Test flinging workspace.
workspace.flingBackward();
@@ -206,7 +206,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
// Test starting a workspace app.
- final AppIcon app = workspace.tryGetWorkspaceAppIcon("Chrome");
+ final AppIcon app = workspace.getWorkspaceAppIcon("Chrome");
assertNotNull("No Chrome app in workspace", app);
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 3206a69bbf..3f35a3a73a 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -69,34 +69,24 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest {
}
@Test
+ @PortraitLandscape
+ @org.junit.Ignore
public void testWidgetConfig() throws Throwable {
- runTest(false, true);
- }
-
- @Test
- @Ignore // b/121280703
- public void testWidgetConfig_rotate() throws Throwable {
- runTest(true, true);
+ runTest(true);
}
@Test
+ @PortraitLandscape
+ @org.junit.Ignore
public void testConfigCancelled() throws Throwable {
- runTest(false, false);
+ runTest(false);
}
- @Test
- @Ignore // b/121280703
- public void testConfigCancelled_rotate() throws Throwable {
- runTest(true, false);
- }
/**
- * @param rotateConfig should the config screen be rotated
* @param acceptConfig accept the config activity
*/
- private void runTest(boolean rotateConfig, boolean acceptConfig) throws Throwable {
- lockRotation(true);
-
+ private void runTest(boolean acceptConfig) throws Throwable {
clearHomescreen();
mDevice.pressHome();
@@ -110,13 +100,6 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest {
// Widget id for which the config activity was opened
mWidgetId = monitor.getWidgetId();
- if (rotateConfig) {
- // Rotate the screen and verify that the config activity is recreated
- monitor = new WidgetConfigStartupMonitor();
- lockRotation(false);
- assertEquals(mWidgetId, monitor.getWidgetId());
- }
-
// Verify that the widget id is valid and bound
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 276c6144a2..1edce22ec6 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.ui.widget;
+import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import androidx.test.filters.LargeTest;
@@ -22,6 +25,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.tapl.Widget;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
@@ -41,19 +45,9 @@ public class AddWidgetTest extends AbstractLauncherUiTest {
@Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
@Test
- public void testDragIcon_portrait() throws Throwable {
- lockRotation(true);
- performTest();
- }
-
- @Test
- @Ignore // b/121280703
- public void testDragIcon_landscape() throws Throwable {
- lockRotation(false);
- performTest();
- }
-
- private void performTest() throws Throwable {
+ @PortraitLandscape
+ @org.junit.Ignore
+ public void testDragIcon() throws Throwable {
clearHomescreen();
mDevice.pressHome();
@@ -70,5 +64,10 @@ public class AddWidgetTest extends AbstractLauncherUiTest {
(info, view) -> info instanceof LauncherAppWidgetInfo &&
((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
widgetInfo.provider.getClassName())).call());
+
+ final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label,
+ DEFAULT_UI_TIMEOUT);
+ assertNotNull("Widget not found on the workspace", widget);
+ widget.launch(getAppPackageName());
}
}
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 3a7df64e81..e6348d9c02 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -44,6 +44,7 @@ import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
@@ -54,7 +55,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashSet;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Tests for bind widget flow.
@@ -326,9 +329,12 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
int count = 0;
String pkg = invalidPackage;
- Set activePackage = getOnUiThread(() ->
- PackageInstallerCompat.getInstance(mTargetContext)
- .updateAndGetActiveSessionCache().keySet());
+ Set activePackage = getOnUiThread(() -> {
+ Set packages = new HashSet<>();
+ PackageInstallerCompat.getInstance(mTargetContext).updateAndGetActiveSessionCache()
+ .keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName));
+ return packages;
+ });
while(true) {
try {
mTargetContext.getPackageManager().getPackageInfo(
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index a9a5090988..07129ddd95 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -128,8 +128,6 @@ public class RequestPinItemTest extends AbstractLauncherUiTest {
if (!Utilities.ATLEAST_OREO) {
return;
}
- lockRotation(true);
-
clearHomescreen();
mDevice.pressHome();
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index 593cce832b..899686bd61 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -1,6 +1,7 @@
package com.android.launcher3.util;
import android.os.SystemClock;
+import android.util.Log;
import org.junit.Assert;
@@ -16,7 +17,9 @@ public class Wait {
}
public static void atMost(String message, Condition condition, long timeout, long sleepMillis) {
- long endTime = SystemClock.uptimeMillis() + timeout;
+ final long startTime = SystemClock.uptimeMillis();
+ long endTime = startTime + timeout;
+ Log.d("Wait", "atMost: " + startTime + " - " + endTime);
while (SystemClock.uptimeMillis() < endTime) {
try {
if (condition.isTrue()) {
@@ -36,6 +39,7 @@ public class Wait {
} catch (Throwable t) {
throw new RuntimeException(t);
}
+ Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
Assert.fail(message);
}
}
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index eef2f24baa..cdda0f0dcc 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -15,17 +15,16 @@ import java.io.IOException;
public class FailureWatcher extends TestWatcher {
private static final String TAG = "FailureWatcher";
- private static int sScreenshotCount = 0;
final private UiDevice mDevice;
public FailureWatcher(UiDevice device) {
mDevice = device;
}
- private void dumpViewHierarchy() {
+ private static void dumpViewHierarchy(UiDevice device) {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
- mDevice.dumpWindowHierarchy(stream);
+ device.dumpWindowHierarchy(stream);
stream.flush();
stream.close();
for (String line : stream.toString().split("\\r?\\n")) {
@@ -38,22 +37,27 @@ public class FailureWatcher extends TestWatcher {
@Override
protected void failed(Throwable e, Description description) {
- if (mDevice == null) return;
+ onError(mDevice, description, e);
+ }
+
+ public static void onError(UiDevice device, Description description, Throwable e) {
+ if (device == null) return;
final String pathname = getInstrumentation().getTargetContext().
- getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png";
+ getFilesDir().getPath() + "/TestScreenshot-" + description.getMethodName()
+ + ".png";
Log.e(TAG, "Failed test " + description.getMethodName() +
", screenshot will be saved to " + pathname +
", track trace is below, UI object dump is further below:\n" +
Log.getStackTraceString(e));
- dumpViewHierarchy();
+ dumpViewHierarchy(device);
try {
- final String dumpsysResult = mDevice.executeShellCommand(
+ final String dumpsysResult = device.executeShellCommand(
"dumpsys activity service TouchInteractionService");
Log.d(TAG, "TouchInteractionService: " + dumpsysResult);
} catch (IOException ex) {
}
- mDevice.takeScreenshot(new File(pathname));
+ device.takeScreenshot(new File(pathname));
}
}
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index 204240324e..62fe26d13b 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -19,6 +19,7 @@ import android.app.Activity;
import android.app.Application;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;
+
import androidx.test.InstrumentationRegistry;
import com.android.launcher3.Launcher;
@@ -84,19 +85,27 @@ public class LauncherActivityRule implements TestRule {
}
@Override
- public void onActivityStarted(Activity activity) { }
+ public void onActivityStarted(Activity activity) {
+ if (activity instanceof Launcher) {
+ mActivity.getRotationHelper().forceAllowRotationForTesting(true);
+ }
+ }
@Override
- public void onActivityResumed(Activity activity) { }
+ public void onActivityResumed(Activity activity) {
+ }
@Override
- public void onActivityPaused(Activity activity) { }
+ public void onActivityPaused(Activity activity) {
+ }
@Override
- public void onActivityStopped(Activity activity) { }
+ public void onActivityStopped(Activity activity) {
+ }
@Override
- public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
+ public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+ }
@Override
public void onActivityDestroyed(Activity activity) {
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 9ff354a7eb..f070280ea2 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -120,7 +120,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector));
- final UiObject2 appIcon = mLauncher.getObjectInContainer(appListRecycler,
+ final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
appIconSelector);
return new AppIcon(mLauncher, appIcon);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index bbd2c29e3e..25e6e8c8f7 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -74,7 +74,7 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
flingForward();
}
- mLauncher.getObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
+ mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"dismissed all tasks")) {
return new Workspace(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 82af7b03ac..df80a51fd7 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -16,6 +16,8 @@
package com.android.launcher3.tapl;
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
import android.graphics.Point;
import androidx.test.uiautomator.By;
@@ -23,13 +25,10 @@ import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
-import com.android.launcher3.testing.TestProtocol;
-
/**
* Ancestor for AppIcon and AppMenuItem.
*/
abstract class Launchable {
- private static final int WAIT_TIME_MS = 60000;
protected final LauncherInstrumentation mLauncher;
protected final UiObject2 mObject;
@@ -53,9 +52,12 @@ abstract class Launchable {
private Background launch(BySelector selector) {
LauncherInstrumentation.log("Launchable.launch before click " +
mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
- mLauncher.assertTrue(
- "Launching an app didn't open a new window: " + mObject.getText(),
- mObject.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
+
+ mLauncher.executeAndWaitForEvent(
+ () -> mObject.click(),
+ event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+ "Launching an app didn't open a new window: " + mObject.getText());
+
mLauncher.assertTrue(
"App didn't start: " + selector,
mLauncher.getDevice().wait(Until.hasObject(selector),
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index fe7401ca62..15615fc6b6 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -124,7 +124,7 @@ public final class LauncherInstrumentation {
private static final String APPS_RES_ID = "apps_view";
private static final String OVERVIEW_RES_ID = "overview_panel";
private static final String WIDGETS_RES_ID = "widgets_list_view";
- public static final int WAIT_TIME_MS = 60000;
+ public static final int WAIT_TIME_MS = 10000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static WeakReference sActiveContainer = new WeakReference<>(null);
@@ -178,6 +178,7 @@ public final class LauncherInstrumentation {
PackageManager pm = getContext().getPackageManager();
ProviderInfo pi = pm.resolveContentProvider(
testProviderAuthority, MATCH_ALL | MATCH_DISABLED_COMPONENTS);
+ assertNotNull("Cannot find content provider for " + testProviderAuthority, pi);
ComponentName cn = new ComponentName(pi.packageName, pi.name);
if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
@@ -367,7 +368,7 @@ public final class LauncherInstrumentation {
}
}
- private void assertEquals(String message, String expected, String actual) {
+ void assertEquals(String message, String expected, String actual) {
if (!TextUtils.equals(expected, actual)) {
fail(message + " expected: '" + expected + "' but was: '" + actual + "'");
}
@@ -677,13 +678,6 @@ public final class LauncherInstrumentation {
return object;
}
- @NonNull
- UiObject2 getObjectInContainer(UiObject2 container, BySelector selector) {
- final UiObject2 object = container.findObject(selector);
- assertNotNull("Can't find an object with selector: " + selector, object);
- return object;
- }
-
@NonNull
List getObjectsInContainer(UiObject2 container, String resName) {
return container.findObjects(getLauncherObjectSelector(resName));
@@ -769,8 +763,7 @@ public final class LauncherInstrumentation {
final Bundle parcel = (Bundle) executeAndWaitForEvent(
() -> linearGesture(startX, startY, endX, endY, steps),
event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
- "Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
- + ", " + endX + ", " + endY);
+ "Swipe failed to receive an event for the swipe end");
assertEquals("Swipe switched launcher to a wrong state;",
TestProtocol.stateOrdinalToString(expectedState),
TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD)));
@@ -888,6 +881,7 @@ public final class LauncherInstrumentation {
}
long movePointer(long downTime, long startTime, long duration, Point from, Point to) {
+ log("movePointer: " + from + " to " + to);
final Point point = new Point();
long steps = duration / GESTURE_STEP_MS;
long currentTime = startTime;
@@ -962,4 +956,17 @@ public final class LauncherInstrumentation {
public void disableDebugTracing() {
getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
}
+
+ public int getTotalPssKb() {
+ return getTestInfo(TestProtocol.REQUEST_TOTAL_PSS_KB).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
+ public void produceJavaLeak() {
+ getTestInfo(TestProtocol.REQUEST_JAVA_LEAK);
+ }
+
+ public void produceNativeLeak() {
+ getTestInfo(TestProtocol.REQUEST_NATIVE_LEAK);
+ }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 6e3332260b..91f0fc4c83 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -16,18 +16,16 @@
package com.android.launcher3.tapl;
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
import android.graphics.Rect;
import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
-import com.android.launcher3.testing.TestProtocol;
/**
* A recent task in the overview panel carousel.
*/
public final class OverviewTask {
- private static final long WAIT_TIME_MS = 60000;
private final LauncherInstrumentation mLauncher;
private final UiObject2 mTask;
private final BaseOverview mOverview;
@@ -66,9 +64,11 @@ public final class OverviewTask {
verifyActiveContainer();
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"clicking an overview task")) {
- mLauncher.assertTrue("Launching task didn't open a new window: " +
- mTask.getParent().getContentDescription(),
- mTask.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
+ mLauncher.executeAndWaitForEvent(
+ () -> mTask.click(),
+ event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+ "Launching task didn't open a new window: " +
+ mTask.getParent().getContentDescription());
}
return new Background(mLauncher);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index 399c59d36d..a089a527ef 100644
--- a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
+++ b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
@@ -109,7 +109,7 @@ public class TestHelpers {
DropBoxManager.Entry entry;
StringBuilder errorDetails = new StringBuilder();
while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
- if (errorDetails.length() != 0) errorDetails.append("------------------------------");
+ errorDetails.append("------------------------------\n");
timestamp = entry.getTimeMillis();
errorDetails.append(new Date(timestamp));
errorDetails.append(": ");
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 2495933ac6..7d308afb99 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -19,6 +19,7 @@ package com.android.launcher3.tapl;
import static org.junit.Assert.fail;
import android.graphics.Point;
+import android.graphics.Rect;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
@@ -31,7 +32,8 @@ import com.android.launcher3.ResourceUtils;
* All widgets container.
*/
public final class Widgets extends LauncherInstrumentation.VisibleContainer {
- private static final int FLING_SPEED = 1500;
+ private static final Rect MARGINS = new Rect(100, 100, 100, 100);
+ private static final int FLING_STEPS = 10;
Widgets(LauncherInstrumentation launcher) {
super(launcher);
@@ -46,11 +48,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
"want to fling forward in widgets")) {
LauncherInstrumentation.log("Widgets.flingForward enter");
final UiObject2 widgetsContainer = verifyActiveContainer();
- widgetsContainer.setGestureMargins(0, 0, 0,
- ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
- mLauncher.getResources()) + 1);
- widgetsContainer.fling(Direction.DOWN,
- (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+ mLauncher.scroll(widgetsContainer, Direction.DOWN, 1f, MARGINS, FLING_STEPS);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
verifyActiveContainer();
}
@@ -66,10 +64,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
"want to fling backwards in widgets")) {
LauncherInstrumentation.log("Widgets.flingBackward enter");
final UiObject2 widgetsContainer = verifyActiveContainer();
- widgetsContainer.setGestureMargin(100);
- widgetsContainer.fling(Direction.UP,
- (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
- mLauncher.waitForIdle();
+ mLauncher.scroll(widgetsContainer, Direction.UP, 1f, MARGINS, FLING_STEPS);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
verifyActiveContainer();
}
@@ -82,7 +77,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
return LauncherInstrumentation.ContainerType.WIDGETS;
}
- public Widget getWidget(String label) {
+ public Widget getWidget(String labelText) {
final int margin = ResourceUtils.getNavbarSize(
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
final UiObject2 widgetsContainer = verifyActiveContainer();
@@ -91,17 +86,24 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
final Point displaySize = mLauncher.getRealDisplaySize();
int i = 0;
- final BySelector selector = By.
- clazz("com.android.launcher3.widget.WidgetCell").
- hasDescendant(By.text(label));
+ final BySelector selector = By.clazz("android.widget.TextView").text(labelText);
for (; ; ) {
- final UiObject2 widget = mLauncher.tryWaitForLauncherObject(selector, 300);
- if (widget != null && widget.getVisibleBounds().bottom <= displaySize.y - margin) {
- return new Widget(mLauncher, widget);
+ final UiObject2 label = mLauncher.tryWaitForLauncherObject(selector, 300);
+ if (label != null) {
+ final UiObject2 widget = label.getParent().getParent();
+ mLauncher.assertEquals(
+ "View is not WidgetCell",
+ "com.android.launcher3.widget.WidgetCell",
+ widget.getClassName());
+
+ if (widget.getVisibleBounds().bottom <= displaySize.y - margin) {
+ return new Widget(mLauncher, widget);
+ }
}
+
if (++i > 40) fail("Too many attempts");
- widgetsContainer.scroll(Direction.DOWN, 1f);
+ mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.7f, MARGINS, 50);
}
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 07f8b64433..510ea14091 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -21,6 +21,7 @@ import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static junit.framework.TestCase.assertTrue;
import android.graphics.Point;
+import android.graphics.Rect;
import android.os.SystemClock;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -40,6 +41,7 @@ public final class Workspace extends Home {
private static final float FLING_SPEED =
LauncherInstrumentation.isAvd() ? 1500.0F : 3500.0F;
private static final int DRAG_DURACTION = 2000;
+ private static final int FLING_STEPS = 10;
private final UiObject2 mHotseat;
Workspace(LauncherInstrumentation launcher) {
@@ -108,10 +110,13 @@ public final class Workspace extends Home {
*/
@NonNull
public AppIcon getWorkspaceAppIcon(String appName) {
- return new AppIcon(mLauncher,
- mLauncher.getObjectInContainer(
- verifyActiveContainer(),
- AppIcon.getAppIconSelector(appName, mLauncher)));
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get a workspace icon")) {
+ return new AppIcon(mLauncher,
+ mLauncher.waitForObjectInContainer(
+ verifyActiveContainer(),
+ AppIcon.getAppIconSelector(appName, mLauncher)));
+ }
}
/**
@@ -142,13 +147,13 @@ public final class Workspace extends Home {
@NonNull
public AppIcon getHotseatAppIcon(String appName) {
- return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
+ return new AppIcon(mLauncher, mLauncher.waitForObjectInContainer(
mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
}
@NonNull
public Folder getHotseatFolder(String appName) {
- return new Folder(mLauncher, mLauncher.getObjectInContainer(
+ return new Folder(mLauncher, mLauncher.waitForObjectInContainer(
mHotseat, Folder.getSelector(appName, mLauncher)));
}
@@ -177,9 +182,9 @@ public final class Workspace extends Home {
*/
public void flingForward() {
final UiObject2 workspace = verifyActiveContainer();
- workspace.setGestureMargins(0, 0, mLauncher.getEdgeSensitivityWidth(), 0);
- workspace.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
- mLauncher.waitForIdle();
+ mLauncher.scroll(workspace, Direction.RIGHT, 1f,
+ new Rect(0, 0, mLauncher.getEdgeSensitivityWidth(), 0),
+ FLING_STEPS);
verifyActiveContainer();
}
@@ -189,9 +194,9 @@ public final class Workspace extends Home {
*/
public void flingBackward() {
final UiObject2 workspace = verifyActiveContainer();
- workspace.setGestureMargins(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0);
- workspace.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
- mLauncher.waitForIdle();
+ mLauncher.scroll(workspace, Direction.LEFT, 1f,
+ new Rect(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0),
+ FLING_STEPS);
verifyActiveContainer();
}