diff options
author | Jason Monk <jmonk@google.com> | 2017-10-19 09:30:56 -0400 |
---|---|---|
committer | Jason Monk <jmonk@google.com> | 2017-10-19 09:30:56 -0400 |
commit | d439404c9988df6001e4ff8bce31537e2692660e (patch) | |
tree | b1462a7177b8a2791140964761eb49d173cdc878 /android/view | |
parent | 93b7ee4fce01df52a6607f0b1965cbafdfeaf1a6 (diff) | |
download | android-28-d439404c9988df6001e4ff8bce31537e2692660e.tar.gz |
Import Android SDK Platform P [4402356]
/google/data/ro/projects/android/fetch_artifact \
--bid 4386628 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4402356.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: Ie49e24e1f4ae9dc96306111e953d3db1e1495b53
Diffstat (limited to 'android/view')
-rw-r--r-- | android/view/SurfaceControl.java | 538 | ||||
-rw-r--r-- | android/view/SurfaceView.java | 1135 | ||||
-rw-r--r-- | android/view/View.java | 262 | ||||
-rw-r--r-- | android/view/ViewDebug.java | 167 | ||||
-rw-r--r-- | android/view/ViewGroup.java | 98 | ||||
-rw-r--r-- | android/view/ViewRootImpl.java | 758 | ||||
-rw-r--r-- | android/view/ViewStructure.java | 24 | ||||
-rw-r--r-- | android/view/WindowManagerInternal.java | 4 | ||||
-rw-r--r-- | android/view/WindowManagerPolicy.java | 7 | ||||
-rw-r--r-- | android/view/accessibility/AccessibilityCache.java | 2 | ||||
-rw-r--r-- | android/view/accessibility/AccessibilityManager.java | 916 | ||||
-rw-r--r-- | android/view/autofill/AutofillManager.java | 149 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassifier.java | 8 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassifierConstants.java | 90 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassifierImpl.java | 14 | ||||
-rw-r--r-- | android/view/textservice/TextServicesManager.java | 200 |
16 files changed, 1607 insertions, 2765 deletions
diff --git a/android/view/SurfaceControl.java b/android/view/SurfaceControl.java index 31daefff..54825895 100644 --- a/android/view/SurfaceControl.java +++ b/android/view/SurfaceControl.java @@ -21,15 +21,22 @@ 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 @@ -54,25 +61,34 @@ public class SurfaceControl { Rect sourceCrop, int width, int height, int minLayer, int maxLayer, boolean allLayers, boolean useIdentityTransform); - 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, + 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, float dtdy, float dsdy); - 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 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 boolean nativeClearContentFrameStats(long nativeObject); private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats); @@ -82,15 +98,16 @@ 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( + private static native void nativeSetDisplaySurface(long transactionObj, IBinder displayToken, long nativeSurfaceObject); - private static native void nativeSetDisplayLayerStack( + private static native void nativeSetDisplayLayerStack(long transactionObj, IBinder displayToken, int layerStack); - private static native void nativeSetDisplayProjection( + private static native void nativeSetDisplayProjection(long transactionObj, 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(IBinder displayToken, int width, int height); + private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken, + int width, int height); private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs( IBinder displayToken); private static native int nativeGetActiveConfig(IBinder displayToken); @@ -101,16 +118,17 @@ public class SurfaceControl { int colorMode); private static native void nativeSetDisplayPowerMode( IBinder displayToken, int mode); - private static native void nativeDeferTransactionUntil(long nativeObject, + private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject, IBinder handle, long frame); - private static native void nativeDeferTransactionUntilSurface(long nativeObject, + private static native void nativeDeferTransactionUntilSurface(long transactionObj, + long nativeObject, long surfaceObject, long frame); - private static native void nativeReparentChildren(long nativeObject, + private static native void nativeReparentChildren(long transactionObj, long nativeObject, IBinder handle); - private static native void nativeReparent(long nativeObject, + private static native void nativeReparent(long transactionObj, long nativeObject, IBinder parentHandle); - private static native void nativeSeverChildren(long nativeObject); - private static native void nativeSetOverrideScalingMode(long nativeObject, + private static native void nativeSeverChildren(long transactionObj, long nativeObject); + private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject, int scalingMode); private static native IBinder nativeGetHandle(long nativeObject); private static native boolean nativeGetTransformToDisplayInverse(long nativeObject); @@ -122,6 +140,9 @@ 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) */ /** @@ -377,11 +398,6 @@ 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. @@ -429,102 +445,141 @@ public class SurfaceControl { /** start a transaction */ public static void openTransaction() { - nativeOpenTransaction(); + 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); + } } /** end a transaction */ public static void closeTransaction() { - nativeCloseTransaction(false); + closeTransaction(false); } public static void closeTransactionSync() { - nativeCloseTransaction(true); + closeTransaction(true); } public void deferTransactionUntil(IBinder handle, long frame) { if (frame > 0) { - nativeDeferTransactionUntil(mNativeObject, handle, frame); + synchronized(SurfaceControl.class) { + sGlobalTransaction.deferTransactionUntil(this, handle, frame); + } } } public void deferTransactionUntil(Surface barrier, long frame) { if (frame > 0) { - nativeDeferTransactionUntilSurface(mNativeObject, barrier.mNativeObject, frame); + synchronized(SurfaceControl.class) { + sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame); + } } } public void reparentChildren(IBinder newParentHandle) { - nativeReparentChildren(mNativeObject, newParentHandle); + synchronized(SurfaceControl.class) { + sGlobalTransaction.reparentChildren(this, newParentHandle); + } } - /** Re-parents this layer to a new parent. */ public void reparent(IBinder newParentHandle) { - nativeReparent(mNativeObject, newParentHandle); + synchronized(SurfaceControl.class) { + sGlobalTransaction.reparent(this, newParentHandle); + } } public void detachChildren() { - nativeSeverChildren(mNativeObject); + synchronized(SurfaceControl.class) { + sGlobalTransaction.detachChildren(this); + } } public void setOverrideScalingMode(int scalingMode) { checkNotReleased(); - nativeSetOverrideScalingMode(mNativeObject, scalingMode); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setOverrideScalingMode(this, scalingMode); + } } public IBinder getHandle() { return nativeGetHandle(mNativeObject); } - /** flag the transaction as an animation */ public static void setAnimationTransaction() { - nativeSetAnimationTransaction(); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setAnimationTransaction(); + } } public void setLayer(int zorder) { checkNotReleased(); - nativeSetLayer(mNativeObject, zorder); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setLayer(this, zorder); + } } public void setRelativeLayer(IBinder relativeTo, int zorder) { checkNotReleased(); - nativeSetRelativeLayer(mNativeObject, relativeTo, zorder); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setRelativeLayer(this, relativeTo, zorder); + } } public void setPosition(float x, float y) { checkNotReleased(); - nativeSetPosition(mNativeObject, x, y); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setPosition(this, 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(); - nativeSetGeometryAppliesWithResize(mNativeObject); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setGeometryAppliesWithResize(this); + } } public void setSize(int w, int h) { checkNotReleased(); - nativeSetSize(mNativeObject, w, h); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setSize(this, w, h); + } } public void hide() { checkNotReleased(); - nativeSetFlags(mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN); + synchronized(SurfaceControl.class) { + sGlobalTransaction.hide(this); + } } public void show() { checkNotReleased(); - nativeSetFlags(mNativeObject, 0, SURFACE_HIDDEN); + synchronized(SurfaceControl.class) { + sGlobalTransaction.show(this); + } } public void setTransparentRegionHint(Region region) { checkNotReleased(); - nativeSetTransparentRegionHint(mNativeObject, region); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setTransparentRegionHint(this, region); + } } public boolean clearContentFrameStats() { @@ -545,80 +600,70 @@ 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(); - nativeSetAlpha(mNativeObject, alpha); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setAlpha(this, 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(); - nativeSetColor(mNativeObject, color); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setColor(this, color); + } } public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) { checkNotReleased(); - nativeSetMatrix(mNativeObject, dsdx, dtdx, dtdy, dsdy); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setMatrix(this, dsdx, dtdx, dtdy, dsdy); + } } public void setWindowCrop(Rect crop) { checkNotReleased(); - if (crop != null) { - nativeSetWindowCrop(mNativeObject, - crop.left, crop.top, crop.right, crop.bottom); - } else { - nativeSetWindowCrop(mNativeObject, 0, 0, 0, 0); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setWindowCrop(this, crop); } } public void setFinalCrop(Rect crop) { checkNotReleased(); - if (crop != null) { - nativeSetFinalCrop(mNativeObject, - crop.left, crop.top, crop.right, crop.bottom); - } else { - nativeSetFinalCrop(mNativeObject, 0, 0, 0, 0); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setFinalCrop(this, crop); } } public void setLayerStack(int layerStack) { checkNotReleased(); - nativeSetLayerStack(mNativeObject, layerStack); + synchronized(SurfaceControl.class) { + sGlobalTransaction.setLayerStack(this, 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(); - if (isOpaque) { - nativeSetFlags(mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE); - } else { - nativeSetFlags(mNativeObject, 0, SURFACE_OPAQUE); + + synchronized (SurfaceControl.class) { + sGlobalTransaction.setOpaque(this, isOpaque); } } - /** - * 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(); - if (isSecure) { - nativeSetFlags(mNativeObject, SECURE, SECURE); - } else { - nativeSetFlags(mNativeObject, 0, SECURE); + + synchronized (SurfaceControl.class) { + sGlobalTransaction.setSecure(this, isSecure); } } + @Override + public String toString() { + return "Surface(name=" + mName + ")/@0x" + + Integer.toHexString(System.identityHashCode(this)); + } + /* * set display parameters. * needs to be inside open/closeTransaction block @@ -741,50 +786,28 @@ public class SurfaceControl { public static void 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"); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setDisplayProjection(displayToken, orientation, + layerStackRect, displayRect); } - 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) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setDisplayLayerStack(displayToken, layerStack); } - nativeSetDisplayLayerStack(displayToken, layerStack); } public static void setDisplaySurface(IBinder displayToken, Surface 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); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setDisplaySurface(displayToken, surface); } } public static void 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"); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setDisplaySize(displayToken, width, height); } - - nativeSetDisplaySize(displayToken, width, height); } public static Display.HdrCapabilities getHdrCapabilities(IBinder displayToken) { @@ -946,4 +969,261 @@ 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 462dad3f..ebb2af45 100644 --- a/android/view/SurfaceView.java +++ b/android/view/SurfaceView.java @@ -16,1208 +16,115 @@ package android.view; -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 com.android.layoutlib.bridge.MockView; 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; /** - * 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. + * 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. * - * <p>Access to the underlying surface is provided via the SurfaceHolder interface, - * which can be retrieved by calling {@link #getHolder}. + * TODO: generate automatically. * - * <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 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 class SurfaceView extends MockView { 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 defStyleAttr) { - this(context, attrs, defStyleAttr, 0); + public SurfaceView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); } 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) { - 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; + return false; } - @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; - } - } - - private void updateOpaqueFlag() { - if (!PixelFormat.formatHasAlpha(mRequestedFormat)) { - mSurfaceFlags |= SurfaceControl.OPAQUE; - } else { - mSurfaceFlags &= ~SurfaceControl.OPAQUE; - } } - 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; + public SurfaceHolder getHolder() { + return mSurfaceHolder; } - private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { - private static final String LOG_TAG = "SurfaceHolder"; + private SurfaceHolder mSurfaceHolder = new SurfaceHolder() { @Override public boolean isCreating() { - return mIsCreating; + return false; } @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 - @Deprecated - public void setType(int type) { } + 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 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 inOutDirty) { - return internalLockCanvas(inOutDirty, false); + return null; } @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(); - + public Canvas lockCanvas(Rect dirty) { 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 mSurface; + return null; } @Override public Rect getSurfaceFrame() { - return mSurfaceFrame; + return null; } }; - - 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 b6be2961..c043dcac 100644 --- a/android/view/View.java +++ b/android/view/View.java @@ -1448,17 +1448,59 @@ 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 = { @@ -2300,9 +2342,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static final int PFLAG_HOVERED = 0x10000000; /** - * no longer needed, should be reused + * Flag set by {@link AutofillManager} if it needs to be notified when this view is clicked. */ - private static final int PFLAG_DOES_NOTHING_REUSE_PLEASE = 0x20000000; + private static final int PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK = 0x20000000; /** {@hide} */ static final int PFLAG_ACTIVATED = 0x40000000; @@ -6355,6 +6397,42 @@ 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 @@ -6363,7 +6441,14 @@ 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) { @@ -8907,7 +8992,21 @@ 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; @@ -8925,7 +9024,21 @@ 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); } @@ -11433,7 +11546,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: { if (isClickable()) { - performClick(); + performClickInternal(); return true; } } break; @@ -12545,7 +12658,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // This is a tap, so remove the longpress check removeLongPressCallback(); if (!event.isCanceled()) { - return performClick(); + return performClickInternal(); } } } @@ -13117,7 +13230,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { - performClick(); + performClickInternal(); } } } @@ -18103,7 +18216,21 @@ 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); @@ -18116,7 +18243,21 @@ 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; @@ -18130,10 +18271,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @SuppressWarnings({"UnusedDeclaration"}) public void outputDirtyFlags(String indent, boolean clear, int clearMask) { - 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) + ")"); + 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) + ")"); if (clear) { mPrivateFlags &= clearMask; } @@ -18257,7 +18399,21 @@ 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); } @@ -18288,7 +18444,21 @@ 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; @@ -18308,7 +18478,21 @@ 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(); @@ -18330,7 +18514,21 @@ 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; @@ -18342,7 +18540,21 @@ 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; @@ -18352,7 +18564,21 @@ 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); } @@ -18379,7 +18605,21 @@ 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)) { @@ -19812,7 +20052,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean changed = false; if (DBG) { - Log.d("View", this + " View.setFrame(" + left + "," + top + "," + Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + "," + right + "," + bottom + ")"); } @@ -24858,7 +25098,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private final class PerformClick implements Runnable { @Override public void run() { - performClick(); + performClickInternal(); } } diff --git a/android/view/ViewDebug.java b/android/view/ViewDebug.java index 3426485e..afa94131 100644 --- a/android/view/ViewDebug.java +++ b/android/view/ViewDebug.java @@ -528,84 +528,23 @@ public class ViewDebug { /** @hide */ public static void profileViewAndChildren(final View view, BufferedWriter out) throws IOException { - profileViewAndChildren(view, out, true); + RenderNode node = RenderNode.create("ViewDebug", null); + profileViewAndChildren(view, node, out, true); + node.destroy(); } - private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root) - throws IOException { - + private static void profileViewAndChildren(View view, RenderNode node, BufferedWriter out, + boolean root) throws IOException { long durationMeasure = (root || (view.mPrivateFlags & View.PFLAG_MEASURED_DIMENSION_SET) != 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; + ? profileViewMeasure(view) : 0; long durationLayout = (root || (view.mPrivateFlags & View.PFLAG_LAYOUT_REQUIRED) != 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; + ? profileViewLayout(view) : 0; long durationDraw = (root || !view.willNotDraw() || (view.mPrivateFlags & View.PFLAG_DRAWN) != 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 - }; - } + ? profileViewDraw(view, node) : 0; - 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)); @@ -616,34 +555,86 @@ public class ViewDebug { ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); for (int i = 0; i < count; i++) { - profileViewAndChildren(group.getChildAt(i), out, false); + 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(); } } } - interface ViewOperation<T> { - T[] pre(); - void run(T... data); - void post(T... data); + interface ViewOperation { + default void pre() {} + + void run(); } - private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) { + private static long profileViewOperation(View view, final ViewOperation operation) { final CountDownLatch latch = new CountDownLatch(1); final long[] duration = new long[1]; - 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(); - } + view.post(() -> { + try { + operation.pre(); + long start = Debug.threadCpuTimeNanos(); + //noinspection unchecked + operation.run(); + duration[0] = Debug.threadCpuTimeNanos() - start; + } finally { + latch.countDown(); } }); diff --git a/android/view/ViewGroup.java b/android/view/ViewGroup.java index b2e5a163..929beaea 100644 --- a/android/view/ViewGroup.java +++ b/android/view/ViewGroup.java @@ -421,22 +421,78 @@ 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 @@ -3769,7 +3825,21 @@ 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; @@ -6331,7 +6401,21 @@ 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"), @@ -6352,7 +6436,21 @@ 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 71106ada..99438d87 100644 --- a/android/view/ViewRootImpl.java +++ b/android/view/ViewRootImpl.java @@ -72,6 +72,7 @@ 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; @@ -1668,8 +1669,6 @@ 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(); @@ -2827,7 +2826,7 @@ public final class ViewRootImpl implements ViewParent, try { mWindowDrawCountDown.await(); } catch (InterruptedException e) { - Log.e(mTag, "Window redraw count down interruped!"); + Log.e(mTag, "Window redraw count down interrupted!"); } mWindowDrawCountDown = null; } @@ -2897,8 +2896,6 @@ 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. @@ -3469,6 +3466,7 @@ public final class ViewRootImpl implements ViewParent, } void dispatchDetachedFromWindow() { + mFirstInputStage.onDetachedFromWindow(); if (mView != null && mView.mAttachInfo != null) { mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false); mView.dispatchDetachedFromWindow(); @@ -3731,266 +3729,273 @@ 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(); - 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) { + case MSG_INVALIDATE: + ((View) msg.obj).invalidate(); break; - } - } // fall through... - case MSG_RESIZED_REPORT: - if (mAdded) { + 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; - - 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); + 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; } + } // 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(); + 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(); + } - if (msg.what == MSG_RESIZED_REPORT) { - reportNextDraw(); + if (mView != null && framesChanged) { + forceLayout(mView); + } + requestLayout(); } - - if (mView != null && framesChanged) { - forceLayout(mView); + 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); } - 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); + 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; try { - if (!mWindowSession.outOfMemory(mWindow)) { - Slog.w(mTag, "No processes killed for memory; killing self"); - Process.killProcess(Process.myPid()); + 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) { } - } catch (RemoteException ex) { + // Retry in a bit. + sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), + 500); + return; } - // Retry in a bit. - sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500); - return; } } - } - 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); + mLastWasImTarget = WindowManager.LayoutParams + .mayUseInputMethod(mWindowAttributes.flags); - if (mAttachInfo.mTooltipHost != null) { - mAttachInfo.mTooltipHost.hideTooltip(); + 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); - // 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); + if (mAttachInfo.mTooltipHost != null) { + mAttachInfo.mTooltipHost.hideTooltip(); + } } - // 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 &= + + // 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; - mHasHadWindowFocus = true; - } else { - if (mPointerCapture) { - handlePointerCaptureChanged(false); + ((WindowManager.LayoutParams) mView.getLayoutParams()) + .softInputMode &= + ~WindowManager.LayoutParams + .SOFT_INPUT_IS_FORWARD_NAVIGATION; + mHasHadWindowFocus = true; + } else { + if (mPointerCapture) { + handlePointerCaptureChanged(false); + } } } - } - } 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(); - } + 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(); + } - // 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; } } } @@ -4203,6 +4208,18 @@ 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); @@ -4956,9 +4973,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(event); + mTrackball.cancel(); } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { - mJoystick.cancel(event); + mJoystick.cancel(); } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION) == InputDevice.SOURCE_TOUCH_NAVIGATION) { mTouchNavigation.cancel(event); @@ -4967,6 +4984,18 @@ 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(); + } } /** @@ -5079,7 +5108,7 @@ public final class ViewRootImpl implements ViewParent, } } - public void cancel(MotionEvent event) { + public void cancel() { mLastTime = Integer.MIN_VALUE; // If we reach this, we consumed a trackball event. @@ -5263,14 +5292,11 @@ 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 int mLastXDirection; - private int mLastYDirection; - private int mLastXKeyCode; - private int mLastYKeyCode; + private final JoystickAxesState mJoystickAxesState = new JoystickAxesState(); + private final SparseArray<KeyEvent> mDeviceKeyEvents = new SparseArray<>(); public SyntheticJoystickHandler() { super(true); @@ -5281,11 +5307,10 @@ 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); @@ -5297,97 +5322,176 @@ public final class ViewRootImpl implements ViewParent, public void process(MotionEvent event) { switch(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()); + case MotionEvent.ACTION_CANCEL: + cancel(); + break; + case MotionEvent.ACTION_MOVE: + update(event); + break; + default: + Log.w(mTag, "Unexpected action: " + event.getActionMasked()); } } - private void cancel(MotionEvent event) { + private void cancel() { removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT); removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT); - update(event, false); - } - - private void update(MotionEvent event, boolean synthesizeNewKeys) { + 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(); - final int metaState = event.getMetaState(); - final int deviceId = event.getDeviceId(); - final int source = event.getSource(); - - int xDirection = joystickAxisValueToDirection( + 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)); - if (xDirection == 0) { - xDirection = joystickAxisValueToDirection(event.getX()); - } - - int yDirection = joystickAxisValueToDirection( + mJoystickAxesState.updateStateForAxis(event, time, MotionEvent.AXIS_HAT_Y, event.getAxisValue(MotionEvent.AXIS_HAT_Y)); - if (yDirection == 0) { - yDirection = joystickAxisValueToDirection(event.getY()); - } + } + + 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); + + final int currentState; + if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Y) { + currentState = mAxisStatesStick[axisStateIndex]; + } else { + currentState = mAxisStatesHat[axisStateIndex]; + } - 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; + if (currentState == newState) { + return; } - mLastXDirection = xDirection; + final int metaState = event.getMetaState(); + final int deviceId = event.getDeviceId(); + final int source = event.getSource(); - 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 (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 (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; + 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; } + } - mLastYDirection = yDirection; + 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; + } - 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()); + 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 (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 joystickAxisValueToDirection(float value) { - if (value >= 0.5f) { - return 1; - } else if (value <= -0.5f) { - return -1; - } else { - return 0; + 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; + } } } } @@ -6108,7 +6212,6 @@ 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) { @@ -6129,7 +6232,6 @@ 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 f671c349..309366c6 100644 --- a/android/view/ViewStructure.java +++ b/android/view/ViewStructure.java @@ -365,6 +365,30 @@ 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 98f8dc8e..69cc1002 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 the stack with the input Id is currently visible. */ - public abstract boolean isStackVisible(int stackId); + /** Returns true if a stack in the windowing mode is currently visible. */ + public abstract boolean isStackVisible(int windowingMode); /** * @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 da72535d..137e551d 100644 --- a/android/view/WindowManagerPolicy.java +++ b/android/view/WindowManagerPolicy.java @@ -467,11 +467,8 @@ public interface WindowManagerPolicy { */ public boolean isDimming(); - /** - * @return the stack id this windows belongs to, or {@link StackId#INVALID_STACK_ID} if - * not attached to any stack. - */ - int getStackId(); + /** @return the current windowing mode of this window. */ + int getWindowingMode(); /** * 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 0f21c5c8..d7851171 100644 --- a/android/view/accessibility/AccessibilityCache.java +++ b/android/view/accessibility/AccessibilityCache.java @@ -329,8 +329,6 @@ 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 0b9bc576..11cb046a 100644 --- a/android/view/accessibility/AccessibilityManager.java +++ b/android/view/accessibility/AccessibilityManager.java @@ -16,152 +16,46 @@ 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, - * and provides facilities for querying the accessibility state of the system. - * Accessibility events are generated when something notable happens in the user interface, + * 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, * 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 - * {@link android.accessibilityservice.AccessibilityService}. + * {@code android.accessibilityservice.AccessibilityService}. * * @see AccessibilityEvent - * @see AccessibilityNodeInfo - * @see android.accessibilityservice.AccessibilityService - * @see Context#getSystemService - * @see Context#ACCESSIBILITY_SERVICE + * @see android.content.Context#getSystemService */ -@SystemService(Context.ACCESSIBILITY_SERVICE) +@SuppressWarnings("UnusedDeclaration") 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; - int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; + private static AccessibilityManager sInstance = new AccessibilityManager(null, null, 0); - 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 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}. + * Listener for the accessibility state. */ public interface AccessibilityStateChangeListener { /** - * Called when the accessibility enabled state changes. + * Called back on change in the accessibility state. * * @param enabled Whether accessibility is enabled. */ - void onAccessibilityStateChanged(boolean enabled); + public void onAccessibilityStateChanged(boolean enabled); } /** @@ -177,24 +71,7 @@ public final class AccessibilityManager { * * @param enabled Whether touch exploration is 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); + public void onTouchExplorationStateChanged(boolean enabled); } /** @@ -202,8 +79,6 @@ 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 { @@ -212,72 +87,26 @@ public final class AccessibilityManager { * * @param enabled Whether high text contrast is enabled. */ - void onHighTextContrastStateChanged(boolean enabled); + public void onHighTextContrastStateChanged(boolean enabled); } private final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() { - @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(); - } - - @Override - public void notifyServicesStateChanged() { - final ArrayMap<AccessibilityServicesStateChangeListener, Handler> listeners; - synchronized (mLock) { - if (mServicesStateChangeListeners.isEmpty()) { - return; + public void setState(int state) { } - listeners = new ArrayMap<>(mServicesStateChangeListeners); - } - 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)); - } - } + public void notifyServicesStateChanged() { + } - @Override - public void setRelevantEventTypes(int eventTypes) { - mRelevantEventTypes = eventTypes; - } - }; + public void setRelevantEventTypes(int 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; } @@ -285,68 +114,21 @@ 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; } /** - * @hide - */ - @VisibleForTesting - public Handler.Callback getCallback() { - return mCallback; - } - - /** - * Returns if the accessibility in the system is enabled. + * Returns if the {@link AccessibilityManager} is enabled. * - * @return True if accessibility is enabled, false otherwise. + * @return True if this {@link AccessibilityManager} is enabled, false otherwise. */ public boolean isEnabled() { - synchronized (mLock) { - IAccessibilityManager service = getServiceLocked(); - if (service == null) { - return false; - } - return mIsEnabled; - } + return false; } /** @@ -355,13 +137,7 @@ public final class AccessibilityManager { * @return True if touch exploration is enabled, false otherwise. */ public boolean isTouchExplorationEnabled() { - synchronized (mLock) { - IAccessibilityManager service = getServiceLocked(); - if (service == null) { - return false; - } - return mIsTouchExplorationEnabled; - } + return true; } /** @@ -371,169 +147,35 @@ 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() { - synchronized (mLock) { - IAccessibilityManager service = getServiceLocked(); - if (service == null) { - return false; - } - return mIsHighTextContrastEnabled; - } + return false; } /** * 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 feedback interruption from all accessibility services. + * Requests interruption of the accessibility feedback 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() { - 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); + return Collections.emptyList(); } - /** - * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services. - * - * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. - */ public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { - 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(); - } + return Collections.emptyList(); } /** @@ -548,48 +190,21 @@ 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) { - 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(); - } + return Collections.emptyList(); } /** * Registers an {@link AccessibilityStateChangeListener} for changes in - * the global accessibility state of the system. Equivalent to calling - * {@link #addAccessibilityStateChangeListener(AccessibilityStateChangeListener, Handler)} - * with a null handler. + * the global accessibility state of the system. * * @param listener The listener. - * @return Always returns {@code true}. + * @return True if successfully registered. */ public boolean addAccessibilityStateChangeListener( - @NonNull AccessibilityStateChangeListener listener) { - addAccessibilityStateChangeListener(listener, null); + AccessibilityStateChangeListener listener) { return true; } @@ -603,40 +218,22 @@ public final class AccessibilityManager { * for a callback on the process's main handler. */ public void addAccessibilityStateChangeListener( - @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) { - synchronized (mLock) { - mAccessibilityStateChangeListeners - .put(listener, (handler == null) ? mHandler : handler); - } - } + @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) {} - /** - * Unregisters an {@link AccessibilityStateChangeListener}. - * - * @param listener The listener. - * @return True if the listener was previously registered. - */ public boolean removeAccessibilityStateChangeListener( - @NonNull AccessibilityStateChangeListener listener) { - synchronized (mLock) { - int index = mAccessibilityStateChangeListeners.indexOfKey(listener); - mAccessibilityStateChangeListeners.remove(listener); - return (index >= 0); - } + AccessibilityStateChangeListener listener) { + return true; } /** * Registers a {@link TouchExplorationStateChangeListener} for changes in - * the global touch exploration state of the system. Equivalent to calling - * {@link #addTouchExplorationStateChangeListener(TouchExplorationStateChangeListener, Handler)} - * with a null handler. + * the global touch exploration state of the system. * * @param listener The listener. - * @return Always returns {@code true}. + * @return True if successfully registered. */ public boolean addTouchExplorationStateChangeListener( @NonNull TouchExplorationStateChangeListener listener) { - addTouchExplorationStateChangeListener(listener, null); return true; } @@ -650,103 +247,17 @@ public final class AccessibilityManager { * for a callback on the process's main handler. */ public void addTouchExplorationStateChangeListener( - @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) { - synchronized (mLock) { - mTouchExplorationStateChangeListeners - .put(listener, (handler == null) ? mHandler : handler); - } - } + @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) {} /** * Unregisters a {@link TouchExplorationStateChangeListener}. * * @param listener The listener. - * @return True if listener was previously registered. + * @return True if successfully unregistered. */ public boolean removeTouchExplorationStateChangeListener( @NonNull TouchExplorationStateChangeListener listener) { - 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); + return true; } /** @@ -758,12 +269,7 @@ public final class AccessibilityManager { * @hide */ public void addHighTextContrastStateChangeListener( - @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) { - synchronized (mLock) { - mHighTextContrastStateChangeListeners - .put(listener, (handler == null) ? mHandler : handler); - } - } + @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {} /** * Unregisters a {@link HighTextContrastChangeListener}. @@ -773,51 +279,7 @@ public final class AccessibilityManager { * @hide */ public void removeHighTextContrastStateChangeListener( - @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; - } - } + @NonNull HighTextContrastChangeListener listener) {} /** * Sets the current state and notifies listeners, if necessary. @@ -825,314 +287,14 @@ 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 4fb2a99a..e564fa34 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 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 + * 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 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user - * selects a dataset from the affordance, all views present in the dataset are autofilled, through + * selects a dataset from the UI, 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 a save UI affordance if the value of savable views have changed. If the user selects the + * shows an autofill save UI 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,6 +150,12 @@ 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} */ @@ -311,6 +317,14 @@ 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 { /** @@ -834,6 +848,46 @@ 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. * @@ -850,12 +904,15 @@ public final class AutofillManager { return; } synchronized (mLock) { - if (!mEnabled && !isActiveLocked()) { - return; - } + commitLocked(); + } + } - finishSessionLocked(); + private void commitLocked() { + if (!mEnabled && !isActiveLocked()) { + return; } + finishSessionLocked(); } /** @@ -874,12 +931,15 @@ public final class AutofillManager { return; } synchronized (mLock) { - if (!mEnabled && !isActiveLocked()) { - return; - } + cancelLocked(); + } + } - cancelSessionLocked(); + private void cancelLocked() { + if (!mEnabled && !isActiveLocked()) { + return; } + cancelSessionLocked(); } /** @hide */ @@ -937,7 +997,12 @@ public final class AutofillManager { } private AutofillClient getClientLocked() { - return mContext.getAutofillClient(); + final AutofillClient client = mContext.getAutofillClient(); + if (client == null && sDebug) { + Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context " + + mContext); + } + return client; } /** @hide */ @@ -959,6 +1024,10 @@ 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()); @@ -1038,6 +1107,7 @@ public final class AutofillManager { mState = STATE_UNKNOWN; mTrackedViews = null; mFillableIds = null; + mSaveTriggerId = null; } private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action, @@ -1289,12 +1359,15 @@ 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, @Nullable AutofillId[] fillableIds) { + boolean saveOnAllViewsInvisible, boolean saveOnFinish, + @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) { synchronized (mLock) { if (mEnabled && mSessionId == sessionId) { if (saveOnAllViewsInvisible) { @@ -1302,6 +1375,7 @@ public final class AutofillManager { } else { mTrackedViews = null; } + mSaveOnFinish = saveOnFinish; if (fillableIds != null) { if (mFillableIds == null) { mFillableIds = new ArraySet<>(fillableIds.length); @@ -1314,10 +1388,30 @@ 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) { @@ -1490,6 +1584,7 @@ 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); @@ -1504,6 +1599,8 @@ 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() { @@ -1752,7 +1849,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 affordance is shown / hidden. + * enable / disable such views when the autofill UI is shown / hidden. */ public abstract static class AutofillCallback { @@ -1762,26 +1859,26 @@ public final class AutofillManager { public @interface AutofillEventType {} /** - * The autofill input UI affordance associated with the view was shown. + * The autofill input UI associated with the view was shown. * - * <p>If the view provides its own auto-complete UI affordance and its currently shown, it + * <p>If the view provides its own auto-complete UI and its currently shown, it * should be hidden upon receiving this event. */ public static final int EVENT_INPUT_SHOWN = 1; /** - * The autofill input UI affordance associated with the view was hidden. + * The autofill input UI associated with the view was hidden. * - * <p>If the view provides its own auto-complete UI affordance that was hidden upon a + * <p>If the view provides its own auto-complete UI 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 affordance associated with the view isn't shown because + * The autofill input UI associated with the view isn't shown because * autofill is not available. * - * <p>If the view provides its own auto-complete UI affordance but was not displaying it + * <p>If the view provides its own auto-complete UI but was not displaying it * to avoid flickering, it could shown it upon receiving this event. */ public static final int EVENT_INPUT_UNAVAILABLE = 3; @@ -1883,12 +1980,12 @@ public final class AutofillManager { @Override public void setTrackedViews(int sessionId, AutofillId[] ids, - boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) { + boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds, + AutofillId saveTriggerId) { final AutofillManager afm = mAfm.get(); if (afm != null) { - afm.post(() -> - afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds) - ); + afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, + saveOnFinish, fillableIds, saveTriggerId)); } } diff --git a/android/view/textclassifier/TextClassifier.java b/android/view/textclassifier/TextClassifier.java index bb1e693f..c3601d9d 100644 --- a/android/view/textclassifier/TextClassifier.java +++ b/android/view/textclassifier/TextClassifier.java @@ -152,4 +152,12 @@ 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 new file mode 100644 index 00000000..51e6168e --- /dev/null +++ b/android/view/textclassifier/TextClassifierConstants.java @@ -0,0 +1,90 @@ +/* + * 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 2aa81a2c..ef087472 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,6 +47,7 @@ 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; @@ -91,6 +92,8 @@ 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); } @@ -189,6 +192,15 @@ 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 f368c74a..8e1f2183 100644 --- a/android/view/textservice/TextServicesManager.java +++ b/android/view/textservice/TextServicesManager.java @@ -1,213 +1,58 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2016 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * 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; /** - * 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> - * + * A stub class of TextServicesManager for Layout-Lib. */ -@SystemService(Context.TEXT_SERVICES_MANAGER_SERVICE) public final class TextServicesManager { - 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)); - } + private static final TextServicesManager sInstance = new TextServicesManager(); + private static final SpellCheckerInfo[] EMPTY_SPELL_CHECKER_INFO = new SpellCheckerInfo[0]; /** * Retrieve the global TextServicesManager instance, creating it if it doesn't already exist. * @hide */ public static TextServicesManager getInstance() { - 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); - } + return sInstance; } - /** - * 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) { - 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; + return null; } /** * @hide */ public SpellCheckerInfo[] getEnabledSpellCheckers() { - 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(); - } + return EMPTY_SPELL_CHECKER_INFO; } /** * @hide */ public SpellCheckerInfo getCurrentSpellChecker() { - try { - // Passing null as a locale for ICS - return mService.getCurrentSpellChecker(null); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return null; } /** @@ -215,22 +60,13 @@ public final class TextServicesManager { */ public SpellCheckerSubtype getCurrentSpellCheckerSubtype( boolean allowImplicitlySelectedSubtype) { - 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(); - } + return null; } /** * @hide */ public boolean isSpellCheckerEnabled() { - try { - return mService.isSpellCheckerEnabled(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return false; } } |