1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package androidx.work
17 
18 import android.annotation.SuppressLint
19 import android.net.NetworkRequest
20 import android.net.Uri
21 import android.os.Build
22 import androidx.annotation.RequiresApi
23 import androidx.annotation.RestrictTo
24 import androidx.room.ColumnInfo
25 import androidx.room.Ignore
26 import androidx.work.impl.utils.NetworkRequest30
27 import androidx.work.impl.utils.NetworkRequestCompat
28 import androidx.work.impl.utils.toMillisCompat
29 import java.time.Duration
30 import java.util.concurrent.TimeUnit
31 
32 /**
33  * A specification of the requirements that need to be met before a [WorkRequest] can run. By
34  * default, WorkRequests do not have any requirements and can run immediately. By adding
35  * requirements, you can make sure that work only runs in certain situations - for example, when you
36  * have an unmetered network and are charging.
37  */
38 class Constraints {
39     /** The type of network required for the work to run. */
40     @ColumnInfo(name = "required_network_type") val requiredNetworkType: NetworkType
41 
42     /**
43      * [NetworkRequest] required for work to run on. It is used only the API levels >= 28 (Android
44      * P). For the older API levels, [requiredNetworkType] will be used instead on the older
45      * platforms and this property will be `null`.
46      *
47      * `NetworkRequest`-s with `NetworkSpecifier` set aren't supported, as well as `NetworkRequest`
48      * with `setIncludeOtherUidNetworks` set. passed.
49      */
50     @get:RequiresApi(21) // NetworkRequest class is available since 21
51     val requiredNetworkRequest: NetworkRequest?
52         get() = requiredNetworkRequestCompat.networkRequest
53 
54     @ColumnInfo(name = "required_network_request", defaultValue = "x''")
55     internal val requiredNetworkRequestCompat: NetworkRequestCompat
56 
57     @ColumnInfo(name = "requires_charging") private val requiresCharging: Boolean
58 
59     @ColumnInfo(name = "requires_device_idle") private val requiresDeviceIdle: Boolean
60 
61     @ColumnInfo(name = "requires_battery_not_low") private val requiresBatteryNotLow: Boolean
62 
63     @ColumnInfo(name = "requires_storage_not_low") private val requiresStorageNotLow: Boolean
64 
65     /**
66      * The delay in milliseconds that is allowed from the time a `content:` [Uri] change is detected
67      * to the time when the [WorkRequest] is scheduled. If there are more changes during this time,
68      * the delay will be reset to the start of the most recent change. This functionality is
69      * identical to the one found in `JobScheduler` and is described in
70      * [android.app.job.JobInfo.Builder.setTriggerContentUpdateDelay]
71      */
72     @get:RequiresApi(24)
73     @ColumnInfo(name = "trigger_content_update_delay")
74     val contentTriggerUpdateDelayMillis: Long
75 
76     /**
77      * The maximum delay in milliseconds that is allowed from the first time a `content:` [Uri]
78      * change is detected to the time when the [WorkRequest] is scheduled. This functionality is
79      * identical to the one found in `JobScheduler` and is described in
80      * [android.app.job.JobInfo.Builder.setTriggerContentMaxDelay].
81      */
82     @get:RequiresApi(24)
83     @ColumnInfo(name = "trigger_max_content_delay")
84     val contentTriggerMaxDelayMillis: Long
85 
86     /**
87      * Set of [ContentUriTrigger]. [WorkRequest] will run when a local `content:` [Uri] of one of
88      * the triggers in the set is updated. This functionality is identical to the one found in
89      * `JobScheduler` and is described in [android.app.job.JobInfo.Builder.addTriggerContentUri].
90      */
91     @ColumnInfo(name = "content_uri_triggers")
92     @get:RequiresApi(24)
93     val contentUriTriggers: Set<ContentUriTrigger>
94 
95     /**
96      * Constructs [Constraints].
97      *
98      * @param requiredNetworkType The type of network required for the work to run. The default
99      *   value is [NetworkType.NOT_REQUIRED].
100      * @param requiresCharging whether device should be charging for the [WorkRequest] to run. The
101      *   default value is `false`.
102      * @param requiresBatteryNotLow whether device battery should be at an acceptable level for the
103      *   [WorkRequest] to run. The default value is `false`.
104      * @param requiresStorageNotLow whether the device's available storage should be at an
105      *   acceptable level for the [WorkRequest] to run. The default value is `false`.
106      */
107     @Ignore
108     @SuppressLint("NewApi")
109     constructor(
110         requiredNetworkType: NetworkType = NetworkType.NOT_REQUIRED,
111         requiresCharging: Boolean = false,
112         requiresBatteryNotLow: Boolean = false,
113         requiresStorageNotLow: Boolean = false,
114     ) : this(
115         requiredNetworkType = requiredNetworkType,
116         requiresCharging = requiresCharging,
117         requiresStorageNotLow = requiresStorageNotLow,
118         requiresBatteryNotLow = requiresBatteryNotLow,
119         requiresDeviceIdle = false
120     )
121 
122     /**
123      * Constructs [Constraints].
124      *
125      * @param requiredNetworkType The type of network required for the work to run. The default
126      *   value is [NetworkType.NOT_REQUIRED].
127      * @param requiresCharging whether device should be charging for the [WorkRequest] to run. The
128      *   default value is `false`.
129      * @param requiresDeviceIdle whether device should be idle for the [WorkRequest] to run. The
130      *   default value is `false`.
131      * @param requiresBatteryNotLow whether device battery should be at an acceptable level for the
132      *   [WorkRequest] to run. The default value is `false`.
133      * @param requiresStorageNotLow whether the device's available storage should be at an
134      *   acceptable level for the [WorkRequest] to run. The default value is `false`.
135      */
136     @Ignore
137     @SuppressLint("NewApi")
138     @RequiresApi(23) // requiresDeviceIdle is supported since API 23
139     constructor(
140         requiredNetworkType: NetworkType = NetworkType.NOT_REQUIRED,
141         requiresCharging: Boolean = false,
142         requiresDeviceIdle: Boolean = false,
143         requiresBatteryNotLow: Boolean = false,
144         requiresStorageNotLow: Boolean = false,
145     ) : this(
146         requiredNetworkType = requiredNetworkType,
147         requiresCharging = requiresCharging,
148         requiresDeviceIdle = requiresDeviceIdle,
149         requiresBatteryNotLow = requiresBatteryNotLow,
150         requiresStorageNotLow = requiresStorageNotLow,
151         contentTriggerUpdateDelayMillis = -1,
152     )
153 
154     /**
155      * Constructs [Constraints].
156      *
157      * @param requiredNetworkType The type of network required for the work to run. The default
158      *   value is [NetworkType.NOT_REQUIRED].
159      * @param requiresCharging whether device should be charging for the [WorkRequest] to run. The
160      *   default value is `false`.
161      * @param requiresDeviceIdle whether device should be idle for the [WorkRequest] to run. The
162      *   default value is `false`.
163      * @param requiresBatteryNotLow whether device battery should be at an acceptable level for the
164      *   [WorkRequest] to run. The default value is `false`.
165      * @param requiresStorageNotLow whether the device's available storage should be at an
166      *   acceptable level for the [WorkRequest] to run. The default value is `false`.
167      * @param contentTriggerUpdateDelayMillis the delay in milliseconds that is allowed from the
168      *   time a `content:` [Uri] change is detected to the time when the [WorkRequest] is scheduled.
169      *   If there are more changes during this time, the delay will be reset to the start of the
170      *   most recent change. This functionality is identical to the one found in `JobScheduler` and
171      *   is described in [android.app.job.JobInfo.Builder.setTriggerContentUpdateDelay]
172      * @param contentTriggerMaxDelayMillis the maximum delay in milliseconds that is allowed from
173      *   the first time a `content:` [Uri] change is detected to the time when the [WorkRequest] is
174      *   scheduled. This functionality is identical to the one found in `JobScheduler` and is
175      *   described in [android.app.job.JobInfo.Builder.setTriggerContentMaxDelay].
176      * @param contentUriTriggers set of [ContentUriTrigger]. [WorkRequest] will run when a local
177      *   `content:` [Uri] of one of the triggers in the set is updated. This functionality is
178      *   identical to the one found in `JobScheduler` and is described in
179      *   [android.app.job.JobInfo.Builder.addTriggerContentUri].
180      */
181     @Ignore
182     @RequiresApi(24)
183     constructor(
184         requiredNetworkType: NetworkType = NetworkType.NOT_REQUIRED,
185         requiresCharging: Boolean = false,
186         requiresDeviceIdle: Boolean = false,
187         requiresBatteryNotLow: Boolean = false,
188         requiresStorageNotLow: Boolean = false,
189         contentTriggerUpdateDelayMillis: Long = -1,
190         contentTriggerMaxDelayMillis: Long = -1,
191         contentUriTriggers: Set<ContentUriTrigger> = setOf(),
192     ) {
193         this.requiredNetworkRequestCompat = NetworkRequestCompat()
194         this.requiredNetworkType = requiredNetworkType
195         this.requiresCharging = requiresCharging
196         this.requiresDeviceIdle = requiresDeviceIdle
197         this.requiresBatteryNotLow = requiresBatteryNotLow
198         this.requiresStorageNotLow = requiresStorageNotLow
199         this.contentTriggerUpdateDelayMillis = contentTriggerUpdateDelayMillis
200         this.contentTriggerMaxDelayMillis = contentTriggerMaxDelayMillis
201         this.contentUriTriggers = contentUriTriggers
202     }
203 
204     internal constructor(
205         requiredNetworkRequestCompat: NetworkRequestCompat,
206         requiredNetworkType: NetworkType = NetworkType.NOT_REQUIRED,
207         requiresCharging: Boolean = false,
208         requiresDeviceIdle: Boolean = false,
209         requiresBatteryNotLow: Boolean = false,
210         requiresStorageNotLow: Boolean = false,
211         contentTriggerUpdateDelayMillis: Long = -1,
212         contentTriggerMaxDelayMillis: Long = -1,
213         contentUriTriggers: Set<ContentUriTrigger> = setOf(),
214     ) {
215         this.requiredNetworkRequestCompat = requiredNetworkRequestCompat
216         this.requiredNetworkType = requiredNetworkType
217         this.requiresCharging = requiresCharging
218         this.requiresDeviceIdle = requiresDeviceIdle
219         this.requiresBatteryNotLow = requiresBatteryNotLow
220         this.requiresStorageNotLow = requiresStorageNotLow
221         this.contentTriggerUpdateDelayMillis = contentTriggerUpdateDelayMillis
222         this.contentTriggerMaxDelayMillis = contentTriggerMaxDelayMillis
223         this.contentUriTriggers = contentUriTriggers
224     }
225 
226     @SuppressLint("NewApi") // just copy everything
227     constructor(other: Constraints) {
228         requiresCharging = other.requiresCharging
229         requiresDeviceIdle = other.requiresDeviceIdle
230         requiredNetworkRequestCompat = other.requiredNetworkRequestCompat
231         requiredNetworkType = other.requiredNetworkType
232         requiresBatteryNotLow = other.requiresBatteryNotLow
233         requiresStorageNotLow = other.requiresStorageNotLow
234         contentUriTriggers = other.contentUriTriggers
235         contentTriggerUpdateDelayMillis = other.contentTriggerUpdateDelayMillis
236         contentTriggerMaxDelayMillis = other.contentTriggerMaxDelayMillis
237     }
238 
239     /** @return `true` if the work should only execute while the device is charging */
requiresChargingnull240     fun requiresCharging(): Boolean {
241         return requiresCharging
242     }
243 
244     /** @return `true` if the work should only execute while the device is idle */
245     @RequiresApi(23)
requiresDeviceIdlenull246     fun requiresDeviceIdle(): Boolean {
247         return requiresDeviceIdle
248     }
249 
250     /** @return `true` if the work should only execute when the battery isn't low */
requiresBatteryNotLownull251     fun requiresBatteryNotLow(): Boolean {
252         return requiresBatteryNotLow
253     }
254 
255     /** @return `true` if the work should only execute when the storage isn't low */
requiresStorageNotLownull256     fun requiresStorageNotLow(): Boolean {
257         return requiresStorageNotLow
258     }
259 
260     /** @return `true` if [ContentUriTrigger] is not empty */
261     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
hasContentUriTriggersnull262     fun hasContentUriTriggers(): Boolean {
263         return Build.VERSION.SDK_INT < 24 || contentUriTriggers.isNotEmpty()
264     }
265 
266     // just use all properties in equals, no actual harm in accessing properties annotated by
267     // RequiresApi(...)
268     @SuppressLint("NewApi")
equalsnull269     override fun equals(other: Any?): Boolean {
270         if (this === other) return true
271         if (other == null || javaClass != other.javaClass) return false
272         val that = other as Constraints
273         if (requiresCharging != that.requiresCharging) return false
274         if (requiresDeviceIdle != that.requiresDeviceIdle) return false
275         if (requiresBatteryNotLow != that.requiresBatteryNotLow) return false
276         if (requiresStorageNotLow != that.requiresStorageNotLow) return false
277         if (contentTriggerUpdateDelayMillis != that.contentTriggerUpdateDelayMillis) return false
278         if (contentTriggerMaxDelayMillis != that.contentTriggerMaxDelayMillis) return false
279         if (requiredNetworkRequest != that.requiredNetworkRequest) return false
280         return if (requiredNetworkType != that.requiredNetworkType) false
281         else contentUriTriggers == that.contentUriTriggers
282     }
283 
284     // just use all properties in hashCode, no actual harm in accessing properties annotated by
285     // RequiresApi(...)
286     @SuppressLint("NewApi")
hashCodenull287     override fun hashCode(): Int {
288         var result = requiredNetworkType.hashCode()
289         result = 31 * result + if (requiresCharging) 1 else 0
290         result = 31 * result + if (requiresDeviceIdle) 1 else 0
291         result = 31 * result + if (requiresBatteryNotLow) 1 else 0
292         result = 31 * result + if (requiresStorageNotLow) 1 else 0
293         result =
294             31 * result +
295                 (contentTriggerUpdateDelayMillis xor (contentTriggerUpdateDelayMillis ushr 32))
296                     .toInt()
297         result =
298             31 * result +
299                 (contentTriggerMaxDelayMillis xor (contentTriggerMaxDelayMillis ushr 32)).toInt()
300         result = 31 * result + contentUriTriggers.hashCode()
301         result = 31 * result + requiredNetworkRequest.hashCode()
302         return result
303     }
304 
305     // just use all properties in toString, no actual harm in accessing properties annotated by
306     // RequiresApi(...)
307     @SuppressLint("NewApi")
toStringnull308     override fun toString(): String {
309         return "Constraints{" +
310             "requiredNetworkType=$requiredNetworkType, " +
311             "requiresCharging=$requiresCharging, " +
312             "requiresDeviceIdle=$requiresDeviceIdle, " +
313             "requiresBatteryNotLow=$requiresBatteryNotLow, " +
314             "requiresStorageNotLow=$requiresStorageNotLow, " +
315             "contentTriggerUpdateDelayMillis=$contentTriggerUpdateDelayMillis, " +
316             "contentTriggerMaxDelayMillis=$contentTriggerMaxDelayMillis, " +
317             "contentUriTriggers=$contentUriTriggers, " +
318             "}"
319     }
320 
321     /** A Builder for a [Constraints] object. */
322     class Builder {
323         private var requiresCharging = false
324         private var requiresDeviceIdle = false
325         private var requiredNetworkRequest: NetworkRequestCompat = NetworkRequestCompat()
326         private var requiredNetworkType = NetworkType.NOT_REQUIRED
327         private var requiresBatteryNotLow = false
328         private var requiresStorageNotLow = false
329 
330         // Same defaults as JobInfo
331         private var triggerContentUpdateDelay: Long = -1
332         private var triggerContentMaxDelay: Long = -1
333         private var contentUriTriggers = mutableSetOf<ContentUriTrigger>()
334 
335         constructor() {
336             // default public constructor
337         }
338 
339         /**  */
340         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
341         constructor(constraints: Constraints) {
342             requiresCharging = constraints.requiresCharging()
343             requiresDeviceIdle = Build.VERSION.SDK_INT >= 23 && constraints.requiresDeviceIdle()
344             requiredNetworkType = constraints.requiredNetworkType
345             requiresBatteryNotLow = constraints.requiresBatteryNotLow()
346             requiresStorageNotLow = constraints.requiresStorageNotLow()
347             if (Build.VERSION.SDK_INT >= 24) {
348                 triggerContentUpdateDelay = constraints.contentTriggerUpdateDelayMillis
349                 triggerContentMaxDelay = constraints.contentTriggerMaxDelayMillis
350                 contentUriTriggers = constraints.contentUriTriggers.toMutableSet()
351             }
352         }
353 
354         /**
355          * Sets whether device should be charging for the [WorkRequest] to run. The default value is
356          * `false`.
357          *
358          * @param requiresCharging `true` if device must be charging for the work to run
359          * @return The current [Builder]
360          */
setRequiresChargingnull361         fun setRequiresCharging(requiresCharging: Boolean): Builder {
362             this.requiresCharging = requiresCharging
363             return this
364         }
365 
366         /**
367          * Sets whether device should be idle for the [WorkRequest] to run. The default value is
368          * `false`.
369          *
370          * @param requiresDeviceIdle `true` if device must be idle for the work to run
371          * @return The current [Builder]
372          */
373         @RequiresApi(23)
setRequiresDeviceIdlenull374         fun setRequiresDeviceIdle(requiresDeviceIdle: Boolean): Builder {
375             this.requiresDeviceIdle = requiresDeviceIdle
376             return this
377         }
378 
379         /**
380          * Sets whether device should have a particular [NetworkType] for the [WorkRequest] to run.
381          * The default value is [NetworkType.NOT_REQUIRED].
382          *
383          * @param networkType The type of network required for the work to run
384          * @return The current [Builder]
385          */
setRequiredNetworkTypenull386         fun setRequiredNetworkType(networkType: NetworkType): Builder {
387             requiredNetworkType = networkType
388             requiredNetworkRequest = NetworkRequestCompat()
389             return this
390         }
391 
392         /**
393          * Sets whether device should have a particular [NetworkRequest] for the [WorkRequest] to
394          * run on the API levels >= 28 (Android P). For the older API levels, `networkType` will be
395          * used instead on the older platforms.
396          *
397          * `NetworkRequest` with `NetworkSpecifier` set aren't supported, as well as
398          * `NetworkRequest` with `setIncludeOtherUidNetworks` set. [IllegalArgumentException] will
399          * be thrown if such requests are passed.
400          *
401          * @param networkRequest
402          * @param networkType The type of network required for t
403          * @return The current [Builder]
404          */
405         @RequiresApi(21)
setRequiredNetworkRequestnull406         fun setRequiredNetworkRequest(
407             networkRequest: NetworkRequest,
408             networkType: NetworkType
409         ): Builder {
410             if (Build.VERSION.SDK_INT >= 28) {
411                 if (
412                     Build.VERSION.SDK_INT >= 31 &&
413                         NetworkRequest30.getNetworkSpecifier(networkRequest) != null
414                 ) {
415                     throw IllegalArgumentException(
416                         "NetworkRequests with NetworkSpecifiers set aren't supported."
417                     )
418                 }
419                 requiredNetworkRequest = NetworkRequestCompat(networkRequest)
420                 requiredNetworkType = NetworkType.NOT_REQUIRED
421             } else {
422                 requiredNetworkType = networkType
423             }
424             return this
425         }
426 
427         /**
428          * Sets whether device battery should be at an acceptable level for the [WorkRequest] to
429          * run. The default value is `false`.
430          *
431          * @param requiresBatteryNotLow `true` if the battery should be at an acceptable level for
432          *   the work to run
433          * @return The current [Builder]
434          */
setRequiresBatteryNotLownull435         fun setRequiresBatteryNotLow(requiresBatteryNotLow: Boolean): Builder {
436             this.requiresBatteryNotLow = requiresBatteryNotLow
437             return this
438         }
439 
440         /**
441          * Sets whether the device's available storage should be at an acceptable level for the
442          * [WorkRequest] to run. The default value is `false`.
443          *
444          * @param requiresStorageNotLow `true` if the available storage should not be below a a
445          *   critical threshold for the work to run
446          * @return The current [Builder]
447          */
setRequiresStorageNotLownull448         fun setRequiresStorageNotLow(requiresStorageNotLow: Boolean): Builder {
449             this.requiresStorageNotLow = requiresStorageNotLow
450             return this
451         }
452 
453         /**
454          * Sets whether the [WorkRequest] should run when a local `content:` [Uri] is updated. This
455          * functionality is identical to the one found in `JobScheduler` and is described in
456          * `JobInfo.Builder#addTriggerContentUri(android.app.job.JobInfo.TriggerContentUri)`.
457          *
458          * @param uri The local `content:` Uri to observe
459          * @param triggerForDescendants `true` if any changes in descendants cause this
460          *   [WorkRequest] to run
461          * @return The current [Builder]
462          */
463         @RequiresApi(24)
addContentUriTriggernull464         fun addContentUriTrigger(uri: Uri, triggerForDescendants: Boolean): Builder {
465             contentUriTriggers.add(ContentUriTrigger(uri, triggerForDescendants))
466             return this
467         }
468 
469         /**
470          * Sets the delay that is allowed from the time a `content:` [Uri] change is detected to the
471          * time when the [WorkRequest] is scheduled. If there are more changes during this time, the
472          * delay will be reset to the start of the most recent change. This functionality is
473          * identical to the one found in `JobScheduler` and is described in
474          * `JobInfo.Builder#setTriggerContentUpdateDelay(long)`.
475          *
476          * @param duration The length of the delay in `timeUnit` units
477          * @param timeUnit The units of time for `duration`
478          * @return The current [Builder]
479          */
480         @RequiresApi(24)
setTriggerContentUpdateDelaynull481         fun setTriggerContentUpdateDelay(duration: Long, timeUnit: TimeUnit): Builder {
482             triggerContentUpdateDelay = timeUnit.toMillis(duration)
483             return this
484         }
485 
486         /**
487          * Sets the delay that is allowed from the time a `content:` [Uri] change is detected to the
488          * time when the [WorkRequest] is scheduled. If there are more changes during this time, the
489          * delay will be reset to the start of the most recent change. This functionality is
490          * identical to the one found in `JobScheduler` and is described in
491          * `JobInfo.Builder#setTriggerContentUpdateDelay(long)`.
492          *
493          * @param duration The length of the delay
494          * @return The current [Builder]
495          */
496         @RequiresApi(26)
setTriggerContentUpdateDelaynull497         fun setTriggerContentUpdateDelay(duration: Duration): Builder {
498             triggerContentUpdateDelay = duration.toMillisCompat()
499             return this
500         }
501 
502         /**
503          * Sets the maximum delay that is allowed from the first time a `content:` [Uri] change is
504          * detected to the time when the [WorkRequest] is scheduled. This functionality is identical
505          * to the one found in `JobScheduler` and is described in
506          * `JobInfo.Builder#setTriggerContentMaxDelay(long)`.
507          *
508          * @param duration The length of the delay in `timeUnit` units
509          * @param timeUnit The units of time for `duration`
510          * @return The current [Builder]
511          */
512         @RequiresApi(24)
setTriggerContentMaxDelaynull513         fun setTriggerContentMaxDelay(duration: Long, timeUnit: TimeUnit): Builder {
514             triggerContentMaxDelay = timeUnit.toMillis(duration)
515             return this
516         }
517 
518         /**
519          * Sets the maximum delay that is allowed from the first time a `content:` [Uri] change is
520          * detected to the time when the [WorkRequest] is scheduled. This functionality is identical
521          * to the one found in `JobScheduler` and is described in
522          * `JobInfo.Builder#setTriggerContentMaxDelay(long)`.
523          *
524          * @param duration The length of the delay
525          * @return The current [Builder]
526          */
527         @RequiresApi(26)
setTriggerContentMaxDelaynull528         fun setTriggerContentMaxDelay(duration: Duration): Builder {
529             triggerContentMaxDelay = duration.toMillisCompat()
530             return this
531         }
532 
533         /**
534          * Generates the [Constraints] from this Builder.
535          *
536          * @return The [Constraints] specified by this Builder
537          */
buildnull538         fun build(): Constraints {
539             val contentUriTriggers: Set<ContentUriTrigger>
540             val triggerContentUpdateDelay: Long
541             val triggerMaxContentDelay: Long
542             if (Build.VERSION.SDK_INT >= 24) {
543                 contentUriTriggers = this.contentUriTriggers.toSet()
544                 triggerContentUpdateDelay = this.triggerContentUpdateDelay
545                 triggerMaxContentDelay = triggerContentMaxDelay
546             } else {
547                 contentUriTriggers = emptySet()
548                 triggerContentUpdateDelay = -1
549                 triggerMaxContentDelay = -1
550             }
551 
552             @Suppress("NewApi")
553             return Constraints(
554                 requiredNetworkRequestCompat = requiredNetworkRequest,
555                 requiredNetworkType = requiredNetworkType,
556                 requiresCharging = requiresCharging,
557                 requiresDeviceIdle = Build.VERSION.SDK_INT >= 23 && requiresDeviceIdle,
558                 requiresBatteryNotLow = requiresBatteryNotLow,
559                 requiresStorageNotLow = requiresStorageNotLow,
560                 contentTriggerMaxDelayMillis = triggerMaxContentDelay,
561                 contentTriggerUpdateDelayMillis = triggerContentUpdateDelay,
562                 contentUriTriggers = contentUriTriggers,
563             )
564         }
565     }
566 
567     /**
568      * This class describes a content uri trigger on the [WorkRequest]: it should run when a local
569      * `content:` [Uri] is updated. This functionality is identical to the one found in
570      * `JobScheduler` and is described in
571      * `JobInfo.Builder#addTriggerContentUri(android.app.job.JobInfo.TriggerContentUri)`.
572      *
573      * @property uri The local `content:` Uri to observe
574      * @property isTriggeredForDescendants `true` if trigger also applies to descendants of the
575      *   [Uri]
576      */
577     class ContentUriTrigger(val uri: Uri, val isTriggeredForDescendants: Boolean) {
equalsnull578         override fun equals(other: Any?): Boolean {
579             if (this === other) return true
580             if (javaClass != other?.javaClass) return false
581 
582             other as ContentUriTrigger
583 
584             if (uri != other.uri) return false
585             if (isTriggeredForDescendants != other.isTriggeredForDescendants) return false
586 
587             return true
588         }
589 
hashCodenull590         override fun hashCode(): Int {
591             var result = uri.hashCode()
592             result = 31 * result + isTriggeredForDescendants.hashCode()
593             return result
594         }
595     }
596 
597     companion object {
598         /** Represents a Constraints object with no requirements. */
599         @JvmField val NONE = Constraints()
600     }
601 }
602 
603 internal const val CONSTRAINTS_COLUMNS =
604     "required_network_type, required_network_request, requires_charging, " +
605         "requires_device_idle, requires_battery_not_low, requires_storage_not_low, " +
606         "trigger_content_update_delay, trigger_max_content_delay, content_uri_triggers"
607