Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promote captureToBitmap and related APIs to stable #2206

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
**API Changes**

* Added ApplicationInfoBuilder.setFlags(int)
* Make suspend function versions of ViewCapture/WindowCapture/DeviceCapture APIs,
and rename existing methods as *Async variants that return ListenableFutures
* Make suspend function versions of ViewCapture/WindowCapture/DeviceCapture APIs
* Make Bitmap.writeToTestStorage use the registered PlatformTestStorage instead of hardcoding TestStorage
* Remove ExperimentalTestApi/RequiresOptIn restrictions from captureToBitmap and takeScreenshot APIs
* Add *Async variants of capture*ToBitmap methods


**Breaking API Changes**

Expand Down
17 changes: 0 additions & 17 deletions core/java/androidx/test/core/api/current_internal.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
// Signature format: 3.0
package androidx.test.core.app {

public final class DeviceCapture {
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean canTakeScreenshot();
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static android.graphics.Bitmap takeScreenshotNoSync() throws java.lang.RuntimeException;
}

}

package androidx.test.core.graphics {

public final class BitmapStorage {
Expand All @@ -16,11 +7,3 @@ package androidx.test.core.graphics {

}

package androidx.test.core.view {

public final class ViewCapture {
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static suspend Object? forceRedraw(android.view.View, kotlin.coroutines.Continuation<? super kotlin.Unit>);
}

}

15 changes: 0 additions & 15 deletions core/java/androidx/test/core/api/current_public.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ package androidx.test.core.app {
method public static <T extends android.content.Context> T! getApplicationContext();
}

public final class DeviceCapture {
method @kotlin.jvm.Throws(exceptionClasses=RuntimeException::class) public static android.graphics.Bitmap takeScreenshot() throws java.lang.RuntimeException;
}

}

package androidx.test.core.content.pm {
Expand Down Expand Up @@ -57,7 +53,6 @@ package androidx.test.core.content.pm {
package androidx.test.core.graphics {

public final class BitmapStorage {
method @kotlin.jvm.Throws(exceptionClasses=IOException::class) public static void writeToTestStorage(android.graphics.Bitmap, String name) throws java.io.IOException;
}

}
Expand Down Expand Up @@ -109,15 +104,5 @@ package androidx.test.core.view {
method public androidx.test.core.view.PointerPropertiesBuilder! setToolType(int);
}

public final class ViewCapture {
method public static suspend Object? captureToBitmap(android.view.View, android.graphics.Rect? rect = null, kotlin.coroutines.Continuation<? super android.graphics.Bitmap>);
method public static com.google.common.util.concurrent.ListenableFuture<android.graphics.Bitmap> captureToBitmapAsync(android.view.View, android.graphics.Rect? rect = null);
}

public final class WindowCapture {
method public static suspend Object? captureRegionToBitmap(android.view.Window, android.graphics.Rect? boundsInWindow = null, kotlin.coroutines.Continuation<? super android.graphics.Bitmap>);
method public static com.google.common.util.concurrent.ListenableFuture<android.graphics.Bitmap> captureRegionToBitmapAsync(android.view.Window, android.graphics.Rect? boundsInWindow = null);
}

}

7 changes: 4 additions & 3 deletions core/java/androidx/test/core/app/DeviceCapture.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import android.os.Looper
import android.util.Log
import android.view.Choreographer
import androidx.annotation.RestrictTo
import androidx.test.annotation.ExperimentalTestApi
import androidx.test.core.internal.os.HandlerExecutor
import androidx.test.core.view.forceRedraw
import androidx.test.internal.util.Checks
Expand All @@ -45,10 +46,8 @@ import kotlinx.coroutines.withTimeout
* this method returns false then attempting to take a screenshot will fail. Note that taking a
* screenshot may still fail if this method returns true, for example if the call to [UiAutomation]
* fails.
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@ExperimentalTestApi
fun canTakeScreenshot(): Boolean =
getInstrumentation().uiAutomation != null && Looper.myLooper() != Looper.getMainLooper()

Expand All @@ -73,6 +72,7 @@ fun canTakeScreenshot(): Boolean =
* @throws [IllegalStateException] if called on the main thread. This is a limitation of connecting
* to UiAutomation, [RuntimeException] if UiAutomation fails to take the screenshot
*/
@ExperimentalTestApi
@Suppress("FutureReturnValueIgnored")
@Throws(RuntimeException::class)
fun takeScreenshot(): Bitmap {
Expand All @@ -91,6 +91,7 @@ fun takeScreenshot(): Bitmap {
* to UiAutomation, [RuntimeException] if UiAutomation fails to take the screenshot
* @hide
*/
@ExperimentalTestApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Suppress("FutureReturnValueIgnored")
@Throws(RuntimeException::class)
Expand Down
2 changes: 2 additions & 0 deletions core/java/androidx/test/core/graphics/BitmapStorageExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package androidx.test.core.graphics

import android.graphics.Bitmap
import androidx.annotation.RestrictTo
import androidx.test.annotation.ExperimentalTestApi
import androidx.test.platform.io.PlatformTestStorage
import androidx.test.platform.io.PlatformTestStorageRegistry
import java.io.IOException
Expand All @@ -30,6 +31,7 @@ import java.io.IOException
* @param name a descriptive base name for the resulting file. '.png' will be appended to this name.
* @throws IOException if bitmap could not be compressed or written to ds
*/
@ExperimentalTestApi
@Throws(IOException::class)
fun Bitmap.writeToTestStorage(name: String) {
writeToTestStorage(PlatformTestStorageRegistry.getInstance(), name)
Expand Down
12 changes: 8 additions & 4 deletions core/java/androidx/test/core/view/ViewCapture.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import android.view.View
import android.view.ViewTreeObserver.OnDrawListener
import android.view.WindowManager
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
import androidx.concurrent.futures.SuspendToFutureAdapter
import androidx.test.annotation.ExperimentalTestApi
import androidx.test.core.internal.os.HandlerExecutor
import androidx.test.internal.platform.ServiceLoaderWrapper
import androidx.test.internal.platform.os.ControlledLooper
Expand Down Expand Up @@ -79,7 +79,10 @@ import kotlinx.coroutines.suspendCancellableCoroutine
* The resulting image is captured after forcing the View to redraw, and waiting for the draw to
* operation complete. This is done as a means to improve the stability of the resulting image -
* especially in cases where hardware rendering drawing is off initially.
*
* This API is currently experimental and subject to change or removal.
*/
@ExperimentalTestApi
suspend fun View.captureToBitmap(rect: Rect? = null): Bitmap {
checkState(isAttachedToWindow, "View must be attached to a window")
checkState(
Expand Down Expand Up @@ -107,6 +110,7 @@ private fun getControlledLooper(): ControlledLooper {
}

/** A ListenableFuture variant of captureToBitmap intended for use from Java. */
@ExperimentalTestApi
fun View.captureToBitmapAsync(rect: Rect? = null): ListenableFuture<Bitmap> {
return SuspendToFutureAdapter.launchFuture(Dispatchers.Main) { captureToBitmap(rect) }
}
Expand All @@ -115,10 +119,10 @@ fun View.captureToBitmapAsync(rect: Rect? = null): ListenableFuture<Bitmap> {
* Trigger a redraw of the given view.
*
* Should only be called on UI thread.
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
// TODO(b/316921934): uncomment once @ExperimentalTestApi is removed
// @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@ExperimentalTestApi
suspend fun View.forceRedraw() {
checkState(handler.looper.isCurrentThread, "Must be called from view's handler thread")
if (!getControlledLooper().areDrawCallbacksSupported()) {
Expand Down
5 changes: 5 additions & 0 deletions core/java/androidx/test/core/view/WindowCapture.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import android.view.PixelCopy
import android.view.Window
import androidx.annotation.RequiresApi
import androidx.concurrent.futures.SuspendToFutureAdapter
import androidx.test.annotation.ExperimentalTestApi
import androidx.test.platform.graphics.HardwareRendererCompat
import com.google.common.util.concurrent.ListenableFuture
import kotlin.coroutines.resumeWithException
Expand All @@ -48,7 +49,10 @@ import kotlinx.coroutines.suspendCancellableCoroutine
* The resulting image is captured after forcing the View to redraw, and waiting for the draw to
* operation complete. This is done as a means to improve the stability of the resulting image -
* especially in cases where hardware rendering drawing is off initially.
*
* This API is currently experimental and subject to change or removal.
*/
@ExperimentalTestApi
suspend fun Window.captureRegionToBitmap(boundsInWindow: Rect? = null): Bitmap {
var bitmap: Bitmap? = null

Expand All @@ -65,6 +69,7 @@ suspend fun Window.captureRegionToBitmap(boundsInWindow: Rect? = null): Bitmap {
}

/** A ListenableFuture variant of captureRegionToBitmap intended for use from Java. */
@ExperimentalTestApi
fun Window.captureRegionToBitmapAsync(boundsInWindow: Rect? = null): ListenableFuture<Bitmap> {
return SuspendToFutureAdapter.launchFuture(Dispatchers.Main) {
captureRegionToBitmap(boundsInWindow)
Expand Down
3 changes: 1 addition & 2 deletions espresso/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ The following artifacts were released:
**API Changes**

* Adapt to ViewCapture API changes
* Delete ViewInteraction.captureToBitmap in favor of ViewActions.captureToBitmap,
and promote to a stable API from ExperimentalTestApi
* Delete ViewInteraction.captureToBitmap in favor of ViewActions.captureToBitmap


**Breaking API Changes**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package androidx.test.espresso.action
import android.os.Handler
import android.os.Looper
import android.view.View
import androidx.test.annotation.ExperimentalTestApi
import androidx.test.core.internal.os.HandlerExecutor
import androidx.test.core.view.captureToBitmapAsync
import androidx.test.espresso.IdlingRegistry
Expand All @@ -13,6 +14,7 @@ import java.util.concurrent.TimeUnit
import org.hamcrest.Matcher
import org.hamcrest.Matchers.any

@ExperimentalTestApi
class CaptureToBitmapAction(val bitmapReceiver: ViewActions.BitmapReceiver) : ViewAction {
override fun getConstraints(): Matcher<View> {
return any(View::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.test.annotation.ExperimentalTestApi;
import androidx.test.espresso.PerformException;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
Expand Down Expand Up @@ -570,6 +571,7 @@ public static ViewAction repeatedlyUntil(
new RepeatActionUntilViewState(action, desiredStateMatcher, maxAttempts));
}

@ExperimentalTestApi
public interface BitmapReceiver {
void onBitmapCaptured(Bitmap bitmap);
}
Expand All @@ -583,6 +585,7 @@ public interface BitmapReceiver {
* thread.
* @return the ViewAction
*/
@ExperimentalTestApi
public static ViewAction captureToBitmap(BitmapReceiver bitmapReceiver) {
return new CaptureToBitmapAction(bitmapReceiver);
}
Expand Down
14 changes: 0 additions & 14 deletions espresso/core/java/androidx/test/espresso/api/current_public.txt
Original file line number Diff line number Diff line change
Expand Up @@ -233,15 +233,6 @@ package androidx.test.espresso.action {
method public static androidx.test.espresso.action.AdapterViewProtocol! standardProtocol();
}

public final class CaptureToBitmapAction implements androidx.test.espresso.ViewAction {
ctor public CaptureToBitmapAction(androidx.test.espresso.action.ViewActions.BitmapReceiver bitmapReceiver);
method public androidx.test.espresso.action.ViewActions.BitmapReceiver getBitmapReceiver();
method public org.hamcrest.Matcher<android.view.View> getConstraints();
method public String getDescription();
method public void perform(androidx.test.espresso.UiController uiController, android.view.View view);
property public final androidx.test.espresso.action.ViewActions.BitmapReceiver bitmapReceiver;
}

public final class CloseKeyboardAction implements androidx.test.espresso.ViewAction {
ctor @androidx.test.espresso.remote.annotation.RemoteMsgConstructor public CloseKeyboardAction();
method public org.hamcrest.Matcher<android.view.View!>! getConstraints();
Expand Down Expand Up @@ -430,7 +421,6 @@ package androidx.test.espresso.action {
@com.google.errorprone.annotations.CheckReturnValue public final class ViewActions {
method public static androidx.test.espresso.ViewAction! actionWithAssertions(androidx.test.espresso.ViewAction!);
method public static void addGlobalAssertion(String!, androidx.test.espresso.ViewAssertion!);
method public static androidx.test.espresso.ViewAction! captureToBitmap(androidx.test.espresso.action.ViewActions.BitmapReceiver!);
method public static void clearGlobalAssertions();
method public static androidx.test.espresso.ViewAction! clearText();
method public static androidx.test.espresso.ViewAction! click();
Expand Down Expand Up @@ -465,10 +455,6 @@ package androidx.test.espresso.action {
method public static androidx.test.espresso.ViewAction! typeTextIntoFocusedView(String!);
}

public static interface ViewActions.BitmapReceiver {
method public void onBitmapCaptured(android.graphics.Bitmap!);
}

}

package androidx.test.espresso.assertion {
Expand Down
Loading