Merge "Add ability to query when cancelAll was last called." into pi-preview1-androidx-dev
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 c1daf54..7168600 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
@@ -36,6 +36,7 @@
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.emptyCollectionOf;
+import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.isIn;
import static org.hamcrest.Matchers.isOneOf;
@@ -57,6 +58,7 @@
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -80,6 +82,8 @@
import androidx.work.impl.model.WorkSpecDao;
import androidx.work.impl.model.WorkTag;
import androidx.work.impl.model.WorkTagDao;
+import androidx.work.impl.utils.CancelWorkRunnable;
+import androidx.work.impl.utils.Preferences;
import androidx.work.impl.utils.taskexecutor.InstantTaskExecutorRule;
import androidx.work.impl.workers.ConstraintTrackingWorker;
import androidx.work.worker.InfiniteTestWorker;
@@ -1285,6 +1289,49 @@
}
@Test
+ @MediumTest
+ public void testCancelAllWork_updatesLastCancelAllTime() {
+ Preferences preferences = new Preferences(InstrumentationRegistry.getTargetContext());
+ preferences.setLastCancelAllTimeMillis(0L);
+
+ OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
+ insertWorkSpecAndTags(work);
+
+ CancelWorkRunnable.forAll(mWorkManagerImpl).run();
+
+ assertThat(preferences.getLastCancelAllTimeMillis(), is(greaterThan(0L)));
+ }
+
+ @Test
+ @SmallTest
+ @SuppressWarnings("unchecked")
+ public void testCancelAllWork_updatesLastCancelAllTimeLiveData() throws InterruptedException {
+ Preferences preferences = new Preferences(InstrumentationRegistry.getTargetContext());
+ preferences.setLastCancelAllTimeMillis(0L);
+
+ TestLifecycleOwner testLifecycleOwner = new TestLifecycleOwner();
+ LiveData<Long> cancelAllTimeLiveData = mWorkManagerImpl.getLastCancelAllTimeMillis();
+ Observer<Long> mockObserver = mock(Observer.class);
+ cancelAllTimeLiveData.observe(testLifecycleOwner, mockObserver);
+
+ ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
+ verify(mockObserver).onChanged(captor.capture());
+ assertThat(captor.getValue(), is(0L));
+
+ OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
+ insertWorkSpecAndTags(work);
+
+ clearInvocations(mockObserver);
+ CancelWorkRunnable.forAll(mWorkManagerImpl).run();
+
+ Thread.sleep(1000L);
+ verify(mockObserver).onChanged(captor.capture());
+ assertThat(captor.getValue(), is(greaterThan(0L)));
+
+ cancelAllTimeLiveData.removeObservers(testLifecycleOwner);
+ }
+
+ @Test
@SmallTest
public void testSynchronousCancelAndGetStatus() {
OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
diff --git a/work/workmanager/src/main/java/androidx/work/SynchronousWorkManager.java b/work/workmanager/src/main/java/androidx/work/SynchronousWorkManager.java
index 3359905..f01e363 100644
--- a/work/workmanager/src/main/java/androidx/work/SynchronousWorkManager.java
+++ b/work/workmanager/src/main/java/androidx/work/SynchronousWorkManager.java
@@ -118,6 +118,17 @@
void cancelAllWorkSync();
/**
+ * Gets the timestamp of the last time all work was cancelled in a synchronous fashion. This
+ * method is intended for use by library and module developers who have dependent data in their
+ * own repository that must be updated or deleted in case someone cancels their work without
+ * their prior knowledge.
+ *
+ * @return The timestamp in milliseconds when a method that cancelled all work was last invoked
+ */
+ @WorkerThread
+ long getLastCancelAllTimeMillisSync();
+
+ /**
* Gets the {@link WorkStatus} of a given work id in a synchronous fashion. This method is
* expected to be called from a background thread.
*
diff --git a/work/workmanager/src/main/java/androidx/work/WorkManager.java b/work/workmanager/src/main/java/androidx/work/WorkManager.java
index 00a41d3..83891dc 100644
--- a/work/workmanager/src/main/java/androidx/work/WorkManager.java
+++ b/work/workmanager/src/main/java/androidx/work/WorkManager.java
@@ -288,6 +288,16 @@
public abstract void cancelAllWork();
/**
+ * Gets a {@link LiveData} of the last time all work was cancelled. This method is intended for
+ * use by library and module developers who have dependent data in their own repository that
+ * must be updated or deleted in case someone cancels their work without their prior knowledge.
+ *
+ * @return A {@link LiveData} of the timestamp in milliseconds when method that cancelled all
+ * work was last invoked
+ */
+ public abstract LiveData<Long> getLastCancelAllTimeMillis();
+
+ /**
* Gets a {@link LiveData} of the {@link WorkStatus} for a given work id.
*
* @param id The id of the work
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java b/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
index 3d61cae..5b659d1 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
@@ -42,6 +42,7 @@
import androidx.work.impl.utils.CancelWorkRunnable;
import androidx.work.impl.utils.ForceStopRunnable;
import androidx.work.impl.utils.LiveDataUtils;
+import androidx.work.impl.utils.Preferences;
import androidx.work.impl.utils.StartWorkRunnable;
import androidx.work.impl.utils.StopWorkRunnable;
import androidx.work.impl.utils.taskexecutor.TaskExecutor;
@@ -69,6 +70,7 @@
private TaskExecutor mTaskExecutor;
private List<Scheduler> mSchedulers;
private Processor mProcessor;
+ private Preferences mPreferences;
private static WorkManagerImpl sDelegatedInstance = null;
private static WorkManagerImpl sDefaultInstance = null;
@@ -167,12 +169,22 @@
mWorkDatabase,
getSchedulers(),
configuration.getExecutor());
+ mPreferences = new Preferences(mContext);
// Checks for app force stops.
mTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}
/**
+ * @return The application {@link Context} associated with this WorkManager.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public Context getApplicationContext() {
+ return mContext;
+ }
+
+ /**
* @return The {@link WorkDatabase} instance associated with this WorkManager.
* @hide
*/
@@ -345,6 +357,16 @@
}
@Override
+ public LiveData<Long> getLastCancelAllTimeMillis() {
+ return mPreferences.getLastCancelAllTimeMillisLiveData();
+ }
+
+ @Override
+ public long getLastCancelAllTimeMillisSync() {
+ return mPreferences.getLastCancelAllTimeMillis();
+ }
+
+ @Override
public LiveData<WorkStatus> getStatusById(@NonNull UUID id) {
WorkSpecDao dao = mWorkDatabase.workSpecDao();
LiveData<List<WorkSpec.WorkStatusPojo>> inputLiveData =
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.java b/work/workmanager/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.java
index 46f1b45..e117c95 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/CancelWorkRunnable.java
@@ -176,6 +176,9 @@
cancel(workManagerImpl, workSpecId);
}
workDatabase.setTransactionSuccessful();
+ // Update the preferences
+ new Preferences(workManagerImpl.getApplicationContext())
+ .setLastCancelAllTimeMillis(System.currentTimeMillis());
} finally {
workDatabase.endTransaction();
}
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/Preferences.java b/work/workmanager/src/main/java/androidx/work/impl/utils/Preferences.java
new file mode 100644
index 0000000..762f6b0
--- /dev/null
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/Preferences.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2018 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.impl.utils;
+
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.MutableLiveData;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.annotation.RestrictTo;
+
+/**
+ * Preferences for WorkManager.
+ *
+ * TODO: Migrate all preferences, including IdGenerator, to this file.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class Preferences {
+
+ private static final String PREFERENCES_FILE_NAME = "androidx.work.util.preferences";
+
+ private static final String KEY_LAST_CANCEL_ALL_TIME_MS = "last_cancel_all_time_ms";
+
+ private SharedPreferences mSharedPreferences;
+
+ public Preferences(Context context) {
+ mSharedPreferences =
+ context.getSharedPreferences(PREFERENCES_FILE_NAME, Context.MODE_PRIVATE);
+ }
+
+ /**
+ * @return The last time (in milliseconds) a {@code cancelAll} method was called
+ */
+ public long getLastCancelAllTimeMillis() {
+ return mSharedPreferences.getLong(KEY_LAST_CANCEL_ALL_TIME_MS, 0L);
+ }
+
+ /**
+ * @return A {@link LiveData} of the last time (in milliseconds) a {@code cancelAll} method was
+ * called
+ */
+ public LiveData<Long> getLastCancelAllTimeMillisLiveData() {
+ return new LastCancelAllLiveData(mSharedPreferences);
+ }
+
+ /**
+ * Sets the last time a {@code cancelAll} method was called
+ *
+ * @param timeMillis The time a {@code cancelAll} method was called (in milliseconds)
+ */
+ public void setLastCancelAllTimeMillis(long timeMillis) {
+ mSharedPreferences.edit().putLong(KEY_LAST_CANCEL_ALL_TIME_MS, timeMillis).apply();
+ }
+
+ /**
+ * A {@link android.arch.lifecycle.LiveData} that responds to changes in
+ * {@link SharedPreferences} for the {@code lastCancelAllTime} value.
+ */
+ private static class LastCancelAllLiveData extends MutableLiveData<Long>
+ implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ private SharedPreferences mSharedPreferences;
+ private long mLastCancelAllTimeMillis;
+
+ LastCancelAllLiveData(SharedPreferences sharedPreferences) {
+ mSharedPreferences = sharedPreferences;
+ mLastCancelAllTimeMillis = mSharedPreferences.getLong(KEY_LAST_CANCEL_ALL_TIME_MS, 0L);
+ postValue(mLastCancelAllTimeMillis);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (KEY_LAST_CANCEL_ALL_TIME_MS.equals(key)) {
+ long lastCancelAllTimeMillis = sharedPreferences.getLong(key, 0L);
+ if (mLastCancelAllTimeMillis != lastCancelAllTimeMillis) {
+ mLastCancelAllTimeMillis = lastCancelAllTimeMillis;
+ setValue(mLastCancelAllTimeMillis);
+ }
+ }
+ }
+
+ @Override
+ protected void onActive() {
+ super.onActive();
+ mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ protected void onInactive() {
+ super.onInactive();
+ mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
+ }
+ }
+}