diff options
| -rw-r--r-- | api/test-current.txt | 3 | ||||
| -rw-r--r-- | core/java/android/view/ViewDebug.java | 63 |
2 files changed, 65 insertions, 1 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index 1a7e4cb83c52..f775b5a3faa3 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2634,7 +2634,8 @@ package android.view { } public class ViewDebug { - method @Nullable public static AutoCloseable startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.function.Function<android.graphics.Picture,java.lang.Boolean>); + method @Deprecated @Nullable public static AutoCloseable startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.function.Function<android.graphics.Picture,java.lang.Boolean>); + method @Nullable public static AutoCloseable startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>); } public interface WindowManager extends android.view.ViewManager { diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 5afc07f35ba0..6f9ee4b554f3 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -846,9 +846,11 @@ public class ViewDebug { * Returns null if the capture stream cannot be started, such as if there's no * HardwareRenderer for the given view tree. * @hide + * @deprecated use {@link #startRenderingCommandsCapture(View, Executor, Callable)} instead. */ @TestApi @Nullable + @Deprecated public static AutoCloseable startRenderingCommandsCapture(View tree, Executor executor, Function<Picture, Boolean> callback) { final View.AttachInfo attachInfo = tree.mAttachInfo; @@ -866,6 +868,67 @@ public class ViewDebug { return null; } + /** + * Begins capturing the entire rendering commands for the view tree referenced by the given + * view. The view passed may be any View in the tree as long as it is attached. That is, + * {@link View#isAttachedToWindow()} must be true. + * + * Every time a frame is rendered the callback will be invoked on the given executor to + * provide an OutputStream to serialize to. As long as the callback returns a valid + * OutputStream the capturing will continue. The system will only invoke the callback at a rate + * that the callback & OutputStream is able to keep up with. That is, if it takes 48ms for the + * callback & serialization to complete and there is a 60fps animation running + * then the callback will only receive 33% of the frames produced. + * + * This method must be called on the same thread as the View tree. + * + * @param tree The View tree to capture the rendering commands. + * @param callback The callback to invoke on every frame produced. Should return an + * OutputStream to write the data to. Return null to cancel capture. The + * same stream may be returned each time as the serialized data contains + * start & end markers. The callback will not be invoked while a previous + * serialization is being performed, so if a single continuous stream is being + * used it is valid for the callback to write its own metadata to that stream + * in response to callback invocation. + * @param executor The executor to invoke the callback on. Recommend using a background thread + * to avoid stalling the UI thread. Must be an asynchronous invoke or an + * exception will be thrown. + * @return a closeable that can be used to stop capturing. May be invoked on any thread. Note + * that the callback may continue to receive another frame or two depending on thread timings. + * Returns null if the capture stream cannot be started, such as if there's no + * HardwareRenderer for the given view tree. + * @hide + */ + @TestApi + @Nullable + public static AutoCloseable startRenderingCommandsCapture(View tree, Executor executor, + Callable<OutputStream> callback) { + final View.AttachInfo attachInfo = tree.mAttachInfo; + if (attachInfo == null) { + throw new IllegalArgumentException("Given view isn't attached"); + } + if (attachInfo.mHandler.getLooper() != Looper.myLooper()) { + throw new IllegalStateException("Called on the wrong thread." + + " Must be called on the thread that owns the given View"); + } + final HardwareRenderer renderer = attachInfo.mThreadedRenderer; + if (renderer != null) { + return new PictureCallbackHandler(renderer, (picture -> { + try { + OutputStream stream = callback.call(); + if (stream != null) { + picture.writeToStream(stream); + return true; + } + } catch (Exception ex) { + // fall through + } + return false; + }), executor); + } + return null; + } + private static void capture(View root, final OutputStream clientStream, String parameter) throws IOException { |