summaryrefslogtreecommitdiff
path: root/android/view
diff options
context:
space:
mode:
authorJason Monk <jmonk@google.com>2017-10-19 18:17:25 +0000
committerJason Monk <jmonk@google.com>2017-10-19 18:17:25 +0000
commit07f9f65561c2b81bcd189b895b31bb2ad0438d74 (patch)
tree49f76f879a89c256a4f65b674086be50760bdffb /android/view
parentd439404c9988df6001e4ff8bce31537e2692660e (diff)
downloadandroid-28-07f9f65561c2b81bcd189b895b31bb2ad0438d74.tar.gz
Revert "Import Android SDK Platform P [4402356]"
This reverts commit d439404c9988df6001e4ff8bce31537e2692660e. Change-Id: I825790bdf38523800388bc1bb531cecfcd7e60bd
Diffstat (limited to 'android/view')
-rw-r--r--android/view/SurfaceControl.java538
-rw-r--r--android/view/SurfaceView.java1135
-rw-r--r--android/view/View.java262
-rw-r--r--android/view/ViewDebug.java167
-rw-r--r--android/view/ViewGroup.java98
-rw-r--r--android/view/ViewRootImpl.java758
-rw-r--r--android/view/ViewStructure.java24
-rw-r--r--android/view/WindowManagerInternal.java4
-rw-r--r--android/view/WindowManagerPolicy.java7
-rw-r--r--android/view/accessibility/AccessibilityCache.java2
-rw-r--r--android/view/accessibility/AccessibilityManager.java916
-rw-r--r--android/view/autofill/AutofillManager.java149
-rw-r--r--android/view/textclassifier/TextClassifier.java8
-rw-r--r--android/view/textclassifier/TextClassifierConstants.java90
-rw-r--r--android/view/textclassifier/TextClassifierImpl.java14
-rw-r--r--android/view/textservice/TextServicesManager.java200
16 files changed, 2765 insertions, 1607 deletions
diff --git a/android/view/SurfaceControl.java b/android/view/SurfaceControl.java
index 54825895..31daefff 100644
--- a/android/view/SurfaceControl.java
+++ b/android/view/SurfaceControl.java
@@ -21,22 +21,15 @@ import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import android.annotation.Size;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
-import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
-import android.os.Debug;
import android.os.IBinder;
import android.util.Log;
import android.view.Surface.OutOfResourcesException;
import dalvik.system.CloseGuard;
-import java.io.Closeable;
-
-import libcore.util.NativeAllocationRegistry;
-
/**
* SurfaceControl
* @hide
@@ -61,34 +54,25 @@ public class SurfaceControl {
Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
boolean allLayers, boolean useIdentityTransform);
- private static native long nativeCreateTransaction();
- private static native long nativeGetNativeTransactionFinalizer();
- private static native void nativeApplyTransaction(long transactionObj, boolean sync);
- private static native void nativeSetAnimationTransaction(long transactionObj);
-
- private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
- private static native void nativeSetRelativeLayer(long transactionObj, long nativeObject,
- IBinder relativeTo, int zorder);
- private static native void nativeSetPosition(long transactionObj, long nativeObject,
- float x, float y);
- private static native void nativeSetGeometryAppliesWithResize(long transactionObj,
- long nativeObject);
- private static native void nativeSetSize(long transactionObj, long nativeObject, int w, int h);
- private static native void nativeSetTransparentRegionHint(long transactionObj,
- long nativeObject, Region region);
- private static native void nativeSetAlpha(long transactionObj, long nativeObject, float alpha);
- private static native void nativeSetMatrix(long transactionObj, long nativeObject,
- float dsdx, float dtdx,
+ private static native void nativeOpenTransaction();
+ private static native void nativeCloseTransaction(boolean sync);
+ private static native void nativeSetAnimationTransaction();
+
+ private static native void nativeSetLayer(long nativeObject, int zorder);
+ private static native void nativeSetRelativeLayer(long nativeObject, IBinder relativeTo,
+ int zorder);
+ private static native void nativeSetPosition(long nativeObject, float x, float y);
+ private static native void nativeSetGeometryAppliesWithResize(long nativeObject);
+ private static native void nativeSetSize(long nativeObject, int w, int h);
+ private static native void nativeSetTransparentRegionHint(long nativeObject, Region region);
+ private static native void nativeSetAlpha(long nativeObject, float alpha);
+ private static native void nativeSetColor(long nativeObject, float[] color);
+ private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx,
float dtdy, float dsdy);
- private static native void nativeSetColor(long transactionObj, long nativeObject, float[] color);
- private static native void nativeSetFlags(long transactionObj, long nativeObject,
- int flags, int mask);
- private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
- int l, int t, int r, int b);
- private static native void nativeSetFinalCrop(long transactionObj, long nativeObject,
- int l, int t, int r, int b);
- private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
- int layerStack);
+ private static native void nativeSetFlags(long nativeObject, int flags, int mask);
+ private static native void nativeSetWindowCrop(long nativeObject, int l, int t, int r, int b);
+ private static native void nativeSetFinalCrop(long nativeObject, int l, int t, int r, int b);
+ private static native void nativeSetLayerStack(long nativeObject, int layerStack);
private static native boolean nativeClearContentFrameStats(long nativeObject);
private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
@@ -98,16 +82,15 @@ public class SurfaceControl {
private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
private static native IBinder nativeCreateDisplay(String name, boolean secure);
private static native void nativeDestroyDisplay(IBinder displayToken);
- private static native void nativeSetDisplaySurface(long transactionObj,
+ private static native void nativeSetDisplaySurface(
IBinder displayToken, long nativeSurfaceObject);
- private static native void nativeSetDisplayLayerStack(long transactionObj,
+ private static native void nativeSetDisplayLayerStack(
IBinder displayToken, int layerStack);
- private static native void nativeSetDisplayProjection(long transactionObj,
+ private static native void nativeSetDisplayProjection(
IBinder displayToken, int orientation,
int l, int t, int r, int b,
int L, int T, int R, int B);
- private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken,
- int width, int height);
+ private static native void nativeSetDisplaySize(IBinder displayToken, int width, int height);
private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
IBinder displayToken);
private static native int nativeGetActiveConfig(IBinder displayToken);
@@ -118,17 +101,16 @@ public class SurfaceControl {
int colorMode);
private static native void nativeSetDisplayPowerMode(
IBinder displayToken, int mode);
- private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
+ private static native void nativeDeferTransactionUntil(long nativeObject,
IBinder handle, long frame);
- private static native void nativeDeferTransactionUntilSurface(long transactionObj,
- long nativeObject,
+ private static native void nativeDeferTransactionUntilSurface(long nativeObject,
long surfaceObject, long frame);
- private static native void nativeReparentChildren(long transactionObj, long nativeObject,
+ private static native void nativeReparentChildren(long nativeObject,
IBinder handle);
- private static native void nativeReparent(long transactionObj, long nativeObject,
+ private static native void nativeReparent(long nativeObject,
IBinder parentHandle);
- private static native void nativeSeverChildren(long transactionObj, long nativeObject);
- private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
+ private static native void nativeSeverChildren(long nativeObject);
+ private static native void nativeSetOverrideScalingMode(long nativeObject,
int scalingMode);
private static native IBinder nativeGetHandle(long nativeObject);
private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
@@ -140,9 +122,6 @@ public class SurfaceControl {
private final String mName;
long mNativeObject; // package visibility only for Surface.java access
- static Transaction sGlobalTransaction;
- static long sTransactionNestCount = 0;
-
/* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */
/**
@@ -398,6 +377,11 @@ public class SurfaceControl {
}
}
+ @Override
+ public String toString() {
+ return "Surface(name=" + mName + ")";
+ }
+
/**
* Release the local reference to the server-side surface.
* Always call release() when you're done with a Surface.
@@ -445,141 +429,102 @@ public class SurfaceControl {
/** start a transaction */
public static void openTransaction() {
- synchronized (SurfaceControl.class) {
- if (sGlobalTransaction == null) {
- sGlobalTransaction = new Transaction();
- }
- synchronized(SurfaceControl.class) {
- sTransactionNestCount++;
- }
- }
- }
-
- private static void closeTransaction(boolean sync) {
- synchronized(SurfaceControl.class) {
- if (sTransactionNestCount == 0) {
- Log.e(TAG, "Call to SurfaceControl.closeTransaction without matching openTransaction");
- } else if (--sTransactionNestCount > 0) {
- return;
- }
- sGlobalTransaction.apply(sync);
- }
+ nativeOpenTransaction();
}
/** end a transaction */
public static void closeTransaction() {
- closeTransaction(false);
+ nativeCloseTransaction(false);
}
public static void closeTransactionSync() {
- closeTransaction(true);
+ nativeCloseTransaction(true);
}
public void deferTransactionUntil(IBinder handle, long frame) {
if (frame > 0) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.deferTransactionUntil(this, handle, frame);
- }
+ nativeDeferTransactionUntil(mNativeObject, handle, frame);
}
}
public void deferTransactionUntil(Surface barrier, long frame) {
if (frame > 0) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
- }
+ nativeDeferTransactionUntilSurface(mNativeObject, barrier.mNativeObject, frame);
}
}
public void reparentChildren(IBinder newParentHandle) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.reparentChildren(this, newParentHandle);
- }
+ nativeReparentChildren(mNativeObject, newParentHandle);
}
+ /** Re-parents this layer to a new parent. */
public void reparent(IBinder newParentHandle) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.reparent(this, newParentHandle);
- }
+ nativeReparent(mNativeObject, newParentHandle);
}
public void detachChildren() {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.detachChildren(this);
- }
+ nativeSeverChildren(mNativeObject);
}
public void setOverrideScalingMode(int scalingMode) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setOverrideScalingMode(this, scalingMode);
- }
+ nativeSetOverrideScalingMode(mNativeObject, scalingMode);
}
public IBinder getHandle() {
return nativeGetHandle(mNativeObject);
}
+ /** flag the transaction as an animation */
public static void setAnimationTransaction() {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setAnimationTransaction();
- }
+ nativeSetAnimationTransaction();
}
public void setLayer(int zorder) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setLayer(this, zorder);
- }
+ nativeSetLayer(mNativeObject, zorder);
}
public void setRelativeLayer(IBinder relativeTo, int zorder) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setRelativeLayer(this, relativeTo, zorder);
- }
+ nativeSetRelativeLayer(mNativeObject, relativeTo, zorder);
}
public void setPosition(float x, float y) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setPosition(this, x, y);
- }
+ nativeSetPosition(mNativeObject, x, y);
}
+ /**
+ * If the buffer size changes in this transaction, position and crop updates specified
+ * in this transaction will not complete until a buffer of the new size
+ * arrives. As transform matrix and size are already frozen in this fashion,
+ * this enables totally freezing the surface until the resize has completed
+ * (at which point the geometry influencing aspects of this transaction will then occur)
+ */
public void setGeometryAppliesWithResize() {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setGeometryAppliesWithResize(this);
- }
+ nativeSetGeometryAppliesWithResize(mNativeObject);
}
public void setSize(int w, int h) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setSize(this, w, h);
- }
+ nativeSetSize(mNativeObject, w, h);
}
public void hide() {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.hide(this);
- }
+ nativeSetFlags(mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN);
}
public void show() {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.show(this);
- }
+ nativeSetFlags(mNativeObject, 0, SURFACE_HIDDEN);
}
public void setTransparentRegionHint(Region region) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setTransparentRegionHint(this, region);
- }
+ nativeSetTransparentRegionHint(mNativeObject, region);
}
public boolean clearContentFrameStats() {
@@ -600,70 +545,80 @@ public class SurfaceControl {
return nativeGetAnimationFrameStats(outStats);
}
+ /**
+ * Sets an alpha value for the entire Surface. This value is combined with the
+ * per-pixel alpha. It may be used with opaque Surfaces.
+ */
public void setAlpha(float alpha) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setAlpha(this, alpha);
- }
+ nativeSetAlpha(mNativeObject, alpha);
}
+ /**
+ * Sets a color for the Surface.
+ * @param color A float array with three values to represent r, g, b in range [0..1]
+ */
public void setColor(@Size(3) float[] color) {
checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setColor(this, color);
- }
+ nativeSetColor(mNativeObject, color);
}
public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setMatrix(this, dsdx, dtdx, dtdy, dsdy);
- }
+ nativeSetMatrix(mNativeObject, dsdx, dtdx, dtdy, dsdy);
}
public void setWindowCrop(Rect crop) {
checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setWindowCrop(this, crop);
+ if (crop != null) {
+ nativeSetWindowCrop(mNativeObject,
+ crop.left, crop.top, crop.right, crop.bottom);
+ } else {
+ nativeSetWindowCrop(mNativeObject, 0, 0, 0, 0);
}
}
public void setFinalCrop(Rect crop) {
checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setFinalCrop(this, crop);
+ if (crop != null) {
+ nativeSetFinalCrop(mNativeObject,
+ crop.left, crop.top, crop.right, crop.bottom);
+ } else {
+ nativeSetFinalCrop(mNativeObject, 0, 0, 0, 0);
}
}
public void setLayerStack(int layerStack) {
checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setLayerStack(this, layerStack);
- }
+ nativeSetLayerStack(mNativeObject, layerStack);
}
+ /**
+ * Sets the opacity of the surface. Setting the flag is equivalent to creating the
+ * Surface with the {@link #OPAQUE} flag.
+ */
public void setOpaque(boolean isOpaque) {
checkNotReleased();
-
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setOpaque(this, isOpaque);
+ if (isOpaque) {
+ nativeSetFlags(mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE);
+ } else {
+ nativeSetFlags(mNativeObject, 0, SURFACE_OPAQUE);
}
}
+ /**
+ * Sets the security of the surface. Setting the flag is equivalent to creating the
+ * Surface with the {@link #SECURE} flag.
+ */
public void setSecure(boolean isSecure) {
checkNotReleased();
-
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setSecure(this, isSecure);
+ if (isSecure) {
+ nativeSetFlags(mNativeObject, SECURE, SECURE);
+ } else {
+ nativeSetFlags(mNativeObject, 0, SECURE);
}
}
- @Override
- public String toString() {
- return "Surface(name=" + mName + ")/@0x" +
- Integer.toHexString(System.identityHashCode(this));
- }
-
/*
* set display parameters.
* needs to be inside open/closeTransaction block
@@ -786,28 +741,50 @@ public class SurfaceControl {
public static void setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setDisplayProjection(displayToken, orientation,
- layerStackRect, displayRect);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ if (layerStackRect == null) {
+ throw new IllegalArgumentException("layerStackRect must not be null");
}
+ if (displayRect == null) {
+ throw new IllegalArgumentException("displayRect must not be null");
+ }
+ nativeSetDisplayProjection(displayToken, orientation,
+ layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom,
+ displayRect.left, displayRect.top, displayRect.right, displayRect.bottom);
}
public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setDisplayLayerStack(displayToken, layerStack);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
}
+ nativeSetDisplayLayerStack(displayToken, layerStack);
}
public static void setDisplaySurface(IBinder displayToken, Surface surface) {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setDisplaySurface(displayToken, surface);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ if (surface != null) {
+ synchronized (surface.mLock) {
+ nativeSetDisplaySurface(displayToken, surface.mNativeObject);
+ }
+ } else {
+ nativeSetDisplaySurface(displayToken, 0);
}
}
public static void setDisplaySize(IBinder displayToken, int width, int height) {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setDisplaySize(displayToken, width, height);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width and height must be positive");
}
+
+ nativeSetDisplaySize(displayToken, width, height);
}
public static Display.HdrCapabilities getHdrCapabilities(IBinder displayToken) {
@@ -969,261 +946,4 @@ public class SurfaceControl {
nativeScreenshot(display, consumer, sourceCrop, width, height,
minLayer, maxLayer, allLayers, useIdentityTransform);
}
-
- public static class Transaction implements Closeable {
- public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- Transaction.class.getClassLoader(),
- nativeGetNativeTransactionFinalizer(), 512);
- private long mNativeObject;
-
- Runnable mFreeNativeResources;
-
- public Transaction() {
- mNativeObject = nativeCreateTransaction();
- mFreeNativeResources
- = sRegistry.registerNativeAllocation(this, mNativeObject);
- }
-
- /**
- * Apply the transaction, clearing it's state, and making it usable
- * as a new transaction.
- */
- public void apply() {
- apply(false);
- }
-
- /**
- * Close the transaction, if the transaction was not already applied this will cancel the
- * transaction.
- */
- @Override
- public void close() {
- mFreeNativeResources.run();
- mNativeObject = 0;
- }
-
- /**
- * Jankier version of apply. Avoid use (b/28068298).
- */
- public void apply(boolean sync) {
- nativeApplyTransaction(mNativeObject, sync);
- }
-
- public Transaction show(SurfaceControl sc) {
- nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN);
- return this;
- }
-
- public Transaction hide(SurfaceControl sc) {
- nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN);
- return this;
- }
-
- public Transaction setPosition(SurfaceControl sc, float x, float y) {
- nativeSetPosition(mNativeObject, sc.mNativeObject, x, y);
- return this;
- }
-
- public Transaction setSize(SurfaceControl sc, int w, int h) {
- nativeSetSize(mNativeObject, sc.mNativeObject,
- w, h);
- return this;
- }
-
- public Transaction setLayer(SurfaceControl sc, int z) {
- nativeSetLayer(mNativeObject, sc.mNativeObject, z);
- return this;
- }
-
- public Transaction setRelativeLayer(SurfaceControl sc, IBinder relativeTo, int z) {
- nativeSetRelativeLayer(mNativeObject, sc.mNativeObject,
- relativeTo, z);
- return this;
- }
-
- public Transaction setTransparentRegionHint(SurfaceControl sc, Region transparentRegion) {
- nativeSetTransparentRegionHint(mNativeObject,
- sc.mNativeObject, transparentRegion);
- return this;
- }
-
- public Transaction setAlpha(SurfaceControl sc, float alpha) {
- nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha);
- return this;
- }
-
- public Transaction setMatrix(SurfaceControl sc,
- float dsdx, float dtdx, float dtdy, float dsdy) {
- nativeSetMatrix(mNativeObject, sc.mNativeObject,
- dsdx, dtdx, dtdy, dsdy);
- return this;
- }
-
- public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
- if (crop != null) {
- nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
- crop.left, crop.top, crop.right, crop.bottom);
- } else {
- nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
- }
-
- return this;
- }
-
- public Transaction setFinalCrop(SurfaceControl sc, Rect crop) {
- if (crop != null) {
- nativeSetFinalCrop(mNativeObject, sc.mNativeObject,
- crop.left, crop.top, crop.right, crop.bottom);
- } else {
- nativeSetFinalCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
- }
-
- return this;
- }
-
- public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
- nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack);
- return this;
- }
-
- public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle, long frameNumber) {
- nativeDeferTransactionUntil(mNativeObject, sc.mNativeObject, handle, frameNumber);
- return this;
- }
-
- public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
- long frameNumber) {
- nativeDeferTransactionUntilSurface(mNativeObject, sc.mNativeObject,
- barrierSurface.mNativeObject, frameNumber);
- return this;
- }
-
- public Transaction reparentChildren(SurfaceControl sc, IBinder newParentHandle) {
- nativeReparentChildren(mNativeObject, sc.mNativeObject, newParentHandle);
- return this;
- }
-
- /** Re-parents a specific child layer to a new parent */
- public Transaction reparent(SurfaceControl sc, IBinder newParentHandle) {
- nativeReparent(mNativeObject, sc.mNativeObject,
- newParentHandle);
- return this;
- }
-
- public Transaction detachChildren(SurfaceControl sc) {
- nativeSeverChildren(mNativeObject, sc.mNativeObject);
- return this;
- }
-
- public Transaction setOverrideScalingMode(SurfaceControl sc, int overrideScalingMode) {
- nativeSetOverrideScalingMode(mNativeObject, sc.mNativeObject,
- overrideScalingMode);
- return this;
- }
-
- /**
- * Sets a color for the Surface.
- * @param color A float array with three values to represent r, g, b in range [0..1]
- */
- public Transaction setColor(SurfaceControl sc, @Size(3) float[] color) {
- nativeSetColor(mNativeObject, sc.mNativeObject, color);
- return this;
- }
-
- /**
- * If the buffer size changes in this transaction, position and crop updates specified
- * in this transaction will not complete until a buffer of the new size
- * arrives. As transform matrix and size are already frozen in this fashion,
- * this enables totally freezing the surface until the resize has completed
- * (at which point the geometry influencing aspects of this transaction will then occur)
- */
- public Transaction setGeometryAppliesWithResize(SurfaceControl sc) {
- nativeSetGeometryAppliesWithResize(mNativeObject, sc.mNativeObject);
- return this;
- }
-
- /**
- * Sets the security of the surface. Setting the flag is equivalent to creating the
- * Surface with the {@link #SECURE} flag.
- */
- Transaction setSecure(SurfaceControl sc, boolean isSecure) {
- if (isSecure) {
- nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE);
- } else {
- nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SECURE);
- }
- return this;
- }
-
- /**
- * Sets the opacity of the surface. Setting the flag is equivalent to creating the
- * Surface with the {@link #OPAQUE} flag.
- */
- public Transaction setOpaque(SurfaceControl sc, boolean isOpaque) {
- if (isOpaque) {
- nativeSetFlags(mNativeObject, sc.mNativeObject, OPAQUE, OPAQUE);
- } else {
- nativeSetFlags(mNativeObject, sc.mNativeObject, 0, OPAQUE);
- }
- return this;
- }
-
- public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
-
- if (surface != null) {
- synchronized (surface.mLock) {
- nativeSetDisplaySurface(mNativeObject, displayToken, surface.mNativeObject);
- }
- } else {
- nativeSetDisplaySurface(mNativeObject, displayToken, 0);
- }
- return this;
- }
-
- public Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- nativeSetDisplayLayerStack(mNativeObject, displayToken, layerStack);
- return this;
- }
-
- public Transaction setDisplayProjection(IBinder displayToken,
- int orientation, Rect layerStackRect, Rect displayRect) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- if (layerStackRect == null) {
- throw new IllegalArgumentException("layerStackRect must not be null");
- }
- if (displayRect == null) {
- throw new IllegalArgumentException("displayRect must not be null");
- }
- nativeSetDisplayProjection(mNativeObject, displayToken, orientation,
- layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom,
- displayRect.left, displayRect.top, displayRect.right, displayRect.bottom);
- return this;
- }
-
- public Transaction setDisplaySize(IBinder displayToken, int width, int height) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- if (width <= 0 || height <= 0) {
- throw new IllegalArgumentException("width and height must be positive");
- }
-
- nativeSetDisplaySize(mNativeObject, displayToken, width, height);
- return this;
- }
-
- /** flag the transaction as an animation */
- public Transaction setAnimationTransaction() {
- nativeSetAnimationTransaction(mNativeObject);
- return this;
- }
- }
}
diff --git a/android/view/SurfaceView.java b/android/view/SurfaceView.java
index ebb2af45..462dad3f 100644
--- a/android/view/SurfaceView.java
+++ b/android/view/SurfaceView.java
@@ -16,115 +16,1208 @@
package android.view;
-import com.android.layoutlib.bridge.MockView;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
+import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER;
+import static android.view.WindowManagerPolicy.APPLICATION_PANEL_SUBLAYER;
import android.content.Context;
+import android.content.res.CompatibilityInfo.Translator;
+import android.content.res.Configuration;
import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.internal.view.SurfaceCallbackHelper;
+
+import java.util.ArrayList;
+import java.util.concurrent.locks.ReentrantLock;
/**
- * Mock version of the SurfaceView.
- * Only non override public methods from the real SurfaceView have been added in there.
- * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * Provides a dedicated drawing surface embedded inside of a view hierarchy.
+ * You can control the format of this surface and, if you like, its size; the
+ * SurfaceView takes care of placing the surface at the correct location on the
+ * screen
+ *
+ * <p>The surface is Z ordered so that it is behind the window holding its
+ * SurfaceView; the SurfaceView punches a hole in its window to allow its
+ * surface to be displayed. The view hierarchy will take care of correctly
+ * compositing with the Surface any siblings of the SurfaceView that would
+ * normally appear on top of it. This can be used to place overlays such as
+ * buttons on top of the Surface, though note however that it can have an
+ * impact on performance since a full alpha-blended composite will be performed
+ * each time the Surface changes.
+ *
+ * <p> The transparent region that makes the surface visible is based on the
+ * layout positions in the view hierarchy. If the post-layout transform
+ * properties are used to draw a sibling view on top of the SurfaceView, the
+ * view may not be properly composited with the surface.
*
- * TODO: generate automatically.
+ * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
+ * which can be retrieved by calling {@link #getHolder}.
*
+ * <p>The Surface will be created for you while the SurfaceView's window is
+ * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
+ * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
+ * Surface is created and destroyed as the window is shown and hidden.
+ *
+ * <p>One of the purposes of this class is to provide a surface in which a
+ * secondary thread can render into the screen. If you are going to use it
+ * this way, you need to be aware of some threading semantics:
+ *
+ * <ul>
+ * <li> All SurfaceView and
+ * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
+ * from the thread running the SurfaceView's window (typically the main thread
+ * of the application). They thus need to correctly synchronize with any
+ * state that is also touched by the drawing thread.
+ * <li> You must ensure that the drawing thread only touches the underlying
+ * Surface while it is valid -- between
+ * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
+ * and
+ * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
+ * </ul>
+ *
+ * <p class="note"><strong>Note:</strong> Starting in platform version
+ * {@link android.os.Build.VERSION_CODES#N}, SurfaceView's window position is
+ * updated synchronously with other View rendering. This means that translating
+ * and scaling a SurfaceView on screen will not cause rendering artifacts. Such
+ * artifacts may occur on previous versions of the platform when its window is
+ * positioned asynchronously.</p>
*/
-public class SurfaceView extends MockView {
+public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
+ private static final String TAG = "SurfaceView";
+ private static final boolean DEBUG = false;
+
+ final ArrayList<SurfaceHolder.Callback> mCallbacks
+ = new ArrayList<SurfaceHolder.Callback>();
+
+ final int[] mLocation = new int[2];
+
+ final ReentrantLock mSurfaceLock = new ReentrantLock();
+ final Surface mSurface = new Surface(); // Current surface in use
+ boolean mDrawingStopped = true;
+ // We use this to track if the application has produced a frame
+ // in to the Surface. Up until that point, we should be careful not to punch
+ // holes.
+ boolean mDrawFinished = false;
+
+ final Rect mScreenRect = new Rect();
+ SurfaceSession mSurfaceSession;
+
+ SurfaceControl mSurfaceControl;
+ // In the case of format changes we switch out the surface in-place
+ // we need to preserve the old one until the new one has drawn.
+ SurfaceControl mDeferredDestroySurfaceControl;
+ final Rect mTmpRect = new Rect();
+ final Configuration mConfiguration = new Configuration();
+
+ int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+
+ boolean mIsCreating = false;
+ private volatile boolean mRtHandlingPositionUpdates = false;
+
+ private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
+ = new ViewTreeObserver.OnScrollChangedListener() {
+ @Override
+ public void onScrollChanged() {
+ updateSurface();
+ }
+ };
+
+ private final ViewTreeObserver.OnPreDrawListener mDrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ // reposition ourselves where the surface is
+ mHaveFrame = getWidth() > 0 && getHeight() > 0;
+ updateSurface();
+ return true;
+ }
+ };
+
+ boolean mRequestedVisible = false;
+ boolean mWindowVisibility = false;
+ boolean mLastWindowVisibility = false;
+ boolean mViewVisibility = false;
+ boolean mWindowStopped = false;
+
+ int mRequestedWidth = -1;
+ int mRequestedHeight = -1;
+ /* Set SurfaceView's format to 565 by default to maintain backward
+ * compatibility with applications assuming this format.
+ */
+ int mRequestedFormat = PixelFormat.RGB_565;
+
+ boolean mHaveFrame = false;
+ boolean mSurfaceCreated = false;
+ long mLastLockTime = 0;
+
+ boolean mVisible = false;
+ int mWindowSpaceLeft = -1;
+ int mWindowSpaceTop = -1;
+ int mSurfaceWidth = -1;
+ int mSurfaceHeight = -1;
+ int mFormat = -1;
+ final Rect mSurfaceFrame = new Rect();
+ int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
+ private Translator mTranslator;
+
+ private boolean mGlobalListenersAdded;
+ private boolean mAttachedToWindow;
+
+ private int mSurfaceFlags = SurfaceControl.HIDDEN;
+
+ private int mPendingReportDraws;
public SurfaceView(Context context) {
this(context, null);
}
public SurfaceView(Context context, AttributeSet attrs) {
- this(context, attrs , 0);
+ this(context, attrs, 0);
}
- public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
}
public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mRenderNode.requestPositionUpdates(this);
+
+ setWillNotDraw(true);
+ }
+
+ /**
+ * Return the SurfaceHolder providing access and control over this
+ * SurfaceView's underlying surface.
+ *
+ * @return SurfaceHolder The holder of the surface.
+ */
+ public SurfaceHolder getHolder() {
+ return mSurfaceHolder;
+ }
+
+ private void updateRequestedVisibility() {
+ mRequestedVisible = mViewVisibility && mWindowVisibility && !mWindowStopped;
+ }
+
+ /** @hide */
+ @Override
+ public void windowStopped(boolean stopped) {
+ mWindowStopped = stopped;
+ updateRequestedVisibility();
+ updateSurface();
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ getViewRootImpl().addWindowStoppedCallback(this);
+ mWindowStopped = false;
+
+ mViewVisibility = getVisibility() == VISIBLE;
+ updateRequestedVisibility();
+
+ mAttachedToWindow = true;
+ mParent.requestTransparentRegion(SurfaceView.this);
+ if (!mGlobalListenersAdded) {
+ ViewTreeObserver observer = getViewTreeObserver();
+ observer.addOnScrollChangedListener(mScrollChangedListener);
+ observer.addOnPreDrawListener(mDrawListener);
+ mGlobalListenersAdded = true;
+ }
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ mWindowVisibility = visibility == VISIBLE;
+ updateRequestedVisibility();
+ updateSurface();
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ mViewVisibility = visibility == VISIBLE;
+ boolean newRequestedVisible = mWindowVisibility && mViewVisibility && !mWindowStopped;
+ if (newRequestedVisible != mRequestedVisible) {
+ // our base class (View) invalidates the layout only when
+ // we go from/to the GONE state. However, SurfaceView needs
+ // to request a re-layout when the visibility changes at all.
+ // This is needed because the transparent region is computed
+ // as part of the layout phase, and it changes (obviously) when
+ // the visibility changes.
+ requestLayout();
+ }
+ mRequestedVisible = newRequestedVisible;
+ updateSurface();
+ }
+
+ private void performDrawFinished() {
+ if (mPendingReportDraws > 0) {
+ mDrawFinished = true;
+ if (mAttachedToWindow) {
+ notifyDrawFinished();
+ invalidate();
+ }
+ } else {
+ Log.e(TAG, System.identityHashCode(this) + "finished drawing"
+ + " but no pending report draw (extra call"
+ + " to draw completion runnable?)");
+ }
+ }
+
+ void notifyDrawFinished() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.pendingDrawFinished();
+ }
+ mPendingReportDraws--;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ // It's possible to create a SurfaceView using the default constructor and never
+ // attach it to a view hierarchy, this is a common use case when dealing with
+ // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage
+ // the lifecycle. Instead of attaching it to a view, he/she can just pass
+ // the SurfaceHolder forward, most live wallpapers do it.
+ if (viewRoot != null) {
+ viewRoot.removeWindowStoppedCallback(this);
+ }
+
+ mAttachedToWindow = false;
+ if (mGlobalListenersAdded) {
+ ViewTreeObserver observer = getViewTreeObserver();
+ observer.removeOnScrollChangedListener(mScrollChangedListener);
+ observer.removeOnPreDrawListener(mDrawListener);
+ mGlobalListenersAdded = false;
+ }
+
+ while (mPendingReportDraws > 0) {
+ notifyDrawFinished();
+ }
+
+ mRequestedVisible = false;
+
+ updateSurface();
+ if (mSurfaceControl != null) {
+ mSurfaceControl.destroy();
+ }
+ mSurfaceControl = null;
+
+ mHaveFrame = false;
+
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = mRequestedWidth >= 0
+ ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
+ : getDefaultSize(0, widthMeasureSpec);
+ int height = mRequestedHeight >= 0
+ ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
+ : getDefaultSize(0, heightMeasureSpec);
+ setMeasuredDimension(width, height);
+ }
+
+ /** @hide */
+ @Override
+ protected boolean setFrame(int left, int top, int right, int bottom) {
+ boolean result = super.setFrame(left, top, right, bottom);
+ updateSurface();
+ return result;
+ }
+
+ @Override
public boolean gatherTransparentRegion(Region region) {
- return false;
+ if (isAboveParent() || !mDrawFinished) {
+ return super.gatherTransparentRegion(region);
+ }
+
+ boolean opaque = true;
+ if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
+ // this view draws, remove it from the transparent region
+ opaque = super.gatherTransparentRegion(region);
+ } else if (region != null) {
+ int w = getWidth();
+ int h = getHeight();
+ if (w>0 && h>0) {
+ getLocationInWindow(mLocation);
+ // otherwise, punch a hole in the whole hierarchy
+ int l = mLocation[0];
+ int t = mLocation[1];
+ region.op(l, t, l+w, t+h, Region.Op.UNION);
+ }
+ }
+ if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
+ opaque = false;
+ }
+ return opaque;
}
+ @Override
+ public void draw(Canvas canvas) {
+ if (mDrawFinished && !isAboveParent()) {
+ // draw() is not called when SKIP_DRAW is set
+ if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
+ // punch a whole in the view-hierarchy below us
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ }
+ }
+ super.draw(canvas);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mDrawFinished && !isAboveParent()) {
+ // draw() is not called when SKIP_DRAW is set
+ if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
+ // punch a whole in the view-hierarchy below us
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ }
+ }
+ super.dispatchDraw(canvas);
+ }
+
+ /**
+ * Control whether the surface view's surface is placed on top of another
+ * regular surface view in the window (but still behind the window itself).
+ * This is typically used to place overlays on top of an underlying media
+ * surface view.
+ *
+ * <p>Note that this must be set before the surface view's containing
+ * window is attached to the window manager.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+ */
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+ mSubLayer = isMediaOverlay
+ ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
}
+ /**
+ * Control whether the surface view's surface is placed on top of its
+ * window. Normally it is placed behind the window, to allow it to
+ * (for the most part) appear to composite with the views in the
+ * hierarchy. By setting this, you cause it to be placed above the
+ * window. This means that none of the contents of the window this
+ * SurfaceView is in will be visible on top of its surface.
+ *
+ * <p>Note that this must be set before the surface view's containing
+ * window is attached to the window manager.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+ */
public void setZOrderOnTop(boolean onTop) {
+ if (onTop) {
+ mSubLayer = APPLICATION_PANEL_SUBLAYER;
+ } else {
+ mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+ }
}
+ /**
+ * Control whether the surface view's content should be treated as secure,
+ * preventing it from appearing in screenshots or from being viewed on
+ * non-secure displays.
+ *
+ * <p>Note that this must be set before the surface view's containing
+ * window is attached to the window manager.
+ *
+ * <p>See {@link android.view.Display#FLAG_SECURE} for details.
+ *
+ * @param isSecure True if the surface view is secure.
+ */
public void setSecure(boolean isSecure) {
+ if (isSecure) {
+ mSurfaceFlags |= SurfaceControl.SECURE;
+ } else {
+ mSurfaceFlags &= ~SurfaceControl.SECURE;
+ }
}
- public SurfaceHolder getHolder() {
- return mSurfaceHolder;
+ private void updateOpaqueFlag() {
+ if (!PixelFormat.formatHasAlpha(mRequestedFormat)) {
+ mSurfaceFlags |= SurfaceControl.OPAQUE;
+ } else {
+ mSurfaceFlags &= ~SurfaceControl.OPAQUE;
+ }
}
- private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+ private Rect getParentSurfaceInsets() {
+ final ViewRootImpl root = getViewRootImpl();
+ if (root == null) {
+ return null;
+ } else {
+ return root.mWindowAttributes.surfaceInsets;
+ }
+ }
+
+ /** @hide */
+ protected void updateSurface() {
+ if (!mHaveFrame) {
+ return;
+ }
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
+ return;
+ }
+
+ mTranslator = viewRoot.mTranslator;
+ if (mTranslator != null) {
+ mSurface.setCompatibilityTranslator(mTranslator);
+ }
+
+ int myWidth = mRequestedWidth;
+ if (myWidth <= 0) myWidth = getWidth();
+ int myHeight = mRequestedHeight;
+ if (myHeight <= 0) myHeight = getHeight();
+
+ final boolean formatChanged = mFormat != mRequestedFormat;
+ final boolean visibleChanged = mVisible != mRequestedVisible;
+ final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
+ && mRequestedVisible;
+ final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
+ final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
+ boolean redrawNeeded = false;
+
+ if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
+ getLocationInWindow(mLocation);
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "Changes: creating=" + creating
+ + " format=" + formatChanged + " size=" + sizeChanged
+ + " visible=" + visibleChanged
+ + " left=" + (mWindowSpaceLeft != mLocation[0])
+ + " top=" + (mWindowSpaceTop != mLocation[1]));
+
+ try {
+ final boolean visible = mVisible = mRequestedVisible;
+ mWindowSpaceLeft = mLocation[0];
+ mWindowSpaceTop = mLocation[1];
+ mSurfaceWidth = myWidth;
+ mSurfaceHeight = myHeight;
+ mFormat = mRequestedFormat;
+ mLastWindowVisibility = mWindowVisibility;
+
+ mScreenRect.left = mWindowSpaceLeft;
+ mScreenRect.top = mWindowSpaceTop;
+ mScreenRect.right = mWindowSpaceLeft + getWidth();
+ mScreenRect.bottom = mWindowSpaceTop + getHeight();
+ if (mTranslator != null) {
+ mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+ }
+
+ final Rect surfaceInsets = getParentSurfaceInsets();
+ mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
+
+ if (creating) {
+ mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
+ mDeferredDestroySurfaceControl = mSurfaceControl;
+
+ updateOpaqueFlag();
+ mSurfaceControl = new SurfaceControlWithBackground(mSurfaceSession,
+ "SurfaceView - " + viewRoot.getTitle().toString(),
+ mSurfaceWidth, mSurfaceHeight, mFormat,
+ mSurfaceFlags);
+ } else if (mSurfaceControl == null) {
+ return;
+ }
+
+ boolean realSizeChanged = false;
+
+ mSurfaceLock.lock();
+ try {
+ mDrawingStopped = !visible;
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "Cur surface: " + mSurface);
+
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.setLayer(mSubLayer);
+ if (mViewVisibility) {
+ mSurfaceControl.show();
+ } else {
+ mSurfaceControl.hide();
+ }
+
+ // While creating the surface, we will set it's initial
+ // geometry. Outside of that though, we should generally
+ // leave it to the RenderThread.
+ //
+ // There is one more case when the buffer size changes we aren't yet
+ // prepared to sync (as even following the transaction applying
+ // we still need to latch a buffer).
+ // b/28866173
+ if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
+ mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
+ mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
+ 0.0f, 0.0f,
+ mScreenRect.height() / (float) mSurfaceHeight);
+ }
+ if (sizeChanged) {
+ mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
+ }
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+
+ if (sizeChanged || creating) {
+ redrawNeeded = true;
+ }
+
+ mSurfaceFrame.left = 0;
+ mSurfaceFrame.top = 0;
+ if (mTranslator == null) {
+ mSurfaceFrame.right = mSurfaceWidth;
+ mSurfaceFrame.bottom = mSurfaceHeight;
+ } else {
+ float appInvertedScale = mTranslator.applicationInvertedScale;
+ mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
+ mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+ }
+
+ final int surfaceWidth = mSurfaceFrame.right;
+ final int surfaceHeight = mSurfaceFrame.bottom;
+ realSizeChanged = mLastSurfaceWidth != surfaceWidth
+ || mLastSurfaceHeight != surfaceHeight;
+ mLastSurfaceWidth = surfaceWidth;
+ mLastSurfaceHeight = surfaceHeight;
+ } finally {
+ mSurfaceLock.unlock();
+ }
+
+ try {
+ redrawNeeded |= visible && !mDrawFinished;
+
+ SurfaceHolder.Callback callbacks[] = null;
+
+ final boolean surfaceChanged = creating;
+ if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
+ mSurfaceCreated = false;
+ if (mSurface.isValid()) {
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "visibleChanged -- surfaceDestroyed");
+ callbacks = getSurfaceCallbacks();
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceDestroyed(mSurfaceHolder);
+ }
+ // Since Android N the same surface may be reused and given to us
+ // again by the system server at a later point. However
+ // as we didn't do this in previous releases, clients weren't
+ // necessarily required to clean up properly in
+ // surfaceDestroyed. This leads to problems for example when
+ // clients don't destroy their EGL context, and try
+ // and create a new one on the same surface following reuse.
+ // Since there is no valid use of the surface in-between
+ // surfaceDestroyed and surfaceCreated, we force a disconnect,
+ // so the next connect will always work if we end up reusing
+ // the surface.
+ if (mSurface.isValid()) {
+ mSurface.forceScopedDisconnect();
+ }
+ }
+ }
+
+ if (creating) {
+ mSurface.copyFrom(mSurfaceControl);
+ }
+
+ if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.O) {
+ // Some legacy applications use the underlying native {@link Surface} object
+ // as a key to whether anything has changed. In these cases, updates to the
+ // existing {@link Surface} will be ignored when the size changes.
+ // Therefore, we must explicitly recreate the {@link Surface} in these
+ // cases.
+ mSurface.createFrom(mSurfaceControl);
+ }
+
+ if (visible && mSurface.isValid()) {
+ if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
+ mSurfaceCreated = true;
+ mIsCreating = true;
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "visibleChanged -- surfaceCreated");
+ if (callbacks == null) {
+ callbacks = getSurfaceCallbacks();
+ }
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceCreated(mSurfaceHolder);
+ }
+ }
+ if (creating || formatChanged || sizeChanged
+ || visibleChanged || realSizeChanged) {
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "surfaceChanged -- format=" + mFormat
+ + " w=" + myWidth + " h=" + myHeight);
+ if (callbacks == null) {
+ callbacks = getSurfaceCallbacks();
+ }
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
+ }
+ }
+ if (redrawNeeded) {
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "surfaceRedrawNeeded");
+ if (callbacks == null) {
+ callbacks = getSurfaceCallbacks();
+ }
+
+ mPendingReportDraws++;
+ viewRoot.drawPending();
+ SurfaceCallbackHelper sch =
+ new SurfaceCallbackHelper(this::onDrawFinished);
+ sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
+ }
+ }
+ } finally {
+ mIsCreating = false;
+ if (mSurfaceControl != null && !mSurfaceCreated) {
+ mSurface.release();
+ // If we are not in the stopped state, then the destruction of the Surface
+ // represents a visual change we need to display, and we should go ahead
+ // and destroy the SurfaceControl. However if we are in the stopped state,
+ // we can just leave the Surface around so it can be a part of animations,
+ // and we let the life-time be tied to the parent surface.
+ if (!mWindowStopped) {
+ mSurfaceControl.destroy();
+ mSurfaceControl = null;
+ }
+ }
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception configuring surface", ex);
+ }
+ if (DEBUG) Log.v(
+ TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top
+ + " w=" + mScreenRect.width() + " h=" + mScreenRect.height()
+ + ", frame=" + mSurfaceFrame);
+ } else {
+ // Calculate the window position in case RT loses the window
+ // and we need to fallback to a UI-thread driven position update
+ getLocationInSurface(mLocation);
+ final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
+ || mWindowSpaceTop != mLocation[1];
+ final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
+ || getHeight() != mScreenRect.height();
+ if (positionChanged || layoutSizeChanged) { // Only the position has changed
+ mWindowSpaceLeft = mLocation[0];
+ mWindowSpaceTop = mLocation[1];
+ // For our size changed check, we keep mScreenRect.width() and mScreenRect.height()
+ // in view local space.
+ mLocation[0] = getWidth();
+ mLocation[1] = getHeight();
+
+ mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
+ mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]);
+
+ if (mTranslator != null) {
+ mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+ }
+
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
+ try {
+ if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " +
+ "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+ mScreenRect.left, mScreenRect.top,
+ mScreenRect.right, mScreenRect.bottom));
+ setParentSpaceRectangle(mScreenRect, -1);
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception configuring surface", ex);
+ }
+ }
+ }
+ }
+ }
+
+ private void onDrawFinished() {
+ if (DEBUG) {
+ Log.i(TAG, System.identityHashCode(this) + " "
+ + "finishedDrawing");
+ }
+
+ if (mDeferredDestroySurfaceControl != null) {
+ mDeferredDestroySurfaceControl.destroy();
+ mDeferredDestroySurfaceControl = null;
+ }
+
+ runOnUiThread(() -> {
+ performDrawFinished();
+ });
+ }
+
+ private void setParentSpaceRectangle(Rect position, long frameNumber) {
+ ViewRootImpl viewRoot = getViewRootImpl();
+
+ SurfaceControl.openTransaction();
+ try {
+ if (frameNumber > 0) {
+ mSurfaceControl.deferTransactionUntil(viewRoot.mSurface, frameNumber);
+ }
+ mSurfaceControl.setPosition(position.left, position.top);
+ mSurfaceControl.setMatrix(position.width() / (float) mSurfaceWidth,
+ 0.0f, 0.0f,
+ position.height() / (float) mSurfaceHeight);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ }
+
+ private Rect mRTLastReportedPosition = new Rect();
+
+ /**
+ * Called by native by a Rendering Worker thread to update the window position
+ * @hide
+ */
+ public final void updateSurfacePosition_renderWorker(long frameNumber,
+ int left, int top, int right, int bottom) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
+ // its 2nd frame if RenderThread is running slowly could potentially see
+ // this as false, enter the branch, get pre-empted, then this comes along
+ // and reports a new position, then the UI thread resumes and reports
+ // its position. This could therefore be de-sync'd in that interval, but
+ // the synchronization would violate the rule that RT must never block
+ // on the UI thread which would open up potential deadlocks. The risk of
+ // a single-frame desync is therefore preferable for now.
+ mRtHandlingPositionUpdates = true;
+ if (mRTLastReportedPosition.left == left
+ && mRTLastReportedPosition.top == top
+ && mRTLastReportedPosition.right == right
+ && mRTLastReportedPosition.bottom == bottom) {
+ return;
+ }
+ try {
+ if (DEBUG) {
+ Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " +
+ "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+ frameNumber, left, top, right, bottom));
+ }
+ mRTLastReportedPosition.set(left, top, right, bottom);
+ setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
+ // Now overwrite mRTLastReportedPosition with our values
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception from repositionChild", ex);
+ }
+ }
+
+ /**
+ * Called by native on RenderThread to notify that the view is no longer in the
+ * draw tree. UI thread is blocked at this point.
+ * @hide
+ */
+ public final void surfacePositionLost_uiRtSync(long frameNumber) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
+ System.identityHashCode(this), frameNumber));
+ }
+ mRTLastReportedPosition.setEmpty();
+
+ if (mSurfaceControl == null) {
+ return;
+ }
+ if (mRtHandlingPositionUpdates) {
+ mRtHandlingPositionUpdates = false;
+ // This callback will happen while the UI thread is blocked, so we can
+ // safely access other member variables at this time.
+ // So do what the UI thread would have done if RT wasn't handling position
+ // updates.
+ if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
+ try {
+ if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " +
+ "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+ mScreenRect.left, mScreenRect.top,
+ mScreenRect.right, mScreenRect.bottom));
+ setParentSpaceRectangle(mScreenRect, frameNumber);
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception configuring surface", ex);
+ }
+ }
+ }
+ }
+
+ private SurfaceHolder.Callback[] getSurfaceCallbacks() {
+ SurfaceHolder.Callback callbacks[];
+ synchronized (mCallbacks) {
+ callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
+ mCallbacks.toArray(callbacks);
+ }
+ return callbacks;
+ }
+
+ /**
+ * This method still exists only for compatibility reasons because some applications have relied
+ * on this method via reflection. See Issue 36345857 for details.
+ *
+ * @deprecated No platform code is using this method anymore.
+ * @hide
+ */
+ @Deprecated
+ public void setWindowType(int type) {
+ if (getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
+ throw new UnsupportedOperationException(
+ "SurfaceView#setWindowType() has never been a public API.");
+ }
+
+ if (type == TYPE_APPLICATION_PANEL) {
+ Log.e(TAG, "If you are calling SurfaceView#setWindowType(TYPE_APPLICATION_PANEL) "
+ + "just to make the SurfaceView to be placed on top of its window, you must "
+ + "call setZOrderOnTop(true) instead.", new Throwable());
+ setZOrderOnTop(true);
+ return;
+ }
+ Log.e(TAG, "SurfaceView#setWindowType(int) is deprecated and now does nothing. "
+ + "type=" + type, new Throwable());
+ }
+
+ private void runOnUiThread(Runnable runnable) {
+ Handler handler = getHandler();
+ if (handler != null && handler.getLooper() != Looper.myLooper()) {
+ handler.post(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ /**
+ * Check to see if the surface has fixed size dimensions or if the surface's
+ * dimensions are dimensions are dependent on its current layout.
+ *
+ * @return true if the surface has dimensions that are fixed in size
+ * @hide
+ */
+ public boolean isFixedSize() {
+ return (mRequestedWidth != -1 || mRequestedHeight != -1);
+ }
+
+ private boolean isAboveParent() {
+ return mSubLayer >= 0;
+ }
+
+ private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+ private static final String LOG_TAG = "SurfaceHolder";
@Override
public boolean isCreating() {
- return false;
+ return mIsCreating;
}
@Override
public void addCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ // This is a linear search, but in practice we'll
+ // have only a couple callbacks, so it doesn't matter.
+ if (mCallbacks.contains(callback) == false) {
+ mCallbacks.add(callback);
+ }
+ }
}
@Override
public void removeCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
}
@Override
public void setFixedSize(int width, int height) {
+ if (mRequestedWidth != width || mRequestedHeight != height) {
+ mRequestedWidth = width;
+ mRequestedHeight = height;
+ requestLayout();
+ }
}
@Override
public void setSizeFromLayout() {
+ if (mRequestedWidth != -1 || mRequestedHeight != -1) {
+ mRequestedWidth = mRequestedHeight = -1;
+ requestLayout();
+ }
}
@Override
public void setFormat(int format) {
+ // for backward compatibility reason, OPAQUE always
+ // means 565 for SurfaceView
+ if (format == PixelFormat.OPAQUE)
+ format = PixelFormat.RGB_565;
+
+ mRequestedFormat = format;
+ if (mSurfaceControl != null) {
+ updateSurface();
+ }
}
+ /**
+ * @deprecated setType is now ignored.
+ */
@Override
- public void setType(int type) {
- }
+ @Deprecated
+ public void setType(int type) { }
@Override
public void setKeepScreenOn(boolean screenOn) {
+ runOnUiThread(() -> SurfaceView.this.setKeepScreenOn(screenOn));
}
+ /**
+ * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
+ *
+ * After drawing into the provided {@link Canvas}, the caller must
+ * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+ *
+ * The caller must redraw the entire surface.
+ * @return A canvas for drawing into the surface.
+ */
@Override
public Canvas lockCanvas() {
- return null;
+ return internalLockCanvas(null, false);
}
+ /**
+ * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
+ *
+ * After drawing into the provided {@link Canvas}, the caller must
+ * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+ *
+ * @param inOutDirty A rectangle that represents the dirty region that the caller wants
+ * to redraw. This function may choose to expand the dirty rectangle if for example
+ * the surface has been resized or if the previous contents of the surface were
+ * not available. The caller must redraw the entire dirty region as represented
+ * by the contents of the inOutDirty rectangle upon return from this function.
+ * The caller may also pass <code>null</code> instead, in the case where the
+ * entire surface should be redrawn.
+ * @return A canvas for drawing into the surface.
+ */
@Override
- public Canvas lockCanvas(Rect dirty) {
+ public Canvas lockCanvas(Rect inOutDirty) {
+ return internalLockCanvas(inOutDirty, false);
+ }
+
+ @Override
+ public Canvas lockHardwareCanvas() {
+ return internalLockCanvas(null, true);
+ }
+
+ private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
+ mSurfaceLock.lock();
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
+ + mDrawingStopped + ", surfaceControl=" + mSurfaceControl);
+
+ Canvas c = null;
+ if (!mDrawingStopped && mSurfaceControl != null) {
+ try {
+ if (hardware) {
+ c = mSurface.lockHardwareCanvas();
+ } else {
+ c = mSurface.lockCanvas(dirty);
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Exception locking surface", e);
+ }
+ }
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
+ if (c != null) {
+ mLastLockTime = SystemClock.uptimeMillis();
+ return c;
+ }
+
+ // If the Surface is not ready to be drawn, then return null,
+ // but throttle calls to this function so it isn't called more
+ // than every 100ms.
+ long now = SystemClock.uptimeMillis();
+ long nextTime = mLastLockTime + 100;
+ if (nextTime > now) {
+ try {
+ Thread.sleep(nextTime-now);
+ } catch (InterruptedException e) {
+ }
+ now = SystemClock.uptimeMillis();
+ }
+ mLastLockTime = now;
+ mSurfaceLock.unlock();
+
return null;
}
+ /**
+ * Posts the new contents of the {@link Canvas} to the surface and
+ * releases the {@link Canvas}.
+ *
+ * @param canvas The canvas previously obtained from {@link #lockCanvas}.
+ */
@Override
public void unlockCanvasAndPost(Canvas canvas) {
+ mSurface.unlockCanvasAndPost(canvas);
+ mSurfaceLock.unlock();
}
@Override
public Surface getSurface() {
- return null;
+ return mSurface;
}
@Override
public Rect getSurfaceFrame() {
- return null;
+ return mSurfaceFrame;
}
};
-}
+ class SurfaceControlWithBackground extends SurfaceControl {
+ private SurfaceControl mBackgroundControl;
+ private boolean mOpaque = true;
+ public boolean mVisible = false;
+
+ public SurfaceControlWithBackground(SurfaceSession s,
+ String name, int w, int h, int format, int flags)
+ throws Exception {
+ super(s, name, w, h, format, flags);
+ mBackgroundControl = new SurfaceControl(s, "Background for - " + name, w, h,
+ PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM);
+ mOpaque = (flags & SurfaceControl.OPAQUE) != 0;
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ super.setAlpha(alpha);
+ mBackgroundControl.setAlpha(alpha);
+ }
+
+ @Override
+ public void setLayer(int zorder) {
+ super.setLayer(zorder);
+ // -3 is below all other child layers as SurfaceView never goes below -2
+ mBackgroundControl.setLayer(-3);
+ }
+
+ @Override
+ public void setPosition(float x, float y) {
+ super.setPosition(x, y);
+ mBackgroundControl.setPosition(x, y);
+ }
+
+ @Override
+ public void setSize(int w, int h) {
+ super.setSize(w, h);
+ mBackgroundControl.setSize(w, h);
+ }
+
+ @Override
+ public void setWindowCrop(Rect crop) {
+ super.setWindowCrop(crop);
+ mBackgroundControl.setWindowCrop(crop);
+ }
+
+ @Override
+ public void setFinalCrop(Rect crop) {
+ super.setFinalCrop(crop);
+ mBackgroundControl.setFinalCrop(crop);
+ }
+
+ @Override
+ public void setLayerStack(int layerStack) {
+ super.setLayerStack(layerStack);
+ mBackgroundControl.setLayerStack(layerStack);
+ }
+
+ @Override
+ public void setOpaque(boolean isOpaque) {
+ super.setOpaque(isOpaque);
+ mOpaque = isOpaque;
+ updateBackgroundVisibility();
+ }
+
+ @Override
+ public void setSecure(boolean isSecure) {
+ super.setSecure(isSecure);
+ }
+
+ @Override
+ public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ super.setMatrix(dsdx, dtdx, dsdy, dtdy);
+ mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
+ }
+
+ @Override
+ public void hide() {
+ super.hide();
+ mVisible = false;
+ updateBackgroundVisibility();
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ mVisible = true;
+ updateBackgroundVisibility();
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ mBackgroundControl.destroy();
+ }
+
+ @Override
+ public void release() {
+ super.release();
+ mBackgroundControl.release();
+ }
+
+ @Override
+ public void setTransparentRegionHint(Region region) {
+ super.setTransparentRegionHint(region);
+ mBackgroundControl.setTransparentRegionHint(region);
+ }
+
+ @Override
+ public void deferTransactionUntil(IBinder handle, long frame) {
+ super.deferTransactionUntil(handle, frame);
+ mBackgroundControl.deferTransactionUntil(handle, frame);
+ }
+
+ @Override
+ public void deferTransactionUntil(Surface barrier, long frame) {
+ super.deferTransactionUntil(barrier, frame);
+ mBackgroundControl.deferTransactionUntil(barrier, frame);
+ }
+
+ void updateBackgroundVisibility() {
+ if (mOpaque && mVisible) {
+ mBackgroundControl.show();
+ } else {
+ mBackgroundControl.hide();
+ }
+ }
+ }
+}
diff --git a/android/view/View.java b/android/view/View.java
index c043dcac..b6be2961 100644
--- a/android/view/View.java
+++ b/android/view/View.java
@@ -1448,59 +1448,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* <p>Enables low quality mode for the drawing cache.</p>
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int DRAWING_CACHE_QUALITY_LOW = 0x00080000;
/**
* <p>Enables high quality mode for the drawing cache.</p>
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000;
/**
* <p>Enables automatic quality mode for the drawing cache.</p>
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int DRAWING_CACHE_QUALITY_AUTO = 0x00000000;
private static final int[] DRAWING_CACHE_QUALITY_FLAGS = {
@@ -2342,9 +2300,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private static final int PFLAG_HOVERED = 0x10000000;
/**
- * Flag set by {@link AutofillManager} if it needs to be notified when this view is clicked.
+ * no longer needed, should be reused
*/
- private static final int PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK = 0x20000000;
+ private static final int PFLAG_DOES_NOTHING_REUSE_PLEASE = 0x20000000;
/** {@hide} */
static final int PFLAG_ACTIVATED = 0x40000000;
@@ -6397,42 +6355,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return null;
}
- /** @hide */
- public void setNotifyAutofillManagerOnClick(boolean notify) {
- if (notify) {
- mPrivateFlags |= PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
- } else {
- mPrivateFlags &= ~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
- }
- }
-
- private void notifyAutofillManagerOnClick() {
- if ((mPrivateFlags & PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK) != 0) {
- try {
- getAutofillManager().notifyViewClicked(this);
- } finally {
- // Set it to already called so it's not called twice when called by
- // performClickInternal()
- mPrivateFlags |= ~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
- }
- }
- }
-
- /**
- * Entry point for {@link #performClick()} - other methods on View should call it instead of
- * {@code performClick()} directly to make sure the autofill manager is notified when
- * necessary (as subclasses could extend {@code performClick()} without calling the parent's
- * method).
- */
- private boolean performClickInternal() {
- // Must notify autofill manager before performing the click actions to avoid scenarios where
- // the app has a click listener that changes the state of views the autofill service might
- // be interested on.
- notifyAutofillManagerOnClick();
-
- return performClick();
- }
-
/**
* Call this view's OnClickListener, if it is defined. Performs all normal
* actions associated with clicking: reporting accessibility event, playing
@@ -6441,14 +6363,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
- // NOTE: other methods on View should not call this method directly, but performClickInternal()
- // instead, to guarantee that the autofill manager is notified when necessary (as subclasses
- // could extend this method without calling super.performClick()).
public boolean performClick() {
- // We still need to call this method to handle the cases where performClick() was called
- // externally, instead of through performClickInternal()
- notifyAutofillManagerOnClick();
-
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
@@ -8992,21 +8907,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #isDrawingCacheEnabled()
*
* @attr ref android.R.styleable#View_drawingCacheQuality
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
@DrawingCacheQuality
public int getDrawingCacheQuality() {
return mViewFlags & DRAWING_CACHE_QUALITY_MASK;
@@ -9024,21 +8925,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #isDrawingCacheEnabled()
*
* @attr ref android.R.styleable#View_drawingCacheQuality
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void setDrawingCacheQuality(@DrawingCacheQuality int quality) {
setFlags(quality, DRAWING_CACHE_QUALITY_MASK);
}
@@ -11546,7 +11433,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK: {
if (isClickable()) {
- performClickInternal();
+ performClick();
return true;
}
} break;
@@ -12658,7 +12545,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// This is a tap, so remove the longpress check
removeLongPressCallback();
if (!event.isCanceled()) {
- return performClickInternal();
+ return performClick();
}
}
}
@@ -13230,7 +13117,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
- performClickInternal();
+ performClick();
}
}
}
@@ -18216,21 +18103,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #getDrawingCache()
* @see #buildDrawingCache()
* @see #setLayerType(int, android.graphics.Paint)
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void setDrawingCacheEnabled(boolean enabled) {
mCachingFailed = false;
setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
@@ -18243,21 +18116,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #setDrawingCacheEnabled(boolean)
* @see #getDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
@ViewDebug.ExportedProperty(category = "drawing")
public boolean isDrawingCacheEnabled() {
return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED;
@@ -18271,11 +18130,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@SuppressWarnings({"UnusedDeclaration"})
public void outputDirtyFlags(String indent, boolean clear, int clearMask) {
- Log.d(VIEW_LOG_TAG, indent + this + " DIRTY("
- + (mPrivateFlags & View.PFLAG_DIRTY_MASK)
- + ") DRAWN(" + (mPrivateFlags & PFLAG_DRAWN) + ")" + " CACHE_VALID("
- + (mPrivateFlags & View.PFLAG_DRAWING_CACHE_VALID)
- + ") INVALIDATED(" + (mPrivateFlags & PFLAG_INVALIDATED) + ")");
+ Log.d("View", indent + this + " DIRTY(" + (mPrivateFlags & View.PFLAG_DIRTY_MASK) +
+ ") DRAWN(" + (mPrivateFlags & PFLAG_DRAWN) + ")" + " CACHE_VALID(" +
+ (mPrivateFlags & View.PFLAG_DRAWING_CACHE_VALID) +
+ ") INVALIDATED(" + (mPrivateFlags & PFLAG_INVALIDATED) + ")");
if (clear) {
mPrivateFlags &= clearMask;
}
@@ -18399,21 +18257,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return A non-scaled bitmap representing this view or null if cache is disabled.
*
* @see #getDrawingCache(boolean)
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public Bitmap getDrawingCache() {
return getDrawingCache(false);
}
@@ -18444,21 +18288,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #isDrawingCacheEnabled()
* @see #buildDrawingCache(boolean)
* @see #destroyDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public Bitmap getDrawingCache(boolean autoScale) {
if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
return null;
@@ -18478,21 +18308,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setDrawingCacheEnabled(boolean)
* @see #buildDrawingCache()
* @see #getDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void destroyDrawingCache() {
if (mDrawingCache != null) {
mDrawingCache.recycle();
@@ -18514,21 +18330,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setDrawingCacheEnabled(boolean)
* @see #buildDrawingCache()
* @see #getDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void setDrawingCacheBackgroundColor(@ColorInt int color) {
if (color != mDrawingCacheBackgroundColor) {
mDrawingCacheBackgroundColor = color;
@@ -18540,21 +18342,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setDrawingCacheBackgroundColor(int)
*
* @return The background color to used for the drawing cache's bitmap
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
@ColorInt
public int getDrawingCacheBackgroundColor() {
return mDrawingCacheBackgroundColor;
@@ -18564,21 +18352,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p>
*
* @see #buildDrawingCache(boolean)
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void buildDrawingCache() {
buildDrawingCache(false);
}
@@ -18605,21 +18379,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #getDrawingCache()
* @see #destroyDrawingCache()
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
mDrawingCache == null : mUnscaledDrawingCache == null)) {
@@ -20052,7 +19812,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
boolean changed = false;
if (DBG) {
- Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","
+ Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
@@ -25098,7 +24858,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private final class PerformClick implements Runnable {
@Override
public void run() {
- performClickInternal();
+ performClick();
}
}
diff --git a/android/view/ViewDebug.java b/android/view/ViewDebug.java
index afa94131..3426485e 100644
--- a/android/view/ViewDebug.java
+++ b/android/view/ViewDebug.java
@@ -528,23 +528,84 @@ public class ViewDebug {
/** @hide */
public static void profileViewAndChildren(final View view, BufferedWriter out)
throws IOException {
- RenderNode node = RenderNode.create("ViewDebug", null);
- profileViewAndChildren(view, node, out, true);
- node.destroy();
+ profileViewAndChildren(view, out, true);
}
- private static void profileViewAndChildren(View view, RenderNode node, BufferedWriter out,
- boolean root) throws IOException {
+ private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root)
+ throws IOException {
+
long durationMeasure =
(root || (view.mPrivateFlags & View.PFLAG_MEASURED_DIMENSION_SET) != 0)
- ? profileViewMeasure(view) : 0;
+ ? profileViewOperation(view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ forceLayout(view);
+ return null;
+ }
+
+ private void forceLayout(View view) {
+ view.forceLayout();
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ final int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ forceLayout(group.getChildAt(i));
+ }
+ }
+ }
+
+ public void run(Void... data) {
+ view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
+ }
+
+ public void post(Void... data) {
+ }
+ })
+ : 0;
long durationLayout =
(root || (view.mPrivateFlags & View.PFLAG_LAYOUT_REQUIRED) != 0)
- ? profileViewLayout(view) : 0;
+ ? profileViewOperation(view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ return null;
+ }
+
+ public void run(Void... data) {
+ view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
+ }
+
+ public void post(Void... data) {
+ }
+ }) : 0;
long durationDraw =
(root || !view.willNotDraw() || (view.mPrivateFlags & View.PFLAG_DRAWN) != 0)
- ? profileViewDraw(view, node) : 0;
+ ? profileViewOperation(view, new ViewOperation<Object>() {
+ public Object[] pre() {
+ final DisplayMetrics metrics =
+ (view != null && view.getResources() != null) ?
+ view.getResources().getDisplayMetrics() : null;
+ final Bitmap bitmap = metrics != null ?
+ Bitmap.createBitmap(metrics, metrics.widthPixels,
+ metrics.heightPixels, Bitmap.Config.RGB_565) : null;
+ final Canvas canvas = bitmap != null ? new Canvas(bitmap) : null;
+ return new Object[] {
+ bitmap, canvas
+ };
+ }
+ public void run(Object... data) {
+ if (data[1] != null) {
+ view.draw((Canvas) data[1]);
+ }
+ }
+
+ public void post(Object... data) {
+ if (data[1] != null) {
+ ((Canvas) data[1]).setBitmap(null);
+ }
+ if (data[0] != null) {
+ ((Bitmap) data[0]).recycle();
+ }
+ }
+ }) : 0;
out.write(String.valueOf(durationMeasure));
out.write(' ');
out.write(String.valueOf(durationLayout));
@@ -555,86 +616,34 @@ public class ViewDebug {
ViewGroup group = (ViewGroup) view;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
- profileViewAndChildren(group.getChildAt(i), node, out, false);
- }
- }
- }
-
- private static long profileViewMeasure(final View view) {
- return profileViewOperation(view, new ViewOperation() {
- @Override
- public void pre() {
- forceLayout(view);
- }
-
- private void forceLayout(View view) {
- view.forceLayout();
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
- final int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- forceLayout(group.getChildAt(i));
- }
- }
- }
-
- @Override
- public void run() {
- view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
- }
- });
- }
-
- private static long profileViewLayout(View view) {
- return profileViewOperation(view,
- () -> view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom));
- }
-
- private static long profileViewDraw(View view, RenderNode node) {
- DisplayMetrics dm = view.getResources().getDisplayMetrics();
- if (dm == null) {
- return 0;
- }
-
- if (view.isHardwareAccelerated()) {
- DisplayListCanvas canvas = node.start(dm.widthPixels, dm.heightPixels);
- try {
- return profileViewOperation(view, () -> view.draw(canvas));
- } finally {
- node.end(canvas);
- }
- } else {
- Bitmap bitmap = Bitmap.createBitmap(
- dm, dm.widthPixels, dm.heightPixels, Bitmap.Config.RGB_565);
- Canvas canvas = new Canvas(bitmap);
- try {
- return profileViewOperation(view, () -> view.draw(canvas));
- } finally {
- canvas.setBitmap(null);
- bitmap.recycle();
+ profileViewAndChildren(group.getChildAt(i), out, false);
}
}
}
- interface ViewOperation {
- default void pre() {}
-
- void run();
+ interface ViewOperation<T> {
+ T[] pre();
+ void run(T... data);
+ void post(T... data);
}
- private static long profileViewOperation(View view, final ViewOperation operation) {
+ private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) {
final CountDownLatch latch = new CountDownLatch(1);
final long[] duration = new long[1];
- view.post(() -> {
- try {
- operation.pre();
- long start = Debug.threadCpuTimeNanos();
- //noinspection unchecked
- operation.run();
- duration[0] = Debug.threadCpuTimeNanos() - start;
- } finally {
- latch.countDown();
+ view.post(new Runnable() {
+ public void run() {
+ try {
+ T[] data = operation.pre();
+ long start = Debug.threadCpuTimeNanos();
+ //noinspection unchecked
+ operation.run(data);
+ duration[0] = Debug.threadCpuTimeNanos() - start;
+ //noinspection unchecked
+ operation.post(data);
+ } finally {
+ latch.countDown();
+ }
}
});
diff --git a/android/view/ViewGroup.java b/android/view/ViewGroup.java
index 929beaea..b2e5a163 100644
--- a/android/view/ViewGroup.java
+++ b/android/view/ViewGroup.java
@@ -421,78 +421,22 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* Used to indicate that no drawing cache should be kept in memory.
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int PERSISTENT_NO_CACHE = 0x0;
/**
* Used to indicate that the animation drawing cache should be kept in memory.
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
/**
* Used to indicate that the scrolling drawing cache should be kept in memory.
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
/**
* Used to indicate that all drawing caches should be kept in memory.
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public static final int PERSISTENT_ALL_CACHES = 0x3;
// Layout Modes
@@ -3825,21 +3769,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* Enables or disables the drawing cache for each child of this view group.
*
* @param enabled true to enable the cache, false to dispose of it
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
protected void setChildrenDrawingCacheEnabled(boolean enabled) {
if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
final View[] children = mChildren;
@@ -6401,21 +6331,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @return one or a combination of {@link #PERSISTENT_NO_CACHE},
* {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
* and {@link #PERSISTENT_ALL_CACHES}
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
@ViewDebug.ExportedProperty(category = "drawing", mapping = {
@ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
@ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
@@ -6436,21 +6352,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
* {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
* and {@link #PERSISTENT_ALL_CACHES}
- *
- * @deprecated The view drawing cache was largely made obsolete with the introduction of
- * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
- * layers are largely unnecessary and can easily result in a net loss in performance due to the
- * cost of creating and updating the layer. In the rare cases where caching layers are useful,
- * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
- * rendering. For software-rendered snapshots of a small part of the View hierarchy or
- * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
- * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
- * software-rendered usages are discouraged and have compatibility issues with hardware-only
- * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
- * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
- * reports or unit testing the {@link PixelCopy} API is recommended.
*/
- @Deprecated
public void setPersistentDrawingCache(int drawingCacheToKeep) {
mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
}
diff --git a/android/view/ViewRootImpl.java b/android/view/ViewRootImpl.java
index 99438d87..71106ada 100644
--- a/android/view/ViewRootImpl.java
+++ b/android/view/ViewRootImpl.java
@@ -72,7 +72,6 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MergedConfiguration;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
import android.view.Surface.OutOfResourcesException;
@@ -1669,6 +1668,8 @@ public final class ViewRootImpl implements ViewParent,
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
+ //Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
+
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
@@ -2826,7 +2827,7 @@ public final class ViewRootImpl implements ViewParent,
try {
mWindowDrawCountDown.await();
} catch (InterruptedException e) {
- Log.e(mTag, "Window redraw count down interrupted!");
+ Log.e(mTag, "Window redraw count down interruped!");
}
mWindowDrawCountDown = null;
}
@@ -2896,6 +2897,8 @@ public final class ViewRootImpl implements ViewParent,
final float appScale = mAttachInfo.mApplicationScale;
final boolean scalingRequired = mAttachInfo.mScalingRequired;
+ int resizeAlpha = 0;
+
final Rect dirty = mDirty;
if (mSurfaceHolder != null) {
// The app owns the surface, we won't draw.
@@ -3466,7 +3469,6 @@ public final class ViewRootImpl implements ViewParent,
}
void dispatchDetachedFromWindow() {
- mFirstInputStage.onDetachedFromWindow();
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
mView.dispatchDetachedFromWindow();
@@ -3729,273 +3731,266 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_INVALIDATE:
- ((View) msg.obj).invalidate();
- break;
- case MSG_INVALIDATE_RECT:
- final View.AttachInfo.InvalidateInfo info =
- (View.AttachInfo.InvalidateInfo) msg.obj;
- info.target.invalidate(info.left, info.top, info.right, info.bottom);
- info.recycle();
- break;
- case MSG_PROCESS_INPUT_EVENTS:
- mProcessInputEventsScheduled = false;
- doProcessInputEvents();
- break;
- case MSG_DISPATCH_APP_VISIBILITY:
- handleAppVisibility(msg.arg1 != 0);
- break;
- case MSG_DISPATCH_GET_NEW_SURFACE:
- handleGetNewSurface();
+ case MSG_INVALIDATE:
+ ((View) msg.obj).invalidate();
+ break;
+ case MSG_INVALIDATE_RECT:
+ final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
+ info.target.invalidate(info.left, info.top, info.right, info.bottom);
+ info.recycle();
+ break;
+ case MSG_PROCESS_INPUT_EVENTS:
+ mProcessInputEventsScheduled = false;
+ doProcessInputEvents();
+ break;
+ case MSG_DISPATCH_APP_VISIBILITY:
+ handleAppVisibility(msg.arg1 != 0);
+ break;
+ case MSG_DISPATCH_GET_NEW_SURFACE:
+ handleGetNewSurface();
+ break;
+ case MSG_RESIZED: {
+ // Recycled in the fall through...
+ SomeArgs args = (SomeArgs) msg.obj;
+ if (mWinFrame.equals(args.arg1)
+ && mPendingOverscanInsets.equals(args.arg5)
+ && mPendingContentInsets.equals(args.arg2)
+ && mPendingStableInsets.equals(args.arg6)
+ && mPendingVisibleInsets.equals(args.arg3)
+ && mPendingOutsets.equals(args.arg7)
+ && mPendingBackDropFrame.equals(args.arg8)
+ && args.arg4 == null
+ && args.argi1 == 0
+ && mDisplay.getDisplayId() == args.argi3) {
break;
- case MSG_RESIZED: {
- // Recycled in the fall through...
+ }
+ } // fall through...
+ case MSG_RESIZED_REPORT:
+ if (mAdded) {
SomeArgs args = (SomeArgs) msg.obj;
- if (mWinFrame.equals(args.arg1)
- && mPendingOverscanInsets.equals(args.arg5)
- && mPendingContentInsets.equals(args.arg2)
- && mPendingStableInsets.equals(args.arg6)
- && mPendingVisibleInsets.equals(args.arg3)
- && mPendingOutsets.equals(args.arg7)
- && mPendingBackDropFrame.equals(args.arg8)
- && args.arg4 == null
- && args.argi1 == 0
- && mDisplay.getDisplayId() == args.argi3) {
- break;
+
+ final int displayId = args.argi3;
+ MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
+ final boolean displayChanged = mDisplay.getDisplayId() != displayId;
+
+ if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) {
+ // If configuration changed - notify about that and, maybe, about move to
+ // display.
+ performConfigurationChange(mergedConfiguration, false /* force */,
+ displayChanged ? displayId : INVALID_DISPLAY /* same display */);
+ } else if (displayChanged) {
+ // Moved to display without config change - report last applied one.
+ onMovedToDisplay(displayId, mLastConfigurationFromResources);
}
- } // fall through...
- case MSG_RESIZED_REPORT:
- if (mAdded) {
- SomeArgs args = (SomeArgs) msg.obj;
-
- final int displayId = args.argi3;
- MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
- final boolean displayChanged = mDisplay.getDisplayId() != displayId;
-
- if (!mLastReportedMergedConfiguration.equals(mergedConfiguration)) {
- // If configuration changed - notify about that and, maybe,
- // about move to display.
- performConfigurationChange(mergedConfiguration, false /* force */,
- displayChanged
- ? displayId : INVALID_DISPLAY /* same display */);
- } else if (displayChanged) {
- // Moved to display without config change - report last applied one.
- onMovedToDisplay(displayId, mLastConfigurationFromResources);
- }
- final boolean framesChanged = !mWinFrame.equals(args.arg1)
- || !mPendingOverscanInsets.equals(args.arg5)
- || !mPendingContentInsets.equals(args.arg2)
- || !mPendingStableInsets.equals(args.arg6)
- || !mPendingVisibleInsets.equals(args.arg3)
- || !mPendingOutsets.equals(args.arg7);
-
- mWinFrame.set((Rect) args.arg1);
- mPendingOverscanInsets.set((Rect) args.arg5);
- mPendingContentInsets.set((Rect) args.arg2);
- mPendingStableInsets.set((Rect) args.arg6);
- mPendingVisibleInsets.set((Rect) args.arg3);
- mPendingOutsets.set((Rect) args.arg7);
- mPendingBackDropFrame.set((Rect) args.arg8);
- mForceNextWindowRelayout = args.argi1 != 0;
- mPendingAlwaysConsumeNavBar = args.argi2 != 0;
-
- args.recycle();
-
- if (msg.what == MSG_RESIZED_REPORT) {
- reportNextDraw();
- }
+ final boolean framesChanged = !mWinFrame.equals(args.arg1)
+ || !mPendingOverscanInsets.equals(args.arg5)
+ || !mPendingContentInsets.equals(args.arg2)
+ || !mPendingStableInsets.equals(args.arg6)
+ || !mPendingVisibleInsets.equals(args.arg3)
+ || !mPendingOutsets.equals(args.arg7);
+
+ mWinFrame.set((Rect) args.arg1);
+ mPendingOverscanInsets.set((Rect) args.arg5);
+ mPendingContentInsets.set((Rect) args.arg2);
+ mPendingStableInsets.set((Rect) args.arg6);
+ mPendingVisibleInsets.set((Rect) args.arg3);
+ mPendingOutsets.set((Rect) args.arg7);
+ mPendingBackDropFrame.set((Rect) args.arg8);
+ mForceNextWindowRelayout = args.argi1 != 0;
+ mPendingAlwaysConsumeNavBar = args.argi2 != 0;
- if (mView != null && framesChanged) {
- forceLayout(mView);
- }
- requestLayout();
+ args.recycle();
+
+ if (msg.what == MSG_RESIZED_REPORT) {
+ reportNextDraw();
}
- break;
- case MSG_WINDOW_MOVED:
- if (mAdded) {
- final int w = mWinFrame.width();
- final int h = mWinFrame.height();
- final int l = msg.arg1;
- final int t = msg.arg2;
- mWinFrame.left = l;
- mWinFrame.right = l + w;
- mWinFrame.top = t;
- mWinFrame.bottom = t + h;
-
- mPendingBackDropFrame.set(mWinFrame);
- maybeHandleWindowMove(mWinFrame);
+
+ if (mView != null && framesChanged) {
+ forceLayout(mView);
}
- break;
- case MSG_WINDOW_FOCUS_CHANGED: {
- final boolean hasWindowFocus = msg.arg1 != 0;
- if (mAdded) {
- mAttachInfo.mHasWindowFocus = hasWindowFocus;
-
- profileRendering(hasWindowFocus);
-
- if (hasWindowFocus) {
- boolean inTouchMode = msg.arg2 != 0;
- ensureTouchModeLocally(inTouchMode);
- if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
- mFullRedrawNeeded = true;
+ requestLayout();
+ }
+ break;
+ case MSG_WINDOW_MOVED:
+ if (mAdded) {
+ final int w = mWinFrame.width();
+ final int h = mWinFrame.height();
+ final int l = msg.arg1;
+ final int t = msg.arg2;
+ mWinFrame.left = l;
+ mWinFrame.right = l + w;
+ mWinFrame.top = t;
+ mWinFrame.bottom = t + h;
+
+ mPendingBackDropFrame.set(mWinFrame);
+ maybeHandleWindowMove(mWinFrame);
+ }
+ break;
+ case MSG_WINDOW_FOCUS_CHANGED: {
+ if (mAdded) {
+ boolean hasWindowFocus = msg.arg1 != 0;
+ mAttachInfo.mHasWindowFocus = hasWindowFocus;
+
+ profileRendering(hasWindowFocus);
+
+ if (hasWindowFocus) {
+ boolean inTouchMode = msg.arg2 != 0;
+ ensureTouchModeLocally(inTouchMode);
+
+ if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()){
+ mFullRedrawNeeded = true;
+ try {
+ final WindowManager.LayoutParams lp = mWindowAttributes;
+ final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
+ mAttachInfo.mThreadedRenderer.initializeIfNeeded(
+ mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
+ } catch (OutOfResourcesException e) {
+ Log.e(mTag, "OutOfResourcesException locking surface", e);
try {
- final WindowManager.LayoutParams lp = mWindowAttributes;
- final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
- mAttachInfo.mThreadedRenderer.initializeIfNeeded(
- mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
- } catch (OutOfResourcesException e) {
- Log.e(mTag, "OutOfResourcesException locking surface", e);
- try {
- if (!mWindowSession.outOfMemory(mWindow)) {
- Slog.w(mTag, "No processes killed for memory;"
- + " killing self");
- Process.killProcess(Process.myPid());
- }
- } catch (RemoteException ex) {
+ if (!mWindowSession.outOfMemory(mWindow)) {
+ Slog.w(mTag, "No processes killed for memory; killing self");
+ Process.killProcess(Process.myPid());
}
- // Retry in a bit.
- sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2),
- 500);
- return;
+ } catch (RemoteException ex) {
}
+ // Retry in a bit.
+ sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500);
+ return;
}
}
+ }
- mLastWasImTarget = WindowManager.LayoutParams
- .mayUseInputMethod(mWindowAttributes.flags);
+ mLastWasImTarget = WindowManager.LayoutParams
+ .mayUseInputMethod(mWindowAttributes.flags);
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPreWindowFocus(mView, hasWindowFocus);
- }
- if (mView != null) {
- mAttachInfo.mKeyDispatchState.reset();
- mView.dispatchWindowFocusChanged(hasWindowFocus);
- mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPreWindowFocus(mView, hasWindowFocus);
+ }
+ if (mView != null) {
+ mAttachInfo.mKeyDispatchState.reset();
+ mView.dispatchWindowFocusChanged(hasWindowFocus);
+ mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
- if (mAttachInfo.mTooltipHost != null) {
- mAttachInfo.mTooltipHost.hideTooltip();
- }
+ if (mAttachInfo.mTooltipHost != null) {
+ mAttachInfo.mTooltipHost.hideTooltip();
}
+ }
- // Note: must be done after the focus change callbacks,
- // so all of the view state is set up correctly.
- if (hasWindowFocus) {
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode,
- !mHasHadWindowFocus, mWindowAttributes.flags);
- }
- // Clear the forward bit. We can just do this directly, since
- // the window manager doesn't care about it.
- mWindowAttributes.softInputMode &=
+ // Note: must be done after the focus change callbacks,
+ // so all of the view state is set up correctly.
+ if (hasWindowFocus) {
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPostWindowFocus(mView, mView.findFocus(),
+ mWindowAttributes.softInputMode,
+ !mHasHadWindowFocus, mWindowAttributes.flags);
+ }
+ // Clear the forward bit. We can just do this directly, since
+ // the window manager doesn't care about it.
+ mWindowAttributes.softInputMode &=
+ ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+ ((WindowManager.LayoutParams)mView.getLayoutParams())
+ .softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
- ((WindowManager.LayoutParams) mView.getLayoutParams())
- .softInputMode &=
- ~WindowManager.LayoutParams
- .SOFT_INPUT_IS_FORWARD_NAVIGATION;
- mHasHadWindowFocus = true;
- } else {
- if (mPointerCapture) {
- handlePointerCaptureChanged(false);
- }
+ mHasHadWindowFocus = true;
+ } else {
+ if (mPointerCapture) {
+ handlePointerCaptureChanged(false);
}
}
- mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
- } break;
- case MSG_DIE:
- doDie();
- break;
- case MSG_DISPATCH_INPUT_EVENT: {
- SomeArgs args = (SomeArgs) msg.obj;
- InputEvent event = (InputEvent) args.arg1;
- InputEventReceiver receiver = (InputEventReceiver) args.arg2;
- enqueueInputEvent(event, receiver, 0, true);
- args.recycle();
- } break;
- case MSG_SYNTHESIZE_INPUT_EVENT: {
- InputEvent event = (InputEvent) msg.obj;
- enqueueInputEvent(event, null, QueuedInputEvent.FLAG_UNHANDLED, true);
- } break;
- case MSG_DISPATCH_KEY_FROM_IME: {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Dispatching key " + msg.obj + " from IME to " + mView);
- }
- KeyEvent event = (KeyEvent) msg.obj;
- if ((event.getFlags() & KeyEvent.FLAG_FROM_SYSTEM) != 0) {
- // The IME is trying to say this event is from the
- // system! Bad bad bad!
- //noinspection UnusedAssignment
- event = KeyEvent.changeFlags(event,
- event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
- }
- enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
- } break;
- case MSG_CHECK_FOCUS: {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- imm.checkFocus();
- }
- } break;
- case MSG_CLOSE_SYSTEM_DIALOGS: {
- if (mView != null) {
- mView.onCloseSystemDialogs((String) msg.obj);
- }
- } break;
- case MSG_DISPATCH_DRAG_EVENT: {
- } // fall through
- case MSG_DISPATCH_DRAG_LOCATION_EVENT: {
- DragEvent event = (DragEvent) msg.obj;
- // only present when this app called startDrag()
- event.mLocalState = mLocalDragState;
- handleDragEvent(event);
- } break;
- case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
- handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj);
- } break;
- case MSG_UPDATE_CONFIGURATION: {
- Configuration config = (Configuration) msg.obj;
- if (config.isOtherSeqNewer(
- mLastReportedMergedConfiguration.getMergedConfiguration())) {
- // If we already have a newer merged config applied - use its global part.
- config = mLastReportedMergedConfiguration.getGlobalConfiguration();
- }
+ }
+ } break;
+ case MSG_DIE:
+ doDie();
+ break;
+ case MSG_DISPATCH_INPUT_EVENT: {
+ SomeArgs args = (SomeArgs)msg.obj;
+ InputEvent event = (InputEvent)args.arg1;
+ InputEventReceiver receiver = (InputEventReceiver)args.arg2;
+ enqueueInputEvent(event, receiver, 0, true);
+ args.recycle();
+ } break;
+ case MSG_SYNTHESIZE_INPUT_EVENT: {
+ InputEvent event = (InputEvent)msg.obj;
+ enqueueInputEvent(event, null, QueuedInputEvent.FLAG_UNHANDLED, true);
+ } break;
+ case MSG_DISPATCH_KEY_FROM_IME: {
+ if (LOCAL_LOGV) Log.v(
+ TAG, "Dispatching key "
+ + msg.obj + " from IME to " + mView);
+ KeyEvent event = (KeyEvent)msg.obj;
+ if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
+ // The IME is trying to say this event is from the
+ // system! Bad bad bad!
+ //noinspection UnusedAssignment
+ event = KeyEvent.changeFlags(event, event.getFlags() &
+ ~KeyEvent.FLAG_FROM_SYSTEM);
+ }
+ enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
+ } break;
+ case MSG_CHECK_FOCUS: {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ imm.checkFocus();
+ }
+ } break;
+ case MSG_CLOSE_SYSTEM_DIALOGS: {
+ if (mView != null) {
+ mView.onCloseSystemDialogs((String)msg.obj);
+ }
+ } break;
+ case MSG_DISPATCH_DRAG_EVENT:
+ case MSG_DISPATCH_DRAG_LOCATION_EVENT: {
+ DragEvent event = (DragEvent)msg.obj;
+ event.mLocalState = mLocalDragState; // only present when this app called startDrag()
+ handleDragEvent(event);
+ } break;
+ case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
+ handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj);
+ } break;
+ case MSG_UPDATE_CONFIGURATION: {
+ Configuration config = (Configuration) msg.obj;
+ if (config.isOtherSeqNewer(
+ mLastReportedMergedConfiguration.getMergedConfiguration())) {
+ // If we already have a newer merged config applied - use its global part.
+ config = mLastReportedMergedConfiguration.getGlobalConfiguration();
+ }
- // Use the newer global config and last reported override config.
- mPendingMergedConfiguration.setConfiguration(config,
- mLastReportedMergedConfiguration.getOverrideConfiguration());
+ // Use the newer global config and last reported override config.
+ mPendingMergedConfiguration.setConfiguration(config,
+ mLastReportedMergedConfiguration.getOverrideConfiguration());
- performConfigurationChange(mPendingMergedConfiguration, false /* force */,
- INVALID_DISPLAY /* same display */);
- } break;
- case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
- setAccessibilityFocus(null, null);
- } break;
- case MSG_INVALIDATE_WORLD: {
- if (mView != null) {
- invalidateWorld(mView);
- }
- } break;
- case MSG_DISPATCH_WINDOW_SHOWN: {
- handleDispatchWindowShown();
- } break;
- case MSG_REQUEST_KEYBOARD_SHORTCUTS: {
- final IResultReceiver receiver = (IResultReceiver) msg.obj;
- final int deviceId = msg.arg1;
- handleRequestKeyboardShortcuts(receiver, deviceId);
- } break;
- case MSG_UPDATE_POINTER_ICON: {
- MotionEvent event = (MotionEvent) msg.obj;
- resetPointerIcon(event);
- } break;
- case MSG_POINTER_CAPTURE_CHANGED: {
- final boolean hasCapture = msg.arg1 != 0;
- handlePointerCaptureChanged(hasCapture);
- } break;
- case MSG_DRAW_FINISHED: {
- pendingDrawFinished();
- } break;
+ performConfigurationChange(mPendingMergedConfiguration, false /* force */,
+ INVALID_DISPLAY /* same display */);
+ } break;
+ case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
+ setAccessibilityFocus(null, null);
+ } break;
+ case MSG_INVALIDATE_WORLD: {
+ if (mView != null) {
+ invalidateWorld(mView);
+ }
+ } break;
+ case MSG_DISPATCH_WINDOW_SHOWN: {
+ handleDispatchWindowShown();
+ } break;
+ case MSG_REQUEST_KEYBOARD_SHORTCUTS: {
+ final IResultReceiver receiver = (IResultReceiver) msg.obj;
+ final int deviceId = msg.arg1;
+ handleRequestKeyboardShortcuts(receiver, deviceId);
+ } break;
+ case MSG_UPDATE_POINTER_ICON: {
+ MotionEvent event = (MotionEvent) msg.obj;
+ resetPointerIcon(event);
+ } break;
+ case MSG_POINTER_CAPTURE_CHANGED: {
+ final boolean hasCapture = msg.arg1 != 0;
+ handlePointerCaptureChanged(hasCapture);
+ } break;
+ case MSG_DRAW_FINISHED: {
+ pendingDrawFinished();
+ } break;
}
}
}
@@ -4208,18 +4203,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- protected void onWindowFocusChanged(boolean hasWindowFocus) {
- if (mNext != null) {
- mNext.onWindowFocusChanged(hasWindowFocus);
- }
- }
-
- protected void onDetachedFromWindow() {
- if (mNext != null) {
- mNext.onDetachedFromWindow();
- }
- }
-
protected boolean shouldDropInputEvent(QueuedInputEvent q) {
if (mView == null || !mAdded) {
Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
@@ -4973,9 +4956,9 @@ public final class ViewRootImpl implements ViewParent,
final MotionEvent event = (MotionEvent)q.mEvent;
final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- mTrackball.cancel();
+ mTrackball.cancel(event);
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
- mJoystick.cancel();
+ mJoystick.cancel(event);
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
mTouchNavigation.cancel(event);
@@ -4984,18 +4967,6 @@ public final class ViewRootImpl implements ViewParent,
}
super.onDeliverToNext(q);
}
-
- @Override
- protected void onWindowFocusChanged(boolean hasWindowFocus) {
- if (!hasWindowFocus) {
- mJoystick.cancel();
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- mJoystick.cancel();
- }
}
/**
@@ -5108,7 +5079,7 @@ public final class ViewRootImpl implements ViewParent,
}
}
- public void cancel() {
+ public void cancel(MotionEvent event) {
mLastTime = Integer.MIN_VALUE;
// If we reach this, we consumed a trackball event.
@@ -5292,11 +5263,14 @@ public final class ViewRootImpl implements ViewParent,
* Creates dpad events from unhandled joystick movements.
*/
final class SyntheticJoystickHandler extends Handler {
+ private final static String TAG = "SyntheticJoystickHandler";
private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1;
private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2;
- private final JoystickAxesState mJoystickAxesState = new JoystickAxesState();
- private final SparseArray<KeyEvent> mDeviceKeyEvents = new SparseArray<>();
+ private int mLastXDirection;
+ private int mLastYDirection;
+ private int mLastXKeyCode;
+ private int mLastYKeyCode;
public SyntheticJoystickHandler() {
super(true);
@@ -5307,10 +5281,11 @@ public final class ViewRootImpl implements ViewParent,
switch (msg.what) {
case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
+ KeyEvent oldEvent = (KeyEvent)msg.obj;
+ KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent,
+ SystemClock.uptimeMillis(),
+ oldEvent.getRepeatCount() + 1);
if (mAttachInfo.mHasWindowFocus) {
- KeyEvent oldEvent = (KeyEvent) msg.obj;
- KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent,
- SystemClock.uptimeMillis(), oldEvent.getRepeatCount() + 1);
enqueueInputEvent(e);
Message m = obtainMessage(msg.what, e);
m.setAsynchronous(true);
@@ -5322,176 +5297,97 @@ public final class ViewRootImpl implements ViewParent,
public void process(MotionEvent event) {
switch(event.getActionMasked()) {
- case MotionEvent.ACTION_CANCEL:
- cancel();
- break;
- case MotionEvent.ACTION_MOVE:
- update(event);
- break;
- default:
- Log.w(mTag, "Unexpected action: " + event.getActionMasked());
+ case MotionEvent.ACTION_CANCEL:
+ cancel(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ update(event, true);
+ break;
+ default:
+ Log.w(mTag, "Unexpected action: " + event.getActionMasked());
}
}
- private void cancel() {
+ private void cancel(MotionEvent event) {
removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
- for (int i = 0; i < mDeviceKeyEvents.size(); i++) {
- final KeyEvent keyEvent = mDeviceKeyEvents.valueAt(i);
- if (keyEvent != null) {
- enqueueInputEvent(KeyEvent.changeTimeRepeat(keyEvent,
- SystemClock.uptimeMillis(), 0));
- }
- }
- mDeviceKeyEvents.clear();
- mJoystickAxesState.resetState();
- }
-
- private void update(MotionEvent event) {
- final int historySize = event.getHistorySize();
- for (int h = 0; h < historySize; h++) {
- final long time = event.getHistoricalEventTime(h);
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X,
- event.getHistoricalAxisValue(MotionEvent.AXIS_X, 0, h));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y,
- event.getHistoricalAxisValue(MotionEvent.AXIS_Y, 0, h));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X,
- event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_X, 0, h));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y,
- event.getHistoricalAxisValue(MotionEvent.AXIS_HAT_Y, 0, h));
- }
- final long time = event.getEventTime();
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_X,
- event.getAxisValue(MotionEvent.AXIS_X));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_Y,
- event.getAxisValue(MotionEvent.AXIS_Y));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_X,
- event.getAxisValue(MotionEvent.AXIS_HAT_X));
- mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y,
- event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+ update(event, false);
}
- final class JoystickAxesState {
- // State machine: from neutral state (no button press) can go into
- // button STATE_UP_OR_LEFT or STATE_DOWN_OR_RIGHT state, emitting an ACTION_DOWN event.
- // From STATE_UP_OR_LEFT or STATE_DOWN_OR_RIGHT state can go into neutral state,
- // emitting an ACTION_UP event.
- private static final int STATE_UP_OR_LEFT = -1;
- private static final int STATE_NEUTRAL = 0;
- private static final int STATE_DOWN_OR_RIGHT = 1;
-
- final int[] mAxisStatesHat = {STATE_NEUTRAL, STATE_NEUTRAL}; // {AXIS_HAT_X, AXIS_HAT_Y}
- final int[] mAxisStatesStick = {STATE_NEUTRAL, STATE_NEUTRAL}; // {AXIS_X, AXIS_Y}
-
- void resetState() {
- mAxisStatesHat[0] = STATE_NEUTRAL;
- mAxisStatesHat[1] = STATE_NEUTRAL;
- mAxisStatesStick[0] = STATE_NEUTRAL;
- mAxisStatesStick[1] = STATE_NEUTRAL;
- }
-
- void updateStateForAxis(MotionEvent event, long time, int axis, float value) {
- // Emit KeyEvent if necessary
- // axis can be AXIS_X, AXIS_Y, AXIS_HAT_X, AXIS_HAT_Y
- final int axisStateIndex;
- final int repeatMessage;
- if (isXAxis(axis)) {
- axisStateIndex = 0;
- repeatMessage = MSG_ENQUEUE_X_AXIS_KEY_REPEAT;
- } else if (isYAxis(axis)) {
- axisStateIndex = 1;
- repeatMessage = MSG_ENQUEUE_Y_AXIS_KEY_REPEAT;
- } else {
- Log.e(mTag, "Unexpected axis " + axis + " in updateStateForAxis!");
- return;
- }
- final int newState = joystickAxisValueToState(value);
+ private void update(MotionEvent event, boolean synthesizeNewKeys) {
+ final long time = event.getEventTime();
+ final int metaState = event.getMetaState();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
- final int currentState;
- if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) {
- currentState = mAxisStatesStick[axisStateIndex];
- } else {
- currentState = mAxisStatesHat[axisStateIndex];
- }
+ int xDirection = joystickAxisValueToDirection(
+ event.getAxisValue(MotionEvent.AXIS_HAT_X));
+ if (xDirection == 0) {
+ xDirection = joystickAxisValueToDirection(event.getX());
+ }
- if (currentState == newState) {
- return;
+ int yDirection = joystickAxisValueToDirection(
+ event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+ if (yDirection == 0) {
+ yDirection = joystickAxisValueToDirection(event.getY());
+ }
+
+ if (xDirection != mLastXDirection) {
+ if (mLastXKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
+ enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+ mLastXKeyCode = 0;
}
- final int metaState = event.getMetaState();
- final int deviceId = event.getDeviceId();
- final int source = event.getSource();
+ mLastXDirection = xDirection;
- if (currentState == STATE_DOWN_OR_RIGHT || currentState == STATE_UP_OR_LEFT) {
- // send a button release event
- final int keyCode = joystickAxisAndStateToKeycode(axis, currentState);
- if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
- enqueueInputEvent(new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode,
- 0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- // remove the corresponding pending UP event if focus lost/view detached
- mDeviceKeyEvents.put(deviceId, null);
- }
- removeMessages(repeatMessage);
+ if (xDirection != 0 && synthesizeNewKeys) {
+ mLastXKeyCode = xDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
+ final KeyEvent e = new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+ enqueueInputEvent(e);
+ Message m = obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
+ m.setAsynchronous(true);
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
}
+ }
- if (newState == STATE_DOWN_OR_RIGHT || newState == STATE_UP_OR_LEFT) {
- // send a button down event
- final int keyCode = joystickAxisAndStateToKeycode(axis, newState);
- if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
- KeyEvent keyEvent = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode,
- 0, metaState, deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
- enqueueInputEvent(keyEvent);
- Message m = obtainMessage(repeatMessage, keyEvent);
- m.setAsynchronous(true);
- sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
- // store the corresponding ACTION_UP event so that it can be sent
- // if focus is lost or root view is removed
- mDeviceKeyEvents.put(deviceId,
- new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode,
- 0, metaState, deviceId, 0,
- KeyEvent.FLAG_FALLBACK | KeyEvent.FLAG_CANCELED,
- source));
- }
- }
- if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) {
- mAxisStatesStick[axisStateIndex] = newState;
- } else {
- mAxisStatesHat[axisStateIndex] = newState;
+ if (yDirection != mLastYDirection) {
+ if (mLastYKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
+ enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+ mLastYKeyCode = 0;
}
- }
- private boolean isXAxis(int axis) {
- return axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_HAT_X;
- }
- private boolean isYAxis(int axis) {
- return axis == MotionEvent.AXIS_Y || axis == MotionEvent.AXIS_HAT_Y;
- }
+ mLastYDirection = yDirection;
- private int joystickAxisAndStateToKeycode(int axis, int state) {
- if (isXAxis(axis) && state == STATE_UP_OR_LEFT) {
- return KeyEvent.KEYCODE_DPAD_LEFT;
- }
- if (isXAxis(axis) && state == STATE_DOWN_OR_RIGHT) {
- return KeyEvent.KEYCODE_DPAD_RIGHT;
- }
- if (isYAxis(axis) && state == STATE_UP_OR_LEFT) {
- return KeyEvent.KEYCODE_DPAD_UP;
+ if (yDirection != 0 && synthesizeNewKeys) {
+ mLastYKeyCode = yDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
+ final KeyEvent e = new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+ enqueueInputEvent(e);
+ Message m = obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
+ m.setAsynchronous(true);
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
}
- if (isYAxis(axis) && state == STATE_DOWN_OR_RIGHT) {
- return KeyEvent.KEYCODE_DPAD_DOWN;
- }
- Log.e(mTag, "Unknown axis " + axis + " or direction " + state);
- return KeyEvent.KEYCODE_UNKNOWN; // should never happen
}
+ }
- private int joystickAxisValueToState(float value) {
- if (value >= 0.5f) {
- return STATE_DOWN_OR_RIGHT;
- } else if (value <= -0.5f) {
- return STATE_UP_OR_LEFT;
- } else {
- return STATE_NEUTRAL;
- }
+ private int joystickAxisValueToDirection(float value) {
+ if (value >= 0.5f) {
+ return 1;
+ } else if (value <= -0.5f) {
+ return -1;
+ } else {
+ return 0;
}
}
}
@@ -6212,6 +6108,7 @@ public final class ViewRootImpl implements ViewParent,
if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
}
+ //Log.d(mTag, ">>>>>> CALLING relayout");
if (params != null && mOrigWindowType != params.type) {
// For compatibility with old apps, don't crash here.
if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
@@ -6232,6 +6129,7 @@ public final class ViewRootImpl implements ViewParent,
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
+ //Log.d(mTag, "<<<<<< BACK FROM relayout");
if (restore) {
params.restore();
}
diff --git a/android/view/ViewStructure.java b/android/view/ViewStructure.java
index 309366c6..f671c349 100644
--- a/android/view/ViewStructure.java
+++ b/android/view/ViewStructure.java
@@ -365,30 +365,6 @@ public abstract class ViewStructure {
public abstract void setDataIsSensitive(boolean sensitive);
/**
- * Sets the minimum width in ems of the text associated with this view, when supported.
- *
- * <p>Should only be set when the node is used for autofill purposes - it will be ignored
- * when used for Assist.
- */
- public abstract void setMinTextEms(int minEms);
-
- /**
- * Sets the maximum width in ems of the text associated with this view, when supported.
- *
- * <p>Should only be set when the node is used for autofill purposes - it will be ignored
- * when used for Assist.
- */
- public abstract void setMaxTextEms(int maxEms);
-
- /**
- * Sets the maximum length of the text associated with this view, when supported.
- *
- * <p>Should only be set when the node is used for autofill purposes - it will be ignored
- * when used for Assist.
- */
- public abstract void setMaxTextLength(int maxLength);
-
- /**
* Call when done populating a {@link ViewStructure} returned by
* {@link #asyncNewChild}.
*/
diff --git a/android/view/WindowManagerInternal.java b/android/view/WindowManagerInternal.java
index 69cc1002..98f8dc8e 100644
--- a/android/view/WindowManagerInternal.java
+++ b/android/view/WindowManagerInternal.java
@@ -335,8 +335,8 @@ public abstract class WindowManagerInternal {
public abstract void setOnHardKeyboardStatusChangeListener(
OnHardKeyboardStatusChangeListener listener);
- /** Returns true if a stack in the windowing mode is currently visible. */
- public abstract boolean isStackVisible(int windowingMode);
+ /** Returns true if the stack with the input Id is currently visible. */
+ public abstract boolean isStackVisible(int stackId);
/**
* @return True if and only if the docked divider is currently in resize mode.
diff --git a/android/view/WindowManagerPolicy.java b/android/view/WindowManagerPolicy.java
index 137e551d..da72535d 100644
--- a/android/view/WindowManagerPolicy.java
+++ b/android/view/WindowManagerPolicy.java
@@ -467,8 +467,11 @@ public interface WindowManagerPolicy {
*/
public boolean isDimming();
- /** @return the current windowing mode of this window. */
- int getWindowingMode();
+ /**
+ * @return the stack id this windows belongs to, or {@link StackId#INVALID_STACK_ID} if
+ * not attached to any stack.
+ */
+ int getStackId();
/**
* Returns true if the window is current in multi-windowing mode. i.e. it shares the
diff --git a/android/view/accessibility/AccessibilityCache.java b/android/view/accessibility/AccessibilityCache.java
index d7851171..0f21c5c8 100644
--- a/android/view/accessibility/AccessibilityCache.java
+++ b/android/view/accessibility/AccessibilityCache.java
@@ -329,6 +329,8 @@ public final class AccessibilityCache {
final long oldParentId = oldInfo.getParentNodeId();
if (info.getParentNodeId() != oldParentId) {
clearSubTreeLocked(windowId, oldParentId);
+ } else {
+ oldInfo.recycle();
}
}
diff --git a/android/view/accessibility/AccessibilityManager.java b/android/view/accessibility/AccessibilityManager.java
index 11cb046a..0b9bc576 100644
--- a/android/view/accessibility/AccessibilityManager.java
+++ b/android/view/accessibility/AccessibilityManager.java
@@ -16,46 +16,152 @@
package android.view.accessibility;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
+
+import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemService;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
import android.view.IWindow;
import android.view.View;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IntPair;
+
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
- * System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
- * Such events are generated when something notable happens in the user interface,
+ * System level service that serves as an event dispatch for {@link AccessibilityEvent}s,
+ * and provides facilities for querying the accessibility state of the system.
+ * Accessibility events are generated when something notable happens in the user interface,
* for example an {@link android.app.Activity} starts, the focus or selection of a
* {@link android.view.View} changes etc. Parties interested in handling accessibility
* events implement and register an accessibility service which extends
- * {@code android.accessibilityservice.AccessibilityService}.
+ * {@link android.accessibilityservice.AccessibilityService}.
*
* @see AccessibilityEvent
- * @see android.content.Context#getSystemService
+ * @see AccessibilityNodeInfo
+ * @see android.accessibilityservice.AccessibilityService
+ * @see Context#getSystemService
+ * @see Context#ACCESSIBILITY_SERVICE
*/
-@SuppressWarnings("UnusedDeclaration")
+@SystemService(Context.ACCESSIBILITY_SERVICE)
public final class AccessibilityManager {
+ private static final boolean DEBUG = false;
+
+ private static final String LOG_TAG = "AccessibilityManager";
+
+ /** @hide */
+ public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 0x00000001;
+
+ /** @hide */
+ public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002;
+
+ /** @hide */
+ public static final int STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED = 0x00000004;
+
+ /** @hide */
+ public static final int DALTONIZER_DISABLED = -1;
+
+ /** @hide */
+ public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0;
+
+ /** @hide */
+ public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12;
+
+ /** @hide */
+ public static final int AUTOCLICK_DELAY_DEFAULT = 600;
+
+ /**
+ * Activity action: Launch UI to manage which accessibility service or feature is assigned
+ * to the navigation bar Accessibility button.
+ * <p>
+ * Input: Nothing.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON =
+ "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON";
+
+ static final Object sInstanceSync = new Object();
+
+ private static AccessibilityManager sInstance;
+
+ private final Object mLock = new Object();
+
+ private IAccessibilityManager mService;
+
+ final int mUserId;
+
+ final Handler mHandler;
+
+ final Handler.Callback mCallback;
+
+ boolean mIsEnabled;
- private static AccessibilityManager sInstance = new AccessibilityManager(null, null, 0);
+ int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+ boolean mIsTouchExplorationEnabled;
+
+ boolean mIsHighTextContrastEnabled;
+
+ private final ArrayMap<AccessibilityStateChangeListener, Handler>
+ mAccessibilityStateChangeListeners = new ArrayMap<>();
+
+ private final ArrayMap<TouchExplorationStateChangeListener, Handler>
+ mTouchExplorationStateChangeListeners = new ArrayMap<>();
+
+ private final ArrayMap<HighTextContrastChangeListener, Handler>
+ mHighTextContrastStateChangeListeners = new ArrayMap<>();
+
+ private final ArrayMap<AccessibilityServicesStateChangeListener, Handler>
+ mServicesStateChangeListeners = new ArrayMap<>();
+
+ /**
+ * Map from a view's accessibility id to the list of request preparers set for that view
+ */
+ private SparseArray<List<AccessibilityRequestPreparer>> mRequestPreparerLists;
/**
- * Listener for the accessibility state.
+ * Listener for the system accessibility state. To listen for changes to the
+ * accessibility state on the device, implement this interface and register
+ * it with the system by calling {@link #addAccessibilityStateChangeListener}.
*/
public interface AccessibilityStateChangeListener {
/**
- * Called back on change in the accessibility state.
+ * Called when the accessibility enabled state changes.
*
* @param enabled Whether accessibility is enabled.
*/
- public void onAccessibilityStateChanged(boolean enabled);
+ void onAccessibilityStateChanged(boolean enabled);
}
/**
@@ -71,7 +177,24 @@ public final class AccessibilityManager {
*
* @param enabled Whether touch exploration is enabled.
*/
- public void onTouchExplorationStateChanged(boolean enabled);
+ void onTouchExplorationStateChanged(boolean enabled);
+ }
+
+ /**
+ * Listener for changes to the state of accessibility services. Changes include services being
+ * enabled or disabled, or changes to the {@link AccessibilityServiceInfo} of a running service.
+ * {@see #addAccessibilityServicesStateChangeListener}.
+ *
+ * @hide
+ */
+ public interface AccessibilityServicesStateChangeListener {
+
+ /**
+ * Called when the state of accessibility services changes.
+ *
+ * @param manager The manager that is calling back
+ */
+ void onAccessibilityServicesStateChanged(AccessibilityManager manager);
}
/**
@@ -79,6 +202,8 @@ public final class AccessibilityManager {
* the high text contrast state on the device, implement this interface and
* register it with the system by calling
* {@link #addHighTextContrastStateChangeListener}.
+ *
+ * @hide
*/
public interface HighTextContrastChangeListener {
@@ -87,26 +212,72 @@ public final class AccessibilityManager {
*
* @param enabled Whether high text contrast is enabled.
*/
- public void onHighTextContrastStateChanged(boolean enabled);
+ void onHighTextContrastStateChanged(boolean enabled);
}
private final IAccessibilityManagerClient.Stub mClient =
new IAccessibilityManagerClient.Stub() {
- public void setState(int state) {
- }
+ @Override
+ public void setState(int state) {
+ // We do not want to change this immediately as the application may
+ // have already checked that accessibility is on and fired an event,
+ // that is now propagating up the view tree, Hence, if accessibility
+ // is now off an exception will be thrown. We want to have the exception
+ // enforcement to guard against apps that fire unnecessary accessibility
+ // events when accessibility is off.
+ mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget();
+ }
- public void notifyServicesStateChanged() {
+ @Override
+ public void notifyServicesStateChanged() {
+ final ArrayMap<AccessibilityServicesStateChangeListener, Handler> listeners;
+ synchronized (mLock) {
+ if (mServicesStateChangeListeners.isEmpty()) {
+ return;
}
+ listeners = new ArrayMap<>(mServicesStateChangeListeners);
+ }
- public void setRelevantEventTypes(int eventTypes) {
- }
- };
+ int numListeners = listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ final AccessibilityServicesStateChangeListener listener =
+ mServicesStateChangeListeners.keyAt(i);
+ mServicesStateChangeListeners.valueAt(i).post(() -> listener
+ .onAccessibilityServicesStateChanged(AccessibilityManager.this));
+ }
+ }
+
+ @Override
+ public void setRelevantEventTypes(int eventTypes) {
+ mRelevantEventTypes = eventTypes;
+ }
+ };
/**
* Get an AccessibilityManager instance (create one if necessary).
*
+ * @param context Context in which this manager operates.
+ *
+ * @hide
*/
public static AccessibilityManager getInstance(Context context) {
+ synchronized (sInstanceSync) {
+ if (sInstance == null) {
+ final int userId;
+ if (Binder.getCallingUid() == Process.SYSTEM_UID
+ || context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS)
+ == PackageManager.PERMISSION_GRANTED
+ || context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED) {
+ userId = UserHandle.USER_CURRENT;
+ } else {
+ userId = UserHandle.myUserId();
+ }
+ sInstance = new AccessibilityManager(context, null, userId);
+ }
+ }
return sInstance;
}
@@ -114,21 +285,68 @@ public final class AccessibilityManager {
* Create an instance.
*
* @param context A {@link Context}.
+ * @param service An interface to the backing service.
+ * @param userId User id under which to run.
+ *
+ * @hide
*/
public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
+ // Constructor can't be chained because we can't create an instance of an inner class
+ // before calling another constructor.
+ mCallback = new MyCallback();
+ mHandler = new Handler(context.getMainLooper(), mCallback);
+ mUserId = userId;
+ synchronized (mLock) {
+ tryConnectToServiceLocked(service);
+ }
+ }
+
+ /**
+ * Create an instance.
+ *
+ * @param handler The handler to use
+ * @param service An interface to the backing service.
+ * @param userId User id under which to run.
+ *
+ * @hide
+ */
+ public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) {
+ mCallback = new MyCallback();
+ mHandler = handler;
+ mUserId = userId;
+ synchronized (mLock) {
+ tryConnectToServiceLocked(service);
+ }
}
+ /**
+ * @hide
+ */
public IAccessibilityManagerClient getClient() {
return mClient;
}
/**
- * Returns if the {@link AccessibilityManager} is enabled.
+ * @hide
+ */
+ @VisibleForTesting
+ public Handler.Callback getCallback() {
+ return mCallback;
+ }
+
+ /**
+ * Returns if the accessibility in the system is enabled.
*
- * @return True if this {@link AccessibilityManager} is enabled, false otherwise.
+ * @return True if accessibility is enabled, false otherwise.
*/
public boolean isEnabled() {
- return false;
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ return mIsEnabled;
+ }
}
/**
@@ -137,7 +355,13 @@ public final class AccessibilityManager {
* @return True if touch exploration is enabled, false otherwise.
*/
public boolean isTouchExplorationEnabled() {
- return true;
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ return mIsTouchExplorationEnabled;
+ }
}
/**
@@ -147,35 +371,169 @@ public final class AccessibilityManager {
* doing its own rendering and does not rely on the platform rendering pipeline.
* </p>
*
+ * @return True if high text contrast is enabled, false otherwise.
+ *
+ * @hide
*/
public boolean isHighTextContrastEnabled() {
- return false;
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ return mIsHighTextContrastEnabled;
+ }
}
/**
* Sends an {@link AccessibilityEvent}.
+ *
+ * @param event The event to send.
+ *
+ * @throws IllegalStateException if accessibility is not enabled.
+ *
+ * <strong>Note:</strong> The preferred mechanism for sending custom accessibility
+ * events is through calling
+ * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
+ * instead of this method to allow predecessors to augment/filter events sent by
+ * their descendants.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ if (!mIsEnabled) {
+ Looper myLooper = Looper.myLooper();
+ if (myLooper == Looper.getMainLooper()) {
+ throw new IllegalStateException(
+ "Accessibility off. Did you forget to check that?");
+ } else {
+ // If we're not running on the thread with the main looper, it's possible for
+ // the state of accessibility to change between checking isEnabled and
+ // calling this method. So just log the error rather than throwing the
+ // exception.
+ Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
+ return;
+ }
+ }
+ if ((event.getEventType() & mRelevantEventTypes) == 0) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event
+ + " that is not among "
+ + AccessibilityEvent.eventTypeToString(mRelevantEventTypes));
+ }
+ return;
+ }
+ userId = mUserId;
+ }
+ try {
+ event.setEventTime(SystemClock.uptimeMillis());
+ // it is possible that this manager is in the same process as the service but
+ // client using it is called through Binder from another process. Example: MMS
+ // app adds a SMS notification and the NotificationManagerService calls this method
+ long identityToken = Binder.clearCallingIdentity();
+ service.sendAccessibilityEvent(event, userId);
+ Binder.restoreCallingIdentity(identityToken);
+ if (DEBUG) {
+ Log.i(LOG_TAG, event + " sent");
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error during sending " + event + " ", re);
+ } finally {
+ event.recycle();
+ }
}
/**
- * Requests interruption of the accessibility feedback from all accessibility services.
+ * Requests feedback interruption from all accessibility services.
*/
public void interrupt() {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ if (!mIsEnabled) {
+ Looper myLooper = Looper.myLooper();
+ if (myLooper == Looper.getMainLooper()) {
+ throw new IllegalStateException(
+ "Accessibility off. Did you forget to check that?");
+ } else {
+ // If we're not running on the thread with the main looper, it's possible for
+ // the state of accessibility to change between checking isEnabled and
+ // calling this method. So just log the error rather than throwing the
+ // exception.
+ Log.e(LOG_TAG, "Interrupt called with accessibility disabled");
+ return;
+ }
+ }
+ userId = mUserId;
+ }
+ try {
+ service.interrupt(userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Requested interrupt from all services");
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re);
+ }
}
/**
* Returns the {@link ServiceInfo}s of the installed accessibility services.
*
* @return An unmodifiable list with {@link ServiceInfo}s.
+ *
+ * @deprecated Use {@link #getInstalledAccessibilityServiceList()}
*/
@Deprecated
public List<ServiceInfo> getAccessibilityServiceList() {
- return Collections.emptyList();
+ List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList();
+ List<ServiceInfo> services = new ArrayList<>();
+ final int infoCount = infos.size();
+ for (int i = 0; i < infoCount; i++) {
+ AccessibilityServiceInfo info = infos.get(i);
+ services.add(info.getResolveInfo().serviceInfo);
+ }
+ return Collections.unmodifiableList(services);
}
+ /**
+ * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services.
+ *
+ * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
+ */
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
- return Collections.emptyList();
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ userId = mUserId;
+ }
+
+ List<AccessibilityServiceInfo> services = null;
+ try {
+ services = service.getInstalledAccessibilityServiceList(userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
+ }
+ if (services != null) {
+ return Collections.unmodifiableList(services);
+ } else {
+ return Collections.emptyList();
+ }
}
/**
@@ -190,21 +548,48 @@ public final class AccessibilityManager {
* @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
* @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
* @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+ * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
*/
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
int feedbackTypeFlags) {
- return Collections.emptyList();
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return Collections.emptyList();
+ }
+ userId = mUserId;
+ }
+
+ List<AccessibilityServiceInfo> services = null;
+ try {
+ services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
+ }
+ if (services != null) {
+ return Collections.unmodifiableList(services);
+ } else {
+ return Collections.emptyList();
+ }
}
/**
* Registers an {@link AccessibilityStateChangeListener} for changes in
- * the global accessibility state of the system.
+ * the global accessibility state of the system. Equivalent to calling
+ * {@link #addAccessibilityStateChangeListener(AccessibilityStateChangeListener, Handler)}
+ * with a null handler.
*
* @param listener The listener.
- * @return True if successfully registered.
+ * @return Always returns {@code true}.
*/
public boolean addAccessibilityStateChangeListener(
- AccessibilityStateChangeListener listener) {
+ @NonNull AccessibilityStateChangeListener listener) {
+ addAccessibilityStateChangeListener(listener, null);
return true;
}
@@ -218,22 +603,40 @@ public final class AccessibilityManager {
* for a callback on the process's main handler.
*/
public void addAccessibilityStateChangeListener(
- @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) {}
+ @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) {
+ synchronized (mLock) {
+ mAccessibilityStateChangeListeners
+ .put(listener, (handler == null) ? mHandler : handler);
+ }
+ }
+ /**
+ * Unregisters an {@link AccessibilityStateChangeListener}.
+ *
+ * @param listener The listener.
+ * @return True if the listener was previously registered.
+ */
public boolean removeAccessibilityStateChangeListener(
- AccessibilityStateChangeListener listener) {
- return true;
+ @NonNull AccessibilityStateChangeListener listener) {
+ synchronized (mLock) {
+ int index = mAccessibilityStateChangeListeners.indexOfKey(listener);
+ mAccessibilityStateChangeListeners.remove(listener);
+ return (index >= 0);
+ }
}
/**
* Registers a {@link TouchExplorationStateChangeListener} for changes in
- * the global touch exploration state of the system.
+ * the global touch exploration state of the system. Equivalent to calling
+ * {@link #addTouchExplorationStateChangeListener(TouchExplorationStateChangeListener, Handler)}
+ * with a null handler.
*
* @param listener The listener.
- * @return True if successfully registered.
+ * @return Always returns {@code true}.
*/
public boolean addTouchExplorationStateChangeListener(
@NonNull TouchExplorationStateChangeListener listener) {
+ addTouchExplorationStateChangeListener(listener, null);
return true;
}
@@ -247,17 +650,103 @@ public final class AccessibilityManager {
* for a callback on the process's main handler.
*/
public void addTouchExplorationStateChangeListener(
- @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) {}
+ @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) {
+ synchronized (mLock) {
+ mTouchExplorationStateChangeListeners
+ .put(listener, (handler == null) ? mHandler : handler);
+ }
+ }
/**
* Unregisters a {@link TouchExplorationStateChangeListener}.
*
* @param listener The listener.
- * @return True if successfully unregistered.
+ * @return True if listener was previously registered.
*/
public boolean removeTouchExplorationStateChangeListener(
@NonNull TouchExplorationStateChangeListener listener) {
- return true;
+ synchronized (mLock) {
+ int index = mTouchExplorationStateChangeListeners.indexOfKey(listener);
+ mTouchExplorationStateChangeListeners.remove(listener);
+ return (index >= 0);
+ }
+ }
+
+ /**
+ * Registers a {@link AccessibilityServicesStateChangeListener}.
+ *
+ * @param listener The listener.
+ * @param handler The handler on which the listener should be called back, or {@code null}
+ * for a callback on the process's main handler.
+ * @hide
+ */
+ public void addAccessibilityServicesStateChangeListener(
+ @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
+ synchronized (mLock) {
+ mServicesStateChangeListeners
+ .put(listener, (handler == null) ? mHandler : handler);
+ }
+ }
+
+ /**
+ * Unregisters a {@link AccessibilityServicesStateChangeListener}.
+ *
+ * @param listener The listener.
+ *
+ * @hide
+ */
+ public void removeAccessibilityServicesStateChangeListener(
+ @NonNull AccessibilityServicesStateChangeListener listener) {
+ // Final CopyOnWriteArrayList - no lock needed.
+ mServicesStateChangeListeners.remove(listener);
+ }
+
+ /**
+ * Registers a {@link AccessibilityRequestPreparer}.
+ */
+ public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
+ if (mRequestPreparerLists == null) {
+ mRequestPreparerLists = new SparseArray<>(1);
+ }
+ int id = preparer.getView().getAccessibilityViewId();
+ List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(id);
+ if (requestPreparerList == null) {
+ requestPreparerList = new ArrayList<>(1);
+ mRequestPreparerLists.put(id, requestPreparerList);
+ }
+ requestPreparerList.add(preparer);
+ }
+
+ /**
+ * Unregisters a {@link AccessibilityRequestPreparer}.
+ */
+ public void removeAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
+ if (mRequestPreparerLists == null) {
+ return;
+ }
+ int viewId = preparer.getView().getAccessibilityViewId();
+ List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(viewId);
+ if (requestPreparerList != null) {
+ requestPreparerList.remove(preparer);
+ if (requestPreparerList.isEmpty()) {
+ mRequestPreparerLists.remove(viewId);
+ }
+ }
+ }
+
+ /**
+ * Get the preparers that are registered for an accessibility ID
+ *
+ * @param id The ID of interest
+ * @return The list of preparers, or {@code null} if there are none.
+ *
+ * @hide
+ */
+ public List<AccessibilityRequestPreparer> getRequestPreparersForAccessibilityId(int id) {
+ if (mRequestPreparerLists == null) {
+ return null;
+ }
+ return mRequestPreparerLists.get(id);
}
/**
@@ -269,7 +758,12 @@ public final class AccessibilityManager {
* @hide
*/
public void addHighTextContrastStateChangeListener(
- @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {}
+ @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {
+ synchronized (mLock) {
+ mHighTextContrastStateChangeListeners
+ .put(listener, (handler == null) ? mHandler : handler);
+ }
+ }
/**
* Unregisters a {@link HighTextContrastChangeListener}.
@@ -279,7 +773,51 @@ public final class AccessibilityManager {
* @hide
*/
public void removeHighTextContrastStateChangeListener(
- @NonNull HighTextContrastChangeListener listener) {}
+ @NonNull HighTextContrastChangeListener listener) {
+ synchronized (mLock) {
+ mHighTextContrastStateChangeListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Check if the accessibility volume stream is active.
+ *
+ * @return True if accessibility volume is active (i.e. some service has requested it). False
+ * otherwise.
+ * @hide
+ */
+ public boolean isAccessibilityVolumeStreamActive() {
+ List<AccessibilityServiceInfo> serviceInfos =
+ getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+ for (int i = 0; i < serviceInfos.size(); i++) {
+ if ((serviceInfos.get(i).flags & FLAG_ENABLE_ACCESSIBILITY_VOLUME) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Report a fingerprint gesture to accessibility. Only available for the system process.
+ *
+ * @param keyCode The key code of the gesture
+ * @return {@code true} if accessibility consumes the event. {@code false} if not.
+ * @hide
+ */
+ public boolean sendFingerprintGesture(int keyCode) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ }
+ try {
+ return service.sendFingerprintGesture(keyCode);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
/**
* Sets the current state and notifies listeners, if necessary.
@@ -287,14 +825,314 @@ public final class AccessibilityManager {
* @param stateFlags The state flags.
*/
private void setStateLocked(int stateFlags) {
+ final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
+ final boolean touchExplorationEnabled =
+ (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
+ final boolean highTextContrastEnabled =
+ (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
+
+ final boolean wasEnabled = mIsEnabled;
+ final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
+ final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
+
+ // Ensure listeners get current state from isZzzEnabled() calls.
+ mIsEnabled = enabled;
+ mIsTouchExplorationEnabled = touchExplorationEnabled;
+ mIsHighTextContrastEnabled = highTextContrastEnabled;
+
+ if (wasEnabled != enabled) {
+ notifyAccessibilityStateChanged();
+ }
+
+ if (wasTouchExplorationEnabled != touchExplorationEnabled) {
+ notifyTouchExplorationStateChanged();
+ }
+
+ if (wasHighTextContrastEnabled != highTextContrastEnabled) {
+ notifyHighTextContrastStateChanged();
+ }
}
+ /**
+ * Find an installed service with the specified {@link ComponentName}.
+ *
+ * @param componentName The name to match to the service.
+ *
+ * @return The info corresponding to the installed service, or {@code null} if no such service
+ * is installed.
+ * @hide
+ */
+ public AccessibilityServiceInfo getInstalledServiceInfoWithComponentName(
+ ComponentName componentName) {
+ final List<AccessibilityServiceInfo> installedServiceInfos =
+ getInstalledAccessibilityServiceList();
+ if ((installedServiceInfos == null) || (componentName == null)) {
+ return null;
+ }
+ for (int i = 0; i < installedServiceInfos.size(); i++) {
+ if (componentName.equals(installedServiceInfos.get(i).getComponentName())) {
+ return installedServiceInfos.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds an accessibility interaction connection interface for a given window.
+ * @param windowToken The window token to which a connection is added.
+ * @param connection The connection.
+ *
+ * @hide
+ */
public int addAccessibilityInteractionConnection(IWindow windowToken,
IAccessibilityInteractionConnection connection) {
+ final IAccessibilityManager service;
+ final int userId;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return View.NO_ID;
+ }
+ userId = mUserId;
+ }
+ try {
+ return service.addAccessibilityInteractionConnection(windowToken, connection, userId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
+ }
return View.NO_ID;
}
+ /**
+ * Removed an accessibility interaction connection interface for a given window.
+ * @param windowToken The window token to which a connection is removed.
+ *
+ * @hide
+ */
public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.removeAccessibilityInteractionConnection(windowToken);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
+ }
+ }
+
+ /**
+ * Perform the accessibility shortcut if the caller has permission.
+ *
+ * @hide
+ */
+ public void performAccessibilityShortcut() {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.performAccessibilityShortcut();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
+ }
+ }
+
+ /**
+ * Notifies that the accessibility button in the system's navigation area has been clicked
+ *
+ * @hide
+ */
+ public void notifyAccessibilityButtonClicked() {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.notifyAccessibilityButtonClicked();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while dispatching accessibility button click", re);
+ }
+ }
+
+ /**
+ * Notifies that the visibility of the accessibility button in the system's navigation area
+ * has changed.
+ *
+ * @param shown {@code true} if the accessibility button is visible within the system
+ * navigation area, {@code false} otherwise
+ * @hide
+ */
+ public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.notifyAccessibilityButtonVisibilityChanged(shown);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re);
+ }
+ }
+
+ /**
+ * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture
+ * window. Intended for use by the System UI only.
+ *
+ * @param connection The connection to handle the actions. Set to {@code null} to avoid
+ * affecting the actions.
+ *
+ * @hide
+ */
+ public void setPictureInPictureActionReplacingConnection(
+ @Nullable IAccessibilityInteractionConnection connection) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.setPictureInPictureActionReplacingConnection(connection);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting picture in picture action replacement", re);
+ }
}
+ private IAccessibilityManager getServiceLocked() {
+ if (mService == null) {
+ tryConnectToServiceLocked(null);
+ }
+ return mService;
+ }
+
+ private void tryConnectToServiceLocked(IAccessibilityManager service) {
+ if (service == null) {
+ IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ if (iBinder == null) {
+ return;
+ }
+ service = IAccessibilityManager.Stub.asInterface(iBinder);
+ }
+
+ try {
+ final long userStateAndRelevantEvents = service.addClient(mClient, mUserId);
+ setStateLocked(IntPair.first(userStateAndRelevantEvents));
+ mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
+ mService = service;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+ }
+ }
+
+ /**
+ * Notifies the registered {@link AccessibilityStateChangeListener}s.
+ */
+ private void notifyAccessibilityStateChanged() {
+ final boolean isEnabled;
+ final ArrayMap<AccessibilityStateChangeListener, Handler> listeners;
+ synchronized (mLock) {
+ if (mAccessibilityStateChangeListeners.isEmpty()) {
+ return;
+ }
+ isEnabled = mIsEnabled;
+ listeners = new ArrayMap<>(mAccessibilityStateChangeListeners);
+ }
+
+ int numListeners = listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ final AccessibilityStateChangeListener listener =
+ mAccessibilityStateChangeListeners.keyAt(i);
+ mAccessibilityStateChangeListeners.valueAt(i)
+ .post(() -> listener.onAccessibilityStateChanged(isEnabled));
+ }
+ }
+
+ /**
+ * Notifies the registered {@link TouchExplorationStateChangeListener}s.
+ */
+ private void notifyTouchExplorationStateChanged() {
+ final boolean isTouchExplorationEnabled;
+ final ArrayMap<TouchExplorationStateChangeListener, Handler> listeners;
+ synchronized (mLock) {
+ if (mTouchExplorationStateChangeListeners.isEmpty()) {
+ return;
+ }
+ isTouchExplorationEnabled = mIsTouchExplorationEnabled;
+ listeners = new ArrayMap<>(mTouchExplorationStateChangeListeners);
+ }
+
+ int numListeners = listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ final TouchExplorationStateChangeListener listener =
+ mTouchExplorationStateChangeListeners.keyAt(i);
+ mTouchExplorationStateChangeListeners.valueAt(i)
+ .post(() -> listener.onTouchExplorationStateChanged(isTouchExplorationEnabled));
+ }
+ }
+
+ /**
+ * Notifies the registered {@link HighTextContrastChangeListener}s.
+ */
+ private void notifyHighTextContrastStateChanged() {
+ final boolean isHighTextContrastEnabled;
+ final ArrayMap<HighTextContrastChangeListener, Handler> listeners;
+ synchronized (mLock) {
+ if (mHighTextContrastStateChangeListeners.isEmpty()) {
+ return;
+ }
+ isHighTextContrastEnabled = mIsHighTextContrastEnabled;
+ listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners);
+ }
+
+ int numListeners = listeners.size();
+ for (int i = 0; i < numListeners; i++) {
+ final HighTextContrastChangeListener listener =
+ mHighTextContrastStateChangeListeners.keyAt(i);
+ mHighTextContrastStateChangeListeners.valueAt(i)
+ .post(() -> listener.onHighTextContrastStateChanged(isHighTextContrastEnabled));
+ }
+ }
+
+ /**
+ * Determines if the accessibility button within the system navigation area is supported.
+ *
+ * @return {@code true} if the accessibility button is supported on this device,
+ * {@code false} otherwise
+ */
+ public static boolean isAccessibilityButtonSupported() {
+ final Resources res = Resources.getSystem();
+ return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
+ }
+
+ private final class MyCallback implements Handler.Callback {
+ public static final int MSG_SET_STATE = 1;
+
+ @Override
+ public boolean handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_SET_STATE: {
+ // See comment at mClient
+ final int state = message.arg1;
+ synchronized (mLock) {
+ setStateLocked(state);
+ }
+ } break;
+ }
+ return true;
+ }
+ }
}
diff --git a/android/view/autofill/AutofillManager.java b/android/view/autofill/AutofillManager.java
index e564fa34..4fb2a99a 100644
--- a/android/view/autofill/AutofillManager.java
+++ b/android/view/autofill/AutofillManager.java
@@ -91,10 +91,10 @@ import java.util.Objects;
* </ul>
*
* <p>When the service returns datasets, the Android System displays an autofill dataset picker
- * UI associated with the view, when the view is focused on and is part of a dataset.
- * The application can be notified when the UI is shown by registering an
+ * UI affordance associated with the view, when the view is focused on and is part of a dataset.
+ * The application can be notified when the affordance is shown by registering an
* {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
- * selects a dataset from the UI, all views present in the dataset are autofilled, through
+ * selects a dataset from the affordance, all views present in the dataset are autofilled, through
* calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
*
* <p>When the service returns ids of savable views, the Android System keeps track of changes
@@ -108,7 +108,7 @@ import java.util.Objects;
* </ul>
*
* <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
- * shows an autofill save UI if the value of savable views have changed. If the user selects the
+ * shows a save UI affordance if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
* <p>It is safe to call into its methods from any thread.
@@ -150,12 +150,6 @@ public final class AutofillManager {
* service authentication will contain the Bundle set by
* {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
*
- * <p>On Android {@link android.os.Build.VERSION_CODES#P} and higher, the autofill service
- * can also add this bundle to the {@link Intent} set as the
- * {@link android.app.Activity#setResult(int, Intent) result} for an authentication request,
- * so the bundle can be recovered later on
- * {@link android.service.autofill.SaveRequest#getClientState()}.
- *
* <p>
* Type: {@link android.os.Bundle}
*/
@@ -317,14 +311,6 @@ public final class AutofillManager {
@GuardedBy("mLock")
@Nullable private ArraySet<AutofillId> mFillableIds;
- /** If set, session is commited when the field is clicked. */
- @GuardedBy("mLock")
- @Nullable private AutofillId mSaveTriggerId;
-
- /** If set, session is commited when the activity is finished; otherwise session is canceled. */
- @GuardedBy("mLock")
- private boolean mSaveOnFinish;
-
/** @hide */
public interface AutofillClient {
/**
@@ -848,46 +834,6 @@ public final class AutofillManager {
}
}
-
- /**
- * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
- *
- * @hide
- */
- public void notifyViewClicked(View view) {
- final AutofillId id = view.getAutofillId();
-
- if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
-
- synchronized (mLock) {
- if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
- if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
- commitLocked();
- mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
- mContext.getPackageName());
- }
- }
- }
-
- /**
- * Called by {@link android.app.Activity} to commit or cancel the session on finish.
- *
- * @hide
- */
- public void onActivityFinished() {
- if (!hasAutofillFeature()) {
- return;
- }
- synchronized (mLock) {
- if (mSaveOnFinish) {
- commitLocked();
- } else {
- if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
- cancelLocked();
- }
- }
- }
-
/**
* Called to indicate the current autofill context should be commited.
*
@@ -904,15 +850,12 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- commitLocked();
- }
- }
+ if (!mEnabled && !isActiveLocked()) {
+ return;
+ }
- private void commitLocked() {
- if (!mEnabled && !isActiveLocked()) {
- return;
+ finishSessionLocked();
}
- finishSessionLocked();
}
/**
@@ -931,15 +874,12 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
- cancelLocked();
- }
- }
+ if (!mEnabled && !isActiveLocked()) {
+ return;
+ }
- private void cancelLocked() {
- if (!mEnabled && !isActiveLocked()) {
- return;
+ cancelSessionLocked();
}
- cancelSessionLocked();
}
/** @hide */
@@ -997,12 +937,7 @@ public final class AutofillManager {
}
private AutofillClient getClientLocked() {
- final AutofillClient client = mContext.getAutofillClient();
- if (client == null && sDebug) {
- Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
- + mContext);
- }
- return client;
+ return mContext.getAutofillClient();
}
/** @hide */
@@ -1024,10 +959,6 @@ public final class AutofillManager {
final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
final Bundle responseData = new Bundle();
responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
- final Bundle newClientState = data.getBundleExtra(EXTRA_CLIENT_STATE);
- if (newClientState != null) {
- responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
- }
try {
mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
mContext.getUserId());
@@ -1107,7 +1038,6 @@ public final class AutofillManager {
mState = STATE_UNKNOWN;
mTrackedViews = null;
mFillableIds = null;
- mSaveTriggerId = null;
}
private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
@@ -1359,15 +1289,12 @@ public final class AutofillManager {
/**
* Set the tracked views.
*
- * @param trackedIds The views to be tracked.
+ * @param trackedIds The views to be tracked
* @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
- * @param saveOnFinish Finish the session once the activity is finished.
* @param fillableIds Views that might anchor FillUI.
- * @param saveTriggerId View that when clicked triggers commit().
*/
private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
- boolean saveOnAllViewsInvisible, boolean saveOnFinish,
- @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
+ boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) {
synchronized (mLock) {
if (mEnabled && mSessionId == sessionId) {
if (saveOnAllViewsInvisible) {
@@ -1375,7 +1302,6 @@ public final class AutofillManager {
} else {
mTrackedViews = null;
}
- mSaveOnFinish = saveOnFinish;
if (fillableIds != null) {
if (mFillableIds == null) {
mFillableIds = new ArraySet<>(fillableIds.length);
@@ -1388,30 +1314,10 @@ public final class AutofillManager {
+ ", mFillableIds" + mFillableIds);
}
}
-
- if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
- // Turn off trigger on previous view id.
- setNotifyOnClickLocked(mSaveTriggerId, false);
- }
-
- if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
- // Turn on trigger on new view id.
- mSaveTriggerId = saveTriggerId;
- setNotifyOnClickLocked(mSaveTriggerId, true);
- }
}
}
}
- private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
- final View view = findView(id);
- if (view == null) {
- Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
- return;
- }
- view.setNotifyAutofillManagerOnClick(notify);
- }
-
private void setSaveUiState(int sessionId, boolean shown) {
if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
synchronized (mLock) {
@@ -1584,7 +1490,6 @@ public final class AutofillManager {
final String pfx = outerPrefix + " ";
pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
- pw.print(pfx); pw.print("context: "); pw.println(mContext);
pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
@@ -1599,8 +1504,6 @@ public final class AutofillManager {
pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
}
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
- pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
- pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
}
private String getStateAsStringLocked() {
@@ -1849,7 +1752,7 @@ public final class AutofillManager {
* Callback for autofill related events.
*
* <p>Typically used for applications that display their own "auto-complete" views, so they can
- * enable / disable such views when the autofill UI is shown / hidden.
+ * enable / disable such views when the autofill UI affordance is shown / hidden.
*/
public abstract static class AutofillCallback {
@@ -1859,26 +1762,26 @@ public final class AutofillManager {
public @interface AutofillEventType {}
/**
- * The autofill input UI associated with the view was shown.
+ * The autofill input UI affordance associated with the view was shown.
*
- * <p>If the view provides its own auto-complete UI and its currently shown, it
+ * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
* should be hidden upon receiving this event.
*/
public static final int EVENT_INPUT_SHOWN = 1;
/**
- * The autofill input UI associated with the view was hidden.
+ * The autofill input UI affordance associated with the view was hidden.
*
- * <p>If the view provides its own auto-complete UI that was hidden upon a
+ * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
* {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
*/
public static final int EVENT_INPUT_HIDDEN = 2;
/**
- * The autofill input UI associated with the view isn't shown because
+ * The autofill input UI affordance associated with the view isn't shown because
* autofill is not available.
*
- * <p>If the view provides its own auto-complete UI but was not displaying it
+ * <p>If the view provides its own auto-complete UI affordance but was not displaying it
* to avoid flickering, it could shown it upon receiving this event.
*/
public static final int EVENT_INPUT_UNAVAILABLE = 3;
@@ -1980,12 +1883,12 @@ public final class AutofillManager {
@Override
public void setTrackedViews(int sessionId, AutofillId[] ids,
- boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
- AutofillId saveTriggerId) {
+ boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
- saveOnFinish, fillableIds, saveTriggerId));
+ afm.post(() ->
+ afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds)
+ );
}
}
diff --git a/android/view/textclassifier/TextClassifier.java b/android/view/textclassifier/TextClassifier.java
index c3601d9d..bb1e693f 100644
--- a/android/view/textclassifier/TextClassifier.java
+++ b/android/view/textclassifier/TextClassifier.java
@@ -152,12 +152,4 @@ public interface TextClassifier {
*/
@WorkerThread
default void logEvent(String source, String event) {}
-
- /**
- * Returns this TextClassifier's settings.
- * @hide
- */
- default TextClassifierConstants getSettings() {
- return TextClassifierConstants.DEFAULT;
- }
}
diff --git a/android/view/textclassifier/TextClassifierConstants.java b/android/view/textclassifier/TextClassifierConstants.java
deleted file mode 100644
index 51e6168e..00000000
--- a/android/view/textclassifier/TextClassifierConstants.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.util.KeyValueListParser;
-import android.util.Slog;
-
-/**
- * TextClassifier specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * <pre>
- * smart_selection_dark_launch (boolean)
- * smart_selection_enabled_for_edit_text (boolean)
- * </pre>
- *
- * <p>
- * Type: string
- * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
- *
- * Example of setting the values for testing.
- * adb shell settings put global text_classifier_constants smart_selection_dark_launch=true,smart_selection_enabled_for_edit_text=true
- * @hide
- */
-public final class TextClassifierConstants {
-
- private static final String LOG_TAG = "TextClassifierConstants";
-
- private static final String SMART_SELECTION_DARK_LAUNCH =
- "smart_selection_dark_launch";
- private static final String SMART_SELECTION_ENABLED_FOR_EDIT_TEXT =
- "smart_selection_enabled_for_edit_text";
-
- private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
- private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
-
- /** Default settings. */
- static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
-
- private final boolean mDarkLaunch;
- private final boolean mSuggestSelectionEnabledForEditableText;
-
- private TextClassifierConstants() {
- mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
- mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
- }
-
- private TextClassifierConstants(@Nullable String settings) {
- final KeyValueListParser parser = new KeyValueListParser(',');
- try {
- parser.setString(settings);
- } catch (IllegalArgumentException e) {
- // Failed to parse the settings string, log this and move on with defaults.
- Slog.e(LOG_TAG, "Bad TextClassifier settings: " + settings);
- }
- mDarkLaunch = parser.getBoolean(
- SMART_SELECTION_DARK_LAUNCH,
- SMART_SELECTION_DARK_LAUNCH_DEFAULT);
- mSuggestSelectionEnabledForEditableText = parser.getBoolean(
- SMART_SELECTION_ENABLED_FOR_EDIT_TEXT,
- SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT);
- }
-
- static TextClassifierConstants loadFromString(String settings) {
- return new TextClassifierConstants(settings);
- }
-
- public boolean isDarkLaunch() {
- return mDarkLaunch;
- }
-
- public boolean isSuggestSelectionEnabledForEditableText() {
- return mSuggestSelectionEnabledForEditableText;
- }
-}
diff --git a/android/view/textclassifier/TextClassifierImpl.java b/android/view/textclassifier/TextClassifierImpl.java
index ef087472..2aa81a2c 100644
--- a/android/view/textclassifier/TextClassifierImpl.java
+++ b/android/view/textclassifier/TextClassifierImpl.java
@@ -24,12 +24,12 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
+import android.icu.text.BreakIterator;
import android.net.Uri;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.provider.Browser;
import android.provider.ContactsContract;
-import android.provider.Settings;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.method.WordIterator;
@@ -47,7 +47,6 @@ import com.android.internal.util.Preconditions;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -92,8 +91,6 @@ final class TextClassifierImpl implements TextClassifier {
@GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
private SmartSelection mSmartSelection;
- private TextClassifierConstants mSettings;
-
TextClassifierImpl(Context context) {
mContext = Preconditions.checkNotNull(context);
}
@@ -192,15 +189,6 @@ final class TextClassifierImpl implements TextClassifier {
}
}
- @Override
- public TextClassifierConstants getSettings() {
- if (mSettings == null) {
- mSettings = TextClassifierConstants.loadFromString(Settings.Global.getString(
- mContext.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
- }
- return mSettings;
- }
-
private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
synchronized (mSmartSelectionLock) {
localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
diff --git a/android/view/textservice/TextServicesManager.java b/android/view/textservice/TextServicesManager.java
index 8e1f2183..f368c74a 100644
--- a/android/view/textservice/TextServicesManager.java
+++ b/android/view/textservice/TextServicesManager.java
@@ -1,58 +1,213 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2011 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
+ * 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
+ * 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.
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
*/
package android.view.textservice;
+import android.annotation.SystemService;
+import android.content.Context;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
+import com.android.internal.textservice.ITextServicesManager;
+
import java.util.Locale;
/**
- * A stub class of TextServicesManager for Layout-Lib.
+ * System API to the overall text services, which arbitrates interaction between applications
+ * and text services.
+ *
+ * The user can change the current text services in Settings. And also applications can specify
+ * the target text services.
+ *
+ * <h3>Architecture Overview</h3>
+ *
+ * <p>There are three primary parties involved in the text services
+ * framework (TSF) architecture:</p>
+ *
+ * <ul>
+ * <li> The <strong>text services manager</strong> as expressed by this class
+ * is the central point of the system that manages interaction between all
+ * other parts. It is expressed as the client-side API here which exists
+ * in each application context and communicates with a global system service
+ * that manages the interaction across all processes.
+ * <li> A <strong>text service</strong> implements a particular
+ * interaction model allowing the client application to retrieve information of text.
+ * The system binds to the current text service that is in use, causing it to be created and run.
+ * <li> Multiple <strong>client applications</strong> arbitrate with the text service
+ * manager for connections to text services.
+ * </ul>
+ *
+ * <h3>Text services sessions</h3>
+ * <ul>
+ * <li>The <strong>spell checker session</strong> is one of the text services.
+ * {@link android.view.textservice.SpellCheckerSession}</li>
+ * </ul>
+ *
*/
+@SystemService(Context.TEXT_SERVICES_MANAGER_SERVICE)
public final class TextServicesManager {
- private static final TextServicesManager sInstance = new TextServicesManager();
- private static final SpellCheckerInfo[] EMPTY_SPELL_CHECKER_INFO = new SpellCheckerInfo[0];
+ private static final String TAG = TextServicesManager.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private static TextServicesManager sInstance;
+
+ private final ITextServicesManager mService;
+
+ private TextServicesManager() throws ServiceNotFoundException {
+ mService = ITextServicesManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE));
+ }
/**
* Retrieve the global TextServicesManager instance, creating it if it doesn't already exist.
* @hide
*/
public static TextServicesManager getInstance() {
- return sInstance;
+ synchronized (TextServicesManager.class) {
+ if (sInstance == null) {
+ try {
+ sInstance = new TextServicesManager();
+ } catch (ServiceNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Returns the language component of a given locale string.
+ */
+ private static String parseLanguageFromLocaleString(String locale) {
+ final int idx = locale.indexOf('_');
+ if (idx < 0) {
+ return locale;
+ } else {
+ return locale.substring(0, idx);
+ }
}
+ /**
+ * Get a spell checker session for the specified spell checker
+ * @param locale the locale for the spell checker. If {@code locale} is null and
+ * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
+ * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true,
+ * the locale specified in Settings will be returned only when it is same as {@code locale}.
+ * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code locale} is
+ * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
+ * selected.
+ * @param listener a spell checker session lister for getting results from a spell checker.
+ * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
+ * languages in settings will be returned.
+ * @return the spell checker session of the spell checker
+ */
public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale,
SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
- return null;
+ if (listener == null) {
+ throw new NullPointerException();
+ }
+ if (!referToSpellCheckerLanguageSettings && locale == null) {
+ throw new IllegalArgumentException("Locale should not be null if you don't refer"
+ + " settings.");
+ }
+
+ if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) {
+ return null;
+ }
+
+ final SpellCheckerInfo sci;
+ try {
+ sci = mService.getCurrentSpellChecker(null);
+ } catch (RemoteException e) {
+ return null;
+ }
+ if (sci == null) {
+ return null;
+ }
+ SpellCheckerSubtype subtypeInUse = null;
+ if (referToSpellCheckerLanguageSettings) {
+ subtypeInUse = getCurrentSpellCheckerSubtype(true);
+ if (subtypeInUse == null) {
+ return null;
+ }
+ if (locale != null) {
+ final String subtypeLocale = subtypeInUse.getLocale();
+ final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale);
+ if (subtypeLanguage.length() < 2 || !locale.getLanguage().equals(subtypeLanguage)) {
+ return null;
+ }
+ }
+ } else {
+ final String localeStr = locale.toString();
+ for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+ final SpellCheckerSubtype subtype = sci.getSubtypeAt(i);
+ final String tempSubtypeLocale = subtype.getLocale();
+ final String tempSubtypeLanguage = parseLanguageFromLocaleString(tempSubtypeLocale);
+ if (tempSubtypeLocale.equals(localeStr)) {
+ subtypeInUse = subtype;
+ break;
+ } else if (tempSubtypeLanguage.length() >= 2 &&
+ locale.getLanguage().equals(tempSubtypeLanguage)) {
+ subtypeInUse = subtype;
+ }
+ }
+ }
+ if (subtypeInUse == null) {
+ return null;
+ }
+ final SpellCheckerSession session = new SpellCheckerSession(sci, mService, listener);
+ try {
+ mService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
+ session.getTextServicesSessionListener(),
+ session.getSpellCheckerSessionListener(), bundle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return session;
}
/**
* @hide
*/
public SpellCheckerInfo[] getEnabledSpellCheckers() {
- return EMPTY_SPELL_CHECKER_INFO;
+ try {
+ final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers();
+ if (DBG) {
+ Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null"));
+ }
+ return retval;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
* @hide
*/
public SpellCheckerInfo getCurrentSpellChecker() {
- return null;
+ try {
+ // Passing null as a locale for ICS
+ return mService.getCurrentSpellChecker(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -60,13 +215,22 @@ public final class TextServicesManager {
*/
public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
boolean allowImplicitlySelectedSubtype) {
- return null;
+ try {
+ // Passing null as a locale until we support multiple enabled spell checker subtypes.
+ return mService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
* @hide
*/
public boolean isSpellCheckerEnabled() {
- return false;
+ try {
+ return mService.isSpellCheckerEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}