Added storage not low as download requirement

Added monitoring storage levels in RequirementsWatcher
Added dependency on extension-workmanager to the demo app to be able to test with WorkManagerScheduler
Added getSupportedRequirements method to Scheduler interface
Implemented getSupportedRequirements for schedulers
This commit is contained in:
Joris de Groot 2020-05-19 15:22:43 +02:00
parent 0de9c007af
commit 86b31e2954
8 changed files with 103 additions and 1 deletions

View File

@ -70,6 +70,7 @@ dependencies {
implementation project(modulePrefix + 'library-hls') implementation project(modulePrefix + 'library-hls')
implementation project(modulePrefix + 'library-smoothstreaming') implementation project(modulePrefix + 'library-smoothstreaming')
implementation project(modulePrefix + 'library-ui') implementation project(modulePrefix + 'library-ui')
withExtensionsImplementation project(path: modulePrefix + 'extension-workmanager')
withExtensionsImplementation project(path: modulePrefix + 'extension-av1') withExtensionsImplementation project(path: modulePrefix + 'extension-av1')
withExtensionsImplementation project(path: modulePrefix + 'extension-ffmpeg') withExtensionsImplementation project(path: modulePrefix + 'extension-ffmpeg')
withExtensionsImplementation project(path: modulePrefix + 'extension-flac') withExtensionsImplementation project(path: modulePrefix + 'extension-flac')

View File

@ -96,6 +96,19 @@ public final class JobDispatcherScheduler implements Scheduler {
return result == FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS; return result == FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS;
} }
@Override
public Requirements getSupportedRequirements(Requirements requirements) {
Requirements supportedRequirements = requirements;
if (requirements.isStorageNotLowRequired()) {
Log.w(TAG, "Storage not low requirement not supported on the JobDispatcherScheduler "
+ "Requirement removed.");
int newRequirements =
supportedRequirements.getRequirements() ^ Requirements.DEVICE_STORAGE_NOT_LOW;
supportedRequirements = new Requirements(newRequirements);
}
return supportedRequirements;
}
private static Job buildJob( private static Job buildJob(
FirebaseJobDispatcher dispatcher, FirebaseJobDispatcher dispatcher,
Requirements requirements, Requirements requirements,

View File

@ -87,6 +87,12 @@ public final class WorkManagerScheduler implements Scheduler {
if (requirements.isIdleRequired() && Util.SDK_INT >= 23) { if (requirements.isIdleRequired() && Util.SDK_INT >= 23) {
setRequiresDeviceIdle(builder); setRequiresDeviceIdle(builder);
} else if (requirements.isIdleRequired()) {
Log.w(TAG, "Is idle requirement is only available on API 23 and up.");
}
if (requirements.isStorageNotLowRequired()) {
builder.setRequiresStorageNotLow(true);
} }
return builder.build(); return builder.build();
@ -108,6 +114,19 @@ public final class WorkManagerScheduler implements Scheduler {
return builder.build(); return builder.build();
} }
@Override
public Requirements getSupportedRequirements(Requirements requirements) {
Requirements supportedRequirements = requirements;
if (requirements.isIdleRequired() && Util.SDK_INT < 23) {
Log.w(TAG, "Is idle requirement not supported on the WorkManagerScheduler on API below 23. "
+ "Requirement removed.");
int newRequirements =
supportedRequirements.getRequirements() ^ Requirements.DEVICE_IDLE;
supportedRequirements = new Requirements(newRequirements);
}
return supportedRequirements;
}
private static OneTimeWorkRequest buildWorkRequest(Constraints constraints, Data inputData) { private static OneTimeWorkRequest buildWorkRequest(Constraints constraints, Data inputData) {
OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(SchedulerWorker.class); OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(SchedulerWorker.class);

View File

@ -658,6 +658,10 @@ public abstract class DownloadService extends Service {
if (requirements == null) { if (requirements == null) {
Log.e(TAG, "Ignored SET_REQUIREMENTS: Missing " + KEY_REQUIREMENTS + " extra"); Log.e(TAG, "Ignored SET_REQUIREMENTS: Missing " + KEY_REQUIREMENTS + " extra");
} else { } else {
@Nullable Scheduler scheduler = getScheduler();
if (scheduler != null) {
requirements = scheduler.getSupportedRequirements(requirements);
}
downloadManager.setRequirements(requirements); downloadManager.setRequirements(requirements);
} }
break; break;

View File

@ -86,6 +86,21 @@ public final class PlatformScheduler implements Scheduler {
return true; return true;
} }
@Override
public Requirements getSupportedRequirements(Requirements requirements) {
Requirements supportedRequirements = requirements;
if (Util.SDK_INT < 26) {
if (requirements.isStorageNotLowRequired()) {
Log.w(TAG, "Storage not low requirement not supported on the PlatformScheduler"
+ "on API below 26. Requirement removed.");
int newRequirements =
supportedRequirements.getRequirements() ^ Requirements.DEVICE_STORAGE_NOT_LOW;
supportedRequirements = new Requirements(newRequirements);
}
}
return supportedRequirements;
}
// @RequiresPermission constructor annotation should ensure the permission is present. // @RequiresPermission constructor annotation should ensure the permission is present.
@SuppressWarnings("MissingPermission") @SuppressWarnings("MissingPermission")
private static JobInfo buildJobInfo( private static JobInfo buildJobInfo(

View File

@ -45,7 +45,7 @@ public final class Requirements implements Parcelable {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef( @IntDef(
flag = true, flag = true,
value = {NETWORK, NETWORK_UNMETERED, DEVICE_IDLE, DEVICE_CHARGING}) value = {NETWORK, NETWORK_UNMETERED, DEVICE_IDLE, DEVICE_CHARGING, DEVICE_STORAGE_NOT_LOW})
public @interface RequirementFlags {} public @interface RequirementFlags {}
/** Requirement that the device has network connectivity. */ /** Requirement that the device has network connectivity. */
@ -56,6 +56,8 @@ public final class Requirements implements Parcelable {
public static final int DEVICE_IDLE = 1 << 2; public static final int DEVICE_IDLE = 1 << 2;
/** Requirement that the device is charging. */ /** Requirement that the device is charging. */
public static final int DEVICE_CHARGING = 1 << 3; public static final int DEVICE_CHARGING = 1 << 3;
/** Requirement that the storage is not low. */
public static final int DEVICE_STORAGE_NOT_LOW = 1 << 4;
@RequirementFlags private final int requirements; @RequirementFlags private final int requirements;
@ -94,6 +96,10 @@ public final class Requirements implements Parcelable {
return (requirements & DEVICE_IDLE) != 0; return (requirements & DEVICE_IDLE) != 0;
} }
public boolean isStorageNotLowRequired() {
return (requirements & DEVICE_STORAGE_NOT_LOW) != 0;
}
/** /**
* Returns whether the requirements are met. * Returns whether the requirements are met.
* *
@ -119,6 +125,9 @@ public final class Requirements implements Parcelable {
if (isIdleRequired() && !isDeviceIdle(context)) { if (isIdleRequired() && !isDeviceIdle(context)) {
notMetRequirements |= DEVICE_IDLE; notMetRequirements |= DEVICE_IDLE;
} }
if (isStorageNotLowRequired() && !isStorageNotLow(context)) {
notMetRequirements |= DEVICE_STORAGE_NOT_LOW;
}
return notMetRequirements; return notMetRequirements;
} }
@ -162,6 +171,34 @@ public final class Requirements implements Parcelable {
: Util.SDK_INT >= 20 ? !powerManager.isInteractive() : !powerManager.isScreenOn(); : Util.SDK_INT >= 20 ? !powerManager.isInteractive() : !powerManager.isScreenOn();
} }
/**
* Implementation taken from the the WorkManager source.
* @see <a href="https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/work/workmanager/src/main/java/androidx/work/impl/constraints/trackers/StorageNotLowTracker.java">StorageNotLowTracker</a>
*/
private boolean isStorageNotLow(Context context) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
Intent intent = context.registerReceiver(null, intentFilter);
if (intent == null || intent.getAction() == null) {
// ACTION_DEVICE_STORAGE_LOW is a sticky broadcast that is removed when sufficient
// storage is available again. ACTION_DEVICE_STORAGE_OK is not sticky. So if we
// don't receive anything here, we can assume that the storage state is okay.
return true;
} else {
switch (intent.getAction()) {
case Intent.ACTION_DEVICE_STORAGE_OK:
return true;
case Intent.ACTION_DEVICE_STORAGE_LOW:
return false;
default:
// This should never happen because the intent filter is configured
// correctly.
return true;
}
}
}
private static boolean isInternetConnectivityValidated(ConnectivityManager connectivityManager) { private static boolean isInternetConnectivityValidated(ConnectivityManager connectivityManager) {
// It's possible to query NetworkCapabilities from API level 23, but RequirementsWatcher only // It's possible to query NetworkCapabilities from API level 23, but RequirementsWatcher only
// fires an event to update its Requirements when NetworkCapabilities change from API level 24. // fires an event to update its Requirements when NetworkCapabilities change from API level 24.

View File

@ -104,6 +104,10 @@ public final class RequirementsWatcher {
filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_OFF);
} }
} }
if (requirements.isStorageNotLowRequired()) {
filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
}
receiver = new DeviceStatusChangeReceiver(); receiver = new DeviceStatusChangeReceiver();
context.registerReceiver(receiver, filter, null, handler); context.registerReceiver(receiver, filter, null, handler);
return notMetRequirements; return notMetRequirements;

View File

@ -45,4 +45,13 @@ public interface Scheduler {
* @return Whether cancellation was successful. * @return Whether cancellation was successful.
*/ */
boolean cancel(); boolean cancel();
/**
* Checks if this {@link Scheduler} supports the provided {@link Requirements}. If all
* requirements are supported the same object is returned. If not all requirements are
* supported a new {@code Requirements} object is returned containing the supported requirements.
* @param requirements The requirements to check.
* @return The requirements supported by this scheduler.
*/
Requirements getSupportedRequirements(Requirements requirements);
} }