summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCraig Mautner <cmautner@google.com>2013-04-23 11:23:27 -0700
committerCraig Mautner <cmautner@google.com>2013-04-23 19:33:27 -0700
commitcf910b0c714b2ca90ea0013e5695850506a1d36f (patch)
tree675cf0274fec4bd86e3664c7826643fec29bbb41
parentf333327782e14688e1c198c1192172d51308e90b (diff)
downloadframeworks_base-cf910b0c714b2ca90ea0013e5695850506a1d36f.zip
frameworks_base-cf910b0c714b2ca90ea0013e5695850506a1d36f.tar.gz
frameworks_base-cf910b0c714b2ca90ea0013e5695850506a1d36f.tar.bz2
Add tap detector for switching stack focus.
- New InputEventReceiver for detecting taps outside of focused stack boundaries. - Fixed bug that wasn't pausing the non-focused window when returning home. Change-Id: Ia51d312a7c029abc01eb5df1102814cc29d33b47
-rw-r--r--core/java/android/app/ActivityManagerNative.java22
-rw-r--r--core/java/android/app/IActivityManager.java22
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java27
-rw-r--r--services/java/com/android/server/am/ActivityStack.java21
-rw-r--r--services/java/com/android/server/am/ActivityStackSupervisor.java48
-rw-r--r--services/java/com/android/server/wm/DisplayContent.java17
-rw-r--r--services/java/com/android/server/wm/StackBox.java20
-rw-r--r--services/java/com/android/server/wm/StackTapDetector.java101
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java44
9 files changed, 293 insertions, 29 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 94f4db2..3dbb636 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -647,6 +647,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case SET_FOCUSED_STACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int stackId = data.readInt();
+ setFocusedStack(stackId);
+ reply.writeNoException();
+ return true;
+ }
+
case GET_TASK_FOR_ACTIVITY_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -2634,7 +2642,7 @@ class ActivityManagerProxy implements IActivityManager
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(stackId);
data.writeFloat(weight);
- mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0);
+ mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
reply.readException();
data.recycle();
reply.recycle();
@@ -2653,6 +2661,18 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return list;
}
+ @Override
+ public void setFocusedStack(int stackId) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(stackId);
+ mRemote.transact(SET_FOCUSED_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException
{
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 46c97d6..5a798de 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -120,6 +120,7 @@ public interface IActivityManager extends IInterface {
public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
public void resizeStack(int stackId, float weight) throws RemoteException;
public List<StackInfo> getStacks() throws RemoteException;
+ public void setFocusedStack(int stackId) throws RemoteException;
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
/* oneway */
public void reportThumbnail(IBinder token,
@@ -157,14 +158,14 @@ public interface IActivityManager extends IInterface {
public void serviceDoneExecuting(IBinder token, int type, int startId,
int res) throws RemoteException;
public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
-
+
public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
throws RemoteException;
public void clearPendingBackup() throws RemoteException;
public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException;
public void killApplicationProcess(String processName, int uid) throws RemoteException;
-
+
public boolean startInstrumentation(ComponentName className, String profileFile,
int flags, Bundle arguments, IInstrumentationWatcher watcher,
IUiAutomationConnection connection, int userId) throws RemoteException;
@@ -176,7 +177,7 @@ public interface IActivityManager extends IInterface {
public void setRequestedOrientation(IBinder token,
int requestedOrientation) throws RemoteException;
public int getRequestedOrientation(IBinder token) throws RemoteException;
-
+
public ComponentName getActivityClassForToken(IBinder token) throws RemoteException;
public String getPackageForToken(IBinder token) throws RemoteException;
@@ -189,16 +190,16 @@ public interface IActivityManager extends IInterface {
final IPackageDataObserver observer, int userId) throws RemoteException;
public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;
public int getUidForIntentSender(IIntentSender sender) throws RemoteException;
-
+
public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
boolean requireFull, String name, String callerPackage) throws RemoteException;
public void setProcessLimit(int max) throws RemoteException;
public int getProcessLimit() throws RemoteException;
-
+
public void setProcessForeground(IBinder token, int pid,
boolean isForeground) throws RemoteException;
-
+
public int checkPermission(String permission, int pid, int uid)
throws RemoteException;
@@ -401,10 +402,12 @@ public interface IActivityManager extends IInterface {
info = _info;
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
info.writeToParcel(dest, 0);
if (provider != null) {
@@ -418,10 +421,12 @@ public interface IActivityManager extends IInterface {
public static final Parcelable.Creator<ContentProviderHolder> CREATOR
= new Parcelable.Creator<ContentProviderHolder>() {
+ @Override
public ContentProviderHolder createFromParcel(Parcel source) {
return new ContentProviderHolder(source);
}
+ @Override
public ContentProviderHolder[] newArray(int size) {
return new ContentProviderHolder[size];
}
@@ -447,10 +452,12 @@ public interface IActivityManager extends IInterface {
public WaitResult() {
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
dest.writeInt(timeout ? 1 : 0);
@@ -461,10 +468,12 @@ public interface IActivityManager extends IInterface {
public static final Parcelable.Creator<WaitResult> CREATOR
= new Parcelable.Creator<WaitResult>() {
+ @Override
public WaitResult createFromParcel(Parcel source) {
return new WaitResult(source);
}
+ @Override
public WaitResult[] newArray(int size) {
return new WaitResult[size];
}
@@ -650,4 +659,5 @@ public interface IActivityManager extends IInterface {
int RESIZE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+167;
int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+168;
int GET_STACKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+169;
+ int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170;
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5ad5544..dd113c3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -958,6 +958,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int USER_SWITCH_TIMEOUT_MSG = 36;
static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int PERSIST_URI_GRANTS = 38;
+ static final int SET_FOCUSED_STACK = 39;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1446,6 +1447,18 @@ public final class ActivityManagerService extends ActivityManagerNative
writeGrantedUriPermissions();
break;
}
+ case SET_FOCUSED_STACK: {
+ synchronized (ActivityManagerService.this) {
+ ActivityStack stack = mStackSupervisor.getStack(msg.arg1);
+ if (stack != null) {
+ ActivityRecord r = stack.topRunningActivityLocked(null);
+ if (r != null) {
+ setFocusedActivityLocked(r);
+ }
+ }
+ }
+ break;
+ }
}
}
};
@@ -1935,6 +1948,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ @Override
+ public void setFocusedStack(int stackId) {
+ mHandler.obtainMessage(SET_FOCUSED_STACK, stackId, 0).sendToTarget();
+ }
+
final void applyUpdateLockStateLocked(ActivityRecord r) {
// Modifications to the UpdateLock state are done on our handler, outside
// the activity manager's locks. The new state is determined based on the
@@ -3119,7 +3137,7 @@ public final class ActivityManagerService extends ActivityManagerNative
IApplicationThread thread) {
mProcDeaths[0]++;
-
+
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
stats.noteProcessDiedLocked(app.info.uid, pid);
@@ -3151,7 +3169,7 @@ public final class ActivityManagerService extends ActivityManagerNative
break;
}
}
-
+
if (!haveBg) {
EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
long now = SystemClock.uptimeMillis();
@@ -3500,7 +3518,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Unless configured otherwise, swallow ANRs in background processes & kill the process.
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-
+
synchronized (this) {
if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
Slog.w(TAG, "Killing " + app + ": background ANR");
@@ -4915,6 +4933,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return false;
}
+ @Override
public Intent getIntentForIntentSender(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return null;
@@ -4927,6 +4946,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
+ @Override
public void setProcessLimit(int max) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
"setProcessLimit()");
@@ -4937,6 +4957,7 @@ public final class ActivityManagerService extends ActivityManagerNative
trimApplications();
}
+ @Override
public int getProcessLimit() {
synchronized (this) {
return mProcessLimitOverride;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 9f31982..3557e90 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -761,7 +761,7 @@ final class ActivityStack {
return null;
}
- private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+ final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
if (mPausingActivity != null) {
Slog.e(TAG, "Trying to pause when pause is already pending for "
+ mPausingActivity, new RuntimeException("here").fillInStackTrace());
@@ -1206,6 +1206,7 @@ final class ActivityStack {
// There are no more activities! Let's just start up the
// Launcher...
ActivityOptions.abort(options);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return mStackSupervisor.resumeHomeActivity(prev);
}
@@ -1219,11 +1220,13 @@ final class ActivityStack {
mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
ActivityOptions.abort(options);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
if (prev != null && prev.mLaunchHomeTaskNext && prev.finishing &&
prev.task.getTopActivity() == null) {
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return mStackSupervisor.resumeHomeActivity(prev);
}
@@ -1237,6 +1240,7 @@ final class ActivityStack {
mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
ActivityOptions.abort(options);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -1246,6 +1250,7 @@ final class ActivityStack {
if (mService.mStartedUsers.get(next.userId) == null) {
Slog.w(TAG, "Skipping resume of top activity " + next
+ ": user " + next.userId + " is stopped");
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -1264,6 +1269,7 @@ final class ActivityStack {
// until that is done.
if (!mStackSupervisor.allPausedActivitiesComplete()) {
if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG, "Skip resume: some activity pausing");
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -1298,10 +1304,7 @@ final class ActivityStack {
// We need to start pausing the current activity so the top one
// can be resumed...
- final ActivityStack lastStack = mStackSupervisor.getLastStack();
- if (lastStack != null && (isHomeStack() ^ lastStack.isHomeStack()) &&
- lastStack.mResumedActivity != null) {
- // TODO: Don't pause when launching to the sibling task.
+ if (mStackSupervisor.pauseBackStacks(userLeaving)) {
if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
// At this point we want to put the upcoming activity's process
// at the top of the LRU list, since we know we will be needing it
@@ -1312,7 +1315,7 @@ final class ActivityStack {
// happen whenever it needs to later.
mService.updateLruProcessLocked(next.app, false);
}
- lastStack.startPausingLocked(userLeaving, false);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -1420,6 +1423,7 @@ final class ActivityStack {
next.clearOptionsLocked();
}
+ ActivityStack lastStack = mStackSupervisor.getLastStack();
if (next.app != null && next.app.thread != null) {
if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
@@ -1472,8 +1476,10 @@ final class ActivityStack {
}
if (mStackSupervisor.reportResumedActivityLocked(next)) {
mNoAnimActivities.clear();
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -1526,6 +1532,7 @@ final class ActivityStack {
null, true);
}
mStackSupervisor.startSpecificActivityLocked(next, true, false);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -1540,6 +1547,7 @@ final class ActivityStack {
Slog.w(TAG, "Exception thrown during resume of " + next, e);
requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
"resume-exception", true);
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
next.stopped = false;
@@ -1563,6 +1571,7 @@ final class ActivityStack {
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
+ if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index af358c2..89b0ff2 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -216,10 +216,6 @@ public class ActivityStackSupervisor {
return !(stack.isHomeStack() ^ getFocusedStack().isHomeStack());
}
- boolean homeIsInFront() {
- return isFrontStack(mHomeStack);
- }
-
void moveHomeStack(boolean toFront) {
final boolean homeInFront = isFrontStack(mHomeStack);
if (homeInFront ^ toFront) {
@@ -390,6 +386,18 @@ public class ActivityStackSupervisor {
return true;
}
+ boolean pauseBackStacks(boolean userLeaving) {
+ boolean someActivityPaused = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (!isFrontStack(stack) && stack.mResumedActivity != null) {
+ stack.startPausingLocked(userLeaving, false);
+ someActivityPaused = true;
+ }
+ }
+ return someActivityPaused;
+ }
+
boolean allPausedActivitiesComplete() {
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
@@ -1983,6 +1991,38 @@ public class ActivityStackSupervisor {
return stops;
}
+ void validateTopActivitiesLocked() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ final ActivityRecord r = stack.topRunningActivityLocked(null);
+ if (isFrontStack(stack)) {
+ if (r == null) {
+ Slog.e(TAG, "validateTop...: null top activity, stack=" + stack);
+ } else {
+ if (stack.mPausingActivity != null) {
+ Slog.e(TAG, "validateTop...: top stack has pausing activity r=" + r +
+ " state=" + r.state);
+ }
+ if (r.state != ActivityState.INITIALIZING &&
+ r.state != ActivityState.RESUMED) {
+ Slog.e(TAG, "validateTop...: activity in front not resumed r=" + r +
+ " state=" + r.state);
+ }
+ }
+ } else {
+ if (stack.mResumedActivity != null) {
+ Slog.e(TAG, "validateTop...: back stack has resumed activity r=" + r +
+ " state=" + r.state);
+ }
+ if (r != null && (r.state == ActivityState.INITIALIZING
+ || r.state == ActivityState.RESUMED)) {
+ Slog.e(TAG, "validateTop...: activity in back resumed r=" + r +
+ " state=" + r.state);
+ }
+ }
+ }
+ }
+
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mDismissKeyguardOnNextActivity:");
pw.println(mDismissKeyguardOnNextActivity);
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 915c696c..8343986 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -24,6 +24,7 @@ import android.graphics.Rect;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.InputChannel;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -84,10 +85,11 @@ class DisplayContent {
*/
final AppTokenList mExitingAppTokens = new AppTokenList();
- /** Array containing the home StackBox and possibly one more which would contain apps */
+ /** Array containing the home StackBox and possibly one more which would contain apps. Array
+ * is stored in display order with the current bottom stack at 0. */
private ArrayList<StackBox> mStackBoxes = new ArrayList<StackBox>();
- /** True when the home StackBox is at the top of mStackBoxes, false otherwise */
+ /** True when the home StackBox is at the top of mStackBoxes, false otherwise. */
private TaskStack mHomeStack = null;
/** Save allocating when retrieving tasks */
@@ -96,6 +98,12 @@ class DisplayContent {
/** Sorted most recent at top, oldest at [0]. */
ArrayList<TaskStack> mStackHistory = new ArrayList<TaskStack>();
+ /** Forward motion events to mTapDetector. */
+ InputChannel mTapInputChannel;
+
+ /** Detect user tapping outside of current focused stack bounds .*/
+ StackTapDetector mTapDetector;
+
/**
* @param display May not be null.
*/
@@ -287,6 +295,11 @@ class DisplayContent {
return null;
}
+ int stackIdFromPoint(int x, int y) {
+ StackBox topBox = mStackBoxes.get(mStackBoxes.size() - 1);
+ return topBox.stackIdFromPoint(x, y);
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
final String subPrefix = " " + prefix;
diff --git a/services/java/com/android/server/wm/StackBox.java b/services/java/com/android/server/wm/StackBox.java
index 83f32e9..3891aea 100644
--- a/services/java/com/android/server/wm/StackBox.java
+++ b/services/java/com/android/server/wm/StackBox.java
@@ -108,6 +108,26 @@ public class StackBox {
return mFirst.contains(stackId) || mSecond.contains(stackId);
}
+ /**
+ * Return the stackId of the stack that intersects the passed point.
+ * @param x coordinate of point.
+ * @param y coordinate of point.
+ * @return -1 if point is outside of mBounds, otherwise the stackId of the containing stack.
+ */
+ int stackIdFromPoint(int x, int y) {
+ if (!mBounds.contains(x, y)) {
+ return -1;
+ }
+ if (mStack != null) {
+ return mStack.mStackId;
+ }
+ int stackId = mFirst.stackIdFromPoint(x, y);
+ if (stackId >= 0) {
+ return stackId;
+ }
+ return mSecond.stackIdFromPoint(x, y);
+ }
+
/** Determine if this StackBox is the first child or second child.
* @return true if this is the first child.
*/
diff --git a/services/java/com/android/server/wm/StackTapDetector.java b/services/java/com/android/server/wm/StackTapDetector.java
new file mode 100644
index 0000000..7d63a9b
--- /dev/null
+++ b/services/java/com/android/server/wm/StackTapDetector.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 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 com.android.server.wm;
+
+import android.graphics.Rect;
+import android.os.Looper;
+import android.view.DisplayInfo;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.MotionEvent;
+
+public class StackTapDetector extends InputEventReceiver {
+ private static final int TAP_TIMEOUT_MSEC = 300;
+ private static final float TAP_MOTION_SLOP_INCHES = 0.125f;
+
+ private final int mMotionSlop;
+ private float mDownX;
+ private float mDownY;
+ private int mPointerId;
+ private Rect mStackBounds = new Rect();
+ private final WindowManagerService mService;
+ private final DisplayContent mDisplayContent;
+
+ public StackTapDetector(WindowManagerService service, DisplayContent displayContent,
+ InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ mService = service;
+ mDisplayContent = displayContent;
+ DisplayInfo info = displayContent.getDisplayInfo();
+ mMotionSlop = (int)(info.logicalDensityDpi * TAP_MOTION_SLOP_INCHES);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (!(event instanceof MotionEvent)
+ || !event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
+ return;
+ }
+ final MotionEvent motionEvent = (MotionEvent)event;
+ final int action = motionEvent.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ mPointerId = motionEvent.getPointerId(0);
+ mDownX = motionEvent.getX();
+ mDownY = motionEvent.getY();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mPointerId >= 0) {
+ int index = motionEvent.findPointerIndex(mPointerId);
+ if ((motionEvent.getEventTime() - motionEvent.getDownTime()) > TAP_TIMEOUT_MSEC
+ || (motionEvent.getX(index) - mDownX) > mMotionSlop
+ || (motionEvent.getY(index) - mDownY) > mMotionSlop) {
+ mPointerId = -1;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP: {
+ int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+ >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ // Extract the index of the pointer that left the touch sensor
+ if (mPointerId == motionEvent.getPointerId(index)) {
+ final int x = (int)motionEvent.getX(index);
+ final int y = (int)motionEvent.getY(index);
+ synchronized (this) {
+ if ((motionEvent.getEventTime() - motionEvent.getDownTime())
+ < TAP_TIMEOUT_MSEC
+ && (x - mDownX) < mMotionSlop && (y - mDownY) < mMotionSlop
+ && !mStackBounds.contains(x, y)) {
+ mService.tapOutsideStackBounds(mDisplayContent, x, y);
+ }
+ }
+ mPointerId = -1;
+ }
+ break;
+ }
+ }
+ }
+
+ void setStackBounds(Rect bounds) {
+ synchronized (this) {
+ mStackBounds.set(bounds);
+ }
+ }
+}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index db8ee5e..5a9d63a 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -275,6 +275,8 @@ public class WindowManagerService extends IWindowManager.Stub
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ private static final String TAP_INPUT_CHANNEL_NAME = "StackTapDetector";
+
private static final int MAX_SCREENSHOT_RETRIES = 3;
final private KeyguardDisableHandler mKeyguardDisableHandler;
@@ -731,6 +733,7 @@ public class WindowManagerService extends IWindowManager.Stub
mOnlyCore = onlyCore;
mLimitedAlphaCompositing = context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_limitedAlpha);
+ mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerService = displayManager;
mHeadless = displayManager.isHeadless();
mDisplaySettings = new DisplaySettings(context);
@@ -782,7 +785,6 @@ public class WindowManagerService extends IWindowManager.Stub
| PowerManager.ON_AFTER_RELEASE, TAG);
mHoldingScreenWakeLock.setReferenceCounted(false);
- mInputManager = inputManager;
mFxSession = new SurfaceSession();
mAnimator = new WindowAnimator(this);
@@ -3703,6 +3705,18 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ void tapOutsideStackBounds(DisplayContent displayContent, int x, int y) {
+ synchronized (mWindowMap) {
+ int stackId = displayContent.stackIdFromPoint(x, y);
+ if (stackId >= 0) {
+ try {
+ mActivityManager.setFocusedStack(stackId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
@Override
public void setFocusedApp(IBinder token, boolean moveFocusNow) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
@@ -3725,10 +3739,9 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Attempted to set focus to non-existing app token: " + token);
return;
}
- Task task = mTaskIdToTask.get(newFocus.groupId);
- task.getDisplayContent().moveStack(task.mStack, true);
changed = mFocusedApp != newFocus;
mFocusedApp = newFocus;
+ moveTaskToTop(newFocus.groupId);
if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp
+ " moveFocusNow=" + moveFocusNow);
if (changed) {
@@ -4663,7 +4676,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/)) {
+ false /*updateInputWindows*/)) {
assignLayersLocked(displayContent.getWindowList());
}
@@ -9260,8 +9273,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- final TaskStack stack = mFocusedApp != null ?
- mTaskIdToTask.get(mFocusedApp.groupId).mStack : null;
+ final TaskStack stack;
+ if (mFocusedApp != null) {
+ Task task = mTaskIdToTask.get(mFocusedApp.groupId);
+ stack = task.mStack;
+ task.getDisplayContent().mTapDetector.setStackBounds(stack.mStackBox.mBounds);
+ } else {
+ stack = null;
+ }
setFocusedStackFrame(stack);
// Check to see if we are now in a state where the screen should
@@ -10541,7 +10560,8 @@ public class WindowManagerService extends IWindowManager.Stub
private DisplayContent newDisplayContentLocked(final Display display) {
DisplayContent displayContent = new DisplayContent(display);
- mDisplayContents.put(display.getDisplayId(), displayContent);
+ final int displayId = display.getDisplayId();
+ mDisplayContents.put(displayId, displayContent);
final Rect rect = new Rect();
DisplayInfo info = displayContent.getDisplayInfo();
mDisplaySettings.getOverscanLocked(info.name, rect);
@@ -10553,6 +10573,15 @@ public class WindowManagerService extends IWindowManager.Stub
rect.right, rect.bottom);
mPolicy.setDisplayOverscan(displayContent.getDisplay(), rect.left, rect.top,
rect.right, rect.bottom);
+
+ // TODO: Create an input channel for each display with touch capability.
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ InputChannel inputChannel = monitorInput(TAP_INPUT_CHANNEL_NAME);
+ displayContent.mTapInputChannel = inputChannel;
+ displayContent.mTapDetector =
+ new StackTapDetector(this, displayContent, inputChannel, Looper.myLooper());
+ }
+
return displayContent;
}
@@ -10732,6 +10761,7 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
mDisplayContents.delete(displayId);
+ displayContent.mTapInputChannel.dispose();
WindowList windows = displayContent.getWindowList();
while (!windows.isEmpty()) {
final WindowState win = windows.get(windows.size() - 1);