package fr.prcaen.externalresources;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.AnyRes;
import android.support.annotation.ArrayRes;
import android.support.annotation.BoolRes;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DimenRes;
import android.support.annotation.IdRes;
import android.support.annotation.IntegerRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.util.DisplayMetrics;
import fr.prcaen.externalresources.converter.Converter;
import fr.prcaen.externalresources.converter.JsonConverter;
import fr.prcaen.externalresources.exception.ExternalResourceException;
import fr.prcaen.externalresources.exception.NotFoundException;
import fr.prcaen.externalresources.listener.OnExternalResourcesChangeListener;
import fr.prcaen.externalresources.listener.OnExternalResourcesLoadFailedListener;
import fr.prcaen.externalresources.model.DimensionResource;
import fr.prcaen.externalresources.model.Resource;
import fr.prcaen.externalresources.model.Resources;
import fr.prcaen.externalresources.url.DefaultUrl;
import fr.prcaen.externalresources.url.Url;
import java.util.ArrayList;
/**
* Update your Android resources (strings, integers, booleans, ...) over the air.
*
* - Use native Android resources or default raw Json / XML files.
* - Define your own URL builder which allow you calling your server with query strings or url
* params
* - Define if a config change through onConfigurationChanged if the library should call your
* server.
* - Cache based on Http last modified header.
* - Event triggered when resources have changed.
* - Event triggered when resources loading has fail.
* - Define your own converter. Json is the default one. This library also provide a Xml converter.
*
* Use {@link #initialize(Context, String)} for the global singleton instance or construct your
* own instance with {@link Builder}.
*/
public class ExternalResources {
public static final String TAG = "ExternalResources";
protected static volatile ExternalResources singleton = null;
@NonNull protected final ArrayList<OnExternalResourcesChangeListener> listeners =
new ArrayList<>();
@NonNull private final Context context;
@NonNull private final DisplayMetrics metrics;
@NonNull private final Dispatcher dispatcher;
@NonNull private final Options options;
private final boolean useApplicationResources;
@Nullable private OnExternalResourcesLoadFailedListener failedListener;
@NonNull private Resources resources;
@NonNull private Configuration configuration;
private ExternalResources(@NonNull Context context, @NonNull Converter converter,
@NonNull Url url, @NonNull Options options, @Cache.Policy int cachePolicy,
@Logger.Level int logLevel, @NonNull Resources defaultResources,
@Nullable OnExternalResourcesLoadFailedListener failedListener,
boolean useApplicationResources) {
Logger.setLevel(logLevel);
this.context = context;
this.dispatcher = new Dispatcher(context, new Downloader(context, converter, url, options),
new ExternalResourcesHandler(this), cachePolicy);
this.configuration = new Configuration(context.getResources().getConfiguration());
this.metrics = context.getResources().getDisplayMetrics();
this.resources = defaultResources;
this.options = options;
this.failedListener = failedListener;
this.useApplicationResources = useApplicationResources;
launch();
}
/**
* Initialize ExternalResources instance with defaults parameters.
*
* @param context Any context, will not be retained.
* @param url Url implementation
* @return ExternalResources instance
* @throws IllegalArgumentException if context is null or if url is null.
* @see Url
*/
@SuppressWarnings("ConstantConditions") public static ExternalResources initialize(
@NonNull Context context, Url url) {
if (null == context) {
throw new IllegalArgumentException("Context must not be null.");
}
if (null == url) {
throw new IllegalArgumentException("Path must not be null.");
}
synchronized (ExternalResources.class) {
if (null != singleton) {
throw new IllegalStateException("Singleton instance already exists.");
}
singleton = new Builder(context, url).build();
}
return singleton;
}
/**
* Initialize ExternalResources instance with defaults parameters.
* eg: If your base url is http://test.com/android-resources.json, it will be append by query
* parameters:
* http://test.com/android-resources.json?locale=fr_FR&density_dpi=320&screen_height_dp=100&navigation_hidden=0...
*
* @param context Any context, will not be retained.
* @param baseUrl URL string composed of a scheme, host, path and optionally port
* @return ExternalResources instance
* @throws IllegalArgumentException if context is null or if path is null.
* @see DefaultUrl
*/
@SuppressWarnings("ConstantConditions") public static ExternalResources initialize(
@NonNull Context context, @NonNull String baseUrl) {
if (null == context) {
throw new IllegalArgumentException("Context must not be null.");
}
if (null == baseUrl) {
throw new IllegalArgumentException("URL must not be null.");
}
synchronized (ExternalResources.class) {
if (null != singleton) {
throw new IllegalStateException("Singleton instance already exists.");
}
singleton = new Builder(context, new DefaultUrl(baseUrl)).build();
}
return singleton;
}
/**
* Initialize ExternalResources singleton with external resources instance.
*
* @param externalResources instance of @ExternalResources.
* @return ExternalResources instance
* @throws IllegalArgumentException if singleton of ExternalResources already exists or if
* externalResources is null.
* @see DefaultUrl
*/
@SuppressWarnings("ConstantConditions") public static ExternalResources initialize(
@NonNull ExternalResources externalResources) {
if (null == externalResources) {
throw new IllegalArgumentException("ExternalResources must not be null.");
}
synchronized (ExternalResources.class) {
if (null != singleton) {
throw new IllegalStateException("Singleton instance already exists.");
}
singleton = externalResources;
}
return singleton;
}
/**
* Get ExternalResources instance, initialized by #initialize method.
*
* @return ExternalResources instance
* @throws IllegalArgumentException if #initialize method has not been called before.
* @see #initialize
*/
public static ExternalResources getInstance() {
if (null == singleton) {
throw new IllegalArgumentException("You should call initialize() before getInstance().");
}
return singleton;
}
/**
* This method should be call on in callback Application#onConfigurationChanged
* This allow to detected changes device configuration changes while your component is running.
*
* @param newConfig The new device configuration.
*/
public void onConfigurationChanged(Configuration newConfig) {
Logger.d(TAG, "onConfigurationChanged");
if (shouldRelaunch(newConfig)) {
Logger.v(TAG, "Relaunch");
launch();
}
this.configuration = new Configuration(newConfig);
}
/**
* Return a boolean associated with a particular resource ID. This resource can come from
* resources you provided via the URL or via default resources.
*
* @param resId The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return Returns the boolean value contained in the resource.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public boolean getBoolean(@BoolRes int resId) throws NotFoundException {
String key = getApplicationResourceEntryName(resId);
if (null == key) {
throw new NotFoundException("Boolean resource with resId: " + resId);
}
return getBoolean(key);
}
/**
* Return a boolean associated with a particular resource key. This resource can come from
* resources you provided via the URL or via default resources.
*
* @param key The desired resource key.
* @return Returns the boolean value contained in the resource.
* @throws NotFoundException Throws NotFoundException if the given key does not exist.
*/
public boolean getBoolean(@NonNull String key) throws NotFoundException {
Resource resource = resources.get(key);
if (null != resource) {
return resource.getAsBoolean();
}
@BoolRes int resId = getApplicationResourceIdentifier(key, "bool");
if (0 != resId) {
boolean value = context.getResources().getBoolean(resId);
resources.add(key, new Resource(value));
return value;
}
throw new NotFoundException("Boolean resource with key: " + key);
}
/**
* Returns a color associated with a particular resource ID and styled for
* the current theme. This resource can come from resources you provided via the URL or
* via default resources.
*
* @param resId The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return A single color value in the form 0xAARRGGBB.
* @throws NotFoundException if the given ID does not exist.
*/
@ColorInt public int getColor(@ColorRes int resId) throws NotFoundException {
String key = getApplicationResourceEntryName(resId);
if (null == key) {
throw new NotFoundException("Color resource with resId: " + resId);
}
Resource resource = resources.get(key);
if (null != resource && null != resource.getAsString()) {
return Color.parseColor(resource.getAsString());
}
int value = Utils.getColor(context, resId);
resources.add(key, new Resource(value));
return value;
}
/**
* Return a color associated with a particular resource key. This resource can come from
* resources you provided via the URL or via default resources.
*
* @param key The desired resource key.
* @return A single color value in the form 0xAARRGGBB.
* @throws NotFoundException Throws NotFoundException if the given key does not exist.
*/
@ColorInt public int getColor(@NonNull String key) throws NotFoundException {
Resource resource = resources.get(key);
if (null != resource) {
try {
return Color.parseColor(resource.getAsString());
} catch (IllegalArgumentException ignored) {
}
}
@ColorRes int resId = getApplicationResourceIdentifier(key, "color");
if (0 != resId) {
return Utils.getColor(context, resId);
}
throw new NotFoundException("Color resource with key: " + key);
}
/**
* Retrieve a dimensional for a particular resource ID. Unit
* conversions are based on the current {@link DisplayMetrics} associated
* with the resources.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param resId The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return Resource dimension value multiplied by the appropriate
* metric.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public float getDimension(@DimenRes int resId) throws NotFoundException {
String key = getApplicationResourceEntryName(resId);
if (null == key) {
throw new NotFoundException("Dimension resource with resId: " + resId);
}
Resource resource = resources.get(key);
if (null != resource && null != resource.getAsString()) {
return DimensionResource.fromString(resource.getAsString()).toFloat(metrics);
}
return context.getResources().getDimension(resId);
}
/**
* Retrieve a dimensional for a particular resource key. Unit
* conversions are based on the current {@link DisplayMetrics} associated
* with the resources.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param key The desired resource key,
* @return Resource dimension value multiplied by the appropriate
* metric.
* @throws NotFoundException Throws NotFoundException if the given key does not exist.
*/
public float getDimension(@NonNull String key) throws NotFoundException {
Resource resource = resources.get(key);
if (null != resource && null != resource.getAsString()) {
try {
return DimensionResource.fromString(resource.getAsString()).toFloat(metrics);
} catch (IllegalArgumentException ignored) {
}
}
@DimenRes int resId = getApplicationResourceIdentifier(key, "dimen");
if (0 != resId) {
float value = context.getResources().getDimension(resId);
resources.add(key, new Resource(value));
return value;
}
throw new NotFoundException("String resource with key: " + key);
}
/**
* Return the string value associated with a particular resource ID. It
* will be stripped of any styled text information.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param resId The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return String The string data associated with the resource,
* stripped of styled text information.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public String getString(@StringRes int resId) throws NotFoundException {
String key = getApplicationResourceEntryName(resId);
if (null == key) {
throw new NotFoundException("String resource with resId: " + resId);
}
return getString(key);
}
/**
* Return the string value associated with a particular resource key. It
* will be stripped of any styled text information.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param key The desired resource key
* @return String The string data associated with the resource,
* stripped of styled text information.
* @throws NotFoundException Throws NotFoundException if the given key does not exist.
*/
public String getString(@NonNull String key) throws NotFoundException {
Resource resource = resources.get(key);
if (null != resource) {
return resource.getAsString();
}
@StringRes int resId = getApplicationResourceIdentifier(key, "string");
if (0 != resId) {
String value = context.getResources().getString(resId);
resources.add(key, new Resource(value));
return value;
}
throw new NotFoundException("String resource with key: " + key);
}
/**
* Return the string value associated with a particular resource ID,
* substituting the format arguments as defined in {@link java.util.Formatter}
* and {@link java.lang.String#format}. It will be stripped of any styled text
* information.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param resId The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @param formatArgs The format arguments that will be used for substitution.
* @return String The string data associated with the resource,
* stripped of styled text information.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public String getString(@StringRes int resId, Object... formatArgs) throws NotFoundException {
String key = getApplicationResourceEntryName(resId);
if (null == key) {
throw new NotFoundException("String resource with resId: " + resId);
}
return getString(key, formatArgs);
}
/**
* Return the string value associated with a particular resource key,
* substituting the format arguments as defined in {@link java.util.Formatter}
* and {@link java.lang.String#format}. It will be stripped of any styled text
* information.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param key The desired resource key.
* @param formatArgs The format arguments that will be used for substitution.
* @return String The string data associated with the resource,
* stripped of styled text information.
* @throws NotFoundException Throws NotFoundException if the given key does not exist.
*/
public String getString(@NonNull String key, Object... formatArgs) throws NotFoundException {
String raw = getString(key);
return String.format(configuration.locale, raw, formatArgs);
}
/**
* Return the string array associated with a particular resource ID.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param resId The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return The string array associated with the resource.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public String[] getStringArray(@ArrayRes int resId) throws NotFoundException {
String key = getApplicationResourceEntryName(resId);
if (null == key) {
throw new NotFoundException("String array resource with resId: " + resId);
}
return getStringArray(key);
}
/**
* Return the string array associated with a particular resource key.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param key The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return The string array associated with the resource.
* @throws NotFoundException Throws NotFoundException if the given key does not exist.
*/
public String[] getStringArray(@NonNull String key) throws NotFoundException {
Resource resource = resources.get(key);
if (null != resource) {
return resource.getAsStringArray();
}
@ArrayRes int resId = getApplicationResourceIdentifier(key, "array");
if (0 != resId) {
String[] values = context.getResources().getStringArray(resId);
Resource[] stringResources = new Resource[values.length];
for (int i = 0; i < values.length; i++) {
stringResources[i] = new Resource(values[i]);
}
resources.add(key, new Resource(stringResources));
return values;
}
throw new NotFoundException("String array resource with key: " + key);
}
/**
* Return an integer associated with a particular resource ID.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param resId The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return Returns the integer value contained in the resource.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public int getInteger(@IntegerRes int resId) throws NotFoundException {
String key = getApplicationResourceEntryName(resId);
if (null != key) {
Resource resource = resources.get(key);
if (null != resource && null != resource.getAsInt()) {
return resource.getAsInt();
}
}
throw new NotFoundException("Integer resource with resId: " + resId);
}
/**
* Return an integer associated with a particular resource key.
* This resource can come from resources you provided via the URL or via default resources.
*
* @param key The desired resource key.
* @return Returns the integer value contained in the resource.
* @throws NotFoundException Throws NotFoundException if the given key does not exist.
*/
public int getInteger(@NonNull String key) throws NotFoundException {
Resource resource = resources.get(key);
if (null != resource && null != resource.getAsInt()) {
return resource.getAsInt();
}
@IntegerRes int resId = getApplicationResourceIdentifier(key, "integer");
if (0 != resId) {
int value = context.getResources().getInteger(resId);
resources.add(key, new Resource(value));
return value;
}
throw new NotFoundException("Integer resource with key: " + key);
}
/**
* Return the int array associated with a particular resource ID.
* * This resource can come from resources you provided via the URL or via default resources.
*
* @param resId The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return The int array associated with the resource.
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public int[] getIntArray(@ArrayRes int resId) throws NotFoundException {
String key = getApplicationResourceEntryName(resId);
if (null == key) {
throw new NotFoundException("Int array resource with resId: " + resId);
}
return getIntArray(key);
}
/**
* Return the int array associated with a particular resource key.
* * This resource can come from resources you provided via the URL or via default resources.
*
* @param key The desired resource identifier.
* @return The int array associated with the resource.
* @throws NotFoundException Throws NotFoundException if the given key does not exist.
*/
public int[] getIntArray(@NonNull String key) throws NotFoundException {
Resource resource = resources.get(key);
if (null != resource && null != resource.getAsIntegerArray()) {
return resource.getAsIntegerArray();
}
@ArrayRes int resId = getApplicationResourceIdentifier(key, "array");
if (0 != resId) {
int[] values = context.getResources().getIntArray(resId);
Resource[] intResources = new Resource[values.length];
for (int i = 0; i < values.length; i++) {
intResources[i] = new Resource(values[i]);
}
resources.add(key, new Resource(intResources));
return values;
}
throw new NotFoundException("Int array resource with key: " + key);
}
/**
* Register a listener which is trigger when resources are loaded or have changed
*
* @param listener receiver callback
* @see OnExternalResourcesChangeListener#onExternalResourcesChange(ExternalResources)
*/
public void register(OnExternalResourcesChangeListener listener) {
Logger.v(TAG, "Register listener:" + listener.getClass().getSimpleName());
listeners.add(listener);
}
/**
* Unregister a listener which is trigger when resources are loaded or have changed
*
* @param listener receiver callback
* @see OnExternalResourcesChangeListener#onExternalResourcesChange(ExternalResources)
*/
public void unregister(OnExternalResourcesChangeListener listener) {
Logger.v(TAG, "Unregister listener:" + listener.getClass().getSimpleName());
listeners.remove(listener);
}
/**
* Unregister fail listener, initialised by Builder#failListener
*/
public void removeFailListener() {
this.failedListener = null;
}
private void onResourcesLoadSuccess(Resources resources) {
Logger.i(TAG, "onResourcesLoadSuccess");
this.resources.merge(resources);
triggerChange();
}
private void onResourcesLoadFailed(ExternalResourceException exception) {
Logger.e(TAG, "onResourcesLoadFailed", exception);
if (null != failedListener) {
failedListener.onExternalResourcesLoadFailed(exception);
}
}
private void launch() {
Logger.v(TAG, "Launch");
dispatcher.dispatchLaunch();
}
@Nullable private String getApplicationResourceEntryName(@AnyRes int resId)
throws IllegalStateException {
if (!useApplicationResources) {
throw new IllegalStateException(
"You have set the useApplicationResources to false, using application resource is an error.");
}
return Utils.getAndroidResourceEntryName(context, resId);
}
@IdRes private int getApplicationResourceIdentifier(String key, String defType) {
return useApplicationResources ? Utils.getAndroidResourceIdentifier(context, key, defType) : 0;
}
@SuppressWarnings("ConstantConditions") private boolean shouldRelaunch(Configuration newConfig) {
return configuration.fontScale != newConfig.fontScale && options.isUseFontScale()
|| configuration.hardKeyboardHidden != newConfig.hardKeyboardHidden
&& options.isUseHardKeyboardHidden()
|| configuration.keyboard != newConfig.keyboard && options.isUseKeyboard()
|| configuration.keyboardHidden != newConfig.keyboardHidden && options.isUseKeyboardHidden()
|| configuration.mcc != newConfig.mcc && options.isUseMcc()
|| configuration.mnc != newConfig.mnc && options.isUseMnc()
|| configuration.navigation != newConfig.navigation && options.isUseNavigation()
|| configuration.navigationHidden != newConfig.navigationHidden
&& options.isUseNavigationHidden()
|| configuration.orientation != newConfig.orientation && options.isUseOrientation()
|| configuration.screenLayout != newConfig.screenLayout && options.isUseScreenLayout()
|| configuration.touchscreen != newConfig.touchscreen && options.isUseTouchscreen()
|| configuration.uiMode != newConfig.uiMode && options.isUseUiMode()
|| !configuration.locale.equals(newConfig.locale) && options.isUseLocale()
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1
&& configuration.densityDpi != newConfig.densityDpi
&& options.isUseDensityDpi()
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2
&& configuration.screenWidthDp != newConfig.screenWidthDp
&& options.isUseScreenWidthDp()
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2
&& configuration.screenHeightDp != newConfig.screenHeightDp
&& options.isUseScreenHeightDp()
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2
&& configuration.smallestScreenWidthDp != newConfig.smallestScreenWidthDp
&& options.isUseSmallestScreenWidthDp();
}
private void triggerChange() {
for (OnExternalResourcesChangeListener listener : listeners) {
if (null != listener) {
Logger.v(TAG, "Trigger change for listener: " + listener.getClass().getSimpleName());
listener.onExternalResourcesChange(this);
}
}
}
@SuppressWarnings("ConstantConditions") public static class Builder {
private final Context context;
private final Url url;
@Cache.Policy private int cachePolicy = Cache.POLICY_ALL;
@Logger.Level private int logLevel = Logger.LEVEL_ERROR;
@Nullable private Resources defaultResources;
@Nullable private OnExternalResourcesLoadFailedListener listener;
@Nullable private Options options;
@Nullable private Converter converter;
private boolean useApplicationResources = true;
/**
* Initialize builder with mandatory parameters.
*
* @param context Any context, will not be retained.
* @param url Url implementation.
*/
public Builder(@NonNull Context context, @NonNull Url url) {
if (null == context) {
throw new IllegalArgumentException("Context must not be null.");
}
if (null == url) {
throw new IllegalArgumentException("Url must not be null.");
}
this.context = context.getApplicationContext();
this.url = url;
}
/**
* Set cache policy for requesting resources.
*
* @param cachePolicy POLICY_NONE no cache, POLICY_OFFLINE to force cache, POLICY_ALL to cache
* all
* @return Builder instance.
*/
public Builder cachePolicy(@Cache.Policy int cachePolicy) {
this.cachePolicy = cachePolicy;
return this;
}
/**
* Set default resources before using resources from the web.
*
* @param defaultResources default instance of resources.
* @return Builder instance.
*/
public Builder defaultResources(@NonNull Resources defaultResources) {
if (null == defaultResources) {
throw new IllegalArgumentException("Default resources must not be null.");
}
if (null != this.defaultResources) {
throw new IllegalStateException("Default resources already set.");
}
this.defaultResources = defaultResources;
return this;
}
/**
* Set a listener which will be trigger if it's impossible to load external resources.
*
* @param listener OnExternalResourcesLoadFailedListener.
* @return Builder instance.
*/
public Builder failListener(@NonNull OnExternalResourcesLoadFailedListener listener) {
if (null == listener) {
throw new IllegalArgumentException("Listener must not be null.");
}
if (null != this.listener) {
throw new IllegalStateException("Listener already set.");
}
this.listener = listener;
return this;
}
/**
* Allow to set which configuration should be take into account when configuration change.
*
* @param options Options
* @return Builder instance.
* @see Options
*/
public Builder options(@NonNull Options options) {
if (null == options) {
throw new IllegalArgumentException("Options must not be null.");
}
if (null != this.options) {
throw new IllegalStateException("Options already set.");
}
this.options = options;
return this;
}
/**
* Set log level.
*
* @param logLevel LEVEL_OFF, LEVEL_ERROR, LEVEL_WARN, LEVEL_INFO, LEVEL_DEBUG, LEVEL_VERBOSE
* @return Builder instance.
*/
public Builder logLevel(@Logger.Level int logLevel) {
this.logLevel = logLevel;
return this;
}
/**
* Define a custom Converter implementation
*
* @param converter Converter implementation
* @return Builder instance.
*/
public Builder converter(@NonNull Converter converter) {
if (null == converter) {
throw new IllegalArgumentException("Converter must not be null.");
}
if (null != this.converter) {
throw new IllegalStateException("Converter already set.");
}
this.converter = converter;
return this;
}
/**
* Allow to use application resources.
*
* @param useApplicationResources boolean true if you want to use them, false if not.
* @return Builder instance.
*/
public Builder useApplicationResources(boolean useApplicationResources) {
this.useApplicationResources = useApplicationResources;
return this;
}
/**
* Build ExternalResources instance.
*
* @return ExternalResources instance.
*/
public ExternalResources build() {
if (null == defaultResources) {
this.defaultResources = new Resources();
}
if (null == options) {
this.options = Options.createDefault();
}
if (null == converter) {
this.converter = new JsonConverter();
}
return new ExternalResources(context, converter, url, options, cachePolicy, logLevel,
defaultResources, listener, useApplicationResources);
}
}
private static class ExternalResourcesHandler extends Handler {
private final ExternalResources externalResources;
public ExternalResourcesHandler(ExternalResources externalResources) {
super(Looper.getMainLooper());
this.externalResources = externalResources;
}
@Override public void handleMessage(Message message) {
super.handleMessage(message);
switch (message.what) {
case Dispatcher.REQUEST_DONE:
externalResources.onResourcesLoadSuccess((Resources) message.obj);
break;
case Dispatcher.REQUEST_FAILED:
externalResources.onResourcesLoadFailed((ExternalResourceException) message.obj);
break;
default:
Logger.e(ExternalResources.TAG, "Unknown message: " + message.what);
break;
}
}
}
}