Adding support for PIP actions.
- Introduced generic RemoteAction to represents an action that can be made across processes with an icon and text description based on a Notification action. - Modified PinnedStackController to ensure that it notifies the listeners from the source of truth, this ensures that SysUI is in the right state if killed and re-registers itself. Test: Enable menu & minimize in SystemUI tuner. Test: android.server.cts.ActivityManagerPinnedStackTests Test: #testNumPipActions Change-Id: I5b5d0cf9de3f06b5687337d59cfb91e17355bdb1 Signed-off-by: Winson Chung <winsonc@google.com>
This commit is contained in:
@@ -45,6 +45,7 @@ import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
@@ -2019,7 +2020,29 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the aspect ratio of the current picture-in-picture activity.
|
||||
* Requests to the system that the activity can be automatically put into picture-in-picture
|
||||
* mode when the user leaves the activity causing it normally to be hidden. Generally, this
|
||||
* happens when another task is brought to the forground or the task containing this activity
|
||||
* is moved to the background. This is a *not* a guarantee that the activity will actually be
|
||||
* put in picture-in-picture mode, and depends on a number of factors, including whether there
|
||||
* is already something in picture-in-picture.
|
||||
*
|
||||
* @param enterPictureInPictureOnMoveToBg whether or not this activity can automatically enter
|
||||
* picture-in-picture
|
||||
*/
|
||||
public void enterPictureInPictureModeOnMoveToBackground(
|
||||
boolean enterPictureInPictureOnMoveToBg) {
|
||||
try {
|
||||
ActivityManagerNative.getDefault().enterPictureInPictureModeOnMoveToBackground(mToken,
|
||||
enterPictureInPictureOnMoveToBg);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the aspect ratio of the current picture-in-picture activity if this activity is
|
||||
* already in picture-in-picture mode, or sets it to be used later if
|
||||
* {@link #enterPictureInPictureModeOnMoveToBackground(boolean)} is requested.
|
||||
*
|
||||
* @param aspectRatio the new aspect ratio of the picture-in-picture.
|
||||
*/
|
||||
@@ -2031,23 +2054,19 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests to the system that the activity can be automatically put into picture-in-picture
|
||||
* mode when the user leaves the activity causing it normally to be hidden. This is a *not*
|
||||
* a guarantee that the activity will actually be put in picture-in-picture mode, and depends
|
||||
* on a number of factors, including whether there is already something in picture-in-picture.
|
||||
* Updates the set of user actions associated with the picture-in-picture activity.
|
||||
*
|
||||
* If {@param enterPictureInPictureOnMoveToBg} is true, then you may also call
|
||||
* {@link #setPictureInPictureAspectRatio(float)} to specify the aspect ratio to automatically
|
||||
* enter picture-in-picture with.
|
||||
*
|
||||
* @param enterPictureInPictureOnMoveToBg whether or not this activity can automatically enter
|
||||
* picture-in-picture
|
||||
* @param actions the new actions for picture-in-picture (can be null to reset the set of
|
||||
* actions). The maximum number of actions that will be displayed on this device
|
||||
* is defined by {@link ActivityManager#getMaxNumPictureInPictureActions()}.
|
||||
*/
|
||||
public void enterPictureInPictureModeOnMoveToBackground(
|
||||
boolean enterPictureInPictureOnMoveToBg) {
|
||||
public void setPictureInPictureActions(List<RemoteAction> actions) {
|
||||
try {
|
||||
ActivityManagerNative.getDefault().enterPictureInPictureModeOnMoveToBackground(mToken,
|
||||
enterPictureInPictureOnMoveToBg);
|
||||
if (actions == null) {
|
||||
actions = new ArrayList<>();
|
||||
}
|
||||
ActivityManagerNative.getDefault().setPictureInPictureActions(mToken,
|
||||
new ParceledListSlice<RemoteAction>(actions));
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,9 @@ public class ActivityManager {
|
||||
|
||||
private static int gMaxRecentTasks = -1;
|
||||
|
||||
private static final int NUM_ALLOWED_PIP_ACTIONS = 3;
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
|
||||
private static volatile boolean sSystemReady = false;
|
||||
|
||||
@@ -491,7 +492,6 @@ public class ActivityManager {
|
||||
|
||||
/*package*/ ActivityManager(Context context, Handler handler) {
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1011,6 +1011,14 @@ public class ActivityManager {
|
||||
com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of actions that will be displayed in the picture-in-picture UI when
|
||||
* the user interacts with the activity currently in picture-in-picture mode.
|
||||
*/
|
||||
public static int getMaxNumPictureInPictureActions() {
|
||||
return NUM_ALLOWED_PIP_ACTIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information you can set and retrieve about the current activity within the recent task list.
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,8 @@ import android.service.voice.IVoiceInteractionSession;
|
||||
|
||||
import com.android.internal.app.IVoiceInteractor;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -61,6 +63,36 @@ public abstract class ActivityManagerInternal {
|
||||
*/
|
||||
public static final int APP_TRANSITION_TIMEOUT = 3;
|
||||
|
||||
/**
|
||||
* Class to hold deferred properties to apply for picture-in-picture for a given activity.
|
||||
*/
|
||||
public static class PictureInPictureArguments {
|
||||
/**
|
||||
* The expected aspect ratio of the picture-in-picture.
|
||||
*/
|
||||
public float aspectRatio;
|
||||
|
||||
/**
|
||||
* The set of actions that are associated with this activity when in picture in picture.
|
||||
*/
|
||||
public List<RemoteAction> userActions = new ArrayList<>();
|
||||
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.println(prefix + "aspectRatio=" + aspectRatio);
|
||||
if (userActions.isEmpty()) {
|
||||
pw.println(prefix + " userActions=[]");
|
||||
} else {
|
||||
pw.println(prefix + " userActions=[");
|
||||
for (int i = 0; i < userActions.size(); i++) {
|
||||
RemoteAction action = userActions.get(i);
|
||||
pw.print(prefix + " Action[" + i + "]: ");
|
||||
action.dump("", pw);
|
||||
}
|
||||
pw.println(prefix + " ]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grant Uri permissions from one app to another. This method only extends
|
||||
* permission grants if {@code callingUid} has permission to them.
|
||||
|
||||
@@ -474,6 +474,7 @@ interface IActivityManager {
|
||||
void enterPictureInPictureModeOnMoveToBackground(in IBinder token,
|
||||
boolean enterPictureInPictureOnMoveToBg);
|
||||
void setPictureInPictureAspectRatio(in IBinder token, float aspectRatio);
|
||||
void setPictureInPictureActions(in IBinder token, in ParceledListSlice actions);
|
||||
void activityRelaunched(in IBinder token);
|
||||
IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
|
||||
/**
|
||||
|
||||
19
core/java/android/app/RemoteAction.aidl
Normal file
19
core/java/android/app/RemoteAction.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2016, 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 android.app;
|
||||
|
||||
parcelable RemoteAction;
|
||||
159
core/java/android/app/RemoteAction.java
Normal file
159
core/java/android/app/RemoteAction.java
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 android.app;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Represents a remote action that can be called from another process. The action can have an
|
||||
* associated visualization including metadata like an icon or title.
|
||||
*/
|
||||
public final class RemoteAction implements Parcelable {
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when an action is invoked.
|
||||
*/
|
||||
public interface OnActionListener {
|
||||
/**
|
||||
* Called when the associated action is invoked.
|
||||
*
|
||||
* @param action The action that was invoked.
|
||||
*/
|
||||
void onAction(RemoteAction action);
|
||||
}
|
||||
|
||||
private static final String TAG = "RemoteAction";
|
||||
|
||||
private static final int MESSAGE_ACTION_INVOKED = 1;
|
||||
|
||||
private final Icon mIcon;
|
||||
private final CharSequence mTitle;
|
||||
private final CharSequence mContentDescription;
|
||||
private OnActionListener mActionCallback;
|
||||
private final Messenger mMessenger;
|
||||
|
||||
RemoteAction(Parcel in) {
|
||||
mIcon = Icon.CREATOR.createFromParcel(in);
|
||||
mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||||
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||||
mMessenger = in.readParcelable(Messenger.class.getClassLoader());
|
||||
}
|
||||
|
||||
public RemoteAction(@NonNull Icon icon, @NonNull CharSequence title,
|
||||
@NonNull CharSequence contentDescription, @NonNull OnActionListener callback) {
|
||||
if (icon == null || title == null || contentDescription == null || callback == null) {
|
||||
throw new IllegalArgumentException("Expected icon, title, content description and " +
|
||||
"action callback");
|
||||
}
|
||||
mIcon = icon;
|
||||
mTitle = title;
|
||||
mContentDescription = contentDescription;
|
||||
mActionCallback = callback;
|
||||
mMessenger = new Messenger(new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MESSAGE_ACTION_INVOKED:
|
||||
mActionCallback.onAction(RemoteAction.this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an icon representing the action.
|
||||
*/
|
||||
public @NonNull Icon getIcon() {
|
||||
return mIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an title representing the action.
|
||||
*/
|
||||
public @NonNull CharSequence getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a content description representing the action.
|
||||
*/
|
||||
public @NonNull CharSequence getContentDescription() {
|
||||
return mContentDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message that the action was invoked.
|
||||
* @hide
|
||||
*/
|
||||
public void sendActionInvoked() {
|
||||
Message m = Message.obtain();
|
||||
m.what = MESSAGE_ACTION_INVOKED;
|
||||
try {
|
||||
mMessenger.send(m);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not send action-invoked", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteAction clone() {
|
||||
return new RemoteAction(mIcon, mTitle, mContentDescription, mActionCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
mIcon.writeToParcel(out, 0);
|
||||
TextUtils.writeToParcel(mTitle, out, flags);
|
||||
TextUtils.writeToParcel(mContentDescription, out, flags);
|
||||
out.writeParcelable(mMessenger, flags);
|
||||
}
|
||||
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
pw.print(prefix);
|
||||
pw.print("title=" + mTitle);
|
||||
pw.print(" contentDescription=" + mContentDescription);
|
||||
pw.print(" icon=" + mIcon);
|
||||
pw.println();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<RemoteAction> CREATOR =
|
||||
new Parcelable.Creator<RemoteAction>() {
|
||||
public RemoteAction createFromParcel(Parcel in) {
|
||||
return new RemoteAction(in);
|
||||
}
|
||||
public RemoteAction[] newArray(int size) {
|
||||
return new RemoteAction[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.view.IPinnedStackController;
|
||||
|
||||
/**
|
||||
* Listener for changes to the pinned stack made by the WindowManager.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
* Listener for changes to the pinned stack made by the WindowManager.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IPinnedStackListener {
|
||||
|
||||
/**
|
||||
@@ -36,4 +37,24 @@ oneway interface IPinnedStackListener {
|
||||
* is first registered to allow the listener to synchronized its state with the controller.
|
||||
*/
|
||||
void onBoundsChanged(boolean adjustedForIme);
|
||||
|
||||
/**
|
||||
* Called when window manager decides to adjust the minimized state, or when the listener
|
||||
* is first registered to allow the listener to synchronized its state with the controller.
|
||||
*/
|
||||
void onMinimizedStateChanged(boolean isMinimized);
|
||||
|
||||
/**
|
||||
* Called when window manager decides to adjust the snap-to-edge state, which determines whether
|
||||
* to snap only to the corners of the screen or to the closest edge. It is called when the
|
||||
* listener is first registered to allow the listener to synchronized its state with the
|
||||
* controller.
|
||||
*/
|
||||
void onSnapToEdgeStateChanged(boolean isSnapToEdge);
|
||||
|
||||
/**
|
||||
* Called when the set of actions for the current PiP activity changes, or when the listener
|
||||
* is first registered to allow the listener to synchronized its state with the controller.
|
||||
*/
|
||||
void onActionsChanged(in ParceledListSlice actions);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user