Merge "Refactor LauncherAppTransitionManager & polish for new app transitions." into ub-launcher3-master
diff --git a/res/layout/predictions_view.xml b/quickstep/res/values/config.xml
similarity index 64%
rename from res/layout/predictions_view.xml
rename to quickstep/res/values/config.xml
index 280290c..94211c6 100644
--- a/res/layout/predictions_view.xml
+++ b/quickstep/res/values/config.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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
+        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,
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.allapps.PredictionRowView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content" />
\ No newline at end of file
+<resources>
+    <string name="task_overlay_factory_class" translatable="false"></string>
+
+</resources>
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
new file mode 100644
index 0000000..c2fb7be
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Preconditions;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * Factory class to create and add an overlays on the TaskView
+ */
+public class TaskOverlayFactory {
+
+    private static TaskOverlayFactory sInstance;
+
+    public static TaskOverlayFactory get(Context context) {
+        Preconditions.assertUIThread();
+        if (sInstance == null) {
+            sInstance = Utilities.getOverrideObject(TaskOverlayFactory.class,
+                    context.getApplicationContext(), R.string.task_overlay_factory_class);
+        }
+        return sInstance;
+    }
+
+    public TaskOverlay createOverlay(View thumbnailView) {
+        return new TaskOverlay();
+    }
+
+    public static class TaskOverlay {
+
+        public void setTaskInfo(ThumbnailData thumbnail, Matrix matrix) { }
+
+        public void reset() { }
+
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
index eeaef09..36a0601 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
@@ -28,7 +28,6 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
 import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.view.View;
@@ -36,6 +35,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -49,6 +49,7 @@
     private final float mCornerRadius;
     private final float mFadeLength;
 
+    private final TaskOverlay mOverlay;
     private final Paint mPaint = new Paint();
 
     private final Matrix mMatrix = new Matrix();
@@ -70,6 +71,11 @@
         super(context, attrs, defStyleAttr);
         mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius);
         mFadeLength = getResources().getDimension(R.dimen.task_fade_length);
+        mOverlay = TaskOverlayFactory.get(context).createOverlay(this);
+    }
+
+    public void bind() {
+        mOverlay.reset();
     }
 
     /**
@@ -89,6 +95,7 @@
             mBitmapShader = null;
             mThumbnailData = null;
             mPaint.setShader(null);
+            mOverlay.reset();
         }
         updateThumbnailPaintFilter();
     }
@@ -173,6 +180,8 @@
             }
             mPaint.setShader(shader);
         }
+
+        mOverlay.setTaskInfo(mThumbnailData, mMatrix);
         invalidate();
     }
 
diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java
index 0e999f8..46fcc72 100644
--- a/quickstep/src/com/android/quickstep/TaskView.java
+++ b/quickstep/src/com/android/quickstep/TaskView.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.R;
 import com.android.quickstep.RecentsView.PageCallbacks;
 import com.android.quickstep.RecentsView.ScrollState;
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -113,6 +114,7 @@
             mTask.removeCallback(this);
         }
         mTask = task;
+        mSnapshotView.bind();
         task.addCallback(this);
     }
 
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 8cf32bd..2ce6b8c 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -31,48 +31,7 @@
 
     <include layout="@layout/all_apps_fast_scroller" />
 
-    <com.android.launcher3.allapps.FloatingHeaderView
-        android:id="@+id/all_apps_header"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="@dimen/all_apps_header_top_padding"
-        android:clipToPadding="false"
-        android:layout_below="@id/search_container_all_apps" >
-
-        <include layout="@layout/predictions_view" android:id="@+id/header_content" />
-
-        <com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
-            android:id="@+id/tabs"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/all_apps_header_tab_height"
-            android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
-            android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
-            android:layout_below="@id/header_content"
-            android:orientation="horizontal">
-            <Button
-                android:id="@+id/tab_personal"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:background="?android:attr/selectableItemBackground"
-                android:fontFamily="sans-serif-medium"
-                android:text="@string/all_apps_personal_tab"
-                android:textAllCaps="true"
-                android:textColor="@color/all_apps_tab_text"
-                android:textSize="14sp"/>
-            <Button
-                android:id="@+id/tab_work"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:background="?android:attr/selectableItemBackground"
-                android:fontFamily="sans-serif-medium"
-                android:text="@string/all_apps_work_tab"
-                android:textAllCaps="true"
-                android:textColor="@color/all_apps_work_tab_text"
-                android:textSize="14sp"/>
-        </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
-    </com.android.launcher3.allapps.FloatingHeaderView>
+    <include layout="@layout/all_apps_floating_header" />
 
     <!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
      platform bug, which prevents using custom attributes in <include> tag -->
diff --git a/res/layout/all_apps_floating_header.xml b/res/layout/all_apps_floating_header.xml
new file mode 100644
index 0000000..166725d
--- /dev/null
+++ b/res/layout/all_apps_floating_header.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<com.android.launcher3.allapps.FloatingHeaderView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/all_apps_header"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_below="@id/search_container_all_apps"
+    android:clipToPadding="false"
+    android:paddingTop="@dimen/all_apps_header_top_padding"
+    android:orientation="vertical" >
+
+    <com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
+        android:id="@+id/tabs"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/all_apps_header_tab_height"
+        android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
+        android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/tab_personal"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:background="?android:attr/selectableItemBackground"
+            android:fontFamily="sans-serif-medium"
+            android:text="@string/all_apps_personal_tab"
+            android:textAllCaps="true"
+            android:textColor="@color/all_apps_tab_text"
+            android:textSize="14sp" />
+
+        <Button
+            android:id="@+id/tab_work"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:background="?android:attr/selectableItemBackground"
+            android:fontFamily="sans-serif-medium"
+            android:text="@string/all_apps_work_tab"
+            android:textAllCaps="true"
+            android:textColor="@color/all_apps_work_tab_text"
+            android:textSize="14sp" />
+    </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
+</com.android.launcher3.allapps.FloatingHeaderView>
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index a5ca3ee..957a5e5 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -118,8 +118,7 @@
         mIconDpi = inv.fillResIconDpi;
         mIconDb = new IconDB(context, inv.iconBitmapSize);
 
-        mIconProvider = Utilities.getOverrideObject(
-                IconProvider.class, context, R.string.icon_provider_class);
+        mIconProvider = IconProvider.newInstance(context);
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
 
         mLowResOptions = new BitmapFactory.Options();
@@ -254,7 +253,7 @@
         // Remove all active icon update tasks.
         mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN);
 
-        mIconProvider.updateSystemStateString();
+        mIconProvider.updateSystemStateString(mContext);
         for (UserHandle user : mUserManager.getUserProfiles()) {
             // Query for the set of apps
             final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index 4dee2b5..b469a8f 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -1,5 +1,6 @@
 package com.android.launcher3;
 
+import android.content.Context;
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -8,17 +9,26 @@
 
 public class IconProvider {
 
-    private static final boolean DBG = false;
-    private static final String TAG = "IconProvider";
-
     protected String mSystemState;
 
-    public IconProvider() {
-        updateSystemStateString();
+    public static IconProvider newInstance(Context context) {
+        IconProvider provider = Utilities.getOverrideObject(
+                IconProvider.class, context, R.string.icon_provider_class);
+        provider.updateSystemStateString(context);
+        return provider;
     }
 
-    public void updateSystemStateString() {
-        mSystemState = Locale.getDefault().toString() + "," + Build.VERSION.SDK_INT;
+    public IconProvider() { }
+
+    public void updateSystemStateString(Context context) {
+        final String locale;
+        if (Utilities.ATLEAST_NOUGAT) {
+            locale = context.getResources().getConfiguration().getLocales().toLanguageTags();
+        } else {
+            locale = Locale.getDefault().toString();
+        }
+
+        mSystemState = locale + "," + Build.VERSION.SDK_INT;
     }
 
     public String getIconSystemState(String packageName) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 2e544ec..dc3de18 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -55,13 +55,11 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.views.BottomUserEducationView;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
 
@@ -76,6 +74,7 @@
     private final ClickShadowView mTouchFeedbackView;
     private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
     private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
+    private final AllAppsStore mAllAppsStore = new AllAppsStore();
 
     private SearchUiManager mSearchUiManager;
     private View mSearchContainer;
@@ -92,8 +91,6 @@
     private boolean mHasPredictions = false;
     private boolean mSearchModeWhileUsingTabs = false;
 
-    private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
-
     public AllAppsContainerView(Context context) {
         this(context, null);
     }
@@ -132,6 +129,10 @@
         // TODO: Reimplement once fast scroller is fixed.
     }
 
+    public AllAppsStore getAppsStore() {
+        return mAllAppsStore;
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
@@ -149,64 +150,29 @@
     public void setApps(List<AppInfo> apps) {
         boolean hasWorkProfileApp = hasWorkProfileApp(apps);
         rebindAdapters(hasWorkProfileApp);
-        mComponentToAppMap.clear();
-        addOrUpdateApps(apps);
+        mAllAppsStore.setApps(apps);
     }
 
     /**
      * Adds or updates existing apps in the list
      */
     public void addOrUpdateApps(List<AppInfo> apps) {
-        for (AppInfo app : apps) {
-            mComponentToAppMap.put(app.toComponentKey(), app);
-        }
-        onAppsUpdated();
-        mSearchUiManager.refreshSearchResult();
-        mHeader.onAppsUpdated();
+        mAllAppsStore.addOrUpdateApps(apps);
     }
 
     /**
      * Removes some apps from the list.
      */
     public void removeApps(List<AppInfo> apps) {
-        for (AppInfo app : apps) {
-            mComponentToAppMap.remove(app.toComponentKey());
-        }
-        onAppsUpdated();
-        mSearchUiManager.refreshSearchResult();
-    }
-
-    private void onAppsUpdated() {
-        for (int i = 0; i < getNumOfAdapters(); i++) {
-            mAH[i].appsList.onAppsUpdated();
-        }
-    }
-
-    private int getNumOfAdapters() {
-        return mUsingTabs ? mAH.length : 1;
+        mAllAppsStore.removeApps(apps);
     }
 
     public void updatePromiseAppProgress(PromiseAppInfo app) {
-        for (int i = 0; i < mAH.length; i++) {
-            updatePromiseAppProgress(app, mAH[i].recyclerView);
-        }
-        if (isHeaderVisible()) {
-            updatePromiseAppProgress(app, mHeader.getPredictionRow());
-        }
-    }
-
-    private void updatePromiseAppProgress(PromiseAppInfo app, ViewGroup parent) {
-        if (parent == null) {
-            return;
-        }
-        int childCount = parent.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = parent.getChildAt(i);
-            if (child instanceof BubbleTextView && child.getTag() == app) {
-                BubbleTextView bubbleTextView = (BubbleTextView) child;
-                bubbleTextView.applyProgressLevel(app.level);
+        mAllAppsStore.updateAllIcons((child) -> {
+            if (child.getTag() == app) {
+                child.applyProgressLevel(app.level);
             }
-        }
+        });
     }
 
     /**
@@ -358,34 +324,15 @@
     }
 
     public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
-        final PackageUserKey packageUserKey = new PackageUserKey(null, null);
-        for (int j = 0; j < mAH.length; j++) {
-            updateIconBadges(updatedBadges, packageUserKey, mAH[j].recyclerView);
-        }
-        if (mHeader != null) {
-            updateIconBadges(updatedBadges, packageUserKey, mHeader.getPredictionRow());
-        }
-    }
-
-    private void updateIconBadges(Set<PackageUserKey> updatedBadges, PackageUserKey packageUserKey,
-            ViewGroup parent) {
-        if (parent == null) {
-            return;
-        }
-        final int n = parent.getChildCount();
-        for (int i = 0; i < n; i++) {
-            View child = parent.getChildAt(i);
-            if (child instanceof PredictionRowView) {
-                updateIconBadges(updatedBadges, packageUserKey, (PredictionRowView) child);
+        PackageUserKey tempKey = new PackageUserKey(null, null);
+        mAllAppsStore.updateAllIcons((child) -> {
+            if (child.getTag() instanceof ItemInfo) {
+                ItemInfo info = (ItemInfo) child.getTag();
+                if (tempKey.updateFromItemInfo(info) && updatedBadges.contains(tempKey)) {
+                    child.applyBadgeState(info, true /* animate */);
+                }
             }
-            if (!(child instanceof BubbleTextView) || !(child.getTag() instanceof ItemInfo)) {
-                continue;
-            }
-            ItemInfo info = (ItemInfo) child.getTag();
-            if (packageUserKey.updateFromItemInfo(info) && updatedBadges.contains(packageUserKey)) {
-                ((BubbleTextView) child).applyBadgeState(info, true /* animate */);
-            }
-        }
+        });
     }
 
     public SpringAnimationHandler getSpringAnimationHandler() {
@@ -403,6 +350,9 @@
         replaceRVContainer(showTabs);
         mUsingTabs = showTabs;
 
+        mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
+        mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.WORK].recyclerView);
+
         if (mUsingTabs) {
             mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
             mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
@@ -419,6 +369,9 @@
             }
         }
 
+        mAllAppsStore.registerIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
+        mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
+
         applyTouchDelegate();
     }
 
@@ -492,9 +445,6 @@
     }
 
     public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
-        if (isHeaderVisible()) {
-            mHeader.getPredictionRow().setPredictedApps(apps);
-        }
         mAH[AdapterHolder.MAIN].appsList.setPredictedApps(apps);
         boolean hasPredictions = !apps.isEmpty();
         if (mHasPredictions != hasPredictions) {
@@ -506,7 +456,7 @@
     }
 
     public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) {
-        return mapper.getItem(mComponentToAppMap);
+        return mAllAppsStore.getApp(mapper);
     }
 
     public AlphabeticalAppsList getApps() {
@@ -526,9 +476,9 @@
             return;
         }
         mHeader.setVisibility(View.VISIBLE);
-        mHeader.setup(mAH, mComponentToAppMap, mNumPredictedAppsPerRow);
+        mHeader.setup(mAH, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null);
 
-        int padding = mHeader.getPredictionRow().getExpectedHeight();
+        int padding = mHeader.getMaxTranslation();
         if (mHasPredictions && !mUsingTabs) {
             padding += mHeader.getPaddingTop() + mHeader.getPaddingBottom();
         }
@@ -582,14 +532,6 @@
         }
     }
 
-    public List<AppInfo> getPredictedApps() {
-        if (isHeaderVisible()) {
-            return mHeader.getPredictionRow().getPredictedApps();
-        } else {
-            return mAH[AdapterHolder.MAIN].appsList.getPredictedApps();
-        }
-    }
-
     public boolean isHeaderVisible() {
         return mHeader != null && mHeader.getVisibility() == View.VISIBLE;
     }
@@ -604,7 +546,7 @@
         public static final int MAIN = 0;
         public static final int WORK = 1;
 
-        final AllAppsGridAdapter adapter;
+        public final AllAppsGridAdapter adapter;
         final LinearLayoutManager layoutManager;
         final SpringAnimationHandler animationHandler;
         final AlphabeticalAppsList appsList;
@@ -614,7 +556,7 @@
         boolean verticalFadingEdge;
 
         AdapterHolder(boolean isWork) {
-            appsList = new AlphabeticalAppsList(mLauncher, mComponentToAppMap, isWork);
+            appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
             adapter = new AllAppsGridAdapter(mLauncher, appsList, mLauncher,
                     AllAppsContainerView.this, true);
             appsList.setAdapter(adapter);
@@ -649,11 +591,6 @@
                         ? paddingTopForTabs : padding.top;
                 recyclerView.setPadding(padding.left, paddingTop, padding.right, padding.bottom);
             }
-            if (isHeaderVisible()) {
-                PredictionRowView prv = mHeader.getPredictionRow();
-                prv.setPadding(padding.left, prv.getPaddingTop() , padding.right,
-                        prv.getPaddingBottom());
-            }
         }
 
         void applyNumsPerRow() {
@@ -663,10 +600,6 @@
                 }
                 adapter.setNumAppsPerRow(mNumAppsPerRow);
                 appsList.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
-                if (isHeaderVisible()) {
-                    mHeader.getPredictionRow()
-                            .setNumAppsPerRow(mNumPredictedAppsPerRow);
-                }
             }
         }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
new file mode 100644
index 0000000..17f1c89
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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.allapps;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A utility class to maintain the collection of all apps.
+ */
+public class AllAppsStore {
+
+    private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
+    private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
+    private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
+
+    public Collection<AppInfo> getApps() {
+        return mComponentToAppMap.values();
+    }
+
+    /**
+     * Sets the current set of apps.
+     */
+    public void setApps(List<AppInfo> apps) {
+        mComponentToAppMap.clear();
+        addOrUpdateApps(apps);
+    }
+
+    public AppInfo getApp(ComponentKey key) {
+        return mComponentToAppMap.get(key);
+    }
+
+    public AppInfo getApp(ComponentKeyMapper<AppInfo> mapper) {
+        return mapper.getItem(mComponentToAppMap);
+    }
+
+    /**
+     * Adds or updates existing apps in the list
+     */
+    public void addOrUpdateApps(List<AppInfo> apps) {
+        for (AppInfo app : apps) {
+            mComponentToAppMap.put(app.toComponentKey(), app);
+        }
+        notifyUpdate();
+    }
+
+    /**
+     * Removes some apps from the list.
+     */
+    public void removeApps(List<AppInfo> apps) {
+        for (AppInfo app : apps) {
+            mComponentToAppMap.remove(app.toComponentKey());
+        }
+        notifyUpdate();
+    }
+
+
+    private void notifyUpdate() {
+        int count = mUpdateListeners.size();
+        for (int i = 0; i < count; i++) {
+            mUpdateListeners.get(i).onAppsUpdated();
+        }
+    }
+
+    public void addUpdateListener(OnUpdateListener listener) {
+        mUpdateListeners.add(listener);
+    }
+
+    public void removeUpdateListener(OnUpdateListener listener) {
+        mUpdateListeners.remove(listener);
+    }
+
+    public void registerIconContainer(ViewGroup container) {
+        if (container != null) {
+            mIconContainers.add(container);
+        }
+    }
+
+    public void unregisterIconContainer(ViewGroup container) {
+        mIconContainers.remove(container);
+    }
+
+    public void updateAllIcons(IconAction action) {
+        for (int i = mIconContainers.size() - 1; i >= 0; i--) {
+            ViewGroup parent = mIconContainers.get(i);
+            int childCount = parent.getChildCount();
+
+            for (int j = 0; j < childCount; j++) {
+                View child = parent.getChildAt(j);
+                if (child instanceof BubbleTextView) {
+                    action.apply((BubbleTextView) child);
+                }
+            }
+        }
+    }
+
+    public interface OnUpdateListener {
+        void onAppsUpdated();
+    }
+
+    public interface IconAction {
+        void apply(BubbleTextView icon);
+    }
+}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 76828de..29b32b0 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -44,7 +44,7 @@
 /**
  * The alphabetically sorted list of applications.
  */
-public class AlphabeticalAppsList {
+public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
 
     public static final String TAG = "AlphabeticalAppsList";
     private static final boolean DEBUG = false;
@@ -153,7 +153,7 @@
 
     // The set of apps from the system not including predictions
     private final List<AppInfo> mApps = new ArrayList<>();
-    private final HashMap<ComponentKey, AppInfo> mComponentToAppMap;
+    private final AllAppsStore mAllAppsStore;
 
     // The set of filtered apps with the current filter
     private final List<AppInfo> mFilteredApps = new ArrayList<>();
@@ -179,16 +179,13 @@
     private int mNumAppRowsInAdapter;
     private ItemInfoMatcher mItemFilter;
 
-    public AlphabeticalAppsList(
-            Context context,
-            HashMap<ComponentKey,
-            AppInfo> componentToAppMap,
-            boolean isWork) {
-        mComponentToAppMap = componentToAppMap;
+    public AlphabeticalAppsList(Context context, AllAppsStore appsStore, boolean isWork) {
+        mAllAppsStore = appsStore;
         mLauncher = Launcher.getLauncher(context);
         mIndexer = new AlphabeticIndexCompat(context);
         mAppNameComparator = new AppInfoComparator(context);
         mIsWork = isWork;
+        mAllAppsStore.addUpdateListener(this);
     }
 
     public void updateItemFilter(ItemInfoMatcher itemFilter) {
@@ -283,14 +280,14 @@
     }
 
     private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) {
-        if (mComponentToAppMap.isEmpty()) {
+        if (mAllAppsStore.getApps().isEmpty()) {
             // Apps have not been bound yet.
             return Collections.emptyList();
         }
 
         List<AppInfo> predictedApps = new ArrayList<>();
         for (ComponentKeyMapper<AppInfo> mapper : components) {
-            AppInfo info = mapper.getItem(mComponentToAppMap);
+            AppInfo info = mAllAppsStore.getApp(mapper);
             if (info != null) {
                 predictedApps.add(info);
             } else {
@@ -359,11 +356,12 @@
     /**
      * Updates internals when the set of apps are updated.
      */
-    void onAppsUpdated() {
+    @Override
+    public void onAppsUpdated() {
         // Sort the list of apps
         mApps.clear();
 
-        for (AppInfo app : mComponentToAppMap.values()) {
+        for (AppInfo app : mAllAppsStore.getApps()) {
             if (mItemFilter == null || mItemFilter.matches(app, null) || hasFilter()) {
                 mApps.add(app);
             }
@@ -580,7 +578,7 @@
         }
         ArrayList<AppInfo> result = new ArrayList<>();
         for (ComponentKey key : mSearchResults) {
-            AppInfo match = mComponentToAppMap.get(key);
+            AppInfo match = mAllAppsStore.getApp(key);
             if (match != null) {
                 result.add(match);
             }
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 2391768..d8a9f63 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.allapps;
 
-
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Point;
@@ -27,18 +26,14 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
-import com.android.launcher3.AppInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.util.ComponentKey;
 
-import java.util.HashMap;
-
-public class FloatingHeaderView extends RelativeLayout implements
+public class FloatingHeaderView extends LinearLayout implements
         ValueAnimator.AnimatorUpdateListener {
 
-
     private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
     private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
     private final Point mTempOffset = new Point();
@@ -63,19 +58,18 @@
         }
     };
 
-    private PredictionRowView mPredictionRow;
     private ViewGroup mTabLayout;
     private AllAppsRecyclerView mMainRV;
     private AllAppsRecyclerView mWorkRV;
     private AllAppsRecyclerView mCurrentRV;
     private ViewGroup mParent;
-    private boolean mTabsHidden;
     private boolean mHeaderCollapsed;
-    private int mMaxTranslation;
     private int mSnappedScrolledY;
     private int mTranslationY;
     private boolean mForwardToRecyclerView;
 
+    protected int mMaxTranslation;
+
     public FloatingHeaderView(@NonNull Context context) {
         this(context, null);
     }
@@ -88,17 +82,10 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mTabLayout = findViewById(R.id.tabs);
-        mPredictionRow = findViewById(R.id.header_content);
     }
 
-    public void setup(AllAppsContainerView.AdapterHolder[] mAH,
-            HashMap<ComponentKey, AppInfo> componentToAppMap, int numPredictedAppsPerRow) {
-        mTabsHidden = mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null;
-        mTabLayout.setVisibility(mTabsHidden ? View.GONE : View.VISIBLE);
-        mPredictionRow.setup(mAH[AllAppsContainerView.AdapterHolder.MAIN].adapter,
-                componentToAppMap, numPredictedAppsPerRow);
-        mPredictionRow.setShowDivider(mTabsHidden);
-        mMaxTranslation = mPredictionRow.getExpectedHeight();
+    public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
+        mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
         mMainRV = setupRV(mMainRV, mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView);
         mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView);
         mParent = (ViewGroup) mMainRV.getParent();
@@ -117,12 +104,12 @@
         mCurrentRV = active ? mMainRV : mWorkRV;
     }
 
-    public PredictionRowView getPredictionRow() {
-        return mPredictionRow;
+    public int getMaxTranslation() {
+        return mMaxTranslation;
     }
 
     private boolean canSnapAt(int currentScrollY) {
-        return Math.abs(currentScrollY) <= mPredictionRow.getHeight();
+        return Math.abs(currentScrollY) <= mMaxTranslation;
     }
 
     private void moved(final int currentScrollY) {
@@ -149,16 +136,12 @@
         }
     }
 
-    private void apply() {
+    protected void applyScroll(int uncappedY, int currentY) { }
+
+    protected void apply() {
         int uncappedTranslationY = mTranslationY;
         mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
-        if (mTranslationY != uncappedTranslationY) {
-            // we hide it completely if already capped (for opening search anim)
-            mPredictionRow.setVisibility(View.INVISIBLE);
-        } else {
-            mPredictionRow.setVisibility(View.VISIBLE);
-            mPredictionRow.setTranslationY(uncappedTranslationY);
-        }
+        applyScroll(uncappedTranslationY, mTranslationY);
         mTabLayout.setTranslationY(mTranslationY);
         mClip.top = mMaxTranslation + mTranslationY;
         // clipping on a draw might cause additional redraw
@@ -218,10 +201,6 @@
         p.x = getLeft() - mCurrentRV.getLeft() - mParent.getLeft();
         p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
     }
-
-    public void onAppsUpdated() {
-        mPredictionRow.onAppsUpdated();
-    }
 }
 
 
diff --git a/src/com/android/launcher3/allapps/PredictionRowView.java b/src/com/android/launcher3/allapps/PredictionRowView.java
deleted file mode 100644
index 267ef3c..0000000
--- a/src/com/android/launcher3/allapps/PredictionRowView.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2017 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.allapps;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
-import com.android.launcher3.util.Themes;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-public class PredictionRowView extends LinearLayout implements
-        UserEventDispatcher.LogContainerProvider {
-
-    private static final String TAG = "PredictionRowView";
-
-    private HashMap<ComponentKey, AppInfo> mComponentToAppMap;
-    private int mNumPredictedAppsPerRow;
-    // The set of predicted app component names
-    private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
-    // The set of predicted apps resolved from the component names and the current set of apps
-    private final ArrayList<AppInfo> mPredictedApps = new ArrayList<>();
-    private final Paint mPaint;
-    // This adapter is only used to create an identical item w/ same behavior as in the all apps RV
-    private AllAppsGridAdapter mAdapter;
-    private boolean mShowDivider;
-
-    public PredictionRowView(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public PredictionRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        setOrientation(LinearLayout.HORIZONTAL);
-        setWillNotDraw(false);
-        mPaint = new Paint();
-        mPaint.setColor(Themes.getAttrColor(context, android.R.attr.colorControlHighlight));
-        mPaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
-    }
-
-    public void setup(AllAppsGridAdapter adapter, HashMap<ComponentKey, AppInfo> componentToAppMap,
-                      int numPredictedAppsPerRow) {
-        mAdapter = adapter;
-        mComponentToAppMap = componentToAppMap;
-        mNumPredictedAppsPerRow = numPredictedAppsPerRow;
-        setVisibility(mPredictedAppComponents.isEmpty() ? View.GONE : View.VISIBLE);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getExpectedHeight(),
-                MeasureSpec.EXACTLY));
-    }
-
-    public int getExpectedHeight() {
-        int height = 0;
-        if (!mPredictedAppComponents.isEmpty()) {
-            height += Launcher.getLauncher(getContext())
-                    .getDeviceProfile().allAppsCellHeightPx;
-            height += getPaddingTop() + getPaddingBottom();
-        }
-        return height;
-    }
-
-    public void setShowDivider(boolean showDivider) {
-        mShowDivider = showDivider;
-        int paddingBottom = showDivider ? getResources()
-                .getDimensionPixelSize(R.dimen.all_apps_prediction_row_divider_height) : 0;
-        setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), paddingBottom);
-    }
-
-    /**
-     * Sets the number of apps per row.
-     */
-    public void setNumAppsPerRow(int numPredictedAppsPerRow) {
-        if (mNumPredictedAppsPerRow != numPredictedAppsPerRow) {
-            mNumPredictedAppsPerRow = numPredictedAppsPerRow;
-            onPredictionsUpdated();
-        }
-    }
-
-    /**
-     * Returns the predicted apps.
-     */
-    public List<AppInfo> getPredictedApps() {
-        return mPredictedApps;
-    }
-
-    /**
-     * Sets the current set of predicted apps.
-     *
-     * This can be called before we get the full set of applications, we should merge the results
-     * only in onPredictionsUpdated() which is idempotent.
-     *
-     * If the number of predicted apps is the same as the previous list of predicted apps,
-     * we can optimize by swapping them in place.
-     */
-    public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
-        mPredictedAppComponents.clear();
-        mPredictedAppComponents.addAll(apps);
-        mPredictedApps.clear();
-        mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
-        onPredictionsUpdated();
-    }
-
-    private void onPredictionsUpdated() {
-        int childCountBefore = getChildCount();
-        if (getChildCount() != mNumPredictedAppsPerRow) {
-            while (getChildCount() > mNumPredictedAppsPerRow) {
-                removeViewAt(0);
-            }
-            while (getChildCount() < mNumPredictedAppsPerRow) {
-                AllAppsGridAdapter.ViewHolder holder = mAdapter
-                        .onCreateViewHolder(this, AllAppsGridAdapter.VIEW_TYPE_ICON);
-                BubbleTextView icon = (BubbleTextView) holder.itemView;
-                LinearLayout.LayoutParams params =
-                        new LayoutParams(0, icon.getLayoutParams().height);
-                params.weight = 1;
-                icon.setLayoutParams(params);
-                addView(icon);
-            }
-        }
-
-        for (int i = 0; i < getChildCount(); i++) {
-            BubbleTextView icon = (BubbleTextView) getChildAt(i);
-            icon.reset();
-            if (mPredictedApps.size() > i) {
-                icon.setVisibility(View.VISIBLE);
-                icon.applyFromApplicationInfo(mPredictedApps.get(i));
-            } else {
-                icon.setVisibility(View.INVISIBLE);
-            }
-        }
-
-        if (getChildCount() > 0 && childCountBefore == 0
-                || getChildCount() == 0 && childCountBefore > 0) {
-            // setting up header to adjust the height
-            // only necessary if childcount switches from/to 0
-            Launcher.getLauncher(getContext()).getAppsView().setupHeader();
-        }
-    }
-
-    /**
-     * Refreshes the app icons in the row view, while preserving the same set of predictions.
-     */
-    public void onAppsUpdated() {
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            if (!(child instanceof BubbleTextView)) {
-                continue;
-            }
-            if (i >= mPredictedApps.size()) {
-                break;
-            }
-            BubbleTextView icon = (BubbleTextView) getChildAt(i);
-            icon.reset();
-            icon.applyFromApplicationInfo(mPredictedApps.get(i));
-        }
-    }
-
-    private List<AppInfo> processPredictedAppComponents(
-            List<ComponentKeyMapper<AppInfo>> components) {
-        if (mComponentToAppMap.isEmpty()) {
-            // Apps have not been bound yet.
-            return Collections.emptyList();
-        }
-
-        List<AppInfo> predictedApps = new ArrayList<>();
-        for (ComponentKeyMapper<AppInfo> mapper : components) {
-            AppInfo info = mapper.getItem(mComponentToAppMap);
-            if (info != null) {
-                predictedApps.add(info);
-            } else {
-                if (FeatureFlags.IS_DOGFOOD_BUILD) {
-                    Log.e(TAG, "Predicted app not found: " + mapper);
-                }
-            }
-            // Stop at the number of predicted apps
-            if (predictedApps.size() == mNumPredictedAppsPerRow) {
-                break;
-            }
-        }
-        return predictedApps;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        if (mShowDivider) {
-            int side = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
-            int y = getHeight() - (getPaddingBottom() / 2);
-            int x1 = getPaddingLeft() + side;
-            int x2 = getWidth() - getPaddingRight() - side;
-            canvas.drawLine(x1, y, x2, y, mPaint);
-        }
-    }
-
-    @Override
-    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
-            LauncherLogProto.Target targetParent) {
-        for (int i = 0; i < mPredictedApps.size(); i++) {
-            AppInfo appInfo = mPredictedApps.get(i);
-            if (appInfo == info) {
-                targetParent.containerType = LauncherLogProto.ContainerType.PREDICTION;
-                target.predictedRank = i;
-                break;
-            }
-        }
-    }
-}
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index f562b6a..bb17ed5 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -35,12 +35,6 @@
     @NonNull SpringAnimation getSpringForFling();
 
     /**
-     * Notifies the search manager that the apps-list has changed and the search UI should be
-     * updated accordingly.
-     */
-    void refreshSearchResult();
-
-    /**
      * Notifies the search manager to close any active search session.
      */
     void reset();
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 6f07eeb..fca3e47 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -37,6 +37,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AlphabeticalAppsList;
 import com.android.launcher3.allapps.SearchUiManager;
 import com.android.launcher3.graphics.TintedDrawableSpan;
@@ -48,7 +49,8 @@
  * Layout to contain the All-apps search UI.
  */
 public class AppsSearchContainerLayout extends FrameLayout
-        implements SearchUiManager, AllAppsSearchBarController.Callbacks {
+        implements SearchUiManager, AllAppsSearchBarController.Callbacks,
+        AllAppsStore.OnUpdateListener {
 
     private final Launcher mLauncher;
     private final int mMinHeight;
@@ -111,6 +113,18 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mLauncher.getAppsView().getAppsStore().addUpdateListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mLauncher.getAppsView().getAppsStore().removeUpdateListener(this);
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
             getLayoutParams().height = mLauncher.getDragLayer().getInsets().top + mMinHeight;
@@ -134,7 +148,7 @@
     }
 
     @Override
-    public void refreshSearchResult() {
+    public void onAppsUpdated() {
         mSearchBarController.refreshSearchResult();
     }
 
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index f3b0d61..4cbf673 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -22,6 +22,7 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.DeviceProfile;
@@ -55,6 +56,7 @@
 
     private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
     private final Launcher mLauncher;
+    private final AccessibilityManager mAccessibilityManager;
 
     private boolean mShouldAutoHide = true;
 
@@ -136,6 +138,8 @@
         boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText();
         mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
         mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
+        mAccessibilityManager = (AccessibilityManager)
+                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
     }
 
     @Override
@@ -274,7 +278,7 @@
 
             setBackgroundResource(0);
             setOnFocusChangeListener(null);
-            setOnClickListener(null);
+            setOnClickListener(mAccessibilityManager.isTouchExplorationEnabled() ? this : null);
         }
 
         setLayoutParams(lp);
diff --git a/src/com/android/launcher3/util/ComponentKeyMapper.java b/src/com/android/launcher3/util/ComponentKeyMapper.java
index 916176a..a7f0d76 100644
--- a/src/com/android/launcher3/util/ComponentKeyMapper.java
+++ b/src/com/android/launcher3/util/ComponentKeyMapper.java
@@ -18,8 +18,6 @@
 
 import android.support.annotation.Nullable;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
 
 public class ComponentKeyMapper<T> {