DeepShortcut - show loading placeholder before content is available.

Sometimes the deep shortcuts can take time to load, show an initial loading
state before any text is set.

Note: changed singleLine to lines=1 as the former causes issues with RTL
layout and is deprecated.

Bug: 78620208
Tested: manual test on pixel 3

Change-Id: I782addb8d64425a1a95cee4f104fc0bd44809815
diff --git a/res/drawable-v26/ic_deepshortcut_placeholder.xml b/res/drawable-v26/ic_deepshortcut_placeholder.xml
new file mode 100644
index 0000000..3fa8506
--- /dev/null
+++ b/res/drawable-v26/ic_deepshortcut_placeholder.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="?attr/popupColorSecondary"/>
+    <foreground android:drawable="?attr/popupColorSecondary"/>
+</adaptive-icon>
diff --git a/res/drawable/deep_shortcuts_text_placeholder.xml b/res/drawable/deep_shortcuts_text_placeholder.xml
new file mode 100644
index 0000000..99da50f
--- /dev/null
+++ b/res/drawable/deep_shortcuts_text_placeholder.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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">
+    <solid android:color="?attr/popupColorSecondary" />
+    <corners android:radius="8dp" />
+    <size android:height="16dp" />
+</shape>
diff --git a/res/drawable/ic_deepshortcut_placeholder.xml b/res/drawable/ic_deepshortcut_placeholder.xml
new file mode 100644
index 0000000..85a9694
--- /dev/null
+++ b/res/drawable/ic_deepshortcut_placeholder.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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="oval">
+    <solid android:color="?attr/popupColorSecondary" />
+    <size
+        android:height="32dp"
+        android:width="32dp" />
+</shape>
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 4a2ad42..92f70e6 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -43,7 +43,8 @@
         android:layout_width="@dimen/deep_shortcut_icon_size"
         android:layout_height="@dimen/deep_shortcut_icon_size"
         android:layout_marginStart="@dimen/popup_padding_start"
-        android:layout_gravity="start|center_vertical" />
+        android:layout_gravity="start|center_vertical"
+        android:background="@drawable/ic_deepshortcut_placeholder"/>
 
     <View
         android:id="@+id/divider"
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7b11891..e6791aa 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -144,7 +144,7 @@
         <item name="android:layout_gravity">center</item>
         <item name="android:focusable">true</item>
         <item name="android:gravity">center_horizontal</item>
-        <item name="android:singleLine">true</item>
+        <item name="android:lines">1</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:fontFamily">sans-serif-condensed</item>
         <item name="android:defaultFocusHighlightEnabled">false</item>
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index c809f27..2daa2fe 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -18,8 +18,10 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.widget.Toast;
@@ -38,6 +40,10 @@
 
     private Toast mInstructionToast;
 
+    private boolean mShowLoadingState;
+    private Drawable mLoadingStatePlaceholder;
+    private final Rect mLoadingStateBounds = new Rect();
+
     public DeepShortcutTextView(Context context) {
         this(context, null, 0);
     }
@@ -53,6 +59,7 @@
         mDragHandleWidth = resources.getDimensionPixelSize(R.dimen.popup_padding_end)
                 + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drag_handle_size)
                 + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drawable_padding) / 2;
+        showLoadingState(true);
     }
 
     @Override
@@ -63,6 +70,25 @@
         if (!Utilities.isRtl(getResources())) {
             mDragHandleBounds.offset(getMeasuredWidth() - mDragHandleBounds.width(), 0);
         }
+
+        setLoadingBounds();
+    }
+
+    private void setLoadingBounds() {
+        if (mLoadingStatePlaceholder == null) {
+            return;
+        }
+        mLoadingStateBounds.set(
+                0,
+                0,
+                getMeasuredWidth() - mDragHandleWidth - getPaddingStart(),
+                mLoadingStatePlaceholder.getIntrinsicHeight());
+        mLoadingStateBounds.offset(
+                Utilities.isRtl(getResources()) ? mDragHandleWidth : getPaddingStart(),
+                (int) ((getMeasuredHeight() - mLoadingStatePlaceholder.getIntrinsicHeight())
+                        / 2.0f)
+        );
+        mLoadingStatePlaceholder.setBounds(mLoadingStateBounds);
     }
 
     @Override
@@ -71,6 +97,15 @@
     }
 
     @Override
+    public void setText(CharSequence text, BufferType type) {
+        super.setText(text, type);
+
+        if (!TextUtils.isEmpty(text)) {
+            showLoadingState(false);
+        }
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             // Show toast if user touches the drag handle (long clicks still start the drag).
@@ -88,6 +123,34 @@
         return super.performClick();
     }
 
+    @Override
+    public void onDraw(Canvas canvas) {
+        if (!mShowLoadingState) {
+            super.onDraw(canvas);
+            return;
+        }
+
+        mLoadingStatePlaceholder.draw(canvas);
+    }
+
+    private void showLoadingState(boolean loading) {
+        if (loading == mShowLoadingState) {
+            return;
+        }
+
+        mShowLoadingState = loading;
+
+        if (loading) {
+            mLoadingStatePlaceholder = getContext().getDrawable(
+                    R.drawable.deep_shortcuts_text_placeholder);
+            setLoadingBounds();
+        } else {
+            mLoadingStatePlaceholder = null;
+        }
+
+        invalidate();
+    }
+
     private void showToast() {
         if (mInstructionToast != null) {
             mInstructionToast.cancel();