Merge "Add device document to MtpDatabase."
diff --git a/api/current.txt b/api/current.txt
index f5ccf0e..df6c473a3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -40337,6 +40337,38 @@
field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
}
+ public final class PointerIcon implements android.os.Parcelable {
+ method public static android.view.PointerIcon createCustomIcon(android.graphics.Bitmap, float, float);
+ method public int describeContents();
+ method public static android.view.PointerIcon getSystemIcon(android.content.Context, int);
+ method public static android.view.PointerIcon loadCustomIcon(android.content.res.Resources, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
+ field public static final int STYLE_ALIAS = 1010; // 0x3f2
+ field public static final int STYLE_ALL_SCROLL = 1013; // 0x3f5
+ field public static final int STYLE_ARROW = 1000; // 0x3e8
+ field public static final int STYLE_CELL = 1006; // 0x3ee
+ field public static final int STYLE_CONTEXT_MENU = 1001; // 0x3e9
+ field public static final int STYLE_COPY = 1011; // 0x3f3
+ field public static final int STYLE_CROSSHAIR = 1007; // 0x3ef
+ field public static final int STYLE_DEFAULT = 1000; // 0x3e8
+ field public static final int STYLE_GRAB = 1020; // 0x3fc
+ field public static final int STYLE_GRABBING = 1021; // 0x3fd
+ field public static final int STYLE_HAND = 1002; // 0x3ea
+ field public static final int STYLE_HELP = 1003; // 0x3eb
+ field public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+ field public static final int STYLE_NO_DROP = 1012; // 0x3f4
+ field public static final int STYLE_NULL = 0; // 0x0
+ field public static final int STYLE_TEXT = 1008; // 0x3f0
+ field public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+ field public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+ field public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+ field public static final int STYLE_VERTICAL_TEXT = 1009; // 0x3f1
+ field public static final int STYLE_WAIT = 1004; // 0x3ec
+ field public static final int STYLE_ZOOM_IN = 1018; // 0x3fa
+ field public static final int STYLE_ZOOM_OUT = 1019; // 0x3fb
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -40696,6 +40728,7 @@
method public android.view.ViewParent getParentForAccessibility();
method public float getPivotX();
method public float getPivotY();
+ method public android.view.PointerIcon getPointerIcon(android.view.MotionEvent, float, float);
method public android.content.res.Resources getResources();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
@@ -40978,6 +41011,7 @@
method public void setPaddingRelative(int, int, int, int);
method public void setPivotX(float);
method public void setPivotY(float);
+ method public void setPointerIcon(android.view.PointerIcon);
method public void setPressed(boolean);
method public final void setRight(int);
method public void setRotation(float);
diff --git a/api/system-current.txt b/api/system-current.txt
index bc63b48..ee975d1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -42675,6 +42675,38 @@
field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
}
+ public final class PointerIcon implements android.os.Parcelable {
+ method public static android.view.PointerIcon createCustomIcon(android.graphics.Bitmap, float, float);
+ method public int describeContents();
+ method public static android.view.PointerIcon getSystemIcon(android.content.Context, int);
+ method public static android.view.PointerIcon loadCustomIcon(android.content.res.Resources, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
+ field public static final int STYLE_ALIAS = 1010; // 0x3f2
+ field public static final int STYLE_ALL_SCROLL = 1013; // 0x3f5
+ field public static final int STYLE_ARROW = 1000; // 0x3e8
+ field public static final int STYLE_CELL = 1006; // 0x3ee
+ field public static final int STYLE_CONTEXT_MENU = 1001; // 0x3e9
+ field public static final int STYLE_COPY = 1011; // 0x3f3
+ field public static final int STYLE_CROSSHAIR = 1007; // 0x3ef
+ field public static final int STYLE_DEFAULT = 1000; // 0x3e8
+ field public static final int STYLE_GRAB = 1020; // 0x3fc
+ field public static final int STYLE_GRABBING = 1021; // 0x3fd
+ field public static final int STYLE_HAND = 1002; // 0x3ea
+ field public static final int STYLE_HELP = 1003; // 0x3eb
+ field public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+ field public static final int STYLE_NO_DROP = 1012; // 0x3f4
+ field public static final int STYLE_NULL = 0; // 0x0
+ field public static final int STYLE_TEXT = 1008; // 0x3f0
+ field public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+ field public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+ field public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+ field public static final int STYLE_VERTICAL_TEXT = 1009; // 0x3f1
+ field public static final int STYLE_WAIT = 1004; // 0x3ec
+ field public static final int STYLE_ZOOM_IN = 1018; // 0x3fa
+ field public static final int STYLE_ZOOM_OUT = 1019; // 0x3fb
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -43034,6 +43066,7 @@
method public android.view.ViewParent getParentForAccessibility();
method public float getPivotX();
method public float getPivotY();
+ method public android.view.PointerIcon getPointerIcon(android.view.MotionEvent, float, float);
method public android.content.res.Resources getResources();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
@@ -43316,6 +43349,7 @@
method public void setPaddingRelative(int, int, int, int);
method public void setPivotX(float);
method public void setPivotY(float);
+ method public void setPointerIcon(android.view.PointerIcon);
method public void setPressed(boolean);
method public final void setRight(int);
method public void setRotation(float);
diff --git a/api/test-current.txt b/api/test-current.txt
index a222037..c494895 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -40339,6 +40339,38 @@
field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
}
+ public final class PointerIcon implements android.os.Parcelable {
+ method public static android.view.PointerIcon createCustomIcon(android.graphics.Bitmap, float, float);
+ method public int describeContents();
+ method public static android.view.PointerIcon getSystemIcon(android.content.Context, int);
+ method public static android.view.PointerIcon loadCustomIcon(android.content.res.Resources, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
+ field public static final int STYLE_ALIAS = 1010; // 0x3f2
+ field public static final int STYLE_ALL_SCROLL = 1013; // 0x3f5
+ field public static final int STYLE_ARROW = 1000; // 0x3e8
+ field public static final int STYLE_CELL = 1006; // 0x3ee
+ field public static final int STYLE_CONTEXT_MENU = 1001; // 0x3e9
+ field public static final int STYLE_COPY = 1011; // 0x3f3
+ field public static final int STYLE_CROSSHAIR = 1007; // 0x3ef
+ field public static final int STYLE_DEFAULT = 1000; // 0x3e8
+ field public static final int STYLE_GRAB = 1020; // 0x3fc
+ field public static final int STYLE_GRABBING = 1021; // 0x3fd
+ field public static final int STYLE_HAND = 1002; // 0x3ea
+ field public static final int STYLE_HELP = 1003; // 0x3eb
+ field public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+ field public static final int STYLE_NO_DROP = 1012; // 0x3f4
+ field public static final int STYLE_NULL = 0; // 0x0
+ field public static final int STYLE_TEXT = 1008; // 0x3f0
+ field public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+ field public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+ field public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+ field public static final int STYLE_VERTICAL_TEXT = 1009; // 0x3f1
+ field public static final int STYLE_WAIT = 1004; // 0x3ec
+ field public static final int STYLE_ZOOM_IN = 1018; // 0x3fa
+ field public static final int STYLE_ZOOM_OUT = 1019; // 0x3fb
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -40698,6 +40730,7 @@
method public android.view.ViewParent getParentForAccessibility();
method public float getPivotX();
method public float getPivotY();
+ method public android.view.PointerIcon getPointerIcon(android.view.MotionEvent, float, float);
method public android.content.res.Resources getResources();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
@@ -40980,6 +41013,7 @@
method public void setPaddingRelative(int, int, int, int);
method public void setPivotX(float);
method public void setPivotY(float);
+ method public void setPointerIcon(android.view.PointerIcon);
method public void setPressed(boolean);
method public final void setRight(int);
method public void setRotation(float);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 174291e..ff33bd9 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -24,6 +24,7 @@
import android.os.IBinder;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.PointerIcon;
/** @hide */
interface IInputManager {
@@ -71,4 +72,5 @@
void cancelVibrate(int deviceId, IBinder token);
void setPointerIconShape(int shapeId);
+ void setCustomPointerIcon(in PointerIcon icon);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 201afee..fab4718 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,7 @@
package android.hardware.input;
+import android.view.PointerIcon;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
@@ -819,6 +820,15 @@
}
}
+ /** @hide */
+ public void setCustomPointerIcon(PointerIcon icon) {
+ try {
+ mIm.setCustomPointerIcon(icon);
+ } catch (RemoteException ex) {
+ // Do nothing.
+ }
+ }
+
private void populateInputDevicesLocked() {
if (mInputDevicesChangedListener == null) {
final InputDevicesChangedListener listener = new InputDevicesChangedListener();
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 46b9a46..88cd7ca 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -784,6 +784,15 @@
}
/**
+ * Specifies the current custom pointer.
+ * @param icon the icon data.
+ * @hide
+ */
+ public void setCustomPointerIcon(PointerIcon icon) {
+ InputManager.getInstance().setCustomPointerIcon(icon);
+ }
+
+ /**
* Provides information about the range of values for a particular {@link MotionEvent} axis.
*
* @see InputDevice#getMotionRange(int)
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index d2a7d4a..aa879cd 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -16,8 +16,10 @@
package android.view;
+import android.annotation.NonNull;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.SparseArray;
import com.android.internal.util.XmlUtils;
import android.annotation.XmlRes;
@@ -39,13 +41,11 @@
* Pointer icons can be provided either by the system using system styles,
* or by applications using bitmaps or application resources.
* </p>
- *
- * @hide
*/
public final class PointerIcon implements Parcelable {
private static final String TAG = "PointerIcon";
- /** Style constant: Custom icon with a user-supplied bitmap. */
+ /** {@hide} Style constant: Custom icon with a user-supplied bitmap. */
public static final int STYLE_CUSTOM = -1;
/** Style constant: Null icon. It has no bitmap. */
@@ -54,6 +54,7 @@
/** Style constant: no icons are specified. If all views uses this, then falls back
* to the default style, but this is helpful to distinguish a view explicitly want
* to have the default icon.
+ * @hide
*/
public static final int STYLE_NOT_SPECIFIED = 1;
@@ -135,10 +136,11 @@
// conflicts with any system styles that may be defined in the future.
private static final int STYLE_OEM_FIRST = 10000;
- /** {@hide} The default pointer icon. */
+ /** The default pointer icon. */
public static final int STYLE_DEFAULT = STYLE_ARROW;
private static final PointerIcon gNullIcon = new PointerIcon(STYLE_NULL);
+ private static final SparseArray<PointerIcon> gSystemIcons = new SparseArray<PointerIcon>();
private final int mStyle;
private int mSystemIconResourceId;
@@ -160,6 +162,7 @@
* @return The null pointer icon.
*
* @see #STYLE_NULL
+ * @hide
*/
public static PointerIcon getNullIcon() {
return gNullIcon;
@@ -172,8 +175,9 @@
* @return The default pointer icon.
*
* @throws IllegalArgumentException if context is null.
+ * @hide
*/
- public static PointerIcon getDefaultIcon(Context context) {
+ public static PointerIcon getDefaultIcon(@NonNull Context context) {
return getSystemIcon(context, STYLE_DEFAULT);
}
@@ -187,7 +191,7 @@
*
* @throws IllegalArgumentException if context is null.
*/
- public static PointerIcon getSystemIcon(Context context, int style) {
+ public static PointerIcon getSystemIcon(@NonNull Context context, int style) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
@@ -196,6 +200,11 @@
return gNullIcon;
}
+ PointerIcon icon = gSystemIcons.get(style);
+ if (icon != null) {
+ return icon;
+ }
+
int styleIndex = getSystemIconStyleIndex(style);
if (styleIndex == 0) {
styleIndex = getSystemIconStyleIndex(STYLE_DEFAULT);
@@ -217,12 +226,13 @@
return style == STYLE_DEFAULT ? gNullIcon : getSystemIcon(context, STYLE_DEFAULT);
}
- PointerIcon icon = new PointerIcon(style);
+ icon = new PointerIcon(style);
if ((resourceId & 0xff000000) == 0x01000000) {
icon.mSystemIconResourceId = resourceId;
} else {
icon.loadResource(context, context.getResources(), resourceId);
}
+ gSystemIcons.append(style, icon);
return icon;
}
@@ -239,7 +249,8 @@
* @throws IllegalArgumentException if bitmap is null, or if the x/y hotspot
* parameters are invalid.
*/
- public static PointerIcon createCustomIcon(Bitmap bitmap, float hotSpotX, float hotSpotY) {
+ public static PointerIcon createCustomIcon(
+ @NonNull Bitmap bitmap, float hotSpotX, float hotSpotY) {
if (bitmap == null) {
throw new IllegalArgumentException("bitmap must not be null");
}
@@ -273,7 +284,7 @@
* @throws Resources.NotFoundException if the resource was not found or the drawable
* linked in the resource was not found.
*/
- public static PointerIcon loadCustomIcon(Resources resources, @XmlRes int resourceId) {
+ public static PointerIcon loadCustomIcon(@NonNull Resources resources, @XmlRes int resourceId) {
if (resources == null) {
throw new IllegalArgumentException("resources must not be null");
}
@@ -291,10 +302,9 @@
* @return The loaded pointer icon.
*
* @throws IllegalArgumentException if context is null.
- * @see #isLoaded()
* @hide
*/
- public PointerIcon load(Context context) {
+ public PointerIcon load(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
@@ -309,83 +319,11 @@
return result;
}
- /**
- * Returns true if the pointer icon style is {@link #STYLE_NULL}.
- *
- * @return True if the pointer icon style is {@link #STYLE_NULL}.
- */
- public boolean isNullIcon() {
- return mStyle == STYLE_NULL;
- }
-
- /**
- * Returns true if the pointer icon has been loaded and its bitmap and hotspot
- * information are available.
- *
- * @return True if the pointer icon is loaded.
- * @see #load(Context)
- */
- public boolean isLoaded() {
- return mBitmap != null || mStyle == STYLE_NULL;
- }
-
- /**
- * Gets the style of the pointer icon.
- *
- * @return The pointer icon style.
- */
+ /** @hide */
public int getStyle() {
return mStyle;
}
- /**
- * Gets the bitmap of the pointer icon.
- *
- * @return The pointer icon bitmap, or null if the style is {@link #STYLE_NULL}.
- *
- * @throws IllegalStateException if the bitmap is not loaded.
- * @see #isLoaded()
- * @see #load(Context)
- */
- public Bitmap getBitmap() {
- throwIfIconIsNotLoaded();
- return mBitmap;
- }
-
- /**
- * Gets the X offset of the pointer icon hotspot.
- *
- * @return The hotspot X offset.
- *
- * @throws IllegalStateException if the bitmap is not loaded.
- * @see #isLoaded()
- * @see #load(Context)
- */
- public float getHotSpotX() {
- throwIfIconIsNotLoaded();
- return mHotSpotX;
- }
-
- /**
- * Gets the Y offset of the pointer icon hotspot.
- *
- * @return The hotspot Y offset.
- *
- * @throws IllegalStateException if the bitmap is not loaded.
- * @see #isLoaded()
- * @see #load(Context)
- */
- public float getHotSpotY() {
- throwIfIconIsNotLoaded();
- return mHotSpotY;
- }
-
- private void throwIfIconIsNotLoaded() {
- if (!isLoaded()) {
- throw new IllegalStateException("The icon is not loaded.");
- }
- }
-
public static final Parcelable.Creator<PointerIcon> CREATOR
= new Parcelable.Creator<PointerIcon>() {
public PointerIcon createFromParcel(Parcel in) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3db18066..2368642 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2628,7 +2628,7 @@
static final int PFLAG3_POINTER_ICON_LSHIFT = 15;
/**
- * Value indicating {@link PointerIcon.STYLE_NOT_SPECIFIED}.
+ * Value indicating no specific pointer icons.
*/
private static final int PFLAG3_POINTER_ICON_NOT_SPECIFIED = 0 << PFLAG3_POINTER_ICON_LSHIFT;
@@ -2638,14 +2638,9 @@
private static final int PFLAG3_POINTER_ICON_NULL = 1 << PFLAG3_POINTER_ICON_LSHIFT;
/**
- * Value incicating {@link PointerIcon.STYLE_CUSTOM}.
- */
- private static final int PFLAG3_POINTER_ICON_CUSTOM = 2 << PFLAG3_POINTER_ICON_LSHIFT;
-
- /**
* The base value for other pointer icon shapes.
*/
- private static final int PFLAG3_POINTER_ICON_VALUE_START = 3 << PFLAG3_POINTER_ICON_LSHIFT;
+ private static final int PFLAG3_POINTER_ICON_VALUE_START = 2 << PFLAG3_POINTER_ICON_LSHIFT;
/**
* Always allow a user to over-scroll this view, provided it is a
@@ -3927,6 +3922,11 @@
private HandlerActionQueue mRunQueue;
/**
+ * The pointer icon when the mouse hovers on this view. The default is null.
+ */
+ private PointerIcon mPointerIcon;
+
+ /**
* @hide
*/
String mStartActivityRequestWho;
@@ -4490,7 +4490,7 @@
case R.styleable.View_pointerShape:
final int pointerShape = a.getInt(attr, PointerIcon.STYLE_NOT_SPECIFIED);
if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) {
- setPointerShape(pointerShape);
+ setPointerIcon(PointerIcon.getSystemIcon(context, pointerShape));
}
break;
}
@@ -21191,42 +21191,25 @@
}
}
- /** @hide */
- public int getPointerShape(MotionEvent event, float x, float y) {
- final int value = (mPrivateFlags3 & PFLAG3_POINTER_ICON_MASK);
- switch (value) {
- case PFLAG3_POINTER_ICON_NOT_SPECIFIED:
- return PointerIcon.STYLE_NOT_SPECIFIED;
- case PFLAG3_POINTER_ICON_NULL:
- return PointerIcon.STYLE_NULL;
- case PFLAG3_POINTER_ICON_CUSTOM:
- return PointerIcon.STYLE_CUSTOM;
- default:
- return ((value - PFLAG3_POINTER_ICON_VALUE_START) >> PFLAG3_POINTER_ICON_LSHIFT)
- + PointerIcon.STYLE_ARROW;
- }
+ /**
+ * Returns the pointer icon for the motion event, or null if it doesn't specify the icon.
+ * The default implementation does not care the location or event types, but some subclasses
+ * may use it (such as WebViews).
+ * @param event The MotionEvent from a mouse
+ * @param x The x position of the event, local to the view
+ * @param y The y position of the event, local to the view
+ * @see PointerIcon
+ */
+ public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
+ return mPointerIcon;
}
- /** @hide */
- public void setPointerShape(int pointerShape) {
- int newValue;
- if (pointerShape == PointerIcon.STYLE_NOT_SPECIFIED) {
- newValue = PFLAG3_POINTER_ICON_NOT_SPECIFIED;
- } else if (pointerShape == PointerIcon.STYLE_NULL) {
- newValue = PFLAG3_POINTER_ICON_NULL;
- } else if (pointerShape == PointerIcon.STYLE_CUSTOM) {
- newValue = PFLAG3_POINTER_ICON_CUSTOM;
- } else if (pointerShape >= PointerIcon.STYLE_ARROW
- && pointerShape <= PointerIcon.STYLE_GRABBING) {
- newValue = ((pointerShape - PointerIcon.STYLE_ARROW) << PFLAG3_POINTER_ICON_LSHIFT)
- + PFLAG3_POINTER_ICON_VALUE_START;
- } else {
- Log.w(VIEW_LOG_TAG, "Invalid pointer shape " + pointerShape + " is specified.");
- return;
- }
- if (newValue != (mPrivateFlags3 & PFLAG3_POINTER_ICON_MASK)) {
- mPrivateFlags3 = (mPrivateFlags3 & ~PFLAG3_POINTER_ICON_MASK) | newValue;
- }
+ /**
+ * Set the pointer icon for the current view.
+ * @param pointerIcon A PointerIcon instance which will be shown when the mouse hovers.
+ */
+ public void setPointerIcon(PointerIcon pointerIcon) {
+ mPointerIcon = pointerIcon;
}
//
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0f7d296..cd93dab 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1715,9 +1715,8 @@
return false;
}
- /** @hide */
@Override
- public int getPointerShape(MotionEvent event, float x, float y) {
+ public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
// Check what the child under the pointer says about the pointer.
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
@@ -1731,9 +1730,9 @@
? children[childIndex] : preorderedList.get(childIndex);
PointF point = getLocalPoint();
if (isTransformedTouchPointInView(x, y, child, point)) {
- final int pointerShape = child.getPointerShape(event, point.x, point.y);
- if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) {
- return pointerShape;
+ final PointerIcon pointerIcon = child.getPointerIcon(event, point.x, point.y);
+ if (pointerIcon != null) {
+ return pointerIcon;
}
break;
}
@@ -1742,7 +1741,7 @@
// The pointer is not a child or the child has no preferences, returning the default
// implementation.
- return super.getPointerShape(event, x, y);
+ return super.getPointerIcon(event, x, y);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 12cf66e..faf2640 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -332,6 +332,7 @@
private int mFpsNumFrames;
private int mPointerIconShape = PointerIcon.STYLE_NOT_SPECIFIED;
+ private PointerIcon mCustomPointerIcon = null;
/**
* see {@link #playSoundEffect(int)}
@@ -4210,16 +4211,23 @@
final float y = event.getY();
if (event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT
&& x >= 0 && x < mView.getWidth() && y >= 0 && y < mView.getHeight()) {
- int pointerShape = mView.getPointerShape(event, x, y);
- if (pointerShape == PointerIcon.STYLE_NOT_SPECIFIED) {
- pointerShape = PointerIcon.STYLE_DEFAULT;
- }
+ PointerIcon pointerIcon = mView.getPointerIcon(event, x, y);
+ int pointerShape = (pointerIcon != null) ?
+ pointerIcon.getStyle() : PointerIcon.STYLE_DEFAULT;
- if (mPointerIconShape != pointerShape) {
- mPointerIconShape = pointerShape;
- final InputDevice inputDevice = event.getDevice();
- if (inputDevice != null) {
- inputDevice.setPointerShape(pointerShape);
+ final InputDevice inputDevice = event.getDevice();
+ if (inputDevice != null) {
+ if (mPointerIconShape != pointerShape) {
+ mPointerIconShape = pointerShape;
+ if (mPointerIconShape != PointerIcon.STYLE_CUSTOM) {
+ mCustomPointerIcon = null;
+ inputDevice.setPointerShape(pointerShape);
+ }
+ }
+ if (mPointerIconShape == PointerIcon.STYLE_CUSTOM &&
+ !pointerIcon.equals(mCustomPointerIcon)) {
+ mCustomPointerIcon = pointerIcon;
+ inputDevice.setCustomPointerIcon(mCustomPointerIcon);
}
}
} else if (event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d6bc27c..2e884cc 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1171,6 +1171,16 @@
*/
public static final int PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY = 0x00004000;
+ /**
+ * Flag to indicate that this window is not expected to be replaced across
+ * configuration change triggered activity relaunches. In general the WindowManager
+ * expects Windows to be replaced after relaunch, and thus it will preserve their surfaces
+ * until the replacement is ready to show in order to prevent visual glitch. However
+ * some windows, such as PopupWindows expect to be cleared across configuration change,
+ * and thus should hint to the WindowManager that it should not wait for a replacement.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH = 0x00008000;
/**
* Control flags that are private to the platform.
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 7b9de79..f4c343a 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
+
import com.android.internal.R;
import android.annotation.NonNull;
@@ -1311,6 +1313,8 @@
p.width = mLastWidth = mWidth;
}
+ p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
+
// Used for debugging.
p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5574f86..17c803f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5955,15 +5955,12 @@
return mLayout != null ? mLayout.getHeight() : 0;
}
- /**
- * @hide
- */
@Override
- public int getPointerShape(MotionEvent event, float x, float y) {
+ public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
if (isTextSelectable() || isTextEditable()) {
- return PointerIcon.STYLE_TEXT;
+ return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_TEXT);
}
- return super.getPointerShape(event, x, y);
+ return super.getPointerIcon(event, x, y);
}
@Override
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index d7e2c02..71be52e 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -65,33 +65,34 @@
return OK;
}
- jobject loadedPointerIconObj = env->CallObjectMethod(pointerIconObj,
- gPointerIconClassInfo.load, contextObj);
- if (env->ExceptionCheck() || !loadedPointerIconObj) {
+ ScopedLocalRef<jobject> loadedPointerIconObj(env, env->CallObjectMethod(pointerIconObj,
+ gPointerIconClassInfo.load, contextObj));
+ if (env->ExceptionCheck() || !loadedPointerIconObj.get()) {
ALOGW("An exception occurred while loading a pointer icon.");
LOGW_EX(env);
env->ExceptionClear();
return UNKNOWN_ERROR;
}
+ return android_view_PointerIcon_getLoadedIcon(env, loadedPointerIconObj.get(), outPointerIcon);
+}
- outPointerIcon->style = env->GetIntField(loadedPointerIconObj,
- gPointerIconClassInfo.mStyle);
- outPointerIcon->hotSpotX = env->GetFloatField(loadedPointerIconObj,
- gPointerIconClassInfo.mHotSpotX);
- outPointerIcon->hotSpotY = env->GetFloatField(loadedPointerIconObj,
- gPointerIconClassInfo.mHotSpotY);
+status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
+ PointerIcon* outPointerIcon) {
+ outPointerIcon->style = env->GetIntField(pointerIconObj, gPointerIconClassInfo.mStyle);
+ outPointerIcon->hotSpotX = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotX);
+ outPointerIcon->hotSpotY = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotY);
- jobject bitmapObj = env->GetObjectField(loadedPointerIconObj, gPointerIconClassInfo.mBitmap);
- if (bitmapObj) {
- GraphicsJNI::getSkBitmap(env, bitmapObj, &(outPointerIcon->bitmap));
- env->DeleteLocalRef(bitmapObj);
+ ScopedLocalRef<jobject> bitmapObj(
+ env, env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmap));
+ if (bitmapObj.get()) {
+ GraphicsJNI::getSkBitmap(env, bitmapObj.get(), &(outPointerIcon->bitmap));
}
ScopedLocalRef<jobjectArray> bitmapFramesObj(env, reinterpret_cast<jobjectArray>(
- env->GetObjectField(loadedPointerIconObj, gPointerIconClassInfo.mBitmapFrames)));
+ env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmapFrames)));
if (bitmapFramesObj.get()) {
outPointerIcon->durationPerFrame = env->GetIntField(
- loadedPointerIconObj, gPointerIconClassInfo.mDurationPerFrame);
+ pointerIconObj, gPointerIconClassInfo.mDurationPerFrame);
jsize size = env->GetArrayLength(bitmapFramesObj.get());
outPointerIcon->bitmapFrames.resize(size);
for (jsize i = 0; i < size; ++i) {
@@ -100,7 +101,6 @@
}
}
- env->DeleteLocalRef(loadedPointerIconObj);
return OK;
}
diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h
index ca08085..00bdfb4 100644
--- a/core/jni/android_view_PointerIcon.h
+++ b/core/jni/android_view_PointerIcon.h
@@ -97,6 +97,11 @@
extern status_t android_view_PointerIcon_load(JNIEnv* env,
jobject pointerIconObj, jobject contextObj, PointerIcon* outPointerIcon);
+/* Obtain the data of pointerIconObj and put to outPointerIcon. */
+extern status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
+ PointerIcon* outPointerIcon);
+
+
/* Loads the bitmap associated with a pointer icon by style.
* If pointerIconObj is NULL, returns OK and a pointer icon with POINTER_ICON_STYLE_NULL. */
extern status_t android_view_PointerIcon_loadSystemIcon(JNIEnv* env,
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 6a1167a..529849e 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -439,6 +439,17 @@
}
}
+void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
+ AutoMutex _l(mLock);
+
+ const int32_t iconId = mPolicy->getCustomPointerIconId();
+ mLocked.additionalMouseResources[iconId] = icon;
+ mLocked.requestedPointerShape = iconId;
+ mLocked.presentationChanged = true;
+
+ updatePointerLocked();
+}
+
void PointerController::handleMessage(const Message& message) {
switch (message.what) {
case MSG_INACTIVITY_TIMEOUT:
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 4fd2d85..9ba37b3 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -67,6 +67,7 @@
virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
std::map<int32_t, PointerAnimation>* outAnimationResources) = 0;
virtual int32_t getDefaultPointerIconId() = 0;
+ virtual int32_t getCustomPointerIconId() = 0;
};
@@ -105,6 +106,7 @@
virtual void clearSpots();
void updatePointerShape(int32_t iconId);
+ void setCustomPointerIcon(const SpriteIcon& icon);
void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 44ff079..6d947d1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -28,6 +28,7 @@
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
@@ -592,9 +593,26 @@
IOException copyError = null;
try {
- srcFile = mSrcClient.openFile(srcInfo.derivedUri, "r", canceller);
+ // If the file is virtual, but can be converted to another format, then try to copy it
+ // as such format.
+ if (srcInfo.isVirtualDocument() && srcInfo.isTypedDocument()) {
+ final String[] streamTypes = mSrcClient.getStreamTypes(srcInfo.derivedUri, "*/*");
+ if (streamTypes.length > 0) {
+ // Pick the first streamable format.
+ final AssetFileDescriptor srcFileAsAsset =
+ mSrcClient.openTypedAssetFileDescriptor(
+ srcInfo.derivedUri, streamTypes[0], null, canceller);
+ srcFile = srcFileAsAsset.getParcelFileDescriptor();
+ src = new AssetFileDescriptor.AutoCloseInputStream(srcFileAsAsset);
+ } else {
+ // TODO: Log failures. b/26192412
+ mFailedFiles.add(srcInfo);
+ }
+ } else {
+ srcFile = mSrcClient.openFile(srcInfo.derivedUri, "r", canceller);
+ src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile);
+ }
dstFile = mDstClient.openFile(dstInfo.derivedUri, "w", canceller);
- src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile);
dst = new ParcelFileDescriptor.AutoCloseOutputStream(dstFile);
byte[] buffer = new byte[8192];
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 69b574d..215c6e6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -251,6 +251,14 @@
return isDirectory() || isArchive();
}
+ public boolean isVirtualDocument() {
+ return (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0;
+ }
+
+ public boolean isTypedDocument() {
+ return (flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) != 0;
+ }
+
public int hashCode() {
return derivedUri.hashCode() + mimeType.hashCode();
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index ebbc5e1..6d57c5b 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -27,7 +27,9 @@
import java.io.FileNotFoundException;
+import static android.provider.DocumentsContract.Document.*;
import static com.android.mtp.MtpDatabase.strings;
+import static com.android.mtp.MtpDatabaseConstants.*;
@SmallTest
public class MtpDatabaseTest extends AndroidTestCase {
@@ -60,6 +62,18 @@
mDatabase = null;
}
+ private static int getInt(Cursor cursor, String columnName) {
+ return cursor.getInt(cursor.getColumnIndex(columnName));
+ }
+
+ private static boolean isNull(Cursor cursor, String columnName) {
+ return cursor.isNull(cursor.getColumnIndex(columnName));
+ }
+
+ private static String getString(Cursor cursor, String columnName) {
+ return cursor.getString(cursor.getColumnIndex(columnName));
+ }
+
public void testPutRootDocuments() throws Exception {
mDatabase.getMapper().startAddingDocuments("deviceDocId");
mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
@@ -73,27 +87,27 @@
assertEquals(3, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("deviceId", 0, cursor.getInt(1));
- assertEquals("storageId", 1, cursor.getInt(2));
- assertTrue("objectHandle", cursor.isNull(3));
- assertEquals("mimeType", DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(4));
- assertEquals("displayName", "Device Storage", cursor.getString(5));
- assertTrue("summary", cursor.isNull(6));
- assertTrue("lastModified", cursor.isNull(7));
- assertEquals("icon", R.drawable.ic_root_mtp, cursor.getInt(8));
- assertEquals("flag", 0, cursor.getInt(9));
- assertEquals("size", 1000, cursor.getInt(10));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
+ assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+ assertTrue(isNull(cursor, COLUMN_SUMMARY));
+ assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
+ assertEquals(R.drawable.ic_root_mtp, getInt(cursor, COLUMN_ICON));
+ assertEquals(0, getInt(cursor, COLUMN_FLAGS));
+ assertEquals(1000, getInt(cursor, COLUMN_SIZE));
assertEquals(
- "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, cursor.getInt(11));
+ MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("displayName", "Device Storage", cursor.getString(5));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 3, cursor.getInt(0));
- assertEquals("displayName", "Device /@#%&<>Storage", cursor.getString(5));
+ assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals("Device /@#%&<>Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -166,61 +180,61 @@
assertEquals(3, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("deviceId", 0, cursor.getInt(1));
- assertEquals("storageId", 0, cursor.getInt(2));
- assertEquals("objectHandle", 100, cursor.getInt(3));
- assertEquals("mimeType", "text/plain", cursor.getString(4));
- assertEquals("displayName", "note.txt", cursor.getString(5));
- assertTrue("summary", cursor.isNull(6));
- assertTrue("lastModified", cursor.isNull(7));
- assertTrue("icon", cursor.isNull(8));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
+ assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals(100, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("text/plain", getString(cursor, COLUMN_MIME_TYPE));
+ assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
+ assertTrue(isNull(cursor, COLUMN_SUMMARY));
+ assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
+ assertTrue(isNull(cursor, COLUMN_ICON));
assertEquals(
- "flag",
+ COLUMN_FLAGS,
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
cursor.getInt(9));
- assertEquals("size", 1024, cursor.getInt(10));
+ assertEquals(1024, getInt(cursor, COLUMN_SIZE));
assertEquals(
- "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, cursor.getInt(11));
+ MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("deviceId", 0, cursor.getInt(1));
- assertEquals("storageId", 0, cursor.getInt(2));
- assertEquals("objectHandle", 101, cursor.getInt(3));
- assertEquals("mimeType", "image/jpeg", cursor.getString(4));
- assertEquals("displayName", "image.jpg", cursor.getString(5));
- assertTrue("summary", cursor.isNull(6));
- assertTrue("lastModified", cursor.isNull(7));
- assertTrue("icon", cursor.isNull(8));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
+ assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals(101, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("image/jpeg", getString(cursor, COLUMN_MIME_TYPE));
+ assertEquals("image.jpg", getString(cursor, COLUMN_DISPLAY_NAME));
+ assertTrue(isNull(cursor, COLUMN_SUMMARY));
+ assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
+ assertTrue(isNull(cursor, COLUMN_ICON));
assertEquals(
- "flag",
+ COLUMN_FLAGS,
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
cursor.getInt(9));
- assertEquals("size", 2 * 1024 * 1024, cursor.getInt(10));
+ assertEquals(2 * 1024 * 1024, getInt(cursor, COLUMN_SIZE));
assertEquals(
- "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, cursor.getInt(11));
+ MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
cursor.moveToNext();
- assertEquals("documentId", 3, cursor.getInt(0));
- assertEquals("deviceId", 0, cursor.getInt(1));
- assertEquals("storageId", 0, cursor.getInt(2));
- assertEquals("objectHandle", 102, cursor.getInt(3));
- assertEquals("mimeType", "audio/mpeg", cursor.getString(4));
- assertEquals("displayName", "music.mp3", cursor.getString(5));
- assertTrue("summary", cursor.isNull(6));
- assertTrue("lastModified", cursor.isNull(7));
- assertTrue("icon", cursor.isNull(8));
+ assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
+ assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals(102, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("audio/mpeg", getString(cursor, COLUMN_MIME_TYPE));
+ assertEquals("music.mp3", getString(cursor, COLUMN_DISPLAY_NAME));
+ assertTrue(isNull(cursor, COLUMN_SUMMARY));
+ assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
+ assertTrue(isNull(cursor, COLUMN_ICON));
assertEquals(
- "flag",
+ COLUMN_FLAGS,
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
cursor.getInt(9));
- assertEquals("size", 3 * 1024 * 1024, cursor.getInt(10));
+ assertEquals(3 * 1024 * 1024, getInt(cursor, COLUMN_SIZE));
assertEquals(
- "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, cursor.getInt(11));
+ MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
cursor.close();
}
@@ -246,13 +260,13 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("storageId", 100, cursor.getInt(1));
- assertEquals("name", "Device Storage A", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("storageId", 101, cursor.getInt(1));
- assertEquals("name", "Device Storage B", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(101, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -260,11 +274,11 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 1000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 1001, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
@@ -274,13 +288,13 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertTrue("storageId", cursor.isNull(1));
- assertEquals("name", "Device Storage A", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertTrue("storageId", cursor.isNull(1));
- assertEquals("name", "Device Storage B", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -288,11 +302,11 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 1000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 1001, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
@@ -306,17 +320,17 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(3, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertTrue("storageId", cursor.isNull(1));
- assertEquals("name", "Device Storage A", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertTrue("storageId", cursor.isNull(1));
- assertEquals("name", "Device Storage B", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 4, cursor.getInt(0));
- assertEquals("storageId", 202, cursor.getInt(1));
- assertEquals("name", "Device Storage C", cursor.getString(2));
+ assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -324,14 +338,14 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(3, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 1000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 1001, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 4, cursor.getInt(0));
- assertEquals("availableBytes", 2002, cursor.getInt(1));
+ assertEquals(4, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2002, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
@@ -341,13 +355,13 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("storageId", 200, cursor.getInt(1));
- assertEquals("name", "Device Storage A", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 4, cursor.getInt(0));
- assertEquals("storageId", 202, cursor.getInt(1));
- assertEquals("name", "Device Storage C", cursor.getString(2));
+ assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -355,11 +369,11 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 2000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 4, cursor.getInt(0));
- assertEquals("availableBytes", 2002, cursor.getInt(1));
+ assertEquals(4, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2002, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
@@ -383,19 +397,19 @@
assertEquals(3, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertTrue("objectHandle", cursor.isNull(1));
- assertEquals("name", "note.txt", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertTrue("objectHandle", cursor.isNull(1));
- assertEquals("name", "image.jpg", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("image.jpg", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 3, cursor.getInt(0));
- assertTrue("objectHandle", cursor.isNull(1));
- assertEquals("name", "music.mp3", cursor.getString(2));
+ assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("music.mp3", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -411,9 +425,9 @@
assertEquals(4, cursor.getCount());
cursor.moveToPosition(3);
- assertEquals("documentId", 5, cursor.getInt(0));
- assertEquals("objectHandle", 203, cursor.getInt(1));
- assertEquals("name", "video.mp4", cursor.getString(2));
+ assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -425,14 +439,14 @@
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("objectHandle", 200, cursor.getInt(1));
- assertEquals("name", "note.txt", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 5, cursor.getInt(0));
- assertEquals("objectHandle", 203, cursor.getInt(1));
- assertEquals("name", "video.mp4", cursor.getString(2));
+ assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
}
@@ -460,13 +474,13 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("storageId", 100, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("storageId", 100, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -474,11 +488,11 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 0, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(0, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 0, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(0, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
@@ -499,13 +513,13 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("storageId", 200, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("storageId", 300, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -513,11 +527,11 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 2000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 3000, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(3000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
@@ -552,16 +566,16 @@
final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId1");
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("objectHandle", 200, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_OBJECT_HANDLE));
cursor.close();
}
{
final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId2");
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertTrue("objectHandle", cursor.isNull(1));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
cursor.close();
}
}
@@ -599,17 +613,17 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("storageId", 300, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
{
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 3000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(3000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
@@ -642,24 +656,24 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("storageId", 200, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 3, cursor.getInt(0));
- assertEquals("storageId", 201, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(201, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
{
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 2000, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 3, cursor.getInt(0));
- assertEquals("availableBytes", 2001, cursor.getInt(1));
+ assertEquals(3, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
@@ -687,9 +701,9 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("storageId", 100, cursor.getInt(1));
- assertEquals("name", "Device Storage B", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
{
@@ -700,8 +714,8 @@
final Cursor cursor = mDatabase.queryRoots(columns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 1000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 93264ff..c01f170 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -30,6 +30,7 @@
import android.view.Display;
import android.view.DisplayInfo;
import android.view.MotionEvent;
+import android.view.PointerIcon;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
@@ -118,8 +119,8 @@
updateDisplayInfo();
boolean landscape = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE;
- mHandle.setPointerShape(
- landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW);
+ mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
+ landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW));
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f2d0031..8b37383 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -213,6 +213,7 @@
private static native void nativeMonitor(long ptr);
private static native void nativeSetPointerIconShape(long ptr, int iconId);
private static native void nativeReloadPointerIcons(long ptr);
+ private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon);
// Input event injection constants defined in InputDispatcher.h.
private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
@@ -1451,6 +1452,12 @@
nativeSetPointerIconShape(mPtr, iconId);
}
+ // Binder call
+ @Override
+ public void setCustomPointerIcon(PointerIcon icon) {
+ nativeSetCustomPointerIcon(mPtr, icon);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fd5c704..b49641f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -122,21 +123,6 @@
// True if the windows associated with this token should be cropped to their stack bounds.
boolean mCropWindowsToStack;
- // This application will have its window replaced due to relaunch. This allows window manager
- // to differentiate between simple removal of a window and replacement. In the latter case it
- // will preserve the old window until the new one is drawn.
- boolean mWillReplaceWindow;
- // If true, the replaced window was already requested to be removed.
- boolean mReplacingRemoveRequested;
- // Whether the replacement of the window should trigger app transition animation.
- boolean mAnimateReplacingWindow;
- // If not null, the window that will be used to replace the old one. This is being set when
- // the window is added and unset when this window reports its first draw.
- WindowState mReplacingWindow;
- // Whether the new window has replaced the old one, so the old one can be removed without
- // blinking.
- boolean mHasReplacedWindow;
-
AppWindowToken(WindowManagerService _service, IApplicationToken _token,
boolean _voiceInteraction) {
super(_service, _token.asBinder(),
@@ -392,6 +378,62 @@
}
}
+ void setReplacingWindows(boolean animate) {
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken
+ + " with replacing windows.");
+
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ final WindowState w = allAppWindows.get(i);
+ w.setReplacing(animate);
+ }
+ if (animate) {
+ // Set-up dummy animation so we can start treating windows associated with this
+ // token like they are in transition before the new app window is ready for us to
+ // run the real transition animation.
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
+ "setReplacingWindow() Setting dummy animation on: " + this);
+ mAppAnimator.setDummyAnimation();
+ }
+ }
+
+ void addWindow(WindowState w) {
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ WindowState candidate = allAppWindows.get(i);
+ if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null &&
+ candidate.getWindowTag().equals(w.getWindowTag().toString())) {
+ candidate.mReplacingWindow = w;
+ }
+ }
+ allAppWindows.add(w);
+ }
+
+ boolean waitingForReplacement() {
+ for (int i = allAppWindows.size() -1; i >= 0; i--) {
+ WindowState candidate = allAppWindows.get(i);
+ if (candidate.mWillReplaceWindow) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void clearTimedoutReplaceesLocked() {
+ for (int i = allAppWindows.size() - 1; i >= 0;
+ // removeWindowLocked at bottom of loop may remove multiple entries from
+ // allAppWindows if the window to be removed has child windows. It also may
+ // not remove any windows from allAppWindows at all if win is exiting and
+ // currently animating away. This ensures that winNdx is monotonically decreasing
+ // and never beyond allAppWindows bounds.
+ i = Math.min(i - 1, allAppWindows.size() - 1)) {
+ WindowState candidate = allAppWindows.get(i);
+ if (candidate.mWillReplaceWindow == false) {
+ continue;
+ }
+ candidate.mWillReplaceWindow = false;
+ service.removeWindowLocked(candidate);
+ }
+ }
+
@Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 3db9ae0..2e424d0 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -506,7 +506,7 @@
inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
- replacing = replacing || (w.mAppToken != null && w.mAppToken.mWillReplaceWindow);
+ replacing = replacing || w.mWillReplaceWindow;
// If the app is executing an animation because the keyguard is going away,
// keep the wallpaper during the animation so it doesn't flicker out.
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index a6523a4..6a5183f 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -918,10 +918,6 @@
}
void requestRemovalOfReplacedWindows(WindowState win) {
- final AppWindowToken token = win.mAppToken;
- if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == win) {
- token.mHasReplacedWindow = true;
- }
mRemoveReplacedWindows = true;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4190bd77..456c416 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -269,6 +269,9 @@
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
+ /** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */
+ static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000;
+
/** Amount of time to allow a last ANR message to exist before freeing the memory. */
static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours
/**
@@ -1350,10 +1353,7 @@
final AppWindowToken appToken = win.mAppToken;
if (appToken != null) {
if (addToToken) {
- appToken.allAppWindows.add(win);
- }
- if (appToken.mWillReplaceWindow) {
- appToken.mReplacingWindow = win;
+ appToken.addWindow(win);
}
}
}
@@ -2088,14 +2088,15 @@
}
private void prepareWindowReplacementTransition(AppWindowToken atoken) {
- if (atoken == null || !atoken.mWillReplaceWindow || !atoken.mAnimateReplacingWindow) {
+ if (atoken == null) {
return;
}
atoken.allDrawn = false;
WindowState replacedWindow = null;
for (int i = atoken.windows.size() - 1; i >= 0 && replacedWindow == null; i--) {
WindowState candidate = atoken.windows.get(i);
- if (candidate.mExiting) {
+ if (candidate.mExiting && candidate.mWillReplaceWindow
+ && candidate.mAnimateReplacingWindow) {
replacedWindow = candidate;
}
}
@@ -2195,7 +2196,7 @@
+ " app-animation="
+ (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
+ " mWillReplaceWindow="
- + (win.mAppToken != null ? win.mAppToken.mWillReplaceWindow : false)
+ + win.mWillReplaceWindow
+ " inPendingTransaction="
+ (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+ " mDisplayFrozen=" + mDisplayFrozen);
@@ -2207,13 +2208,13 @@
// animation wouldn't be seen.
if (win.mHasSurface && okToDisplay()) {
final AppWindowToken appToken = win.mAppToken;
- if (appToken != null && appToken.mWillReplaceWindow) {
+ if (win.mWillReplaceWindow) {
// This window is going to be replaced. We need to keep it around until the new one
// gets added, then we will get rid of this one.
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Preserving " + win + " until the new one is "
+ "added");
win.mExiting = true;
- appToken.mReplacingRemoveRequested = true;
+ win.mReplacingRemoveRequested = true;
Binder.restoreCallingIdentity(origId);
return;
}
@@ -4052,7 +4053,7 @@
// transition animation
// * or this is an opening app and windows are being replaced.
if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
- (visible && wtoken.mWillReplaceWindow)) {
+ (visible && wtoken.waitingForReplacement())) {
boolean changed = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG_WM, "Changing app " + wtoken + " hidden=" + wtoken.hidden
@@ -7514,6 +7515,8 @@
public static final int TWO_FINGER_SCROLL_START = 45;
public static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = 46;
+ public static final int WINDOW_REPLACEMENT_TIMEOUT = 47;
+
/**
* Used to denote that an integer field in a message will not be used.
*/
@@ -8096,6 +8099,13 @@
toast.show();
}
break;
+ case WINDOW_REPLACEMENT_TIMEOUT: {
+ final AppWindowToken token = (AppWindowToken) msg.obj;
+ synchronized (mWindowMap) {
+ token.clearTimedoutReplaceesLocked();
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -8618,7 +8628,7 @@
final int numTokens = tokens.size();
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
- if (wtoken.mIsExiting && !wtoken.mWillReplaceWindow) {
+ if (wtoken.mIsExiting && !wtoken.waitingForReplacement()) {
continue;
}
i = reAddAppWindowsLocked(displayContent, i, wtoken);
@@ -8727,8 +8737,8 @@
private void forceHigherLayerIfNeeded(WindowState w, WindowStateAnimator winAnimator,
AppWindowToken wtoken) {
boolean force = false;
- if (wtoken.mWillReplaceWindow && wtoken.mReplacingWindow != w
- && wtoken.mAnimateReplacingWindow) {
+
+ if (w.mWillReplaceWindow) {
// We know that we will be animating a relaunching window in the near future,
// which will receive a z-order increase. We want the replaced window to
// immediately receive the same treatment, e.g. to be above the dock divider.
@@ -10244,26 +10254,20 @@
* @param token Application token for which the activity will be relaunched.
*/
public void setReplacingWindow(IBinder token, boolean animate) {
+ AppWindowToken appWindowToken = null;
synchronized (mWindowMap) {
- AppWindowToken appWindowToken = findAppWindowToken(token);
+ appWindowToken = findAppWindowToken(token);
if (appWindowToken == null || !appWindowToken.isVisible()) {
Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token);
return;
}
- if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken
- + " as replacing window.");
- appWindowToken.mWillReplaceWindow = true;
- appWindowToken.mHasReplacedWindow = false;
- appWindowToken.mAnimateReplacingWindow = animate;
+ appWindowToken.setReplacingWindows(animate);
+ }
- if (animate) {
- // Set-up dummy animation so we can start treating windows associated with this
- // token like they are in transition before the new app window is ready for us to
- // run the real transition animation.
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
- "setReplacingWindow() Setting dummy animation on: " + appWindowToken);
- appWindowToken.mAppAnimator.setDummyAnimation();
- }
+ if (appWindowToken != null) {
+ mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
+ mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken),
+ WINDOW_REPLACEMENT_TIMEOUT_DURATION);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5a589e3..e4a6806 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -75,6 +75,7 @@
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -403,6 +404,18 @@
// used to start an entering animation earlier.
public boolean mSurfaceSaved = false;
+ // This window will be replaced due to relaunch. This allows window manager
+ // to differentiate between simple removal of a window and replacement. In the latter case it
+ // will preserve the old window until the new one is drawn.
+ boolean mWillReplaceWindow = false;
+ // If true, the replaced window was already requested to be removed.
+ boolean mReplacingRemoveRequested = false;
+ // Whether the replacement of the window should trigger app transition animation.
+ boolean mAnimateReplacingWindow = false;
+ // If not null, the window that will be used to replace the old one. This is being set when
+ // the window is added and unset when this window reports its first draw.
+ WindowState mReplacingWindow = null;
+
/**
* Wake lock for drawing.
* Even though it's slightly more expensive to do so, we will use a separate wake lock
@@ -580,8 +593,7 @@
@Override
public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
Rect osf) {
- if (mAppToken != null && mAppToken.mWillReplaceWindow
- && (mExiting || !mAppToken.mReplacingRemoveRequested)) {
+ if (mWillReplaceWindow && (mExiting || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
// want to apply any more changes to it, so it remains in this state until new window
@@ -1343,17 +1355,17 @@
}
void maybeRemoveReplacedWindow() {
- AppWindowToken token = mAppToken;
- if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == this
- && token.mHasReplacedWindow) {
- if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replacing window: " + this);
- token.mWillReplaceWindow = false;
- token.mAnimateReplacingWindow = false;
- token.mReplacingRemoveRequested = false;
- token.mReplacingWindow = null;
- token.mHasReplacedWindow = false;
- for (int i = token.allAppWindows.size() - 1; i >= 0; i--) {
- final WindowState win = token.allAppWindows.get(i);
+ if (mAppToken == null) {
+ return;
+ }
+ for (int i = mAppToken.allAppWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = mAppToken.allAppWindows.get(i);
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + win);
+ if (win.mWillReplaceWindow && win.mReplacingWindow == this) {
+ win.mWillReplaceWindow = false;
+ win.mAnimateReplacingWindow = false;
+ win.mReplacingRemoveRequested = false;
+ win.mReplacingWindow = null;
if (win.mExiting) {
mService.removeWindowInnerLocked(win);
}
@@ -2161,7 +2173,7 @@
+ " " + getWindowTag();
}
- private CharSequence getWindowTag() {
+ CharSequence getWindowTag() {
CharSequence tag = mAttrs.getTitle();
if (tag == null || tag.length() <= 0) {
tag = mAttrs.packageName;
@@ -2259,4 +2271,12 @@
boolean isChildWindow() {
return mAttachedWindow != null;
}
+
+ void setReplacing(boolean animate) {
+ if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) == 0) {
+ mWillReplaceWindow = true;
+ mReplacingWindow = null;
+ mAnimateReplacingWindow = animate;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index cd82a5f..d4001cd 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1192,7 +1192,7 @@
// different stack. If we suddenly crop it to the new stack bounds, it might get cut off.
// We don't want it to happen, so we let it ignore the stack bounds until it gets removed.
// The window that will replace it will abide them.
- if (isAnimating() && (appToken.mWillReplaceWindow || w.inDockedWorkspace()
+ if (isAnimating() && (w.mWillReplaceWindow || w.inDockedWorkspace()
|| w.inFreeformWorkspace())) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 6001321..160c97f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1193,7 +1193,7 @@
// if app window is removed, or window relayout to invisible. We don't want to
// clear it out for windows that get replaced, because the animation depends on
// the flag to remove the replaced window.
- if (win.mAppToken == null || !win.mAppToken.mWillReplaceWindow) {
+ if (!win.mWillReplaceWindow) {
win.mExiting = false;
}
if (win.mWinAnimator.mAnimLayer > layer) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9c9a5da..4c8474a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -206,6 +206,7 @@
void reloadCalibration();
void setPointerIconShape(int32_t iconId);
void reloadPointerIcons();
+ void setCustomPointerIcon(const SpriteIcon& icon);
/* --- InputReaderPolicyInterface implementation --- */
@@ -248,6 +249,7 @@
virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
std::map<int32_t, PointerAnimation>* outAnimationResources);
virtual int32_t getDefaultPointerIconId();
+ virtual int32_t getCustomPointerIconId();
private:
sp<InputManager> mInputManager;
@@ -790,6 +792,14 @@
}
}
+void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
+ AutoMutex _l(mLock);
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != NULL) {
+ controller->setCustomPointerIcon(icon);
+ }
+}
+
TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
JNIEnv *env, jfloatArray matrixArr) {
ScopedFloatArrayRO matrix(env, matrixArr);
@@ -1090,6 +1100,10 @@
return POINTER_ICON_STYLE_ARROW;
}
+int32_t NativeInputManager::getCustomPointerIconId() {
+ return POINTER_ICON_STYLE_CUSTOM;
+}
+
// ----------------------------------------------------------------------------
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
@@ -1437,6 +1451,20 @@
im->reloadPointerIcons();
}
+static void nativeSetCustomPointerIcon(JNIEnv* env, jclass /* clazz */,
+ jlong ptr, jobject iconObj) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ PointerIcon pointerIcon;
+ android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+
+ SpriteIcon spriteIcon;
+ pointerIcon.bitmap.copyTo(&spriteIcon.bitmap, kN32_SkColorType);
+ spriteIcon.hotSpotX = pointerIcon.hotSpotX;
+ spriteIcon.hotSpotY = pointerIcon.hotSpotY;
+ im->setCustomPointerIcon(spriteIcon);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputManagerMethods[] = {
@@ -1499,6 +1527,8 @@
(void*) nativeSetPointerIconShape },
{ "nativeReloadPointerIcons", "(J)V",
(void*) nativeReloadPointerIcons },
+ { "nativeSetCustomPointerIcon", "(JLandroid/view/PointerIcon;)V",
+ (void*) nativeSetCustomPointerIcon },
};
#define FIND_CLASS(var, className) \