diff options
author | Dianne Hackborn <hackbod@google.com> | 2011-04-27 18:52:56 -0400 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2011-05-09 17:03:24 -0700 |
commit | e2515eebf42c763c0a2d9f873a153711778cfc17 (patch) | |
tree | ee159940f05c439d0524771bfbfe6b551c82c2d4 /services/java/com/android | |
parent | 158e3582c03ab9e0221718da20fc8854bb9216d0 (diff) | |
download | frameworks_base-e2515eebf42c763c0a2d9f873a153711778cfc17.zip frameworks_base-e2515eebf42c763c0a2d9f873a153711778cfc17.tar.gz frameworks_base-e2515eebf42c763c0a2d9f873a153711778cfc17.tar.bz2 |
Better compat mode part one: start scaling windows.
First step of improving app screen size compatibility mode. When
running in compat mode, an application's windows are scaled up on
the screen rather than being small with 1:1 pixels.
Currently we scale the application to fill the entire screen, so
don't use an even pixel scaling. Though this may have some
negative impact on the appearance (it looks okay to me), it has a
big benefit of allowing us to now treat these apps as normal
full-screens apps and do the normal transition animations as you
move in and out and around in them.
This introduces fun stuff in the input system to take care of
modifying pointer coordinates to account for the app window
surface scaling. The input dispatcher is told about the scale
that is being applied to each window and, when there is one,
adjusts pointer events appropriately as they are being sent
to the transport.
Also modified is CompatibilityInfo, which has been greatly
simplified to not be so insane and incomprehendible. It is
now simple -- when constructed it determines if the given app
is compatible with the current screen size and density, and
that is that.
There are new APIs on ActivityManagerService to put applications
that we would traditionally consider compatible with larger screens
in compatibility mode. This is the start of a facility to have
a UI affordance for a user to switch apps in and out of
compatibility.
To test switching of modes, there is a new variation of the "am"
command to do this: am screen-compat [on|off] [package]
This mode switching has the fundamentals of restarting activities
when it is changed, though the state still needs to be persisted
and the overall mode switch cleaned up.
For the few small apps I have tested, things mostly seem to be
working well. I know of one problem with the text selection
handles being drawn at the wrong position because at some point
the window offset is being scaled incorrectly. There are
probably other similar issues around the interaction between
two windows because the different window coordinate spaces are
done in a hacky way instead of being formally integrated into
the window manager layout process.
Change-Id: Ie038e3746b448135117bd860859d74e360938557
Diffstat (limited to 'services/java/com/android')
8 files changed, 268 insertions, 60 deletions
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 54cc885..811221e 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -74,6 +74,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Proxy; @@ -146,7 +147,7 @@ public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { static final String TAG = "ActivityManager"; static final boolean DEBUG = false; - static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; + static final boolean localLOGV = DEBUG; static final boolean DEBUG_SWITCH = localLOGV || false; static final boolean DEBUG_TASKS = localLOGV || false; static final boolean DEBUG_PAUSE = localLOGV || false; @@ -546,6 +547,12 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord mHomeProcess; /** + * Packages that the user has asked to have run in screen size + * compatibility mode instead of filling the screen. + */ + final HashSet<String> mScreenCompatPackages = new HashSet<String>(); + + /** * Set of PendingResultRecord objects that are currently active. */ final HashSet mPendingResultRecords = new HashSet(); @@ -2091,6 +2098,74 @@ public final class ActivityManagerService extends ActivityManagerNative } } + CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { + return new CompatibilityInfo(ai, mConfiguration.screenLayout, + mScreenCompatPackages.contains(ai.packageName)); + } + + public void setPackageScreenCompatMode(String packageName, boolean compatEnabled) { + synchronized (this) { + ApplicationInfo ai = null; + try { + ai = AppGlobals.getPackageManager(). + getApplicationInfo(packageName, STOCK_PM_FLAGS); + } catch (RemoteException e) { + } + if (ai == null) { + Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); + return; + } + boolean changed = false; + if (compatEnabled) { + if (!mScreenCompatPackages.contains(packageName)) { + changed = true; + mScreenCompatPackages.add(packageName); + } + } else { + if (mScreenCompatPackages.contains(packageName)) { + changed = true; + mScreenCompatPackages.remove(packageName); + } + } + if (changed) { + CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); + + // Tell all processes that loaded this package about the change. + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); + if (!app.pkgList.contains(packageName)) { + continue; + } + try { + if (app.thread != null) { + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " + + app.processName + " new compat " + ci); + app.thread.updatePackageCompatibilityInfo(packageName, ci); + } + } catch (Exception e) { + } + } + + // All activities that came from the packge must be + // restarted as if there was a config change. + for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { + ActivityRecord a = (ActivityRecord)mMainStack.mHistory.get(i); + if (a.info.packageName.equals(packageName)) { + a.forceNewConfig = true; + } + } + + ActivityRecord starting = mMainStack.topRunningActivityLocked(null); + if (starting != null) { + mMainStack.ensureActivityConfigurationLocked(starting, 0); + // And we need to make sure at this point that all other activities + // are made visible with the correct configuration. + mMainStack.ensureActivitiesVisibleLocked(starting, 0); + } + } + } + } + void reportResumedActivityLocked(ActivityRecord r) { //Slog.i(TAG, "**** REPORT RESUME: " + r); @@ -3589,12 +3664,14 @@ public final class ActivityManagerService extends ActivityManagerNative } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Binding proc " + processName + " with config " + mConfiguration); - thread.bindApplication(processName, app.instrumentationInfo != null - ? app.instrumentationInfo : app.info, providers, + ApplicationInfo appInfo = app.instrumentationInfo != null + ? app.instrumentationInfo : app.info; + thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, app.instrumentationProfileFile, app.instrumentationArguments, app.instrumentationWatcher, testMode, isRestrictedBackupMode || !normalMode, - mConfiguration, getCommonServicesLocked(), + mConfiguration, compatibilityInfoForPackageLocked(appInfo), + getCommonServicesLocked(), mCoreSettingsObserver.getCoreSettingsLocked()); updateLruProcessLocked(app, false, true); app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis(); @@ -3685,7 +3762,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app); ensurePackageDexOpt(mBackupTarget.appInfo.packageName); try { - thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode); + thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, + compatibilityInfoForPackageLocked(mBackupTarget.appInfo), + mBackupTarget.backupMode); } catch (Exception e) { Slog.w(TAG, "Exception scheduling backup agent creation: "); e.printStackTrace(); @@ -7776,6 +7855,10 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(" mConfiguration: " + mConfiguration); pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange); + if (mScreenCompatPackages.size() > 0) { + pw.print(" mScreenCompatPackages="); + pw.println(mScreenCompatPackages); + } pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown); if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { @@ -9238,7 +9321,8 @@ public final class ActivityManagerService extends ActivityManagerNative r.stats.startLaunchedLocked(); } ensurePackageDexOpt(r.serviceInfo.packageName); - app.thread.scheduleCreateService(r, r.serviceInfo); + app.thread.scheduleCreateService(r, r.serviceInfo, + compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo)); r.postNotification(); created = true; } finally { @@ -10342,7 +10426,8 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc.thread != null) { if (DEBUG_BACKUP) Slog.v(TAG, "Agent proc already running: " + proc); try { - proc.thread.scheduleCreateBackupAgent(app, backupMode); + proc.thread.scheduleCreateBackupAgent(app, + compatibilityInfoForPackageLocked(app), backupMode); } catch (RemoteException e) { // Will time out on the backup manager side } @@ -10414,7 +10499,8 @@ public final class ActivityManagerService extends ActivityManagerNative // If the app crashed during backup, 'thread' will be null here if (proc.thread != null) { try { - proc.thread.scheduleDestroyBackupAgent(appInfo); + proc.thread.scheduleDestroyBackupAgent(appInfo, + compatibilityInfoForPackageLocked(appInfo)); } catch (Exception e) { Slog.e(TAG, "Exception when unbinding backup agent:"); e.printStackTrace(); @@ -11261,6 +11347,7 @@ public final class ActivityManagerService extends ActivityManagerNative + ": " + r); ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, + compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo), r.resultCode, r.resultData, r.resultExtras, r.ordered); if (DEBUG_BROADCAST) Slog.v(TAG, "Process cur broadcast " + r + " DELIVERED for app " + app); diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 0fb30ff..cb0a0f0 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -111,6 +111,7 @@ class ActivityRecord extends IApplicationToken.Stub { boolean hasBeenLaunched;// has this activity ever been launched? boolean frozenBeforeDestroy;// has been frozen but not yet destroyed. boolean immersive; // immersive mode (don't interrupt if possible) + boolean forceNewConfig; // force re-create with new config next time String stringName; // for caching of toString(). @@ -174,7 +175,8 @@ class ActivityRecord extends IApplicationToken.Stub { pw.print(" immersive="); pw.print(immersive); pw.print(" launchMode="); pw.println(launchMode); pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy); - pw.print(" thumbnailNeeded="); pw.println(thumbnailNeeded); + pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded); + pw.print(" forceNewConfig="); pw.println(forceNewConfig); if (launchTime != 0 || startTime != 0) { pw.print(prefix); pw.print("launchTime="); TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime="); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index c087aec..f385042 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -537,9 +537,11 @@ public class ActivityStack { } mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName()); r.sleeping = false; + r.forceNewConfig = false; app.thread.scheduleLaunchActivity(new Intent(r.intent), r, System.identityHashCode(r), - r.info, r.icicle, results, newIntents, !andResume, + r.info, mService.compatibilityInfoForPackageLocked(r.info.applicationInfo), + r.icicle, results, newIntents, !andResume, mService.isNextTransitionForward()); if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) { @@ -3750,7 +3752,7 @@ public class ActivityStack { // Short circuit: if the two configurations are the exact same // object (the common case), then there is nothing to do. Configuration newConfig = mService.mConfiguration; - if (r.configuration == newConfig) { + if (r.configuration == newConfig && !r.forceNewConfig) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration unchanged in " + r); return true; @@ -3775,6 +3777,7 @@ public class ActivityStack { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration doesn't matter not running " + r); r.stopFreezingScreenLocked(false); + r.forceNewConfig = false; return true; } @@ -3786,10 +3789,11 @@ public class ActivityStack { + Integer.toHexString(r.info.configChanges) + ", newConfig=" + newConfig); } - if ((changes&(~r.info.configChanges)) != 0) { + if ((changes&(~r.info.configChanges)) != 0 || r.forceNewConfig) { // Aha, the activity isn't handling the change, so DIE DIE DIE. r.configChangeFlags |= changes; r.startFreezingScreenLocked(r.app, globalChanges); + r.forceNewConfig = false; if (r.app == null || r.app.thread == null) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Switch is destroying non-running " + r); @@ -3860,6 +3864,7 @@ public class ActivityStack { try { if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r); + r.forceNewConfig = false; r.app.thread.scheduleRelaunchActivity(r, results, newIntents, changes, !andResume, mService.mConfiguration); // Note: don't need to call pauseIfSleepingLocked() here, because diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 353ff6d..a63ffae 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -24,6 +24,7 @@ import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.content.ComponentName; import android.content.pm.ApplicationInfo; +import android.content.res.CompatibilityInfo; import android.os.Bundle; import android.os.IBinder; import android.os.SystemClock; diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java index 45a78af..57f0799 100644 --- a/services/java/com/android/server/wm/InputMonitor.java +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -205,12 +205,21 @@ final class InputMonitor { inputWindow.ownerPid = child.mSession.mPid; inputWindow.ownerUid = child.mSession.mUid; - final Rect frame = child.mFrame; + final Rect frame = child.mScaledFrame; inputWindow.frameLeft = frame.left; inputWindow.frameTop = frame.top; inputWindow.frameRight = frame.right; inputWindow.frameBottom = frame.bottom; + if (child.mGlobalScale != 1) { + // If we are scaling the window, input coordinates need + // to be inversely scaled to map from what is on screen + // to what is actually being touched in the UI. + inputWindow.scaleFactor = 1.0f/child.mGlobalScale; + } else { + inputWindow.scaleFactor = 1; + } + child.getTouchableRegion(inputWindow.touchableRegion); } diff --git a/services/java/com/android/server/wm/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java index e3eb473..578120e 100644 --- a/services/java/com/android/server/wm/InputWindow.java +++ b/services/java/com/android/server/wm/InputWindow.java @@ -46,6 +46,10 @@ public final class InputWindow { public int frameRight; public int frameBottom; + // Global scaling factor applied to touch events when they are dispatched + // to the window + public float scaleFactor; + // Window touchable region. public final Region touchableRegion = new Region(); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 33e6a36..769e423 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND; -import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -586,6 +585,7 @@ public class WindowManagerService extends IWindowManager.Stub // The frame use to limit the size of the app running in compatibility mode. Rect mCompatibleScreenFrame = new Rect(); + float mCompatibleScreenScale; // The surface used to fill the outer rim of the app running in compatibility mode. Surface mBackgroundFillerSurface = null; WindowState mBackgroundFillerTarget = null; @@ -1757,7 +1757,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean rawChanged = false; float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f; float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f; - int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw; + int availw = wallpaperWin.mScaledFrame.right-wallpaperWin.mScaledFrame.left-dw; int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0; changed = wallpaperWin.mXOffset != offset; if (changed) { @@ -2887,14 +2887,14 @@ public class WindowManagerService extends IWindowManager.Stub } private boolean applyAnimationLocked(AppWindowToken wtoken, - WindowManager.LayoutParams lp, int transit, boolean enter) { + WindowManager.LayoutParams lp, int transit, boolean enter, boolean bgFiller) { // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. if (!mDisplayFrozen && mPolicy.isScreenOn()) { Animation a; - if (lp != null && (lp.flags & FLAG_COMPATIBLE_WINDOW) != 0) { + if (bgFiller) { a = new FadeInOutAnimation(enter); if (DEBUG_ANIM) Slog.v(TAG, "applying FadeInOutAnimation for a window in compatibility mode"); @@ -3680,7 +3680,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, - boolean visible, int transit, boolean performLayout) { + boolean visible, int transit, boolean performLayout, boolean bgFiller) { boolean delayed = false; if (wtoken.clientHidden == visible) { @@ -3702,7 +3702,7 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken.animation == sDummyAnimation) { wtoken.animation = null; } - applyAnimationLocked(wtoken, lp, transit, visible); + applyAnimationLocked(wtoken, lp, transit, visible, bgFiller); changed = true; if (wtoken.animation != null) { delayed = runningAppAnimation = true; @@ -3855,7 +3855,8 @@ public class WindowManagerService extends IWindowManager.Stub } final long origId = Binder.clearCallingIdentity(); - setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, true); + setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, + true, false); wtoken.updateReportedVisibilityLocked(); Binder.restoreCallingIdentity(origId); } @@ -3981,7 +3982,8 @@ public class WindowManagerService extends IWindowManager.Stub WindowToken basewtoken = mTokenMap.remove(token); if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken); - delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_UNSET, true); + delayed = setTokenVisibilityLocked(wtoken, null, false, + WindowManagerPolicy.TRANSIT_UNSET, true, false); wtoken.inPendingTransaction = false; mOpeningApps.remove(wtoken); wtoken.waitingToShow = false; @@ -4753,8 +4755,8 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { long ident = Binder.clearCallingIdentity(); - dw = mDisplay.getWidth(); - dh = mDisplay.getHeight(); + dw = mPolicy.getNonDecorDisplayWidth(mDisplay.getWidth()); + dh = mPolicy.getNonDecorDisplayHeight(mDisplay.getHeight()); int aboveAppLayer = mPolicy.windowTypeToLayerLw( WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER @@ -4802,7 +4804,7 @@ public class WindowManagerService extends IWindowManager.Stub // Don't include wallpaper in bounds calculation if (!ws.mIsWallpaper) { - final Rect wf = ws.mFrame; + final Rect wf = ws.mScaledFrame; final Rect cr = ws.mContentInsets; int left = wf.left + cr.left; int top = wf.top + cr.top; @@ -5447,7 +5449,10 @@ public class WindowManagerService extends IWindowManager.Stub DisplayMetrics dm = new DisplayMetrics(); mDisplay.getMetrics(dm); - CompatibilityInfo.updateCompatibleScreenFrame(dm, orientation, mCompatibleScreenFrame); + dm.realWidthPixels = mPolicy.getNonDecorDisplayWidth(dm.realWidthPixels); + dm.realHeightPixels = mPolicy.getNonDecorDisplayHeight(dm.realHeightPixels); + mCompatibleScreenScale = CompatibilityInfo.updateCompatibleScreenFrame( + dm, mCompatibleScreenFrame, null); if (mScreenLayout == Configuration.SCREENLAYOUT_SIZE_UNDEFINED) { // Note we only do this once because at this point we don't @@ -6582,6 +6587,9 @@ public class WindowManagerService extends IWindowManager.Stub final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); + final int innerDw = mPolicy.getNonDecorDisplayWidth(dw); + final int innerDh = mPolicy.getNonDecorDisplayHeight(dh); + final int N = mWindows.size(); int i; @@ -6634,7 +6642,9 @@ public class WindowManagerService extends IWindowManager.Stub //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); win.mContentChanged = false; } + win.prelayout(); mPolicy.layoutWindowLw(win, win.mAttrs, null); + win.evalNeedsBackgroundFiller(innerDw, innerDh); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame=" + win.mFrame + " mContainingFrame=" @@ -6669,7 +6679,9 @@ public class WindowManagerService extends IWindowManager.Stub //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); win.mContentChanged = false; } + win.prelayout(); mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); + win.evalNeedsBackgroundFiller(innerDw, innerDh); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame=" + win.mFrame + " mContainingFrame=" @@ -6700,6 +6712,9 @@ public class WindowManagerService extends IWindowManager.Stub final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); + final int innerDw = mPolicy.getNonDecorDisplayWidth(dw); + final int innerDh = mPolicy.getNonDecorDisplayHeight(dh); + int i; if (mFocusMayChange) { @@ -6799,13 +6814,15 @@ public class WindowManagerService extends IWindowManager.Stub boolean tokensAnimating = false; final int NAT = mAppTokens.size(); for (i=0; i<NAT; i++) { - if (mAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) { + if (mAppTokens.get(i).stepAnimationLocked(currentTime, + innerDw, innerDh)) { tokensAnimating = true; } } final int NEAT = mExitingAppTokens.size(); for (i=0; i<NEAT; i++) { - if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) { + if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, + innerDw, innerDh)) { tokensAnimating = true; } } @@ -6858,8 +6875,8 @@ public class WindowManagerService extends IWindowManager.Stub final boolean wasAnimating = w.mAnimating; - int animDw = dw; - int animDh = dh; + int animDw = innerDw; + int animDh = innerDh; // If the window has moved due to its containing // content frame changing, then we'd like to animate @@ -7116,6 +7133,7 @@ public class WindowManagerService extends IWindowManager.Stub LayoutParams animLp = null; int bestAnimLayer = -1; boolean fullscreenAnim = false; + boolean needBgFiller = false; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New wallpaper target=" + mWallpaperTarget @@ -7155,9 +7173,10 @@ public class WindowManagerService extends IWindowManager.Stub if (ws != null) { // If this is a compatibility mode // window, we will always use its anim. - if ((ws.mAttrs.flags&FLAG_COMPATIBLE_WINDOW) != 0) { + if (ws.mNeedsBackgroundFiller) { animLp = ws.mAttrs; bestAnimLayer = Integer.MAX_VALUE; + needBgFiller = true; } else if (!fullscreenAnim || ws.mLayer > bestAnimLayer) { animLp = ws.mAttrs; bestAnimLayer = ws.mLayer; @@ -7222,7 +7241,8 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.reportedVisible = false; wtoken.inPendingTransaction = false; wtoken.animation = null; - setTokenVisibilityLocked(wtoken, animLp, true, transit, false); + setTokenVisibilityLocked(wtoken, animLp, true, + transit, false, needBgFiller); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToShow = false; wtoken.showAllWindowsLocked(); @@ -7234,7 +7254,8 @@ public class WindowManagerService extends IWindowManager.Stub "Now closing app" + wtoken); wtoken.inPendingTransaction = false; wtoken.animation = null; - setTokenVisibilityLocked(wtoken, animLp, false, transit, false); + setTokenVisibilityLocked(wtoken, animLp, false, + transit, false, needBgFiller); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToHide = false; // Force the allDrawn flag, because we want to start @@ -7737,12 +7758,14 @@ public class WindowManagerService extends IWindowManager.Stub } boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn(); - if (opaqueDrawn && w.isFullscreen(dw, dh)) { + if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) { // This window completely covers everything behind it, // so we want to leave all of them as unblurred (for // performance reasons). obscured = true; - } else if (w.needsBackgroundFiller(dw, dh) && (canBeSeen || w.isAnimating())) { + } else if (w.mNeedsBackgroundFiller && w.mHasDrawn + && w.mViewVisibility == View.VISIBLE + && (canBeSeen || w.isAnimating())) { // This window is in compatibility mode, and needs background filler. obscured = true; mBackgroundFillerTarget = w; diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index f8ff5f8..72049ec 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -72,6 +72,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { final boolean mIsImWindow; final boolean mIsWallpaper; final boolean mIsFloatingLayer; + final boolean mEnforceSizeCompat; int mViewVisibility; boolean mPolicyVisibility = true; boolean mPolicyVisibilityAfterAnim = true; @@ -91,6 +92,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { int mLastLayer; boolean mHaveFrame; boolean mObscured; + boolean mNeedsBackgroundFiller; boolean mTurnOnScreen; int mLayoutSeq = -1; @@ -154,6 +156,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Current transformation being applied. boolean mHaveMatrix; + float mGlobalScale=1; float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; float mHScale=1, mVScale=1; @@ -163,6 +166,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // "Real" frame that the application sees. final Rect mFrame = new Rect(); final Rect mLastFrame = new Rect(); + final Rect mScaledFrame = new Rect(); final Rect mContainingFrame = new Rect(); final Rect mDisplayFrame = new Rect(); @@ -273,6 +277,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mViewVisibility = viewVisibility; DeathRecipient deathRecipient = new DeathRecipient(); mAlpha = a.alpha; + mEnforceSizeCompat = (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0; if (WindowManagerService.localLOGV) Slog.v( WindowManagerService.TAG, "Window " + this + " client=" + c.asBinder() + " token=" + token + " (" + mAttrs.token + ")"); @@ -368,7 +373,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { final Rect display = mDisplayFrame; display.set(df); - if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) { + if (mEnforceSizeCompat) { container.intersect(mService.mCompatibleScreenFrame); if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) { display.intersect(mService.mCompatibleScreenFrame); @@ -416,6 +421,28 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Now make sure the window fits in the overall display. Gravity.applyDisplay(mAttrs.gravity, df, frame); + int adjRight=0, adjBottom=0; + + if (mEnforceSizeCompat) { + // Adjust window offsets by the scaling factor. + int xoff = (int)((frame.left-mService.mCompatibleScreenFrame.left)*mGlobalScale) + - (frame.left-mService.mCompatibleScreenFrame.left); + int yoff = (int)((frame.top-mService.mCompatibleScreenFrame.top)*mGlobalScale) + - (frame.top-mService.mCompatibleScreenFrame.top); + frame.offset(xoff, yoff); + + // We are temporarily going to apply the compatibility scale + // to the window so that we can correctly associate it with the + // content and visible frame. + adjRight = frame.right - frame.left; + adjRight = (int)((adjRight)*mGlobalScale + .5f) - adjRight; + adjBottom = frame.bottom - frame.top; + adjBottom = (int)((adjBottom)*mGlobalScale + .5f) - adjBottom; + frame.right += adjRight; + frame.bottom += adjBottom; + } + mScaledFrame.set(frame); + // Make sure the content and visible frames are inside of the // final window frame. if (content.left < frame.left) content.left = frame.left; @@ -439,6 +466,22 @@ final class WindowState implements WindowManagerPolicy.WindowState { visibleInsets.right = frame.right-visible.right; visibleInsets.bottom = frame.bottom-visible.bottom; + if (mEnforceSizeCompat) { + // Scale the computed insets back to the window's compatibility + // coordinate space, and put frame back to correct size. + final float invScale = 1.0f/mGlobalScale; + contentInsets.left = (int)(contentInsets.left*invScale); + contentInsets.top = (int)(contentInsets.top*invScale); + contentInsets.right = (int)(contentInsets.right*invScale); + contentInsets.bottom = (int)(contentInsets.bottom*invScale); + visibleInsets.left = (int)(visibleInsets.left*invScale); + visibleInsets.top = (int)(visibleInsets.top*invScale); + visibleInsets.right = (int)(visibleInsets.right*invScale); + visibleInsets.bottom = (int)(visibleInsets.bottom*invScale); + frame.right -= adjRight; + frame.bottom -= adjBottom; + } + if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) { mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getWidth(), mService.mDisplay.getHeight(), false); @@ -819,9 +862,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (!mLocalAnimating) { if (WindowManagerService.DEBUG_ANIM) Slog.v( WindowManagerService.TAG, "Starting animation in " + this + - " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + + " @ " + currentTime + ": ww=" + mScaledFrame.width() + + " wh=" + mScaledFrame.height() + " dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale); - mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); + mAnimation.initialize(mScaledFrame.width(), mScaledFrame.height(), dw, dh); mAnimation.setStartTime(currentTime); mLocalAnimating = true; mAnimating = true; @@ -988,6 +1032,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { return true; } + void prelayout() { + if (mEnforceSizeCompat) { + mGlobalScale = mService.mCompatibleScreenScale; + } else { + mGlobalScale = 1; + } + } + void computeShownFrameLocked() { final boolean selfTransformation = mHasLocalTransformation; Transformation attachedTransformation = @@ -1031,6 +1083,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Compute the desired transformation. tmpMatrix.setTranslate(0, 0); + tmpMatrix.postScale(mGlobalScale, mGlobalScale); if (selfTransformation) { tmpMatrix.postConcat(mTransformation.getMatrix()); } @@ -1105,10 +1158,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { } mShownAlpha = mAlpha; mHaveMatrix = false; - mDsDx = 1; + mDsDx = mGlobalScale; mDtDx = 0; mDsDy = 0; - mDtDy = 1; + mDtDy = mGlobalScale; } /** @@ -1281,12 +1334,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { && mService.mPolicy.isScreenOn(); } - boolean needsBackgroundFiller(int screenWidth, int screenHeight) { - return + void evalNeedsBackgroundFiller(int screenWidth, int screenHeight) { + mNeedsBackgroundFiller = // only if the application is requesting compatible window - (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 && + mEnforceSizeCompat && // only if it's visible mHasDrawn && mViewVisibility == View.VISIBLE && + // not needed if the compat window is actually full screen + !isFullscreenIgnoringCompat(screenWidth, screenHeight) && // and only if the application fills the compatible screen mFrame.left <= mService.mCompatibleScreenFrame.left && mFrame.top <= mService.mCompatibleScreenFrame.top && @@ -1295,8 +1350,19 @@ final class WindowState implements WindowManagerPolicy.WindowState { } boolean isFullscreen(int screenWidth, int screenHeight) { - return mFrame.left <= 0 && mFrame.top <= 0 && - mFrame.right >= screenWidth && mFrame.bottom >= screenHeight; + if (mEnforceSizeCompat) { + return mFrame.left <= mService.mCompatibleScreenFrame.left && + mFrame.top <= mService.mCompatibleScreenFrame.top && + mFrame.right >= mService.mCompatibleScreenFrame.right && + mFrame.bottom >= mService.mCompatibleScreenFrame.bottom; + } else { + return isFullscreenIgnoringCompat(screenWidth, screenHeight); + } + } + + boolean isFullscreenIgnoringCompat(int screenWidth, int screenHeight) { + return mScaledFrame.left <= 0 && mScaledFrame.top <= 0 && + mScaledFrame.right >= screenWidth && mScaledFrame.bottom >= screenHeight; } void removeLocked() { @@ -1426,30 +1492,38 @@ final class WindowState implements WindowManagerPolicy.WindowState { return true; } + private static void applyScaledInsets(Region outRegion, Rect frame, Rect inset, float scale) { + if (scale != 1) { + outRegion.set(frame.left + (int)(inset.left*scale), + frame.top + (int)(inset.top*scale), + frame.right - (int)(inset.right*scale), + frame.bottom - (int)(inset.bottom*scale)); + } else { + outRegion.set( + frame.left + inset.left, frame.top + inset.top, + frame.right - inset.right, frame.bottom - inset.bottom); + } + } + public void getTouchableRegion(Region outRegion) { - final Rect frame = mFrame; + final Rect frame = mScaledFrame; switch (mTouchableInsets) { default: case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME: outRegion.set(frame); break; - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: { - final Rect inset = mGivenContentInsets; - outRegion.set( - frame.left + inset.left, frame.top + inset.top, - frame.right - inset.right, frame.bottom - inset.bottom); + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: + applyScaledInsets(outRegion, frame, mGivenContentInsets, mGlobalScale); break; - } - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: { - final Rect inset = mGivenVisibleInsets; - outRegion.set( - frame.left + inset.left, frame.top + inset.top, - frame.right - inset.right, frame.bottom - inset.bottom); + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: + applyScaledInsets(outRegion, frame, mGivenVisibleInsets, mGlobalScale); break; - } case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: { final Region givenTouchableRegion = mGivenTouchableRegion; outRegion.set(givenTouchableRegion); + if (mGlobalScale != 1) { + outRegion.scale(mGlobalScale); + } outRegion.translate(frame.left, frame.top); break; } @@ -1512,7 +1586,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { } pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); pw.print(" h="); pw.print(mRequestedHeight); - pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); + pw.print(" mLayoutSeq="); pw.print(mLayoutSeq); + pw.print(" mNeedsBackgroundFiller="); pw.println(mNeedsBackgroundFiller); if (mXOffset != 0 || mYOffset != 0) { pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset); pw.print(" y="); pw.println(mYOffset); @@ -1533,6 +1608,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { pw.println(); pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw); pw.print(" last="); mLastFrame.printShortString(pw); + pw.print(" scaled="); mScaledFrame.printShortString(pw); pw.println(); pw.print(prefix); pw.print("mContainingFrame="); mContainingFrame.printShortString(pw); @@ -1568,8 +1644,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { pw.print(" mAlpha="); pw.print(mAlpha); pw.print(" mLastAlpha="); pw.println(mLastAlpha); } - if (mHaveMatrix) { - pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx); + if (mHaveMatrix || mGlobalScale != 1) { + pw.print(prefix); pw.print("mGlobalScale="); pw.print(mGlobalScale); + pw.print(" mDsDx="); pw.print(mDsDx); pw.print(" mDtDx="); pw.print(mDtDx); pw.print(" mDsDy="); pw.print(mDsDy); pw.print(" mDtDy="); pw.println(mDtDy); |