diff options
author | 2015-12-26 07:36:26 -0800 | |
---|---|---|
committer | 2016-01-04 22:23:56 +0000 | |
commit | 6cae765b67d3a3b37e2cacbc9816665b5bc080b9 (patch) | |
tree | 33f08d077dd23e35c92dba6cb8233c6a255e515d | |
parent | 46ca282851ef12755a64810658a6043e70d6db5d (diff) |
Added support for android.R.attr#alwaysFocusable
Allows an activity to always be focusable regardless of if it is in a
stack whose activities are normally not focusable. For example, activities
in pinned stack aren't focusable. This flag allows them to be focusable.
Also, changed ActivityInfo.#{resizeable, supportsPip} to use flags.
Bug: 26273032
Bug: 26034613
Change-Id: I8c63e6d3256757e2e6931e08b8a65269f5169d35
14 files changed, 82 insertions, 49 deletions
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 326735ec4f36..dedf07f5dcff 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -281,6 +281,29 @@ public class ActivityInfo extends ComponentInfo * {@see android.app.Activity#setVrMode(boolean)}. */ public static final int FLAG_ENABLE_VR_MODE = 0x8000; + + /** + * Bit in {@link #flags} indicating if the activity is resizeable to any dimension. + * See {@link android.R.attr#resizeableActivity}. + * @hide + */ + public static final int FLAG_RESIZEABLE = 0x10000; + + /** + * Bit in {@link #flags} indicating if the activity is supports picture-in-picture form of + * multi-window mode. See {@link android.R.attr#supportsPictureInPicture}. + * @hide + */ + public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x20000; + + /** + * Bit in {@link #flags} indicating if the activity is always focusable regardless of if it is + * in a task/stack whose activities are normally not focusable. + * See android.R.attr#alwaysFocusable. + * @hide + */ + public static final int FLAG_ALWAYS_FOCUSABLE = 0x40000; + /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the system user. Only works with broadcast receivers. Set from the @@ -670,20 +693,6 @@ public class ActivityInfo extends ComponentInfo */ public String parentActivityName; - /** - * Value indicating if the activity is resizeable to any dimension. - * See {@link android.R.attr#resizeableActivity}. - * @hide - */ - public boolean resizeable; - - /** - * Value indicating if the activity is supports picture-in-picture form of multi-window mode. - * See {@link android.R.attr#supportsPictureInPicture}. - * @hide - */ - public boolean supportsPip; - /** @hide */ public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0; /** @hide */ @@ -735,8 +744,6 @@ public class ActivityInfo extends ComponentInfo uiOptions = orig.uiOptions; parentActivityName = orig.parentActivityName; maxRecents = orig.maxRecents; - resizeable = orig.resizeable; - supportsPip = orig.supportsPip; lockTaskLaunchMode = orig.lockTaskLaunchMode; layout = orig.layout; } @@ -791,7 +798,6 @@ public class ActivityInfo extends ComponentInfo pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions)); } if ((flags&DUMP_FLAG_DETAILS) != 0) { - pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip); pw.println(prefix + "lockTaskLaunchMode=" + lockTaskLaunchModeToString(lockTaskLaunchMode)); } @@ -829,8 +835,6 @@ public class ActivityInfo extends ComponentInfo dest.writeString(parentActivityName); dest.writeInt(persistableMode); dest.writeInt(maxRecents); - dest.writeInt(resizeable ? 1 : 0); - dest.writeInt(supportsPip ? 1 : 0); dest.writeInt(lockTaskLaunchMode); if (layout != null) { dest.writeInt(1); @@ -871,8 +875,6 @@ public class ActivityInfo extends ComponentInfo parentActivityName = source.readString(); persistableMode = source.readInt(); maxRecents = source.readInt(); - resizeable = (source.readInt() == 1); - supportsPip = (source.readInt() == 1); lockTaskLaunchMode = source.readInt(); if (source.readInt() == 1) { layout = new Layout(source); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index f445cf8208b6..a14523174b35 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3223,12 +3223,19 @@ public class PackageParser { a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING; } - a.info.resizeable = sa.getBoolean( - R.styleable.AndroidManifestActivity_resizeableActivity, - owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N); + if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity, + owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N)) { + a.info.flags |= ActivityInfo.FLAG_RESIZEABLE; - a.info.supportsPip = a.info.resizeable ? sa.getBoolean( - R.styleable.AndroidManifestActivity_supportsPictureInPicture, false) : false; + if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture, + false)) { + a.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; + } + } + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) { + a.info.flags |= ActivityInfo.FLAG_ALWAYS_FOCUSABLE; + } a.info.screenOrientation = sa.getInt( R.styleable.AndroidManifestActivity_screenOrientation, diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 7a379d50ba05..2ef082cbd3dd 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -101,11 +101,14 @@ interface IWindowManager * the task doesn't exist yet. * @param configuration Configuration that is being used with this task. * @param cropWindowsToStack True if the app windows should be cropped to the stack bounds. + * @param alwaysFocusable True if the app windows are always focusable regardless of the stack + * they are in. */ void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, - in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack); + in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack, + boolean alwaysFocusable); /** * * @param token The token we are adding to the input task Id. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 2a1108155f17..58a77e809235 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1857,6 +1857,11 @@ <attr name="lockTaskMode" /> <attr name="showForAllUsers" /> <attr name="encryptionAware" /> + <!-- @hide This activity is always focusable regardless of if it is in a task/stack whose + activities are normally not focusable. + For example, {@link android.R.attr#supportsPictureInPicture} activities are placed + in a task/stack that isn't focusable. This flag allows them to be focusable.--> + <attr name="alwaysFocusable" format="boolean" /> </declare-styleable> <!-- The <code>activity-alias</code> tag declares a new diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 295fea053d0c..05b3800e7687 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7274,7 +7274,7 @@ public final class ActivityManagerService extends ActivityManagerNative + "Can't find activity for token=" + token); } - if (!r.info.supportsPip) { + if (!r.supportsPictureInPicture()) { throw new IllegalArgumentException("enterPictureInPictureMode: " + "Picture-In-Picture not supported for r=" + r); } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index f0df0c43763a..4fb87c3a2660 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -18,6 +18,9 @@ package com.android.server.am; import static android.app.ActivityManager.StackId; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; +import static android.content.pm.ActivityInfo.FLAG_RESIZEABLE; +import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; @@ -741,7 +744,19 @@ final class ActivityRecord { } boolean isFocusable() { - return StackId.canReceiveKeys(task.stack.mStackId); + return StackId.canReceiveKeys(task.stack.mStackId) || isAlwaysFocusable(); + } + + boolean isResizeable() { + return (info.flags & FLAG_RESIZEABLE) != 0; + } + + boolean supportsPictureInPicture() { + return (info.flags & FLAG_SUPPORTS_PICTURE_IN_PICTURE) != 0; + } + + boolean isAlwaysFocusable() { + return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0; } void makeFinishingLocked() { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 9a6a7f72d751..4e9d1b1a128d 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -4749,9 +4749,9 @@ final class ActivityStack { task.updateOverrideConfiguration(bounds); mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, - (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, - r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind, - bounds, task.mOverrideConfig, !r.isHomeActivity()); + (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges, + task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig, + !r.isHomeActivity(), r.isAlwaysFocusable()); mWindowManager.setTaskResizeable(task.taskId, task.mResizeable); r.taskConfigOverride = task.mOverrideConfig; } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 688243d48fa1..043a2e779428 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1375,7 +1375,7 @@ public final class ActivityStackSupervisor implements DisplayListener { Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) { Rect newBounds = null; - if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) { + if (options != null && (r.isResizeable() || (inTask != null && inTask.mResizeable))) { if (canUseActivityOptionsLaunchBounds(options)) { newBounds = options.getLaunchBounds(); } @@ -2172,7 +2172,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return false; } - if (!mService.mForceResizableActivities && !r.info.supportsPip) { + if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) { Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for " + " r=" + r); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index bcb22155dbad..2794f74ea457 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1444,7 +1444,7 @@ public class ActivityStarter { final boolean canUseFocusedStack = focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID || focusedStackId == DOCKED_STACK_ID - || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable); + || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeable()); if (canUseFocusedStack && (!newTask || mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index e77bb20f654f..c97d09c2ebbd 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -23,6 +23,7 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; +import static android.content.pm.ActivityInfo.FLAG_RESIZEABLE; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; @@ -441,7 +442,7 @@ final class TaskRecord { } else { autoRemoveRecents = false; } - mResizeable = info.resizeable || mService.mForceResizableActivities; + mResizeable = (info.flags & FLAG_RESIZEABLE) != 0 || mService.mForceResizableActivities; mLockTaskMode = info.lockTaskLaunchMode; mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0; setLockTaskAuth(); @@ -697,7 +698,7 @@ final class TaskRecord { if (mActivities.isEmpty()) { taskType = r.mActivityType; if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivities) { - mResizeable = r.info.resizeable; + mResizeable = r.isResizeable(); } isPersistable = r.isPersistable(); mCallingUid = r.launchedFromUid; diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index b49641fb6ab6..9b9f14bee982 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.StackId; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; @@ -123,6 +124,8 @@ class AppWindowToken extends WindowToken { // True if the windows associated with this token should be cropped to their stack bounds. boolean mCropWindowsToStack; + boolean mAlwaysFocusable; + AppWindowToken(WindowManagerService _service, IApplicationToken _token, boolean _voiceInteraction) { super(_service, _token.asBinder(), @@ -256,8 +259,8 @@ class AppWindowToken extends WindowToken { return candidate; } - boolean stackCanReceiveKeys() { - return (windows.size() > 0) ? windows.get(windows.size() - 1).stackCanReceiveKeys() : false; + boolean windowsAreFocusable() { + return StackId.canReceiveKeys(mTask.mStack.mStackId) || mAlwaysFocusable; } boolean isVisible() { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6385caac3fa9..06e2e30d6050 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3203,7 +3203,8 @@ public class WindowManagerService extends IWindowManager.Stub public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, - Rect taskBounds, Configuration config, boolean cropWindowsToStack) { + Rect taskBounds, Configuration config, boolean cropWindowsToStack, + boolean alwaysFocusable) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addAppToken()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3238,6 +3239,7 @@ public class WindowManagerService extends IWindowManager.Stub (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0; atoken.mLaunchTaskBehind = launchTaskBehind; atoken.mCropWindowsToStack = cropWindowsToStack; + atoken.mAlwaysFocusable = alwaysFocusable; if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken + " to stack=" + stackId + " task=" + taskId + " at " + addPos); @@ -9078,8 +9080,8 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken == token) { break; } - if (mFocusedApp == token && token.stackCanReceiveKeys()) { - // Whoops, we are below the focused app whose stack can receive keys... + if (mFocusedApp == token && token.windowsAreFocusable()) { + // Whoops, we are below the focused app whose windows are focusable... // No focus for you!!! if (localLOGV || DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Reached focused app=" + mFocusedApp); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 733bc2993d90..a825e80e26b2 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1558,12 +1558,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { return isVisibleOrAdding() && (mViewVisibility == View.VISIBLE) && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) - && stackCanReceiveKeys(); - } - - boolean stackCanReceiveKeys() { - final TaskStack stack = getStack(); - return stack != null && StackId.canReceiveKeys(stack.mStackId); + && (mAppToken == null || mAppToken.windowsAreFocusable()); } @Override diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 3c2659f6ab59..b78fd494e555 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -93,7 +93,7 @@ public class WindowManagerPermissionTests extends TestCase { try { mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null, - Configuration.EMPTY, false); + Configuration.EMPTY, false, false); fail("IWindowManager.addAppToken did not throw SecurityException as" + " expected"); } catch (SecurityException e) { |