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

Commit a492c3a7 authored by Jeff Brown's avatar Jeff Brown
Browse files

Initial draft of high-level multi-display APIs.

This patch introduces the ability to create a Context that
is bound to a Display.  The context gets its configuration and
metrics from that display and is able to provide a WindowManager
that is bound to the display.

To make it easier to use, we also add a new kind of Dialog
called a Presentation.  Presentation takes care of setting
up the context as needed and watches for significant changes
in the display configuration.  If the display is removed,
then the presentation simply dismisses itself.

Change-Id: Idc54b4ec84b1ff91505cfb78910cf8cd09696d7d
parent 00453e7a
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -773,6 +773,7 @@ package android {
    field public static final int preferenceLayoutChild = 16842900; // 0x1010094
    field public static final int preferenceScreenStyle = 16842891; // 0x101008b
    field public static final int preferenceStyle = 16842894; // 0x101008e
    field public static final int presentationTheme = 16843712; // 0x10103c0
    field public static final int previewImage = 16843482; // 0x10102da
    field public static final int priority = 16842780; // 0x101001c
    field public static final int privateImeOptions = 16843299; // 0x1010223
@@ -3901,6 +3902,15 @@ package android.app {
    method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
  }
  public class Presentation extends android.app.Dialog {
    ctor public Presentation(android.content.Context, android.view.Display);
    ctor public Presentation(android.content.Context, android.view.Display, int);
    method public android.view.Display getDisplay();
    method public android.content.res.Resources getResources();
    method public void onDisplayChanged();
    method public void onDisplayRemoved();
  }
  public class ProgressDialog extends android.app.AlertDialog {
    ctor public ProgressDialog(android.content.Context);
    ctor public ProgressDialog(android.content.Context, int);
@@ -5268,6 +5278,7 @@ package android.content {
    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 createDisplayContext(android.view.Display);
    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);
@@ -5417,6 +5428,7 @@ package android.content {
    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 createDisplayContext(android.view.Display);
    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);
@@ -21228,6 +21240,7 @@ package android.test.mock {
    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 createDisplayContext(android.view.Display);
    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);
@@ -22911,6 +22924,7 @@ package android.util {
  public class DisplayMetrics {
    ctor public DisplayMetrics();
    method public boolean equals(android.util.DisplayMetrics);
    method public void setTo(android.util.DisplayMetrics);
    method public void setToDefaults();
    field public static final int DENSITY_DEFAULT = 160; // 0xa0
+71 −24
Original line number Diff line number Diff line
@@ -203,7 +203,7 @@ public final class ActivityThread {
            = new HashMap<String, WeakReference<LoadedApk>>();
    final HashMap<String, WeakReference<LoadedApk>> mResourcePackages
            = new HashMap<String, WeakReference<LoadedApk>>();
    final HashMap<CompatibilityInfo, DisplayMetrics> mDisplayMetrics
    final HashMap<CompatibilityInfo, DisplayMetrics> mDefaultDisplayMetrics
            = new HashMap<CompatibilityInfo, DisplayMetrics>();
    final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources
            = new HashMap<ResourcesKey, WeakReference<Resources> >();
@@ -1475,12 +1475,14 @@ public final class ActivityThread {

    private static class ResourcesKey {
        final private String mResDir;
        final private int mDisplayId;
        final private Configuration mOverrideConfiguration;
        final private float mScale;
        final private int mHash;

        ResourcesKey(String resDir, Configuration overrideConfiguration, float scale) {
        ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration, float scale) {
            mResDir = resDir;
            mDisplayId = displayId;
            if (overrideConfiguration != null) {
                if (Configuration.EMPTY.equals(overrideConfiguration)) {
                    overrideConfiguration = null;
@@ -1490,6 +1492,7 @@ public final class ActivityThread {
            mScale = scale;
            int hash = 17;
            hash = 31 * hash + mResDir.hashCode();
            hash = 31 * hash + mDisplayId;
            hash = 31 * hash + (mOverrideConfiguration != null
                    ? mOverrideConfiguration.hashCode() : 0);
            hash = 31 * hash + Float.floatToIntBits(mScale);
@@ -1510,6 +1513,9 @@ public final class ActivityThread {
            if (!mResDir.equals(peer.mResDir)) {
                return false;
            }
            if (mDisplayId != peer.mDisplayId) {
                return false;
            }
            if (mOverrideConfiguration != peer.mOverrideConfiguration) {
                if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
                    return false;
@@ -1552,28 +1558,32 @@ public final class ActivityThread {
        return sPackageManager;
    }

    DisplayMetrics getDisplayMetricsLocked(CompatibilityInfo ci, boolean forceUpdate) {
        DisplayMetrics dm = mDisplayMetrics.get(ci);
        if (dm != null && !forceUpdate) {
    private void flushDisplayMetricsLocked() {
        mDefaultDisplayMetrics.clear();
    }

    DisplayMetrics getDisplayMetricsLocked(int displayId, CompatibilityInfo ci) {
        boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
        DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(ci) : null;
        if (dm != null) {
            return dm;
        }
        dm = new DisplayMetrics();

        DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
        if (displayManager == null) {
            // may be null early in system startup
            dm = new DisplayMetrics();
            dm.setToDefaults();
            return dm;
        }

        if (dm == null) {
            dm = new DisplayMetrics();
            mDisplayMetrics.put(ci, dm);
        if (isDefaultDisplay) {
            mDefaultDisplayMetrics.put(ci, dm);
        }

        CompatibilityInfoHolder cih = new CompatibilityInfoHolder();
        cih.set(ci);
        Display d = displayManager.getCompatibleDisplay(Display.DEFAULT_DISPLAY, cih);
        Display d = displayManager.getCompatibleDisplay(displayId, cih);
        d.getMetrics(dm);
        //Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h="
        //        + metrics.heightPixels + " den=" + metrics.density
@@ -1602,9 +1612,11 @@ public final class ActivityThread {
     * @param compInfo the compability info. It will use the default compatibility info when it's
     * null.
     */
    Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
    Resources getTopLevelResources(String resDir,
            int displayId, Configuration overrideConfiguration,
            CompatibilityInfo compInfo) {
        ResourcesKey key = new ResourcesKey(resDir, overrideConfiguration,
        ResourcesKey key = new ResourcesKey(resDir,
                displayId, overrideConfiguration,
                compInfo.applicationScale);
        Resources r;
        synchronized (mPackages) {
@@ -1636,15 +1648,21 @@ public final class ActivityThread {
        }

        //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
        DisplayMetrics metrics = getDisplayMetricsLocked(null, false);
        DisplayMetrics dm = getDisplayMetricsLocked(displayId, null);
        Configuration config;
        if (key.mOverrideConfiguration != null) {
        boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
        if (!isDefaultDisplay || key.mOverrideConfiguration != null) {
            config = new Configuration(getConfiguration());
            if (!isDefaultDisplay) {
                applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
            }
            if (key.mOverrideConfiguration != null) {
                config.updateFrom(key.mOverrideConfiguration);
            }
        } else {
            config = getConfiguration();
        }
        r = new Resources(assets, metrics, config, compInfo);
        r = new Resources(assets, dm, config, compInfo);
        if (false) {
            Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
                    + r.getConfiguration() + " appScale="
@@ -1670,9 +1688,10 @@ public final class ActivityThread {
    /**
     * Creates the top level resources for the given package.
     */
    Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
    Resources getTopLevelResources(String resDir,
            int displayId, Configuration overrideConfiguration,
            LoadedApk pkgInfo) {
        return getTopLevelResources(resDir, overrideConfiguration,
        return getTopLevelResources(resDir, displayId, overrideConfiguration,
                pkgInfo.mCompatibilityInfo.get());
    }

@@ -1844,7 +1863,8 @@ public final class ActivityThread {
                context.init(info, null, this);
                context.getResources().updateConfiguration(
                        getConfiguration(), getDisplayMetricsLocked(
                                CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, false));
                                Display.DEFAULT_DISPLAY,
                                CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO));
                mSystemContext = context;
                //Slog.i(TAG, "Created system resources " + context.getResources()
                //        + ": " + context.getResources().getConfiguration());
@@ -3707,7 +3727,9 @@ public final class ActivityThread {
            return false;
        }
        int changes = mResConfiguration.updateFrom(config);
        DisplayMetrics dm = getDisplayMetricsLocked(null, true);
        flushDisplayMetricsLocked();
        DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(
                Display.DEFAULT_DISPLAY, null);

        if (compat != null && (mResCompatibilityInfo == null ||
                !mResCompatibilityInfo.equals(compat))) {
@@ -3722,7 +3744,7 @@ public final class ActivityThread {
            Locale.setDefault(config.locale);
        }

        Resources.updateSystemConfiguration(config, dm, compat);
        Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);

        ApplicationPackageManager.configurationChanged();
        //Slog.i(TAG, "Configuration changed in " + currentPackageName());
@@ -3737,13 +3759,22 @@ public final class ActivityThread {
            if (r != null) {
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
                        + r + " config to: " + config);
                Configuration override = entry.getKey().mOverrideConfiguration;
                if (override != null) {
                int displayId = entry.getKey().mDisplayId;
                boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
                DisplayMetrics dm = defaultDisplayMetrics;
                Configuration overrideConfig = entry.getKey().mOverrideConfiguration;
                if (!isDefaultDisplay || overrideConfig != null) {
                    if (tmpConfig == null) {
                        tmpConfig = new Configuration();
                    }
                    tmpConfig.setTo(config);
                    tmpConfig.updateFrom(override);
                    if (!isDefaultDisplay) {
                        dm = getDisplayMetricsLocked(displayId, null);
                        applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
                    }
                    if (overrideConfig != null) {
                        tmpConfig.updateFrom(overrideConfig);
                    }
                    r.updateConfiguration(tmpConfig, dm, compat);
                } else {
                    r.updateConfiguration(config, dm, compat);
@@ -3759,6 +3790,22 @@ public final class ActivityThread {
        return changes != 0;
    }

    final void applyNonDefaultDisplayMetricsToConfigurationLocked(
            DisplayMetrics dm, Configuration config) {
        config.screenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE
                | Configuration.SCREENLAYOUT_LONG_NO;
        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
        config.orientation = (dm.widthPixels >= dm.heightPixels) ?
                Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
        config.densityDpi = dm.densityDpi;
        config.screenWidthDp = (int)(dm.widthPixels / dm.density);
        config.screenHeightDp = (int)(dm.heightPixels / dm.density);
        config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
        config.compatScreenWidthDp = config.screenWidthDp;
        config.compatScreenHeightDp = config.screenHeightDp;
        config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
    }

    final Configuration applyCompatConfiguration(int displayDensity) {
        Configuration config = mConfiguration;
        if (mCompatConfiguration == null) {
+3 −2
Original line number Diff line number Diff line
@@ -110,8 +110,9 @@ public class AlertDialog extends Dialog implements DialogInterface {
        this(context, theme, true);
    }

    AlertDialog(Context context, int theme, boolean createContextWrapper) {
        super(context, resolveDialogTheme(context, theme), createContextWrapper);
    AlertDialog(Context context, int theme, boolean createThemeContextWrapper) {
        super(context, resolveDialogTheme(context, theme), createThemeContextWrapper);

        mWindow.alwaysReadCloseOnTouchAttr();
        mAlert = new AlertController(getContext(), this, getWindow());
    }
+3 −2
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.Display;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -722,8 +723,8 @@ final class ApplicationPackageManager extends PackageManager {
            return mContext.mMainThread.getSystemContext().getResources();
        }
        Resources r = mContext.mMainThread.getTopLevelResources(
            app.uid == Process.myUid() ? app.sourceDir
            : app.publicSourceDir, null, mContext.mPackageInfo);
                app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
                        Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
        if (r != null) {
            return r;
        }
+44 −6
Original line number Diff line number Diff line
@@ -168,6 +168,7 @@ class ContextImpl extends Context {
    private int mThemeResource = 0;
    private Resources.Theme mTheme = null;
    private PackageManager mPackageManager;
    private Display mDisplay; // may be null if default display
    private Context mReceiverRestrictedContext = null;
    private boolean mRestricted;

@@ -502,8 +503,13 @@ class ContextImpl extends Context {

        registerService(WINDOW_SERVICE, new ServiceFetcher() {
                public Object getService(ContextImpl ctx) {
                    return new WindowManagerImpl(ctx.getOuterContext(),
                            Display.DEFAULT_DISPLAY);
                    Display display = ctx.mDisplay;
                    if (display == null) {
                        DisplayManager dm = (DisplayManager)ctx.getOuterContext().getSystemService(
                                Context.DISPLAY_SERVICE);
                        display = dm.getDisplay(Display.DEFAULT_DISPLAY);
                    }
                    return new WindowManagerImpl(display);
                }});

        registerService(USER_SERVICE, new ServiceFetcher() {
@@ -1676,22 +1682,52 @@ class ContextImpl extends Context {

    @Override
    public Context createConfigurationContext(Configuration overrideConfiguration) {
        if (overrideConfiguration == null) {
            throw new IllegalArgumentException("overrideConfiguration must not be null");
        }

        ContextImpl c = new ContextImpl();
        c.init(mPackageInfo, null, mMainThread);
        c.mResources = mMainThread.getTopLevelResources(
                mPackageInfo.getResDir(), overrideConfiguration,
                mPackageInfo.getResDir(),
                getDisplayId(), overrideConfiguration,
                mResources.getCompatibilityInfo());
        return c;
    }

    @Override
    public Context createDisplayContext(Display display) {
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }

        int displayId = display.getDisplayId();
        CompatibilityInfo ci = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
        CompatibilityInfoHolder cih = getCompatibilityInfo(displayId);
        if (cih != null) {
            ci = cih.get();
        }

        ContextImpl context = new ContextImpl();
        context.init(mPackageInfo, null, mMainThread);
        context.mDisplay = display;
        context.mResources = mMainThread.getTopLevelResources(
                mPackageInfo.getResDir(), displayId, null, ci);
        return context;
    }

    private int getDisplayId() {
        return mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
    }

    @Override
    public boolean isRestricted() {
        return mRestricted;
    }

    @Override
    public CompatibilityInfoHolder getCompatibilityInfo() {
        return mPackageInfo.mCompatibilityInfo;
    public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
        return displayId == Display.DEFAULT_DISPLAY ? mPackageInfo.mCompatibilityInfo : null;
    }

    private File getDataDirFile() {
@@ -1735,6 +1771,7 @@ class ContextImpl extends Context {
        mResources = context.mResources;
        mMainThread = context.mMainThread;
        mContentResolver = context.mContentResolver;
        mDisplay = context.mDisplay;
        mOuterContext = this;
    }

@@ -1758,7 +1795,8 @@ class ContextImpl extends Context {
                        " compatiblity info:" + container.getDisplayMetrics());
            }
            mResources = mainThread.getTopLevelResources(
                    mPackageInfo.getResDir(), null, container.getCompatibilityInfo());
                    mPackageInfo.getResDir(), Display.DEFAULT_DISPLAY,
                    null, container.getCompatibilityInfo());
        }
        mMainThread = mainThread;
        mContentResolver = new ApplicationContentResolver(this, mainThread);
Loading