Add progress to WorkInfo.

* Added it to the public API.
* Will add progress specific tests in a separate CL.

Test: Existing unit tests pass.
Change-Id: Id682d5c6541dc2a5d3eeec767a89f4d3a7019b72
diff --git a/work/workmanager-testing/api/2.3.0-alpha01.txt b/work/workmanager-testing/api/2.3.0-alpha01.txt
index 8827453..89982e5 100644
--- a/work/workmanager-testing/api/2.3.0-alpha01.txt
+++ b/work/workmanager-testing/api/2.3.0-alpha01.txt
@@ -19,6 +19,7 @@
     method public androidx.work.testing.TestListenableWorkerBuilder setId(java.util.UUID);
     method public androidx.work.testing.TestListenableWorkerBuilder setInputData(androidx.work.Data);
     method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder setNetwork(android.net.Network);
+    method public androidx.work.testing.TestListenableWorkerBuilder setProgressUpdater(androidx.work.ProgressUpdater);
     method public androidx.work.testing.TestListenableWorkerBuilder setRunAttemptCount(int);
     method public androidx.work.testing.TestListenableWorkerBuilder setTags(java.util.List<java.lang.String!>);
     method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
diff --git a/work/workmanager-testing/api/current.txt b/work/workmanager-testing/api/current.txt
index 8827453..89982e5 100644
--- a/work/workmanager-testing/api/current.txt
+++ b/work/workmanager-testing/api/current.txt
@@ -19,6 +19,7 @@
     method public androidx.work.testing.TestListenableWorkerBuilder setId(java.util.UUID);
     method public androidx.work.testing.TestListenableWorkerBuilder setInputData(androidx.work.Data);
     method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder setNetwork(android.net.Network);
+    method public androidx.work.testing.TestListenableWorkerBuilder setProgressUpdater(androidx.work.ProgressUpdater);
     method public androidx.work.testing.TestListenableWorkerBuilder setRunAttemptCount(int);
     method public androidx.work.testing.TestListenableWorkerBuilder setTags(java.util.List<java.lang.String!>);
     method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
diff --git a/work/workmanager-testing/api/restricted_2.3.0-alpha01.txt b/work/workmanager-testing/api/restricted_2.3.0-alpha01.txt
index 8827453..9d37854 100644
--- a/work/workmanager-testing/api/restricted_2.3.0-alpha01.txt
+++ b/work/workmanager-testing/api/restricted_2.3.0-alpha01.txt
@@ -19,6 +19,7 @@
     method public androidx.work.testing.TestListenableWorkerBuilder setId(java.util.UUID);
     method public androidx.work.testing.TestListenableWorkerBuilder setInputData(androidx.work.Data);
     method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder setNetwork(android.net.Network);
+    method public androidx.work.testing.TestListenableWorkerBuilder setProgressUpdater(androidx.work.ProgressUpdater);
     method public androidx.work.testing.TestListenableWorkerBuilder setRunAttemptCount(int);
     method public androidx.work.testing.TestListenableWorkerBuilder setTags(java.util.List<java.lang.String!>);
     method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
@@ -31,6 +32,11 @@
     method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W> TestListenableWorkerBuilder(android.content.Context context, androidx.work.Data inputData = androidx.work.Data.EMPTY, java.util.List<java.lang.String> tags = emptyList(), int runAttemptCount = 1, java.util.List<? extends android.net.Uri> triggeredContentUris = emptyList(), java.util.List<java.lang.String> triggeredContentAuthorities = emptyList());
   }
 
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class TestProgressUpdater implements androidx.work.ProgressUpdater {
+    ctor public TestProgressUpdater();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+  }
+
   public class TestWorkerBuilder<W extends androidx.work.Worker> extends androidx.work.testing.TestListenableWorkerBuilder<W> {
     method public static androidx.work.testing.TestWorkerBuilder<? extends androidx.work.Worker> from(android.content.Context, androidx.work.WorkRequest, java.util.concurrent.Executor);
     method public static <W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W!> from(android.content.Context, Class<W!>, java.util.concurrent.Executor);
diff --git a/work/workmanager-testing/api/restricted_current.txt b/work/workmanager-testing/api/restricted_current.txt
index 8827453..9d37854 100644
--- a/work/workmanager-testing/api/restricted_current.txt
+++ b/work/workmanager-testing/api/restricted_current.txt
@@ -19,6 +19,7 @@
     method public androidx.work.testing.TestListenableWorkerBuilder setId(java.util.UUID);
     method public androidx.work.testing.TestListenableWorkerBuilder setInputData(androidx.work.Data);
     method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder setNetwork(android.net.Network);
+    method public androidx.work.testing.TestListenableWorkerBuilder setProgressUpdater(androidx.work.ProgressUpdater);
     method public androidx.work.testing.TestListenableWorkerBuilder setRunAttemptCount(int);
     method public androidx.work.testing.TestListenableWorkerBuilder setTags(java.util.List<java.lang.String!>);
     method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
@@ -31,6 +32,11 @@
     method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W> TestListenableWorkerBuilder(android.content.Context context, androidx.work.Data inputData = androidx.work.Data.EMPTY, java.util.List<java.lang.String> tags = emptyList(), int runAttemptCount = 1, java.util.List<? extends android.net.Uri> triggeredContentUris = emptyList(), java.util.List<java.lang.String> triggeredContentAuthorities = emptyList());
   }
 
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class TestProgressUpdater implements androidx.work.ProgressUpdater {
+    ctor public TestProgressUpdater();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+  }
+
   public class TestWorkerBuilder<W extends androidx.work.Worker> extends androidx.work.testing.TestListenableWorkerBuilder<W> {
     method public static androidx.work.testing.TestWorkerBuilder<? extends androidx.work.Worker> from(android.content.Context, androidx.work.WorkRequest, java.util.concurrent.Executor);
     method public static <W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W!> from(android.content.Context, Class<W!>, java.util.concurrent.Executor);
diff --git a/work/workmanager/api/2.3.0-alpha01.txt b/work/workmanager/api/2.3.0-alpha01.txt
index 8d3b024..3656be3 100644
--- a/work/workmanager/api/2.3.0-alpha01.txt
+++ b/work/workmanager/api/2.3.0-alpha01.txt
@@ -143,6 +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 @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
@@ -208,6 +209,10 @@
     ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration, java.time.Duration);
   }
 
+  public interface ProgressUpdater {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+  }
+
   public abstract class WorkContinuation {
     ctor public WorkContinuation();
     method public static androidx.work.WorkContinuation combine(java.util.List<androidx.work.WorkContinuation!>);
@@ -221,6 +226,7 @@
   public final class WorkInfo {
     method public java.util.UUID getId();
     method public androidx.work.Data getOutputData();
+    method public androidx.work.Data getProgress();
     method @IntRange(from=0) public int getRunAttemptCount();
     method public androidx.work.WorkInfo.State getState();
     method public java.util.Set<java.lang.String!> getTags();
diff --git a/work/workmanager/api/current.txt b/work/workmanager/api/current.txt
index 8d3b024..3656be3 100644
--- a/work/workmanager/api/current.txt
+++ b/work/workmanager/api/current.txt
@@ -143,6 +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 @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
@@ -208,6 +209,10 @@
     ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration, java.time.Duration);
   }
 
+  public interface ProgressUpdater {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+  }
+
   public abstract class WorkContinuation {
     ctor public WorkContinuation();
     method public static androidx.work.WorkContinuation combine(java.util.List<androidx.work.WorkContinuation!>);
@@ -221,6 +226,7 @@
   public final class WorkInfo {
     method public java.util.UUID getId();
     method public androidx.work.Data getOutputData();
+    method public androidx.work.Data getProgress();
     method @IntRange(from=0) public int getRunAttemptCount();
     method public androidx.work.WorkInfo.State getState();
     method public java.util.Set<java.lang.String!> getTags();
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
index f2032dc..c142b99 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
@@ -963,12 +963,14 @@
                 ENQUEUED,
                 Data.EMPTY,
                 Collections.singletonList(TestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         WorkInfo workInfo1 = new WorkInfo(
                 work1.getId(),
                 ENQUEUED,
                 Data.EMPTY,
                 Collections.singletonList(TestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         assertThat(captor.getValue(), containsInAnyOrder(workInfo0, workInfo1));
 
@@ -984,6 +986,7 @@
                 RUNNING,
                 Data.EMPTY,
                 Collections.singletonList(TestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         assertThat(captor.getValue(), containsInAnyOrder(workInfo0, workInfo1));
 
@@ -999,6 +1002,7 @@
                 RUNNING,
                 Data.EMPTY,
                 Collections.singletonList(TestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         assertThat(captor.getValue(), containsInAnyOrder(workInfo0, workInfo1));
 
@@ -1033,18 +1037,21 @@
                 RUNNING,
                 Data.EMPTY,
                 Arrays.asList(TestWorker.class.getName(), firstTag, secondTag),
+                Data.EMPTY,
                 0);
         WorkInfo workInfo1 = new WorkInfo(
                 work1.getId(),
                 BLOCKED,
                 Data.EMPTY,
                 Arrays.asList(TestWorker.class.getName(), firstTag),
+                Data.EMPTY,
                 0);
         WorkInfo workInfo2 = new WorkInfo(
                 work2.getId(),
                 SUCCEEDED,
                 Data.EMPTY,
                 Arrays.asList(TestWorker.class.getName(), secondTag),
+                Data.EMPTY,
                 0);
 
         List<WorkInfo> workInfos = mWorkManagerImpl.getWorkInfosByTag(firstTag).get();
@@ -1080,18 +1087,21 @@
                 RUNNING,
                 Data.EMPTY,
                 Collections.singletonList(InfiniteTestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         WorkInfo workInfo1 = new WorkInfo(
                 work1.getId(),
                 BLOCKED,
                 Data.EMPTY,
                 Collections.singletonList(InfiniteTestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         WorkInfo workInfo2 = new WorkInfo(
                 work2.getId(),
                 BLOCKED,
                 Data.EMPTY,
                 Collections.singletonList(InfiniteTestWorker.class.getName()),
+                Data.EMPTY,
                 0);
 
         List<WorkInfo> workInfos = mWorkManagerImpl.getWorkInfosForUniqueWork(uniqueName).get();
@@ -1138,18 +1148,21 @@
                 RUNNING,
                 Data.EMPTY,
                 Collections.singletonList(InfiniteTestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         WorkInfo workInfo1 = new WorkInfo(
                 work1.getId(),
                 BLOCKED,
                 Data.EMPTY,
                 Collections.singletonList(InfiniteTestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         WorkInfo workInfo2 = new WorkInfo(
                 work2.getId(),
                 BLOCKED,
                 Data.EMPTY,
                 Collections.singletonList(InfiniteTestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         assertThat(captor.getValue(), containsInAnyOrder(workInfo0, workInfo1, workInfo2));
 
@@ -1164,6 +1177,7 @@
                 ENQUEUED,
                 Data.EMPTY,
                 Collections.singletonList(InfiniteTestWorker.class.getName()),
+                Data.EMPTY,
                 0);
         assertThat(captor.getValue(), containsInAnyOrder(workInfo0, workInfo1, workInfo2));
 
diff --git a/work/workmanager/src/main/java/androidx/work/WorkInfo.java b/work/workmanager/src/main/java/androidx/work/WorkInfo.java
index 0f68b2a..c1417ee 100644
--- a/work/workmanager/src/main/java/androidx/work/WorkInfo.java
+++ b/work/workmanager/src/main/java/androidx/work/WorkInfo.java
@@ -37,6 +37,7 @@
     private @NonNull State mState;
     private @NonNull Data mOutputData;
     private @NonNull Set<String> mTags;
+    private @NonNull Data mProgress;
     private int mRunAttemptCount;
 
     /**
@@ -48,11 +49,13 @@
             @NonNull State state,
             @NonNull Data outputData,
             @NonNull List<String> tags,
+            @NonNull Data progress,
             int runAttemptCount) {
         mId = id;
         mState = state;
         mOutputData = outputData;
         mTags = new HashSet<>(tags);
+        mProgress = progress;
         mRunAttemptCount = runAttemptCount;
     }
 
@@ -94,6 +97,15 @@
     }
 
     /**
+     * Gets the progress {@link Data} associated with the {@link WorkRequest}.
+     *
+     * @return The progress {@link Data} associated with the {@link WorkRequest}
+     */
+    public @NonNull Data getProgress() {
+        return mProgress;
+    }
+
+    /**
      * Gets the run attempt count of the {@link WorkRequest}.  Note that for
      * {@link PeriodicWorkRequest}s, the run attempt count gets reset between successful runs.
      *
@@ -115,7 +127,8 @@
         if (!mId.equals(workInfo.mId)) return false;
         if (mState != workInfo.mState) return false;
         if (!mOutputData.equals(workInfo.mOutputData)) return false;
-        return mTags.equals(workInfo.mTags);
+        if (!mTags.equals(workInfo.mTags)) return false;
+        return mProgress.equals(workInfo.mProgress);
     }
 
     @Override
@@ -124,6 +137,7 @@
         result = 31 * result + mState.hashCode();
         result = 31 * result + mOutputData.hashCode();
         result = 31 * result + mTags.hashCode();
+        result = 31 * result + mProgress.hashCode();
         result = 31 * result + mRunAttemptCount;
         return result;
     }
@@ -135,6 +149,7 @@
                 +   ", mState=" + mState
                 +   ", mOutputData=" + mOutputData
                 +   ", mTags=" + mTags
+                +   ", mProgress=" + mProgress
                 + '}';
     }
 
diff --git a/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpec.java b/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpec.java
index 85e12b0..cf357f9 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpec.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpec.java
@@ -398,13 +398,33 @@
                 projection = {"tag"})
         public List<String> tags;
 
+        // This is actually a 1-1 relationship. However Room 2.1 models the type as a List.
+        // This will change in Room 2.2
+        @Relation(
+                parentColumn = "id",
+                entityColumn = "work_spec_id",
+                entity = WorkProgress.class,
+                projection = {"progress"})
+        public List<Data> progress;
+
         /**
          * Converts this POJO to a {@link WorkInfo}.
          *
          * @return The {@link WorkInfo} represented by this POJO
          */
+        @NonNull
         public WorkInfo toWorkInfo() {
-            return new WorkInfo(UUID.fromString(id), state, output, tags, runAttemptCount);
+            Data progress = this.progress != null && !this.progress.isEmpty()
+                    ? this.progress.get(0)
+                    : Data.EMPTY;
+
+            return new WorkInfo(
+                    UUID.fromString(id),
+                    state,
+                    output,
+                    tags,
+                    progress,
+                    runAttemptCount);
         }
 
         @Override
@@ -418,7 +438,8 @@
             if (id != null ? !id.equals(that.id) : that.id != null) return false;
             if (state != that.state) return false;
             if (output != null ? !output.equals(that.output) : that.output != null) return false;
-            return tags != null ? tags.equals(that.tags) : that.tags == null;
+            if (tags != null ? !tags.equals(that.tags) : that.tags != null) return false;
+            return progress != null ? progress.equals(that.progress) : that.progress == null;
         }
 
         @Override
@@ -428,6 +449,7 @@
             result = 31 * result + (output != null ? output.hashCode() : 0);
             result = 31 * result + runAttemptCount;
             result = 31 * result + (tags != null ? tags.hashCode() : 0);
+            result = 31 * result + (progress != null ? progress.hashCode() : 0);
             return result;
         }
     }