Merge "Revert "cache shourtcut image"" into ub-launcher3-qt-future-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/src/com/android/launcher3/uioverrides/TogglableFlag.java b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
index e425088..853a1c6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -16,17 +16,34 @@
 
 package com.android.launcher3.uioverrides;
 
+import android.content.Context;
 import android.provider.DeviceConfig;
 import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
 
 public class TogglableFlag extends BaseTogglableFlag {
+    public static final String NAMESPACE_LAUNCHER = "launcher";
+    public static final String TAG = "TogglableFlag";
 
     public TogglableFlag(String key, boolean defaultValue, String description) {
         super(key, defaultValue, description);
     }
 
     @Override
-    public boolean getInitialValue(boolean value) {
-        return DeviceConfig.getBoolean("launcher", getKey(), value);
+    public boolean getOverridenDefaultValue(boolean value) {
+        return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, getKey(), value);
+    }
+
+    @Override
+    public void addChangeListener(Context context, Runnable r) {
+        DeviceConfig.addOnPropertiesChangedListener(
+            NAMESPACE_LAUNCHER,
+            context.getMainExecutor(),
+            (properties) -> {
+                if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
+                    return;
+                }
+                initialize(context);
+                r.run();
+            });
     }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2df4adc..d667e8c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -158,6 +158,9 @@
 import java.util.List;
 import java.util.function.Predicate;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
 /**
  * Default launcher application.
  */
@@ -209,10 +212,9 @@
     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;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 2a801d6..b4a2216 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -94,6 +94,7 @@
         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 da9617a..2467ace 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
@@ -52,6 +53,10 @@
 import com.android.launcher3.model.ShortcutsChangedTask;
 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;
 import com.android.launcher3.util.Thunk;
@@ -170,6 +175,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/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index c423d13..2e22cba 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -139,15 +139,20 @@
      * Add a promise app icon to the workspace iff:
      * - The settings for it are enabled
      * - The user installed the app
-     * - There is a provided app icon (For apps with no launching activity, no icon is provided).
+     * - 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)
-                && sessionInfo != null
+                && verify(sessionInfo) != null
                 && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
                 && sessionInfo.getAppIcon() != null
-                && !mPromiseIconIds.contains(sessionInfo.getSessionId())) {
+                && !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();
@@ -182,12 +187,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/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 1523278..9455889 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -147,6 +147,8 @@
 
     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;
@@ -156,8 +158,9 @@
                 boolean defaultValue,
                 String description) {
             this.key = checkNotNull(key);
-            this.currentValue = this.defaultValue = getInitialValue(defaultValue);
+            this.currentValue = this.defaultValue = defaultValue;
             this.description = checkNotNull(description);
+
             synchronized (sLock) {
                 sFlags.add((TogglableFlag)this);
             }
@@ -173,16 +176,18 @@
             return key;
         }
 
-        void initialize(Context context) {
-            currentValue = getFromStorage(context, defaultValue);
+        protected void initialize(Context context) {
+            currentValue = getFromStorage(context, getDefaultValue());
         }
 
-        protected abstract boolean getInitialValue(boolean value);
+        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();
@@ -191,11 +196,11 @@
 
         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. */
@@ -212,6 +217,8 @@
             return "TogglableFlag{"
                     + "key=" + key + ", "
                     + "defaultValue=" + defaultValue + ", "
+                    + "overriddenDefaultValue=" + getOverridenDefaultValue(defaultValue) + ", "
+                    + "currentValue=" + currentValue + ", "
                     + "description=" + description
                     + "}";
         }
@@ -224,7 +231,7 @@
             if (o instanceof TogglableFlag) {
                 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;
@@ -236,7 +243,7 @@
             h$ *= 1000003;
             h$ ^= key.hashCode();
             h$ *= 1000003;
-            h$ ^= defaultValue ? 1231 : 1237;
+            h$ ^= getDefaultValue() ? 1231 : 1237;
             h$ *= 1000003;
             h$ ^= description.hashCode();
             return h$;
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 5fb833c..a8ff232 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -43,6 +43,7 @@
 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;
@@ -236,7 +237,8 @@
 
     @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/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/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
index e875a3c..60f12d8 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.uioverrides;
 
+import android.content.Context;
 import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
 
 public class TogglableFlag extends BaseTogglableFlag {
@@ -25,7 +26,10 @@
     }
 
     @Override
-    public boolean getInitialValue(boolean value) {
+    public boolean getOverridenDefaultValue(boolean value) {
         return value;
     }
+
+    @Override
+    public void addChangeListener(Context context, Runnable r) { }
 }
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 0000000..efbd9c9
--- /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/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 5c38c8d..3f35a3a 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -41,6 +41,7 @@
 import com.android.launcher3.util.rule.ShellCommandRule;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -69,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 b9abc2e..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,10 +25,12 @@
 
 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;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,6 +46,7 @@
 
     @Test
     @PortraitLandscape
+    @org.junit.Ignore
     public void testDragIcon() throws Throwable {
         clearHomescreen();
         mDevice.pressHome();
@@ -58,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/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 14f2c85..c6e7dac 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -368,7 +368,7 @@
         }
     }
 
-    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 + "'");
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index f3295d0..7d308af 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -77,7 +77,7 @@
         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();
@@ -86,17 +86,24 @@
         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");
-            mLauncher.scroll(widgetsContainer, Direction.DOWN, 1f, MARGINS, 50);
+            mLauncher.scroll(widgetsContainer, Direction.DOWN, 0.7f, MARGINS, 50);
         }
     }
 }