Merge "ConstraintTrackingWorkerTest has large timeouts." into androidx-master-dev
diff --git a/car/core/res/drawable/car_card_ripple_background.xml b/car/core/res/drawable/car_card_ripple_background.xml
index ca20e0f..51e3ef9 100644
--- a/car/core/res/drawable/car_card_ripple_background.xml
+++ b/car/core/res/drawable/car_card_ripple_background.xml
@@ -17,7 +17,9 @@
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:color="@color/car_card_ripple_background">
-    <item
-        android:id="@android:id/mask"
-        android:drawable="@android:color/white" />
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
 </ripple>
diff --git a/car/core/res/drawable/car_card_ripple_background_dark.xml b/car/core/res/drawable/car_card_ripple_background_dark.xml
index 880ff7a..031d495 100644
--- a/car/core/res/drawable/car_card_ripple_background_dark.xml
+++ b/car/core/res/drawable/car_card_ripple_background_dark.xml
@@ -17,7 +17,9 @@
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:color="@color/car_card_ripple_background_dark">
-    <item
-        android:id="@android:id/mask"
-        android:drawable="@android:color/white" />
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
 </ripple>
diff --git a/car/core/res/drawable/car_card_ripple_background_light.xml b/car/core/res/drawable/car_card_ripple_background_light.xml
index 5d4f2c6..a0fb41f 100644
--- a/car/core/res/drawable/car_card_ripple_background_light.xml
+++ b/car/core/res/drawable/car_card_ripple_background_light.xml
@@ -17,7 +17,9 @@
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:color="@color/car_card_ripple_background_light">
-    <item
-        android:id="@android:id/mask"
-        android:drawable="@android:color/white" />
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
 </ripple>
diff --git a/car/core/res/layout/car_alert_dialog.xml b/car/core/res/layout/car_alert_dialog.xml
index e0ae46d..bb32134 100644
--- a/car/core/res/layout/car_alert_dialog.xml
+++ b/car/core/res/layout/car_alert_dialog.xml
@@ -83,13 +83,13 @@
 
             <!-- Margins for buttons set dynamically. -->
             <Button
-                android:id="@+id/positive_button"
+                android:id="@+id/negative_button"
                 android:layout_width="wrap_content"
                 android:visibility="gone"
                 style="?attr/dialogButtonStyle" />
 
             <Button
-                android:id="@+id/negative_button"
+                android:id="@+id/positive_button"
                 android:layout_width="wrap_content"
                 android:visibility="gone"
                 style="?attr/dialogButtonStyle" />
diff --git a/car/core/res/values/styles.xml b/car/core/res/values/styles.xml
index b9d089f..ea9f5de 100644
--- a/car/core/res/values/styles.xml
+++ b/car/core/res/values/styles.xml
@@ -393,7 +393,7 @@
 
     <!-- The style for borderless buttons. -->
     <style name="Widget.Car.Button.Borderless.Colored" parent="Widget.MaterialComponents.Button.TextButton">
-        <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
+        <item name="android:background">?android:attr/selectableItemBackground</item>
         <item name="android:fadingEdgeLength">@dimen/car_button_fading_edge_length</item>
         <item name="android:textAppearance">@style/TextAppearance.Car.Body3.Medium</item>
         <item name="android:ellipsize">none</item>
diff --git a/car/core/src/main/java/androidx/car/app/CarAlertDialog.java b/car/core/src/main/java/androidx/car/app/CarAlertDialog.java
index 9b7d908..b3110c0 100644
--- a/car/core/src/main/java/androidx/car/app/CarAlertDialog.java
+++ b/car/core/src/main/java/androidx/car/app/CarAlertDialog.java
@@ -234,13 +234,13 @@
         // If both buttons are visible, then there needs to be spacing between them.
         if ((mPositiveButton.getVisibility() == View.VISIBLE
                 && mNegativeButton.getVisibility() == View.VISIBLE)) {
-            int extraSpacingOffset = CarDialogUtil.calculateExtraButtonSpace(mPositiveButton) / 2;
-            positiveButtonLayoutParams.setMarginStart(buttonOffset - extraSpacingOffset);
-            positiveButtonLayoutParams.setMarginEnd(mButtonSpacing);
-            mPositiveButton.requestLayout();
-
-            negativeButtonLayoutParams.setMarginStart(mButtonSpacing);
+            int extraSpacingOffset = CarDialogUtil.calculateExtraButtonSpace(mNegativeButton) / 2;
+            negativeButtonLayoutParams.setMarginStart(buttonOffset - extraSpacingOffset);
+            negativeButtonLayoutParams.setMarginEnd(mButtonSpacing);
             mNegativeButton.requestLayout();
+
+            positiveButtonLayoutParams.setMarginStart(mButtonSpacing);
+            mPositiveButton.requestLayout();
         } else if (mPositiveButton.getVisibility() == View.VISIBLE) {
             int extraSpacingOffset = CarDialogUtil.calculateExtraButtonSpace(mPositiveButton) / 2;
             positiveButtonLayoutParams.setMarginStart(buttonOffset - extraSpacingOffset);
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt
index 61625db..0a320fc 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt
@@ -16,17 +16,16 @@
 
 package androidx.compose.benchmark
 
-import android.widget.FrameLayout
 import androidx.benchmark.BenchmarkRule
 import androidx.benchmark.measureRepeated
 import androidx.compose.Composable
-import androidx.compose.Compose
 import androidx.compose.Composer
 import androidx.compose.FrameManager
 import androidx.compose.Model
 import androidx.compose.Observe
 import androidx.compose.benchmark.realworld4.RealWorld4_FancyWidget_000
 import androidx.compose.composer
+import androidx.compose.disposeComposition
 import androidx.compose.runWithCurrent
 import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -38,6 +37,7 @@
 import androidx.ui.graphics.Color
 import org.junit.Assert.assertTrue
 import org.junit.FixMethodOrder
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -152,6 +152,7 @@
 
     @UiThreadTest
     @Test
+    @Ignore("Disabled as it appears to not do anything")
     fun benchmark_realworld4_mid_recompose() {
         val model = androidx.compose.benchmark.realworld4.createSampleData()
         measureRecompose {
@@ -166,19 +167,14 @@
 
     private fun measureCompose(block: @Composable() () -> Unit) {
         benchmarkRule.measureRepeated {
-            val root = runWithTimingDisabled {
-                val root = FrameLayout(activityRule.activity)
-                activityRule.activity.setContentView(root)
+            val activity = activityRule.activity
 
-                root
-            }
-
-            root.setContent {
+            activity.setContent {
                 block()
             }
 
             runWithTimingDisabled {
-                Compose.disposeComposition(root)
+                activity.disposeComposition()
             }
         }
     }
@@ -188,9 +184,9 @@
         receiver.block()
         var activeComposer: Composer<*>? = null
 
-        val root = FrameLayout(activityRule.activity)
-        activityRule.activity.setContentView(root)
-        root.setContent {
+        val activity = activityRule.activity
+
+        activity.setContent {
             activeComposer = composer.composer
             receiver.composeCb()
         }
@@ -209,7 +205,7 @@
             assertTrue(didSomething)
         }
 
-        Compose.disposeComposition(root)
+        activity.disposeComposition()
     }
 }
 
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt b/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt
index a2090a3..f94b0a5 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt
@@ -16,8 +16,6 @@
 
 package androidx.compose
 
-import java.util.Stack
-
 /**
  * An adapter that performs tree based operations on some tree startNode N without requiring a specific base type for N
  */
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt b/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
index b5b4175..e5fb5ec 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
@@ -16,8 +16,6 @@
 
 package androidx.compose
 
-import java.util.Stack
-
 internal typealias Change<N> = (
     applier: Applier<N>,
     slots: SlotWriter,
@@ -474,8 +472,8 @@
         if (insertedProviders.isNotEmpty()) {
             var current = insertedProviders.size - 1
             while (current >= 0) {
-                val element = insertedProviders[current]
-                if (element is Ambient.Holder<*> && element.ambient === key) {
+                val element = insertedProviders.peek(current)
+                if (element.ambient === key) {
                     @Suppress("UNCHECKED_CAST")
                     return element.value as? T ?: key.defaultValue
                 }
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/IntStack.kt b/compose/compose-runtime/src/main/java/androidx/compose/Stack.kt
similarity index 73%
rename from compose/compose-runtime/src/main/java/androidx/compose/IntStack.kt
rename to compose/compose-runtime/src/main/java/androidx/compose/Stack.kt
index 340c424..b586297 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/IntStack.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Stack.kt
@@ -16,6 +16,20 @@
 
 package androidx.compose
 
+internal class Stack<T> {
+    private val backing = ArrayList<T>()
+
+    val size: Int get() = backing.size
+
+    fun push(value: T) = backing.add(value)
+    fun pop(): T = backing.removeAt(size - 1)
+    fun peek(): T = backing.get(size - 1)
+    fun peek(index: Int): T = backing.get(index)
+    fun isEmpty() = backing.isEmpty()
+    fun isNotEmpty() = !isEmpty()
+    fun clear() = backing.clear()
+}
+
 internal class IntStack {
     private var slots = IntArray(10)
     private var tos = 0
@@ -35,4 +49,4 @@
     fun isEmpty() = tos == 0
     fun isNotEmpty() = tos != 0
     fun clear() { tos = 0 }
-}
\ No newline at end of file
+}
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt b/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt
index 8b6d1f1..b7ec9f7 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt
@@ -20,7 +20,6 @@
 import android.view.View
 import android.view.ViewGroup
 import androidx.compose.adapters.getViewAdapterIfExists
-import java.util.Stack
 
 class ViewAdapters {
     private val adapters = mutableListOf<(parent: Any, child: Any) -> Any?>()
diff --git a/work/workmanager-ktx/api/2.3.0-alpha01.txt b/work/workmanager-ktx/api/2.3.0-alpha01.txt
index 4ed72f8..ddfb009 100644
--- a/work/workmanager-ktx/api/2.3.0-alpha01.txt
+++ b/work/workmanager-ktx/api/2.3.0-alpha01.txt
@@ -6,6 +6,7 @@
     method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
     method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
     method public final void onStopped();
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
     property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
   }
diff --git a/work/workmanager-ktx/api/current.txt b/work/workmanager-ktx/api/current.txt
index 4ed72f8..ddfb009 100644
--- a/work/workmanager-ktx/api/current.txt
+++ b/work/workmanager-ktx/api/current.txt
@@ -6,6 +6,7 @@
     method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
     method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
     method public final void onStopped();
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
     property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
   }
diff --git a/work/workmanager-ktx/api/restricted_2.3.0-alpha01.txt b/work/workmanager-ktx/api/restricted_2.3.0-alpha01.txt
index 2a2fd52..5b6423e 100644
--- a/work/workmanager-ktx/api/restricted_2.3.0-alpha01.txt
+++ b/work/workmanager-ktx/api/restricted_2.3.0-alpha01.txt
@@ -6,6 +6,7 @@
     method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
     method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
     method public final void onStopped();
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
     property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
   }
diff --git a/work/workmanager-ktx/api/restricted_current.txt b/work/workmanager-ktx/api/restricted_current.txt
index 2a2fd52..5b6423e 100644
--- a/work/workmanager-ktx/api/restricted_current.txt
+++ b/work/workmanager-ktx/api/restricted_current.txt
@@ -6,6 +6,7 @@
     method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
     method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
     method public final void onStopped();
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
     property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
   }
diff --git a/work/workmanager-ktx/build.gradle b/work/workmanager-ktx/build.gradle
index 93c1a0e..0cee793 100644
--- a/work/workmanager-ktx/build.gradle
+++ b/work/workmanager-ktx/build.gradle
@@ -53,6 +53,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(WORK_ARCH_ROOM_TESTING)
     testImplementation(JUNIT)
 }
 
diff --git a/work/workmanager-ktx/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt b/work/workmanager-ktx/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt
index 6232be6..125f4e7 100644
--- a/work/workmanager-ktx/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt
+++ b/work/workmanager-ktx/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt
@@ -19,15 +19,18 @@
 import android.content.Context
 import android.util.Log
 import androidx.arch.core.executor.ArchTaskExecutor
-
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
 import androidx.test.filters.SmallTest
 import androidx.work.impl.WorkDatabase
 import androidx.work.impl.WorkManagerImpl
+import androidx.work.impl.utils.WorkProgressUpdater
 import androidx.work.impl.utils.futures.SettableFuture
 import androidx.work.impl.utils.taskexecutor.TaskExecutor
+import androidx.work.workers.ProgressUpdatingWorker
 import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.runBlocking
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.instanceOf
 import org.hamcrest.MatcherAssert.assertThat
@@ -35,6 +38,11 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
 import java.util.UUID
 import java.util.concurrent.Executor
 
@@ -65,7 +73,7 @@
                 }
             })
 
-        context = ApplicationProvider.getApplicationContext() as android.content.Context
+        context = ApplicationProvider.getApplicationContext()
         configuration = Configuration.Builder()
             .setExecutor(SynchronousExecutor())
             .setMinimumLoggingLevel(Log.DEBUG)
@@ -141,6 +149,44 @@
         assertThat(worker.job.isCancelled, `is`(true))
     }
 
+    @Test
+    @LargeTest
+    fun testProgressUpdates() {
+        val workerFactory = WorkerFactory.getDefaultWorkerFactory()
+        val progressUpdater = spy(WorkProgressUpdater(database, workManagerImpl.workTaskExecutor))
+        val workRequest = OneTimeWorkRequestBuilder<ProgressUpdatingWorker>().build()
+        database.workSpecDao().insertWorkSpec(workRequest.workSpec)
+        val worker = workerFactory.createWorkerWithDefaultFallback(
+            context,
+            ProgressUpdatingWorker::class.java.name,
+            WorkerParameters(
+                workRequest.id,
+                Data.EMPTY,
+                emptyList(),
+                WorkerParameters.RuntimeExtras(),
+                1,
+                configuration.executor,
+                workManagerImpl.workTaskExecutor,
+                workerFactory,
+                progressUpdater
+            )
+        ) as ProgressUpdatingWorker
+
+        runBlocking {
+            val result = worker.doWork()
+            val captor = ArgumentCaptor.forClass(Data::class.java)
+            verify(progressUpdater, times(2))
+                .updateProgress(
+                    any(Context::class.java),
+                    any(UUID::class.java),
+                    captor.capture()
+                )
+            assertThat(result, `is`(instanceOf(ListenableWorker.Result.Success::class.java)))
+            val recent = captor.allValues.lastOrNull()
+            assertThat(recent?.getInt(ProgressUpdatingWorker.Progress, 0), `is`(100))
+        }
+    }
+
     class SynchronousExecutor : Executor {
 
         override fun execute(command: Runnable) {
diff --git a/work/workmanager-ktx/src/androidTest/java/androidx/work/workers/ProgressUpdatingWorker.kt b/work/workmanager-ktx/src/androidTest/java/androidx/work/workers/ProgressUpdatingWorker.kt
new file mode 100644
index 0000000..f6d65ba
--- /dev/null
+++ b/work/workmanager-ktx/src/androidTest/java/androidx/work/workers/ProgressUpdatingWorker.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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 androidx.work.workers
+
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.Data
+import androidx.work.WorkerParameters
+import kotlinx.coroutines.delay
+
+class ProgressUpdatingWorker(context: Context, parameters: WorkerParameters) :
+    CoroutineWorker(context, parameters) {
+
+    companion object {
+        const val Progress = "Progress"
+        private const val delayDuration = 1L
+    }
+
+    override suspend fun doWork(): Result {
+        val firstUpdate = Data.Builder().putInt(Progress, 10).build()
+        val lastUpdate = Data.Builder().putInt(Progress, 100).build()
+        setProgress(firstUpdate)
+        delay(delayDuration)
+        setProgress(lastUpdate)
+        return Result.success()
+    }
+}
\ No newline at end of file
diff --git a/work/workmanager-ktx/src/main/java/androidx/work/CoroutineWorker.kt b/work/workmanager-ktx/src/main/java/androidx/work/CoroutineWorker.kt
index 56e5679..c1d12bb 100644
--- a/work/workmanager-ktx/src/main/java/androidx/work/CoroutineWorker.kt
+++ b/work/workmanager-ktx/src/main/java/androidx/work/CoroutineWorker.kt
@@ -88,6 +88,16 @@
      */
     abstract suspend fun doWork(): Result
 
+    /**
+     * Updates the progress for the [CoroutineWorker]. This is a suspending function unlike the
+     * [setProgressAsync] API which returns a [ListenableFuture].
+     *
+     * @param data The progress [Data]
+     */
+    suspend fun setProgress(data: Data) {
+        setProgressAsync(data).await()
+    }
+
     final override fun onStopped() {
         super.onStopped()
         future.cancel(false)
diff --git a/work/workmanager-rxjava2/api/2.3.0-alpha01.txt b/work/workmanager-rxjava2/api/2.3.0-alpha01.txt
index e43f0e5..757ca4c 100644
--- a/work/workmanager-rxjava2/api/2.3.0-alpha01.txt
+++ b/work/workmanager-rxjava2/api/2.3.0-alpha01.txt
@@ -5,6 +5,7 @@
     ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
     method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
     method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager-rxjava2/api/current.txt b/work/workmanager-rxjava2/api/current.txt
index e43f0e5..757ca4c 100644
--- a/work/workmanager-rxjava2/api/current.txt
+++ b/work/workmanager-rxjava2/api/current.txt
@@ -5,6 +5,7 @@
     ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
     method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
     method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager-rxjava2/api/restricted_2.3.0-alpha01.txt b/work/workmanager-rxjava2/api/restricted_2.3.0-alpha01.txt
index e43f0e5..757ca4c 100644
--- a/work/workmanager-rxjava2/api/restricted_2.3.0-alpha01.txt
+++ b/work/workmanager-rxjava2/api/restricted_2.3.0-alpha01.txt
@@ -5,6 +5,7 @@
     ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
     method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
     method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager-rxjava2/api/restricted_current.txt b/work/workmanager-rxjava2/api/restricted_current.txt
index e43f0e5..757ca4c 100644
--- a/work/workmanager-rxjava2/api/restricted_current.txt
+++ b/work/workmanager-rxjava2/api/restricted_current.txt
@@ -5,6 +5,7 @@
     ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
     method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
     method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager-rxjava2/src/main/java/androidx/work/RxWorker.java b/work/workmanager-rxjava2/src/main/java/androidx/work/RxWorker.java
index de7fcf0..2200863 100644
--- a/work/workmanager-rxjava2/src/main/java/androidx/work/RxWorker.java
+++ b/work/workmanager-rxjava2/src/main/java/androidx/work/RxWorker.java
@@ -117,10 +117,22 @@
     @MainThread
     public abstract @NonNull Single<Result> createWork();
 
+    /**
+     * Updates the progress for a {@link RxWorker}. This method returns a {@link Single} unlike the
+     * {@link ListenableWorker#setProgressAsync(Data)} API.
+     *
+     * @param data The progress {@link Data}
+     * @return The {@link Single}
+     */
+    @NonNull
+    public final Single<Void> setProgress(@NonNull Data data) {
+        return Single.fromFuture(setProgressAsync(data));
+    }
+
     @Override
     public void onStopped() {
         super.onStopped();
-        final SingleFutureAdapter observer = mSingleFutureObserverAdapter;
+        final SingleFutureAdapter<Result> observer = mSingleFutureObserverAdapter;
         if (observer != null) {
             observer.dispose();
             mSingleFutureObserverAdapter = null;
diff --git a/work/workmanager/api/2.3.0-alpha01.txt b/work/workmanager/api/2.3.0-alpha01.txt
index 3656be3..a6cb786 100644
--- a/work/workmanager/api/2.3.0-alpha01.txt
+++ b/work/workmanager/api/2.3.0-alpha01.txt
@@ -143,7 +143,7 @@
     method @RequiresApi(24) public final java.util.List<android.net.Uri!> getTriggeredContentUris();
     method public final boolean isStopped();
     method public void onStopped();
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgress(androidx.work.Data);
+    method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgressAsync(androidx.work.Data);
     method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager/api/current.txt b/work/workmanager/api/current.txt
index 3656be3..a6cb786 100644
--- a/work/workmanager/api/current.txt
+++ b/work/workmanager/api/current.txt
@@ -143,7 +143,7 @@
     method @RequiresApi(24) public final java.util.List<android.net.Uri!> getTriggeredContentUris();
     method public final boolean isStopped();
     method public void onStopped();
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgress(androidx.work.Data);
+    method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgressAsync(androidx.work.Data);
     method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager/src/main/java/androidx/work/ListenableWorker.java b/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
index 72a7d5d..2203618 100644
--- a/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
+++ b/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
@@ -195,7 +195,7 @@
      * Cancelling this future is a no-op.
      */
     @NonNull
-    public ListenableFuture<Void> setProgress(@NonNull Data data) {
+    public final ListenableFuture<Void> setProgressAsync(@NonNull Data data) {
         return mWorkerParams.getProgressUpdater()
                 .updateProgress(getApplicationContext(), getId(), data);
     }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/WorkProgressUpdater.java b/work/workmanager/src/main/java/androidx/work/impl/utils/WorkProgressUpdater.java
index 38cd978..dc114ee 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/WorkProgressUpdater.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/WorkProgressUpdater.java
@@ -73,13 +73,14 @@
                     if (state == null) {
                         Logger.get().warning(TAG,
                                 String.format(
-                                        "Ignoring setProgress(...). WorkSpec (%s) does not exist.",
+                                        "Ignoring setProgressAsync(...). WorkSpec (%s) does not "
+                                                + "exist.",
                                         workSpecId));
                     } else if (state.isFinished()) {
                         Logger.get().warning(TAG,
                                 String.format(
-                                        "Ignoring setProgress(...). WorkSpec (%s) has finished "
-                                                + "execution.",
+                                        "Ignoring setProgressAsync(...). WorkSpec (%s) has "
+                                                + "finished execution.",
                                         workSpecId));
                     } else {
                         WorkProgress progress = new WorkProgress(workSpecId, data);