Support People search result in launcher

NOTE: this uses PackageManager and should be removed after people<>shortcut mapping in search provider.

Bug: 166337069
Test: Manual
Change-Id: I016ff0d5e6ada1599cce3d75bdcd55347c85193e
diff --git a/res/layout/search_result_people_item.xml b/res/layout/search_result_people_item.xml
new file mode 100644
index 0000000..a505e4a
--- /dev/null
+++ b/res/layout/search_result_people_item.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.views.SearchResultPeopleView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:gravity="center_vertical"
+    android:layout_height="wrap_content"
+    android:padding="8dp"
+    android:orientation="horizontal">
+
+    <View
+        android:id="@+id/icon"
+        android:layout_marginRight="8dp"
+        android:layout_width="@dimen/default_icon_bitmap_size"
+        android:layout_height="@dimen/default_icon_bitmap_size" />
+
+    <TextView
+        android:layout_width="0dp"
+        android:textColor="?android:attr/textColorPrimary"
+        android:id="@+id/title"
+        android:layout_height="wrap_content"
+        android:layout_weight="1" />
+
+    <ImageButton
+        android:id="@+id/provider_0"
+        android:scaleType="fitCenter"
+        android:adjustViewBounds="true"
+        android:layout_margin="5dp"
+        android:background="?android:attr/selectableItemBackground"
+        android:layout_width="@dimen/default_icon_bitmap_size"
+        android:layout_height="@dimen/default_icon_bitmap_size" />
+
+    <ImageButton
+        android:id="@+id/provider_1"
+        android:layout_margin="5dp"
+        android:scaleType="fitCenter"
+        android:adjustViewBounds="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:layout_width="@dimen/default_icon_bitmap_size"
+        android:layout_height="@dimen/default_icon_bitmap_size" />
+
+    <ImageButton
+        android:id="@+id/provider_2"
+        android:layout_margin="5dp"
+        android:scaleType="fitCenter"
+        android:adjustViewBounds="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:layout_width="@dimen/default_icon_bitmap_size"
+        android:layout_height="@dimen/default_icon_bitmap_size" />
+
+</com.android.launcher3.views.SearchResultPeopleView>
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index ad6aa63..4e801a7 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -91,6 +91,8 @@
 
     public static final int VIEW_TYPE_SEARCH_SHORTCUT = 1 << 10;
 
+    public static final int VIEW_TYPE_SEARCH_PEOPLE = 1 << 11;
+
     // Common view type masks
     public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
     public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
@@ -182,6 +184,7 @@
                     || viewType == VIEW_TYPE_SEARCH_ROW_WITH_BUTTON
                     || viewType == VIEW_TYPE_SEARCH_SLICE
                     || viewType == VIEW_TYPE_SEARCH_ROW
+                    || viewType == VIEW_TYPE_SEARCH_PEOPLE
                     || viewType == VIEW_TYPE_SEARCH_SHORTCUT;
         }
     }
@@ -432,6 +435,9 @@
             case VIEW_TYPE_SEARCH_SHORTCUT:
                 return new ViewHolder(mLayoutInflater.inflate(
                         R.layout.search_result_shortcut, parent, false));
+            case VIEW_TYPE_SEARCH_PEOPLE:
+                return new ViewHolder(mLayoutInflater.inflate(
+                        R.layout.search_result_people_item, parent, false));
             default:
                 throw new RuntimeException("Unexpected view type");
         }
@@ -475,6 +481,7 @@
             case VIEW_TYPE_SEARCH_HERO_APP:
             case VIEW_TYPE_SEARCH_ROW:
             case VIEW_TYPE_SEARCH_SHORTCUT:
+            case VIEW_TYPE_SEARCH_PEOPLE:
                 PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
                 payloadResultView.applyAdapterInfo(
                         (AdapterItemWithPayload) mApps.getAdapterItems().get(position));
diff --git a/src/com/android/launcher3/views/SearchResultPeopleView.java b/src/com/android/launcher3/views/SearchResultPeopleView.java
new file mode 100644
index 0000000..d1e34fa
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchResultPeopleView.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import static android.content.Intent.URI_ALLOW_UNSAFE;
+import static android.content.Intent.URI_ANDROID_APP_SCHEME;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.util.Themes;
+
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+
+/**
+ * A view representing a single people search result in all apps
+ */
+public class SearchResultPeopleView extends LinearLayout implements
+        AllAppsSearchBarController.PayloadResultHandler<Bundle> {
+
+    private final int mIconSize;
+    private final int mButtonSize;
+    private final PackageManager mPackageManager;
+    View mIconView;
+    TextView mTitleView;
+    ImageButton[] mProviderButtons = new ImageButton[3];
+
+    public SearchResultPeopleView(Context context) {
+        this(context, null, 0);
+    }
+
+    public SearchResultPeopleView(Context context,
+            @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SearchResultPeopleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        DeviceProfile deviceProfile = Launcher.getLauncher(getContext()).getDeviceProfile();
+        mPackageManager = getContext().getPackageManager();
+        mIconSize = deviceProfile.iconSizePx;
+        mButtonSize = (int) (deviceProfile.iconSizePx / 1.5f);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mIconView = findViewById(R.id.icon);
+        mIconView.getLayoutParams().height = mIconSize;
+        mIconView.getLayoutParams().width = mIconSize;
+        mTitleView = findViewById(R.id.title);
+        mProviderButtons[0] = findViewById(R.id.provider_0);
+        mProviderButtons[1] = findViewById(R.id.provider_1);
+        mProviderButtons[2] = findViewById(R.id.provider_2);
+        for (ImageButton button : mProviderButtons) {
+            button.getLayoutParams().width = mButtonSize;
+            button.getLayoutParams().height = mButtonSize;
+        }
+        setOnClickListener(v -> handleSelection());
+    }
+
+    @Override
+    public void applyAdapterInfo(
+            AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
+        Launcher launcher = Launcher.getLauncher(getContext());
+        Bundle payload = adapterItemWithPayload.getPayload();
+        mTitleView.setText(payload.getString("title"));
+        mContactUri = payload.getParcelable("contact_uri");
+        Bitmap icon = payload.getParcelable("icon");
+        if (icon != null) {
+            RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
+            float radius = Themes.getDialogCornerRadius(getContext());
+            d.setCornerRadius(radius);
+            d.setBounds(0, 0, mIconSize, mIconSize);
+            BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),
+                    Bitmap.createScaledBitmap(icon, mIconSize, mIconSize, false));
+            mIconView.setBackground(d);
+        }
+
+        ArrayList<Bundle> providers = payload.getParcelableArrayList("providers");
+        for (int i = 0; i < mProviderButtons.length; i++) {
+            ImageButton button = mProviderButtons[i];
+            if (providers != null && i < providers.size()) {
+                try {
+                    Bundle provider = providers.get(i);
+                    Intent intent = Intent.parseUri(provider.getString("intent_uri_str"),
+                            URI_ANDROID_APP_SCHEME | URI_ALLOW_UNSAFE);
+                    String pkg = provider.getString("package_name");
+                    button.setOnClickListener(b -> launcher.startActivitySafely(b, intent, null));
+                    UI_HELPER_EXECUTOR.post(() -> {
+                        try {
+                            ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
+                                    pkg, 0);
+                            Drawable appIcon = applicationInfo.loadIcon(mPackageManager);
+                            button.setImageDrawable(appIcon);
+                        } catch (PackageManager.NameNotFoundException ignored) {
+                        }
+
+                    });
+                } catch (URISyntaxException ex) {
+                    button.setVisibility(GONE);
+                }
+            } else {
+                button.setVisibility(GONE);
+            }
+        }
+        adapterItemWithPayload.setSelectionHandler(this::handleSelection);
+    }
+
+    Uri mContactUri;
+
+    private void handleSelection() {
+        if (mContactUri != null) {
+            Launcher launcher = Launcher.getLauncher(getContext());
+            launcher.startActivitySafely(this, new Intent(Intent.ACTION_VIEW, mContactUri).setFlags(
+                    Intent.FLAG_ACTIVITY_NEW_TASK), null);
+        }
+    }
+}