Merge "Set default throttle to 30m."
diff --git a/api/current.txt b/api/current.txt
index b59af05..7db661b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -118,6 +118,7 @@
field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+ field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -5677,10 +5678,11 @@
}
public final class RemoteAction implements android.os.Parcelable {
- ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener);
+ ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.RemoteAction clone();
method public int describeContents();
method public void dump(java.lang.String, java.io.PrintWriter);
+ method public android.app.PendingIntent getActionIntent();
method public java.lang.CharSequence getContentDescription();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getTitle();
@@ -5688,10 +5690,6 @@
field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
}
- public static abstract interface RemoteAction.OnActionListener {
- method public abstract void onAction(android.app.RemoteAction);
- }
-
public final class RemoteInput implements android.os.Parcelable {
method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
@@ -44368,6 +44366,7 @@
ctor public View(android.content.Context, android.util.AttributeSet, int);
ctor public View(android.content.Context, android.util.AttributeSet, int, int);
method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
+ method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
@@ -45063,6 +45062,7 @@
public static class View.AccessibilityDelegate {
ctor public View.AccessibilityDelegate();
+ method public void addExtraDataToAccessibilityNodeInfo(android.view.View, android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(android.view.View);
method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
@@ -46183,6 +46183,7 @@
method public android.view.accessibility.AccessibilityNodeInfo focusSearch(int);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getActionList();
method public deprecated int getActions();
+ method public java.util.List<java.lang.String> getAvailableExtraData();
method public void getBoundsInParent(android.graphics.Rect);
method public void getBoundsInScreen(android.graphics.Rect);
method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
@@ -46239,11 +46240,13 @@
method public boolean performAction(int, android.os.Bundle);
method public void recycle();
method public boolean refresh();
+ method public boolean refreshWithExtraData(java.lang.String, android.os.Bundle);
method public deprecated void removeAction(int);
method public boolean removeAction(android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction);
method public boolean removeChild(android.view.View);
method public boolean removeChild(android.view.View, int);
method public void setAccessibilityFocused(boolean);
+ method public void setAvailableExtraData(java.util.List<java.lang.String>);
method public void setBoundsInParent(android.graphics.Rect);
method public void setBoundsInScreen(android.graphics.Rect);
method public void setCanOpenPopup(boolean);
@@ -46326,6 +46329,9 @@
field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
field public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+ field public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ field public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+ field public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
field public static final int FOCUS_INPUT = 1; // 0x1
field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
@@ -46407,6 +46413,7 @@
public abstract class AccessibilityNodeProvider {
ctor public AccessibilityNodeProvider();
+ method public void addExtraDataToAccessibilityNodeInfo(int, android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 70a7a2d..f77f6bb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -206,6 +206,7 @@
field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
field public static final java.lang.String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+ field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
@@ -5873,10 +5874,11 @@
}
public final class RemoteAction implements android.os.Parcelable {
- ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener);
+ ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.RemoteAction clone();
method public int describeContents();
method public void dump(java.lang.String, java.io.PrintWriter);
+ method public android.app.PendingIntent getActionIntent();
method public java.lang.CharSequence getContentDescription();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getTitle();
@@ -5884,10 +5886,6 @@
field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
}
- public static abstract interface RemoteAction.OnActionListener {
- method public abstract void onAction(android.app.RemoteAction);
- }
-
public final class RemoteInput implements android.os.Parcelable {
method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
@@ -47775,6 +47773,7 @@
ctor public View(android.content.Context, android.util.AttributeSet, int);
ctor public View(android.content.Context, android.util.AttributeSet, int, int);
method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
+ method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
@@ -48470,6 +48469,7 @@
public static class View.AccessibilityDelegate {
ctor public View.AccessibilityDelegate();
+ method public void addExtraDataToAccessibilityNodeInfo(android.view.View, android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(android.view.View);
method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
@@ -49593,6 +49593,7 @@
method public android.view.accessibility.AccessibilityNodeInfo focusSearch(int);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getActionList();
method public deprecated int getActions();
+ method public java.util.List<java.lang.String> getAvailableExtraData();
method public void getBoundsInParent(android.graphics.Rect);
method public void getBoundsInScreen(android.graphics.Rect);
method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
@@ -49649,11 +49650,13 @@
method public boolean performAction(int, android.os.Bundle);
method public void recycle();
method public boolean refresh();
+ method public boolean refreshWithExtraData(java.lang.String, android.os.Bundle);
method public deprecated void removeAction(int);
method public boolean removeAction(android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction);
method public boolean removeChild(android.view.View);
method public boolean removeChild(android.view.View, int);
method public void setAccessibilityFocused(boolean);
+ method public void setAvailableExtraData(java.util.List<java.lang.String>);
method public void setBoundsInParent(android.graphics.Rect);
method public void setBoundsInScreen(android.graphics.Rect);
method public void setCanOpenPopup(boolean);
@@ -49736,6 +49739,9 @@
field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
field public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+ field public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ field public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+ field public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
field public static final int FOCUS_INPUT = 1; // 0x1
field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
@@ -49817,6 +49823,7 @@
public abstract class AccessibilityNodeProvider {
ctor public AccessibilityNodeProvider();
+ method public void addExtraDataToAccessibilityNodeInfo(int, android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 7597585..37da0d3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -118,6 +118,7 @@
field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+ field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -5688,10 +5689,11 @@
}
public final class RemoteAction implements android.os.Parcelable {
- ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener);
+ ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.RemoteAction clone();
method public int describeContents();
method public void dump(java.lang.String, java.io.PrintWriter);
+ method public android.app.PendingIntent getActionIntent();
method public java.lang.CharSequence getContentDescription();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getTitle();
@@ -5699,10 +5701,6 @@
field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
}
- public static abstract interface RemoteAction.OnActionListener {
- method public abstract void onAction(android.app.RemoteAction);
- }
-
public final class RemoteInput implements android.os.Parcelable {
method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
@@ -44674,6 +44672,7 @@
ctor public View(android.content.Context, android.util.AttributeSet, int);
ctor public View(android.content.Context, android.util.AttributeSet, int, int);
method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
+ method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
@@ -45370,6 +45369,7 @@
public static class View.AccessibilityDelegate {
ctor public View.AccessibilityDelegate();
+ method public void addExtraDataToAccessibilityNodeInfo(android.view.View, android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(android.view.View);
method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
@@ -46494,6 +46494,7 @@
method public android.view.accessibility.AccessibilityNodeInfo focusSearch(int);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getActionList();
method public deprecated int getActions();
+ method public java.util.List<java.lang.String> getAvailableExtraData();
method public void getBoundsInParent(android.graphics.Rect);
method public void getBoundsInScreen(android.graphics.Rect);
method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
@@ -46550,11 +46551,13 @@
method public boolean performAction(int, android.os.Bundle);
method public void recycle();
method public boolean refresh();
+ method public boolean refreshWithExtraData(java.lang.String, android.os.Bundle);
method public deprecated void removeAction(int);
method public boolean removeAction(android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction);
method public boolean removeChild(android.view.View);
method public boolean removeChild(android.view.View, int);
method public void setAccessibilityFocused(boolean);
+ method public void setAvailableExtraData(java.util.List<java.lang.String>);
method public void setBoundsInParent(android.graphics.Rect);
method public void setBoundsInScreen(android.graphics.Rect);
method public void setCanOpenPopup(boolean);
@@ -46638,6 +46641,9 @@
field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
field public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+ field public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ field public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+ field public static final java.lang.String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
field public static final int FOCUS_INPUT = 1; // 0x1
field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
@@ -46719,6 +46725,7 @@
public abstract class AccessibilityNodeProvider {
ctor public AccessibilityNodeProvider();
+ method public void addExtraDataToAccessibilityNodeInfo(int, android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 5bd37220..7a1931718 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -37,7 +37,8 @@
boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int flags, long threadId);
+ IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
+ in Bundle arguments);
boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId,
String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9db2b92..16989c0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1896,7 +1896,7 @@
pi.getResDir(),
pi.getSplitResDirs(),
pi.getOverlayDirs(),
- pi.getSharedLibraries(),
+ pi.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfig,
compatInfo,
@@ -2180,7 +2180,7 @@
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
- packageInfo.getSharedLibraries(),
+ packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 17f5edd..7ed96af 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -311,7 +311,7 @@
}
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
- splitPaths, mOverlayDirs, mSharedLibraries,
+ splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
getClassLoader());
}
@@ -923,10 +923,6 @@
return mOverlayDirs;
}
- public String[] getSharedLibraries() {
- return mSharedLibraries;
- }
-
public String getDataDir() {
return mDataDir;
}
@@ -958,7 +954,7 @@
}
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
- splitPaths, mOverlayDirs, mSharedLibraries,
+ splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
getClassLoader());
}
diff --git a/core/java/android/app/RemoteAction.java b/core/java/android/app/RemoteAction.java
index a37680f..5958bc1 100644
--- a/core/java/android/app/RemoteAction.java
+++ b/core/java/android/app/RemoteAction.java
@@ -35,55 +35,30 @@
*/
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;
+ private final PendingIntent mActionIntent;
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());
+ mActionIntent = PendingIntent.CREATOR.createFromParcel(in);
}
public RemoteAction(@NonNull Icon icon, @NonNull CharSequence title,
- @NonNull CharSequence contentDescription, @NonNull OnActionListener callback) {
- if (icon == null || title == null || contentDescription == null || callback == null) {
+ @NonNull CharSequence contentDescription, @NonNull PendingIntent intent) {
+ if (icon == null || title == null || contentDescription == null || intent == 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;
- }
- }
- });
+ mActionIntent = intent;
}
/**
@@ -108,22 +83,15 @@
}
/**
- * Sends a message that the action was invoked.
- * @hide
+ * Return the action intent.
*/
- 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);
- }
+ public @NonNull PendingIntent getActionIntent() {
+ return mActionIntent;
}
@Override
public RemoteAction clone() {
- return new RemoteAction(mIcon, mTitle, mContentDescription, mActionCallback);
+ return new RemoteAction(mIcon, mTitle, mContentDescription, mActionIntent);
}
@Override
@@ -136,7 +104,7 @@
mIcon.writeToParcel(out, 0);
TextUtils.writeToParcel(mTitle, out, flags);
TextUtils.writeToParcel(mContentDescription, out, flags);
- out.writeParcelable(mMessenger, flags);
+ mActionIntent.writeToParcel(out, flags);
}
public void dump(String prefix, PrintWriter pw) {
@@ -144,6 +112,7 @@
pw.print("title=" + mTitle);
pw.print(" contentDescription=" + mContentDescription);
pw.print(" icon=" + mIcon);
+ pw.print(" action=" + mActionIntent.getIntent());
pw.println();
}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 13ee48e..4ff4840 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -17,9 +17,11 @@
package android.view;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Region;
import android.os.Binder;
import android.os.Bundle;
@@ -29,7 +31,6 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
-import android.text.TextUtils;
import android.text.style.AccessibilityClickableSpan;
import android.text.style.ClickableSpan;
import android.util.LongSparseArray;
@@ -93,6 +94,19 @@
mPrefetcher = new AccessibilityNodePrefetcher();
}
+ private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid) {
+ // If the interrogation is performed by the same thread as the main UI
+ // thread in this process, set the message as a static reference so
+ // after this call completes the same thread but in the interrogating
+ // client can handle the message to generate the result.
+ if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
+ AccessibilityInteractionClient.getInstanceForThread(
+ interrogatingTid).setSameThreadMessage(message);
+ } else {
+ mHandler.sendMessage(message);
+ }
+ }
+
private boolean isShown(View view) {
// The first two checks are made also made by isShown() which
// however traverses the tree up to the parent to catch that.
@@ -106,7 +120,7 @@
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
long accessibilityNodeId, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
- long interrogatingTid, MagnificationSpec spec) {
+ long interrogatingTid, MagnificationSpec spec, Bundle arguments) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
message.arg1 = flags;
@@ -118,18 +132,10 @@
args.arg1 = callback;
args.arg2 = spec;
args.arg3 = interactiveRegion;
+ args.arg4 = arguments;
message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
- } else {
- mHandler.sendMessage(message);
- }
+ scheduleMessage(message, interrogatingPid, interrogatingTid);
}
private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
@@ -143,6 +149,7 @@
(IAccessibilityInteractionConnectionCallback) args.arg1;
final MagnificationSpec spec = (MagnificationSpec) args.arg2;
final Region interactiveRegion = (Region) args.arg3;
+ final Bundle arguments = (Bundle) args.arg4;
args.recycle();
@@ -160,29 +167,12 @@
root = findViewByAccessibilityId(accessibilityViewId);
}
if (root != null && isShown(root)) {
- mPrefetcher.prefetchAccessibilityNodeInfos(root, virtualDescendantId, flags, infos);
+ mPrefetcher.prefetchAccessibilityNodeInfos(
+ root, virtualDescendantId, flags, infos, arguments);
}
} finally {
- try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
- applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
- // Recycle if called from another process. Specs are cached in the
- // system process and obtained from a pool when read from parcel.
- if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
- spec.recycle();
- }
- adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
- callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
- infos.clear();
- } catch (RemoteException re) {
- /* ignore - the other side will time out */
- }
-
- // Recycle if called from the same process. Regions are obtained in
- // the system process and instantiated when read from parcel.
- if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
- interactiveRegion.recycle();
- }
+ updateInfosForViewportAndReturnFindNodeResult(
+ infos, callback, interactionId, spec, interactiveRegion);
}
}
@@ -201,19 +191,9 @@
args.arg2 = spec;
args.arg3 = viewId;
args.arg4 = interactiveRegion;
-
message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
- } else {
- mHandler.sendMessage(message);
- }
+ scheduleMessage(message, interrogatingPid, interrogatingTid);
}
private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
@@ -227,7 +207,6 @@
final MagnificationSpec spec = (MagnificationSpec) args.arg2;
final String viewId = (String) args.arg3;
final Region interactiveRegion = (Region) args.arg4;
-
args.recycle();
final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
@@ -257,25 +236,8 @@
mAddNodeInfosForViewId.reset();
}
} finally {
- try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
- applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
- // Recycle if called from another process. Specs are cached in the
- // system process and obtained from a pool when read from parcel.
- if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
- spec.recycle();
- }
- adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
- callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
- } catch (RemoteException re) {
- /* ignore - the other side will time out */
- }
-
- // Recycle if called from the same process. Regions are obtained in
- // the system process and instantiated when read from parcel.
- if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
- interactiveRegion.recycle();
- }
+ updateInfosForViewportAndReturnFindNodeResult(
+ infos, callback, interactionId, spec, interactiveRegion);
}
}
@@ -297,16 +259,7 @@
args.arg4 = interactiveRegion;
message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
- } else {
- mHandler.sendMessage(message);
- }
+ scheduleMessage(message, interrogatingPid, interrogatingTid);
}
private void findAccessibilityNodeInfosByTextUiThread(Message message) {
@@ -375,31 +328,14 @@
}
}
} finally {
- try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
- applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
- // Recycle if called from another process. Specs are cached in the
- // system process and obtained from a pool when read from parcel.
- if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
- spec.recycle();
- }
- adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
- callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
- } catch (RemoteException re) {
- /* ignore - the other side will time out */
- }
-
- // Recycle if called from the same process. Regions are obtained in
- // the system process and instantiated when read from parcel.
- if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
- interactiveRegion.recycle();
- }
+ updateInfosForViewportAndReturnFindNodeResult(
+ infos, callback, interactionId, spec, interactiveRegion);
}
}
public void findFocusClientThread(long accessibilityNodeId, int focusType,
Region interactiveRegion, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid, MagnificationSpec spec) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FIND_FOCUS;
@@ -416,16 +352,7 @@
message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
- } else {
- mHandler.sendMessage(message);
- }
+ scheduleMessage(message, interrogatingPid, interrogatingTid);
}
private void findFocusUiThread(Message message) {
@@ -497,31 +424,14 @@
}
}
} finally {
- try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
- applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
- // Recycle if called from another process. Specs are cached in the
- // system process and obtained from a pool when read from parcel.
- if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
- spec.recycle();
- }
- adjustIsVisibleToUserIfNeeded(focused, interactiveRegion);
- callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
- } catch (RemoteException re) {
- /* ignore - the other side will time out */
- }
-
- // Recycle if called from the same process. Regions are obtained in
- // the system process and instantiated when read from parcel.
- if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
- interactiveRegion.recycle();
- }
+ updateInfoForViewportAndReturnFindNodeResult(
+ focused, callback, interactionId, spec, interactiveRegion);
}
}
public void focusSearchClientThread(long accessibilityNodeId, int direction,
Region interactiveRegion, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid, MagnificationSpec spec) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FOCUS_SEARCH;
@@ -537,16 +447,7 @@
message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
- } else {
- mHandler.sendMessage(message);
- }
+ scheduleMessage(message, interrogatingPid, interrogatingTid);
}
private void focusSearchUiThread(Message message) {
@@ -582,31 +483,14 @@
}
}
} finally {
- try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
- applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
- // Recycle if called from another process. Specs are cached in the
- // system process and obtained from a pool when read from parcel.
- if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
- spec.recycle();
- }
- adjustIsVisibleToUserIfNeeded(next, interactiveRegion);
- callback.setFindAccessibilityNodeInfoResult(next, interactionId);
- } catch (RemoteException re) {
- /* ignore - the other side will time out */
- }
-
- // Recycle if called from the same process. Regions are obtained in
- // the system process and instantiated when read from parcel.
- if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
- interactiveRegion.recycle();
- }
+ updateInfoForViewportAndReturnFindNodeResult(
+ next, callback, interactionId, spec, interactiveRegion);
}
}
public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Bundle arguments, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
+ IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
@@ -622,16 +506,7 @@
message.obj = args;
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
- } else {
- mHandler.sendMessage(message);
- }
+ scheduleMessage(message, interrogatingPid, interrogatingTid);
}
private void performAccessibilityActionUiThread(Message message) {
@@ -742,26 +617,6 @@
}
}
- private void applyAppScaleAndMagnificationSpecIfNeeded(Point point,
- MagnificationSpec spec) {
- final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
- if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
- return;
- }
-
- if (applicationScale != 1.0f) {
- point.x *= applicationScale;
- point.y *= applicationScale;
- }
-
- if (spec != null) {
- point.x *= spec.scale;
- point.y *= spec.scale;
- point.x += (int) spec.offsetX;
- point.y += (int) spec.offsetY;
- }
- }
-
private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
MagnificationSpec spec) {
if (info == null) {
@@ -791,6 +646,25 @@
info.setBoundsInParent(boundsInParent);
info.setBoundsInScreen(boundsInScreen);
+ // Scale text locations if they are present
+ if (info.hasExtras()) {
+ Bundle extras = info.getExtras();
+ Parcelable[] textLocations =
+ extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
+ if (textLocations != null) {
+ for (int i = 0; i < textLocations.length; i++) {
+ // Unchecked cast - an app that puts other objects in this bundle with this
+ // key will crash.
+ RectF textLocation = ((RectF) textLocations[i]);
+ textLocation.scale(applicationScale);
+ if (spec != null) {
+ textLocation.scale(spec.scale);
+ textLocation.offset(spec.offsetX, spec.offsetY);
+ }
+ }
+ }
+ }
+
if (spec != null) {
AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
if (attachInfo.mDisplay == null) {
@@ -829,6 +703,53 @@
return (appScale != 1.0f || (spec != null && !spec.isNop()));
}
+ private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
+ IAccessibilityInteractionConnectionCallback callback, int interactionId,
+ MagnificationSpec spec, Region interactiveRegion) {
+ try {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
+ adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
+ callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
+ infos.clear();
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ } finally {
+ recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
+ }
+ }
+
+ private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
+ IAccessibilityInteractionConnectionCallback callback, int interactionId,
+ MagnificationSpec spec, Region interactiveRegion) {
+ try {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
+ adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
+ callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ } finally {
+ recycleMagnificationSpecAndRegionIfNeeded(spec, interactiveRegion);
+ }
+ }
+
+ private void recycleMagnificationSpecAndRegionIfNeeded(MagnificationSpec spec, Region region) {
+ if (android.os.Process.myPid() != Binder.getCallingPid()) {
+ // Specs are cached in the system process and obtained from a pool when read from
+ // a parcel, so only recycle the spec if called from another process.
+ if (spec != null) {
+ spec.recycle();
+ }
+ } else {
+ // Regions are obtained in the system process and instantiated when read from
+ // a parcel, so only recycle the region if caled from the same process.
+ if (region != null) {
+ region.recycle();
+ }
+ }
+ }
+
private boolean handleClickableSpanActionUiThread(
View view, int virtualDescendantId, Bundle arguments) {
Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN);
@@ -872,11 +793,18 @@
private final ArrayList<View> mTempViewList = new ArrayList<View>();
public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
- List<AccessibilityNodeInfo> outInfos) {
+ List<AccessibilityNodeInfo> outInfos, Bundle arguments) {
AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+ // Determine if we'll be populating extra data
+ final String extraDataRequested = (arguments == null) ? null
+ : arguments.getString(AccessibilityNodeInfo.EXTRA_DATA_REQUESTED_KEY);
if (provider == null) {
AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
if (root != null) {
+ if (extraDataRequested != null) {
+ view.addExtraDataToAccessibilityNodeInfo(
+ root, extraDataRequested, arguments);
+ }
outInfos.add(root);
if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
prefetchPredecessorsOfRealNode(view, outInfos);
@@ -889,14 +817,14 @@
}
}
} else {
- final AccessibilityNodeInfo root;
- if (virtualViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- root = provider.createAccessibilityNodeInfo(virtualViewId);
- } else {
- root = provider.createAccessibilityNodeInfo(
- AccessibilityNodeProvider.HOST_VIEW_ID);
- }
+ final int idForRoot = (virtualViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
+ ? AccessibilityNodeProvider.HOST_VIEW_ID : virtualViewId;
+ final AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(idForRoot);
if (root != null) {
+ if (extraDataRequested != null) {
+ provider.addExtraDataToAccessibilityNodeInfo(
+ idForRoot, root, extraDataRequested, arguments);
+ }
outInfos.add(root);
if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
@@ -1202,12 +1130,12 @@
}
private class PrivateHandler extends Handler {
- private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
- private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
- private final static int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
- private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
- private final static int MSG_FIND_FOCUS = 5;
- private final static int MSG_FOCUS_SEARCH = 6;
+ private static final int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
+ private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
+ private static final int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
+ private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
+ private static final int MSG_FIND_FOCUS = 5;
+ private static final int MSG_FOCUS_SEARCH = 6;
public PrivateHandler(Looper looper) {
super(looper);
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 41a13cf..7fde8a6 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -52,7 +52,9 @@
final Rect mFocusedRect = new Rect();
final Rect mOtherRect = new Rect();
final Rect mBestCandidateRect = new Rect();
- final SequentialFocusComparator mSequentialFocusComparator = new SequentialFocusComparator();
+ private final UserSpecifiedFocusComparator mUserSpecifiedFocusComparator =
+ new UserSpecifiedFocusComparator();
+ private final FocusComparator mFocusComparator = new FocusComparator();
private final ArrayList<View> mTempList = new ArrayList<View>();
@@ -225,12 +227,10 @@
View focused, Rect focusedRect, int direction) {
try {
// Note: This sort is stable.
- mSequentialFocusComparator.setRoot(root);
- mSequentialFocusComparator.setIsLayoutRtl(root.isLayoutRtl());
- mSequentialFocusComparator.setFocusables(focusables);
- Collections.sort(focusables, mSequentialFocusComparator);
+ mUserSpecifiedFocusComparator.setFocusables(focusables);
+ Collections.sort(focusables, mUserSpecifiedFocusComparator);
} finally {
- mSequentialFocusComparator.recycle();
+ mUserSpecifiedFocusComparator.recycle();
}
final int count = focusables.size();
@@ -703,36 +703,80 @@
return id != 0 && id != View.NO_ID;
}
- /**
- * Sorts views according to their visual layout and geometry for default tab order.
- * If views are part of a focus chain (nextFocusForwardId), then they are all grouped
- * together. The head of the chain is used to determine the order of the chain and is
- * first in the order and the tail of the chain is the last in the order. The views
- * in the middle of the chain can be arbitrary order.
- * This is used for sequential focus traversal.
- */
- private static final class SequentialFocusComparator implements Comparator<View> {
+ static FocusComparator getFocusComparator(ViewGroup root, boolean isRtl) {
+ FocusComparator comparator = getInstance().mFocusComparator;
+ comparator.setRoot(root);
+ comparator.setIsLayoutRtl(isRtl);
+ return comparator;
+ }
+
+ static final class FocusComparator implements Comparator<View> {
private final Rect mFirstRect = new Rect();
private final Rect mSecondRect = new Rect();
- private ViewGroup mRoot;
+ private ViewGroup mRoot = null;
private boolean mIsLayoutRtl;
- private final SparseArray<View> mFocusables = new SparseArray<View>();
- private final SparseBooleanArray mIsConnectedTo = new SparseBooleanArray();
- private final ArrayMap<View, View> mHeadsOfChains = new ArrayMap<View, View>();
- public void recycle() {
- mRoot = null;
- mFocusables.clear();
- mHeadsOfChains.clear();
- mIsConnectedTo.clear();
+ public void setIsLayoutRtl(boolean b) {
+ mIsLayoutRtl = b;
}
public void setRoot(ViewGroup root) {
mRoot = root;
}
- public void setIsLayoutRtl(boolean b) {
- mIsLayoutRtl = b;
+ public int compare(View first, View second) {
+ if (first == second) {
+ return 0;
+ }
+
+ getRect(first, mFirstRect);
+ getRect(second, mSecondRect);
+
+ if (mFirstRect.top < mSecondRect.top) {
+ return -1;
+ } else if (mFirstRect.top > mSecondRect.top) {
+ return 1;
+ } else if (mFirstRect.left < mSecondRect.left) {
+ return mIsLayoutRtl ? 1 : -1;
+ } else if (mFirstRect.left > mSecondRect.left) {
+ return mIsLayoutRtl ? -1 : 1;
+ } else if (mFirstRect.bottom < mSecondRect.bottom) {
+ return -1;
+ } else if (mFirstRect.bottom > mSecondRect.bottom) {
+ return 1;
+ } else if (mFirstRect.right < mSecondRect.right) {
+ return mIsLayoutRtl ? 1 : -1;
+ } else if (mFirstRect.right > mSecondRect.right) {
+ return mIsLayoutRtl ? -1 : 1;
+ } else {
+ // The view are distinct but completely coincident so we consider
+ // them equal for our purposes. Since the sort is stable, this
+ // means that the views will retain their layout order relative to one another.
+ return 0;
+ }
+ }
+
+ private void getRect(View view, Rect rect) {
+ view.getDrawingRect(rect);
+ mRoot.offsetDescendantRectToMyCoords(view, rect);
+ }
+ }
+
+ /**
+ * Sorts views according to any explicitly-specified focus-chains. If there are no explicitly
+ * specified focus chains (eg. no nextFocusForward attributes defined), this should be a no-op.
+ */
+ private static final class UserSpecifiedFocusComparator implements Comparator<View> {
+ private final SparseArray<View> mFocusables = new SparseArray<View>();
+ private final SparseBooleanArray mIsConnectedTo = new SparseBooleanArray();
+ private final ArrayMap<View, View> mHeadsOfChains = new ArrayMap<View, View>();
+ private final ArrayMap<View, Integer> mOriginalOrdinal = new ArrayMap<>();
+
+ public void recycle() {
+ mFocusables.clear();
+ mHeadsOfChains.clear();
+ mIsConnectedTo.clear();
+ mOriginalOrdinal.clear();
}
public void setFocusables(ArrayList<View> focusables) {
@@ -755,6 +799,10 @@
setHeadOfChain(view);
}
}
+
+ for (int i = 0; i < focusables.size(); ++i) {
+ mOriginalOrdinal.put(focusables.get(i), i);
+ }
}
private void setHeadOfChain(View head) {
@@ -793,44 +841,22 @@
return 1; // first is end of chain
}
}
+ boolean involvesChain = false;
if (firstHead != null) {
first = firstHead;
+ involvesChain = true;
}
if (secondHead != null) {
second = secondHead;
+ involvesChain = true;
}
- // First see if they belong to the same focus chain.
- getRect(first, mFirstRect);
- getRect(second, mSecondRect);
-
- if (mFirstRect.top < mSecondRect.top) {
- return -1;
- } else if (mFirstRect.top > mSecondRect.top) {
- return 1;
- } else if (mFirstRect.left < mSecondRect.left) {
- return mIsLayoutRtl ? 1 : -1;
- } else if (mFirstRect.left > mSecondRect.left) {
- return mIsLayoutRtl ? -1 : 1;
- } else if (mFirstRect.bottom < mSecondRect.bottom) {
- return -1;
- } else if (mFirstRect.bottom > mSecondRect.bottom) {
- return 1;
- } else if (mFirstRect.right < mSecondRect.right) {
- return mIsLayoutRtl ? 1 : -1;
- } else if (mFirstRect.right > mSecondRect.right) {
- return mIsLayoutRtl ? -1 : 1;
+ if (involvesChain) {
+ // keep original order between chains
+ return mOriginalOrdinal.get(first) < mOriginalOrdinal.get(second) ? -1 : 1;
} else {
- // The view are distinct but completely coincident so we consider
- // them equal for our purposes. Since the sort is stable, this
- // means that the views will retain their layout order relative to one another.
return 0;
}
}
-
- private void getRect(View view, Rect rect) {
- view.getDrawingRect(rect);
- mRoot.offsetDescendantRectToMyCoords(view, rect);
- }
}
}
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index d59be02..2fe98c0 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -16,8 +16,6 @@
package android.view;
-import android.graphics.Rect;
-
/**
* An interface to the PinnedStackController to update it of state changes, and to query
* information based on the current state.
@@ -27,17 +25,7 @@
interface IPinnedStackController {
/**
- * Notifies the controller that the user is currently interacting with the PIP.
- */
- oneway void setInInteractiveMode(boolean inInteractiveMode);
-
- /**
- * Notifies the controller that the PIP is currently minimized.
+ * Notifies the controller that the PiP is currently minimized.
*/
oneway void setIsMinimized(boolean isMinimized);
-
- /**
- * Notifies the controller that the desired snap mode is to the closest edge.
- */
- oneway void setSnapToEdge(boolean snapToEdge);
}
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index 3c348c5..c7340bf 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -17,6 +17,7 @@
package android.view;
import android.content.pm.ParceledListSlice;
+import android.graphics.Rect;
import android.view.IPinnedStackController;
/**
@@ -33,10 +34,22 @@
void onListenerRegistered(IPinnedStackController controller);
/**
- * Called when window manager decides to adjust the pinned stack bounds, or when the listener
- * is first registered to allow the listener to synchronized its state with the controller.
+ * Called when the window manager has detected a change that would cause the movement bounds
+ * to be changed (ie. after configuration change, aspect ratio change, etc). It then provides
+ * the components that allow the listener to calculate the movement bounds itself. The
+ * {@param normalBounds} are also the default bounds that the PiP would be entered in its
+ * current state with the aspect ratio applied.
*/
- void onBoundsChanged(boolean adjustedForIme);
+ void onMovementBoundsChanged(in Rect insetBounds, in Rect normalBounds,
+ boolean fromImeAdjustement);
+
+ /**
+ * Called when window manager decides to adjust the pinned stack bounds because of the IME, or
+ * when the listener is first registered to allow the listener to synchronized its state with
+ * the controller. This call will always be followed by a onMovementBoundsChanged() call
+ * with fromImeAdjustement set to true.
+ */
+ void onImeVisibilityChanged(boolean imeVisible, int imeHeight);
/**
* Called when window manager decides to adjust the minimized state, or when the listener
@@ -45,14 +58,6 @@
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.
*/
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c789f8c..dd76fcf 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -337,16 +337,6 @@
void registerPinnedStackListener(int displayId, IPinnedStackListener listener);
/**
- * Returns the initial bounds that PIP will be shown when it is first started.
- */
- Rect getPictureInPictureDefaultBounds(int displayId);
-
- /**
- * Returns the bounds that the PIP can move on the screen in the current PIP state.
- */
- Rect getPictureInPictureMovementBounds(int displayId);
-
- /**
* Updates the dim layer used while resizing.
*
* @param visible Whether the dim layer should be visible.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5ea5b0e..ee97984 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7333,6 +7333,25 @@
}
/**
+ * Adds extra data to an {@link AccessibilityNodeInfo} based on an explicit request for the
+ * additional data.
+ * <p>
+ * This method only needs overloading if the node is marked as having extra data available.
+ * </p>
+ *
+ * @param info The info to which to add the extra data
+ * @param extraDataKey A key specifying the type of extra data to add to the info. The
+ * extra data should be added to the {@link Bundle} returned by
+ * the info's {@link AccessibilityNodeInfo#getExtras} method.
+ * @param arguments A {@link Bundle} holding any arguments relevant for this request.
+ *
+ * @see AccessibilityNodeInfo#setExtraAvailableData
+ */
+ public void addExtraDataToAccessibilityNodeInfo(
+ AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
+ }
+
+ /**
* Determine the order in which this view will be drawn relative to its siblings for a11y
*
* @param info The info whose drawing order should be populated
@@ -24334,6 +24353,32 @@
}
/**
+ * Adds extra data to an {@link AccessibilityNodeInfo} based on an explicit request for the
+ * additional data.
+ * <p>
+ * This method only needs to be implemented if the View offers to provide additional data.
+ * </p>
+ * <p>
+ * The default implementation behaves as
+ * {@link View#addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo, int) for
+ * the case where no accessibility delegate is set.
+ * </p>
+ *
+ * @param host The View hosting the delegate.
+ * @param info The info to which to add the extra data
+ * @param extraDataKey A key specifying the type of extra data to add to the info. The
+ * extra data should be added to the {@link Bundle} returned by
+ * the info's {@link AccessibilityNodeInfo#getExtras} method.
+ * @param arguments A {@link Bundle} holding any arguments relevant for this request.
+ *
+ * @see AccessibilityNodeInfo#setExtraAvailableData
+ */
+ public void addExtraDataToAccessibilityNodeInfo(
+ View host, AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
+ host.addExtraDataToAccessibilityNodeInfo(info, extraDataKey, arguments);
+ }
+
+ /**
* Called when a child of the host View has requested sending an
* {@link AccessibilityEvent} and gives an opportunity to the parent (the host)
* to augment the event.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index aa580fa..36beaaa 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -60,6 +60,7 @@
import com.android.internal.util.Predicate;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -1140,31 +1141,42 @@
final int focusableCount = views.size();
final int descendantFocusability = getDescendantFocusability();
+ final boolean focusSelf = (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen());
- if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
- if (shouldBlockFocusForTouchscreen()) {
- focusableMode |= FOCUSABLES_TOUCH_MODE;
+ if (descendantFocusability == FOCUS_BLOCK_DESCENDANTS) {
+ if (focusSelf) {
+ super.addFocusables(views, direction, focusableMode);
}
-
- final int count = mChildrenCount;
- final View[] children = mChildren;
-
- for (int i = 0; i < count; i++) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
- child.addFocusables(views, direction, focusableMode);
- }
- }
+ return;
}
- // we add ourselves (if focusable) in all cases except for when we are
- // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
+ if (shouldBlockFocusForTouchscreen()) {
+ focusableMode |= FOCUSABLES_TOUCH_MODE;
+ }
+
+ if ((descendantFocusability == FOCUS_BEFORE_DESCENDANTS) && focusSelf) {
+ super.addFocusables(views, direction, focusableMode);
+ }
+
+ int count = 0;
+ final View[] children = new View[mChildrenCount];
+ for (int i = 0; i < mChildrenCount; ++i) {
+ View child = mChildren[i];
+ if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+ children[count++] = child;
+ }
+ }
+ Arrays.sort(children, 0, count, FocusFinder.getFocusComparator(this, false));
+ for (int i = 0; i < count; ++i) {
+ children[i].addFocusables(views, direction, focusableMode);
+ }
+
+ // When set to FOCUS_AFTER_DESCENDANTS, we only add ourselves if
+ // there aren't any focusable descendants. this is
// to avoid the focus search finding layouts when a more precise search
// among the focusable children would be more interesting.
- if ((descendantFocusability != FOCUS_AFTER_DESCENDANTS
- // No focusable descendants
- || (focusableCount == views.size())) &&
- (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())) {
+ if ((descendantFocusability == FOCUS_AFTER_DESCENDANTS) && focusSelf
+ && focusableCount == views.size()) {
super.addFocusables(views, direction, focusableMode);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9bfc260..9e8dda8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7492,13 +7492,13 @@
public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
interactiveRegion, interactionId, callback, flags, interrogatingPid,
- interrogatingTid, spec);
+ interrogatingTid, spec, args);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index db2ea48..143c49a 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -554,7 +554,7 @@
// Layer of indirection included to break dependency chain for testing
public static class AccessibilityNodeRefresher {
public boolean refreshNode(AccessibilityNodeInfo info, boolean bypassCache) {
- return info.refresh(bypassCache);
+ return info.refresh(null, bypassCache);
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 1543597..828583c 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -159,7 +159,7 @@
public AccessibilityNodeInfo getRootInActiveWindow(int connectionId) {
return findAccessibilityNodeInfoByAccessibilityId(connectionId,
AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
- false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
+ false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS, null);
}
/**
@@ -259,7 +259,7 @@
*/
public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache,
- int prefetchFlags) {
+ int prefetchFlags, Bundle arguments) {
if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0
&& (prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) == 0) {
throw new IllegalArgumentException("FLAG_PREFETCH_SIBLINGS"
@@ -285,9 +285,8 @@
final long identityToken = Binder.clearCallingIdentity();
final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
accessibilityWindowId, accessibilityNodeId, interactionId, this,
- prefetchFlags, Thread.currentThread().getId());
+ prefetchFlags, Thread.currentThread().getId(), arguments);
Binder.restoreCallingIdentity(identityToken);
- // If the scale is zero the call has failed.
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 56d45b0..b0a11cd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -16,6 +16,8 @@
package android.view.accessibility;
+import static java.util.Collections.EMPTY_LIST;
+
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
@@ -517,6 +519,46 @@
*/
public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
+ /**
+ * Key used to request and locate extra data for text character location. This key requests that
+ * an array of {@link android.graphics.RectF}s be added to the extras. This request is made with
+ * {@link #refreshWithExtraData(String, Bundle)}. The arguments taken by this request are two
+ * integers: {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX} and
+ * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}. The starting index must be valid
+ * inside the CharSequence returned by {@link #getText()}, and the length must be positive.
+ * <p>
+ * The data can be retrieved from the {@code Bundle} returned by {@link #getExtras()} using this
+ * string as a key for {@link Bundle#getParcelableArray(String)}. The
+ * {@link android.graphics.RectF} will be null for characters that either do not exist or are
+ * off the screen.
+ *
+ * {@see #refreshWithExtraData(String, Bundle)}
+ */
+ public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY =
+ "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
+
+ /**
+ * Integer argument specifying the start index of the requested text location data. Must be
+ * valid inside the CharSequence returned by {@link #getText()}.
+ *
+ * {@see EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY}
+ */
+ public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX =
+ "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+
+ /**
+ * Integer argument specifying the end index of the requested text location data. Must be
+ * positive.
+ *
+ * {@see EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY}
+ */
+ public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH =
+ "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+
+ /** @hide */
+ public static final String EXTRA_DATA_REQUESTED_KEY =
+ "android.view.accessibility.AccessibilityNodeInfo.extra_data_requested";
+
// Boolean attributes.
private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001;
@@ -651,6 +693,7 @@
private CharSequence mError;
private CharSequence mContentDescription;
private String mViewIdResourceName;
+ private ArrayList<String> mExtraDataKeys;
private LongArray mChildNodeIds;
private ArrayList<AccessibilityAction> mActions;
@@ -786,14 +829,14 @@
*
* @hide
*/
- public boolean refresh(boolean bypassCache) {
+ public boolean refresh(Bundle arguments, boolean bypassCache) {
enforceSealed();
if (!canPerformRequestOverConnection(mSourceNodeId)) {
return false;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId(
- mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0);
+ mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0, arguments);
if (refreshedInfo == null) {
return false;
}
@@ -804,15 +847,33 @@
/**
* Refreshes this info with the latest state of the view it represents.
- * <p>
- * <strong>Note:</strong> If this method returns false this info is obsolete
- * since it represents a view that is no longer in the view tree and should
- * be recycled.
- * </p>
- * @return Whether the refresh succeeded.
+ *
+ * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented
+ * by this node is no longer in the view tree (and thus this node is obsolete and should be
+ * recycled).
*/
public boolean refresh() {
- return refresh(true);
+ return refresh(null, true);
+ }
+
+ /**
+ * Refreshes this info with the latest state of the view it represents, and request new
+ * data be added by the View.
+ *
+ * @param extraDataKey A bitmask of the extra data requested. Data that must be requested
+ * with this mechanism is generally expensive to retrieve, so should only be
+ * requested when needed. See
+ * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and
+ * {@link #getAvailableExtraData()}.
+ * @param args A bundle of arguments for the request. These depend on the particular request.
+ *
+ * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented
+ * by this node is no longer in the view tree (and thus this node is obsolete and should be
+ * recycled).
+ */
+ public boolean refreshWithExtraData(String extraDataKey, Bundle args) {
+ args.putString(EXTRA_DATA_REQUESTED_KEY, extraDataKey);
+ return refresh(args, true);
}
/**
@@ -872,7 +933,7 @@
final long childId = mChildNodeIds.get(index);
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
- childId, false, FLAG_PREFETCH_DESCENDANTS);
+ childId, false, FLAG_PREFETCH_DESCENDANTS, null);
}
/**
@@ -1263,6 +1324,45 @@
}
/**
+ * Get the extra data available for this node.
+ * <p>
+ * Some data that is useful for some accessibility services is expensive to compute, and would
+ * place undue overhead on apps to compute all the time. That data can be requested with
+ * {@link #refreshWithExtraData(String, Bundle)}.
+ *
+ * @return An unmodifiable list of keys corresponding to extra data that can be requested.
+ * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
+ */
+ public List<String> getAvailableExtraData() {
+ if (mExtraDataKeys != null) {
+ return Collections.unmodifiableList(mExtraDataKeys);
+ } else {
+ return EMPTY_LIST;
+ }
+ }
+
+ /**
+ * Set the extra data available for this node.
+ * <p>
+ * <strong>Note:</strong> When a {@code View} passes in a non-empty list, it promises that
+ * it will populate the node's extras with corresponding pieces of information in
+ * {@link View#addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo, String, Bundle)}.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ *
+ * @param extraDataKeys A list of types of extra data that are available.
+ * @see #getAvailableExtraData()
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setAvailableExtraData(List<String> extraDataKeys) {
+ enforceNotSealed();
+ mExtraDataKeys = new ArrayList<>(extraDataKeys);
+ }
+
+ /**
* Sets the maximum text length, or -1 for no limit.
* <p>
* Typically used to indicate that an editable text field has a limit on
@@ -2658,6 +2758,14 @@
}
/**
+ * Check if a node has an extras bundle
+ * @hide
+ */
+ public boolean hasExtras() {
+ return mExtras != null;
+ }
+
+ /**
* Gets the value of a boolean property.
*
* @param property The property.
@@ -2955,6 +3063,12 @@
parcel.writeInt(mInputType);
parcel.writeInt(mLiveRegion);
parcel.writeInt(mDrawingOrderInParent);
+ if (mExtraDataKeys != null) {
+ parcel.writeInt(1);
+ parcel.writeStringList(mExtraDataKeys);
+ } else {
+ parcel.writeInt(0);
+ }
if (mExtras != null) {
parcel.writeInt(1);
@@ -3054,6 +3168,8 @@
mInputType = other.mInputType;
mLiveRegion = other.mLiveRegion;
mDrawingOrderInParent = other.mDrawingOrderInParent;
+ mExtraDataKeys = other.mExtraDataKeys;
+
if (other.mExtras != null) {
mExtras = new Bundle(other.mExtras);
} else {
@@ -3137,6 +3253,12 @@
mDrawingOrderInParent = parcel.readInt();
if (parcel.readInt() == 1) {
+ mExtraDataKeys = parcel.createStringArrayList();
+ } else {
+ mExtraDataKeys = null;
+ }
+
+ if (parcel.readInt() == 1) {
mExtras = parcel.readBundle();
} else {
mExtras = null;
@@ -3455,7 +3577,7 @@
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
mWindowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS
- | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+ | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null);
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index abcbb70..722b659 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -102,6 +102,27 @@
}
/**
+ * Adds extra data to an {@link AccessibilityNodeInfo} based on an explicit request for the
+ * additional data.
+ * <p>
+ * This method only needs to be implemented if a virtual view offers to provide additional
+ * data.
+ * </p>
+ *
+ * @param virtualViewId The virtual view id used to create the node
+ * @param info The info to which to add the extra data
+ * @param extraDataKey A key specifying the type of extra data to add to the info. The
+ * extra data should be added to the {@link Bundle} returned by
+ * the info's {@link AccessibilityNodeInfo#getExtras} method.
+ * @param arguments A {@link Bundle} holding any arguments relevant for this request.
+ *
+ * @see AccessibilityNodeInfo#setExtraAvailableData
+ */
+ public void addExtraDataToAccessibilityNodeInfo(
+ int virtualViewId, AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
+ }
+
+ /**
* Performs an accessibility action on a virtual view, i.e. a descendant of the
* host View, with the given <code>virtualViewId</code> or the host View itself
* if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 3287298..c390406 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -183,7 +183,7 @@
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
mId, AccessibilityNodeInfo.ROOT_NODE_ID,
- true, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
+ true, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS, null);
}
/**
@@ -209,7 +209,7 @@
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- mParentId, mAnchorId, true, 0);
+ mParentId, mAnchorId, true, 0, null);
}
/**
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index cecc4af..4c0fdfd 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -33,7 +33,8 @@
void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, in Region bounds,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid, in MagnificationSpec spec);
+ int interrogatingPid, long interrogatingTid, in MagnificationSpec spec,
+ in Bundle arguments);
void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId,
in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback,
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index a2cb491..76ed80f 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -95,7 +95,6 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -140,7 +139,6 @@
private static final boolean DEBUG_UNDO = false;
static final int BLINK = 500;
- private static final float[] TEMP_POSITION = new float[2];
private static final int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
private static final float LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS = 0.5f;
private static final int UNSET_X_VALUE = -1;
@@ -1032,46 +1030,6 @@
boolean parentPositionChanged, boolean parentScrolled);
}
- private boolean isPositionVisible(final float positionX, final float positionY) {
- synchronized (TEMP_POSITION) {
- final float[] position = TEMP_POSITION;
- position[0] = positionX;
- position[1] = positionY;
- View view = mTextView;
-
- while (view != null) {
- if (view != mTextView) {
- // Local scroll is already taken into account in positionX/Y
- position[0] -= view.getScrollX();
- position[1] -= view.getScrollY();
- }
-
- if (position[0] < 0 || position[1] < 0 || position[0] > view.getWidth()
- || position[1] > view.getHeight()) {
- return false;
- }
-
- if (!view.getMatrix().isIdentity()) {
- view.getMatrix().mapPoints(position);
- }
-
- position[0] += view.getLeft();
- position[1] += view.getTop();
-
- final ViewParent parent = view.getParent();
- if (parent instanceof View) {
- view = (View) parent;
- } else {
- // We've reached the ViewRoot, stop iterating
- view = null;
- }
- }
- }
-
- // We've been able to walk up the view hierarchy and the position was never clipped
- return true;
- }
-
private boolean isOffsetVisible(int offset) {
Layout layout = mTextView.getLayout();
if (layout == null) return false;
@@ -1079,7 +1037,8 @@
final int line = layout.getLineForOffset(offset);
final int lineBottom = layout.getLineBottom(line);
final int primaryHorizontal = (int) layout.getPrimaryHorizontal(offset);
- return isPositionVisible(primaryHorizontal + mTextView.viewportToContentHorizontalOffset(),
+ return mTextView.isPositionVisible(
+ primaryHorizontal + mTextView.viewportToContentHorizontalOffset(),
lineBottom + mTextView.viewportToContentVerticalOffset());
}
@@ -4099,69 +4058,9 @@
final CharSequence composingText = text.subSequence(composingTextStart,
composingTextEnd);
builder.setComposingText(composingTextStart, composingText);
-
- final int minLine = layout.getLineForOffset(composingTextStart);
- final int maxLine = layout.getLineForOffset(composingTextEnd - 1);
- for (int line = minLine; line <= maxLine; ++line) {
- final int lineStart = layout.getLineStart(line);
- final int lineEnd = layout.getLineEnd(line);
- final int offsetStart = Math.max(lineStart, composingTextStart);
- final int offsetEnd = Math.min(lineEnd, composingTextEnd);
- final boolean ltrLine =
- layout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT;
- final float[] widths = new float[offsetEnd - offsetStart];
- layout.getPaint().getTextWidths(text, offsetStart, offsetEnd, widths);
- final float top = layout.getLineTop(line);
- final float bottom = layout.getLineBottom(line);
- for (int offset = offsetStart; offset < offsetEnd; ++offset) {
- final float charWidth = widths[offset - offsetStart];
- final boolean isRtl = layout.isRtlCharAt(offset);
- final float primary = layout.getPrimaryHorizontal(offset);
- final float secondary = layout.getSecondaryHorizontal(offset);
- // TODO: This doesn't work perfectly for text with custom styles and
- // TAB chars.
- final float left;
- final float right;
- if (ltrLine) {
- if (isRtl) {
- left = secondary - charWidth;
- right = secondary;
- } else {
- left = primary;
- right = primary + charWidth;
- }
- } else {
- if (!isRtl) {
- left = secondary;
- right = secondary + charWidth;
- } else {
- left = primary - charWidth;
- right = primary;
- }
- }
- // TODO: Check top-right and bottom-left as well.
- final float localLeft = left + viewportToContentHorizontalOffset;
- final float localRight = right + viewportToContentHorizontalOffset;
- final float localTop = top + viewportToContentVerticalOffset;
- final float localBottom = bottom + viewportToContentVerticalOffset;
- final boolean isTopLeftVisible = isPositionVisible(localLeft, localTop);
- final boolean isBottomRightVisible =
- isPositionVisible(localRight, localBottom);
- int characterBoundsFlags = 0;
- if (isTopLeftVisible || isBottomRightVisible) {
- characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
- }
- if (!isTopLeftVisible || !isBottomRightVisible) {
- characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
- }
- if (isRtl) {
- characterBoundsFlags |= CursorAnchorInfo.FLAG_IS_RTL;
- }
- // Here offset is the index in Java chars.
- builder.addCharacterBounds(offset, localLeft, localTop, localRight,
- localBottom, characterBoundsFlags);
- }
- }
+ mTextView.populateCharacterBounds(builder, composingTextStart,
+ composingTextEnd, viewportToContentHorizontalOffset,
+ viewportToContentVerticalOffset);
}
}
@@ -4177,10 +4076,10 @@
+ viewportToContentVerticalOffset;
final float insertionMarkerBottom = layout.getLineBottom(line)
+ viewportToContentVerticalOffset;
- final boolean isTopVisible =
- isPositionVisible(insertionMarkerX, insertionMarkerTop);
- final boolean isBottomVisible =
- isPositionVisible(insertionMarkerX, insertionMarkerBottom);
+ final boolean isTopVisible = mTextView
+ .isPositionVisible(insertionMarkerX, insertionMarkerTop);
+ final boolean isBottomVisible = mTextView
+ .isPositionVisible(insertionMarkerX, insertionMarkerBottom);
int insertionMarkerFlags = 0;
if (isTopVisible || isBottomVisible) {
insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
@@ -4388,7 +4287,8 @@
return false;
}
- return isPositionVisible(mPositionX + mHotspotX + getHorizontalOffset(), mPositionY);
+ return mTextView.isPositionVisible(
+ mPositionX + mHotspotX + getHorizontalOffset(), mPositionY);
}
public abstract int getCurrentCursorOffset();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7ef0a06..b18ca45 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,6 +17,10 @@
package android.widget;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
+import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
import android.R;
import android.annotation.ColorInt;
@@ -142,6 +146,7 @@
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@@ -166,6 +171,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Locale;
/**
@@ -268,6 +274,7 @@
static final String LOG_TAG = "TextView";
static final boolean DEBUG_EXTRACT = false;
static final boolean DEBUG_AUTOFILL = false;
+ private static final float[] TEMP_POSITION = new float[2];
// Enum for the "typeface" XML parameter.
// TODO: How can we get this from the XML instead of hardcoding it here?
@@ -9906,6 +9913,8 @@
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
+ info.setAvailableExtraData(
+ Arrays.asList(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY));
}
if (isFocused()) {
@@ -9942,6 +9951,164 @@
}
}
+ @Override
+ public void addExtraDataToAccessibilityNodeInfo(
+ AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
+ if (extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
+ int positionInfoStartIndex = arguments.getInt(
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX, -1);
+ int positionInfoLength = arguments.getInt(
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH, -1);
+ if ((positionInfoLength <= 0) || (positionInfoStartIndex < 0)
+ || (positionInfoStartIndex >= mText.length())) {
+ Log.e(LOG_TAG, "Invalid arguments for accessibility character locations");
+ return;
+ }
+ RectF[] boundingRects = new RectF[positionInfoLength];
+ final CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder();
+ populateCharacterBounds(builder, positionInfoStartIndex,
+ positionInfoStartIndex + positionInfoLength,
+ viewportToContentHorizontalOffset(), viewportToContentVerticalOffset());
+ CursorAnchorInfo cursorAnchorInfo = builder.setMatrix(null).build();
+ if (mTempRect == null) mTempRect = new Rect();
+ Rect viewBoundsInScreen = mTempRect;
+ info.getBoundsInScreen(viewBoundsInScreen);
+ for (int i = 0; i < positionInfoLength; i++) {
+ int flags = cursorAnchorInfo.getCharacterBoundsFlags(positionInfoStartIndex + i);
+ if ((flags & FLAG_HAS_VISIBLE_REGION) == FLAG_HAS_VISIBLE_REGION) {
+ RectF bounds = cursorAnchorInfo
+ .getCharacterBounds(positionInfoStartIndex + i);
+ if (bounds != null) {
+ bounds.offset(viewBoundsInScreen.left, viewBoundsInScreen.top);
+ boundingRects[i] = bounds;
+ }
+ }
+ }
+ info.getExtras().putParcelableArray(extraDataKey, boundingRects);
+ }
+ }
+
+ /**
+ * Populate requested character bounds in a {@link CursorAnchorInfo.Builder}
+ *
+ * @param builder The builder to populate
+ * @param startIndex The starting character index to populate
+ * @param endIndex The ending character index to populate
+ * @param viewportToContentHorizontalOffset The horizontal offset from the viewport to the
+ * content
+ * @param viewportToContentVerticalOffset The vertical offset from the viewport to the content
+ * @hide
+ */
+ public void populateCharacterBounds(CursorAnchorInfo.Builder builder,
+ int startIndex, int endIndex, float viewportToContentHorizontalOffset,
+ float viewportToContentVerticalOffset) {
+ final int minLine = mLayout.getLineForOffset(startIndex);
+ final int maxLine = mLayout.getLineForOffset(endIndex - 1);
+ for (int line = minLine; line <= maxLine; ++line) {
+ final int lineStart = mLayout.getLineStart(line);
+ final int lineEnd = mLayout.getLineEnd(line);
+ final int offsetStart = Math.max(lineStart, startIndex);
+ final int offsetEnd = Math.min(lineEnd, endIndex);
+ final boolean ltrLine =
+ mLayout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT;
+ final float[] widths = new float[offsetEnd - offsetStart];
+ mLayout.getPaint().getTextWidths(mText, offsetStart, offsetEnd, widths);
+ final float top = mLayout.getLineTop(line);
+ final float bottom = mLayout.getLineBottom(line);
+ for (int offset = offsetStart; offset < offsetEnd; ++offset) {
+ final float charWidth = widths[offset - offsetStart];
+ final boolean isRtl = mLayout.isRtlCharAt(offset);
+ final float primary = mLayout.getPrimaryHorizontal(offset);
+ final float secondary = mLayout.getSecondaryHorizontal(offset);
+ // TODO: This doesn't work perfectly for text with custom styles and
+ // TAB chars.
+ final float left;
+ final float right;
+ if (ltrLine) {
+ if (isRtl) {
+ left = secondary - charWidth;
+ right = secondary;
+ } else {
+ left = primary;
+ right = primary + charWidth;
+ }
+ } else {
+ if (!isRtl) {
+ left = secondary;
+ right = secondary + charWidth;
+ } else {
+ left = primary - charWidth;
+ right = primary;
+ }
+ }
+ // TODO: Check top-right and bottom-left as well.
+ final float localLeft = left + viewportToContentHorizontalOffset;
+ final float localRight = right + viewportToContentHorizontalOffset;
+ final float localTop = top + viewportToContentVerticalOffset;
+ final float localBottom = bottom + viewportToContentVerticalOffset;
+ final boolean isTopLeftVisible = isPositionVisible(localLeft, localTop);
+ final boolean isBottomRightVisible =
+ isPositionVisible(localRight, localBottom);
+ int characterBoundsFlags = 0;
+ if (isTopLeftVisible || isBottomRightVisible) {
+ characterBoundsFlags |= FLAG_HAS_VISIBLE_REGION;
+ }
+ if (!isTopLeftVisible || !isBottomRightVisible) {
+ characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+ }
+ if (isRtl) {
+ characterBoundsFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+ }
+ // Here offset is the index in Java chars.
+ builder.addCharacterBounds(offset, localLeft, localTop, localRight,
+ localBottom, characterBoundsFlags);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isPositionVisible(final float positionX, final float positionY) {
+ synchronized (TEMP_POSITION) {
+ final float[] position = TEMP_POSITION;
+ position[0] = positionX;
+ position[1] = positionY;
+ View view = this;
+
+ while (view != null) {
+ if (view != this) {
+ // Local scroll is already taken into account in positionX/Y
+ position[0] -= view.getScrollX();
+ position[1] -= view.getScrollY();
+ }
+
+ if (position[0] < 0 || position[1] < 0 || position[0] > view.getWidth()
+ || position[1] > view.getHeight()) {
+ return false;
+ }
+
+ if (!view.getMatrix().isIdentity()) {
+ view.getMatrix().mapPoints(position);
+ }
+
+ position[0] += view.getLeft();
+ position[1] += view.getTop();
+
+ final ViewParent parent = view.getParent();
+ if (parent instanceof View) {
+ view = (View) parent;
+ } else {
+ // We've reached the ViewRoot, stop iterating
+ view = null;
+ }
+ }
+ }
+
+ // We've been able to walk up the view hierarchy and the position was never clipped
+ return true;
+ }
+
/**
* Performs an accessibility action after it has been offered to the
* delegate.
diff --git a/core/java/com/android/internal/policy/PipMotionHelper.java b/core/java/com/android/internal/policy/PipMotionHelper.java
deleted file mode 100644
index 944cd32..0000000
--- a/core/java/com/android/internal/policy/PipMotionHelper.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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 com.android.internal.policy;
-
-import android.animation.RectEvaluator;
-import android.animation.ValueAnimator;
-import android.app.ActivityManager;
-import android.app.IActivityManager;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-/**
- * A helper to animate the PIP.
- */
-public class PipMotionHelper {
-
- private static final String TAG = "PipMotionHelper";
-
- private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
- private static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
- private static final int DEFAULT_DURATION = 225;
-
- private IActivityManager mActivityManager;
- private Handler mHandler;
-
- public PipMotionHelper(Handler handler) {
- mHandler = handler;
- }
-
- /**
- * Moves the PIP to give given {@param bounds}.
- */
- public void resizeToBounds(Rect toBounds) {
- mHandler.post(() -> {
- if (mActivityManager == null) {
- mActivityManager = ActivityManager.getService();
- }
- try {
- mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not move pinned stack to bounds: " + toBounds, e);
- }
- });
- }
-
- /**
- * Creates an animation to move the PIP to give given {@param toBounds} with the default
- * animation properties.
- */
- public ValueAnimator createAnimationToBounds(Rect fromBounds, Rect toBounds) {
- return createAnimationToBounds(fromBounds, toBounds, DEFAULT_DURATION, FAST_OUT_SLOW_IN,
- null);
- }
-
- /**
- * Creates an animation to move the PIP to give given {@param toBounds}.
- */
- public ValueAnimator createAnimationToBounds(Rect fromBounds, Rect toBounds, int duration,
- Interpolator interpolator, ValueAnimator.AnimatorUpdateListener updateListener) {
- ValueAnimator anim = ValueAnimator.ofObject(RECT_EVALUATOR, fromBounds, toBounds);
- anim.setDuration(duration);
- anim.setInterpolator(interpolator);
- anim.addUpdateListener((ValueAnimator animation) -> {
- resizeToBounds((Rect) animation.getAnimatedValue());
- });
- if (updateListener != null) {
- anim.addUpdateListener(updateListener);
- }
- return anim;
- }
-
-
-}
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index ec92aa9..bf047c1 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -84,13 +84,6 @@
}
/**
- * Enables snapping to the closest edge.
- */
- public void setSnapToEdge(boolean snapToEdge) {
- mSnapMode = snapToEdge ? SNAP_MODE_EDGE : mDefaultSnapMode;
- }
-
- /**
* @return the closest absolute snap stack bounds for the given {@param stackBounds} moving at
* the given {@param velocityX} and {@param velocityY}. The {@param movementBounds} should be
* those for the given {@param stackBounds}.
@@ -233,6 +226,21 @@
}
/**
+ * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given
+ * {@param stackBounds}.
+ */
+ public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut,
+ int imeHeight) {
+ // Adjust the right/bottom to ensure the stack bounds never goes offscreen
+ movementBoundsOut.set(insetBounds);
+ movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right -
+ stackBounds.width());
+ movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom -
+ stackBounds.height());
+ movementBoundsOut.bottom -= imeHeight;
+ }
+
+ /**
* @return the closest point in {@param points} to the given {@param x} and {@param y}.
*/
private Point findClosestPoint(int x, int y, Point[] points) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a6e43ff..7b800b3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3220,6 +3220,11 @@
<permission android:name="android.permission.BIND_VR_LISTENER_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by system apps when accessing restricted VR APIs.
+ <p>Protection level: signature -->
+ <permission android:name="android.permission.RESTRICTED_VR_ACCESS"
+ android:protectionLevel="signature|preinstalled" />
+
<!-- Required to make calls to {@link android.service.vr.IVrManager}.
@hide -->
<permission android:name="android.permission.ACCESS_VR_MANAGER"
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index f5cedfa..b490545 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -583,4 +583,17 @@
right = in.readFloat();
bottom = in.readFloat();
}
+
+ /**
+ * Scales up the rect by the given scale.
+ * @hide
+ */
+ public void scale(float scale) {
+ if (scale != 1.0f) {
+ left = left * scale;
+ top = top * scale ;
+ right = right * scale;
+ bottom = bottom * scale;
+ }
+ }
}
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 1dad58f..71bee93 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -205,6 +205,10 @@
*d++ = a * (start.g * oppAmount + end.g * amount);
*d++ = a * (start.b * oppAmount + end.b * amount);
#else
+ // What we're doing to the alpha channel here is technically incorrect
+ // but reproduces Android's old behavior when the alpha was pre-multiplied
+ // with gamma-encoded colors
+ a = EOCF_sRGB(a);
*d++ = a * OECF_sRGB(start.r * oppAmount + end.r * amount);
*d++ = a * OECF_sRGB(start.g * oppAmount + end.g * amount);
*d++ = a * OECF_sRGB(start.b * oppAmount + end.b * amount);
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 7107679..42ef762 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -164,24 +164,39 @@
// Dithering must be done in the quantization space
// When we are writing to an sRGB framebuffer, we must do the following:
// EOCF(OECF(color) + dither)
-// We approximate the transfer functions with gamma 2.0 to avoid branches and pow()
// The dithering pattern is generated with a triangle noise generator in the range [-0.0,1.0]
// TODO: Handle linear fp16 render targets
-const char* gFS_Gradient_Functions =
- "\nfloat triangleNoise(const highp vec2 n) {\n"
- " highp vec2 p = fract(n * vec2(5.3987, 5.4421));\n"
- " p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));\n"
- " highp float xy = p.x * p.y;\n"
- " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
- "}\n";
+const char* gFS_Gradient_Functions = R"__SHADER__(
+ float triangleNoise(const highp vec2 n) {
+ highp vec2 p = fract(n * vec2(5.3987, 5.4421));
+ p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
+ highp float xy = p.x * p.y;
+ return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
+ }
+
+ float OECF_sRGB(const float linear) {
+ // IEC 61966-2-1:1999
+ return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+ }
+
+ vec3 OECF_sRGB(const vec3 linear) {
+ return vec3(OECF_sRGB(linear.r), OECF_sRGB(linear.g), OECF_sRGB(linear.b));
+ }
+
+ float EOCF_sRGB(float srgb) {
+ // IEC 61966-2-1:1999
+ return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+ }
+)__SHADER__";
const char* gFS_Gradient_Preamble[2] = {
// Linear framebuffer
"\nvec4 dither(const vec4 color) {\n"
" return vec4(color.rgb + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0), color.a);\n"
"}\n"
"\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
- " vec4 c = pow(mix(a, b, v), vec4(vec3(1.0 / 2.2), 1.0));\n"
- " return vec4(c.rgb * c.a, c.a);\n"
+ " vec4 c = mix(a, b, v);\n"
+ " c.a = EOCF_sRGB(c.a);\n" // This is technically incorrect but preserves compatibility
+ " return vec4(OECF_sRGB(c.rgb) * c.a, c.a);\n"
"}\n",
// sRGB framebuffer
"\nvec4 dither(const vec4 color) {\n"
@@ -200,13 +215,15 @@
// The gamma coefficient is chosen to thicken or thin the text accordingly
// The dot product used to compute the luminance could be approximated with
// a simple max(color.r, color.g, color.b)
-const char* gFS_Gamma_Preamble =
- "\n#define GAMMA (%.2f)\n"
- "#define GAMMA_INV (%.2f)\n"
- "\nfloat gamma(float a, const vec3 color) {\n"
- " float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));\n"
- " return pow(a, luminance < 0.5 ? GAMMA_INV : GAMMA);\n"
- "}\n";
+const char* gFS_Gamma_Preamble = R"__SHADER__(
+ #define GAMMA (%.2f)
+ #define GAMMA_INV (%.2f)
+
+ float gamma(float a, const vec3 color) {
+ float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
+ return pow(a, luminance < 0.5 ? GAMMA_INV : GAMMA);
+ }
+)__SHADER__";
const char* gFS_Main =
"\nvoid main(void) {\n"
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 705395e..f6850a1 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -44,7 +44,7 @@
case GL_RGBA16F:
return 8;
default:
- LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat);
+ LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat);
}
}
diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
new file mode 100644
index 0000000..a63a585
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "TestSceneBase.h"
+
+#include <SkColorMatrixFilter.h>
+#include <SkGradientShader.h>
+
+class SimpleColorMatrixAnimation;
+
+static TestScene::Registrar _SimpleColorMatrix(TestScene::Info{
+ "simpleColorMatrix",
+ "A color matrix shader benchmark for the simple scale/translate case, which has R, G, and B "
+ "all scaled and translated the same amount.",
+ TestScene::simpleCreateScene<SimpleColorMatrixAnimation>
+});
+
+class SimpleColorMatrixAnimation : public TestScene {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
+
+ sp<RenderNode> card = createCard(0, 0, width, height);
+ canvas.drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 20;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ return TestUtils::createNode(x, y, x + width, y + height,
+ [width, height](RenderProperties& props, Canvas& canvas) {
+ SkPaint paint;
+ float matrix[20] = { 0 };
+
+ // Simple scale/translate case where R, G, and B are all treated equivalently
+ matrix[SkColorMatrix::kR_Scale] = 1.1f;
+ matrix[SkColorMatrix::kG_Scale] = 1.1f;
+ matrix[SkColorMatrix::kB_Scale] = 1.1f;
+ matrix[SkColorMatrix::kA_Scale] = 0.5f;
+
+ matrix[SkColorMatrix::kR_Trans] = 5.0f;
+ matrix[SkColorMatrix::kG_Trans] = 5.0f;
+ matrix[SkColorMatrix::kB_Trans] = 5.0f;
+ matrix[SkColorMatrix::kA_Trans] = 10.0f;
+
+ paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix));
+
+ // set a shader so it's not likely for the matrix to be optimized away (since a clever
+ // enough renderer might apply it directly to the paint color)
+ float pos[] = { 0, 1 };
+ SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(width, height) };
+ SkColor colors[2] = { Color::DeepPurple_500, Color::DeepOrange_500 };
+ paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2,
+ SkShader::kClamp_TileMode));
+
+ // overdraw several times to emphasize shader cost
+ for (int i = 0; i < 10; i++) {
+ canvas.drawRect(i, i, width, height, paint);
+ }
+ });
+ }
+};
diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
new file mode 100644
index 0000000..053eb6d
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+
+#include "TestSceneBase.h"
+
+#include <SkGradientShader.h>
+
+class SimpleGradientAnimation;
+
+static TestScene::Registrar _SimpleGradient(TestScene::Info{
+ "simpleGradient",
+ "A benchmark of shader performance of linear, 2 color gradients with black in them.",
+ TestScene::simpleCreateScene<SimpleGradientAnimation>
+});
+
+class SimpleGradientAnimation : public TestScene {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
+
+ sp<RenderNode> card = createCard(0, 0, width, height);
+ canvas.drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 20;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ return TestUtils::createNode(x, y, x + width, y + height,
+ [width, height](RenderProperties& props, Canvas& canvas) {
+ float pos[] = { 0, 1 };
+ SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(width, height) };
+ SkPaint paint;
+ // overdraw several times to emphasize shader cost
+ for (int i = 0; i < 10; i++) {
+ // use i%2 start position to pick 2 color combo with black in it
+ SkColor colors[3] = { Color::Transparent, Color::Black, Color::Cyan_500 };
+ paint.setShader(SkGradientShader::MakeLinear(pts, colors + (i % 2), pos, 2,
+ SkShader::kClamp_TileMode));
+ canvas.drawRect(i, i, width, height, paint);
+ }
+ });
+ }
+};
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index cd38b50..9083c16 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1956,7 +1956,13 @@
e.printStackTrace();
}
baseSetStartDelayMs(0);
- startImpl();
+ try {
+ startImpl();
+ } catch (IllegalStateException e) {
+ // fail silently for a state exception when it is happening after
+ // a delayed start, as the player state could have changed between the
+ // call to start() and the execution of startImpl()
+ }
}
}.start();
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 03dc2ea..1a1d0f3 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1258,7 +1258,13 @@
e.printStackTrace();
}
baseSetStartDelayMs(0);
- startImpl();
+ try {
+ startImpl();
+ } catch (IllegalStateException e) {
+ // fail silently for a state exception when it is happening after
+ // a delayed start, as the player state could have changed between the
+ // call to start() and the execution of startImpl()
+ }
}
}.start();
}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2cc2621..8553952 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1763,14 +1763,6 @@
not appear on production builds ever. -->
<string name="pip_drag_to_dismiss_summary" translatable="false">Drag to the dismiss target at the bottom of the screen to close the PIP</string>
- <!-- PIP allow minimize title. Non-translatable since it should
- not appear on production builds ever. -->
- <string name="pip_allow_minimize_title" translatable="false">Allow PIP to minimize</string>
-
- <!-- PIP allow minimize description. Non-translatable since it should
- not appear on production builds ever. -->
- <string name="pip_allow_minimize_summary" translatable="false">Allow PIP to minimize slightly offscreen</string>
-
<!-- Tuner string -->
<string name="change_theme_reboot" translatable="false">Changing the theme requires a restart.</string>
<!-- Tuner string -->
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 94a7c07..6198ab7 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -131,12 +131,6 @@
android:summary="@string/pip_drag_to_dismiss_summary"
sysui:defValue="false" />
- <com.android.systemui.tuner.TunerSwitch
- android:key="pip_allow_minimize"
- android:title="@string/pip_allow_minimize_title"
- android:summary="@string/pip_allow_minimize_summary"
- sysui:defValue="true" />
-
</PreferenceScreen>
<PreferenceScreen
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index e182176..beec137 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -17,18 +17,15 @@
package com.android.systemui.pip.phone;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.TouchDelegate;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
import com.android.systemui.Interpolators;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 3df557d..f59b2a4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -16,20 +16,16 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityManager;
-import android.app.ActivityManager.StackInfo;
-import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
@@ -94,7 +90,7 @@
}
}
if (expandPipToFullscreen) {
- mTouchHandler.expandPinnedStackToFullscreen();
+ mTouchHandler.getMotionHelper().expandPip();
} else {
Log.w(TAG, "Can not expand PiP to fullscreen via intent from the same package.");
}
@@ -114,28 +110,31 @@
}
@Override
- public void onBoundsChanged(boolean adjustedForIme) {
- // Do nothing
- }
-
- @Override
- public void onActionsChanged(ParceledListSlice actions) {
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
mHandler.post(() -> {
- mMenuController.setAppActions(actions);
+ mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
});
}
@Override
public void onMinimizedStateChanged(boolean isMinimized) {
mHandler.post(() -> {
- mTouchHandler.onMinimizedStateChanged(isMinimized);
+ mTouchHandler.setMinimizedState(isMinimized, true /* fromController */);
});
}
@Override
- public void onSnapToEdgeStateChanged(boolean isSnapToEdge) {
+ public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
+ boolean fromImeAdjustement) {
mHandler.post(() -> {
- mTouchHandler.onSnapToEdgeStateChanged(isSnapToEdge);
+ mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, fromImeAdjustement);
+ });
+ }
+
+ @Override
+ public void onActionsChanged(ParceledListSlice actions) {
+ mHandler.post(() -> {
+ mMenuController.setAppActions(actions);
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index d96baa6..5a665a9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -16,16 +16,22 @@
package com.android.systemui.pip.phone;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
+
import android.app.IActivityManager;
+import android.app.PendingIntent;
import android.app.RemoteAction;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.drawable.Icon;
import android.media.session.MediaController;
-import android.media.session.MediaController.TransportControls;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
+import android.os.UserHandle;
import com.android.systemui.R;
@@ -40,6 +46,9 @@
*/
public class PipMediaController {
+ private static final String ACTION_PLAY = "com.android.systemui.pip.phone.PLAY";
+ private static final String ACTION_PAUSE = "com.android.systemui.pip.phone.PAUSE";
+
/**
* A listener interface to receive notification on changes to the media actions.
*/
@@ -59,6 +68,18 @@
private RemoteAction mPauseAction;
private RemoteAction mPlayAction;
+ private BroadcastReceiver mPlayPauseActionReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(ACTION_PLAY)) {
+ mMediaController.getTransportControls().play();
+ } else if (action.equals(ACTION_PAUSE)) {
+ mMediaController.getTransportControls().pause();
+ }
+ }
+ };
+
private MediaController.Callback mPlaybackChangedListener = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
@@ -73,6 +94,10 @@
public PipMediaController(Context context, IActivityManager activityManager) {
mContext = context;
mActivityManager = activityManager;
+ IntentFilter mediaControlFilter = new IntentFilter();
+ mediaControlFilter.addAction(ACTION_PLAY);
+ mediaControlFilter.addAction(ACTION_PAUSE);
+ mContext.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter);
createMediaActions();
mMediaSessionManager =
@@ -135,12 +160,14 @@
String pauseDescription = mContext.getString(R.string.pip_pause);
mPauseAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.ic_pause_white_24dp), pauseDescription, pauseDescription,
- action -> mMediaController.getTransportControls().pause());
+ PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PAUSE),
+ FLAG_UPDATE_CURRENT));
String playDescription = mContext.getString(R.string.pip_play);
mPlayAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.ic_play_arrow_white_24dp), playDescription, playDescription,
- action -> mMediaController.getTransportControls().play());
+ PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PLAY),
+ FLAG_UPDATE_CURRENT));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 438b8dd..9066977 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.PendingIntent.CanceledException;
import android.app.RemoteAction;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
@@ -258,7 +259,11 @@
}, mHandler);
actionView.setContentDescription(action.getContentDescription());
actionView.setOnClickListener(v -> {
- action.sendActionInvoked();
+ try {
+ action.getActionIntent().send();
+ } catch (CanceledException e) {
+ Log.w(TAG, "Failed to send action", e);
+ }
});
actionsGroup.addView(actionView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
new file mode 100644
index 0000000..28ab3fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -0,0 +1,376 @@
+/*
+ * 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 com.android.systemui.pip.phone;
+
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+
+import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN;
+import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.ActivityManager.StackInfo;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.animation.Interpolator;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.policy.PipSnapAlgorithm;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+/**
+ * A helper to animate and manipulate the PiP.
+ */
+public class PipMotionHelper {
+
+ private static final String TAG = "PipMotionHelper";
+
+ private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
+
+ private static final int DEFAULT_MOVE_STACK_DURATION = 225;
+ private static final int SNAP_STACK_DURATION = 225;
+ private static final int DISMISS_STACK_DURATION = 375;
+ private static final int SHRINK_STACK_FROM_MENU_DURATION = 175;
+ private static final int EXPAND_STACK_TO_MENU_DURATION = 175;
+ private static final int EXPAND_STACK_TO_FULLSCREEN_DURATION = 225;
+ private static final int MINIMIZE_STACK_MAX_DURATION = 200;
+
+ // The fraction of the stack width that the user has to drag offscreen to minimize the PiP
+ private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f;
+
+ private Context mContext;
+ private IActivityManager mActivityManager;
+ private Handler mHandler;
+
+ private PipSnapAlgorithm mSnapAlgorithm;
+ private FlingAnimationUtils mFlingAnimationUtils;
+
+ private final Rect mBounds = new Rect();
+ private final Rect mStableInsets = new Rect();
+
+ private ValueAnimator mBoundsAnimator = null;
+ private ValueAnimator.AnimatorUpdateListener mUpdateBoundsListener =
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mBounds.set((Rect) animation.getAnimatedValue());
+ }
+ };
+
+ public PipMotionHelper(Context context, IActivityManager activityManager,
+ PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils) {
+ mContext = context;
+ mHandler = BackgroundThread.getHandler();
+ mActivityManager = activityManager;
+ mSnapAlgorithm = snapAlgorithm;
+ mFlingAnimationUtils = flingAnimationUtils;
+ onConfigurationChanged();
+ }
+
+ /**
+ * Updates whenever the configuration changes.
+ */
+ void onConfigurationChanged() {
+ mSnapAlgorithm.onConfigurationChanged();
+ SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets);
+ }
+
+ /**
+ * Synchronizes the current bounds with the pinned stack.
+ */
+ void synchronizePinnedStackBounds() {
+ cancelAnimations();
+ try {
+ StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ mBounds.set(stackInfo.bounds);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get pinned stack bounds");
+ }
+ }
+
+ /**
+ * Tries to the move the pinned stack to the given {@param bounds}.
+ */
+ void movePip(Rect toBounds) {
+ cancelAnimations();
+ resizePipUnchecked(toBounds);
+ mBounds.set(toBounds);
+ }
+
+ /**
+ * Resizes the pinned stack back to fullscreen.
+ */
+ void expandPip() {
+ cancelAnimations();
+ mHandler.post(() -> {
+ try {
+ mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
+ true /* allowResizeInDockedMode */, true /* preserveWindows */,
+ true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error showing PiP menu activity", e);
+ }
+ });
+ }
+
+ /**
+ * Dismisses the pinned stack.
+ */
+ void dismissPip() {
+ cancelAnimations();
+ mHandler.post(() -> {
+ try {
+ mActivityManager.removeStack(PINNED_STACK_ID);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to remove PiP", e);
+ }
+ });
+ }
+
+ /**
+ * @return the PiP bounds.
+ */
+ Rect getBounds() {
+ return mBounds;
+ }
+
+ /**
+ * @return the closest minimized PiP bounds.
+ */
+ Rect getClosestMinimizedBounds(Rect stackBounds, Rect movementBounds) {
+ Point displaySize = new Point();
+ mContext.getDisplay().getRealSize(displaySize);
+ Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, stackBounds);
+ mSnapAlgorithm.applyMinimizedOffset(toBounds, movementBounds, displaySize, mStableInsets);
+ return toBounds;
+ }
+
+ /**
+ * @return whether the PiP at the current bounds should be minimized.
+ */
+ boolean shouldMinimizePip() {
+ Point displaySize = new Point();
+ mContext.getDisplay().getRealSize(displaySize);
+ if (mBounds.left < 0) {
+ float offscreenFraction = (float) -mBounds.left / mBounds.width();
+ return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
+ } else if (mBounds.right > displaySize.x) {
+ float offscreenFraction = (float) (mBounds.right - displaySize.x) /
+ mBounds.width();
+ return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Flings the minimized PiP to the closest minimized snap target.
+ */
+ Rect flingToMinimizedState(float velocityY, Rect movementBounds) {
+ cancelAnimations();
+ // We currently only allow flinging the minimized stack up and down, so just lock the
+ // movement bounds to the current stack bounds horizontally
+ movementBounds = new Rect(mBounds.left, movementBounds.top, mBounds.left,
+ movementBounds.bottom);
+ Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
+ 0 /* velocityX */, velocityY);
+ if (!mBounds.equals(toBounds)) {
+ mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN,
+ mUpdateBoundsListener);
+ mFlingAnimationUtils.apply(mBoundsAnimator, 0,
+ distanceBetweenRectOffsets(mBounds, toBounds),
+ velocityY);
+ mBoundsAnimator.start();
+ }
+ return toBounds;
+ }
+
+ /**
+ * Animates the PiP to the minimized state, slightly offscreen.
+ */
+ Rect animateToClosestMinimizedState(Rect movementBounds,
+ final PipMenuActivityController menuController) {
+ cancelAnimations();
+ Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
+ if (!mBounds.equals(toBounds)) {
+ mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
+ MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, mUpdateBoundsListener);
+ mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ menuController.hideMenu();
+ }
+ });
+ mBoundsAnimator.start();
+ }
+ return toBounds;
+ }
+
+ /**
+ * Flings the PiP to the closest snap target.
+ */
+ Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds) {
+ cancelAnimations();
+ Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
+ velocityX, velocityY);
+ if (!mBounds.equals(toBounds)) {
+ mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN,
+ mUpdateBoundsListener);
+ mFlingAnimationUtils.apply(mBoundsAnimator, 0,
+ distanceBetweenRectOffsets(mBounds, toBounds),
+ velocity);
+ mBoundsAnimator.start();
+ }
+ return toBounds;
+ }
+
+ /**
+ * Animates the PiP to the closest snap target.
+ */
+ Rect animateToClosestSnapTarget(Rect movementBounds) {
+ cancelAnimations();
+ Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds);
+ if (!mBounds.equals(toBounds)) {
+ mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, SNAP_STACK_DURATION,
+ FAST_OUT_SLOW_IN, mUpdateBoundsListener);
+ mBoundsAnimator.start();
+ }
+ return toBounds;
+ }
+
+ /**
+ * Animates the PiP to the expanded state to show the menu.
+ */
+ float animateToExpandedState(Rect expandedBounds, Rect movementBounds,
+ Rect expandedMovementBounds) {
+ float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), movementBounds);
+ mSnapAlgorithm.applySnapFraction(expandedBounds, expandedMovementBounds, savedSnapFraction);
+ mBoundsAnimator = createAnimationToBounds(mBounds, expandedBounds,
+ EXPAND_STACK_TO_MENU_DURATION, FAST_OUT_SLOW_IN, mUpdateBoundsListener);
+ mBoundsAnimator.start();
+ return savedSnapFraction;
+ }
+
+ /**
+ * Animates the PiP from the expanded state to the normal state after the menu is hidden.
+ */
+ void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
+ Rect normalMovementBounds) {
+ if (savedSnapFraction >= 0f) {
+ mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
+ mBoundsAnimator = createAnimationToBounds(mBounds, normalBounds,
+ SHRINK_STACK_FROM_MENU_DURATION, FAST_OUT_SLOW_IN, mUpdateBoundsListener);
+ mBoundsAnimator.start();
+ } else {
+ animateToClosestSnapTarget(normalMovementBounds);
+ }
+ }
+
+ /**
+ * Animates the dismissal of the PiP over the dismiss target bounds.
+ */
+ Rect animateDismissFromDrag(Rect dismissBounds) {
+ cancelAnimations();
+ Rect toBounds = new Rect(dismissBounds.centerX(),
+ dismissBounds.centerY(),
+ dismissBounds.centerX() + 1,
+ dismissBounds.centerY() + 1);
+ mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, DISMISS_STACK_DURATION,
+ FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
+ mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ dismissPip();
+ }
+ });
+ mBoundsAnimator.start();
+ return toBounds;
+ }
+
+ /**
+ * Animates the PiP to some given bounds.
+ */
+ void animateToBounds(Rect toBounds) {
+ cancelAnimations();
+ if (!mBounds.equals(toBounds)) {
+ mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
+ DEFAULT_MOVE_STACK_DURATION, FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
+ mBoundsAnimator.start();
+ }
+ }
+
+ /**
+ * Cancels all existing animations.
+ */
+ void cancelAnimations() {
+ if (mBoundsAnimator != null) {
+ mBoundsAnimator.cancel();
+ mBoundsAnimator = null;
+ }
+ }
+
+ /**
+ * Creates an animation to move the PiP to give given {@param toBounds}.
+ */
+ private ValueAnimator createAnimationToBounds(Rect fromBounds, Rect toBounds, int duration,
+ Interpolator interpolator, ValueAnimator.AnimatorUpdateListener updateListener) {
+ ValueAnimator anim = ValueAnimator.ofObject(RECT_EVALUATOR, fromBounds, toBounds);
+ anim.setDuration(duration);
+ anim.setInterpolator(interpolator);
+ anim.addUpdateListener((ValueAnimator animation) -> {
+ resizePipUnchecked((Rect) animation.getAnimatedValue());
+ });
+ if (updateListener != null) {
+ anim.addUpdateListener(updateListener);
+ }
+ return anim;
+ }
+
+ /**
+ * Directly resizes the PiP to the given {@param bounds}.
+ */
+ private void resizePipUnchecked(Rect toBounds) {
+ if (!toBounds.equals(mBounds)) {
+ mHandler.post(() -> {
+ try {
+ mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
+ mBounds.set(toBounds);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not move pinned stack to bounds: " + toBounds, e);
+ }
+ });
+ }
+ }
+
+ /**
+ * @return the distance between points {@param p1} and {@param p2}.
+ */
+ private float distanceBetweenRectOffsets(Rect r1, Rect r2) {
+ return PointF.length(r1.left - r2.left, r1.top - r2.top);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 10393c6..b3adee0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -16,21 +16,10 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
-import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN;
-import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.ActivityManager.StackInfo;
import android.app.IActivityManager;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
@@ -47,8 +36,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.os.BackgroundThread;
-import com.android.internal.policy.PipMotionHelper;
import com.android.internal.policy.PipSnapAlgorithm;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -60,24 +47,15 @@
*/
public class PipTouchHandler implements TunerService.Tunable {
private static final String TAG = "PipTouchHandler";
- private static final boolean DEBUG_ALLOW_OUT_OF_BOUNDS_STACK = false;
// These values are used for metrics and should never change
private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
private static final String TUNER_KEY_DRAG_TO_DISMISS = "pip_drag_to_dismiss";
- private static final String TUNER_KEY_ALLOW_MINIMIZE = "pip_allow_minimize";
- private static final int SNAP_STACK_DURATION = 225;
- private static final int DISMISS_STACK_DURATION = 375;
- private static final int EXPAND_STACK_DURATION = 225;
- private static final int MINIMIZE_STACK_MAX_DURATION = 200;
private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 200;
- // The fraction of the stack width that the user has to drag offscreen to minimize the PIP
- private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f;
-
private final Context mContext;
private final IActivityManager mActivityManager;
private final IWindowManager mWindowManager;
@@ -86,34 +64,28 @@
private IPinnedStackController mPinnedStackController;
private PipInputEventReceiver mInputEventReceiver;
- private PipMenuActivityController mMenuController;
- private PipDismissViewController mDismissViewController;
+ private final PipMenuActivityController mMenuController;
+ private final PipDismissViewController mDismissViewController;
private final PipSnapAlgorithm mSnapAlgorithm;
- private PipMotionHelper mMotionHelper;
// Allow dragging the PIP to a location to close it
private boolean mEnableDragToDismiss = false;
- // Allow the PIP to be "docked" slightly offscreen
- private boolean mEnableMinimizing = true;
- private final Rect mStableInsets = new Rect();
- private final Rect mPinnedStackBounds = new Rect();
- private final Rect mBoundedPinnedStackBounds = new Rect();
- private ValueAnimator mPinnedStackBoundsAnimator = null;
- private ValueAnimator.AnimatorUpdateListener mUpdatePinnedStackBoundsListener =
- new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mPinnedStackBounds.set((Rect) animation.getAnimatedValue());
- }
- };
+ // The current movement bounds
+ private Rect mMovementBounds = new Rect();
+
+ // The reference bounds used to calculate the normal/expanded target bounds
+ private Rect mNormalBounds = new Rect();
+ private Rect mNormalMovementBounds = new Rect();
+ private Rect mExpandedBounds = new Rect();
+ private Rect mExpandedMovementBounds = new Rect();
private Handler mHandler = new Handler();
private Runnable mShowDismissAffordance = new Runnable() {
@Override
public void run() {
if (mEnableDragToDismiss) {
- mDismissViewController.showDismissTarget(mPinnedStackBounds);
+ mDismissViewController.showDismissTarget(mMotionHelper.getBounds());
}
}
};
@@ -121,13 +93,18 @@
// Behaviour states
private boolean mIsTappingThrough;
private boolean mIsMinimized;
+ private boolean mIsMenuVisible;
+ private boolean mIsImeShowing;
+ private int mImeHeight;
+ private float mSavedSnapFraction = -1f;
// Touch state
private final PipTouchState mTouchState;
private final FlingAnimationUtils mFlingAnimationUtils;
private final PipTouchGesture[] mGestures;
+ private final PipMotionHelper mMotionHelper;
- // Temporary vars
+ // Temp vars
private final Rect mTmpBounds = new Rect();
/**
@@ -160,32 +137,25 @@
private class PipMenuListener implements PipMenuActivityController.Listener {
@Override
public void onPipMenuVisibilityChanged(boolean visible) {
- if (!visible) {
- mIsTappingThrough = false;
- registerInputConsumer();
- } else {
- unregisterInputConsumer();
- }
- MetricsLogger.visibility(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
- visible);
+ setMenuVisibilityState(visible);
}
@Override
public void onPipExpand() {
if (!mIsMinimized) {
- expandPinnedStackToFullscreen();
+ mMotionHelper.expandPip();
}
}
@Override
public void onPipMinimize() {
- setMinimizedState(true);
- animateToClosestMinimizedTarget();
+ setMinimizedStateInternal(true);
+ mMotionHelper.animateToClosestMinimizedState(mMovementBounds, mMenuController);
}
@Override
public void onPipDismiss() {
- BackgroundThread.getHandler().post(PipTouchHandler.this::dismissPinnedStack);
+ mMotionHelper.dismissPip();
MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_TAP);
}
@@ -208,13 +178,12 @@
mGestures = new PipTouchGesture[] {
mDefaultMovementGesture
};
- mMotionHelper = new PipMotionHelper(BackgroundThread.getHandler());
+ mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mSnapAlgorithm,
+ mFlingAnimationUtils);
registerInputConsumer();
- setSnapToEdge(true);
// Register any tuner settings changes
- Dependency.get(TunerService.class).addTunable(this, TUNER_KEY_DRAG_TO_DISMISS,
- TUNER_KEY_ALLOW_MINIMIZE);
+ Dependency.get(TunerService.class).addTunable(this, TUNER_KEY_DRAG_TO_DISMISS);
}
@Override
@@ -222,17 +191,12 @@
if (newValue == null) {
// Reset back to default
mEnableDragToDismiss = false;
- mEnableMinimizing = true;
- setMinimizedState(false);
return;
}
switch (key) {
case TUNER_KEY_DRAG_TO_DISMISS:
mEnableDragToDismiss = Integer.parseInt(newValue) != 0;
break;
- case TUNER_KEY_ALLOW_MINIMIZE:
- mEnableMinimizing = Integer.parseInt(newValue) != 0;
- break;
}
}
@@ -243,26 +207,70 @@
registerInputConsumer();
}
if (mIsMinimized) {
- setMinimizedState(false);
+ setMinimizedStateInternal(false);
}
}
public void onConfigurationChanged() {
- mSnapAlgorithm.onConfigurationChanged();
- updateBoundedPinnedStackBounds(false /* updatePinnedStackBounds */);
+ mMotionHelper.onConfigurationChanged();
+ mMotionHelper.synchronizePinnedStackBounds();
}
- public void onMinimizedStateChanged(boolean isMinimized) {
- if (mIsMinimized != isMinimized) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
- isMinimized);
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ mIsImeShowing = imeVisible;
+ mImeHeight = imeHeight;
+ }
+
+ public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
+ boolean fromImeAdjustement) {
+ // Re-calculate the expanded bounds
+ mNormalBounds = normalBounds;
+ Rect normalMovementBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
+ mIsImeShowing ? mImeHeight : 0);
+ // TODO: Figure out the expanded size policy
+ mExpandedBounds = new Rect(normalBounds);
+ Rect expandedMovementBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
+ mIsImeShowing ? mImeHeight : 0);
+
+
+ // If this is from an IME adjustment, then we should move the PiP so that it is not occluded
+ // by the IME
+ if (fromImeAdjustement) {
+ if (mTouchState.isUserInteracting()) {
+ // Defer the update of the current movement bounds until after the user finishes
+ // touching the screen
+ } else {
+ final Rect bounds = new Rect(mMotionHelper.getBounds());
+ final Rect toMovementBounds = mIsMenuVisible
+ ? expandedMovementBounds
+ : normalMovementBounds;
+ if (mIsImeShowing) {
+ // IME visible
+ if (bounds.top == mMovementBounds.bottom) {
+ // If the PIP is currently resting on top of the IME, then adjust it with
+ // the hiding IME
+ bounds.offsetTo(bounds.left, toMovementBounds.bottom);
+ } else {
+ bounds.offset(0, Math.min(0, toMovementBounds.bottom - bounds.top));
+ }
+ } else {
+ // IME hidden
+ if (bounds.top == mMovementBounds.bottom) {
+ // If the PIP is resting on top of the IME, then adjust it with the hiding IME
+ bounds.offsetTo(bounds.left, toMovementBounds.bottom);
+ }
+ }
+ mMotionHelper.animateToBounds(bounds);
+ }
}
- mIsMinimized = isMinimized;
- mSnapAlgorithm.setMinimized(isMinimized);
- }
- public void onSnapToEdgeStateChanged(boolean isSnapToEdge) {
- mSnapAlgorithm.setSnapToEdge(isSnapToEdge);
+ // Update the movement bounds after doing the calculations based on the old movement bounds
+ // above
+ mNormalMovementBounds = normalMovementBounds;
+ mExpandedMovementBounds = expandedMovementBounds;
+ updateMovementBounds();
}
private boolean handleTouchEvent(MotionEvent ev) {
@@ -276,20 +284,11 @@
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
- // Cancel any existing animations on the pinned stack
- if (mPinnedStackBoundsAnimator != null) {
- mPinnedStackBoundsAnimator.cancel();
- }
+ mMotionHelper.synchronizePinnedStackBounds();
- updateBoundedPinnedStackBounds(true /* updatePinnedStackBounds */);
for (PipTouchGesture gesture : mGestures) {
gesture.onDown(mTouchState);
}
- try {
- mPinnedStackController.setInInteractiveMode(true);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not set dragging state", e);
- }
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -303,7 +302,7 @@
case MotionEvent.ACTION_UP: {
// Update the movement bounds again if the state has changed since the user started
// dragging (ie. when the IME shows)
- updateBoundedPinnedStackBounds(false /* updatePinnedStackBounds */);
+ updateMovementBounds();
for (PipTouchGesture gesture : mGestures) {
if (gesture.onUp(mTouchState)) {
@@ -314,11 +313,6 @@
// Fall through to clean up
}
case MotionEvent.ACTION_CANCEL: {
- try {
- mPinnedStackController.setInInteractiveMode(false);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not set dragging state", e);
- }
break;
}
}
@@ -326,16 +320,6 @@
}
/**
- * @return whether the current touch state places the pip partially offscreen.
- */
- private boolean isDraggingOffscreen(PipTouchState touchState) {
- PointF lastDelta = touchState.getLastTouchDelta();
- PointF downDelta = touchState.getDownTouchDelta();
- float left = mPinnedStackBounds.left + lastDelta.x;
- return !(mBoundedPinnedStackBounds.left <= left && left <= mBoundedPinnedStackBounds.right);
- }
-
- /**
* Registers the input consumer.
*/
private void registerInputConsumer() {
@@ -374,27 +358,30 @@
}
/**
- * Sets the snap-to-edge state and notifies the controller.
+ * Sets the minimized state.
*/
- private void setSnapToEdge(boolean snapToEdge) {
- onSnapToEdgeStateChanged(snapToEdge);
-
- if (mPinnedStackController != null) {
- try {
- mPinnedStackController.setSnapToEdge(snapToEdge);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not set snap mode to edge", e);
- }
- }
+ void setMinimizedStateInternal(boolean isMinimized) {
+ setMinimizedState(isMinimized, false /* fromController */);
}
/**
- * Sets the minimized state and notifies the controller.
+ * Sets the minimized state.
*/
- private void setMinimizedState(boolean isMinimized) {
- onMinimizedStateChanged(isMinimized);
+ void setMinimizedState(boolean isMinimized, boolean fromController) {
+ if (mIsMinimized != isMinimized) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
+ isMinimized);
+ }
+ mIsMinimized = isMinimized;
+ mSnapAlgorithm.setMinimized(isMinimized);
- if (mPinnedStackController != null) {
+ if (fromController) {
+ if (isMinimized) {
+ // Move the PiP to the new bounds immediately if minimized
+ mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds,
+ mMovementBounds));
+ }
+ } else if (mPinnedStackController != null) {
try {
mPinnedStackController.setIsMinimized(isMinimized);
} catch (RemoteException e) {
@@ -404,178 +391,43 @@
}
/**
- * @return whether the given {@param pinnedStackBounds} indicates the PIP should be minimized.
+ * Sets the menu visibility.
*/
- private boolean shouldMinimizedPinnedStack() {
- Point displaySize = new Point();
- mContext.getDisplay().getRealSize(displaySize);
- if (mPinnedStackBounds.left < 0) {
- float offscreenFraction = (float) -mPinnedStackBounds.left / mPinnedStackBounds.width();
- return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
- } else if (mPinnedStackBounds.right > displaySize.x) {
- float offscreenFraction = (float) (mPinnedStackBounds.right - displaySize.x) /
- mPinnedStackBounds.width();
- return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
+ void setMenuVisibilityState(boolean isMenuVisible) {
+ if (!isMenuVisible) {
+ mIsTappingThrough = false;
+ registerInputConsumer();
} else {
- return false;
+ unregisterInputConsumer();
}
- }
+ MetricsLogger.visibility(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
+ isMenuVisible);
- /**
- * Flings the minimized PIP to the closest minimized snap target.
- */
- private void flingToMinimizedSnapTarget(float velocityY) {
- // We currently only allow flinging the minimized stack up and down, so just lock the
- // movement bounds to the current stack bounds horizontally
- Rect movementBounds = new Rect(mPinnedStackBounds.left, mBoundedPinnedStackBounds.top,
- mPinnedStackBounds.left, mBoundedPinnedStackBounds.bottom);
- Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mPinnedStackBounds,
- 0 /* velocityX */, velocityY);
- if (!mPinnedStackBounds.equals(toBounds)) {
- mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
- toBounds, 0, FAST_OUT_SLOW_IN, mUpdatePinnedStackBoundsListener);
- mFlingAnimationUtils.apply(mPinnedStackBoundsAnimator, 0,
- distanceBetweenRectOffsets(mPinnedStackBounds, toBounds),
- velocityY);
- mPinnedStackBoundsAnimator.start();
- }
- }
-
- /**
- * Animates the PIP to the minimized state, slightly offscreen.
- */
- private void animateToClosestMinimizedTarget() {
- Point displaySize = new Point();
- mContext.getDisplay().getRealSize(displaySize);
- Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
- mPinnedStackBounds);
- mSnapAlgorithm.applyMinimizedOffset(toBounds, mBoundedPinnedStackBounds, displaySize,
- mStableInsets);
- mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
- toBounds, MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN,
- mUpdatePinnedStackBoundsListener);
- mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mMenuController.hideMenu();
+ if (isMenuVisible != mIsMenuVisible) {
+ if (isMenuVisible) {
+ // Save the current snap fraction and if we do not drag or move the PiP, then
+ // we store back to this snap fraction. Otherwise, we'll reset the snap
+ // fraction and snap to the closest edge
+ Rect expandedBounds = new Rect(mExpandedBounds);
+ mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
+ mMovementBounds, mExpandedMovementBounds);
+ } else {
+ // Try and restore the PiP to the closest edge, using the saved snap fraction
+ // if possible
+ Rect normalBounds = new Rect(mNormalBounds);
+ mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
+ mNormalMovementBounds);
}
- });
- mPinnedStackBoundsAnimator.start();
- }
-
- /**
- * Flings the PIP to the closest snap target.
- */
- private Rect flingToSnapTarget(float velocity, float velocityX, float velocityY) {
- Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
- mPinnedStackBounds, velocityX, velocityY);
- if (!mPinnedStackBounds.equals(toBounds)) {
- mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
- toBounds, 0, FAST_OUT_SLOW_IN, mUpdatePinnedStackBoundsListener);
- mFlingAnimationUtils.apply(mPinnedStackBoundsAnimator, 0,
- distanceBetweenRectOffsets(mPinnedStackBounds, toBounds),
- velocity);
- mPinnedStackBoundsAnimator.start();
- }
- return toBounds;
- }
-
- /**
- * Animates the PIP to the closest snap target.
- */
- private Rect animateToClosestSnapTarget() {
- Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
- mPinnedStackBounds);
- if (!mPinnedStackBounds.equals(toBounds)) {
- mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
- toBounds, SNAP_STACK_DURATION, FAST_OUT_SLOW_IN, mUpdatePinnedStackBoundsListener);
- mPinnedStackBoundsAnimator.start();
- }
- return toBounds;
- }
-
- /**
- * Animates the dismissal of the PIP over the dismiss target bounds.
- */
- private void animateDismissPinnedStack(Rect dismissBounds) {
- Rect toBounds = new Rect(dismissBounds.centerX(),
- dismissBounds.centerY(),
- dismissBounds.centerX() + 1,
- dismissBounds.centerY() + 1);
- mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
- toBounds, DISMISS_STACK_DURATION, FAST_OUT_LINEAR_IN, mUpdatePinnedStackBoundsListener);
- mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- BackgroundThread.getHandler().post(PipTouchHandler.this::dismissPinnedStack);
- }
- });
- mPinnedStackBoundsAnimator.start();
- }
-
- /**
- * Resizes the pinned stack back to fullscreen.
- */
- void expandPinnedStackToFullscreen() {
- BackgroundThread.getHandler().post(() -> {
- try {
- mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
- true /* allowResizeInDockedMode */, true /* preserveWindows */,
- true /* animate */, EXPAND_STACK_DURATION);
- } catch (RemoteException e) {
- Log.e(TAG, "Error showing PIP menu activity", e);
- }
- });
- }
-
- /**
- * Tries to the move the pinned stack to the given {@param bounds}.
- */
- private void movePinnedStack(Rect bounds) {
- if (!bounds.equals(mPinnedStackBounds)) {
- mPinnedStackBounds.set(bounds);
- if (mEnableDragToDismiss) {
- mDismissViewController.updateDismissTarget(bounds);
- }
- mMotionHelper.resizeToBounds(mPinnedStackBounds);
+ mIsMenuVisible = isMenuVisible;
+ updateMovementBounds();
}
}
/**
- * Dismisses the pinned stack.
+ * @return the motion helper.
*/
- private void dismissPinnedStack() {
- try {
- mActivityManager.removeStack(PINNED_STACK_ID);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to remove PIP", e);
- }
- }
-
- /**
- * Updates the movement bounds of the pinned stack.
- */
- private void updateBoundedPinnedStackBounds(boolean updatePinnedStackBounds) {
- try {
- StackInfo info = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (info != null) {
- if (updatePinnedStackBounds) {
- mPinnedStackBounds.set(info.bounds);
- }
- mWindowManager.getStableInsets(info.displayId, mStableInsets);
- mBoundedPinnedStackBounds.set(mWindowManager.getPictureInPictureMovementBounds(
- info.displayId));
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Could not fetch PIP movement bounds.", e);
- }
- }
-
- /**
- * @return the distance between points {@param p1} and {@param p2}.
- */
- private float distanceBetweenRectOffsets(Rect r1, Rect r2) {
- return PointF.length(r1.left - r2.left, r1.top - r2.top);
+ public PipMotionHelper getMotionHelper() {
+ return mMotionHelper;
}
/**
@@ -593,25 +445,31 @@
@Override
boolean onMove(PipTouchState touchState) {
+ if (touchState.startedDragging()) {
+ mSavedSnapFraction = -1f;
+ }
+
if (touchState.startedDragging() && mEnableDragToDismiss) {
mHandler.removeCallbacks(mShowDismissAffordance);
- mDismissViewController.showDismissTarget(mPinnedStackBounds);
+ mDismissViewController.showDismissTarget(mMotionHelper.getBounds());
}
if (touchState.isDragging()) {
// Move the pinned stack freely
- PointF lastDelta = touchState.getLastTouchDelta();
- float left = mPinnedStackBounds.left + lastDelta.x;
- float top = mPinnedStackBounds.top + lastDelta.y;
+ mTmpBounds.set(mMotionHelper.getBounds());
+ final PointF lastDelta = touchState.getLastTouchDelta();
+ float left = mTmpBounds.left + lastDelta.x;
+ float top = mTmpBounds.top + lastDelta.y;
if (!touchState.allowDraggingOffscreen()) {
- left = Math.max(mBoundedPinnedStackBounds.left, Math.min(
- mBoundedPinnedStackBounds.right, left));
+ left = Math.max(mMovementBounds.left, Math.min(mMovementBounds.right, left));
}
- top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
- mBoundedPinnedStackBounds.bottom, top));
- mTmpBounds.set(mPinnedStackBounds);
+ top = Math.max(mMovementBounds.top, Math.min(mMovementBounds.bottom, top));
mTmpBounds.offsetTo((int) left, (int) top);
- movePinnedStack(mTmpBounds);
+ mMotionHelper.movePip(mTmpBounds);
+
+ if (mEnableDragToDismiss) {
+ mDismissViewController.updateDismissTarget(mTmpBounds);
+ }
return true;
}
return false;
@@ -626,9 +484,12 @@
final float velocity = PointF.length(vel.x, vel.y);
if (touchState.isDragging()
&& velocity < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
- if (mDismissViewController.shouldDismiss(mPinnedStackBounds)) {
+ if (mDismissViewController.shouldDismiss(mMotionHelper.getBounds())) {
Rect dismissBounds = mDismissViewController.getDismissBounds();
- animateDismissPinnedStack(dismissBounds);
+ mMotionHelper.animateDismissFromDrag(dismissBounds);
+ MetricsLogger.action(mContext,
+ MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
+ METRIC_VALUE_DISMISSED_BY_DRAG);
return true;
}
}
@@ -638,34 +499,34 @@
}
if (touchState.isDragging()) {
PointF vel = mTouchState.getVelocity();
- if (!mIsMinimized && (shouldMinimizedPinnedStack()
+ if (!mIsMinimized && (mMotionHelper.shouldMinimizePip()
|| isHorizontalFlingTowardsCurrentEdge(vel))) {
// Pip should be minimized
- setMinimizedState(true);
- animateToClosestMinimizedTarget();
+ setMinimizedStateInternal(true);
+ mMotionHelper.animateToClosestMinimizedState(mMovementBounds, mMenuController);
return true;
}
if (mIsMinimized) {
// If we're dragging and it wasn't a minimize gesture
// then we shouldn't be minimized.
- setMinimizedState(false);
+ setMinimizedStateInternal(false);
}
final float velocity = PointF.length(vel.x, vel.y);
if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
- flingToSnapTarget(velocity, vel.x, vel.y);
+ mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds);
} else {
- animateToClosestSnapTarget();
+ mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
}
} else if (mIsMinimized) {
// This was a tap, so no longer minimized
- animateToClosestSnapTarget();
- setMinimizedState(false);
+ mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
+ setMinimizedStateInternal(false);
} else if (!mIsTappingThrough) {
mMenuController.showMenu();
mIsTappingThrough = true;
} else {
- expandPinnedStackToFullscreen();
+ mMotionHelper.expandPip();
}
return true;
}
@@ -679,17 +540,30 @@
final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
final boolean isFling = PointF.length(vel.x, vel.y) > mFlingAnimationUtils
.getMinVelocityPxPerSecond();
- final boolean towardsCurrentEdge = onEdge(true /* left */) && vel.x < 0
- || onEdge(false /* right */) && vel.x > 0;
+ final boolean towardsCurrentEdge = isOverEdge(true /* left */) && vel.x < 0
+ || isOverEdge(false /* right */) && vel.x > 0;
return towardsCurrentEdge && isHorizontal && isFling;
}
- private boolean onEdge(boolean checkLeft) {
+ /**
+ * @return whether the given bounds are on the left or right edge (depending on
+ * {@param checkLeft})
+ */
+ private boolean isOverEdge(boolean checkLeft) {
+ final Rect bounds = mMotionHelper.getBounds();
if (checkLeft) {
- return mPinnedStackBounds.left <= mBoundedPinnedStackBounds.left;
+ return bounds.left <= mMovementBounds.left;
} else {
- return mPinnedStackBounds.right >= mBoundedPinnedStackBounds.right
- + mPinnedStackBounds.width();
+ return bounds.right >= mMovementBounds.right + bounds.width();
}
}
+
+ /**
+ * Updates the current movement bounds based on whether the menu is currently visible.
+ */
+ private void updateMovementBounds() {
+ mMovementBounds = mIsMenuVisible
+ ? mExpandedMovementBounds
+ : mNormalMovementBounds;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index 2e84ced..868b34b7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -34,6 +34,7 @@
private final PointF mLastTouch = new PointF();
private final PointF mLastDelta = new PointF();
private final PointF mVelocity = new PointF();
+ private boolean mIsUserInteracting = false;
private boolean mIsDragging = false;
private boolean mStartedDragging = false;
private boolean mAllowDraggingOffscreen = false;
@@ -57,6 +58,7 @@
mIsDragging = false;
mStartedDragging = false;
mAllowDraggingOffscreen = true;
+ mIsUserInteracting = true;
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -107,6 +109,7 @@
// Fall through to clean up
}
case MotionEvent.ACTION_CANCEL: {
+ mIsUserInteracting = false;
recycleVelocityTracker();
break;
}
@@ -151,6 +154,13 @@
}
/**
+ * @return whether the user is currently interacting with the PiP.
+ */
+ public boolean isUserInteracting() {
+ return mIsUserInteracting;
+ }
+
+ /**
* @return whether the user has started dragging just in the last handled touch event.
*/
public boolean startedDragging() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 964fefa..cf7b05e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Rect;
import android.media.session.MediaController;
@@ -39,6 +40,8 @@
import android.util.Log;
import android.util.Pair;
import android.view.Display;
+import android.view.IPinnedStackController;
+import android.view.IPinnedStackListener;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
@@ -51,6 +54,8 @@
import java.util.List;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.systemui.Prefs.Key.TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN;
/**
@@ -159,6 +164,8 @@
private boolean mOnboardingShown;
private String[] mLastPackagesResourceGranted;
+ private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
+
private final Runnable mResizePinnedStackRunnable = new Runnable() {
@Override
public void run() {
@@ -196,6 +203,32 @@
}
};
+ /**
+ * Handler for messages from the PIP controller.
+ */
+ private class PinnedStackListener extends IPinnedStackListener.Stub {
+
+ @Override
+ public void onListenerRegistered(IPinnedStackController controller) {}
+
+ @Override
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
+
+ @Override
+ public void onMinimizedStateChanged(boolean isMinimized) {}
+
+ @Override
+ public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
+ boolean fromImeAdjustement) {
+ mHandler.post(() -> {
+ mDefaultPipBounds.set(normalBounds);
+ });
+ }
+
+ @Override
+ public void onActionsChanged(ParceledListSlice actions) {}
+ }
+
private PipManager() { }
/**
@@ -221,16 +254,16 @@
mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+
+ try {
+ mWindowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register pinned stack listener", e);
+ }
}
private void loadConfigurationsAndApply() {
Resources res = mContext.getResources();
- try {
- mDefaultPipBounds = mWindowManager.getPictureInPictureDefaultBounds(
- Display.DEFAULT_DISPLAY);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get default PIP bounds", e);
- }
mSettingsPipBounds = Rect.unflattenFromString(res.getString(
R.string.pip_settings_bounds));
mMenuModePipBounds = Rect.unflattenFromString(res.getString(
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3d9ef02..aae5dd8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2932,7 +2932,7 @@
public boolean findAccessibilityNodeInfoByAccessibilityId(
int accessibilityWindowId, long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
- long interrogatingTid) throws RemoteException {
+ long interrogatingTid, Bundle arguments) throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
Region partialInteractiveRegion = Region.obtain();
@@ -2964,7 +2964,7 @@
try {
connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
partialInteractiveRegion, interactionId, callback, mFetchFlags | flags,
- interrogatingPid, interrogatingTid, spec);
+ interrogatingPid, interrogatingTid, spec, arguments);
return true;
} catch (RemoteException re) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ae76b61..93fb911 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1624,10 +1624,6 @@
int mThumbnailHeight;
float mFullscreenThumbnailScale;
- /** The aspect ratio bounds of the PIP. */
- float mMinPipAspectRatio;
- float mMaxPipAspectRatio;
-
final ServiceThread mHandlerThread;
final MainHandler mHandler;
final UiHandler mUiHandler;
@@ -7662,12 +7658,13 @@
r.pictureInPictureArgs.copyOnlySet(args);
final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
- final Rect bounds = isValidPictureInPictureAspectRatio(aspectRatio)
- ? mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY, aspectRatio)
- : mWindowManager.getPictureInPictureDefaultBounds(DEFAULT_DISPLAY);
+ final Rect bounds = mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY,
+ aspectRatio);
mStackSupervisor.moveActivityToPinnedStackLocked(r, "enterPictureInPictureMode",
bounds, true /* moveHomeStackToFront */);
- mStackSupervisor.getStack(PINNED_STACK_ID).setPictureInPictureActions(actions);
+ final ActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
+ stack.setPictureInPictureAspectRatio(aspectRatio);
+ stack.setPictureInPictureActions(actions);
MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
r.supportsPictureInPictureWhilePausing);
@@ -7744,10 +7741,6 @@
}
}
- private boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
- return mMinPipAspectRatio <= aspectRatio && aspectRatio <= mMaxPipAspectRatio;
- }
-
/**
* Checks the state of the system and the activity associated with the given {@param token} to
* verify that picture-in-picture is supported for that activity.
@@ -7778,10 +7771,15 @@
}
if (args.hasSetAspectRatio()
- && !isValidPictureInPictureAspectRatio(args.getAspectRatio())) {
+ && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
+ args.getAspectRatio())) {
+ final float minAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
+ final float maxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
throw new IllegalArgumentException(String.format(caller
+ ": Aspect ratio is too extreme (must be between %f and %f).",
- mMinPipAspectRatio, mMaxPipAspectRatio));
+ minAspectRatio, maxAspectRatio));
}
if (args.hasSetActions()
@@ -13464,10 +13462,6 @@
com.android.internal.R.dimen.thumbnail_width);
mThumbnailHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_height);
- mMinPipAspectRatio = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
- mMaxPipAspectRatio = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
com.android.internal.R.string.config_appsNotReportingCrashes));
mUserController.mUserSwitchUiEnabled = !res.getBoolean(
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ba25120..104fc6a 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -544,6 +544,10 @@
mWindowContainerController.setPictureInPictureAspectRatio(aspectRatio);
}
+ void setPictureInPictureActions(List<RemoteAction> actions) {
+ mWindowContainerController.setPictureInPictureActions(actions);
+ }
+
void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds, Rect outTempInsetBounds,
boolean ignoreVisibility) {
mWindowContainerController.getStackDockedModeBounds(outBounds, outTempBounds,
@@ -554,10 +558,6 @@
mWindowContainerController.prepareFreezingTaskBounds();
}
- void setPictureInPictureActions(List<RemoteAction> actions) {
- mWindowContainerController.setPictureInPictureActions(actions);
- }
-
void getWindowContainerBounds(Rect outBounds) {
if (mWindowContainerController != null) {
mWindowContainerController.getBounds(outBounds);
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 596c3d8..a872ea4 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -22,7 +22,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.animation.ValueAnimator;
import android.app.RemoteAction;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
@@ -42,7 +41,6 @@
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
-import com.android.internal.policy.PipMotionHelper;
import com.android.internal.policy.PipSnapAlgorithm;
import com.android.server.UiThread;
@@ -51,7 +49,20 @@
import java.util.List;
/**
- * Holds the common state of the pinned stack between the system and SystemUI.
+ * Holds the common state of the pinned stack between the system and SystemUI. If SystemUI ever
+ * needs to be restarted, it will be notified with the last known state.
+ *
+ * Changes to the pinned stack also flow through this controller, and generally, the system only
+ * changes the pinned stack bounds through this controller in two ways:
+ *
+ * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio
+ * and IME state into account.
+ * 2) When rotating the device: the controller calculates the new bounds in the new orientation,
+ * taking the minimized and IME state into account. In this case, we currently ignore the
+ * SystemUI adjustments (ie. expanded for menu, interaction, etc).
+ *
+ * Other changes in the system, including adjustment of IME, configuration change, and more are
+ * handled by SystemUI (similar to the docked stack divider).
*/
class PinnedStackController {
@@ -67,18 +78,15 @@
private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback();
private final PipSnapAlgorithm mSnapAlgorithm;
- private final PipMotionHelper mMotionHelper;
// States that affect how the PIP can be manipulated
- private boolean mInInteractiveMode;
private boolean mIsMinimized;
- private boolean mIsSnappingToEdge;
private boolean mIsImeShowing;
private int mImeHeight;
- private ValueAnimator mBoundsAnimator = null;
- // The set of actions that are currently allowed on the PiP activity
+ // The set of actions and aspect-ratio for the that are currently allowed on the PiP activity
private ArrayList<RemoteAction> mActions = new ArrayList<>();
+ private float mAspectRatio = -1f;
// Used to calculate stack bounds across rotations
private final DisplayInfo mDisplayInfo = new DisplayInfo();
@@ -89,6 +97,10 @@
private Size mDefaultStackSize;
private Point mScreenEdgeInsets;
+ // The aspect ratio bounds of the PIP.
+ private float mMinAspectRatio;
+ private float mMaxAspectRatio;
+
// Temp vars for calculation
private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
private final Rect mTmpInsets = new Rect();
@@ -100,31 +112,12 @@
private class PinnedStackControllerCallback extends IPinnedStackController.Stub {
@Override
- public void setInInteractiveMode(final boolean inInteractiveMode) {
- mHandler.post(() -> {
- // Cancel any existing animations on the PIP once the user starts dragging it
- if (mBoundsAnimator != null && inInteractiveMode) {
- mBoundsAnimator.cancel();
- }
- mInInteractiveMode = inInteractiveMode;
- });
- }
-
- @Override
public void setIsMinimized(final boolean isMinimized) {
mHandler.post(() -> {
mIsMinimized = isMinimized;
mSnapAlgorithm.setMinimized(isMinimized);
});
}
-
- @Override
- public void setSnapToEdge(final boolean snapToEdge) {
- mHandler.post(() -> {
- mIsSnappingToEdge = snapToEdge;
- mSnapAlgorithm.setSnapToEdge(snapToEdge);
- });
- }
}
/**
@@ -135,7 +128,6 @@
@Override
public void binderDied() {
// Clean up the state if the listener dies
- mInInteractiveMode = false;
mPinnedStackListener = null;
}
}
@@ -144,13 +136,13 @@
mService = service;
mDisplayContent = displayContent;
mSnapAlgorithm = new PipSnapAlgorithm(service.mContext);
- mMotionHelper = new PipMotionHelper(UiThread.getHandler());
mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
reloadResources();
}
void onConfigurationChanged() {
reloadResources();
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
}
/**
@@ -169,6 +161,10 @@
dpToPx(defaultSizeDp.getHeight(), mTmpMetrics));
mScreenEdgeInsets = new Point(dpToPx(screenEdgeInsetsDp.getWidth(), mTmpMetrics),
dpToPx(screenEdgeInsetsDp.getHeight(), mTmpMetrics));
+ mMinAspectRatio = res.getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
+ mMaxAspectRatio = res.getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
}
/**
@@ -179,20 +175,29 @@
listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0);
listener.onListenerRegistered(mCallbacks);
mPinnedStackListener = listener;
- notifyBoundsChanged(mIsImeShowing);
- notifyMinimizeChanged(mIsMinimized);
- notifySnapToEdgeChanged(mIsSnappingToEdge);
+ notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
+ // The movement bounds notification needs to be sent before the minimized state, since
+ // SystemUI may use the bounds to retore the minimized position
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
notifyActionsChanged(mActions);
+ notifyMinimizeChanged(mIsMinimized);
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
}
/**
+ * @return whether the given {@param aspectRatio} is valid.
+ */
+ public boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
+ return mMinAspectRatio <= aspectRatio && aspectRatio <= mMaxAspectRatio;
+ }
+
+ /**
* Returns the current bounds (or the default bounds if there are no current bounds) with the
* specified aspect ratio.
*/
- Rect getAspectRatioBounds(Rect stackBounds, float aspectRatio) {
+ Rect transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio) {
// Save the snap fraction, calculate the aspect ratio based on the current bounds
final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
getMovementBounds(stackBounds));
@@ -236,27 +241,20 @@
final Rect movementBounds = new Rect();
getInsetBounds(movementBounds);
- // Adjust the right/bottom to ensure the stack bounds never goes offscreen
- movementBounds.right = Math.max(movementBounds.left, movementBounds.right -
- stackBounds.width());
- movementBounds.bottom = Math.max(movementBounds.top, movementBounds.bottom -
- stackBounds.height());
-
// Apply the movement bounds adjustments based on the current state
- if (adjustForIme) {
- if (mIsImeShowing) {
- movementBounds.bottom -= mImeHeight;
- }
- }
+ mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+ (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
return movementBounds;
}
/**
+ * @param preChangeTargetBounds The final bounds of the stack if it is currently animating
* @return the repositioned PIP bounds given it's pre-change bounds, and the new display
* content.
*/
- Rect onDisplayChanged(Rect preChangeStackBounds, DisplayContent displayContent) {
- final Rect postChangeStackBounds = new Rect(preChangeStackBounds);
+ Rect onDisplayChanged(Rect preChangeStackBounds, Rect preChangeTargetBounds,
+ DisplayContent displayContent) {
+ final Rect postChangeStackBounds = new Rect(preChangeTargetBounds);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (!mDisplayInfo.equals(displayInfo)) {
// Calculate the snap fraction of the current stack along the old movement bounds, and
@@ -277,6 +275,7 @@
mSnapAlgorithm.applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds,
displaySize, mStableInsets);
}
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
}
return postChangeStackBounds;
}
@@ -290,42 +289,19 @@
return;
}
- final Rect stackBounds = new Rect();
- mService.getStackBounds(PINNED_STACK_ID, stackBounds);
- final Rect prevMovementBounds = getMovementBounds(stackBounds);
mIsImeShowing = adjustedForIme;
mImeHeight = imeHeight;
- if (mInInteractiveMode) {
- // If the user is currently interacting with the PIP and the ime state changes, then
- // don't adjust the bounds and defer that to after the interaction
- notifyBoundsChanged(adjustedForIme /* adjustedForIme */);
- } else {
- // Otherwise, we can move the PIP to a sane location to ensure that it does not block
- // the user from interacting with the IME
- final Rect movementBounds = getMovementBounds(stackBounds);
- final Rect toBounds = new Rect(stackBounds);
- if (adjustedForIme) {
- // IME visible
- if (stackBounds.top == prevMovementBounds.bottom) {
- // If the PIP is resting on top of the IME, then adjust it with the hiding IME
- toBounds.offsetTo(toBounds.left, movementBounds.bottom);
- } else {
- toBounds.offset(0, Math.min(0, movementBounds.bottom - stackBounds.top));
- }
- } else {
- // IME hidden
- if (stackBounds.top == prevMovementBounds.bottom) {
- // If the PIP is resting on top of the IME, then adjust it with the hiding IME
- toBounds.offsetTo(toBounds.left, movementBounds.bottom);
- }
- }
- if (!toBounds.equals(stackBounds)) {
- if (mBoundsAnimator != null) {
- mBoundsAnimator.cancel();
- }
- mBoundsAnimator = mMotionHelper.createAnimationToBounds(stackBounds, toBounds);
- mBoundsAnimator.start();
- }
+ notifyImeVisibilityChanged(adjustedForIme, imeHeight);
+ notifyMovementBoundsChanged(true /* fromImeAdjustment */);
+ }
+
+ /**
+ * Sets the current aspect ratio.
+ */
+ void setAspectRatio(float aspectRatio) {
+ if (Float.compare(mAspectRatio, aspectRatio) != 0) {
+ mAspectRatio = aspectRatio;
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
}
}
@@ -341,12 +317,12 @@
}
/**
- * Notifies listeners that the PIP movement bounds have changed.
+ * Notifies listeners that the PIP needs to be adjusted for the IME.
*/
- private void notifyBoundsChanged(boolean adjustedForIme) {
+ private void notifyImeVisibilityChanged(boolean imeVisible, int imeHeight) {
if (mPinnedStackListener != null) {
try {
- mPinnedStackListener.onBoundsChanged(adjustedForIme);
+ mPinnedStackListener.onImeVisibilityChanged(imeVisible, imeHeight);
} catch (RemoteException e) {
Slog.e(TAG_WM, "Error delivering bounds changed event.", e);
}
@@ -367,19 +343,6 @@
}
/**
- * Notifies listeners that the PIP snap-to-edge state has changed.
- */
- private void notifySnapToEdgeChanged(boolean isSnappingToEdge) {
- if (mPinnedStackListener != null) {
- try {
- mPinnedStackListener.onSnapToEdgeStateChanged(isSnappingToEdge);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering snap-to-edge changed event.", e);
- }
- }
- }
-
- /**
* Notifies listeners that the PIP actions have changed.
*/
private void notifyActionsChanged(List<RemoteAction> actions) {
@@ -393,6 +356,26 @@
}
/**
+ * Notifies listeners that the PIP movement bounds have changed.
+ */
+ private void notifyMovementBoundsChanged(boolean fromImeAdjustement) {
+ if (mPinnedStackListener != null) {
+ try {
+ Rect insetBounds = new Rect();
+ getInsetBounds(insetBounds);
+ Rect normalBounds = getDefaultBounds();
+ if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
+ transformBoundsToAspectRatio(normalBounds, mAspectRatio);
+ }
+ mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
+ fromImeAdjustement);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering actions changed event.", e);
+ }
+ }
+ }
+
+ /**
* @return the bounds on the screen that the PIP can be visible in.
*/
private void getInsetBounds(Rect outRect) {
@@ -418,7 +401,6 @@
pw.print(prefix + " movementBounds="); getMovementBounds(mTmpRect).printShortString(pw);
pw.println();
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
- pw.println(prefix + " mInInteractiveMode=" + mInInteractiveMode);
pw.println(prefix + " mIsMinimized=" + mIsMinimized);
if (mActions.isEmpty()) {
pw.println(prefix + " mActions=[]");
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index e2ea2c5..36d07e0 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -216,7 +216,28 @@
final int displayId = mContainer.getDisplayContent().getDisplayId();
final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
- animateResizePinnedStack(toBounds, -1 /* duration */);
+ final Rect targetBounds = new Rect();
+ mContainer.getAnimatingBounds(targetBounds);
+ if (!toBounds.equals(targetBounds)) {
+ animateResizePinnedStack(toBounds, -1 /* duration */);
+ }
+
+ final PinnedStackController pinnedStackController =
+ mContainer.getDisplayContent().getPinnedStackController();
+ pinnedStackController.setAspectRatio(
+ pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
+ ? aspectRatio : -1f);
+ }
+ }
+
+ /** Sets the current picture-in-picture actions. */
+ public void setPictureInPictureActions(List<RemoteAction> actions) {
+ synchronized (mWindowMap) {
+ if (!mService.mSupportsPictureInPicture || mContainer == null) {
+ return;
+ }
+
+ mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
}
}
@@ -244,17 +265,6 @@
}
}
- /** Sets the current picture-in-picture actions. */
- public void setPictureInPictureActions(List<RemoteAction> actions) {
- synchronized (mWindowMap) {
- if (!mService.mSupportsPictureInPicture || mContainer == null) {
- return;
- }
-
- mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
- }
- }
-
private void getRawBounds(Rect outBounds) {
if (mContainer.getRawFullscreen()) {
outBounds.setEmpty();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index a1c9c29..544d1e3 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -393,8 +393,10 @@
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
switch (mStackId) {
case PINNED_STACK_ID:
+ Rect targetBounds = new Rect();
+ getAnimatingBounds(targetBounds);
mTmpRect2 = mDisplayContent.getPinnedStackController().onDisplayChanged(mBounds,
- mDisplayContent);
+ targetBounds, mDisplayContent);
break;
case DOCKED_STACK_ID:
repositionDockedStackAfterRotation(mTmpRect2);
@@ -670,7 +672,9 @@
// Update the pinned stack controller after the display info is updated
if (mStackId == PINNED_STACK_ID) {
- mDisplayContent.getPinnedStackController().onDisplayChanged(oldBounds,
+ Rect targetBounds = new Rect();
+ getAnimatingBounds(targetBounds);
+ mDisplayContent.getPinnedStackController().onDisplayChanged(oldBounds, targetBounds,
mDisplayContent);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 971794b..597e8b6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2829,36 +2829,6 @@
mDockedStackCreateBounds = bounds;
}
- @Override
- public Rect getPictureInPictureDefaultBounds(int displayId) {
- synchronized (mWindowMap) {
- if (!mSupportsPictureInPicture) {
- return null;
- }
-
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- return displayContent.getPinnedStackController().getDefaultBounds();
- }
- }
-
- @Override
- public Rect getPictureInPictureMovementBounds(int displayId) {
- synchronized (mWindowMap) {
- if (!mSupportsPictureInPicture) {
- return null;
- }
-
- final Rect stackBounds = new Rect();
- getStackBounds(PINNED_STACK_ID, stackBounds);
- if (stackBounds.isEmpty()) {
- return stackBounds;
- }
-
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- return displayContent.getPinnedStackController().getMovementBounds(stackBounds);
- }
- }
-
public Rect getPictureInPictureBounds(int displayId, float aspectRatio) {
synchronized (mWindowMap) {
if (!mSupportsPictureInPicture) {
@@ -2871,6 +2841,8 @@
return null;
}
+ final PinnedStackController pinnedStackController =
+ displayContent.getPinnedStackController();
final TaskStack stack = displayContent.getStackById(PINNED_STACK_ID);
if (stack != null) {
// If the stack exists, then use its final bounds to calculate the new aspect ratio
@@ -2879,13 +2851,23 @@
stack.getAnimatingBounds(stackBounds);
} else {
// Otherwise, just calculate the aspect ratio bounds from the default bounds
- stackBounds = displayContent.getPinnedStackController().getDefaultBounds();
+ stackBounds = pinnedStackController.getDefaultBounds();
}
- return displayContent.getPinnedStackController().getAspectRatioBounds(stackBounds,
- aspectRatio);
+
+ if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
+ return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio);
+ } else {
+ return stackBounds;
+ }
}
}
+ public boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
+ aspectRatio);
+ }
+
@Override
public void getStackBounds(int stackId, Rect bounds) {
synchronized (mWindowMap) {
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
index 517fce0..05ef0d1 100644
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ b/services/core/jni/com_android_server_location_ContextHubService.cpp
@@ -558,12 +558,12 @@
void initContextHubService() {
db.hubInfo.numHubs = 0;
- db.hubInfo.contextHub = IContexthub::getService("context_hub_hal");
+ db.hubInfo.contextHub = IContexthub::getService("context_hub");
if (db.hubInfo.contextHub == nullptr) {
ALOGE("Could not load context hub hal");
} else {
- ALOGI("Loaded context hub hal");
+ ALOGI("Loaded context hub hal, isRemote %s", db.hubInfo.contextHub->isRemote() ? "TRUE" : "FALSE");
}
// Prep for storing app info
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 56898f1..253ea6f 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -483,16 +483,6 @@
}
@Override
- public Rect getPictureInPictureDefaultBounds(int displayId) {
- return null;
- }
-
- @Override
- public Rect getPictureInPictureMovementBounds(int displayId) {
- return null;
- }
-
- @Override
public void setResizeDimLayer(boolean visible, int targetStackId, float alpha)
throws RemoteException {
}