Constraints.java
/*
* 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;
import static androidx.work.NetworkType.NOT_REQUIRED;
import android.net.Uri;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.room.ColumnInfo;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
/**
* A specification of the requirements that need to be met before a {@link WorkRequest} can run. By
* default, WorkRequests do not have any requirements and can run immediately. By adding
* requirements, you can make sure that work only runs in certain situations - for example, when you
* have an unmetered network and are charging.
*/
public final class Constraints {
/**
* Represents a Constraints object with no requirements.
*/
public static final Constraints NONE = new Constraints.Builder().build();
// NOTE: this is effectively a @NonNull, but changing the annotation would result in a really
// annoying database migration that we can deal with later.
@ColumnInfo(name = "required_network_type")
private NetworkType mRequiredNetworkType = NOT_REQUIRED;
@ColumnInfo(name = "requires_charging")
private boolean mRequiresCharging;
@ColumnInfo(name = "requires_device_idle")
private boolean mRequiresDeviceIdle;
@ColumnInfo(name = "requires_battery_not_low")
private boolean mRequiresBatteryNotLow;
@ColumnInfo(name = "requires_storage_not_low")
private boolean mRequiresStorageNotLow;
@ColumnInfo(name = "trigger_content_update_delay")
private long mTriggerContentUpdateDelay = -1;
@ColumnInfo(name = "trigger_max_content_delay")
private long mTriggerMaxContentDelay = -1;
// NOTE: this is effectively a @NonNull, but changing the annotation would result in a really
// annoying database migration that we can deal with later.
@ColumnInfo(name = "content_uri_triggers")
private ContentUriTriggers mContentUriTriggers = new ContentUriTriggers();
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public Constraints() { // stub required for room
}
Constraints(Builder builder) {
mRequiresCharging = builder.mRequiresCharging;
mRequiresDeviceIdle = Build.VERSION.SDK_INT >= 23 && builder.mRequiresDeviceIdle;
mRequiredNetworkType = builder.mRequiredNetworkType;
mRequiresBatteryNotLow = builder.mRequiresBatteryNotLow;
mRequiresStorageNotLow = builder.mRequiresStorageNotLow;
if (Build.VERSION.SDK_INT >= 24) {
mContentUriTriggers = builder.mContentUriTriggers;
mTriggerContentUpdateDelay = builder.mTriggerContentUpdateDelay;
mTriggerMaxContentDelay = builder.mTriggerContentMaxDelay;
}
}
public Constraints(@NonNull Constraints other) {
mRequiresCharging = other.mRequiresCharging;
mRequiresDeviceIdle = other.mRequiresDeviceIdle;
mRequiredNetworkType = other.mRequiredNetworkType;
mRequiresBatteryNotLow = other.mRequiresBatteryNotLow;
mRequiresStorageNotLow = other.mRequiresStorageNotLow;
mContentUriTriggers = other.mContentUriTriggers;
}
public @NonNull NetworkType getRequiredNetworkType() {
return mRequiredNetworkType;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void setRequiredNetworkType(@NonNull NetworkType requiredNetworkType) {
mRequiredNetworkType = requiredNetworkType;
}
/**
* @return {@code true} if the work should only execute while the device is charging
*/
public boolean requiresCharging() {
return mRequiresCharging;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void setRequiresCharging(boolean requiresCharging) {
mRequiresCharging = requiresCharging;
}
/**
* @return {@code true} if the work should only execute while the device is idle
*/
@RequiresApi(23)
public boolean requiresDeviceIdle() {
return mRequiresDeviceIdle;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(23)
public void setRequiresDeviceIdle(boolean requiresDeviceIdle) {
mRequiresDeviceIdle = requiresDeviceIdle;
}
/**
* @return {@code true} if the work should only execute when the battery isn't low
*/
public boolean requiresBatteryNotLow() {
return mRequiresBatteryNotLow;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void setRequiresBatteryNotLow(boolean requiresBatteryNotLow) {
mRequiresBatteryNotLow = requiresBatteryNotLow;
}
/**
* @return {@code true} if the work should only execute when the storage isn't low
*/
public boolean requiresStorageNotLow() {
return mRequiresStorageNotLow;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void setRequiresStorageNotLow(boolean requiresStorageNotLow) {
mRequiresStorageNotLow = requiresStorageNotLow;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public long getTriggerContentUpdateDelay() {
return mTriggerContentUpdateDelay;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void setTriggerContentUpdateDelay(long triggerContentUpdateDelay) {
mTriggerContentUpdateDelay = triggerContentUpdateDelay;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public long getTriggerMaxContentDelay() {
return mTriggerMaxContentDelay;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void setTriggerMaxContentDelay(long triggerMaxContentDelay) {
mTriggerMaxContentDelay = triggerMaxContentDelay;
}
/**
* Needed by Room.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(24)
public void setContentUriTriggers(@Nullable ContentUriTriggers mContentUriTriggers) {
this.mContentUriTriggers = mContentUriTriggers;
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(24)
public @NonNull ContentUriTriggers getContentUriTriggers() {
return mContentUriTriggers;
}
/**
* @return {@code true} if {@link ContentUriTriggers} is not empty
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(24)
public boolean hasContentUriTriggers() {
return mContentUriTriggers.size() > 0;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Constraints that = (Constraints) o;
if (mRequiresCharging != that.mRequiresCharging) return false;
if (mRequiresDeviceIdle != that.mRequiresDeviceIdle) return false;
if (mRequiresBatteryNotLow != that.mRequiresBatteryNotLow) return false;
if (mRequiresStorageNotLow != that.mRequiresStorageNotLow) return false;
if (mTriggerContentUpdateDelay != that.mTriggerContentUpdateDelay) return false;
if (mTriggerMaxContentDelay != that.mTriggerMaxContentDelay) return false;
if (mRequiredNetworkType != that.mRequiredNetworkType) return false;
return mContentUriTriggers.equals(that.mContentUriTriggers);
}
@Override
public int hashCode() {
int result = mRequiredNetworkType.hashCode();
result = 31 * result + (mRequiresCharging ? 1 : 0);
result = 31 * result + (mRequiresDeviceIdle ? 1 : 0);
result = 31 * result + (mRequiresBatteryNotLow ? 1 : 0);
result = 31 * result + (mRequiresStorageNotLow ? 1 : 0);
result = 31 * result + (int) (mTriggerContentUpdateDelay ^ (mTriggerContentUpdateDelay
>>> 32));
result = 31 * result + (int) (mTriggerMaxContentDelay ^ (mTriggerMaxContentDelay >>> 32));
result = 31 * result + mContentUriTriggers.hashCode();
return result;
}
/**
* A Builder for a {@link Constraints} object.
*/
public static final class Builder {
boolean mRequiresCharging = false;
boolean mRequiresDeviceIdle = false;
NetworkType mRequiredNetworkType = NOT_REQUIRED;
boolean mRequiresBatteryNotLow = false;
boolean mRequiresStorageNotLow = false;
// Same defaults as JobInfo
long mTriggerContentUpdateDelay = -1;
long mTriggerContentMaxDelay = -1;
ContentUriTriggers mContentUriTriggers = new ContentUriTriggers();
/**
* Sets whether device should be charging for the {@link WorkRequest} to run. The
* default value is {@code false}.
*
* @param requiresCharging {@code true} if device must be charging for the work to run
* @return The current {@link Builder}
*/
public @NonNull Builder setRequiresCharging(boolean requiresCharging) {
this.mRequiresCharging = requiresCharging;
return this;
}
/**
* Sets whether device should be idle for the {@link WorkRequest} to run. The default
* value is {@code false}.
*
* @param requiresDeviceIdle {@code true} if device must be idle for the work to run
* @return The current {@link Builder}
*/
@RequiresApi(23)
public @NonNull Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
this.mRequiresDeviceIdle = requiresDeviceIdle;
return this;
}
/**
* Sets whether device should have a particular {@link NetworkType} for the
* {@link WorkRequest} to run. The default value is {@link NetworkType#NOT_REQUIRED}.
*
* @param networkType The type of network required for the work to run
* @return The current {@link Builder}
*/
public @NonNull Builder setRequiredNetworkType(@NonNull NetworkType networkType) {
this.mRequiredNetworkType = networkType;
return this;
}
/**
* Sets whether device battery should be at an acceptable level for the
* {@link WorkRequest} to run. The default value is {@code false}.
*
* @param requiresBatteryNotLow {@code true} if the battery should be at an acceptable level
* for the work to run
* @return The current {@link Builder}
*/
public @NonNull Builder setRequiresBatteryNotLow(boolean requiresBatteryNotLow) {
this.mRequiresBatteryNotLow = requiresBatteryNotLow;
return this;
}
/**
* Sets whether the device's available storage should be at an acceptable level for the
* {@link WorkRequest} to run. The default value is {@code false}.
*
* @param requiresStorageNotLow {@code true} if the available storage should not be below a
* a critical threshold for the work to run
* @return The current {@link Builder}
*/
public @NonNull Builder setRequiresStorageNotLow(boolean requiresStorageNotLow) {
this.mRequiresStorageNotLow = requiresStorageNotLow;
return this;
}
/**
* Sets whether the {@link WorkRequest} should run when a local {@code content:} {@link Uri}
* is updated. This functionality is identical to the one found in {@code JobScheduler} and
* is described in
* {@code JobInfo.Builder#addTriggerContentUri(android.app.job.JobInfo.TriggerContentUri)}.
*
* @param uri The local {@code content:} Uri to observe
* @param triggerForDescendants {@code true} if any changes in descendants cause this
* {@link WorkRequest} to run
* @return The current {@link Builder}
*/
@RequiresApi(24)
public @NonNull Builder addContentUriTrigger(
@NonNull Uri uri,
boolean triggerForDescendants) {
mContentUriTriggers.add(uri, triggerForDescendants);
return this;
}
/**
* Sets the delay that is allowed from the time a {@code content:} {@link Uri}
* change is detected to the time when the {@link WorkRequest} is scheduled. If there are
* more changes during this time, the delay will be reset to the start of the most recent
* change. This functionality is identical to the one found in {@code JobScheduler} and
* is described in {@code JobInfo.Builder#setTriggerContentUpdateDelay(long)}.
*
* @param duration The length of the delay in {@code timeUnit} units
* @param timeUnit The units of time for {@code duration}
* @return The current {@link Builder}
*/
@RequiresApi(24)
@NonNull
public Builder setTriggerContentUpdateDelay(
long duration,
@NonNull TimeUnit timeUnit) {
mTriggerContentUpdateDelay = timeUnit.toMillis(duration);
return this;
}
/**
* Sets the delay that is allowed from the time a {@code content:} {@link Uri} change
* is detected to the time when the {@link WorkRequest} is scheduled. If there are more
* changes during this time, the delay will be reset to the start of the most recent change.
* This functionality is identical to the one found in {@code JobScheduler} and
* is described in {@code JobInfo.Builder#setTriggerContentUpdateDelay(long)}.
*
* @param duration The length of the delay
* @return The current {@link Builder}
*/
@RequiresApi(26)
@NonNull
public Builder setTriggerContentUpdateDelay(Duration duration) {
mTriggerContentUpdateDelay = duration.toMillis();
return this;
}
/**
* Sets the maximum delay that is allowed from the first time a {@code content:}
* {@link Uri} change is detected to the time when the {@link WorkRequest} is scheduled.
* This functionality is identical to the one found in {@code JobScheduler} and
* is described in {@code JobInfo.Builder#setTriggerContentMaxDelay(long)}.
*
* @param duration The length of the delay in {@code timeUnit} units
* @param timeUnit The units of time for {@code duration}
* @return The current {@link Builder}
*/
@RequiresApi(24)
@NonNull
public Builder setTriggerContentMaxDelay(
long duration,
@NonNull TimeUnit timeUnit) {
mTriggerContentMaxDelay = timeUnit.toMillis(duration);
return this;
}
/**
* Sets the maximum delay that is allowed from the first time a {@code content:} {@link Uri}
* change is detected to the time when the {@link WorkRequest} is scheduled. This
* functionality is identical to the one found in {@code JobScheduler} and is described
* in {@code JobInfo.Builder#setTriggerContentMaxDelay(long)}.
*
* @param duration The length of the delay
* @return The current {@link Builder}
*/
@RequiresApi(26)
@NonNull
public Builder setTriggerContentMaxDelay(Duration duration) {
mTriggerContentMaxDelay = duration.toMillis();
return this;
}
/**
* Generates the {@link Constraints} from this Builder.
*
* @return The {@link Constraints} specified by this Builder
*/
public @NonNull Constraints build() {
return new Constraints(this);
}
}
}