Merge "Support for tests that a leak is detected" into ub-launcher3-qt-qpr1-dev
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 371161e..7115943 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -18,16 +18,11 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
-import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
-import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -35,18 +30,17 @@
import android.content.Context;
import android.view.View;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/**
* A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
* {@link RecentsView}.
@@ -156,8 +150,11 @@
return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
RecentsView.CONTENT_ALPHA, values);
case INDEX_RECENTS_TRANSLATE_X_ANIM:
- return new SpringObjectAnimator<>(mLauncher.getOverviewPanel(),
- VIEW_TRANSLATE_X, MIN_VISIBLE_CHANGE_PIXELS, 0.8f, 250, values);
+ return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), VIEW_TRANSLATE_X)
+ .setDampingRatio(0.8f)
+ .setStiffness(250)
+ .setValues(values)
+ .build(mLauncher);
default:
return super.createStateElementAnimation(index, values);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index 085bbc4..1a59770 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -24,7 +24,6 @@
import android.app.prediction.AppTarget;
import android.content.ComponentName;
import android.content.Context;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
@@ -32,6 +31,8 @@
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
@@ -58,7 +59,7 @@
* 4) Maintains the current active client id (for the predictions) and all updates are performed on
* that client id.
*/
-public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver,
+public class PredictionUiStateManager implements StateListener, ItemInfoUpdateReceiver,
OnIDPChangeListener, OnUpdateListener {
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
@@ -153,7 +154,10 @@
public void reapplyItemInfo(ItemInfoWithIcon info) { }
@Override
- public void onGlobalLayout() {
+ public void onStateTransitionStart(LauncherState toState) { }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState state) {
if (mAppsView == null) {
return;
}
@@ -162,7 +166,8 @@
mPendingState = null;
}
if (mPendingState == null) {
- mAppsView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ Launcher.getLauncher(mAppsView.getContext()).getStateManager()
+ .removeStateListener(this);
}
}
@@ -170,9 +175,8 @@
boolean registerListener = mPendingState == null;
mPendingState = state;
if (registerListener) {
- // OnGlobalLayoutListener is called whenever a view in the view tree changes
- // visibility. Add a listener and wait until appsView is invisible again.
- mAppsView.getViewTreeObserver().addOnGlobalLayoutListener(this);
+ // Add a listener and wait until appsView is invisible again.
+ Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 0d29e5d..a3bd348 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -217,6 +217,7 @@
private AnimationFactory mAnimationFactory = (t) -> { };
private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
+ private boolean mLiveTileOverlayAttached = false;
private boolean mWasLauncherAlreadyVisible;
@@ -323,8 +324,7 @@
mRecentsView = activity.getOverviewPanel();
linkRecentsViewScroll();
- mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
- mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
+ addLiveTileOverlay();
mStateCallback.setState(STATE_LAUNCHER_PRESENT);
if (alreadyOnHome) {
@@ -972,7 +972,7 @@
@Override
public void onAnimationStart(Animator animation) {
if (mActivity != null) {
- mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
+ removeLiveTileOverlay();
}
}
@@ -1071,7 +1071,7 @@
mRecentsView.onGestureAnimationEnd();
mActivity.getRootView().setOnApplyWindowInsetsListener(null);
- mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
+ removeLiveTileOverlay();
}
private void endLauncherTransitionController() {
@@ -1201,6 +1201,22 @@
updateFinalShift();
}
+ private synchronized void addLiveTileOverlay() {
+ if (!mLiveTileOverlayAttached) {
+ mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
+ mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
+ mLiveTileOverlayAttached = true;
+ }
+ }
+
+ private synchronized void removeLiveTileOverlay() {
+ if (mLiveTileOverlayAttached) {
+ mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
+ mRecentsView.setLiveTileOverlay(null);
+ mLiveTileOverlayAttached = false;
+ }
+ }
+
public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, float expectedAlpha) {
if (!isNotInRecents(app)) {
return 0;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 1bf77f5..3e1d61a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -825,7 +825,7 @@
*/
public void onSwipeUpAnimationSuccess() {
if (getRunningTaskView() != null) {
- float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get()
+ float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlay != null
? mLiveTileOverlay.cancelIconAnimation()
: 0f;
animateUpRunningTaskIconScale(startProgress);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d79f5d5..a041489 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -20,6 +20,7 @@
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.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 @@
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<Boolean> 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/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 0c1303b..0c12c60 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,14 +32,14 @@
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;
@@ -164,6 +166,13 @@
}
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/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
new file mode 100644
index 0000000..0f34c1e
--- /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<T> extends FloatProperty<T> {
+
+ private final T mTarget;
+ private final FloatProperty<T> 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<T> property) {
+ super("dynamic-spring-property");
+ mTarget = target;
+ mProperty = property;
+
+ mStartValue = mProperty.get(target);
+ }
+
+ public SpringAnimationBuilder<T> setEndValue(float value) {
+ mEndValue = value;
+ return this;
+ }
+
+ public SpringAnimationBuilder<T> setStartValue(float value) {
+ mStartValue = value;
+ return this;
+ }
+
+ public SpringAnimationBuilder<T> setValues(float... values) {
+ if (values.length > 1) {
+ mStartValue = values[0];
+ mEndValue = values[values.length - 1];
+ } else {
+ mEndValue = values[0];
+ }
+ return this;
+ }
+
+ public SpringAnimationBuilder<T> 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<T> 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<T> 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<T> 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/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 11cb1f8..55df98b 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -19,6 +19,7 @@
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;
@@ -29,6 +30,7 @@
import androidx.annotation.NonNull;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.PackageUserKey;
public abstract class PackageInstallerCompat {
@@ -52,19 +54,19 @@
}
}
- public static UserHandle getUserHandle(PackageInstaller.SessionInfo info) {
+ 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<String, PackageInstaller.SessionInfo> updateAndGetActiveSessionCache();
+ public abstract HashMap<PackageUserKey, SessionInfo> 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();
@@ -75,7 +77,7 @@
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, "");
@@ -91,7 +93,7 @@
this.user = user;
}
- public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) {
+ public static PackageInstallInfo fromInstallingState(SessionInfo info) {
return new PackageInstallInfo(info);
}
@@ -101,7 +103,7 @@
}
- public abstract List<PackageInstaller.SessionInfo> getAllVerifiedSessions();
+ public abstract List<SessionInfo> getAllVerifiedSessions();
/**
* Returns true if a promise icon was already added to the home screen for {@param sessionId}.
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 48ac05b..879d963 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -90,12 +90,13 @@
}
@Override
- public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
- HashMap<String, SessionInfo> activePackages = new HashMap<>();
+ public HashMap<PackageUserKey, SessionInfo> updateAndGetActiveSessionCache() {
+ HashMap<PackageUserKey, SessionInfo> activePackages = new HashMap<>();
for (SessionInfo info : getAllVerifiedSessions()) {
addSessionInfoToCache(info, getUserHandle(info));
if (info.getAppPackageName() != null) {
- activePackages.put(info.getAppPackageName(), info);
+ activePackages.put(new PackageUserKey(info.getAppPackageName(),
+ getUserHandle(info)), info);
mActiveSessions.put(info.getSessionId(),
new PackageUserKey(info.getAppPackageName(), getUserHandle(info)));
}
@@ -141,6 +142,8 @@
* - 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()
@@ -149,7 +152,9 @@
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
&& sessionInfo.getAppIcon() != null
&& !TextUtils.isEmpty(sessionInfo.getAppLabel())
- && !mPromiseIconIds.contains(sessionInfo.getSessionId())) {
+ && !mPromiseIconIds.contains(sessionInfo.getSessionId())
+ && mLauncherApps.getApplicationInfo(sessionInfo.getAppPackageName(), 0,
+ getUserHandle(sessionInfo)) == null) {
SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
mPromiseIconIds.add(sessionInfo.getSessionId());
updatePromiseIconPrefs();
@@ -184,12 +189,14 @@
sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
packageName, key.mUser));
- if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
+ if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+ && mPromiseIconIds.contains(sessionId)) {
LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
if (appState != null) {
- LauncherModel model = appState.getModel();
- model.onPackageRemoved(packageName, key.mUser);
+ appState.getModel().onSessionFailure(packageName, key.mUser);
}
+ // If it is successful, the id is removed in the the package added flow.
+ removePromiseIconId(sessionId);
}
}
}
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index 1149b55..a0b7177 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.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.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 @@
private final MultiHashMap<String, String> mPackagesForInstaller;
- public FirstScreenBroadcast(HashMap<String, SessionInfo> sessionInfoForPackage) {
+ public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage);
}
@@ -69,11 +72,13 @@
* of packages with active sessions for that installer.
*/
private MultiHashMap<String, String> getPackagesForInstaller(
- HashMap<String, SessionInfo> sessionInfoForPackage) {
+ HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
- for (Map.Entry<String, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
- packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
- entry.getKey());
+ for (Map.Entry<PackageUserKey, SessionInfo> 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 faecc06..783e908 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.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 @@
.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/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4b01b5e..18d17a5 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -19,6 +19,7 @@
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;
@@ -72,6 +73,7 @@
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;
@@ -281,8 +283,9 @@
synchronized (mBgDataModel) {
mBgDataModel.clear();
- final HashMap<String, SessionInfo> installingPkgs =
+ final HashMap<PackageUserKey, SessionInfo> installingPkgs =
mPackageInstaller.updateAndGetActiveSessionCache();
+ final PackageUserKey tempPackageKey = new PackageUserKey(null, null);
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
@@ -419,9 +422,10 @@
// 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,
diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java
index 8dc45f5..aa11968 100644
--- a/src/com/android/launcher3/util/PackageUserKey.java
+++ b/src/com/android/launcher3/util/PackageUserKey.java
@@ -31,7 +31,7 @@
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});
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index 792d69f..af99713 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -23,37 +23,57 @@
import com.android.launcher3.shortcuts.ShortcutKey;
public class ShortcutUtil {
- public static boolean supportsShortcuts(ItemInfo info) {
- return isActive(info) && (isApp(info) || isPinnedShortcut(info));
- }
+ /**
+ * Returns true when we should show shortcut menu for the item.
+ */
+ public static boolean supportsShortcuts(ItemInfo info) {
+ return isActive(info) && (isApp(info) || isPinnedShortcut(info));
+ }
- public static boolean supportsDeepShortcuts(ItemInfo info) {
- return isActive(info) && isApp(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);
+ }
- public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
- return isActive(info) && isPinnedShortcut(info) ?
- ShortcutKey.fromItemInfo(info).getId() : null;
- }
+ /**
+ * 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;
+ }
- public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
- return isActive(info) && isPinnedShortcut(info) ?
- ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
- }
+ /**
+ * 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;
+ }
- private static boolean isActive(ItemInfo info) {
- boolean isLoading = info instanceof WorkspaceItemInfo
- && ((WorkspaceItemInfo) info).hasPromiseIconUi();
- return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
- }
+ /**
+ * 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 isApp(ItemInfo info) {
- return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- }
+ 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 isPinnedShortcut(ItemInfo info) {
- return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
- && info.container != ItemInfo.NO_ID
- && info instanceof WorkspaceItemInfo;
- }
+ 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/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 7f427b3..3f35a3a 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -70,12 +70,14 @@
@Test
@PortraitLandscape
+ @org.junit.Ignore
public void testWidgetConfig() throws Throwable {
runTest(true);
}
@Test
@PortraitLandscape
+ @org.junit.Ignore
public void testConfigCancelled() throws Throwable {
runTest(false);
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 1c99f31..1edce22 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 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;
@@ -42,6 +46,7 @@
@Test
@PortraitLandscape
+ @org.junit.Ignore
public void testDragIcon() throws Throwable {
clearHomescreen();
mDevice.pressHome();
@@ -59,5 +64,10 @@
(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 3a7df64..e6348d9 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.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.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 @@
int count = 0;
String pkg = invalidPackage;
- Set<String> activePackage = getOnUiThread(() ->
- PackageInstallerCompat.getInstance(mTargetContext)
- .updateAndGetActiveSessionCache().keySet());
+ Set<String> activePackage = getOnUiThread(() -> {
+ Set<String> 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/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 208a223..7d308af 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -103,7 +103,7 @@
}
if (++i > 40) fail("Too many attempts");
- mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.8f, MARGINS, 50);
+ mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.7f, MARGINS, 50);
}
}
}