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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user