Merge "Fixing unrelated icons getting removed during sesison failure"
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
index 9ce196e..3343cf5 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
@@ -23,6 +23,8 @@
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
+import com.android.app.search.LayoutType;
+import com.android.app.search.ResultType;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
@@ -78,10 +80,10 @@
         SearchTargetHandler
                 payloadResultView =
                 (SearchTargetHandler) holder.itemView;
-        if (FeatureFlags.SEARCH_TARGET_LEGACY.get()) {
+        if (!FeatureFlags.USE_SEARCH_API.get()) {
             payloadResultView.applySearchTarget(item.getSearchTargetLegacy());
         } else {
-            payloadResultView.applySearchTarget(item.getSearchTarget());
+            payloadResultView.applySearchTarget(item.getSearchTarget(), item.getInlineItems());
         }
     }
 
@@ -123,9 +125,24 @@
      * Returns -1 if viewType is not found
      */
     public int getViewTypeForSearchTarget(SearchTarget t) {
-        //TODO: Replace with values from :SearchUi
-        if (t.getResultType() == 1 && t.getLayoutType().equals("icon")) {
-            return VIEW_TYPE_SEARCH_ICON;
+        if (t.getLayoutType().equals(LayoutType.TEXT_HEADER)) {
+            return VIEW_TYPE_SEARCH_CORPUS_TITLE;
+        }
+        switch (t.getResultType()) {
+            case ResultType.APPLICATION:
+                if (t.getLayoutType().equals(LayoutType.ICON_SINGLE_VERTICAL_TEXT)) {
+                    return VIEW_TYPE_SEARCH_ICON;
+                }
+                break;
+            case ResultType.SETTING:
+                if (t.getLayoutType().equals(LayoutType.ICON_SLICE)) {
+                    return VIEW_TYPE_SEARCH_SLICE;
+                }
+                return VIEW_TYPE_SEARCH_ROW;
+            case ResultType.SHORTCUT:
+                return VIEW_TYPE_SEARCH_ICON_ROW;
+            case ResultType.PLAY:
+                return VIEW_TYPE_SEARCH_ROW_WITH_BUTTON;
         }
         return -1;
     }
diff --git a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
index 258d977..65ac3f9 100644
--- a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
+++ b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
@@ -32,12 +32,16 @@
 import com.android.launcher3.allapps.AllAppsGridAdapter;
 import com.android.systemui.plugins.shared.SearchTargetLegacy;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Extension of AdapterItem that contains an extra payload specific to item
  */
 public class SearchAdapterItem extends AllAppsGridAdapter.AdapterItem {
     private SearchTargetLegacy mSearchTargetLegacy;
     private SearchTarget mSearchTarget;
+    private List<SearchTarget> mInlineItems = new ArrayList<>();
 
 
     private static final int AVAILABLE_FOR_ACCESSIBILITY = VIEW_TYPE_SEARCH_ROW_WITH_BUTTON
@@ -65,6 +69,9 @@
         return mSearchTarget;
     }
 
+    public List<SearchTarget> getInlineItems() {
+        return mInlineItems;
+    }
     @Override
     protected boolean isCountedForAccessibility() {
         return (AVAILABLE_FOR_ACCESSIBILITY & viewType) == viewType;
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
index e4d737c..d5fe0e8 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
@@ -31,10 +31,12 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.app.search.ResultType;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.AppInfo;
@@ -46,6 +48,7 @@
 import com.android.systemui.plugins.shared.SearchTargetEventLegacy;
 import com.android.systemui.plugins.shared.SearchTargetLegacy;
 
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -128,10 +131,27 @@
         }
     }
 
+    /**
+     * Applies {@link SearchTarget} to view. registers a consumer after a corresponding
+     * {@link ItemInfoWithIcon} is created
+     */
+    public void applySearchTarget(SearchTarget searchTarget, List<SearchTarget> inlineItems,
+            Consumer<ItemInfoWithIcon> cb) {
+        mOnItemInfoChanged = cb;
+        applySearchTarget(searchTarget, inlineItems);
+    }
+
     @Override
-    public void applySearchTarget(SearchTarget searchTarget) {
-        prepareUsingApp(new ComponentName(searchTarget.getPackageName(),
-                searchTarget.getExtras().getString("class")), searchTarget.getUserHandle());
+    public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+        switch (parentTarget.getResultType()) {
+            case ResultType.APPLICATION:
+                prepareUsingApp(new ComponentName(parentTarget.getPackageName(),
+                        parentTarget.getExtras().getString("class")), parentTarget.getUserHandle());
+                break;
+            case ResultType.SHORTCUT:
+                prepareUsingShortcutInfo(parentTarget.getShortcutInfo());
+                break;
+        }
     }
 
     private void prepareUsingApp(ComponentName componentName, UserHandle userHandle) {
@@ -185,7 +205,9 @@
     @Override
     public void handleSelection(int eventType) {
         mLauncher.getItemOnClickListener().onClick(this);
-        reportEvent(eventType);
+        if (!FeatureFlags.USE_SEARCH_API.get()) {
+            reportEvent(eventType);
+        }
     }
 
     private void reportEvent(int eventType) {
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
index 8c491d2..80d543a 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.app.search.SearchTarget;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ShortcutInfo;
@@ -32,6 +33,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.app.search.ResultType;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
@@ -112,6 +114,16 @@
     }
 
     @Override
+    public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+        mResultIcon.applySearchTarget(parentTarget, children, this);
+        if (parentTarget.getResultType() == ResultType.SHORTCUT) {
+            ShortcutInfo shortcutInfo = parentTarget.getShortcutInfo();
+            setProviderDetails(new ComponentName(shortcutInfo.getPackage(), ""),
+                    shortcutInfo.getUserHandle());
+        }
+    }
+
+    @Override
     public void applySearchTarget(SearchTargetLegacy searchTarget) {
         mSearchTarget = searchTarget;
         mResultIcon.applySearchTarget(searchTarget, this);
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultPlayItem.java b/quickstep/src/com/android/launcher3/search/SearchResultPlayItem.java
index 3bb821f..840bde9 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultPlayItem.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultPlayItem.java
@@ -17,6 +17,8 @@
 
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
+import android.app.search.SearchAction;
+import android.app.search.SearchTarget;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -27,8 +29,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.os.Bundle;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -43,12 +43,11 @@
 import com.android.launcher3.R;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.util.Themes;
-import com.android.systemui.plugins.shared.SearchTargetEventLegacy;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
 
 import java.io.IOException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.util.List;
 
 /**
  * A View representing a PlayStore item.
@@ -67,9 +66,8 @@
     private TextView[] mDetailViews = new TextView[3];
     private Button mPreviewButton;
     private String mPackageName;
-    private boolean mIsInstantGame;
-
-    private SearchTargetLegacy mSearchTarget;
+    private Intent mIntent;
+    private Intent mSecondaryIntent;
 
 
     public SearchResultPlayItem(Context context) {
@@ -93,7 +91,7 @@
         mIconView = findViewById(R.id.icon);
         mTitleView = findViewById(R.id.title_view);
         mPreviewButton = findViewById(R.id.try_button);
-        mPreviewButton.setOnClickListener(view -> launchInstantGame());
+        mPreviewButton.setOnClickListener(view -> launchIntent(mSecondaryIntent));
         mDetailViews[0] = findViewById(R.id.detail_0);
         mDetailViews[1] = findViewById(R.id.detail_1);
         mDetailViews[2] = findViewById(R.id.detail_2);
@@ -101,9 +99,59 @@
         ViewGroup.LayoutParams iconParams = mIconView.getLayoutParams();
         iconParams.height = mDeviceProfile.allAppsIconSizePx;
         iconParams.width = mDeviceProfile.allAppsIconSizePx;
-        setOnClickListener(view -> handleSelection(SearchTargetEventLegacy.SELECT));
+        setOnClickListener(view -> launchIntent(mIntent));
     }
 
+    private void showIfNecessary(TextView textView, @Nullable String string) {
+        if (string == null || string.isEmpty()) {
+            textView.setVisibility(GONE);
+        } else {
+            textView.setText(string);
+            textView.setVisibility(VISIBLE);
+        }
+    }
+
+    private void launchIntent(Intent intent) {
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        getContext().startActivity(intent);
+    }
+
+    @Override
+    public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+        if (parentTarget.getPackageName().equals(mPackageName)) {
+            return;
+        }
+        mPackageName = parentTarget.getPackageName();
+        SearchAction action = parentTarget.getSearchAction();
+        mTitleView.setText(action.getTitle());
+        showIfNecessary(mDetailViews[0], action.getSubtitle().toString());
+        mIntent = action.getIntent();
+
+        mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
+        loadIcon(action.getIcon().getUri().toString());
+
+        mSecondaryIntent = children.size() == 1 ? children.get(0).getSearchAction().getIntent()
+                : null;
+        mPreviewButton.setVisibility(mSecondaryIntent == null ? GONE : VISIBLE);
+    }
+
+    private void loadIcon(String iconUrl) {
+        UI_HELPER_EXECUTOR.execute(() -> {
+            try {
+                URL url = new URL(iconUrl);
+                URLConnection con = url.openConnection();
+                con.addRequestProperty("Cache-Control", "max-age: 0");
+                con.setUseCaches(true);
+                Bitmap bitmap = BitmapFactory.decodeStream(con.getInputStream());
+                BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), getRoundedBitmap(
+                        Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx,
+                                mDeviceProfile.allAppsIconSizePx, false)));
+                mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        });
+    }
 
     private Bitmap getRoundedBitmap(Bitmap bitmap) {
         final int iconSize = bitmap.getWidth();
@@ -124,80 +172,4 @@
         });
         return output;
     }
-
-
-    @Override
-    public void applySearchTarget(SearchTargetLegacy searchTarget) {
-        mSearchTarget = searchTarget;
-        Bundle bundle = searchTarget.getExtras();
-        SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
-        if (bundle.getString("package", "").equals(mPackageName)) {
-            return;
-        }
-        mIsInstantGame = bundle.getBoolean("instant_game", false);
-        mPackageName = bundle.getString("package");
-        mPreviewButton.setVisibility(mIsInstantGame ? VISIBLE : GONE);
-        mTitleView.setText(bundle.getString("title"));
-//        TODO: Should use a generic type to get values b/165320033
-        showIfNecessary(mDetailViews[0], bundle.getString("price"));
-        showIfNecessary(mDetailViews[1], bundle.getString("rating"));
-
-        mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
-        UI_HELPER_EXECUTOR.execute(() -> {
-            try {
-                URL url = new URL(bundle.getString("icon_url"));
-                URLConnection con = url.openConnection();
-//                TODO: monitor memory and investigate if it's better to use glide
-                con.addRequestProperty("Cache-Control", "max-age: 0");
-                con.setUseCaches(true);
-                Bitmap bitmap = BitmapFactory.decodeStream(con.getInputStream());
-                BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), getRoundedBitmap(
-                        Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx,
-                                mDeviceProfile.allAppsIconSizePx, false)));
-                mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        });
-    }
-
-    private void showIfNecessary(TextView textView, @Nullable String string) {
-        if (string == null || string.isEmpty()) {
-            textView.setVisibility(GONE);
-        } else {
-            textView.setText(string);
-            textView.setVisibility(VISIBLE);
-        }
-    }
-
-    @Override
-    public void handleSelection(int eventType) {
-        if (mPackageName == null) return;
-        Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(
-                "https://play.google.com/store/apps/details?id="
-                        + mPackageName));
-        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getContext().startActivity(i);
-        logSearchEvent(eventType);
-    }
-
-    private void launchInstantGame() {
-        if (!mIsInstantGame) return;
-        Intent intent = new Intent(Intent.ACTION_VIEW);
-        String referrer = "Pixel_Launcher";
-        String id = mPackageName;
-        String deepLinkUrl = "market://details?id=" + id + "&launch=true&referrer=" + referrer;
-        intent.setPackage("com.android.vending");
-        intent.setData(Uri.parse(deepLinkUrl));
-        intent.putExtra("overlay", true);
-        intent.putExtra("callerId", getContext().getPackageName());
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getContext().startActivity(intent);
-        logSearchEvent(SearchTargetEventLegacy.CHILD_SELECT);
-    }
-
-    private void logSearchEvent(int eventType) {
-        SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
-                new SearchTargetEventLegacy.Builder(mSearchTarget, eventType).build());
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultSettingsSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultSettingsSlice.java
index 80ad305..bf50b67 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultSettingsSlice.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultSettingsSlice.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.search;
 
+import android.app.search.SearchTarget;
 import android.content.Context;
 import android.net.Uri;
 import android.util.AttributeSet;
@@ -35,6 +36,8 @@
 import com.android.systemui.plugins.shared.SearchTargetEventLegacy;
 import com.android.systemui.plugins.shared.SearchTargetLegacy;
 
+import java.util.List;
+
 /**
  * A slice view wrapper with settings app icon at start
  */
@@ -89,6 +92,18 @@
     }
 
     @Override
+    public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+        reset();
+        try {
+            mSliceLiveData = mLauncher.getLiveSearchManager().getSliceForUri(
+                    parentTarget.getSliceUri());
+            mSliceLiveData.observe(mLauncher, mSliceView);
+        } catch (Exception ex) {
+            Log.e(TAG, "unable to bind slice", ex);
+        }
+    }
+
+    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mSliceView.setOnSliceActionListener(this);
diff --git a/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java b/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
index eb40938..ccc38db 100644
--- a/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
+++ b/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.search;
 
+import android.app.search.SearchTarget;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.widget.TextView;
@@ -23,6 +24,8 @@
 
 import com.android.systemui.plugins.shared.SearchTargetLegacy;
 
+import java.util.List;
+
 /**
  * Header text view that shows a title for a given section in All apps search
  */
@@ -53,4 +56,10 @@
             setVisibility(INVISIBLE);
         }
     }
+
+    @Override
+    public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+        setText(parentTarget.getSearchAction().getTitle());
+        setVisibility(VISIBLE);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java b/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
new file mode 100644
index 0000000..6585213
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.search;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.app.search.Query;
+import android.app.search.SearchContext;
+import android.app.search.SearchSession;
+import android.app.search.SearchTarget;
+import android.app.search.SearchUiManager;
+import android.content.Context;
+import android.os.CancellationSignal;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.app.search.ResultType;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsSectionDecorator;
+import com.android.launcher3.allapps.search.SearchPipeline;
+import com.android.launcher3.allapps.search.SearchSectionInfo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Search pipeline utilizing {@link android.app.search.SearchUiManager}
+ */
+public class SearchServicePipeline implements SearchPipeline {
+    private static final int SUPPORTED_RESULT_TYPES =
+            ResultType.APPLICATION | ResultType.SHORTCUT | ResultType.PLAY | ResultType.PEOPLE
+                    | ResultType.SETTING;
+    private static final int REQUEST_TIMEOUT = 200;
+    private static final String TAG = "SearchServicePipeline";
+
+
+    private final Context mContext;
+    private final SearchSession mSession;
+    private final DeviceSearchAdapterProvider mAdapterProvider;
+
+    private boolean mCanceled = false;
+
+
+    public SearchServicePipeline(Context context, DeviceSearchAdapterProvider adapterProvider) {
+        mContext = context;
+        mAdapterProvider = adapterProvider;
+        SearchUiManager manager = context.getSystemService(SearchUiManager.class);
+        mSession = manager.createSearchSession(
+                new SearchContext(SUPPORTED_RESULT_TYPES, REQUEST_TIMEOUT, null));
+    }
+
+    @Override
+    public void query(String input, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
+            CancellationSignal cancellationSignal) {
+        mCanceled = false;
+        Query query = new Query(input, System.currentTimeMillis(), null);
+        mSession.query(query, UI_HELPER_EXECUTOR, items -> {
+            if (!mCanceled) {
+                callback.accept(this.onResult(items));
+            }
+            Log.w(TAG, "Ignoring results due to cancel signal");
+        });
+    }
+
+    /**
+     * Given A list of search Targets, pairs a group of search targets to a AdapterItem that can
+     * be inflated in AllAppsRecyclerView
+     */
+    private ArrayList<AllAppsGridAdapter.AdapterItem> onResult(List<SearchTarget> searchTargets) {
+        HashMap<String, SearchAdapterItem> adapterMap = new LinkedHashMap<>();
+        List<SearchTarget> unmappedChildren = new ArrayList<>();
+        SearchSectionInfo section = new SearchSectionInfo();
+        section.setDecorationHandler(
+                new AllAppsSectionDecorator.SectionDecorationHandler(mContext, true));
+        for (SearchTarget target : searchTargets) {
+            if (!TextUtils.isEmpty(target.getParentId())) {
+                if (!addChildToParent(target, adapterMap)) {
+                    unmappedChildren.add(target);
+                }
+                continue;
+            }
+            int viewType = mAdapterProvider.getViewTypeForSearchTarget(target);
+            if (viewType != -1) {
+                SearchAdapterItem adapterItem = new SearchAdapterItem(target, viewType);
+                adapterItem.searchSectionInfo = section;
+                adapterMap.put(target.getId(), adapterItem);
+            }
+        }
+        for (SearchTarget s : unmappedChildren) {
+            if (!addChildToParent(s, adapterMap)) {
+                Log.w(TAG,
+                        "Unable to pair child " + s.getId() + " to parent " + s.getParentId());
+            }
+        }
+        return new ArrayList<>(adapterMap.values());
+    }
+
+    /**
+     * Adds a child SearchTarget to a collection of searchTarget children with a shared parentId.
+     * Returns false if no parent searchTarget with id=$parentId does not exists.
+     */
+    private boolean addChildToParent(SearchTarget target, HashMap<String, SearchAdapterItem> map) {
+        if (!map.containsKey(target.getParentId())) return false;
+        map.get(target.getParentId()).getInlineItems().add(target);
+        return true;
+    }
+
+    /**
+     * Unregister callbacks and destroy search session
+     */
+    public void destroy() {
+        mSession.destroy();
+    }
+
+    /**
+     * Cancels current ongoing search request.
+     */
+    public void cancel() {
+        mCanceled = true;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/search/SearchSettingsRowView.java b/quickstep/src/com/android/launcher3/search/SearchSettingsRowView.java
index 8306e3b..6fc0046 100644
--- a/quickstep/src/com/android/launcher3/search/SearchSettingsRowView.java
+++ b/quickstep/src/com/android/launcher3/search/SearchSettingsRowView.java
@@ -19,6 +19,8 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.app.search.SearchAction;
+import android.app.search.SearchTarget;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -97,6 +99,14 @@
         SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
     }
 
+    @Override
+    public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+        SearchAction action = parentTarget.getSearchAction();
+        mIconView.setContentDescription(action.getTitle());
+        showIfAvailable(mTitleView, action.getTitle().toString());
+        showIfAvailable(mBreadcrumbsView, action.getSubtitle().toString());
+    }
+
     private void showIfAvailable(TextView view, @Nullable String string) {
         if (TextUtils.isEmpty(string)) {
             view.setVisibility(GONE);
diff --git a/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java b/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
index 9ff057f..e72578d 100644
--- a/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
+++ b/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
@@ -20,6 +20,8 @@
 
 import com.android.systemui.plugins.shared.SearchTargetLegacy;
 
+import java.util.List;
+
 /**
  * An interface for supporting dynamic search results
  */
@@ -28,13 +30,14 @@
     /**
      * Update view using values from {@link SearchTargetLegacy}
      */
-    void applySearchTarget(SearchTargetLegacy searchTarget);
+    default void applySearchTarget(SearchTargetLegacy searchTarget) {
+    }
+
 
     /**
      * Update view using values from {@link SearchTargetLegacy}
      */
-    default void applySearchTarget(SearchTarget searchTarget){
-
+    default void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 3be1ced..4f62d2f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -220,6 +220,7 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
+        getAppsView().getSearchUiManager().destroy();
         if (mHotseatPredictionController != null) {
             mHotseatPredictionController.destroy();
             mHotseatPredictionController = null;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 74b56e9..4301377 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -24,6 +24,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
@@ -383,6 +384,7 @@
      */
     public boolean canStartSystemGesture() {
         boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+                || (mSystemUiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
                 || mRotationTouchHelper.isTaskListFrozen();
         return canStartWithNavHidden
                 && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f281296..2f2b566 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -72,7 +72,6 @@
 import android.text.TextPaint;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
-import android.util.Property;
 import android.util.SparseBooleanArray;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
@@ -1515,7 +1514,9 @@
                 }
                 int scrollDiff = newScroll[i] - oldScroll[i] + offset;
                 if (scrollDiff != 0) {
-                    Property translationProperty = mOrientationHandler.getPrimaryViewTranslate();
+                    FloatProperty translationProperty = child instanceof TaskView
+                            ? ((TaskView) child).getPrimaryFillDismissGapTranslationProperty()
+                            : mOrientationHandler.getPrimaryViewTranslate();
 
                     ResourceProvider rp = DynamicResource.provider(mActivity);
                     SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END)
@@ -1927,7 +1928,11 @@
                             ? modalLeftOffsetSize
                             : modalRightOffsetSize;
             float totalTranslation = translation + modalTranslation;
-            mOrientationHandler.getPrimaryViewTranslate().set(getChildAt(i),
+            View child = getChildAt(i);
+            FloatProperty translationProperty = child instanceof TaskView
+                    ? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
+                    : mOrientationHandler.getPrimaryViewTranslate();
+            translationProperty.set(child,
                     totalTranslation * mOrientationHandler.getPrimaryTranslationDirectionFactor());
         }
         updateCurveProperties();
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index d94e623..b791d29 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -152,6 +152,58 @@
                 }
             };
 
+    private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_X =
+            new FloatProperty<TaskView>("fillDismissGapTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setFillDismissGapTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mFillDismissGapTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_Y =
+            new FloatProperty<TaskView>("fillDismissGapTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setFillDismissGapTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mFillDismissGapTranslationY;
+                }
+            };
+
+    private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_X =
+            new FloatProperty<TaskView>("taskOffsetTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setTaskOffsetTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mTaskOffsetTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_Y =
+            new FloatProperty<TaskView>("taskOffsetTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setTaskOffsetTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mTaskOffsetTranslationY;
+                }
+            };
+
     private final OnAttachStateChangeListener mTaskMenuStateListener =
             new OnAttachStateChangeListener() {
                 @Override
@@ -179,6 +231,13 @@
     private final FullscreenDrawParams mCurrentFullscreenParams;
     private final StatefulActivity mActivity;
 
+    // Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
+    // TODO: We should do this for secondary translation properties as well.
+    private float mFillDismissGapTranslationX;
+    private float mFillDismissGapTranslationY;
+    private float mTaskOffsetTranslationX;
+    private float mTaskOffsetTranslationY;
+
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
     private float mFocusTransitionProgress = 1;
@@ -601,6 +660,8 @@
 
     protected void resetViewTransforms() {
         setCurveScale(1);
+        mFillDismissGapTranslationX = mTaskOffsetTranslationX = 0f;
+        mFillDismissGapTranslationY = mTaskOffsetTranslationY = 0f;
         setTranslationX(0f);
         setTranslationY(0f);
         setTranslationZ(0);
@@ -745,6 +806,44 @@
         return mCurveScale;
     }
 
+    private void setFillDismissGapTranslationX(float x) {
+        mFillDismissGapTranslationX = x;
+        applyTranslationX();
+    }
+
+    private void setFillDismissGapTranslationY(float y) {
+        mFillDismissGapTranslationY = y;
+        applyTranslationY();
+    }
+
+    private void setTaskOffsetTranslationX(float x) {
+        mTaskOffsetTranslationX = x;
+        applyTranslationX();
+    }
+
+    private void setTaskOffsetTranslationY(float y) {
+        mTaskOffsetTranslationY = y;
+        applyTranslationY();
+    }
+
+    private void applyTranslationX() {
+        setTranslationX(mFillDismissGapTranslationX + mTaskOffsetTranslationX);
+    }
+
+    private void applyTranslationY() {
+        setTranslationY(mFillDismissGapTranslationY + mTaskOffsetTranslationY);
+    }
+
+    public FloatProperty<TaskView> getPrimaryFillDismissGapTranslationProperty() {
+        return getPagedOrientationHandler().getPrimaryValue(
+                FILL_DISMISS_GAP_TRANSLATION_X, FILL_DISMISS_GAP_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getPrimaryTaskOffsetTranslationProperty() {
+        return getPagedOrientationHandler().getPrimaryValue(
+                TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
+    }
+
     @Override
     public boolean hasOverlappingRendering() {
         // TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index aa056a0..f926086 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -59,6 +59,11 @@
             Interpolator interpolator);
 
     /**
+     * Called when activity is destroyed. Used to close search system services
+     */
+    default void destroy(){}
+
+    /**
      * Returns true if the QSB should be visible for the given set of visible elements
      */
     default boolean isQsbVisible(int visibleElements) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 2455706..81174d8 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -98,9 +98,8 @@
     public static final BooleanFlag ENABLE_DEVICE_SEARCH = getDebugFlag(
             "ENABLE_DEVICE_SEARCH", false, "Allows on device search in all apps");
 
-    public static final BooleanFlag SEARCH_TARGET_LEGACY = getDebugFlag(
-            "SEARCH_TARGET_LEGACY", true,
-            "Use SearchTarget provided by plugin lib (only during migration)");
+    public static final BooleanFlag USE_SEARCH_API = getDebugFlag(
+            "USE_SEARCH_API", true, "Use SearchUIManager api for device search");
 
     public static final BooleanFlag DISABLE_INITIAL_IME_IN_ALLAPPS = getDebugFlag(
             "DISABLE_INITIAL_IME_IN_ALLAPPS", false, "Disable default IME state in all apps");
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index ecf4f36..ba28e82 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -51,7 +51,7 @@
     public static final int REQUEST_ROTATE = 1;
     public static final int REQUEST_LOCK = 2;
 
-    private final Activity mActivity;
+    private Activity mActivity;
     private final SharedPreferences mSharedPrefs;
 
     private boolean mIgnoreAutoRotateSettings;
@@ -95,6 +95,7 @@
 
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+        if (mDestroyed) return;
         boolean wasRotationEnabled = mHomeRotationEnabled;
         mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
                 getAllowRotationDefaultValue());
@@ -141,6 +142,7 @@
     public void destroy() {
         if (!mDestroyed) {
             mDestroyed = true;
+            mActivity = null;
             if (mSharedPrefs != null) {
                 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
             }