Merge "Removing some Launcher3 dependencies from BaseIconCache" into ub-launcher3-master
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index 0a9ad7b..0080898 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -36,16 +36,14 @@
         android:restoreAnyVersion="true"
         android:supportsRtl="true" >
 
-        <!-- Activity for handling PinItemRequest. Only supports shortcuts -->
+        <!-- Activity for handling PinItemRequest is disabled on Android Go. -->
         <activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
             android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
             android:excludeFromRecents="true"
             android:autoRemoveFromRecents="true"
             android:label="@string/action_add_to_workspace"
+            android:enabled="false"
             tools:node="replace" >
-            <intent-filter>
-                <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
-            </intent-filter>
         </activity>
 
     </application>
diff --git a/go/res/layout/widget_cell_content.xml b/go/res/layout/widget_cell_content.xml
deleted file mode 100644
index 49506d9..0000000
--- a/go/res/layout/widget_cell_content.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="@dimen/widget_preview_label_vertical_padding"
-        android:paddingBottom="@dimen/widget_preview_label_vertical_padding"
-        android:paddingLeft="@dimen/widget_preview_label_horizontal_padding"
-        android:paddingRight="@dimen/widget_preview_label_horizontal_padding"
-        android:orientation="horizontal">
-
-        <!-- The name of the widget. -->
-        <TextView
-            android:id="@+id/widget_name"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:ellipsize="end"
-            android:fadingEdge="horizontal"
-            android:fontFamily="sans-serif-condensed"
-            android:gravity="center"
-            android:singleLine="true"
-            android:maxLines="1"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textSize="14sp" />
-
-        <!-- The original dimensions of the widget (can't be the same text as above due to different
-             style. -->
-        <TextView
-            android:id="@+id/widget_dims"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="5dp"
-            android:layout_marginLeft="5dp"
-            android:visibility="gone"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textSize="14sp"
-            android:fontFamily="sans-serif-condensed"
-            android:alpha="0.8" />
-    </LinearLayout>
-
-    <!-- The image of the widget. This view does not support padding. Any placement adjustment
-         should be done using margins. -->
-    <com.android.launcher3.widget.WidgetImageView
-        android:id="@+id/widget_preview"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
-</merge>
\ No newline at end of file
diff --git a/proguard.flags b/proguard.flags
index bb52a6c..eb27737 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -24,7 +24,7 @@
 -keep class androidx.recyclerview.widget.RecyclerView { *; }
 
 # Preference fragments
--keep class ** extends android.preference.PreferenceFragment {
+-keep class ** extends android.app.Fragment {
     public <init>(...);
 }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 1e787a2..6834070 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -463,7 +463,7 @@
         }
 
         if (action == R.string.accessibility_app_usage_settings) {
-            openAppUsageSettings();
+            openAppUsageSettings(this);
             return true;
         }
 
@@ -485,14 +485,16 @@
         return super.performAccessibilityAction(action, arguments);
     }
 
-    private void openAppUsageSettings() {
+    private void openAppUsageSettings(View view) {
         final Intent intent = new Intent(SEE_TIME_IN_APP_TEMPLATE)
                 .putExtra(Intent.EXTRA_PACKAGE_NAME,
                         mTask.getTopComponent().getPackageName()).addFlags(
                         Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         try {
             final Launcher launcher = Launcher.getLauncher(getContext());
-            launcher.startActivity(intent);
+            final ActivityOptions options = ActivityOptions.makeScaleUpAnimation(view, 0, 0,
+                    view.getWidth(), view.getHeight());
+            launcher.startActivity(intent, options.toBundle());
             launcher.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
                     LauncherLogProto.ControlType.APP_USAGE_SETTINGS, this);
         } catch (ActivityNotFoundException e) {
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index b1e6f2e..33ff46b 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -30,7 +30,50 @@
         layout="@layout/all_apps_rv_layout"
         android:visibility="gone" />
 
-    <include layout="@layout/all_apps_floating_header" />
+    <com.android.launcher3.allapps.FloatingHeaderView
+        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" >
+
+        <include layout="@layout/floating_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: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_tab_text"
+                android:textSize="14sp" />
+        </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
+    </com.android.launcher3.allapps.FloatingHeaderView>
 
     <include
         android:id="@id/search_container_all_apps"
diff --git a/res/layout/all_apps_floating_header.xml b/res/layout/all_apps_floating_header.xml
deleted file mode 100644
index c4240f8..0000000
--- a/res/layout/all_apps_floating_header.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?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_tab_text"
-            android:textSize="14sp" />
-    </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
-</com.android.launcher3.allapps.FloatingHeaderView>
diff --git a/res/layout/floating_header_content.xml b/res/layout/floating_header_content.xml
new file mode 100644
index 0000000..e4061c2
--- /dev/null
+++ b/res/layout/floating_header_content.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<merge />
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
new file mode 100644
index 0000000..922e4f1
--- /dev/null
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -0,0 +1,58 @@
+/*
+ * 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.graphics.Rect;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.PropertySetter;
+
+/**
+ * A abstract representation of a row in all-apps view
+ */
+public interface FloatingHeaderRow {
+
+    FloatingHeaderRow[] NO_ROWS = new FloatingHeaderRow[0];
+
+    void setup(FloatingHeaderView parent, FloatingHeaderRow[] allRows, boolean tabsHidden);
+
+    void setInsets(Rect insets, DeviceProfile grid);
+
+    int getExpectedHeight();
+
+    /**
+     * Returns true if the row should draw based on its current position and layout.
+     */
+    boolean shouldDraw();
+
+    /**
+     * Returns true if the view has anything worth drawing. This is different than
+     * {@link #shouldDraw()} as this is called earlier in the layout to determine the view
+     * position.
+     */
+    boolean hasVisibleContent();
+
+    void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
+            PropertySetter setter, Interpolator fadeInterpolator);
+
+    /**
+     * Scrolls the content vertically.
+     */
+    void setVerticalScroll(int scroll, boolean isScrolledOut);
+
+    Class<? extends FloatingHeaderRow> getTypeClass();
+}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 90e195b..66dced9 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -19,28 +19,34 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.systemui.plugins.AllAppsRow;
+import com.android.systemui.plugins.AllAppsRow.OnHeightUpdatedListener;
 import com.android.systemui.plugins.PluginListener;
 
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
 public class FloatingHeaderView extends LinearLayout implements
-        ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow> {
+        ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow>, Insettable,
+        OnHeightUpdatedListener {
 
     private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
     private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
@@ -62,17 +68,18 @@
 
             int current = -mCurrentRV.getCurrentScrollY();
             moved(current);
-            apply();
+            applyVerticalMove();
         }
     };
 
+    private final int mHeaderTopPadding;
+
+    protected final Map<AllAppsRow, PluginHeaderRow> mPluginRows = new ArrayMap<>();
+
     protected ViewGroup mTabLayout;
     private AllAppsRecyclerView mMainRV;
     private AllAppsRecyclerView mWorkRV;
     private AllAppsRecyclerView mCurrentRV;
-    protected final Map<AllAppsRow, View> mPluginRows;
-    // Contains just the values of the above map so we can iterate without extracting a new list.
-    protected final List<View> mPluginRowViews;
     private ViewGroup mParent;
     private boolean mHeaderCollapsed;
     private int mSnappedScrolledY;
@@ -85,20 +92,42 @@
     protected int mMaxTranslation;
     private boolean mMainRVActive = true;
 
+    private boolean mCollapsed = false;
+
+    // This is initialized once during inflation and stays constant after that. Fixed views
+    // cannot be added or removed dynamically.
+    private FloatingHeaderRow[] mFixedRows = FloatingHeaderRow.NO_ROWS;
+
+    // Array of all fixed rows and plugin rows. This is initialized every time a plugin is
+    // enabled or disabled, and represent the current set of all rows.
+    private FloatingHeaderRow[] mAllRows = FloatingHeaderRow.NO_ROWS;
+
     public FloatingHeaderView(@NonNull Context context) {
         this(context, null);
     }
 
     public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
-        mPluginRows = new HashMap<>();
-        mPluginRowViews = new ArrayList<>();
+        mHeaderTopPadding = context.getResources()
+                .getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mTabLayout = findViewById(R.id.tabs);
+
+        // Find all floating header rows.
+        ArrayList<FloatingHeaderRow> rows = new ArrayList<>();
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            if (child instanceof FloatingHeaderRow) {
+                rows.add((FloatingHeaderRow) child);
+            }
+        }
+        mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
+        mAllRows = mFixedRows;
     }
 
     @Override
@@ -114,50 +143,70 @@
         PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this);
     }
 
-    @Override
-    public void onPluginConnected(AllAppsRow allAppsRowPlugin, Context context) {
-        mPluginRows.put(allAppsRowPlugin, null);
-        setupPluginRows();
-        allAppsRowPlugin.setOnHeightUpdatedListener(this::onPluginRowHeightUpdated);
+    private void recreateAllRowsArray() {
+        int pluginCount = mPluginRows.size();
+        if (pluginCount == 0) {
+            mAllRows = mFixedRows;
+        } else {
+            int count = mFixedRows.length;
+            mAllRows = new FloatingHeaderRow[count + pluginCount];
+            for (int i = 0; i < count; i++) {
+                mAllRows[i] = mFixedRows[i];
+            }
+
+            for (PluginHeaderRow row : mPluginRows.values()) {
+                mAllRows[count] = row;
+                count++;
+            }
+        }
     }
 
-    protected void onPluginRowHeightUpdated() {
+    @Override
+    public void onPluginConnected(AllAppsRow allAppsRowPlugin, Context context) {
+        PluginHeaderRow headerRow = new PluginHeaderRow(allAppsRowPlugin, this);
+        addView(headerRow.mView, indexOfChild(mTabLayout));
+        mPluginRows.put(allAppsRowPlugin, headerRow);
+        recreateAllRowsArray();
+        allAppsRowPlugin.setOnHeightUpdatedListener(this);
+    }
+
+    @Override
+    public void onHeightUpdated() {
+        int oldMaxHeight = mMaxTranslation;
+        updateExpectedHeight();
+
+        if (mMaxTranslation != oldMaxHeight) {
+            AllAppsContainerView parent = (AllAppsContainerView) getParent();
+            if (parent != null) {
+                parent.setupHeader();
+            }
+        }
     }
 
     @Override
     public void onPluginDisconnected(AllAppsRow plugin) {
-        View pluginRowView = mPluginRows.get(plugin);
-        removeView(pluginRowView);
+        PluginHeaderRow row = mPluginRows.get(plugin);
+        removeView(row.mView);
         mPluginRows.remove(plugin);
-        mPluginRowViews.remove(pluginRowView);
-        onPluginRowHeightUpdated();
+        recreateAllRowsArray();
+        onHeightUpdated();
     }
 
     public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
+        for (FloatingHeaderRow row : mAllRows) {
+            row.setup(this, mAllRows, tabsHidden);
+        }
+        updateExpectedHeight();
+
         mTabsHidden = 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();
         setMainActive(mMainRVActive || mWorkRV == null);
-        setupPluginRows();
         reset(false);
     }
 
-    private void setupPluginRows() {
-        for (Map.Entry<AllAppsRow, View> rowPluginEntry : mPluginRows.entrySet()) {
-            if (rowPluginEntry.getValue() == null) {
-                View pluginRow = rowPluginEntry.getKey().setup(this);
-                addView(pluginRow, indexOfChild(mTabLayout));
-                rowPluginEntry.setValue(pluginRow);
-                mPluginRowViews.add(pluginRow);
-            }
-        }
-        for (View plugin : mPluginRowViews) {
-            plugin.setVisibility(mHeaderCollapsed ? GONE : VISIBLE);
-        }
-    }
-
     private AllAppsRecyclerView setupRV(AllAppsRecyclerView old, AllAppsRecyclerView updated) {
         if (old != updated && updated != null ) {
             updated.addOnScrollListener(mOnScrollListener);
@@ -165,6 +214,16 @@
         return updated;
     }
 
+    private void updateExpectedHeight() {
+        mMaxTranslation = 0;
+        if (mCollapsed) {
+            return;
+        }
+        for (FloatingHeaderRow row : mAllRows) {
+            mMaxTranslation += row.getExpectedHeight();
+        }
+    }
+
     public void setMainActive(boolean active) {
         mCurrentRV = active ? mMainRV : mWorkRV;
         mMainRVActive = active;
@@ -208,12 +267,21 @@
         }
     }
 
-    protected void applyScroll(int uncappedY, int currentY) { }
-
-    protected void apply() {
+    protected void applyVerticalMove() {
         int uncappedTranslationY = mTranslationY;
         mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
-        applyScroll(uncappedTranslationY, mTranslationY);
+
+        if (mCollapsed || uncappedTranslationY < mTranslationY - mHeaderTopPadding) {
+            // we hide it completely if already capped (for opening search anim)
+            for (FloatingHeaderRow row : mAllRows) {
+                row.setVerticalScroll(0, true /* isScrolledOut */);
+            }
+        } else {
+            for (FloatingHeaderRow row : mAllRows) {
+                row.setVerticalScroll(uncappedTranslationY, false /* isScrolledOut */);
+            }
+        }
+
         mTabLayout.setTranslationY(mTranslationY);
         mClip.top = mMaxTranslation + mTranslationY;
         // clipping on a draw might cause additional redraw
@@ -223,6 +291,16 @@
         }
     }
 
+    /**
+     * Hides all the floating rows
+     */
+    public void setCollapsed(boolean collapse) {
+        if (mCollapsed == collapse) return;
+
+        mCollapsed = collapse;
+        onHeightUpdated();
+    }
+
     public void reset(boolean animate) {
         if (mAnimator.isStarted()) {
             mAnimator.cancel();
@@ -234,7 +312,7 @@
             mAnimator.start();
         } else {
             mTranslationY = 0;
-            apply();
+            applyVerticalMove();
         }
         mHeaderCollapsed = false;
         mSnappedScrolledY = -mMaxTranslation;
@@ -248,7 +326,7 @@
     @Override
     public void onAnimationUpdate(ValueAnimator animation) {
         mTranslationY = (Integer) animation.getAnimatedValue();
-        apply();
+        applyVerticalMove();
     }
 
     @Override
@@ -287,8 +365,12 @@
 
     public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter,
             Interpolator fadeInterpolator) {
-        setter.setViewAlpha(this, hasContent ? 1 : 0, fadeInterpolator);
+        for (FloatingHeaderRow row : mAllRows) {
+            row.setContentVisibility(hasHeader, hasContent, setter, fadeInterpolator);
+        }
+
         allowTouchForwarding(hasContent);
+        setter.setFloat(mTabLayout, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
     }
 
     protected void allowTouchForwarding(boolean allow) {
@@ -296,6 +378,11 @@
     }
 
     public boolean hasVisibleContent() {
+        for (FloatingHeaderRow row : mAllRows) {
+            if (row.hasVisibleContent()) {
+                return true;
+            }
+        }
         return false;
     }
 
@@ -303,6 +390,23 @@
     public boolean hasOverlappingRendering() {
         return false;
     }
+
+    @Override
+    public void setInsets(Rect insets) {
+        DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+        for (FloatingHeaderRow row : mAllRows) {
+            row.setInsets(insets, grid);
+        }
+    }
+
+    public <T extends FloatingHeaderRow> T findFixedRowByType(Class<T> type) {
+        for (FloatingHeaderRow row : mAllRows) {
+            if (row.getTypeClass() == type) {
+                return (T) row;
+            }
+        }
+        return null;
+    }
 }
 
 
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
new file mode 100644
index 0000000..b283ff4
--- /dev/null
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -0,0 +1,85 @@
+/*
+ * 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 static android.view.View.ALPHA;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.systemui.plugins.AllAppsRow;
+
+/**
+ * Wrapper over an {@link AllAppsRow} plugin with {@link FloatingHeaderRow} interface so that
+ * it can be easily added in {@link FloatingHeaderView}.
+ */
+public class PluginHeaderRow implements FloatingHeaderRow {
+
+    private final AllAppsRow mPlugin;
+    final View mView;
+
+    PluginHeaderRow(AllAppsRow plugin, FloatingHeaderView parent) {
+        mPlugin = plugin;
+        mView = mPlugin.setup(parent);
+    }
+
+    @Override
+    public void setup(FloatingHeaderView parent, FloatingHeaderRow[] allRows,
+            boolean tabsHidden) { }
+
+    @Override
+    public void setInsets(Rect insets, DeviceProfile grid) { }
+
+    @Override
+    public int getExpectedHeight() {
+        return mPlugin.getExpectedHeight();
+    }
+
+    @Override
+    public boolean shouldDraw() {
+        return true;
+    }
+
+    @Override
+    public boolean hasVisibleContent() {
+        return true;
+    }
+
+    @Override
+    public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
+            PropertySetter setter, Interpolator fadeInterpolator) {
+        // Don't use setViewAlpha as we want to control the visibility ourselves.
+        setter.setFloat(mView, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+    }
+
+    @Override
+    public void setVerticalScroll(int scroll, boolean isScrolledOut) {
+        mView.setVisibility(isScrolledOut ? INVISIBLE : VISIBLE);
+        if (!isScrolledOut) {
+            mView.setTranslationY(scroll);
+        }
+    }
+
+    @Override
+    public Class<PluginHeaderRow> getTypeClass() {
+        return PluginHeaderRow.class;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/icons/IconCacheUpdateHandler.java b/src/com/android/launcher3/icons/IconCacheUpdateHandler.java
index c679da4..0c601b9 100644
--- a/src/com/android/launcher3/icons/IconCacheUpdateHandler.java
+++ b/src/com/android/launcher3/icons/IconCacheUpdateHandler.java
@@ -27,11 +27,8 @@
 import android.util.Log;
 import android.util.SparseBooleanArray;
 
-import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.BaseIconCache.IconDB;
-import com.android.launcher3.util.IntArray;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -204,18 +201,31 @@
         }
     }
 
+    /**
+     * Commits all updates as part of the update handler to disk. Not more calls should be made
+     * to this class after this.
+     */
     public void finish() {
         // Commit all deletes
-        IntArray deleteIds = new IntArray();
+        int deleteCount = 0;
+        StringBuilder queryBuilder = new StringBuilder()
+                .append(IconDB.COLUMN_ROWID)
+                .append(" IN (");
+
         int count = mItemsToDelete.size();
         for (int i = 0;  i < count; i++) {
             if (mItemsToDelete.valueAt(i)) {
-                deleteIds.add(mItemsToDelete.keyAt(i));
+                if (deleteCount > 0) {
+                    queryBuilder.append(", ");
+                }
+                queryBuilder.append(mItemsToDelete.keyAt(i));
+                deleteCount++;
             }
         }
-        if (!deleteIds.isEmpty()) {
-            mIconCache.mIconDb.delete(
-                    Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, deleteIds), null);
+        queryBuilder.append(')');
+
+        if (deleteCount > 0) {
+            mIconCache.mIconDb.delete(queryBuilder.toString(), null);
         }
     }