Skip to content

Commit 34743ac

Browse files
Dianne HackbornAndroid (Google) Code Review
authored andcommitted
Merge "Add API to create new contexts with custom configurations." into jb-mr1-dev
2 parents 07d1b28 + 756220b commit 34743ac

File tree

13 files changed

+203
-29
lines changed

13 files changed

+203
-29
lines changed

api/current.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5262,6 +5262,7 @@ package android.content {
52625262
method public abstract int checkUriPermission(android.net.Uri, int, int, int);
52635263
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
52645264
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
5265+
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
52655266
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
52665267
method public abstract java.lang.String[] databaseList();
52675268
method public abstract boolean deleteDatabase(java.lang.String);
@@ -5406,6 +5407,7 @@ package android.content {
54065407
method public int checkUriPermission(android.net.Uri, int, int, int);
54075408
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
54085409
method public void clearWallpaper() throws java.io.IOException;
5410+
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
54095411
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
54105412
method public java.lang.String[] databaseList();
54115413
method public boolean deleteDatabase(java.lang.String);
@@ -21148,6 +21150,7 @@ package android.test.mock {
2114821150
method public int checkUriPermission(android.net.Uri, int, int, int);
2114921151
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
2115021152
method public void clearWallpaper();
21153+
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
2115121154
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
2115221155
method public java.lang.String[] databaseList();
2115321156
method public boolean deleteDatabase(java.lang.String);
@@ -23352,6 +23355,7 @@ package android.view {
2335223355
public class ContextThemeWrapper extends android.content.ContextWrapper {
2335323356
ctor public ContextThemeWrapper();
2335423357
ctor public ContextThemeWrapper(android.content.Context, int);
23358+
method public void applyOverrideConfiguration(android.content.res.Configuration);
2335523359
method protected void onApplyThemeResource(android.content.res.Resources.Theme, int, boolean);
2335623360
}
2335723361

core/java/android/app/ActivityThread.java

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,13 +1471,25 @@ public final boolean queueIdle() {
14711471

14721472
private static class ResourcesKey {
14731473
final private String mResDir;
1474+
final private Configuration mOverrideConfiguration;
14741475
final private float mScale;
14751476
final private int mHash;
14761477

1477-
ResourcesKey(String resDir, float scale) {
1478+
ResourcesKey(String resDir, Configuration overrideConfiguration, float scale) {
14781479
mResDir = resDir;
1480+
if (overrideConfiguration != null) {
1481+
if (Configuration.EMPTY.equals(overrideConfiguration)) {
1482+
overrideConfiguration = null;
1483+
}
1484+
}
1485+
mOverrideConfiguration = overrideConfiguration;
14791486
mScale = scale;
1480-
mHash = mResDir.hashCode() << 2 + (int) (mScale * 2);
1487+
int hash = 17;
1488+
hash = 31 * hash + mResDir.hashCode();
1489+
hash = 31 * hash + (mOverrideConfiguration != null
1490+
? mOverrideConfiguration.hashCode() : 0);
1491+
hash = 31 * hash + Float.floatToIntBits(mScale);
1492+
mHash = hash;
14811493
}
14821494

14831495
@Override
@@ -1491,7 +1503,21 @@ public boolean equals(Object obj) {
14911503
return false;
14921504
}
14931505
ResourcesKey peer = (ResourcesKey) obj;
1494-
return mResDir.equals(peer.mResDir) && mScale == peer.mScale;
1506+
if (!mResDir.equals(peer.mResDir)) {
1507+
return false;
1508+
}
1509+
if (mOverrideConfiguration != peer.mOverrideConfiguration) {
1510+
if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
1511+
return false;
1512+
}
1513+
if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) {
1514+
return false;
1515+
}
1516+
}
1517+
if (mScale != peer.mScale) {
1518+
return false;
1519+
}
1520+
return true;
14951521
}
14961522
}
14971523

@@ -1562,8 +1588,10 @@ Configuration applyConfigCompatMainThread(int displayDensity, Configuration conf
15621588
* @param compInfo the compability info. It will use the default compatibility info when it's
15631589
* null.
15641590
*/
1565-
Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
1566-
ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);
1591+
Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
1592+
CompatibilityInfo compInfo) {
1593+
ResourcesKey key = new ResourcesKey(resDir, overrideConfiguration,
1594+
compInfo.applicationScale);
15671595
Resources r;
15681596
synchronized (mPackages) {
15691597
// Resources is app scale dependent.
@@ -1595,13 +1623,20 @@ Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
15951623

15961624
//Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
15971625
DisplayMetrics metrics = getDisplayMetricsLocked(null, false);
1598-
r = new Resources(assets, metrics, getConfiguration(), compInfo);
1626+
Configuration config;
1627+
if (key.mOverrideConfiguration != null) {
1628+
config = new Configuration(getConfiguration());
1629+
config.updateFrom(key.mOverrideConfiguration);
1630+
} else {
1631+
config = getConfiguration();
1632+
}
1633+
r = new Resources(assets, metrics, config, compInfo);
15991634
if (false) {
16001635
Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
16011636
+ r.getConfiguration() + " appScale="
16021637
+ r.getCompatibilityInfo().applicationScale);
16031638
}
1604-
1639+
16051640
synchronized (mPackages) {
16061641
WeakReference<Resources> wr = mActiveResources.get(key);
16071642
Resources existing = wr != null ? wr.get() : null;
@@ -1621,8 +1656,10 @@ Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
16211656
/**
16221657
* Creates the top level resources for the given package.
16231658
*/
1624-
Resources getTopLevelResources(String resDir, LoadedApk pkgInfo) {
1625-
return getTopLevelResources(resDir, pkgInfo.mCompatibilityInfo.get());
1659+
Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
1660+
LoadedApk pkgInfo) {
1661+
return getTopLevelResources(resDir, overrideConfiguration,
1662+
pkgInfo.mCompatibilityInfo.get());
16261663
}
16271664

16281665
final Handler getHandler() {
@@ -3675,18 +3712,28 @@ final boolean applyConfigurationToResourcesLocked(Configuration config,
36753712

36763713
ApplicationPackageManager.configurationChanged();
36773714
//Slog.i(TAG, "Configuration changed in " + currentPackageName());
3678-
3679-
Iterator<WeakReference<Resources>> it =
3680-
mActiveResources.values().iterator();
3681-
//Iterator<Map.Entry<String, WeakReference<Resources>>> it =
3682-
// mActiveResources.entrySet().iterator();
3715+
3716+
Configuration tmpConfig = null;
3717+
3718+
Iterator<Map.Entry<ResourcesKey, WeakReference<Resources>>> it =
3719+
mActiveResources.entrySet().iterator();
36833720
while (it.hasNext()) {
3684-
WeakReference<Resources> v = it.next();
3685-
Resources r = v.get();
3721+
Map.Entry<ResourcesKey, WeakReference<Resources>> entry = it.next();
3722+
Resources r = entry.getValue().get();
36863723
if (r != null) {
36873724
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
36883725
+ r + " config to: " + config);
3689-
r.updateConfiguration(config, dm, compat);
3726+
Configuration override = entry.getKey().mOverrideConfiguration;
3727+
if (override != null) {
3728+
if (tmpConfig == null) {
3729+
tmpConfig = new Configuration();
3730+
}
3731+
tmpConfig.setTo(config);
3732+
tmpConfig.updateFrom(override);
3733+
r.updateConfiguration(tmpConfig, dm, compat);
3734+
} else {
3735+
r.updateConfiguration(config, dm, compat);
3736+
}
36903737
//Slog.i(TAG, "Updated app resources " + v.getKey()
36913738
// + " " + r + ": " + r.getConfiguration());
36923739
} else {

core/java/android/app/ApplicationPackageManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ public Drawable getApplicationLogo(String packageName)
713713
}
714714
Resources r = mContext.mMainThread.getTopLevelResources(
715715
app.uid == Process.myUid() ? app.sourceDir
716-
: app.publicSourceDir, mContext.mPackageInfo);
716+
: app.publicSourceDir, null, mContext.mPackageInfo);
717717
if (r != null) {
718718
return r;
719719
}

core/java/android/app/ContextImpl.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import android.content.pm.PackageManager;
3838
import android.content.res.AssetManager;
3939
import android.content.res.CompatibilityInfo;
40+
import android.content.res.Configuration;
4041
import android.content.res.Resources;
4142
import android.database.DatabaseErrorHandler;
4243
import android.database.sqlite.SQLiteDatabase;
@@ -525,7 +526,7 @@ static ContextImpl getImpl(Context context) {
525526

526527
@Override
527528
public AssetManager getAssets() {
528-
return mResources.getAssets();
529+
return getResources().getAssets();
529530
}
530531

531532
@Override
@@ -1590,6 +1591,16 @@ public Context createPackageContext(String packageName, int flags)
15901591
"Application package " + packageName + " not found");
15911592
}
15921593

1594+
@Override
1595+
public Context createConfigurationContext(Configuration overrideConfiguration) {
1596+
ContextImpl c = new ContextImpl();
1597+
c.init(mPackageInfo, null, mMainThread);
1598+
c.mResources = mMainThread.getTopLevelResources(
1599+
mPackageInfo.getResDir(), overrideConfiguration,
1600+
mResources.getCompatibilityInfo());
1601+
return c;
1602+
}
1603+
15931604
@Override
15941605
public boolean isRestricted() {
15951606
return mRestricted;
@@ -1659,12 +1670,11 @@ final void init(LoadedApk packageInfo,
16591670
" compatiblity info:" + container.getDisplayMetrics());
16601671
}
16611672
mResources = mainThread.getTopLevelResources(
1662-
mPackageInfo.getResDir(), container.getCompatibilityInfo());
1673+
mPackageInfo.getResDir(), null, container.getCompatibilityInfo());
16631674
}
16641675
mMainThread = mainThread;
16651676
mContentResolver = new ApplicationContentResolver(this, mainThread);
1666-
1667-
setActivityToken(activityToken);
1677+
mActivityToken = activityToken;
16681678
}
16691679

16701680
final void init(Resources resources, ActivityThread mainThread) {
@@ -1691,10 +1701,6 @@ final Context getReceiverRestrictedContext() {
16911701
return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext());
16921702
}
16931703

1694-
final void setActivityToken(IBinder token) {
1695-
mActivityToken = token;
1696-
}
1697-
16981704
final void setOuterContext(Context context) {
16991705
mOuterContext = context;
17001706
}

core/java/android/app/LoadedApk.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ public AssetManager getAssets(ActivityThread mainThread) {
471471

472472
public Resources getResources(ActivityThread mainThread) {
473473
if (mResources == null) {
474-
mResources = mainThread.getTopLevelResources(mResDir, this);
474+
mResources = mainThread.getTopLevelResources(mResDir, null, this);
475475
}
476476
return mResources;
477477
}

core/java/android/content/Context.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.content.pm.ApplicationInfo;
2020
import android.content.pm.PackageManager;
2121
import android.content.res.AssetManager;
22+
import android.content.res.Configuration;
2223
import android.content.res.Resources;
2324
import android.content.res.TypedArray;
2425
import android.database.DatabaseErrorHandler;
@@ -2444,6 +2445,23 @@ public abstract void enforceUriPermission(
24442445
public abstract Context createPackageContext(String packageName,
24452446
int flags) throws PackageManager.NameNotFoundException;
24462447

2448+
/**
2449+
* Return a new Context object for the current Context but whose resources
2450+
* are adjusted to match the given Configuration. Each call to this method
2451+
* returns a new instance of a Contex object; Context objects are not
2452+
* shared, however common state (ClassLoader, other Resources for the
2453+
* same configuration) may be so the Context itself can be fairly lightweight.
2454+
*
2455+
* @param overrideConfiguration A {@link Configuration} specifying what
2456+
* values to modify in the base Configuration of the original Context's
2457+
* resources. If the base configuration changes (such as due to an
2458+
* orientation change), the resources of this context will also change except
2459+
* for those that have been explicitly overridden with a value here.
2460+
*
2461+
* @return A Context for the application.
2462+
*/
2463+
public abstract Context createConfigurationContext(Configuration overrideConfiguration);
2464+
24472465
/**
24482466
* Indicates whether this Context is restricted.
24492467
*

core/java/android/content/ContextWrapper.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.content.pm.ApplicationInfo;
2020
import android.content.pm.PackageManager;
2121
import android.content.res.AssetManager;
22+
import android.content.res.Configuration;
2223
import android.content.res.Resources;
2324
import android.database.DatabaseErrorHandler;
2425
import android.database.sqlite.SQLiteDatabase;
@@ -532,6 +533,11 @@ public Context createPackageContext(String packageName, int flags)
532533
return mBase.createPackageContext(packageName, flags);
533534
}
534535

536+
@Override
537+
public Context createConfigurationContext(Configuration overrideConfiguration) {
538+
return mBase.createConfigurationContext(overrideConfiguration);
539+
}
540+
535541
@Override
536542
public boolean isRestricted() {
537543
return mBase.isRestricted();

core/java/android/content/res/Configuration.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
* <pre>Configuration config = getResources().getConfiguration();</pre>
3636
*/
3737
public final class Configuration implements Parcelable, Comparable<Configuration> {
38+
/** @hide */
39+
public static final Configuration EMPTY = new Configuration();
40+
3841
/**
3942
* Current user preference for the scaling factor for fonts, relative
4043
* to the base density scaling.

core/java/android/content/res/Resources.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,9 +1435,12 @@ public void updateConfiguration(Configuration config,
14351435
int configChanges = 0xfffffff;
14361436
if (config != null) {
14371437
mTmpConfig.setTo(config);
1438+
int density = config.densityDpi;
1439+
if (density == Configuration.DENSITY_DPI_UNDEFINED) {
1440+
density = mMetrics.noncompatDensityDpi;
1441+
}
14381442
if (mCompatibilityInfo != null) {
1439-
mCompatibilityInfo.applyToConfiguration(mMetrics.noncompatDensityDpi,
1440-
mTmpConfig);
1443+
mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
14411444
}
14421445
if (mTmpConfig.locale == null) {
14431446
mTmpConfig.locale = Locale.getDefault();
@@ -1448,6 +1451,10 @@ public void updateConfiguration(Configuration config,
14481451
if (mConfiguration.locale == null) {
14491452
mConfiguration.locale = Locale.getDefault();
14501453
}
1454+
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
1455+
mMetrics.densityDpi = mConfiguration.densityDpi;
1456+
mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
1457+
}
14511458
mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
14521459

14531460
String locale = null;

core/java/android/view/ContextThemeWrapper.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import android.content.Context;
2020
import android.content.ContextWrapper;
21+
import android.content.res.Configuration;
2122
import android.content.res.Resources;
2223
import android.os.Build;
2324

@@ -30,6 +31,8 @@ public class ContextThemeWrapper extends ContextWrapper {
3031
private int mThemeResource;
3132
private Resources.Theme mTheme;
3233
private LayoutInflater mInflater;
34+
private Configuration mOverrideConfiguration;
35+
private Resources mResources;
3336

3437
public ContextThemeWrapper() {
3538
super(null);
@@ -45,6 +48,41 @@ public ContextThemeWrapper(Context base, int themeres) {
4548
super.attachBaseContext(newBase);
4649
mBase = newBase;
4750
}
51+
52+
/**
53+
* Call to set an "override configuration" on this context -- this is
54+
* a configuration that replies one or more values of the standard
55+
* configuration that is applied to the context. See
56+
* {@link Context#createConfigurationContext(Configuration)} for more
57+
* information.
58+
*
59+
* <p>This method can only be called once, and must be called before any
60+
* calls to {@link #getResources()} are made.
61+
*/
62+
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
63+
if (mResources != null) {
64+
throw new IllegalStateException("getResources() has already been called");
65+
}
66+
if (mOverrideConfiguration != null) {
67+
throw new IllegalStateException("Override configuration has already been set");
68+
}
69+
mOverrideConfiguration = new Configuration(overrideConfiguration);
70+
}
71+
72+
@Override
73+
public Resources getResources() {
74+
if (mResources != null) {
75+
return mResources;
76+
}
77+
if (mOverrideConfiguration == null) {
78+
mResources = super.getResources();
79+
return mResources;
80+
} else {
81+
Context resc = createConfigurationContext(mOverrideConfiguration);
82+
mResources = resc.getResources();
83+
return mResources;
84+
}
85+
}
4886

4987
@Override public void setTheme(int resid) {
5088
mThemeResource = resid;

0 commit comments

Comments
 (0)