Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 756220bd authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Add API to create new contexts with custom configurations.

This allows you to, say, make a Context whose configuration
is set to a different density than the actual density of the device.

The main API is Context.createConfigurationContext().  There is
also a new API on ContextThemeWrapper that allows you to apply
an override context before its resources are retrieved, which
addresses some feature requests from developers to be able to
customize the context their app is running in.

Change-Id: I88364986660088521e24b567e2fda22fb7042819
parent 863b19bc
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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);
@@ -21147,6 +21149,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);
@@ -23351,6 +23354,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);
  }
+64 −17
Original line number Diff line number Diff line
@@ -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,7 +1623,14 @@ 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="
@@ -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() {
@@ -3676,17 +3713,27 @@ 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);
                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 {
+1 −1
Original line number Diff line number Diff line
@@ -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;
        }
+14 −8
Original line number Diff line number Diff line
@@ -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;
    }
+1 −1
Original line number Diff line number Diff line
@@ -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;
    }
Loading