blob: 2f367a862a901b948532a010c6b0d818e1eb68c7 [file] [log] [blame]
/*
* 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;
}
}