Skip to content

Commit

Permalink
Promote captureToBitmap and related APIs to stable
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 628167988
  • Loading branch information
brettchabot authored and copybara-androidxtest committed Apr 25, 2024
1 parent 3a23fe9 commit f3182f5
Show file tree
Hide file tree
Showing 11 changed files with 58 additions and 28 deletions.
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
* Make suspend function versions of ViewCapture/WindowCapture/DeviceCapture APIs,
and rename existing methods as *Async variants that return ListenableFutures
* Make Bitmap.writeToTestStorage use the registered PlatformTestStorage instead of hardcoding TestStorage
* Add *Async variants of capture*ToBitmap methods

* Remove ExperimentalTestApi/RequiresOptIn restrictions from captureToBitmap and takeScreenshot APIs

**Breaking API Changes**

Expand Down
17 changes: 17 additions & 0 deletions core/java/androidx/test/core/api/current_internal.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
// 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 @@ -7,3 +16,11 @@ 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: 15 additions & 0 deletions core/java/androidx/test/core/api/current_public.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ 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 @@ -53,6 +57,7 @@ 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 @@ -104,5 +109,15 @@ 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: 3 additions & 4 deletions core/java/androidx/test/core/app/DeviceCapture.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ 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 @@ -46,8 +45,10 @@ 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
*/
@ExperimentalTestApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun canTakeScreenshot(): Boolean =
getInstrumentation().uiAutomation != null && Looper.myLooper() != Looper.getMainLooper()

Expand All @@ -72,7 +73,6 @@ 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,7 +91,6 @@ 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: 0 additions & 2 deletions core/java/androidx/test/core/graphics/BitmapStorageExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ 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 @@ -31,7 +30,6 @@ 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: 4 additions & 8 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,10 +79,7 @@ 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 @@ -110,7 +107,6 @@ 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 @@ -119,10 +115,10 @@ fun View.captureToBitmapAsync(rect: Rect? = null): ListenableFuture<Bitmap> {
* Trigger a redraw of the given view.
*
* Should only be called on UI thread.
*
* @hide
*/
// TODO(b/316921934): uncomment once @ExperimentalTestApi is removed
// @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@ExperimentalTestApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
suspend fun View.forceRedraw() {
checkState(handler.looper.isCurrentThread, "Must be called from view's handler thread")
if (!getControlledLooper().areDrawCallbacksSupported()) {
Expand Down
5 changes: 0 additions & 5 deletions core/java/androidx/test/core/view/WindowCapture.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ 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 @@ -49,10 +48,7 @@ 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 @@ -69,7 +65,6 @@ 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: 2 additions & 1 deletion espresso/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ The following artifacts were released:
**API Changes**

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


**Breaking API Changes**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ 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 @@ -14,7 +13,6 @@ 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,7 +30,6 @@
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 @@ -571,7 +570,6 @@ public static ViewAction repeatedlyUntil(
new RepeatActionUntilViewState(action, desiredStateMatcher, maxAttempts));
}

@ExperimentalTestApi
public interface BitmapReceiver {
void onBitmapCaptured(Bitmap bitmap);
}
Expand All @@ -585,7 +583,6 @@ public interface BitmapReceiver {
* thread.
* @return the ViewAction
*/
@ExperimentalTestApi
public static ViewAction captureToBitmap(BitmapReceiver bitmapReceiver) {
return new CaptureToBitmapAction(bitmapReceiver);
}
Expand Down
14 changes: 14 additions & 0 deletions espresso/core/java/androidx/test/espresso/api/current_public.txt
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,15 @@ 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 @@ -419,6 +428,7 @@ 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 @@ -453,6 +463,10 @@ 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

0 comments on commit f3182f5

Please sign in to comment.