Merge "Initial draft of high-level multi-display APIs." into jb-mr1-dev

This commit is contained in:
Jeff Brown
2012-08-31 15:48:25 -07:00
committed by Android (Google) Code Review
21 changed files with 600 additions and 93 deletions

View File

@ -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

View File

@ -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());
config.updateFrom(key.mOverrideConfiguration);
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) {

View File

@ -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());
}

View File

@ -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;
}

View File

@ -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);

View File

@ -147,15 +147,19 @@ public class Dialog implements DialogInterface, Window.Callback,
this(context, theme, true);
}
Dialog(Context context, int theme, boolean createContextWrapper) {
if (theme == 0) {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
outValue, true);
theme = outValue.resourceId;
Dialog(Context context, int theme, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (theme == 0) {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
outValue, true);
theme = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, theme);
} else {
mContext = context;
}
mContext = createContextWrapper ? new ContextThemeWrapper(context, theme) : context;
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
@ -164,7 +168,7 @@ public class Dialog implements DialogInterface, Window.Callback,
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
/**
* @deprecated
* @hide

View File

@ -41,6 +41,7 @@ import android.os.UserHandle;
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.view.CompatibilityInfoHolder;
import android.view.Display;
import java.io.File;
import java.io.IOException;
@ -139,7 +140,8 @@ public final class LoadedApk {
ContextImpl.createSystemContext(mainThread);
ActivityThread.mSystemContext.getResources().updateConfiguration(
mainThread.getConfiguration(),
mainThread.getDisplayMetricsLocked(compatInfo, false),
mainThread.getDisplayMetricsLocked(
Display.DEFAULT_DISPLAY, compatInfo),
compatInfo);
//Slog.i(TAG, "Created system resources "
// + mSystemContext.getResources() + ": "
@ -471,7 +473,8 @@ public final class LoadedApk {
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, null, this);
mResources = mainThread.getTopLevelResources(mResDir,
Display.DEFAULT_DISPLAY, null, this);
}
return mResources;
}

View File

@ -0,0 +1,256 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.Gravity;
import android.view.WindowManagerImpl;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
/**
* Base class for presentations.
*
* A presentation is a special kind of dialog whose purpose is to present
* content on a secondary display. A {@link Presentation} is associated with
* the target {@link Display} at creation time and configures its context and
* resource configuration according to the display's metrics.
*
* Notably, the {@link Context} of a presentation is different from the context
* of its containing {@link Activity}. It is important to inflate the layout
* of a presentation and load other resources using the presentation's own context
* to ensure that assets of the correct size and density for the target display
* are loaded.
*
* A presentation is automatically canceled (see {@link Dialog#cancel()}) when
* the display to which it is attached is removed. An activity should take
* care of pausing and resuming whatever content is playing within the presentation
* whenever the activity itself is paused or resume.
*
* @see {@link DisplayManager} for information on how to enumerate displays.
*/
public class Presentation extends Dialog {
private static final String TAG = "Presentation";
private static final int MSG_CANCEL = 1;
private final Display mDisplay;
private final DisplayManager mDisplayManager;
/**
* Creates a new presentation that is attached to the specified display
* using the default theme.
*
* @param outerContext The context of the application that is showing the presentation.
* The presentation will create its own context (see {@link #getContext()}) based
* on this context and information about the associated display.
* @param display The display to which the presentation should be attached.
*/
public Presentation(Context outerContext, Display display) {
this(outerContext, display, 0);
}
/**
* Creates a new presentation that is attached to the specified display
* using the optionally specified theme.
*
* @param outerContext The context of the application that is showing the presentation.
* The presentation will create its own context (see {@link #getContext()}) based
* on this context and information about the associated display.
* @param display The display to which the presentation should be attached.
* @param theme A style resource describing the theme to use for the window.
* See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
* Style and Theme Resources</a> for more information about defining and using
* styles. This theme is applied on top of the current theme in
* <var>outerContext</var>. If 0, the default presentation theme will be used.
*/
public Presentation(Context outerContext, Display display, int theme) {
super(createPresentationContext(outerContext, display, theme), theme, false);
mDisplay = display;
mDisplayManager = (DisplayManager)getContext().getSystemService(Context.DISPLAY_SERVICE);
getWindow().setGravity(Gravity.FILL);
setCanceledOnTouchOutside(false);
}
/**
* Gets the {@link Display} that this presentation appears on.
*
* @return The display.
*/
public Display getDisplay() {
return mDisplay;
}
/**
* Gets the {@link Resources} that should be used to inflate the layout of this presentation.
* This resources object has been configured according to the metrics of the
* display that the presentation appears on.
*
* @return The presentation resources object.
*/
public Resources getResources() {
return getContext().getResources();
}
@Override
protected void onStart() {
super.onStart();
mDisplayManager.registerDisplayListener(mDisplayListener, null);
// Since we were not watching for display changes until just now, there is a
// chance that the display metrics have changed. If so, we will need to
// dismiss the presentation immediately. This case is expected
// to be rare but surprising, so we'll write a log message about it.
if (!isConfigurationStillValid()) {
Log.i(TAG, "Presentation is being immediately dismissed because the "
+ "display metrics have changed since it was created.");
mHandler.sendEmptyMessage(MSG_CANCEL);
}
}
@Override
protected void onStop() {
mDisplayManager.unregisterDisplayListener(mDisplayListener);
super.onStop();
}
/**
* Called by the system when the {@link Display} to which the presentation
* is attached has been removed.
*
* The system automatically calls {@link #cancel} to dismiss the presentation
* after sending this event.
*
* @see #getDisplay
*/
public void onDisplayRemoved() {
}
/**
* Called by the system when the properties of the {@link Display} to which
* the presentation is attached have changed.
*
* If the display metrics have changed (for example, if the display has been
* resized or rotated), then the system automatically calls
* {@link #cancel} to dismiss the presentation.
*
* @see #getDisplay
*/
public void onDisplayChanged() {
}
private void handleDisplayRemoved() {
onDisplayRemoved();
cancel();
}
private void handleDisplayChanged() {
onDisplayChanged();
// We currently do not support configuration changes for presentations
// (although we could add that feature with a bit more work).
// If the display metrics have changed in any way then the current configuration
// is invalid and the application must recreate the presentation to get
// a new context.
if (!isConfigurationStillValid()) {
cancel();
}
}
private boolean isConfigurationStillValid() {
DisplayMetrics dm = new DisplayMetrics();
mDisplay.getMetrics(dm);
return dm.equals(getResources().getDisplayMetrics());
}
private static Context createPresentationContext(
Context outerContext, Display display, int theme) {
if (outerContext == null) {
throw new IllegalArgumentException("outerContext must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
Context displayContext = outerContext.createDisplayContext(display);
if (theme == 0) {
TypedValue outValue = new TypedValue();
displayContext.getTheme().resolveAttribute(
com.android.internal.R.attr.presentationTheme, outValue, true);
theme = outValue.resourceId;
}
// Derive the display's window manager from the outer window manager.
// We do this because the outer window manager have some extra information
// such as the parent window, which is important if the presentation uses
// an application window type.
final WindowManagerImpl outerWindowManager =
(WindowManagerImpl)outerContext.getSystemService(Context.WINDOW_SERVICE);
final WindowManagerImpl displayWindowManager =
outerWindowManager.createPresentationWindowManager(display);
return new ContextThemeWrapper(displayContext, theme) {
@Override
public Object getSystemService(String name) {
if (Context.WINDOW_SERVICE.equals(name)) {
return displayWindowManager;
}
return super.getSystemService(name);
}
};
}
private final DisplayListener mDisplayListener = new DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
}
@Override
public void onDisplayRemoved(int displayId) {
if (displayId == mDisplay.getDisplayId()) {
handleDisplayRemoved();
}
}
@Override
public void onDisplayChanged(int displayId) {
if (displayId == mDisplay.getDisplayId()) {
handleDisplayChanged();
}
}
};
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_CANCEL:
cancel();
break;
}
}
};
}

View File

@ -35,6 +35,8 @@ import android.os.Looper;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.CompatibilityInfoHolder;
import android.view.Display;
import android.view.WindowManager;
import java.io.File;
import java.io.FileInputStream;
@ -2547,7 +2549,7 @@ public abstract class Context {
/**
* 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
* returns a new instance of a Context 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.
*
@ -2557,19 +2559,40 @@ public abstract class Context {
* 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.
* @return A Context with the given configuration override.
*/
public abstract Context createConfigurationContext(Configuration overrideConfiguration);
/**
* Return a new Context object for the current Context but whose resources
* are adjusted to match the metrics of the given Display. Each call to this method
* returns a new instance of a Context 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.
*
* The returned display Context provides a {@link WindowManager}
* (see {@link #getSystemService(String)}) that is configured to show windows
* on the given display. The WindowManager's {@link WindowManager#getDefaultDisplay}
* method can be used to retrieve the Display from the returned Context.
*
* @param display A {@link Display} object specifying the display
* for whose metrics the Context's resources should be tailored and upon which
* new windows should be shown.
*
* @return A Context for the display.
*/
public abstract Context createDisplayContext(Display display);
/**
* Gets the compatibility info holder for this context. This information
* is provided on a per-application basis and is used to simulate lower density
* display metrics for legacy applications.
*
* @param displayId The display id for which to get compatibility info.
* @return The compatibility info holder, or null if not required by the application.
* @hide
*/
public abstract CompatibilityInfoHolder getCompatibilityInfo();
public abstract CompatibilityInfoHolder getCompatibilityInfo(int displayId);
/**
* Indicates whether this Context is restricted.

View File

@ -36,6 +36,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.view.CompatibilityInfoHolder;
import android.view.Display;
import java.io.File;
import java.io.FileInputStream;
@ -581,6 +582,11 @@ public class ContextWrapper extends Context {
return mBase.createConfigurationContext(overrideConfiguration);
}
@Override
public Context createDisplayContext(Display display) {
return mBase.createDisplayContext(display);
}
@Override
public boolean isRestricted() {
return mBase.isRestricted();
@ -588,7 +594,7 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
public CompatibilityInfoHolder getCompatibilityInfo() {
return mBase.getCompatibilityInfo();
public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
return mBase.getCompatibilityInfo(displayId);
}
}

View File

@ -19,7 +19,6 @@ package android.hardware.display;
import android.content.Context;
import android.os.Handler;
import android.util.SparseArray;
import android.view.CompatibilityInfoHolder;
import android.view.Display;
/**
@ -92,7 +91,7 @@ public final class DisplayManager {
Display display = mDisplays.get(displayId);
if (display == null) {
display = mGlobal.getCompatibleDisplay(displayId,
getCompatibilityInfoForDisplayLocked(displayId));
mContext.getCompatibilityInfo(displayId));
if (display != null) {
mDisplays.put(displayId, display);
}
@ -102,14 +101,6 @@ public final class DisplayManager {
return display;
}
private CompatibilityInfoHolder getCompatibilityInfoForDisplayLocked(int displayId) {
CompatibilityInfoHolder cih = null;
if (displayId == Display.DEFAULT_DISPLAY) {
cih = mContext.getCompatibilityInfo();
}
return cih;
}
/**
* Registers an display listener to receive notifications about when
* displays are added, removed or changed.

View File

@ -206,13 +206,52 @@ public class DisplayMetrics {
public void setToDefaults() {
widthPixels = 0;
heightPixels = 0;
density = noncompatDensity = DENSITY_DEVICE / (float) DENSITY_DEFAULT;
densityDpi = noncompatDensityDpi = DENSITY_DEVICE;
density = DENSITY_DEVICE / (float) DENSITY_DEFAULT;
densityDpi = DENSITY_DEVICE;
scaledDensity = density;
xdpi = noncompatXdpi = DENSITY_DEVICE;
ydpi = noncompatYdpi = DENSITY_DEVICE;
noncompatWidthPixels = 0;
noncompatHeightPixels = 0;
xdpi = DENSITY_DEVICE;
ydpi = DENSITY_DEVICE;
noncompatWidthPixels = widthPixels;
noncompatHeightPixels = heightPixels;
noncompatDensity = density;
noncompatDensityDpi = densityDpi;
noncompatScaledDensity = scaledDensity;
noncompatXdpi = xdpi;
noncompatYdpi = ydpi;
}
@Override
public boolean equals(Object o) {
return o instanceof DisplayMetrics && equals((DisplayMetrics)o);
}
/**
* Returns true if these display metrics equal the other display metrics.
*
* @param other The display metrics with which to compare.
* @return True if the display metrics are equal.
*/
public boolean equals(DisplayMetrics other) {
return other != null
&& widthPixels == other.widthPixels
&& heightPixels == other.heightPixels
&& density == other.density
&& densityDpi == other.densityDpi
&& scaledDensity == other.scaledDensity
&& xdpi == other.xdpi
&& ydpi == other.ydpi
&& noncompatWidthPixels == other.noncompatWidthPixels
&& noncompatHeightPixels == other.noncompatHeightPixels
&& noncompatDensity == other.noncompatDensity
&& noncompatDensityDpi == other.noncompatDensityDpi
&& noncompatScaledDensity == other.noncompatScaledDensity
&& noncompatXdpi == other.noncompatXdpi
&& noncompatYdpi == other.noncompatYdpi;
}
@Override
public int hashCode() {
return widthPixels * heightPixels * densityDpi;
}
@Override

View File

@ -16,6 +16,8 @@
package android.view;
import android.app.Presentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.IBinder;
@ -29,6 +31,17 @@ import android.util.Log;
* The interface that apps use to talk to the window manager.
* <p>
* Use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code> to get one of these.
* </p><p>
* Each window manager instance is bound to a particular {@link Display}.
* To obtain a {@link WindowManager} for a different display, use
* {@link Context#createDisplayContext} to obtain a {@link Context} for that
* display, then use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code>
* to get the WindowManager.
* </p><p>
* The simplest way to show a window on another display is to create a
* {@link Presentation}. The presentation will automatically obtain a
* {@link WindowManager} and {@link Context} for that display.
* </p>
*
* @see android.content.Context#getSystemService
* @see android.content.Context#WINDOW_SERVICE
@ -49,12 +62,24 @@ public interface WindowManager extends ViewManager {
}
/**
* Use this method to get the default Display object.
*
* @return default Display object
* Returns the {@link Display} upon which this {@link WindowManager} instance
* will create new windows.
* <p>
* Despite the name of this method, the display that is returned is not
* necessarily the primary display of the system (see {@link Display#DEFAULT_DISPLAY}).
* The returned display could instead be a secondary display that this
* window manager instance is managing. Think of it as the display that
* this {@link WindowManager} instance uses by default.
* </p><p>
* To create windows on a different display, you need to obtain a
* {@link WindowManager} for that {@link Display}. (See the {@link WindowManager}
* class documentation for more information.)
* </p>
*
* @return The display that this window manager is managing.
*/
public Display getDefaultDisplay();
/**
* Special variation of {@link #removeView} that immediately invokes
* the given view hierarchy's {@link View#onDetachedFromWindow()

View File

@ -16,9 +16,6 @@
package android.view;
import android.content.Context;
import android.hardware.display.DisplayManager;
/**
* Provides low-level communication with the system window manager for
* operations that are bound to a particular context, display or parent window.
@ -47,25 +44,24 @@ import android.hardware.display.DisplayManager;
*/
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Display mDisplay;
private final Window mParentWindow;
public WindowManagerImpl(Context context, int displayId) {
DisplayManager dm = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mContext = context;
mDisplay = dm.getDisplay(displayId);
mParentWindow = null;
public WindowManagerImpl(Display display) {
this(display, null);
}
private WindowManagerImpl(Context context, Display display, Window parentWindow) {
mContext = context;
private WindowManagerImpl(Display display, Window parentWindow) {
mDisplay = display;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, mDisplay, parentWindow);
return new WindowManagerImpl(mDisplay, parentWindow);
}
public WindowManagerImpl createPresentationWindowManager(Display display) {
return new WindowManagerImpl(display, mParentWindow);
}
@Override

View File

@ -768,11 +768,15 @@
<attr name="dialogCustomTitleDecorLayout" format="reference" />
<!-- Window decor layout to use in dialog mode with title only -->
<attr name="dialogTitleDecorLayout" format="reference" />
<!-- Theme to use for alert dialogs spawned from this theme. -->
<attr name="alertDialogTheme" format="reference" />
<!-- Icon drawable to use for alerts -->
<attr name="alertDialogIcon" format="reference" />
<!-- Theme to use for presentations spawned from this theme. -->
<attr name="presentationTheme" format="reference" />
<!-- Drawable to use for generic vertical dividers. -->
<attr name="dividerVertical" format="reference" />

View File

@ -3759,5 +3759,6 @@
<public type="attr" name="listPreferredItemPaddingStart" />
<public type="attr" name="listPreferredItemPaddingEnd" />
<public type="attr" name="singleUser" />
<public type="attr" name="presentationTheme" />
</resources>

View File

@ -673,7 +673,6 @@ easier.
</style>
<!-- Animation Styles -->
<style name="Animation.DeviceDefault.Activity" parent="Animation.Holo.Activity">

View File

@ -186,17 +186,24 @@ please see themes_device_defaults.xml.
<item name="windowFixedHeightMinor">0dp</item>
<!-- Dialog attributes -->
<item name="alertDialogStyle">@android:style/AlertDialog</item>
<item name="dialogTheme">@android:style/Theme.Dialog</item>
<item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons</item>
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title</item>
<!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.Dialog.Alert</item>
<item name="alertDialogStyle">@android:style/AlertDialog</item>
<item name="alertDialogCenterButtons">true</item>
<item name="alertDialogIcon">@android:drawable/ic_dialog_alert</item>
<!-- Presentation attributes (introduced after API level 10 so does not
have a special old-style theme. -->
<item name="presentationTheme">@android:style/Theme.DeviceDefault.Dialog.Presentation</item>
<!-- Toast attributes -->
<item name="toastFrameBackground">@android:drawable/toast_frame</item>
<!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_background</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
@ -1012,17 +1019,23 @@ please see themes_device_defaults.xml.
<item name="windowActionModeOverlay">false</item>
<!-- Dialog attributes -->
<item name="alertDialogStyle">@android:style/AlertDialog.Holo</item>
<item name="dialogTheme">@android:style/Theme.Holo.Dialog</item>
<item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_holo</item>
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_holo</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title_holo</item>
<!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.Holo.Dialog.Alert</item>
<item name="alertDialogStyle">@android:style/AlertDialog.Holo</item>
<item name="alertDialogCenterButtons">false</item>
<item name="alertDialogIcon">@android:drawable/ic_dialog_alert_holo_dark</item>
<!-- Presentation attributes -->
<item name="presentationTheme">@android:style/Theme.Holo.Dialog.Presentation</item>
<!-- Toast attributes -->
<item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
<!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_dark</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
@ -1319,17 +1332,23 @@ please see themes_device_defaults.xml.
<item name="windowActionModeOverlay">false</item>
<!-- Dialog attributes -->
<item name="alertDialogStyle">@android:style/AlertDialog.Holo.Light</item>
<item name="dialogTheme">@android:style/Theme.Holo.Light.Dialog</item>
<item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_holo</item>
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_holo</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title_holo</item>
<item name="alertDialogCenterButtons">false</item>
<!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.Holo.Light.Dialog.Alert</item>
<item name="alertDialogStyle">@android:style/AlertDialog.Holo.Light</item>
<item name="alertDialogCenterButtons">false</item>
<item name="alertDialogIcon">@android:drawable/ic_dialog_alert_holo_light</item>
<!-- Presentation attributes -->
<item name="presentationTheme">@android:style/Theme.Holo.Light.Dialog.Presentation</item>
<!-- Toast attributes -->
<item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
<!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_light</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
@ -1663,6 +1682,10 @@ please see themes_device_defaults.xml.
<style name="Theme.Holo.DialogWhenLarge.NoActionBar" parent="@android:style/Theme.Holo.NoActionBar">
</style>
<!-- Theme for a presentation window on a secondary display. -->
<style name="Theme.Holo.Dialog.Presentation" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
</style>
<!-- Light holo dialog themes -->
<!-- Holo light theme for dialog windows and activities, which is used by the
@ -1760,6 +1783,10 @@ please see themes_device_defaults.xml.
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
</style>
<!-- Theme for a presentation window on a secondary display. -->
<style name="Theme.Holo.Light.Dialog.Presentation" parent="@android:style/Theme.Holo.Light.NoActionBar.Fullscreen" >
</style>
<!-- Default holographic (dark) for windows that want to have the user's selected
wallpaper appear behind them. -->
<style name="Theme.Holo.Wallpaper">

View File

@ -87,9 +87,14 @@ easier.
<item name="android:windowAnimationStyle">@android:style/Animation.DeviceDefault.Activity</item>
<!-- Dialog attributes -->
<item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault</item>
<item name="dialogTheme">@android:style/Theme.DeviceDefault.Dialog</item>
<!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.DeviceDefault.Dialog.Alert</item>
<item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault</item>
<!-- Presentation attributes -->
<item name="presentationTheme">@android:style/Theme.DeviceDefault.Dialog.Presentation</item>
<!-- Text selection handle attributes -->
<item name="textSelectHandleWindowStyle">@android:style/Widget.DeviceDefault.TextSelectHandle</item>
@ -239,9 +244,14 @@ easier.
<item name="android:windowAnimationStyle">@android:style/Animation.DeviceDefault.Activity</item>
<!-- Dialog attributes -->
<item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault.Light</item>
<item name="dialogTheme">@android:style/Theme.DeviceDefault.Light.Dialog</item>
<!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.DeviceDefault.Light.Dialog.Alert</item>
<item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault.Light</item>
<!-- Presentation attributes -->
<item name="presentationTheme">@android:style/Theme.DeviceDefault.Light.Dialog.Presentation</item>
<!-- Text selection handle attributes -->
<item name="textSelectHandleWindowStyle">@android:style/Widget.DeviceDefault.TextSelectHandle</item>
@ -460,6 +470,15 @@ easier.
<style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Holo.Light.DialogWhenLarge.NoActionBar" >
</style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
<style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Holo.Dialog.Presentation">
</style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
<style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Holo.Light.Dialog.Presentation">
</style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->

View File

@ -40,6 +40,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.view.CompatibilityInfoHolder;
import android.view.Display;
import java.io.File;
import java.io.FileInputStream;
@ -520,6 +521,11 @@ public class MockContext extends Context {
throw new UnsupportedOperationException();
}
@Override
public Context createDisplayContext(Display display) {
throw new UnsupportedOperationException();
}
@Override
public boolean isRestricted() {
throw new UnsupportedOperationException();
@ -527,7 +533,7 @@ public class MockContext extends Context {
/** @hide */
@Override
public CompatibilityInfoHolder getCompatibilityInfo() {
public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
throw new UnsupportedOperationException();
}
}

View File

@ -67,6 +67,7 @@ import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.BridgeInflater;
import android.view.CompatibilityInfoHolder;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
@ -924,6 +925,12 @@ public final class BridgeContext extends Context {
return null;
}
@Override
public Context createDisplayContext(Display display) {
// pass
return null;
}
@Override
public String[] databaseList() {
// pass
@ -1357,7 +1364,7 @@ public final class BridgeContext extends Context {
}
@Override
public CompatibilityInfoHolder getCompatibilityInfo() {
public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
// pass
return null;
}