| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.car.setupwizardlib.partner; |
| |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.content.res.Resources; |
| import android.content.res.Resources.NotFoundException; |
| import android.graphics.drawable.Drawable; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.util.Log; |
| import android.util.TypedValue; |
| |
| import androidx.annotation.ColorInt; |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.VisibleForTesting; |
| |
| import java.util.EnumMap; |
| |
| /** The helper reads and caches the partner configurations from Car Setup Wizard. */ |
| public class PartnerConfigHelper { |
| |
| private static final String TAG = PartnerConfigHelper.class.getSimpleName(); |
| |
| @VisibleForTesting |
| static final String SUW_AUTHORITY = "com.google.android.car.setupwizard.partner"; |
| |
| @VisibleForTesting |
| static final String SUW_GET_PARTNER_CONFIG_METHOD = "getOverlayConfig"; |
| private static volatile PartnerConfigHelper sInstance = null; |
| |
| @VisibleForTesting Bundle mResultBundle = null; |
| |
| @VisibleForTesting |
| final EnumMap<PartnerConfig, Object> mPartnerResourceCache = new EnumMap<>(PartnerConfig.class); |
| |
| /** Factory method to get an instance */ |
| public static PartnerConfigHelper get(@NonNull Context context) { |
| if (sInstance == null) { |
| synchronized (PartnerConfigHelper.class) { |
| if (sInstance == null) { |
| sInstance = new PartnerConfigHelper(context); |
| } |
| } |
| } |
| return sInstance; |
| } |
| |
| /** |
| * Returns the color of given {@code partnerConfig}, or 0 if the given {@code partnerConfig} |
| * is not found. If the {@code ResourceType} of the given {@code partnerConfig} is not color, |
| * IllegalArgumentException will be thrown. |
| * |
| * @param context The context of client activity |
| * @param partnerConfig The {@code PartnerConfig} of target resource |
| */ |
| @ColorInt |
| public int getColor(@NonNull Context context, PartnerConfig partnerConfig) { |
| if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.COLOR) { |
| throw new IllegalArgumentException("Not a color resource"); |
| } |
| |
| if (mPartnerResourceCache.containsKey(partnerConfig)) { |
| return (int) mPartnerResourceCache.get(partnerConfig); |
| } |
| |
| int result = 0; |
| try { |
| String resourceName = partnerConfig.getResourceName(); |
| ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName); |
| if (resourceEntry == null) { |
| Log.w(TAG, "Resource not found: " + resourceName); |
| return 0; |
| } |
| |
| Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName()); |
| result = resource.getColor(resourceEntry.getResourceId(), null); |
| mPartnerResourceCache.put(partnerConfig, result); |
| } catch (PackageManager.NameNotFoundException exception) { |
| Log.e(TAG, exception.getMessage()); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the {@code Drawable} of given {@code partnerConfig}, or {@code null} if the given |
| * {@code partnerConfig} is not found. If the {@code ResourceType} of the given {@code |
| * resourceConfig} is not drawable, IllegalArgumentException will be thrown. |
| * |
| * @param context The context of client activity |
| * @param partnerConfig The {@code PartnerConfig} of target resource |
| */ |
| @Nullable |
| public Drawable getDrawable(@NonNull Context context, PartnerConfig partnerConfig) { |
| if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.DRAWABLE) { |
| throw new IllegalArgumentException("Not a drawable resource"); |
| } |
| |
| if (mPartnerResourceCache.containsKey(partnerConfig)) { |
| return (Drawable) mPartnerResourceCache.get(partnerConfig); |
| } |
| |
| Drawable result = null; |
| try { |
| String resourceName = partnerConfig.getResourceName(); |
| ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName); |
| if (resourceEntry == null) { |
| Log.w(TAG, "Resource not found: " + resourceName); |
| return null; |
| } |
| Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName()); |
| |
| // for @null |
| TypedValue outValue = new TypedValue(); |
| resource.getValue(resourceEntry.getResourceId(), outValue, true); |
| if (outValue.type == TypedValue.TYPE_REFERENCE && outValue.data == 0) { |
| return result; |
| } |
| |
| result = resource.getDrawable(resourceEntry.getResourceId(), null); |
| mPartnerResourceCache.put(partnerConfig, result); |
| } catch (PackageManager.NameNotFoundException | NotFoundException exception) { |
| Log.e(TAG, exception.getMessage()); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the string of the given {@code partnerConfig}, or {@code null} if the given {@code |
| * resourceConfig} is not found. If the {@code ResourceType} of the given {@code partnerConfig} |
| * is not string, IllegalArgumentException will be thrown. |
| * |
| * @param context The context of client activity |
| * @param partnerConfig The {@code PartnerConfig} of target resource |
| */ |
| @Nullable |
| public String getString(@NonNull Context context, PartnerConfig partnerConfig) { |
| if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.STRING) { |
| throw new IllegalArgumentException("Not a string resource"); |
| } |
| |
| if (mPartnerResourceCache.containsKey(partnerConfig)) { |
| return (String) mPartnerResourceCache.get(partnerConfig); |
| } |
| |
| String result = null; |
| try { |
| String resourceName = partnerConfig.getResourceName(); |
| ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName); |
| if (resourceEntry == null) { |
| Log.w(TAG, "Resource not found: " + resourceName); |
| return null; |
| } |
| Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName()); |
| result = resource.getString(resourceEntry.getResourceId()); |
| mPartnerResourceCache.put(partnerConfig, result); |
| } catch (PackageManager.NameNotFoundException exception) { |
| Log.e(TAG, exception.getMessage()); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the dimension of given {@code partnerConfig}. The default return value is 0. |
| * |
| * @param context The context of client activity |
| * @param resourceConfig The {@code PartnerConfig} of target resource |
| */ |
| public float getDimension(@NonNull Context context, PartnerConfig resourceConfig) { |
| return getDimension(context, resourceConfig, 0); |
| } |
| |
| /** |
| * Returns the dimension of given {@code partnerConfig}. If the given {@code partnerConfig} |
| * not found, will return {@code defaultValue}. If the {@code ResourceType} of given {@code |
| * resourceConfig} is not dimension, will throw IllegalArgumentException. |
| * |
| * @param context The context of client activity |
| * @param partnerConfig The {@code PartnerConfig} of target resource |
| * @param defaultValue The default value |
| */ |
| public float getDimension( |
| @NonNull Context context, PartnerConfig partnerConfig, float defaultValue) { |
| if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.DIMENSION) { |
| throw new IllegalArgumentException("Not a dimension resource"); |
| } |
| |
| if (mPartnerResourceCache.containsKey(partnerConfig)) { |
| return (float) mPartnerResourceCache.get(partnerConfig); |
| } |
| |
| float result = defaultValue; |
| try { |
| String resourceName = partnerConfig.getResourceName(); |
| ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName); |
| if (resourceEntry == null) { |
| Log.w(TAG, "Resource not found: " + resourceName); |
| return defaultValue; |
| } |
| Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName()); |
| result = resource.getDimension(resourceEntry.getResourceId()); |
| mPartnerResourceCache.put(partnerConfig, result); |
| } catch (PackageManager.NameNotFoundException exception) { |
| Log.e(TAG, exception.getMessage()); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the boolean value of given {@code partnerConfig}. If the given {@code partnerConfig} |
| * not found, will return {@code defaultValue}. If the {@code ResourceType} of given {@code |
| * resourceConfig} is not boolean, will throw IllegalArgumentException. |
| * |
| * @param context The context of client activity |
| * @param partnerConfig The {@code PartnerConfig} of target resource |
| * @param defaultValue The default value |
| */ |
| public boolean getBoolean( |
| @NonNull Context context, PartnerConfig partnerConfig, boolean defaultValue) { |
| if (partnerConfig.getResourceType() != PartnerConfig.ResourceType.BOOLEAN) { |
| throw new IllegalArgumentException("Not a boolean resource"); |
| } |
| |
| if (mPartnerResourceCache.containsKey(partnerConfig)) { |
| return (boolean) mPartnerResourceCache.get(partnerConfig); |
| } |
| |
| boolean result = defaultValue; |
| try { |
| String resourceName = partnerConfig.getResourceName(); |
| ResourceEntry resourceEntry = getResourceEntryFromKey(resourceName); |
| if (resourceEntry == null) { |
| Log.w(TAG, "Resource not found: " + resourceName); |
| return defaultValue; |
| } |
| Resources resource = getResourcesByPackageName(context, resourceEntry.getPackageName()); |
| result = resource.getBoolean(resourceEntry.getResourceId()); |
| mPartnerResourceCache.put(partnerConfig, result); |
| } catch (PackageManager.NameNotFoundException exception) { |
| Log.e(TAG, exception.getMessage()); |
| } |
| return result; |
| } |
| |
| private void getPartnerConfigBundle(Context context) { |
| if (mResultBundle == null) { |
| try { |
| Uri contentUri = |
| new Uri.Builder() |
| .scheme(ContentResolver.SCHEME_CONTENT) |
| .authority(SUW_AUTHORITY) |
| .appendPath(SUW_GET_PARTNER_CONFIG_METHOD) |
| .build(); |
| mResultBundle = context.getContentResolver().call( |
| contentUri, |
| SUW_GET_PARTNER_CONFIG_METHOD, |
| /* arg= */ null, |
| /* extras= */ null); |
| mPartnerResourceCache.clear(); |
| } catch (IllegalArgumentException exception) { |
| Log.w(TAG, "Fail to get config from suw provider"); |
| } |
| } |
| } |
| |
| private Resources getResourcesByPackageName(Context context, String packageName) |
| throws PackageManager.NameNotFoundException { |
| PackageManager manager = context.getPackageManager(); |
| return manager.getResourcesForApplication(packageName); |
| } |
| |
| private ResourceEntry getResourceEntryFromKey(String resourceName) { |
| if (mResultBundle == null) { |
| return null; |
| } |
| return ResourceEntry.fromBundle(mResultBundle.getBundle(resourceName)); |
| } |
| |
| private PartnerConfigHelper(Context context) { |
| getPartnerConfigBundle(context); |
| } |
| |
| |
| @VisibleForTesting |
| static synchronized void resetForTesting() { |
| sInstance = null; |
| } |
| } |