summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2011-04-27 18:52:56 -0400
committerDianne Hackborn <hackbod@google.com>2011-05-09 17:03:24 -0700
commite2515eebf42c763c0a2d9f873a153711778cfc17 (patch)
treeee159940f05c439d0524771bfbfe6b551c82c2d4 /services
parent158e3582c03ab9e0221718da20fc8854bb9216d0 (diff)
downloadframeworks_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')
-rw-r--r--services/input/InputDispatcher.cpp71
-rw-r--r--services/input/InputDispatcher.h7
-rw-r--r--services/input/InputWindow.cpp3
-rw-r--r--services/input/InputWindow.h6
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java103
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java4
-rw-r--r--services/java/com/android/server/am/ActivityStack.java11
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java1
-rw-r--r--services/java/com/android/server/wm/InputMonitor.java11
-rw-r--r--services/java/com/android/server/wm/InputWindow.java4
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java65
-rw-r--r--services/java/com/android/server/wm/WindowState.java129
-rw-r--r--services/jni/com_android_server_InputWindow.cpp6
13 files changed, 341 insertions, 80 deletions
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 19295e6..f8a5cfb 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -156,6 +156,14 @@ static bool validateMotionEvent(int32_t action, size_t pointerCount,
return true;
}
+static void scalePointerCoords(const PointerCoords* inCoords, size_t count, float scaleFactor,
+ PointerCoords* outCoords) {
+ for (size_t i = 0; i < count; i++) {
+ outCoords[i] = inCoords[i];
+ outCoords[i].scale(scaleFactor);
+ }
+}
+
static void dumpRegion(String8& dump, const SkRegion& region) {
if (region.isEmpty()) {
dump.append("<empty>");
@@ -1494,6 +1502,7 @@ void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t t
target.flags = targetFlags;
target.xOffset = - window->frameLeft;
target.yOffset = - window->frameTop;
+ target.scaleFactor = window->scaleFactor;
target.pointerIds = pointerIds;
}
@@ -1506,6 +1515,7 @@ void InputDispatcher::addMonitoringTargetsLocked() {
target.flags = 0;
target.xOffset = 0;
target.yOffset = 0;
+ target.scaleFactor = 1.0f;
}
}
@@ -1607,12 +1617,12 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
bool resumeWithAppendedMotionSample) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
- "xOffset=%f, yOffset=%f, "
+ "xOffset=%f, yOffset=%f, scaleFactor=%f"
"pointerIds=0x%x, "
"resumeWithAppendedMotionSample=%s",
connection->getInputChannelName(), inputTarget->flags,
inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->pointerIds.value,
+ inputTarget->scaleFactor, inputTarget->pointerIds.value,
toString(resumeWithAppendedMotionSample));
#endif
@@ -1693,8 +1703,19 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
// consumed the motion event (or if the channel is broken).
MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
MotionSample* appendedMotionSample = motionEntry->lastSample;
- status_t status = connection->inputPublisher.appendMotionSample(
- appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
+ status_t status;
+ if (motionEventDispatchEntry->scaleFactor == 1.0f) {
+ status = connection->inputPublisher.appendMotionSample(
+ appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
+ } else {
+ PointerCoords scaledCoords[MAX_POINTERS];
+ for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+ scaledCoords[i] = appendedMotionSample->pointerCoords[i];
+ scaledCoords[i].scale(motionEventDispatchEntry->scaleFactor);
+ }
+ status = connection->inputPublisher.appendMotionSample(
+ appendedMotionSample->eventTime, scaledCoords);
+ }
if (status == OK) {
#if DEBUG_BATCHING
LOGD("channel '%s' ~ Successfully streamed new motion sample.",
@@ -1731,7 +1752,8 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
- inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
+ inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset,
+ inputTarget->scaleFactor);
if (dispatchEntry->hasForegroundTarget()) {
incrementPendingForegroundDispatchesLocked(eventEntry);
}
@@ -1827,24 +1849,34 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
firstMotionSample = & motionEntry->firstSample;
}
+ PointerCoords scaledCoords[MAX_POINTERS];
+ const PointerCoords* usingCoords = firstMotionSample->pointerCoords;
+
// Set the X and Y offset depending on the input source.
- float xOffset, yOffset;
+ float xOffset, yOffset, scaleFactor;
if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
- xOffset = dispatchEntry->xOffset;
- yOffset = dispatchEntry->yOffset;
+ scaleFactor = dispatchEntry->scaleFactor;
+ xOffset = dispatchEntry->xOffset * scaleFactor;
+ yOffset = dispatchEntry->yOffset * scaleFactor;
+ if (scaleFactor != 1.0f) {
+ for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+ scaledCoords[i] = firstMotionSample->pointerCoords[i];
+ scaledCoords[i].scale(scaleFactor);
+ }
+ usingCoords = scaledCoords;
+ }
} else {
xOffset = 0.0f;
yOffset = 0.0f;
+ scaleFactor = 1.0f;
}
// Publish the motion event and the first motion sample.
status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState,
- xOffset, yOffset,
- motionEntry->xPrecision, motionEntry->yPrecision,
+ xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
motionEntry->downTime, firstMotionSample->eventTime,
- motionEntry->pointerCount, motionEntry->pointerIds,
- firstMotionSample->pointerCoords);
+ motionEntry->pointerCount, motionEntry->pointerIds, usingCoords);
if (status) {
LOGE("channel '%s' ~ Could not publish motion event, "
@@ -1857,7 +1889,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
MotionSample* nextMotionSample = firstMotionSample->next;
for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
status = connection->inputPublisher.appendMotionSample(
- nextMotionSample->eventTime, nextMotionSample->pointerCoords);
+ nextMotionSample->eventTime, usingCoords);
if (status == NO_MEMORY) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ Shared memory buffer full. Some motion samples will "
@@ -2095,18 +2127,21 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
}
int32_t xOffset, yOffset;
+ float scaleFactor;
const InputWindow* window = getWindowLocked(connection->inputChannel);
if (window) {
xOffset = -window->frameLeft;
yOffset = -window->frameTop;
+ scaleFactor = window->scaleFactor;
} else {
xOffset = 0;
yOffset = 0;
+ scaleFactor = 1.0f;
}
DispatchEntry* cancelationDispatchEntry =
mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref
- 0, xOffset, yOffset);
+ 0, xOffset, yOffset, scaleFactor);
connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
mAllocator.releaseEventEntry(cancelationEventEntry);
@@ -2957,7 +2992,7 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
const InputWindow& window = mWindows[i];
dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
"visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
- "frame=[%d,%d][%d,%d], "
+ "frame=[%d,%d][%d,%d], scale=%f, "
"touchableRegion=",
i, window.name.string(),
toString(window.paused),
@@ -2968,7 +3003,8 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
window.layoutParamsFlags, window.layoutParamsType,
window.layer,
window.frameLeft, window.frameTop,
- window.frameRight, window.frameBottom);
+ window.frameRight, window.frameBottom,
+ window.scaleFactor);
dumpRegion(dump, window.touchableRegion);
dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
window.ownerPid, window.ownerUid,
@@ -3460,13 +3496,14 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec
InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset) {
+ int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) {
DispatchEntry* entry = mDispatchEntryPool.alloc();
entry->eventEntry = eventEntry;
eventEntry->refCount += 1;
entry->targetFlags = targetFlags;
entry->xOffset = xOffset;
entry->yOffset = yOffset;
+ entry->scaleFactor = scaleFactor;
entry->inProgress = false;
entry->headMotionSample = NULL;
entry->tailMotionSample = NULL;
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 1e118c4..dd89328 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -112,6 +112,10 @@ struct InputTarget {
// (ignored for KeyEvents)
float xOffset, yOffset;
+ // Scaling factor to apply to MotionEvent as it is delivered.
+ // (ignored for KeyEvents)
+ float scaleFactor;
+
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
@@ -431,6 +435,7 @@ private:
int32_t targetFlags;
float xOffset;
float yOffset;
+ float scaleFactor;
// True if dispatch has started.
bool inProgress;
@@ -559,7 +564,7 @@ private:
nsecs_t downTime, uint32_t pointerCount,
const int32_t* pointerIds, const PointerCoords* pointerCoords);
DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset);
+ int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
CommandEntry* obtainCommandEntry(Command command);
void releaseInjectionState(InjectionState* injectionState);
diff --git a/services/input/InputWindow.cpp b/services/input/InputWindow.cpp
index b552f6d..ccea9e4 100644
--- a/services/input/InputWindow.cpp
+++ b/services/input/InputWindow.cpp
@@ -29,8 +29,7 @@ bool InputWindow::touchableRegionContainsPoint(int32_t x, int32_t y) const {
}
bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const {
- return x >= frameLeft && x <= frameRight
- && y >= frameTop && y <= frameBottom;
+ return x <= frameRight || y <= frameBottom;
}
bool InputWindow::isTrustedOverlay() const {
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index 9c43067..72f1773 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -131,6 +131,7 @@ struct InputWindow {
int32_t frameTop;
int32_t frameRight;
int32_t frameBottom;
+ float scaleFactor;
SkRegion touchableRegion;
bool visible;
bool canReceiveKeys;
@@ -144,6 +145,11 @@ struct InputWindow {
bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
bool frameContainsPoint(int32_t x, int32_t y) const;
+ /* These use the globalScale to convert a given screen offset to the
+ * corresponding location within the window.
+ */
+ int32_t displayToWindowX(int32_t x) const;
+
/* Returns true if the window is of a trusted type that is allowed to silently
* overlay other windows for the purpose of implementing the secure views feature.
* Trusted overlays, such as IME windows, can partly obscure other windows without causing
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);
diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp
index 8548b47..d36c010 100644
--- a/services/jni/com_android_server_InputWindow.cpp
+++ b/services/jni/com_android_server_InputWindow.cpp
@@ -40,6 +40,7 @@ static struct {
jfieldID frameTop;
jfieldID frameRight;
jfieldID frameBottom;
+ jfieldID scaleFactor;
jfieldID touchableRegion;
jfieldID visible;
jfieldID canReceiveKeys;
@@ -102,6 +103,8 @@ void android_server_InputWindow_toNative(
gInputWindowClassInfo.frameRight);
outInputWindow->frameBottom = env->GetIntField(inputWindowObj,
gInputWindowClassInfo.frameBottom);
+ outInputWindow->scaleFactor = env->GetFloatField(inputWindowObj,
+ gInputWindowClassInfo.scaleFactor);
jobject regionObj = env->GetObjectField(inputWindowObj,
gInputWindowClassInfo.touchableRegion);
@@ -176,6 +179,9 @@ int register_android_server_InputWindow(JNIEnv* env) {
GET_FIELD_ID(gInputWindowClassInfo.frameBottom, gInputWindowClassInfo.clazz,
"frameBottom", "I");
+ GET_FIELD_ID(gInputWindowClassInfo.scaleFactor, gInputWindowClassInfo.clazz,
+ "scaleFactor", "F");
+
GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, gInputWindowClassInfo.clazz,
"touchableRegion", "Landroid/graphics/Region;");