• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.accessibility.hearingaid;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ActivityInfo;
23 import android.content.pm.PackageManager;
24 import android.content.res.Resources;
25 import android.graphics.drawable.Drawable;
26 import android.util.Log;
27 
28 import androidx.annotation.VisibleForTesting;
29 
30 import com.google.common.collect.ImmutableList;
31 
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.List;
35 
36 /**
37  * Utility class for managing and parsing tool items related to hearing devices.
38  */
39 public class HearingDevicesToolItemParser {
40     private static final String TAG = "HearingDevicesToolItemParser";
41     private static final String SPLIT_DELIMITER = "/";
42     private static final String RES_TYPE = "drawable";
43     @VisibleForTesting
44     static final int MAX_NUM = 2;
45 
46     /**
47      * Parses the string arrays to create a list of {@link ToolItem}.
48      *
49      * This method validates the structure of {@code toolNameArray} and {@code toolIconArray}.
50      * If {@code toolIconArray} is empty or mismatched in length with {@code toolNameArray}, the
51      * icon from {@link ActivityInfo#loadIcon(PackageManager)} will be used instead.
52      *
53      * @param context A valid context.
54      * @param toolNameArray An array of tool names in the format of {@link ComponentName}.
55      * @param toolIconArray An optional array of resource names for tool icons (can be empty).
56      * @return A list of {@link ToolItem} or an empty list if there are errors during parsing.
57      */
parseStringArray(Context context, String[] toolNameArray, String[] toolIconArray)58     public static ImmutableList<ToolItem> parseStringArray(Context context, String[] toolNameArray,
59             String[] toolIconArray) {
60         if (toolNameArray.length == 0) {
61             Log.i(TAG, "Empty hearing device related tool name in array.");
62             return ImmutableList.of();
63         }
64         // For the performance concern, especially `getIdentifier` in `parseValidIcon`, we will
65         // limit the maximum number.
66         String[] nameArrayCpy = Arrays.copyOfRange(toolNameArray, 0,
67                 Math.min(toolNameArray.length, MAX_NUM));
68         String[] iconArrayCpy = Arrays.copyOfRange(toolIconArray, 0,
69                 Math.min(toolIconArray.length, MAX_NUM));
70 
71         final PackageManager packageManager = context.getPackageManager();
72         final ImmutableList.Builder<ToolItem> toolItemList = ImmutableList.builder();
73         final List<ActivityInfo> activityInfoList = parseValidActivityInfo(context, nameArrayCpy);
74         final List<Drawable> iconList = parseValidIcon(context, iconArrayCpy);
75         final int size = activityInfoList.size();
76         // Only use custom icon if provided icon's list size is equal to provided name's list size.
77         final boolean useCustomIcons = (size == iconList.size());
78 
79         for (int i = 0; i < size; i++) {
80             toolItemList.add(new ToolItem(
81                     activityInfoList.get(i).loadLabel(packageManager).toString(),
82                     useCustomIcons ? iconList.get(i)
83                             : activityInfoList.get(i).loadIcon(packageManager),
84                     new Intent(Intent.ACTION_MAIN).setComponent(
85                             activityInfoList.get(i).getComponentName()),
86                     useCustomIcons
87             ));
88         }
89 
90         return toolItemList.build();
91     }
92 
parseValidActivityInfo(Context context, String[] toolNameArray)93     private static List<ActivityInfo> parseValidActivityInfo(Context context,
94             String[] toolNameArray) {
95         final PackageManager packageManager = context.getPackageManager();
96         final List<ActivityInfo> activityInfoList = new ArrayList<>();
97         for (String toolName : toolNameArray) {
98             String[] nameParts = toolName.split(SPLIT_DELIMITER);
99             if (nameParts.length == 2) {
100                 ComponentName componentName = ComponentName.unflattenFromString(toolName);
101                 try {
102                     ActivityInfo activityInfo = packageManager.getActivityInfo(
103                             componentName, /* flags= */ 0);
104                     activityInfoList.add(activityInfo);
105                 } catch (PackageManager.NameNotFoundException e) {
106                     Log.e(TAG, "Unable to find hearing device related tool: "
107                             + componentName.flattenToString());
108                 }
109             } else {
110                 Log.e(TAG, "Malformed hearing device related tool name item in array: "
111                         + toolName);
112             }
113         }
114         return activityInfoList;
115     }
116 
parseValidIcon(Context context, String[] toolIconArray)117     private static List<Drawable> parseValidIcon(Context context, String[] toolIconArray) {
118         final List<Drawable> drawableList = new ArrayList<>();
119         for (String icon : toolIconArray) {
120             int resId = context.getResources().getIdentifier(icon, RES_TYPE,
121                     context.getPackageName());
122             try {
123                 drawableList.add(context.getDrawable(resId));
124             } catch (Resources.NotFoundException e) {
125                 Log.e(TAG, "Resource does not exist: " + icon);
126             }
127         }
128         return drawableList;
129     }
130 }
131