1 /*
2  * Copyright (C) 2022 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.companiondevicemanager;
18 
19 import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
20 import static com.android.companiondevicemanager.Utils.getIcon;
21 
22 import static java.util.Collections.unmodifiableMap;
23 
24 import android.content.Context;
25 import android.text.Spanned;
26 import android.util.ArrayMap;
27 import android.view.LayoutInflater;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.view.accessibility.AccessibilityNodeInfo;
31 import android.widget.ImageButton;
32 import android.widget.ImageView;
33 import android.widget.TextView;
34 
35 import androidx.recyclerview.widget.RecyclerView;
36 
37 import java.util.List;
38 import java.util.Map;
39 
40 class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.ViewHolder> {
41     private final Context mContext;
42     private List<Integer> mPermissions;
43     // Add the expand buttons if permissions are more than PERMISSION_SIZE in the permission list.
44     private static final int PERMISSION_SIZE = 2;
45 
46     static final int PERMISSION_NOTIFICATION = 0;
47     static final int PERMISSION_STORAGE = 1;
48     static final int PERMISSION_APP_STREAMING = 2;
49     static final int PERMISSION_PHONE = 3;
50     static final int PERMISSION_SMS = 4;
51     static final int PERMISSION_CONTACTS = 5;
52     static final int PERMISSION_CALENDAR = 6;
53     static final int PERMISSION_NEARBY_DEVICES = 7;
54     static final int PERMISSION_NEARBY_DEVICE_STREAMING = 8;
55     static final int PERMISSION_MICROPHONE = 9;
56     static final int PERMISSION_CALL_LOGS = 10;
57 
58     private static final Map<Integer, Integer> sTitleMap;
59     static {
60         final Map<Integer, Integer> map = new ArrayMap<>();
map.put(PERMISSION_NOTIFICATION, R.string.permission_notification)61         map.put(PERMISSION_NOTIFICATION, R.string.permission_notification);
map.put(PERMISSION_STORAGE, R.string.permission_storage)62         map.put(PERMISSION_STORAGE, R.string.permission_storage);
map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming)63         map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming);
map.put(PERMISSION_PHONE, R.string.permission_phone)64         map.put(PERMISSION_PHONE, R.string.permission_phone);
map.put(PERMISSION_SMS, R.string.permission_sms)65         map.put(PERMISSION_SMS, R.string.permission_sms);
map.put(PERMISSION_CONTACTS, R.string.permission_contacts)66         map.put(PERMISSION_CONTACTS, R.string.permission_contacts);
map.put(PERMISSION_CALENDAR, R.string.permission_calendar)67         map.put(PERMISSION_CALENDAR, R.string.permission_calendar);
map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices)68         map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices);
map.put(PERMISSION_NEARBY_DEVICE_STREAMING, R.string.permission_nearby_device_streaming)69         map.put(PERMISSION_NEARBY_DEVICE_STREAMING, R.string.permission_nearby_device_streaming);
map.put(PERMISSION_MICROPHONE, R.string.permission_microphone)70         map.put(PERMISSION_MICROPHONE, R.string.permission_microphone);
map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs)71         map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs);
72         sTitleMap = unmodifiableMap(map);
73     }
74 
75     private static final Map<Integer, Integer> sSummaryMap;
76     static {
77         final Map<Integer, Integer> map = new ArrayMap<>();
map.put(PERMISSION_NOTIFICATION, R.string.permission_notification_summary)78         map.put(PERMISSION_NOTIFICATION, R.string.permission_notification_summary);
map.put(PERMISSION_STORAGE, R.string.permission_storage_summary)79         map.put(PERMISSION_STORAGE, R.string.permission_storage_summary);
map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming_summary)80         map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming_summary);
map.put(PERMISSION_PHONE, R.string.permission_phone_summary)81         map.put(PERMISSION_PHONE, R.string.permission_phone_summary);
map.put(PERMISSION_SMS, R.string.permission_sms_summary)82         map.put(PERMISSION_SMS, R.string.permission_sms_summary);
map.put(PERMISSION_CONTACTS, R.string.permission_contacts_summary)83         map.put(PERMISSION_CONTACTS, R.string.permission_contacts_summary);
map.put(PERMISSION_CALENDAR, R.string.permission_calendar_summary)84         map.put(PERMISSION_CALENDAR, R.string.permission_calendar_summary);
map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices_summary)85         map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices_summary);
map.put(PERMISSION_NEARBY_DEVICE_STREAMING, R.string.permission_nearby_device_streaming_summary)86         map.put(PERMISSION_NEARBY_DEVICE_STREAMING,
87                 R.string.permission_nearby_device_streaming_summary);
map.put(PERMISSION_MICROPHONE, R.string.permission_microphone_summary)88         map.put(PERMISSION_MICROPHONE, R.string.permission_microphone_summary);
map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs_summary)89         map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs_summary);
90         sSummaryMap = unmodifiableMap(map);
91     }
92 
93     private static final Map<Integer, Integer> sIconMap;
94     static {
95         final Map<Integer, Integer> map = new ArrayMap<>();
map.put(PERMISSION_NOTIFICATION, R.drawable.ic_permission_notifications)96         map.put(PERMISSION_NOTIFICATION, R.drawable.ic_permission_notifications);
map.put(PERMISSION_STORAGE, R.drawable.ic_permission_storage)97         map.put(PERMISSION_STORAGE, R.drawable.ic_permission_storage);
map.put(PERMISSION_APP_STREAMING, R.drawable.ic_permission_app_streaming)98         map.put(PERMISSION_APP_STREAMING, R.drawable.ic_permission_app_streaming);
map.put(PERMISSION_PHONE, R.drawable.ic_permission_phone)99         map.put(PERMISSION_PHONE, R.drawable.ic_permission_phone);
map.put(PERMISSION_SMS, R.drawable.ic_permission_sms)100         map.put(PERMISSION_SMS, R.drawable.ic_permission_sms);
map.put(PERMISSION_CONTACTS, R.drawable.ic_permission_contacts)101         map.put(PERMISSION_CONTACTS, R.drawable.ic_permission_contacts);
map.put(PERMISSION_CALENDAR, R.drawable.ic_permission_calendar)102         map.put(PERMISSION_CALENDAR, R.drawable.ic_permission_calendar);
map.put(PERMISSION_NEARBY_DEVICES, R.drawable.ic_permission_nearby_devices)103         map.put(PERMISSION_NEARBY_DEVICES, R.drawable.ic_permission_nearby_devices);
map.put(PERMISSION_NEARBY_DEVICE_STREAMING, R.drawable.ic_permission_nearby_device_streaming)104         map.put(PERMISSION_NEARBY_DEVICE_STREAMING,
105                 R.drawable.ic_permission_nearby_device_streaming);
map.put(PERMISSION_MICROPHONE, R.drawable.ic_permission_microphone)106         map.put(PERMISSION_MICROPHONE, R.drawable.ic_permission_microphone);
map.put(PERMISSION_CALL_LOGS, R.drawable.ic_permission_call_logs)107         map.put(PERMISSION_CALL_LOGS, R.drawable.ic_permission_call_logs);
108         sIconMap = unmodifiableMap(map);
109     }
110 
PermissionListAdapter(Context context)111     PermissionListAdapter(Context context) {
112         mContext = context;
113     }
114 
115     @Override
onCreateViewHolder(ViewGroup parent, int viewType)116     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
117         View view = LayoutInflater.from(parent.getContext()).inflate(
118                 R.layout.list_item_permission, parent, false);
119         ViewHolder viewHolder = new ViewHolder(view);
120         viewHolder.mPermissionIcon.setImageDrawable(getIcon(mContext, sIconMap.get(viewType)));
121 
122         if (viewHolder.mExpandButton.getTag() == null) {
123             viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more);
124         }
125 
126         // Add expand buttons if the permissions are more than PERMISSION_SIZE in this list also
127         // make the summary invisible by default.
128         if (mPermissions.size() > PERMISSION_SIZE) {
129             setAccessibility(view, viewType,
130                     AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand, 0);
131 
132             viewHolder.mPermissionSummary.setVisibility(View.GONE);
133 
134             view.setOnClickListener(v -> {
135                 if ((Integer) viewHolder.mExpandButton.getTag() == R.drawable.btn_expand_more) {
136                     viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_less);
137                     viewHolder.mPermissionSummary.setVisibility(View.VISIBLE);
138                     viewHolder.mExpandButton.setTag(R.drawable.btn_expand_less);
139                     setAccessibility(view, viewType,
140                             AccessibilityNodeInfo.ACTION_CLICK,
141                             R.string.permission_collapse, R.string.permission_expand);
142                 } else {
143                     viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_more);
144                     viewHolder.mPermissionSummary.setVisibility(View.GONE);
145                     viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more);
146                     setAccessibility(view, viewType,
147                             AccessibilityNodeInfo.ACTION_CLICK,
148                             R.string.permission_expand, R.string.permission_collapse);
149                 }
150             });
151         } else {
152             // Remove expand buttons if the permissions are less than PERMISSION_SIZE in this list
153             // also show the summary by default.
154             viewHolder.mPermissionSummary.setVisibility(View.VISIBLE);
155             viewHolder.mExpandButton.setVisibility(View.GONE);
156         }
157 
158         return viewHolder;
159     }
160 
161     @Override
onBindViewHolder(ViewHolder holder, int position)162     public void onBindViewHolder(ViewHolder holder, int position) {
163         int type = getItemViewType(position);
164         final Spanned title = getHtmlFromResources(mContext, sTitleMap.get(type));
165         final Spanned summary = getHtmlFromResources(mContext, sSummaryMap.get(type));
166 
167         holder.mPermissionSummary.setText(summary);
168         holder.mPermissionName.setText(title);
169     }
170 
171     @Override
getItemViewType(int position)172     public int getItemViewType(int position) {
173         return mPermissions.get(position);
174     }
175 
176     @Override
getItemId(int position)177     public long getItemId(int position) {
178         return position;
179     }
180 
181     @Override
getItemCount()182     public int getItemCount() {
183         return mPermissions != null ? mPermissions.size() : 0;
184     }
185 
186     static class ViewHolder extends RecyclerView.ViewHolder {
187         private final TextView mPermissionName;
188         private final TextView mPermissionSummary;
189         private final ImageView mPermissionIcon;
190         private final ImageButton mExpandButton;
ViewHolder(View itemView)191         ViewHolder(View itemView) {
192             super(itemView);
193             mPermissionName = itemView.findViewById(R.id.permission_name);
194             mPermissionSummary = itemView.findViewById(R.id.permission_summary);
195             mPermissionIcon = itemView.findViewById(R.id.permission_icon);
196             mExpandButton = itemView.findViewById(R.id.permission_expand_button);
197         }
198     }
199 
setAccessibility(View view, int viewType, int action, int statusResourceId, int actionResourceId)200     private void setAccessibility(View view, int viewType, int action, int statusResourceId,
201             int actionResourceId) {
202         final String permission = mContext.getString(sTitleMap.get(viewType));
203 
204         if (actionResourceId != 0) {
205             view.announceForAccessibility(
206                     getHtmlFromResources(mContext, actionResourceId, permission));
207         }
208 
209         view.setAccessibilityDelegate(new View.AccessibilityDelegate() {
210             public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
211                 super.onInitializeAccessibilityNodeInfo(host, info);
212                 info.addAction(new AccessibilityNodeInfo.AccessibilityAction(action,
213                         getHtmlFromResources(mContext, statusResourceId, permission)));
214             }
215         });
216     }
217 
setPermissionType(List<Integer> permissions)218     void setPermissionType(List<Integer> permissions) {
219         mPermissions = permissions;
220     }
221 }
222