1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.car.systembar;
18 
19 import android.annotation.Nullable;
20 import android.app.role.OnRoleHoldersChangedListener;
21 import android.app.role.RoleManager;
22 import android.content.Context;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.PackageManager;
25 import android.graphics.drawable.Drawable;
26 import android.os.UserHandle;
27 import android.util.Log;
28 import android.view.View;
29 import android.view.ViewGroup;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.systemui.car.CarDeviceProvisionedController;
33 import com.android.systemui.dagger.SysUISingleton;
34 
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 
39 import javax.inject.Inject;
40 
41 /**
42  * Some CarSystemBarButtons can be associated to a {@link RoleManager} role. When they are, it is
43  * possible to have them display the icon of the default application (role holder) for the given
44  * role.
45  *
46  * This class monitors the current role holders for each role type and updates the button icon for
47  * this buttons with have this feature enabled.
48  */
49 @SysUISingleton
50 public class ButtonRoleHolderController {
51     private static final String TAG = "ButtonRoleHolderController";
52 
53     private final Context mContext;
54     private final PackageManager mPackageManager;
55     private final RoleManager mRoleManager;
56     private final CarDeviceProvisionedController mDeviceController;
57     private final Map<String, CarSystemBarButton> mButtonMap = new HashMap<>();
58     private final OnRoleHoldersChangedListener mListener = this::onRoleChanged;
59     private boolean mRegistered;
60 
61     @Inject
ButtonRoleHolderController(Context context, PackageManager packageManager, RoleManager roleManager, CarDeviceProvisionedController deviceController)62     public ButtonRoleHolderController(Context context, PackageManager packageManager,
63             RoleManager roleManager, CarDeviceProvisionedController deviceController) {
64         mContext = context;
65         mPackageManager = packageManager;
66         mRoleManager = roleManager;
67         mDeviceController = deviceController;
68     }
69 
70     /**
71      * Iterate through a view looking for CarSystemBarButton and add it to this controller if it
72      * opted to be associated with a {@link RoleManager} role type.
73      *
74      * @param v the View that may contain CarFacetButtons
75      */
addAllButtonsWithRoleName(View v)76     void addAllButtonsWithRoleName(View v) {
77         if (v instanceof CarSystemBarButton) {
78             CarSystemBarButton button = (CarSystemBarButton) v;
79             String roleName = button.getRoleName();
80             if (roleName != null && button.isDefaultAppIconForRoleEnabled()) {
81                 addButtonWithRoleName(button, roleName);
82             }
83         } else if (v instanceof ViewGroup) {
84             ViewGroup viewGroup = (ViewGroup) v;
85             for (int i = 0; i < viewGroup.getChildCount(); i++) {
86                 addAllButtonsWithRoleName(viewGroup.getChildAt(i));
87             }
88         }
89     }
90 
addButtonWithRoleName(CarSystemBarButton button, String roleName)91     private void addButtonWithRoleName(CarSystemBarButton button, String roleName) {
92         mButtonMap.put(roleName, button);
93         updateIcon(roleName);
94         if (!mRegistered) {
95             mRoleManager.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
96                     mListener, UserHandle.ALL);
97             mRegistered = true;
98         }
99     }
100 
removeAll()101     void removeAll() {
102         mButtonMap.clear();
103         if (mRegistered) {
104             mRoleManager.removeOnRoleHoldersChangedListenerAsUser(mListener, UserHandle.ALL);
105             mRegistered = false;
106         }
107     }
108 
109     @VisibleForTesting
onRoleChanged(String roleName, UserHandle user)110     void onRoleChanged(String roleName, UserHandle user) {
111         if (RoleManager.ROLE_ASSISTANT.equals(roleName)
112                 && user.getIdentifier() == mDeviceController.getCurrentUser()) {
113             updateIcon(roleName);
114         }
115     }
116 
updateIcon(String roleName)117     private void updateIcon(String roleName) {
118         CarSystemBarButton button = mButtonMap.get(roleName);
119         if (button == null) {
120             return;
121         }
122         List<String> holders = mRoleManager.getRoleHoldersAsUser(button.getRoleName(),
123                 UserHandle.of(mDeviceController.getCurrentUser()));
124         if (holders == null || holders.isEmpty()) {
125             button.setAppIcon(null);
126         } else {
127             button.setAppIcon(loadIcon(holders.get(0)));
128         }
129     }
130 
131     @Nullable
loadIcon(String packageName)132     private Drawable loadIcon(String packageName) {
133         try {
134             ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName,
135                     PackageManager.MATCH_ANY_USER);
136             return appInfo.loadIcon(mPackageManager);
137         } catch (PackageManager.NameNotFoundException e) {
138             Log.e(ButtonRoleHolderController.TAG, "Package not found: " + packageName, e);
139             return null;
140         }
141     }
142 }
143