Add hints in Overview

If the ENABLE_HINTS_IN_OVERVIEW flag is enabled, pulls chip hint
data (currently from the NavBarHint plugin; eventually from AiAi)
and shows the chips between the task view screenshot and the QSB.

Screenshot: https://screenshot.googleplex.com/Ww05W13XjBv

BUG:124390101

Change-Id: I7686673b705257eca31f2fa40e2744e197153c7c
diff --git a/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml b/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml
new file mode 100644
index 0000000..7b4da83
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <stroke
+        android:color="@color/chip_hint_foreground_color"
+        android:width="@dimen/chip_hint_border_width"/>
+    <corners android:radius="@dimen/chip_hint_corner_radius"/>
+    <padding
+        android:left="@dimen/chip_hint_outer_padding"
+        android:top="@dimen/chip_hint_outer_padding"
+        android:right="@dimen/chip_hint_outer_padding"
+        android:bottom="@dimen/chip_hint_outer_padding"/>
+</shape>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/layout/hint.xml b/quickstep/recents_ui_overrides/res/layout/hint.xml
new file mode 100644
index 0000000..7e2d6af
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/hint.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.quickstep.hints.HintView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/chip_hint_height"
+    android:layout_gravity="center_horizontal|bottom"
+    android:paddingStart="@dimen/chip_hint_start_padding"
+    android:paddingEnd="@dimen/chip_hint_end_padding"
+    android:background="@drawable/chip_hint_background_light"
+    android:gravity="center"
+    android:layout_marginHorizontal="@dimen/chip_hint_horizontal_margin"
+    android:orientation="horizontal"
+    android:elevation="@dimen/chip_hint_elevation"
+    android:layoutDirection="ltr">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="@dimen/chip_icon_size"
+        android:layout_height="@dimen/chip_icon_size"
+        android:visibility="gone"
+        android:scaleType="fitCenter"
+        android:adjustViewBounds="true"
+        android:contentDescription="@null"/>
+
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/chip_text_height"
+        android:paddingTop="@dimen/chip_text_top_padding"
+        android:paddingStart="@dimen/chip_text_start_padding"
+        android:fontFamily="google-sans-medium"
+        android:textAlignment="textStart"
+        android:singleLine="true"
+        android:textColor="@color/chip_hint_foreground_color"
+        android:textSize="@dimen/chip_text_size"
+        android:ellipsize="none"
+        android:includeFontPadding="true"/>
+
+
+</com.android.quickstep.hints.HintView>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/layout/hint_container.xml b/quickstep/recents_ui_overrides/res/layout/hint_container.xml
new file mode 100644
index 0000000..336f63e
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/hint_container.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.quickstep.hints.HintsContainer
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:orientation="horizontal"/>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/colors.xml b/quickstep/recents_ui_overrides/res/values/colors.xml
new file mode 100644
index 0000000..1e8d0cc
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/values/colors.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="chip_hint_foreground_color">#fff</color>
+</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
new file mode 100644
index 0000000..b654d5c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <dimen name="chip_hint_border_width">1dp</dimen>
+    <dimen name="chip_hint_corner_radius">20dp</dimen>
+    <dimen name="chip_hint_outer_padding">20dp</dimen>
+    <dimen name="chip_hint_start_padding">10dp</dimen>
+    <dimen name="chip_hint_end_padding">12dp</dimen>
+    <dimen name="chip_hint_horizontal_margin">20dp</dimen>
+    <dimen name="chip_hint_elevation">2dp</dimen>
+    <dimen name="chip_icon_size">16dp</dimen>
+    <dimen name="chip_text_height">26dp</dimen>
+    <dimen name="chip_text_top_padding">4dp</dimen>
+    <dimen name="chip_text_start_padding">10dp</dimen>
+    <dimen name="chip_text_size">14sp</dimen>
+</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 1a61be6..0b3bd6c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -23,6 +23,8 @@
 import android.os.Build;
 import android.util.FloatProperty;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
@@ -30,8 +32,6 @@
 import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsView;
 
-import androidx.annotation.NonNull;
-
 /**
  * State handler for handling UI changes for {@link LauncherRecentsView}. In addition to managing
  * the basic view properties, this class also manages changes in the task visuals.
@@ -50,6 +50,9 @@
         if (state.overviewUi) {
             mRecentsView.updateEmptyMessage();
             mRecentsView.resetTaskVisuals();
+            mRecentsView.setHintVisibility(1);
+        } else {
+            mRecentsView.setHintVisibility(0);
         }
     }
 
@@ -60,6 +63,7 @@
 
         if (!toState.overviewUi) {
             builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);
+            mRecentsView.setHintVisibility(0);
         }
 
         if (toState.overviewUi) {
@@ -71,6 +75,7 @@
             updateAnim.setDuration(config.duration);
             builder.play(updateAnim);
             mRecentsView.updateEmptyMessage();
+            builder.addOnFinishRunnable(() -> mRecentsView.setHintVisibility(1));
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/hints/HintUtil.java b/quickstep/src/com/android/quickstep/hints/HintUtil.java
new file mode 100644
index 0000000..f2d40ec
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/hints/HintUtil.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.hints;
+
+import android.app.PendingIntent;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+public final class HintUtil {
+
+    public static final String ID_KEY = "id";
+    public static final String ICON_KEY = "icon";
+    public static final String TEXT_KEY = "text";
+    public static final String TAP_ACTION_KEY = "tap_action";
+
+    private HintUtil() {}
+
+    public static Bundle makeHint(String id, Icon icon, CharSequence text) {
+        Bundle hint = new Bundle();
+        hint.putString(ID_KEY, id);
+        hint.putParcelable(ICON_KEY, icon);
+        hint.putCharSequence(TEXT_KEY, text);
+        return hint;
+    }
+
+    public static Bundle makeHint(Icon icon, CharSequence text, PendingIntent tapAction) {
+        Bundle hint = new Bundle();
+        hint.putParcelable(ICON_KEY, icon);
+        hint.putCharSequence(TEXT_KEY, text);
+        hint.putParcelable(TAP_ACTION_KEY, tapAction);
+        return hint;
+    }
+
+    public static String getId(Bundle hint) {
+        String id = hint.getString(ID_KEY);
+        if (id == null) {
+            throw new IllegalArgumentException("Hint does not contain an ID");
+        }
+        return id;
+    }
+
+    public static Icon getIcon(Bundle hint) {
+        Icon icon = hint.getParcelable(ICON_KEY);
+        if (icon == null) {
+            throw new IllegalArgumentException("Hint does not contain an icon");
+        }
+        return icon;
+    }
+
+    public static CharSequence getText(Bundle hint) {
+        CharSequence text = hint.getCharSequence(TEXT_KEY);
+        if (text == null) {
+            throw new IllegalArgumentException("Hint does not contain text");
+        }
+        return text;
+    }
+
+    public static PendingIntent getTapAction(Bundle hint) {
+        PendingIntent tapAction = hint.getParcelable(TAP_ACTION_KEY);
+        if (tapAction == null) {
+            throw new IllegalArgumentException("Hint does not contain a tap action");
+        }
+        return tapAction;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/hints/HintView.java b/quickstep/src/com/android/quickstep/hints/HintView.java
new file mode 100644
index 0000000..5399cc4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/hints/HintView.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.hints;
+
+import static com.android.quickstep.hints.HintUtil.getIcon;
+import static com.android.quickstep.hints.HintUtil.getText;
+
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+
+public class HintView extends LinearLayout {
+    private ImageView mIconView;
+    private TextView mLabelView;
+
+    public HintView(Context context) {
+        super(context);
+    }
+
+    public HintView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public HintView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public HintView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public void setHint(Bundle hint) {
+        mLabelView.setText(getText(hint));
+
+        Icon icon = getIcon(hint);
+        if (icon == null) {
+            mIconView.setVisibility(GONE);
+        } else {
+            mIconView.setImageIcon(icon);
+            mIconView.setVisibility(VISIBLE);
+        }
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mIconView = findViewById(R.id.icon);
+        mLabelView = findViewById(R.id.label);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/hints/HintsContainer.java b/quickstep/src/com/android/quickstep/hints/HintsContainer.java
new file mode 100644
index 0000000..22b1217
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/hints/HintsContainer.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.hints;
+
+import static com.android.quickstep.hints.UiHintListenerConstants.HINTS_KEY;
+import static com.android.quickstep.hints.UiHintListenerConstants.ON_HINTS_RETURNED_CODE;
+import static com.android.quickstep.hints.UiInterfaceConstants.REQUEST_HINTS_CODE;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+
+import java.util.ArrayList;
+
+public class HintsContainer extends LinearLayout {
+
+    private static final String TAG = "HintsView";
+
+    public static final FloatProperty<HintsContainer> HINT_VISIBILITY =
+            new FloatProperty<HintsContainer>("hint_visibility") {
+                @Override
+                public void setValue(HintsContainer hintsContainer, float v) {
+                    hintsContainer.setHintVisibility(v);
+                }
+
+                @Override
+                public Float get(HintsContainer hintsContainer) {
+                    return hintsContainer.mHintVisibility;
+                }
+            };
+
+    private static Intent mServiceIntent =
+            new Intent("com.android.systemui.action.UI_PULL_INTERFACE")
+                    .setClassName(
+                            "com.android.systemui.navbarhint",
+                            "com.android.systemui.navbarhint.service.HintService");
+
+    @Nullable
+    private Messenger mHintServiceInterface;
+    private UiHintListener mUiHintListener;
+    private boolean mBound = false;
+    private float mHintVisibility;
+
+    private final ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+            mHintServiceInterface = new Messenger(iBinder);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+            mHintServiceInterface = null;
+            attemptBinding();
+        }
+
+        @Override
+        public void onBindingDied(ComponentName componentName) {
+            mHintServiceInterface = null;
+            attemptBinding();
+        }
+    };
+
+    public HintsContainer(Context context) {
+        super(context);
+    }
+
+    public HintsContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public HintsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public HintsContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mUiHintListener == null) {
+            mUiHintListener = new UiHintListener(this);
+        }
+        if (!mBound) {
+            attemptBinding();
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if (mBound) {
+            getContext().unbindService(mServiceConnection);
+            mBound = false;
+        }
+        super.onDetachedFromWindow();
+    }
+
+    public void setHintVisibility(float v) {
+        if (v == 1) {
+            getHints();
+            setVisibility(VISIBLE);
+        } else {
+            setVisibility(GONE);
+        }
+        mHintVisibility = v;
+    }
+
+    private void attemptBinding() {
+        if (mBound) {
+            getContext().unbindService(mServiceConnection);
+            mBound = false;
+        }
+        boolean success = getContext().bindService(mServiceIntent,
+                mServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
+        if (success) {
+            mBound = true;
+        } else {
+            Log.w(TAG, "Binding to hint supplier failed");
+        }
+    }
+    
+    private void sendOnHintTap(Bundle hint) {
+        if (mHintServiceInterface != null) {
+            Message msg = Message.obtain(null, UiInterfaceConstants.ON_HINT_TAP_CODE);
+            Bundle data = new Bundle();
+            data.putString(UiInterfaceConstants.HINT_ID_KEY, HintUtil.getId(hint));
+            data.putInt(UiInterfaceConstants.WIDTH_PX_KEY, getWidth());
+            data.putInt(UiInterfaceConstants.HEIGHT_PX_KEY, getHeight());
+            data.putInt(UiInterfaceConstants.HINT_SPACE_WIDTH_PX_KEY, 0);
+            data.putInt(UiInterfaceConstants.HINT_SPACE_HEIGHT_PX_KEY, 0);
+            msg.setData(data);
+            try {
+                mHintServiceInterface.send(msg);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to send hint tap", e);
+            }
+        }
+    }
+
+    private void getHints() {
+        if (mHintServiceInterface != null) {
+            try {
+                Message m = Message.obtain(null, REQUEST_HINTS_CODE);
+                m.replyTo = new Messenger(mUiHintListener);
+                mHintServiceInterface.send(m);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to send message", e);
+            }
+        }
+    }
+
+    private static class UiHintListener extends Handler {
+        private HintsContainer mView;
+
+        UiHintListener(HintsContainer v) {
+            mView = v;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case ON_HINTS_RETURNED_CODE:
+                    handleHints(msg);
+                    break;
+                default:
+                    Log.e(TAG, "UiPullInterface got unrecognized code: " + msg.what);
+                    break;
+            }
+        }
+
+        private void handleHints(Message msg) {
+            Bundle bundle = msg.getData();
+            ArrayList<Bundle> hints = bundle.getParcelableArrayList(HINTS_KEY);
+
+            if (hints != null) {
+                mView.removeAllViews();
+
+                for (Bundle hint : hints) {
+                    HintView h = (HintView) LayoutInflater.from(mView.getContext()).inflate(
+                            R.layout.hint, mView, false);
+                    h.setHint(hint);
+                    h.setOnClickListener((v) -> mView.sendOnHintTap(hint));
+                    mView.addView(h);
+                }
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/hints/UiHintListenerConstants.java b/quickstep/src/com/android/quickstep/hints/UiHintListenerConstants.java
new file mode 100644
index 0000000..420033d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/hints/UiHintListenerConstants.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.hints;
+
+public final class UiHintListenerConstants {
+
+    private UiHintListenerConstants() {}
+
+    // Operations
+    public static final int ON_HINTS_RETURNED_CODE = 5;
+
+    // Keys
+    public static final String SESSION_ID_KEY = "session_id";
+    public static final String HINTS_KEY = "hints";
+}
diff --git a/quickstep/src/com/android/quickstep/hints/UiInterfaceConstants.java b/quickstep/src/com/android/quickstep/hints/UiInterfaceConstants.java
new file mode 100644
index 0000000..0140613
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/hints/UiInterfaceConstants.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.hints;
+
+public final class UiInterfaceConstants {
+
+    private UiInterfaceConstants() {}
+
+    // Operations
+    public static final int ON_HINT_TAP_CODE = 4;
+
+    public static final int REQUEST_HINTS_CODE = 7;
+
+    // Keys
+    public static final String SESSION_ID_KEY = "session_id";
+    public static final String HINT_ID_KEY = "hint_id";
+    public static final String WIDTH_PX_KEY = "width_px";
+    public static final String HEIGHT_PX_KEY = "height_px";
+    public static final String HINT_SPACE_WIDTH_PX_KEY = "hint_space_width_px";
+    public static final String HINT_SPACE_HEIGHT_PX_KEY = "hint_space_height_px";
+}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index ed585c1..a06209a 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -43,6 +43,8 @@
         float extraSpace;
         if (dp.isVerticalBarLayout()) {
             extraSpace = 0;
+        } else if (FeatureFlags.ENABLE_HINTS_IN_OVERVIEW.get()){
+            extraSpace = dp.hotseatBarSizePx + dp.verticalDragHandleSizePx + dp.chipHintHeightPx;
         } else {
             extraSpace = dp.hotseatBarSizePx + dp.verticalDragHandleSizePx;
         }
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index d2adef7..88fe2ee 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 
 import android.animation.AnimatorSet;
@@ -40,8 +41,10 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.util.PendingAnimation;
 import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.hints.HintsContainer;
 import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
 import com.android.quickstep.util.LayoutUtils;
@@ -74,6 +77,7 @@
     private float mTranslationYFactor;
 
     private final TransformParams mTransformParams = new TransformParams();
+    private HintsContainer mHintsContainer;
 
     public LauncherRecentsView(Context context) {
         this(context, null);
@@ -104,6 +108,13 @@
         setTranslationYFactor(mTranslationYFactor);
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mHintsContainer = mActivity.findViewById(R.id.hints);
+        mHintsContainer.setPadding(0, 0, 0, mActivity.getDeviceProfile().chipHintBottomMarginPx);
+    }
+
     public void setTranslationYFactor(float translationFactor) {
         mTranslationYFactor = translationFactor;
         setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
@@ -119,6 +130,12 @@
         return translationYFactor * (getPaddingBottom() - getPaddingTop());
     }
 
+    public void setHintVisibility(float v) {
+        if (mHintsContainer != null && ENABLE_HINTS_IN_OVERVIEW.get()) {
+            mHintsContainer.setHintVisibility(v);
+        }
+    }
+
     @Override
     public void draw(Canvas canvas) {
         maybeDrawEmptyMessage(canvas);
@@ -170,6 +187,37 @@
     }
 
     @Override
+    public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
+        PendingAnimation anim = super.createTaskLauncherAnimation(tv, duration);
+
+        if (ENABLE_HINTS_IN_OVERVIEW.get()) {
+            anim.anim.play(ObjectAnimator.ofFloat(
+                    mHintsContainer, HintsContainer.HINT_VISIBILITY, 0));
+        }
+
+        return anim;
+    }
+
+    @Override
+    public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
+            boolean shouldRemoveTask, long duration) {
+        PendingAnimation anim = super.createTaskDismissAnimation(taskView, animateTaskView,
+                shouldRemoveTask, duration);
+
+        if (ENABLE_HINTS_IN_OVERVIEW.get()) {
+            anim.anim.play(ObjectAnimator.ofFloat(
+                    mHintsContainer, HintsContainer.HINT_VISIBILITY, 0));
+            anim.addEndListener(onEndListener -> {
+                if (!onEndListener.isSuccess) {
+                    mHintsContainer.setHintVisibility(1);
+                }
+            });
+        }
+
+        return anim;
+    }
+
+    @Override
     protected void getTaskSize(DeviceProfile dp, Rect outRect) {
         LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
     }
diff --git a/res/layout/hint_container.xml b/res/layout/hint_container.xml
new file mode 100644
index 0000000..75aa913
--- /dev/null
+++ b/res/layout/hint_container.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<merge/>
\ No newline at end of file
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 87078b9..91c3705 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -43,6 +43,11 @@
             layout="@layout/overview_panel"
             android:visibility="gone" />
 
+        <include
+            android:id="@+id/hints"
+            layout="@layout/hint_container"
+            android:visibility="gone"/>
+
         <!-- Keep these behind the workspace so that they are not visible when
          we go into AllApps -->
         <com.android.launcher3.pageindicators.WorkspacePageIndicator
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 078ce60..04e4591 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -236,4 +236,8 @@
     <dimen name="snackbar_elevation">3dp</dimen>
     <dimen name="snackbar_min_text_size">12sp</dimen>
     <dimen name="snackbar_max_text_size">14sp</dimen>
+
+<!-- Hints -->
+    <dimen name="chip_hint_height">26dp</dimen>
+    <dimen name="chip_hint_bottom_margin">194dp</dimen>
 </resources>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 296c951..7aea870 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -103,6 +103,10 @@
     public int folderChildTextSizePx;
     public int folderChildDrawablePaddingPx;
 
+    // Hints
+    public int chipHintHeightPx;
+    public int chipHintBottomMarginPx;
+
     // Hotseat
     public int hotseatCellHeightPx;
     // In portrait: size = height, in landscape: size = width
@@ -200,6 +204,9 @@
 
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
+        chipHintHeightPx = res.getDimensionPixelSize(R.dimen.chip_hint_height);
+        chipHintBottomMarginPx = res.getDimensionPixelSize(R.dimen.chip_hint_bottom_margin);
+
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         hotseatBarBottomPaddingPx = (isTallDevice ? 0
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index cf16759..f571aa3 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -257,7 +257,6 @@
 
     private RotationHelper mRotationHelper;
 
-
     private final Handler mHandler = new Handler();
     private final Runnable mHandleDeferredResume = this::handleDeferredResume;
 
@@ -756,6 +755,7 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStop();
         }
+
         getUserEventDispatcher().logActionCommand(Action.Command.STOP,
                 mStateManager.getState().containerType, -1);
 
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 61467e0..882529d 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -108,6 +108,10 @@
             = new ToggleableGlobalSettingsFlag("SWIPE_HOME", false,
             "Swiping up on the nav bar goes home. Swipe and hold goes to recent apps.");
 
+    public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag(
+            "ENABLE_HINTS_IN_OVERVIEW", false,
+            "Show chip hints and gleams on the overview screen");
+
     public static void initialize(Context context) {
         // Avoid the disk read for user builds
         if (Utilities.IS_DEBUG_DEVICE) {