Added user icon and (device | profile) owner info on KitchenSink USER screen.

Also created a UserAvatarView on car-admin-ui-lib, so it properly
shows the managed user badge when needed.

Test: manual verification
Bug: 175214581
Bug: 176262528

Change-Id: I6c412c909830bbbc0cea366a175579721d2630ab
diff --git a/car-admin-ui-lib/Android.bp b/car-admin-ui-lib/Android.bp
index 930278a..3b68619 100644
--- a/car-admin-ui-lib/Android.bp
+++ b/car-admin-ui-lib/Android.bp
@@ -23,5 +23,10 @@
     srcs: [
         "src/**/*.java",
     ],
-    resource_dirs: ["src/main/res"],
+    libs: [
+        "SettingsLib"
+    ],
+    resource_dirs: [
+        "src/main/res"
+    ],
 }
diff --git a/car-admin-ui-lib/src/main/java/com/android/car/admin/ui/UserAvatarView.java b/car-admin-ui-lib/src/main/java/com/android/car/admin/ui/UserAvatarView.java
new file mode 100644
index 0000000..a011e24
--- /dev/null
+++ b/car-admin-ui-lib/src/main/java/com/android/car/admin/ui/UserAvatarView.java
@@ -0,0 +1,138 @@
+/*
+ * 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.car.admin.ui;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.settingslib.drawable.UserIconDrawable;
+
+// TODO(b/176262528): copied from com.android.systemui, ideally it should be provided by a common
+// library like SettingsLib. If not, then this whole project / package should be renamed to
+// "car-user-ui-lib", not "car-admin-ui-lib".
+
+/**
+ * A view that displays a user image cropped to a circle with an optional frame.
+ */
+public class UserAvatarView extends View {
+
+    private final UserIconDrawable mDrawable = new UserIconDrawable();
+
+    public UserAvatarView(Context context, AttributeSet attrs,
+            int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
+        final int N = a.getIndexCount();
+        for (int i = 0; i < N; i++) {
+            int attr = a.getIndex(i);
+            if (attr == R.styleable.UserAvatarView_avatarPadding) {
+                setAvatarPadding(a.getDimension(attr, 0));
+            } else if (attr == R.styleable.UserAvatarView_frameWidth) {
+                setFrameWidth(a.getDimension(attr, 0));
+            } else if (attr == R.styleable.UserAvatarView_framePadding) {
+                setFramePadding(a.getDimension(attr, 0));
+            } else if (attr == R.styleable.UserAvatarView_frameColor) {
+                setFrameColor(a.getColorStateList(attr));
+            } else if (attr == R.styleable.UserAvatarView_badgeDiameter) {
+                setBadgeDiameter(a.getDimension(attr, 0));
+            } else if (attr == R.styleable.UserAvatarView_badgeMargin) {
+                setBadgeMargin(a.getDimension(attr, 0));
+            }
+            else {
+                setBadgeDiameter(a.getDimension(attr, 0));
+            }
+        }
+        a.recycle();
+        setBackground(mDrawable);
+    }
+
+    public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public UserAvatarView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public UserAvatarView(Context context) {
+        this(context, null);
+    }
+
+    @Override
+    public void setActivated(boolean activated) {
+        super.setActivated(activated);
+        mDrawable.invalidateSelf();
+    }
+
+    public void setFrameColor(ColorStateList color) {
+        mDrawable.setFrameColor(color);
+    }
+
+    public void setFrameWidth(float frameWidth) {
+        mDrawable.setFrameWidth(frameWidth);
+    }
+
+    public void setFramePadding(float framePadding) {
+        mDrawable.setFramePadding(framePadding);
+    }
+
+    public void setAvatarPadding(float avatarPadding) {
+        mDrawable.setPadding(avatarPadding);
+    }
+
+    public void setBadgeDiameter(float diameter) {
+        mDrawable.setBadgeRadius(diameter * 0.5f);
+    }
+
+    public void setBadgeMargin(float margin) {
+        mDrawable.setBadgeMargin(margin);
+    }
+
+    public void setAvatar(Bitmap avatar) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadge(null);
+    }
+
+    public void setAvatarWithBadge(Bitmap avatar, int userId) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
+    }
+
+    public void setDrawable(Drawable d) {
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
+        }
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadge(null);
+    }
+
+    public void setDrawableWithBadge(Drawable d, int userId) {
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
+        }
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
+    }
+}
diff --git a/car-admin-ui-lib/src/main/res/values/attrs.xml b/car-admin-ui-lib/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..f41c690
--- /dev/null
+++ b/car-admin-ui-lib/src/main/res/values/attrs.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<resources>
+<!-- TODO(b/176262528): copy-and-pasted from frameworks/base/packages/SystemUI/res/values/attrs.xml
+     if moved to a proper library, whole file could be removed -->
+    <attr name="frameColor" format="color" />
+    <declare-styleable name="UserAvatarView">
+        <attr name="avatarPadding" format="dimension" />
+        <attr name="frameWidth" format="dimension" />
+        <attr name="framePadding" format="dimension" />
+        <!-- {@deprecated Use a statelist in frameColor instead.} -->
+        <attr name="activeFrameColor" format="color" />
+        <attr name="frameColor" />
+        <attr name="badgeDiameter" format="dimension" />
+        <attr name="badgeMargin" format="dimension" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/Android.bp b/tests/EmbeddedKitchenSinkApp/Android.bp
index 7bd0245..926221e 100644
--- a/tests/EmbeddedKitchenSinkApp/Android.bp
+++ b/tests/EmbeddedKitchenSinkApp/Android.bp
@@ -39,6 +39,7 @@
         "car-service-test-static-lib",
         "com.google.android.material_material",
         "androidx.appcompat_appcompat",
+        "car-admin-ui-lib",
         "car-ui-lib",
         "android.hidl.base-V1.0-java",
         "android.hardware.automotive.vehicle-V2.0-java",
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/user_info_view.xml b/tests/EmbeddedKitchenSinkApp/res/layout/user_info_view.xml
index e27dac8..0ea3f8b 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/user_info_view.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/user_info_view.xml
@@ -18,54 +18,78 @@
     NOTE: This layout is meant to be used by the UserInfoView widget only
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:admin_ui="http://schemas.android.com/apk/res-auto"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:orientation="vertical" >
+              android:orientation="horizontal" >
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginRight="30dp">
+        <com.android.car.admin.ui.UserAvatarView
+            android:id="@+id/user_avatar"
+            android:layout_width="108dp"
+            android:layout_height="108dp"
+            admin_ui:badgeDiameter="36dp"
+            admin_ui:badgeMargin="2dp"
+            />
+   </FrameLayout>
+
     <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content" android:text="ID: "/>
-        <EditText
-            android:id="@+id/user_id"
-            android:enabled="false"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content" android:text=""/>
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content" android:text="Name: "/>
-        <EditText
-            android:id="@+id/user_name"
-            android:enabled="false"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content" android:text=""/>
-    </LinearLayout>
-    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal" >
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content" android:text="Type: "/>
-        <EditText
-            android:id="@+id/user_type"
-            android:enabled="false"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content" android:text=""/>
-    </LinearLayout>
-    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal" >
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content" android:text="Flags: "/>
-        <EditText
-            android:id="@+id/user_flags"
-            android:enabled="false"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content" android:text=""/>
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:orientation="vertical" >
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:text="ID: "/>
+            <EditText
+                android:id="@+id/user_id"
+                android:enabled="false"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:text=""/>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:text="Name: "/>
+            <EditText
+                android:id="@+id/user_name"
+                android:enabled="false"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:text=""/>
+            <TextView
+                android:id="@+id/managed"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"/>
+        </LinearLayout>
+        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal" >
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:text="Type: "/>
+            <EditText
+                android:id="@+id/user_type"
+                android:enabled="false"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:text=""/>
+        </LinearLayout>
+        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal" >
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:text="Flags: "/>
+            <EditText
+                android:id="@+id/user_flags"
+                android:enabled="false"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" android:text=""/>
+        </LinearLayout>
     </LinearLayout>
 </LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserInfoView.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserInfoView.java
index 7087b6c..6135bef 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserInfoView.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserInfoView.java
@@ -13,15 +13,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.google.android.car.kitchensink.users;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.widget.EditText;
 import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.car.admin.ui.UserAvatarView;
+import com.android.internal.util.UserIcons;
 
 import com.google.android.car.kitchensink.R;
 
@@ -32,38 +43,74 @@
 
     private static final String TAG = UserInfoView.class.getSimpleName();
 
+    private UserAvatarView mUserAvatarView;
     private EditText mUserIdEditText;
     private EditText mUserNameEditText;
     private EditText mUserTypeEditText;
     private EditText mUserFlagsEditText;
+    private TextView mManagedTextView;
 
     public UserInfoView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         inflate(context, R.layout.user_info_view, this);
+        mUserAvatarView = findViewById(R.id.user_avatar);
         mUserIdEditText = findViewById(R.id.user_id);
         mUserNameEditText = findViewById(R.id.user_name);
         mUserTypeEditText = findViewById(R.id.user_type);
         mUserFlagsEditText = findViewById(R.id.user_flags);
+        mManagedTextView = findViewById(R.id.managed);
     }
 
     /**
      * Initializes the widget with the given user.
      */
     public void update(@NonNull UserInfo user) {
-        Log.v(TAG, "initializing with " + user);
-        String userId, userName, userType, userFlags;
-        if (user == null) {
-            userId = userName = userType = userFlags = "N/A";
-        } else {
-            userId = String.valueOf(user.id);
-            userName = user.name;
-            userType = user.userType;
-            userFlags = UserInfo.flagsToString(user.flags);
+        Log.v(TAG, "initializing with " + user.toFullString());
+
+        setUserIcon(user.id);
+        mUserIdEditText.setText(String.valueOf(user.id));
+        mUserNameEditText.setText(user.name);
+        mUserTypeEditText.setText(user.userType);
+        mUserFlagsEditText.setText(UserInfo.flagsToString(user.flags));
+        setManagedStatus(user.id);
+    }
+
+    private void setUserIcon(@UserIdInt int userId) {
+        UserManager um = UserManager.get(getContext());
+        Bitmap icon = um.getUserIcon(userId);
+        if (icon == null) {
+            Log.v(TAG, "No icon for user " + userId); // Happens on system user
+            icon = UserIcons.convertToBitmap(
+                    UserIcons.getDefaultUserIcon(getResources(), userId, /* light= */ false));
         }
-        mUserIdEditText.setText(userId);
-        mUserNameEditText.setText(userName);
-        mUserTypeEditText.setText(userType);
-        mUserFlagsEditText.setText(userFlags);
+        mUserAvatarView.setAvatarWithBadge(icon, userId);
+    }
+
+    private void setManagedStatus(@UserIdInt int userId) {
+        // Check managed status
+        DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+        if (dpm == null) return;
+
+        UserHandle deviceOwner = dpm.getDeviceOwnerUser();
+        Log.v(TAG, "Device owner is " + deviceOwner);
+
+        if (deviceOwner != null && deviceOwner.getIdentifier() == userId) {
+            setManagedText("DeviceOwner");
+            return;
+        }
+
+        ComponentName profileOwner = dpm.getProfileOwnerAsUser(userId);
+        if (profileOwner != null) {
+            Log.v(TAG, "User " + userId + " has profile owner: " + profileOwner);
+            setManagedText("ProfileOwner");
+            return;
+        }
+        mManagedTextView.setVisibility(GONE);
+    }
+
+    private void setManagedText(@NonNull CharSequence text) {
+        mManagedTextView.setText(text);
+        mManagedTextView.setVisibility(VISIBLE);
     }
 }