[go: nahoru, domu]

Migrate *Worker to Kotlin

bug: 209145335
Test: N/A
Change-Id: I26be62e34a7eb0558573c827368efd068fe12265
diff --git a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/MainActivity.java b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/MainActivity.java
index 78f913c..aa83e1c 100644
--- a/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/MainActivity.java
+++ b/work/integration-tests/testapp/src/main/java/androidx/work/integration/testapp/MainActivity.java
@@ -54,6 +54,7 @@
 import androidx.work.WorkRequest;
 import androidx.work.impl.background.systemjob.SystemJobService;
 import androidx.work.impl.workers.ConstraintTrackingWorker;
+import androidx.work.impl.workers.ConstraintTrackingWorkerKt;
 import androidx.work.integration.testapp.imageprocessing.ImageProcessingActivity;
 import androidx.work.integration.testapp.sherlockholmes.AnalyzeSherlockHolmesActivity;
 import androidx.work.multiprocess.RemoteWorkerService;
@@ -376,7 +377,7 @@
                     @Override
                     public void onClick(View v) {
                         Data inputData = new Data.Builder()
-                                .putString(ConstraintTrackingWorker.ARGUMENT_CLASS_NAME,
+                                .putString(ConstraintTrackingWorkerKt.ARGUMENT_CLASS_NAME,
                                         ForegroundWorker.class.getName())
                                 .build();
 
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java b/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
index 2edd07d..52cc8fe 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
@@ -29,6 +29,7 @@
 import static androidx.work.WorkInfo.State.RUNNING;
 import static androidx.work.WorkInfo.State.SUCCEEDED;
 import static androidx.work.impl.model.WorkSpec.SCHEDULE_NOT_REQUESTED_YET;
+import static androidx.work.impl.workers.ConstraintTrackingWorkerKt.ARGUMENT_CLASS_NAME;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
@@ -104,6 +105,7 @@
 import androidx.work.impl.utils.PreferenceUtils;
 import androidx.work.impl.utils.taskexecutor.InstantWorkTaskExecutor;
 import androidx.work.impl.workers.ConstraintTrackingWorker;
+import androidx.work.impl.workers.ConstraintTrackingWorkerKt;
 import androidx.work.worker.InfiniteTestWorker;
 import androidx.work.worker.StopAwareWorker;
 import androidx.work.worker.TestWorker;
@@ -1793,7 +1795,7 @@
         WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
         assertThat(workSpec.workerClassName, is(ConstraintTrackingWorker.class.getName()));
         assertThat(workSpec.input.getString(
-                ConstraintTrackingWorker.ARGUMENT_CLASS_NAME),
+                        ConstraintTrackingWorkerKt.ARGUMENT_CLASS_NAME),
                 is(TestWorker.class.getName()));
     }
 
@@ -1811,8 +1813,7 @@
 
         WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
         assertThat(workSpec.workerClassName, is(ConstraintTrackingWorker.class.getName()));
-        assertThat(workSpec.input.getString(
-                ConstraintTrackingWorker.ARGUMENT_CLASS_NAME),
+        assertThat(workSpec.input.getString(ARGUMENT_CLASS_NAME),
                 is(TestWorker.class.getName()));
     }
 
@@ -1822,7 +1823,7 @@
     public void testEnqueueApi23To25_withConstraintTrackingWorker_expectsOriginalWorker()
             throws ExecutionException, InterruptedException {
         Data data = new Data.Builder()
-                .put(ConstraintTrackingWorker.ARGUMENT_CLASS_NAME, TestWorker.class.getName())
+                .put(ARGUMENT_CLASS_NAME, TestWorker.class.getName())
                 .build();
 
         OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(ConstraintTrackingWorker.class)
@@ -1835,8 +1836,7 @@
 
         WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
         assertThat(workSpec.workerClassName, is(ConstraintTrackingWorker.class.getName()));
-        assertThat(workSpec.input.getString(
-                ConstraintTrackingWorker.ARGUMENT_CLASS_NAME),
+        assertThat(workSpec.input.getString(ARGUMENT_CLASS_NAME),
                 is(TestWorker.class.getName()));
     }
 
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/workers/ConstraintTrackingWorkerTest.java b/work/work-runtime/src/androidTest/java/androidx/work/impl/workers/ConstraintTrackingWorkerTest.java
index d1ed219..eeb8176 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/workers/ConstraintTrackingWorkerTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/workers/ConstraintTrackingWorkerTest.java
@@ -16,6 +16,8 @@
 
 package androidx.work.impl.workers;
 
+import static androidx.work.impl.workers.ConstraintTrackingWorkerKt.ARGUMENT_CLASS_NAME;
+
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -318,7 +320,7 @@
                 .build();
 
         Data input = new Data.Builder()
-                .putString(ConstraintTrackingWorker.ARGUMENT_CLASS_NAME, delegateName)
+                .putString(ARGUMENT_CLASS_NAME, delegateName)
                 .putBoolean(TEST_ARGUMENT_NAME, true)
                 .build();
 
@@ -350,7 +352,6 @@
                 is(CoreMatchers.<ListenableWorker>instanceOf(ConstraintTrackingWorker.class)));
         // mWorker is already a spy
         mWorker = (ConstraintTrackingWorker) worker;
-        when(mWorker.getWorkDatabase()).thenReturn(mDatabase);
     }
 
     private WorkerWrapper.Builder createWorkerWrapperBuilder() {
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
index 5831aed..977793e 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
@@ -25,7 +25,7 @@
 import static androidx.work.WorkInfo.State.FAILED;
 import static androidx.work.WorkInfo.State.RUNNING;
 import static androidx.work.WorkInfo.State.SUCCEEDED;
-import static androidx.work.impl.workers.ConstraintTrackingWorker.ARGUMENT_CLASS_NAME;
+import static androidx.work.impl.workers.ConstraintTrackingWorkerKt.ARGUMENT_CLASS_NAME;
 
 import android.content.Context;
 import android.os.Build;
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/workers/CombineContinuationsWorker.kt b/work/work-runtime/src/main/java/androidx/work/impl/workers/CombineContinuationsWorker.kt
index ef29c98..484da27 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/workers/CombineContinuationsWorker.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/workers/CombineContinuationsWorker.kt
@@ -13,31 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package androidx.work.impl.workers
 
-package androidx.work.impl.workers;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.work.Worker;
-import androidx.work.WorkerParameters;
+import android.content.Context
+import androidx.work.Worker
+import androidx.work.WorkerParameters
 
 /**
- * A {@link Worker} that helps combine work continuations.
- *
- * @hide
+ * A [Worker] that helps combine work continuations.
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class CombineContinuationsWorker extends Worker {
-
-    public CombineContinuationsWorker(@NonNull Context context,
-            @NonNull WorkerParameters workerParams) {
-        super(context, workerParams);
-    }
-
-    @Override
-    public @NonNull Result doWork() {
-        return Result.success(getInputData());
-    }
-}
+internal class CombineContinuationsWorker(
+    context: Context,
+    workerParams: WorkerParameters
+) : Worker(context, workerParams) {
+    override fun doWork() = Result.success(inputData)
+}
\ No newline at end of file
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/workers/ConstraintTrackingWorker.kt b/work/work-runtime/src/main/java/androidx/work/impl/workers/ConstraintTrackingWorker.kt
index 10c3d99..788346c 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/workers/ConstraintTrackingWorker.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/workers/ConstraintTrackingWorker.kt
@@ -13,233 +13,150 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package androidx.work.impl.workers
 
-package androidx.work.impl.workers;
-
-import android.content.Context;
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-import androidx.work.ListenableWorker;
-import androidx.work.Logger;
-import androidx.work.Worker;
-import androidx.work.WorkerParameters;
-import androidx.work.impl.WorkDatabase;
-import androidx.work.impl.WorkManagerImpl;
-import androidx.work.impl.constraints.WorkConstraintsCallback;
-import androidx.work.impl.constraints.WorkConstraintsTrackerImpl;
-import androidx.work.impl.constraints.trackers.Trackers;
-import androidx.work.impl.model.WorkSpec;
-import androidx.work.impl.utils.futures.SettableFuture;
-import androidx.work.impl.utils.taskexecutor.TaskExecutor;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.Collections;
-import java.util.List;
+import android.content.Context
+import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
+import androidx.work.ListenableWorker
+import androidx.work.ListenableWorker.Result
+import androidx.work.Logger
+import androidx.work.WorkerParameters
+import androidx.work.impl.WorkManagerImpl
+import androidx.work.impl.constraints.WorkConstraintsCallback
+import androidx.work.impl.constraints.WorkConstraintsTrackerImpl
+import androidx.work.impl.utils.futures.SettableFuture
+import com.google.common.util.concurrent.ListenableFuture
 
 /**
- * Is an implementation of a {@link Worker} that can delegate to a different {@link Worker}
- * when the constraints are met.
+ * Is an implementation of a [androidx.work.Worker] that can delegate to a different
+ * [androidx.work.Worker] when the constraints are met.
  *
  * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class ConstraintTrackingWorker extends ListenableWorker implements WorkConstraintsCallback {
+class ConstraintTrackingWorker(
+    appContext: Context,
+    private val workerParameters: WorkerParameters
+) : ListenableWorker(appContext, workerParameters), WorkConstraintsCallback {
 
-    private static final String TAG = Logger.tagWithPrefix("ConstraintTrkngWrkr");
+    private val lock = Any()
+
+    // Marking this volatile as the delegated workers could switch threads.
+    @Volatile
+    private var areConstraintsUnmet: Boolean = false
+    private val future = SettableFuture.create<Result>()
 
     /**
-     * The {@code className} of the {@link Worker} to delegate to.
+     * @return The [androidx.work.Worker] used for delegated work
+     * @hide
      */
-    public static final String ARGUMENT_CLASS_NAME =
-            "androidx.work.impl.workers.ConstraintTrackingWorker.ARGUMENT_CLASS_NAME";
+    @get:VisibleForTesting
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    var delegate: ListenableWorker? = null
+        private set
 
-    private WorkerParameters mWorkerParameters;
-
-    // These are package-private to avoid synthetic accessor.
-    final Object mLock;
-    // Marking this volatile as the delegated workers could switch threads.
-    volatile boolean mAreConstraintsUnmet;
-    SettableFuture<Result> mFuture;
-
-    @Nullable private ListenableWorker mDelegate;
-
-    public ConstraintTrackingWorker(@NonNull Context appContext,
-            @NonNull WorkerParameters workerParams) {
-        super(appContext, workerParams);
-        mWorkerParameters = workerParams;
-        mLock = new Object();
-        mAreConstraintsUnmet = false;
-        mFuture = SettableFuture.create();
+    override fun startWork(): ListenableFuture<Result> {
+        backgroundExecutor.execute { setupAndRunConstraintTrackingWork() }
+        return future
     }
 
-    @NonNull
-    @Override
-    public ListenableFuture<Result> startWork() {
-        getBackgroundExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                setupAndRunConstraintTrackingWork();
-            }
-        });
-        return mFuture;
-    }
+    private fun setupAndRunConstraintTrackingWork() {
+        if (future.isCancelled) return
 
-    // Package-private to avoid synthetic accessor.
-    void setupAndRunConstraintTrackingWork() {
-        String className = getInputData().getString(ARGUMENT_CLASS_NAME);
-        if (TextUtils.isEmpty(className)) {
-            Logger.get().error(TAG, "No worker to delegate to.");
-            setFutureFailed();
-            return;
+        val className = inputData.getString(ARGUMENT_CLASS_NAME)
+        val logger = Logger.get()
+        if (className.isNullOrEmpty()) {
+            logger.error(TAG, "No worker to delegate to.")
+            future.setFailed()
+            return
+        }
+        delegate = workerFactory.createWorkerWithDefaultFallback(
+            applicationContext, className, workerParameters
+        )
+        if (delegate == null) {
+            logger.debug(TAG, "No worker to delegate to.")
+            future.setFailed()
+            return
         }
 
-        mDelegate = getWorkerFactory().createWorkerWithDefaultFallback(
-                getApplicationContext(),
-                className,
-                mWorkerParameters);
-
-        if (mDelegate == null) {
-            Logger.get().debug(TAG, "No worker to delegate to.");
-            setFutureFailed();
-            return;
-        }
-
-        WorkDatabase workDatabase = getWorkDatabase();
-
+        val workManagerImpl = WorkManagerImpl.getInstance(applicationContext)
         // We need to know what the real constraints are for the delegate.
-        WorkSpec workSpec = workDatabase.workSpecDao().getWorkSpec(getId().toString());
+        val workSpec = workManagerImpl.workDatabase.workSpecDao().getWorkSpec(id.toString())
         if (workSpec == null) {
-            setFutureFailed();
-            return;
+            future.setFailed()
+            return
         }
-        WorkConstraintsTrackerImpl workConstraintsTracker =
-                new WorkConstraintsTrackerImpl(getTrackers(), this);
+        val workConstraintsTracker = WorkConstraintsTrackerImpl(workManagerImpl.trackers, this)
 
         // Start tracking
-        workConstraintsTracker.replace(Collections.singletonList(workSpec));
-
-        if (workConstraintsTracker.areAllConstraintsMet(getId().toString())) {
-            Logger.get().debug(TAG, "Constraints met for delegate " + className);
+        workConstraintsTracker.replace(listOf(workSpec))
+        if (workConstraintsTracker.areAllConstraintsMet(id.toString())) {
+            logger.debug(TAG, "Constraints met for delegate $className")
 
             // Wrapping the call to mDelegate#doWork() in a try catch, because
             // changes in constraints can cause the worker to throw RuntimeExceptions, and
             // that should cause a retry.
             try {
-                final ListenableFuture<Result> innerFuture = mDelegate.startWork();
-                innerFuture.addListener(new Runnable() {
-                    @Override
-                    public void run() {
-                        synchronized (mLock) {
-                            if (mAreConstraintsUnmet) {
-                                setFutureRetry();
-                            } else {
-                                mFuture.setFuture(innerFuture);
-                            }
+                val innerFuture = delegate!!.startWork()
+                innerFuture.addListener({
+                    synchronized(lock) {
+                        if (areConstraintsUnmet) {
+                            future.setRetry()
+                        } else {
+                            future.setFuture(innerFuture)
                         }
                     }
-                }, getBackgroundExecutor());
-            } catch (Throwable exception) {
-                Logger.get().debug(TAG, String.format(
-                        "Delegated worker %s threw exception in startWork.", className),
-                        exception);
-                synchronized (mLock) {
-                    if (mAreConstraintsUnmet) {
-                        Logger.get().debug(TAG, "Constraints were unmet, Retrying.");
-                        setFutureRetry();
+                }, backgroundExecutor)
+            } catch (exception: Throwable) {
+                logger.debug(
+                    TAG, "Delegated worker $className threw exception in startWork.", exception
+                )
+                synchronized(lock) {
+                    if (areConstraintsUnmet) {
+                        logger.debug(TAG, "Constraints were unmet, Retrying.")
+                        future.setRetry()
                     } else {
-                        setFutureFailed();
+                        future.setFailed()
                     }
                 }
             }
         } else {
-            Logger.get().debug(TAG, String.format(
-                    "Constraints not met for delegate %s. Requesting retry.", className));
-            setFutureRetry();
+            logger.debug(
+                TAG, "Constraints not met for delegate $className. Requesting retry."
+            )
+            future.setRetry()
         }
-
     }
 
-    // Package-private to avoid synthetic accessor.
-    void setFutureFailed() {
-        mFuture.set(Result.failure());
-    }
-
-    // Package-private to avoid synthetic accessor.
-    void setFutureRetry() {
-        mFuture.set(Result.retry());
-    }
-
-    @Override
-    public void onStopped() {
-        super.onStopped();
-        if (mDelegate != null && !mDelegate.isStopped()) {
+    override fun onStopped() {
+        super.onStopped()
+        val delegateInner = delegate
+        if (delegateInner != null && !delegateInner.isStopped) {
             // Stop is the method that sets the stopped and cancelled bits and invokes onStopped.
-            mDelegate.stop();
+            delegateInner.stop()
         }
     }
 
-    /**
-     * @return The instance of {@link WorkDatabase}
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @VisibleForTesting
-    @NonNull
-    public WorkDatabase getWorkDatabase() {
-        return WorkManagerImpl.getInstance(getApplicationContext()).getWorkDatabase();
-    }
-
-    /**
-     * @return The instance of {@link TaskExecutor}.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @VisibleForTesting
-    @NonNull
-    @Override
-    public TaskExecutor getTaskExecutor() {
-        return WorkManagerImpl.getInstance(getApplicationContext()).getWorkTaskExecutor();
-    }
-
-    /**
-     * @return The instance of {@link Trackers}.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @VisibleForTesting
-    @NonNull
-    public Trackers getTrackers() {
-        return WorkManagerImpl.getInstance(getApplicationContext()).getTrackers();
-    }
-
-    /**
-     * @return The {@link Worker} used for delegated work
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @VisibleForTesting
-    @Nullable
-    public ListenableWorker getDelegate() {
-        return mDelegate;
-    }
-
-    @Override
-    public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
+    override fun onAllConstraintsMet(workSpecIds: List<String>) {
         // WorkConstraintTracker notifies on the main thread. So we don't want to trampoline
         // between the background thread and the main thread in this case.
     }
 
-    @Override
-    public void onAllConstraintsNotMet(@NonNull List<String> workSpecIds) {
+    override fun onAllConstraintsNotMet(workSpecIds: List<String>) {
         // If at any point, constraints are not met mark it so we can retry the work.
-        Logger.get().debug(TAG, "Constraints changed for " + workSpecIds);
-        synchronized (mLock) {
-            mAreConstraintsUnmet = true;
-        }
+        Logger.get().debug(TAG, "Constraints changed for $workSpecIds")
+        synchronized(lock) { areConstraintsUnmet = true }
     }
-}
\ No newline at end of file
+}
+
+private fun SettableFuture<Result>.setFailed() = set(Result.failure())
+private fun SettableFuture<Result>.setRetry() = set(Result.retry())
+
+private val TAG = Logger.tagWithPrefix("ConstraintTrkngWrkr")
+
+/**
+ * The `className` of the [androidx.work.Worker] to delegate to.
+ */
+internal const val ARGUMENT_CLASS_NAME =
+    "androidx.work.impl.workers.ConstraintTrackingWorker.ARGUMENT_CLASS_NAME"
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/workers/DiagnosticsWorker.kt b/work/work-runtime/src/main/java/androidx/work/impl/workers/DiagnosticsWorker.kt
index 3f20289..7755b70 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/workers/DiagnosticsWorker.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/workers/DiagnosticsWorker.kt
@@ -13,122 +13,73 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package androidx.work.impl.workers
 
-package androidx.work.impl.workers;
+import android.content.Context
+import android.os.Build
+import androidx.work.Logger
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import androidx.work.impl.Scheduler
+import androidx.work.impl.WorkManagerImpl
+import androidx.work.impl.model.SystemIdInfoDao
+import androidx.work.impl.model.WorkNameDao
+import androidx.work.impl.model.WorkSpec
+import androidx.work.impl.model.WorkTagDao
+import java.util.concurrent.TimeUnit
 
-import static androidx.work.impl.Scheduler.MAX_GREEDY_SCHEDULER_LIMIT;
-
-import android.content.Context;
-import android.os.Build;
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.work.Logger;
-import androidx.work.Worker;
-import androidx.work.WorkerParameters;
-import androidx.work.impl.WorkDatabase;
-import androidx.work.impl.WorkManagerImpl;
-import androidx.work.impl.model.SystemIdInfo;
-import androidx.work.impl.model.SystemIdInfoDao;
-import androidx.work.impl.model.WorkNameDao;
-import androidx.work.impl.model.WorkSpec;
-import androidx.work.impl.model.WorkSpecDao;
-import androidx.work.impl.model.WorkTagDao;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/**
- * The {@link androidx.work.Worker} which dumps diagnostic information.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class DiagnosticsWorker extends Worker {
-
-    private static final String TAG = Logger.tagWithPrefix("DiagnosticsWrkr");
-
-    public DiagnosticsWorker(@NonNull Context context, @NonNull WorkerParameters parameters) {
-        super(context, parameters);
-    }
-
-    @NonNull
-    @Override
-    public Result doWork() {
-        WorkManagerImpl workManager = WorkManagerImpl.getInstance(getApplicationContext());
-        WorkDatabase database = workManager.getWorkDatabase();
-        WorkSpecDao workSpecDao = database.workSpecDao();
-        WorkNameDao workNameDao = database.workNameDao();
-        WorkTagDao workTagDao = database.workTagDao();
-        SystemIdInfoDao systemIdInfoDao = database.systemIdInfoDao();
-        long startAt = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1);
-        List<WorkSpec> completed = workSpecDao.getRecentlyCompletedWork(startAt);
-        List<WorkSpec> running = workSpecDao.getRunningWork();
-        List<WorkSpec> enqueued = workSpecDao.getAllEligibleWorkSpecsForScheduling(
-                MAX_GREEDY_SCHEDULER_LIMIT);
-
-        if (completed != null && !completed.isEmpty()) {
-            Logger.get().info(TAG, "Recently completed work:\n\n");
-            Logger.get().info(TAG,
-                    workSpecRows(workNameDao, workTagDao, systemIdInfoDao, completed));
+internal class DiagnosticsWorker(context: Context, parameters: WorkerParameters) :
+    Worker(context, parameters) {
+    override fun doWork(): Result {
+        val workManager = WorkManagerImpl.getInstance(applicationContext)
+        val database = workManager.workDatabase
+        val workSpecDao = database.workSpecDao()
+        val workNameDao = database.workNameDao()
+        val workTagDao = database.workTagDao()
+        val systemIdInfoDao = database.systemIdInfoDao()
+        val startAt = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)
+        val completed = workSpecDao.getRecentlyCompletedWork(startAt)
+        val running = workSpecDao.getRunningWork()
+        val enqueued = workSpecDao.getAllEligibleWorkSpecsForScheduling(
+            Scheduler.MAX_GREEDY_SCHEDULER_LIMIT
+        )
+        if (completed.isNotEmpty()) {
+            Logger.get().info(TAG, "Recently completed work:\n\n")
+            Logger.get().info(
+                TAG, workSpecRows(workNameDao, workTagDao, systemIdInfoDao, completed)
+            )
         }
-        if (running != null && !running.isEmpty()) {
-            Logger.get().info(TAG, "Running work:\n\n");
-            Logger.get().info(TAG, workSpecRows(workNameDao, workTagDao, systemIdInfoDao, running));
+        if (running.isNotEmpty()) {
+            Logger.get().info(TAG, "Running work:\n\n")
+            Logger.get().info(TAG, workSpecRows(workNameDao, workTagDao, systemIdInfoDao, running))
         }
-        if (enqueued != null && !enqueued.isEmpty()) {
-            Logger.get().info(TAG, "Enqueued work:\n\n");
-            Logger.get().info(TAG,
-                    workSpecRows(workNameDao, workTagDao, systemIdInfoDao, enqueued));
+        if (enqueued.isNotEmpty()) {
+            Logger.get().info(TAG, "Enqueued work:\n\n")
+            Logger.get().info(TAG, workSpecRows(workNameDao, workTagDao, systemIdInfoDao, enqueued))
         }
-        return Result.success();
-    }
-
-    @NonNull
-    private static String workSpecRows(
-            @NonNull WorkNameDao workNameDao,
-            @NonNull WorkTagDao workTagDao,
-            @NonNull SystemIdInfoDao systemIdInfoDao,
-            @NonNull List<WorkSpec> workSpecs) {
-
-        StringBuilder sb = new StringBuilder();
-        String systemIdHeader = Build.VERSION.SDK_INT >= 23 ? "Job Id" : "Alarm Id";
-        String header = String.format("\n Id \t Class Name\t %s\t State\t Unique Name\t Tags\t",
-                systemIdHeader);
-        sb.append(header);
-        for (WorkSpec workSpec : workSpecs) {
-            Integer systemId = null;
-            SystemIdInfo info = systemIdInfoDao.getSystemIdInfo(workSpec.id);
-            if (info != null) {
-                systemId = info.systemId;
-            }
-            List<String> names = workNameDao.getNamesForWorkSpecId(workSpec.id);
-            List<String> tags = workTagDao.getTagsForWorkSpecId(workSpec.id);
-            sb.append(workSpecRow(
-                    workSpec,
-                    TextUtils.join(",", names),
-                    systemId,
-                    TextUtils.join(",", tags)
-            ));
-        }
-        return sb.toString();
-    }
-
-    @NonNull
-    private static String workSpecRow(
-            @NonNull WorkSpec workSpec,
-            @Nullable String name,
-            @Nullable Integer systemId,
-            @NonNull String tags) {
-        return String.format(
-                "\n%s\t %s\t %s\t %s\t %s\t %s\t",
-                workSpec.id,
-                workSpec.workerClassName,
-                systemId,
-                workSpec.state.name(),
-                name,
-                tags);
+        return Result.success()
     }
 }
+
+private val TAG = Logger.tagWithPrefix("DiagnosticsWrkr")
+
+private fun workSpecRows(
+    workNameDao: WorkNameDao,
+    workTagDao: WorkTagDao,
+    systemIdInfoDao: SystemIdInfoDao,
+    workSpecs: List<WorkSpec>
+) = buildString {
+    val systemIdHeader = if (Build.VERSION.SDK_INT >= 23) "Job Id" else "Alarm Id"
+    val header = "\n Id \t Class Name\t ${systemIdHeader}\t State\t Unique Name\t Tags\t"
+    append(header)
+    workSpecs.forEach { workSpec ->
+        val systemId = systemIdInfoDao.getSystemIdInfo(workSpec.id)?.systemId
+        val names = workNameDao.getNamesForWorkSpecId(workSpec.id).joinToString(",")
+        val tags = workTagDao.getTagsForWorkSpecId(workSpec.id).joinToString(",")
+        append(workSpecRow(workSpec, names, systemId, tags))
+    }
+}
+
+private fun workSpecRow(workSpec: WorkSpec, name: String, systemId: Int?, tags: String) =
+    "\n${workSpec.id}\t ${workSpec.workerClassName}\t $systemId\t " +
+        "${workSpec.state.name}\t $name\t $tags\t"