1 /*
2  * Copyright (C) 2018 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.phone;
18 
19 import android.content.Context;
20 import android.telephony.emergency.EmergencyNumber;
21 import android.text.TextUtils;
22 import android.view.View;
23 import android.view.ViewGroup;
24 import android.widget.BaseAdapter;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 
29 import com.google.common.collect.LinkedListMultimap;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 /**
35  * An abstract adapter between ECC data and the view contains ECC shortcuts.
36  * This adapter prepares description and icon for every promoted emergency number.
37  * The subclass should implements {@link #inflateView} to provide the view for an ECC data, when the
38  * view container calls {@link #getView}.
39  */
40 public abstract class EccShortcutAdapter extends BaseAdapter {
41     private List<EccDisplayMaterial> mEccDisplayMaterialList;
42 
43     private CharSequence mPoliceDescription;
44     private CharSequence mAmbulanceDescription;
45     private CharSequence mFireDescription;
46 
47     private static class EccDisplayMaterial {
48         public CharSequence number = null;
49         public int iconRes = 0;
50         public CharSequence description = null;
51     }
52 
EccShortcutAdapter(@onNull Context context)53     public EccShortcutAdapter(@NonNull Context context) {
54         mPoliceDescription = context.getText(R.string.police_type_description);
55         mAmbulanceDescription = context.getText(R.string.ambulance_type_description);
56         mFireDescription = context.getText(R.string.fire_type_description);
57 
58         mEccDisplayMaterialList = new ArrayList<>();
59     }
60 
61     @Override
getCount()62     public int getCount() {
63         return mEccDisplayMaterialList.size();
64     }
65 
66     @Override
getItem(int position)67     public EccDisplayMaterial getItem(int position) {
68         return mEccDisplayMaterialList.get(position);
69     }
70 
71     @Override
getItemId(int position)72     public long getItemId(int position) {
73         return position;
74     }
75 
76     @Override
getView(int position, View convertView, ViewGroup parent)77     public View getView(int position, View convertView, ViewGroup parent) {
78         EccDisplayMaterial material = getItem(position);
79         return inflateView(convertView, parent, material.number, material.description,
80                 material.iconRes);
81     }
82 
83     /**
84      * Get a View that display the given ECC data: number, description and iconRes.
85      *
86      * @param convertView The old view to reuse, if possible. Note: You should check that this view
87      *                    is non-null and of an appropriate type before using. If it is not possible
88      *                    to convert this view to display the correct data, this method can create a
89      *                    new view. Heterogeneous lists can specify their number of view types, so
90      *                    that this View is always of the right type (see {@link
91      *                    BaseAdapter#getViewTypeCount()} and {@link
92      *                    BaseAdapter#getItemViewType(int)}).
93      * @param parent      The parent that this view will eventually be attached to.
94      * @param number      The number of the ECC shortcut to display in the view.
95      * @param description The description of the ECC shortcut to display in the view.
96      * @param iconRes     The icon resource ID represent for the ECC shortcut.
97      * @return A View corresponding to the data at the specified position.
98      */
inflateView(View convertView, ViewGroup parent, CharSequence number, CharSequence description, int iconRes)99     public abstract View inflateView(View convertView, ViewGroup parent, CharSequence number,
100             CharSequence description, int iconRes);
101 
102     /**
103      * Update country ECC info. This method converts given country ECC info to ECC data that could
104      * be display by the short container View.
105      *
106      * @param context The context used to access resources.
107      * @param phoneInfo Information of the phone to make an emergency call.
108      */
updateCountryEccInfo(@onNull Context context, @Nullable ShortcutViewUtils.PhoneInfo phoneInfo)109     public void updateCountryEccInfo(@NonNull Context context,
110             @Nullable ShortcutViewUtils.PhoneInfo phoneInfo) {
111         List<EccDisplayMaterial> displayMaterials = new ArrayList<>();
112 
113         try {
114             if (phoneInfo == null) {
115                 return;
116             }
117 
118             LinkedListMultimap<String, Integer> emergencyNumbers = LinkedListMultimap.create();
119             for (int category : ShortcutViewUtils.PROMOTED_CATEGORIES) {
120                 String number = pickEmergencyNumberForCategory(category,
121                         phoneInfo.getPromotedEmergencyNumbers());
122                 if (number != null) {
123                     emergencyNumbers.put(number, category);
124                 }
125             }
126 
127             // prepare display material for picked ECC
128             for (String number : emergencyNumbers.keySet()) {
129                 EccDisplayMaterial material = prepareEccMaterial(context, number,
130                         emergencyNumbers.get(number));
131                 if (material != null) {
132                     displayMaterials.add(material);
133                 }
134             }
135         } finally {
136             mEccDisplayMaterialList = displayMaterials;
137             notifyDataSetChanged();
138         }
139     }
140 
hasShortcut(String number)141     boolean hasShortcut(String number) {
142         if (mEccDisplayMaterialList == null) {
143             return false;
144         }
145 
146         for (EccDisplayMaterial displayMaterial : mEccDisplayMaterialList) {
147             if (displayMaterial.number.equals(number)) {
148                 return true;
149             }
150         }
151         return false;
152     }
153 
154     @Nullable
pickEmergencyNumberForCategory(int category, @NonNull List<EmergencyNumber> emergencyNumbers)155     private String pickEmergencyNumberForCategory(int category,
156             @NonNull List<EmergencyNumber> emergencyNumbers) {
157         for (EmergencyNumber number : emergencyNumbers) {
158             if ((number.getEmergencyServiceCategoryBitmask() & category) != 0) {
159                 return number.getNumber();
160             }
161         }
162         return null;
163     }
164 
165     @Nullable
prepareEccMaterial(@onNull Context context, @NonNull String number, @NonNull List<Integer> categories)166     private EccDisplayMaterial prepareEccMaterial(@NonNull Context context, @NonNull String number,
167             @NonNull List<Integer> categories) {
168         EccDisplayMaterial material = new EccDisplayMaterial();
169         material.number = number;
170         for (int category : categories) {
171             CharSequence description;
172             switch (category) {
173                 case EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE:
174                     description = mPoliceDescription;
175                     material.iconRes = R.drawable.ic_local_police_gm2_24px;
176                     break;
177                 case EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE:
178                     description = mAmbulanceDescription;
179                     material.iconRes = R.drawable.ic_local_hospital_gm2_24px;
180                     break;
181                 case EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE:
182                     description = mFireDescription;
183                     material.iconRes = R.drawable.ic_local_fire_department_gm2_24px;
184                     break;
185                 default:
186                     // ignore unknown types
187                     continue;
188             }
189 
190             if (TextUtils.isEmpty(material.description)) {
191                 material.description = description;
192             } else {
193                 // concatenate multiple types
194                 material.iconRes = R.drawable.ic_local_hospital_gm2_24px;
195                 material.description = context.getString(R.string.description_concat_format,
196                         material.description, description);
197             }
198         }
199 
200         if (TextUtils.isEmpty(material.description) || material.iconRes == 0) {
201             return null;
202         }
203         return material;
204     }
205 }
206