am 34743ac7: Merge "Add API to create new contexts with custom configurations." into jb-mr1-dev

* commit '34743ac7d688a7ecf4daec84078fc7ec74a6dac9':
  Add API to create new contexts with custom configurations.
This commit is contained in:
Dianne Hackborn
2012-08-14 17:35:09 -07:00
committed by Android Git Automerger
13 changed files with 203 additions and 29 deletions

View File

@@ -5262,6 +5262,7 @@ package android.content {
method public abstract int checkUriPermission(android.net.Uri, int, int, int);
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] databaseList();
method public abstract boolean deleteDatabase(java.lang.String);
@@ -5406,6 +5407,7 @@ package android.content {
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper() throws java.io.IOException;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
method public boolean deleteDatabase(java.lang.String);
@@ -21148,6 +21150,7 @@ package android.test.mock {
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
method public boolean deleteDatabase(java.lang.String);
@@ -23352,6 +23355,7 @@ package android.view {
public class ContextThemeWrapper extends android.content.ContextWrapper {
ctor public ContextThemeWrapper();
ctor public ContextThemeWrapper(android.content.Context, int);
method public void applyOverrideConfiguration(android.content.res.Configuration);
method protected void onApplyThemeResource(android.content.res.Resources.Theme, int, boolean);
}

View File

@@ -1471,13 +1471,25 @@ public final class ActivityThread {
private static class ResourcesKey {
final private String mResDir;
final private Configuration mOverrideConfiguration;
final private float mScale;
final private int mHash;
ResourcesKey(String resDir, float scale) {
ResourcesKey(String resDir, Configuration overrideConfiguration, float scale) {
mResDir = resDir;
if (overrideConfiguration != null) {
if (Configuration.EMPTY.equals(overrideConfiguration)) {
overrideConfiguration = null;
}
}
mOverrideConfiguration = overrideConfiguration;
mScale = scale;
mHash = mResDir.hashCode() << 2 + (int) (mScale * 2);
int hash = 17;
hash = 31 * hash + mResDir.hashCode();
hash = 31 * hash + (mOverrideConfiguration != null
? mOverrideConfiguration.hashCode() : 0);
hash = 31 * hash + Float.floatToIntBits(mScale);
mHash = hash;
}
@Override
@@ -1491,7 +1503,21 @@ public final class ActivityThread {
return false;
}
ResourcesKey peer = (ResourcesKey) obj;
return mResDir.equals(peer.mResDir) && mScale == peer.mScale;
if (!mResDir.equals(peer.mResDir)) {
return false;
}
if (mOverrideConfiguration != peer.mOverrideConfiguration) {
if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
return false;
}
if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) {
return false;
}
}
if (mScale != peer.mScale) {
return false;
}
return true;
}
}
@@ -1562,8 +1588,10 @@ public final class ActivityThread {
* @param compInfo the compability info. It will use the default compatibility info when it's
* null.
*/
Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);
Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
CompatibilityInfo compInfo) {
ResourcesKey key = new ResourcesKey(resDir, overrideConfiguration,
compInfo.applicationScale);
Resources r;
synchronized (mPackages) {
// Resources is app scale dependent.
@@ -1595,13 +1623,20 @@ public final class ActivityThread {
//Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
DisplayMetrics metrics = getDisplayMetricsLocked(null, false);
r = new Resources(assets, metrics, getConfiguration(), compInfo);
Configuration config;
if (key.mOverrideConfiguration != null) {
config = new Configuration(getConfiguration());
config.updateFrom(key.mOverrideConfiguration);
} else {
config = getConfiguration();
}
r = new Resources(assets, metrics, config, compInfo);
if (false) {
Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale="
+ r.getCompatibilityInfo().applicationScale);
}
synchronized (mPackages) {
WeakReference<Resources> wr = mActiveResources.get(key);
Resources existing = wr != null ? wr.get() : null;
@@ -1621,8 +1656,10 @@ public final class ActivityThread {
/**
* Creates the top level resources for the given package.
*/
Resources getTopLevelResources(String resDir, LoadedApk pkgInfo) {
return getTopLevelResources(resDir, pkgInfo.mCompatibilityInfo.get());
Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
return getTopLevelResources(resDir, overrideConfiguration,
pkgInfo.mCompatibilityInfo.get());
}
final Handler getHandler() {
@@ -3675,18 +3712,28 @@ public final class ActivityThread {
ApplicationPackageManager.configurationChanged();
//Slog.i(TAG, "Configuration changed in " + currentPackageName());
Iterator<WeakReference<Resources>> it =
mActiveResources.values().iterator();
//Iterator<Map.Entry<String, WeakReference<Resources>>> it =
// mActiveResources.entrySet().iterator();
Configuration tmpConfig = null;
Iterator<Map.Entry<ResourcesKey, WeakReference<Resources>>> it =
mActiveResources.entrySet().iterator();
while (it.hasNext()) {
WeakReference<Resources> v = it.next();
Resources r = v.get();
Map.Entry<ResourcesKey, WeakReference<Resources>> entry = it.next();
Resources r = entry.getValue().get();
if (r != null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ r + " config to: " + config);
r.updateConfiguration(config, dm, compat);
Configuration override = entry.getKey().mOverrideConfiguration;
if (override != null) {
if (tmpConfig == null) {
tmpConfig = new Configuration();
}
tmpConfig.setTo(config);
tmpConfig.updateFrom(override);
r.updateConfiguration(tmpConfig, dm, compat);
} else {
r.updateConfiguration(config, dm, compat);
}
//Slog.i(TAG, "Updated app resources " + v.getKey()
// + " " + r + ": " + r.getConfiguration());
} else {

View File

@@ -713,7 +713,7 @@ final class ApplicationPackageManager extends PackageManager {
}
Resources r = mContext.mMainThread.getTopLevelResources(
app.uid == Process.myUid() ? app.sourceDir
: app.publicSourceDir, mContext.mPackageInfo);
: app.publicSourceDir, null, mContext.mPackageInfo);
if (r != null) {
return r;
}

View File

@@ -37,6 +37,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
@@ -525,7 +526,7 @@ class ContextImpl extends Context {
@Override
public AssetManager getAssets() {
return mResources.getAssets();
return getResources().getAssets();
}
@Override
@@ -1590,6 +1591,16 @@ class ContextImpl extends Context {
"Application package " + packageName + " not found");
}
@Override
public Context createConfigurationContext(Configuration overrideConfiguration) {
ContextImpl c = new ContextImpl();
c.init(mPackageInfo, null, mMainThread);
c.mResources = mMainThread.getTopLevelResources(
mPackageInfo.getResDir(), overrideConfiguration,
mResources.getCompatibilityInfo());
return c;
}
@Override
public boolean isRestricted() {
return mRestricted;
@@ -1659,12 +1670,11 @@ class ContextImpl extends Context {
" compatiblity info:" + container.getDisplayMetrics());
}
mResources = mainThread.getTopLevelResources(
mPackageInfo.getResDir(), container.getCompatibilityInfo());
mPackageInfo.getResDir(), null, container.getCompatibilityInfo());
}
mMainThread = mainThread;
mContentResolver = new ApplicationContentResolver(this, mainThread);
setActivityToken(activityToken);
mActivityToken = activityToken;
}
final void init(Resources resources, ActivityThread mainThread) {
@@ -1691,10 +1701,6 @@ class ContextImpl extends Context {
return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext());
}
final void setActivityToken(IBinder token) {
mActivityToken = token;
}
final void setOuterContext(Context context) {
mOuterContext = context;
}

View File

@@ -471,7 +471,7 @@ public final class LoadedApk {
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, this);
mResources = mainThread.getTopLevelResources(mResDir, null, this);
}
return mResources;
}

View File

@@ -19,6 +19,7 @@ package android.content;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.DatabaseErrorHandler;
@@ -2444,6 +2445,23 @@ public abstract class Context {
public abstract Context createPackageContext(String packageName,
int flags) throws PackageManager.NameNotFoundException;
/**
* Return a new Context object for the current Context but whose resources
* are adjusted to match the given Configuration. Each call to this method
* returns a new instance of a Contex object; Context objects are not
* shared, however common state (ClassLoader, other Resources for the
* same configuration) may be so the Context itself can be fairly lightweight.
*
* @param overrideConfiguration A {@link Configuration} specifying what
* values to modify in the base Configuration of the original Context's
* resources. If the base configuration changes (such as due to an
* orientation change), the resources of this context will also change except
* for those that have been explicitly overridden with a value here.
*
* @return A Context for the application.
*/
public abstract Context createConfigurationContext(Configuration overrideConfiguration);
/**
* Indicates whether this Context is restricted.
*

View File

@@ -19,6 +19,7 @@ package android.content;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
@@ -532,6 +533,11 @@ public class ContextWrapper extends Context {
return mBase.createPackageContext(packageName, flags);
}
@Override
public Context createConfigurationContext(Configuration overrideConfiguration) {
return mBase.createConfigurationContext(overrideConfiguration);
}
@Override
public boolean isRestricted() {
return mBase.isRestricted();

View File

@@ -35,6 +35,9 @@ import java.util.Locale;
* <pre>Configuration config = getResources().getConfiguration();</pre>
*/
public final class Configuration implements Parcelable, Comparable<Configuration> {
/** @hide */
public static final Configuration EMPTY = new Configuration();
/**
* Current user preference for the scaling factor for fonts, relative
* to the base density scaling.

View File

@@ -1435,9 +1435,12 @@ public class Resources {
int configChanges = 0xfffffff;
if (config != null) {
mTmpConfig.setTo(config);
int density = config.densityDpi;
if (density == Configuration.DENSITY_DPI_UNDEFINED) {
density = mMetrics.noncompatDensityDpi;
}
if (mCompatibilityInfo != null) {
mCompatibilityInfo.applyToConfiguration(mMetrics.noncompatDensityDpi,
mTmpConfig);
mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
}
if (mTmpConfig.locale == null) {
mTmpConfig.locale = Locale.getDefault();
@@ -1448,6 +1451,10 @@ public class Resources {
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
}
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
mMetrics.densityDpi = mConfiguration.densityDpi;
mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
}
mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
String locale = null;

View File

@@ -18,6 +18,7 @@ package android.view;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
@@ -30,6 +31,8 @@ public class ContextThemeWrapper extends ContextWrapper {
private int mThemeResource;
private Resources.Theme mTheme;
private LayoutInflater mInflater;
private Configuration mOverrideConfiguration;
private Resources mResources;
public ContextThemeWrapper() {
super(null);
@@ -45,6 +48,41 @@ public class ContextThemeWrapper extends ContextWrapper {
super.attachBaseContext(newBase);
mBase = newBase;
}
/**
* Call to set an "override configuration" on this context -- this is
* a configuration that replies one or more values of the standard
* configuration that is applied to the context. See
* {@link Context#createConfigurationContext(Configuration)} for more
* information.
*
* <p>This method can only be called once, and must be called before any
* calls to {@link #getResources()} are made.
*/
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
if (mResources != null) {
throw new IllegalStateException("getResources() has already been called");
}
if (mOverrideConfiguration != null) {
throw new IllegalStateException("Override configuration has already been set");
}
mOverrideConfiguration = new Configuration(overrideConfiguration);
}
@Override
public Resources getResources() {
if (mResources != null) {
return mResources;
}
if (mOverrideConfiguration == null) {
mResources = super.getResources();
return mResources;
} else {
Context resc = createConfigurationContext(mOverrideConfiguration);
mResources = resc.getResources();
return mResources;
}
}
@Override public void setTheme(int resid) {
mThemeResource = resid;

View File

@@ -28,6 +28,7 @@ import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
@@ -476,6 +477,11 @@ public class MockContext extends Context {
throw new UnsupportedOperationException();
}
@Override
public Context createConfigurationContext(Configuration overrideConfiguration) {
throw new UnsupportedOperationException();
}
@Override
public boolean isRestricted() {
throw new UnsupportedOperationException();

View File

@@ -40,12 +40,16 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.content.Context;
import android.content.res.Configuration;
import android.util.Log;
public class ActivityTestMain extends Activity {
static final String TAG = "ActivityTest";
static final String KEY_CONFIGURATION = "configuration";
ActivityManager mAm;
Configuration mOverrideConfig;
class BroadcastResultReceiver extends BroadcastReceiver {
@Override
@@ -111,6 +115,12 @@ public class ActivityTestMain extends Activity {
super.onCreate(savedInstanceState);
mAm = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
if (savedInstanceState != null) {
mOverrideConfig = savedInstanceState.getParcelable(KEY_CONFIGURATION);
if (mOverrideConfig != null) {
applyOverrideConfiguration(mOverrideConfig);
}
}
}
@Override
@@ -182,6 +192,21 @@ public class ActivityTestMain extends Activity {
return true;
}
});
menu.add("Density!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
if (mOverrideConfig == null) {
mOverrideConfig = new Configuration();
}
if (mOverrideConfig.densityDpi == Configuration.DENSITY_DPI_UNDEFINED) {
mOverrideConfig.densityDpi = (getApplicationContext().getResources()
.getConfiguration().densityDpi*2)/3;
} else {
mOverrideConfig.densityDpi = Configuration.DENSITY_DPI_UNDEFINED;
}
recreate();
return true;
}
});
return true;
}
@@ -191,6 +216,14 @@ public class ActivityTestMain extends Activity {
buildUi();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mOverrideConfig != null) {
outState.putParcelable(KEY_CONFIGURATION, mOverrideConfig);
}
}
private View scrollWrap(View view) {
ScrollView scroller = new ScrollView(this);
scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT,

View File

@@ -916,6 +916,12 @@ public final class BridgeContext extends Context {
return null;
}
@Override
public Context createConfigurationContext(Configuration overrideConfiguration) {
// pass
return null;
}
@Override
public String[] databaseList() {
// pass