diff options
author | Craig Mautner <cmautner@google.com> | 2013-04-23 11:23:27 -0700 |
---|---|---|
committer | Craig Mautner <cmautner@google.com> | 2013-04-23 19:33:27 -0700 |
commit | cf910b0c714b2ca90ea0013e5695850506a1d36f (patch) | |
tree | 675cf0274fec4bd86e3664c7826643fec29bbb41 | |
parent | f333327782e14688e1c198c1192172d51308e90b (diff) | |
download | frameworks_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
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); |