blob: 2f367a862a901b948532a010c6b0d818e1eb68c7 [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
2 * Copyright (C) 2019 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
17package com.android.car.setupwizardlib.partner;
18
19import android.content.ContentResolver;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.content.res.Resources;
23import android.content.res.Resources.NotFoundException;
24import android.graphics.drawable.Drawable;
25import android.net.Uri;
26import android.os.Bundle;
27import android.util.Log;
28import android.util.TypedValue;
29
30import androidx.annotation.ColorInt;
31import androidx.annotation.NonNull;
32import androidx.annotation.Nullable;
33import androidx.annotation.VisibleForTesting;
34
35import java.util.EnumMap;
36
37/** The helper reads and caches the partner configurations from Car Setup Wizard. */
38public class PartnerConfigHelper {
39
40 private static final String TAG = PartnerConfigHelper.class.getSimpleName();
41
42 @VisibleForTesting
43 static final String SUW_AUTHORITY = "com.google.android.car.setupwizard.partner";
44
45 @VisibleForTesting
46 static final String SUW_GET_PARTNER_CONFIG_METHOD = "getOverlayConfig";
47 private static volatile PartnerConfigHelper sInstance = null;
48
49 @VisibleForTesting Bundle mResultBundle = null;
50
51 @VisibleForTesting
52 final EnumMap<PartnerConfig, Object> mPartnerResourceCache = new EnumMap<>(PartnerConfig.class);
53
54 /** Factory method to get an instance */
55 public static PartnerConfigHelper get(@NonNull Context context) {
56 if (sInstance == null) {
57 synchronized (PartnerConfigHelper.class) {
58 if (sInstance == null) {
59 sInstance = new PartnerConfigHelper(context);
60 }
61 }
62 }
63 return sInstance;
64 }
65
66 /**
67 * Returns the color of given {@code partnerConfig}, or 0 if the given {@code partnerConfig}
68 * is not found. If the {@code ResourceType} of the given {@code partnerConfig} is not color,
69 * IllegalArgumentException will be thrown.
70 *
71 * @param context The context of client activity
72 * @param partnerConfig The {@code PartnerConfig} of target resource
73 */
74 @ColorInt
75 public int getColor(@NonNull Context context, PartnerConfig partnerConfig) {
76 if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.COLOR) {
77 throw new IllegalArgumentException("Not a color resource");
78 }
79
80 if (mPartnerResourceCache.containsKey(partnerConfig)) {
81 return (int) mPartnerResourceCache.get(partnerConfig);
82 }
83
84 int result = 0;
85 try {
86 String resourceName = partnerConfig.getResourceName();
87 ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
88 if (resourceEntry == null) {
89 Log.w(TAG, "Resource not found: " + resourceName);
90 return 0;
91 }
92
93 Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
94 result = resource.getColor(resourceEntry.getResourceId(), null);
95 mPartnerResourceCache.put(partnerConfig, result);
96 } catch (PackageManager.NameNotFoundException exception) {
97 Log.e(TAG, exception.getMessage());
98 }
99 return result;
100 }
101
102 /**
103 * Returns the {@code Drawable} of given {@code partnerConfig}, or {@code null} if the given
104 * {@code partnerConfig} is not found. If the {@code ResourceType} of the given {@code
105 * resourceConfig} is not drawable, IllegalArgumentException will be thrown.
106 *
107 * @param context The context of client activity
108 * @param partnerConfig The {@code PartnerConfig} of target resource
109 */
110 @Nullable
111 public Drawable getDrawable(@NonNull Context context, PartnerConfig partnerConfig) {
112 if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.DRAWABLE) {
113 throw new IllegalArgumentException("Not a drawable resource");
114 }
115
116 if (mPartnerResourceCache.containsKey(partnerConfig)) {
117 return (Drawable) mPartnerResourceCache.get(partnerConfig);
118 }
119
120 Drawable result = null;
121 try {
122 String resourceName = partnerConfig.getResourceName();
123 ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
124 if (resourceEntry == null) {
125 Log.w(TAG, "Resource not found: " + resourceName);
126 return null;
127 }
128 Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
129
130 // for @null
131 TypedValue outValue = new TypedValue();
132 resource.getValue(resourceEntry.getResourceId(), outValue, true);
133 if (outValue.type == TypedValue.TYPE_REFERENCE && outValue.data == 0) {
134 return result;
135 }
136
137 result = resource.getDrawable(resourceEntry.getResourceId(), null);
138 mPartnerResourceCache.put(partnerConfig, result);
139 } catch (PackageManager.NameNotFoundException | NotFoundException exception) {
140 Log.e(TAG, exception.getMessage());
141 }
142 return result;
143 }
144
145 /**
146 * Returns the string of the given {@code partnerConfig}, or {@code null} if the given {@code
147 * resourceConfig} is not found. If the {@code ResourceType} of the given {@code partnerConfig}
148 * is not string, IllegalArgumentException will be thrown.
149 *
150 * @param context The context of client activity
151 * @param partnerConfig The {@code PartnerConfig} of target resource
152 */
153 @Nullable
154 public String getString(@NonNull Context context, PartnerConfig partnerConfig) {
155 if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.STRING) {
156 throw new IllegalArgumentException("Not a string resource");
157 }
158
159 if (mPartnerResourceCache.containsKey(partnerConfig)) {
160 return (String) mPartnerResourceCache.get(partnerConfig);
161 }
162
163 String result = null;
164 try {
165 String resourceName = partnerConfig.getResourceName();
166 ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
167 if (resourceEntry == null) {
168 Log.w(TAG, "Resource not found: " + resourceName);
169 return null;
170 }
171 Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
172 result = resource.getString(resourceEntry.getResourceId());
173 mPartnerResourceCache.put(partnerConfig, result);
174 } catch (PackageManager.NameNotFoundException exception) {
175 Log.e(TAG, exception.getMessage());
176 }
177 return result;
178 }
179
180 /**
181 * Returns the dimension of given {@code partnerConfig}. The default return value is 0.
182 *
183 * @param context The context of client activity
184 * @param resourceConfig The {@code PartnerConfig} of target resource
185 */
186 public float getDimension(@NonNull Context context, PartnerConfig resourceConfig) {
187 return getDimension(context, resourceConfig, 0);
188 }
189
190 /**
191 * Returns the dimension of given {@code partnerConfig}. If the given {@code partnerConfig}
192 * not found, will return {@code defaultValue}. If the {@code ResourceType} of given {@code
193 * resourceConfig} is not dimension, will throw IllegalArgumentException.
194 *
195 * @param context The context of client activity
196 * @param partnerConfig The {@code PartnerConfig} of target resource
197 * @param defaultValue The default value
198 */
199 public float getDimension(
200 @NonNull Context context, PartnerConfig partnerConfig, float defaultValue) {
201 if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.DIMENSION) {
202 throw new IllegalArgumentException("Not a dimension resource");
203 }
204
205 if (mPartnerResourceCache.containsKey(partnerConfig)) {
206 return (float) mPartnerResourceCache.get(partnerConfig);
207 }
208
209 float result = defaultValue;
210 try {
211 String resourceName = partnerConfig.getResourceName();
212 ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
213 if (resourceEntry == null) {
214 Log.w(TAG, "Resource not found: " + resourceName);
215 return defaultValue;
216 }
217 Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
218 result = resource.getDimension(resourceEntry.getResourceId());
219 mPartnerResourceCache.put(partnerConfig, result);
220 } catch (PackageManager.NameNotFoundException exception) {
221 Log.e(TAG, exception.getMessage());
222 }
223 return result;
224 }
225
226 /**
227 * Returns the boolean value of given {@code partnerConfig}. If the given {@code partnerConfig}
228 * not found, will return {@code defaultValue}. If the {@code ResourceType} of given {@code
229 * resourceConfig} is not boolean, will throw IllegalArgumentException.
230 *
231 * @param context The context of client activity
232 * @param partnerConfig The {@code PartnerConfig} of target resource
233 * @param defaultValue The default value
234 */
235 public boolean getBoolean(
236 @NonNull Context context, PartnerConfig partnerConfig, boolean defaultValue) {
237 if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.BOOLEAN) {
238 throw new IllegalArgumentException("Not a boolean resource");
239 }
240
241 if (mPartnerResourceCache.containsKey(partnerConfig)) {
242 return (boolean) mPartnerResourceCache.get(partnerConfig);
243 }
244
245 boolean result = defaultValue;
246 try {
247 String resourceName = partnerConfig.getResourceName();
248 ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName);
249 if (resourceEntry == null) {
250 Log.w(TAG, "Resource not found: " + resourceName);
251 return defaultValue;
252 }
253 Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName());
254 result = resource.getBoolean(resourceEntry.getResourceId());
255 mPartnerResourceCache.put(partnerConfig, result);
256 } catch (PackageManager.NameNotFoundException exception) {
257 Log.e(TAG, exception.getMessage());
258 }
259 return result;
260 }
261
262 private void getPartnerConfigBundle(Context context) {
263 if (mResultBundle == null) {
264 try {
265 Uri contentUri =
266 new Uri.Builder()
267 .scheme(ContentResolver.SCHEME_CONTENT)
268 .authority(SUW_AUTHORITY)
269 .appendPath(SUW_GET_PARTNER_CONFIG_METHOD)
270 .build();
271 mResultBundle = context.getContentResolver().call(
272 contentUri,
273 SUW_GET_PARTNER_CONFIG_METHOD,
274 /* arg= */ null,
275 /* extras= */ null);
276 mPartnerResourceCache.clear();
277 } catch (IllegalArgumentException exception) {
278 Log.w(TAG, "Fail to get config from suw provider");
279 }
280 }
281 }
282
283 private Resources getResourcesByPackageName(Context context, String packageName)
284 throws PackageManager.NameNotFoundException {
285 PackageManager manager = context.getPackageManager();
286 return manager.getResourcesForApplication(packageName);
287 }
288
289 private ResourceEntry getResourceEntryFromKey(String resourceName) {
290 if (mResultBundle == null) {
291 return null;
292 }
293 return ResourceEntry.fromBundle(mResultBundle.getBundle(resourceName));
294 }
295
296 private PartnerConfigHelper(Context context) {
297 getPartnerConfigBundle(context);
298 }
299
300
301 @VisibleForTesting
302 static synchronized void resetForTesting() {
303 sInstance = null;
304 }
305}