WorkTypeConverters.kt
/*
* 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.model
import android.net.Uri
import android.os.Build
import androidx.room.TypeConverter
import androidx.work.BackoffPolicy
import androidx.work.ContentUriTriggers
import androidx.work.NetworkType
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkInfo
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.lang.IllegalArgumentException
/**
* TypeConverters for WorkManager enums and classes.
*/
object WorkTypeConverters {
/**
* Integer identifiers that map to [WorkInfo.State].
*/
object StateIds {
const val ENQUEUED = 0
const val RUNNING = 1
const val SUCCEEDED = 2
const val FAILED = 3
const val BLOCKED = 4
const val CANCELLED = 5
const val COMPLETED_STATES = "($SUCCEEDED, $FAILED, $CANCELLED)"
}
/**
* Integer identifiers that map to [BackoffPolicy].
*/
private object BackoffPolicyIds {
const val EXPONENTIAL = 0
const val LINEAR = 1
}
/**
* Integer identifiers that map to [NetworkType].
*/
private object NetworkTypeIds {
const val NOT_REQUIRED = 0
const val CONNECTED = 1
const val UNMETERED = 2
const val NOT_ROAMING = 3
const val METERED = 4
const val TEMPORARILY_UNMETERED = 5
}
/**
* Integer identifiers that map to [OutOfQuotaPolicy].
*/
private object OutOfPolicyIds {
const val RUN_AS_NON_EXPEDITED_WORK_REQUEST = 0
const val DROP_WORK_REQUEST = 1
}
/**
* TypeConverter for a State to an int.
*
* @param state The input State
* @return The associated int constant
*/
@JvmStatic
@TypeConverter
fun stateToInt(state: WorkInfo.State): Int {
return when (state) {
WorkInfo.State.ENQUEUED -> StateIds.ENQUEUED
WorkInfo.State.RUNNING -> StateIds.RUNNING
WorkInfo.State.SUCCEEDED -> StateIds.SUCCEEDED
WorkInfo.State.FAILED -> StateIds.FAILED
WorkInfo.State.BLOCKED -> StateIds.BLOCKED
WorkInfo.State.CANCELLED -> StateIds.CANCELLED
}
}
/**
* TypeConverter for an int to a State.
*
* @param value The input integer
* @return The associated State enum value
*/
@JvmStatic
@TypeConverter
fun intToState(value: Int): WorkInfo.State {
return when (value) {
StateIds.ENQUEUED -> WorkInfo.State.ENQUEUED
StateIds.RUNNING -> WorkInfo.State.RUNNING
StateIds.SUCCEEDED -> WorkInfo.State.SUCCEEDED
StateIds.FAILED -> WorkInfo.State.FAILED
StateIds.BLOCKED -> WorkInfo.State.BLOCKED
StateIds.CANCELLED -> WorkInfo.State.CANCELLED
else -> throw IllegalArgumentException("Could not convert $value to State")
}
}
/**
* TypeConverter for a BackoffPolicy to an int.
*
* @param backoffPolicy The input BackoffPolicy
* @return The associated int constant
*/
@JvmStatic
@TypeConverter
fun backoffPolicyToInt(backoffPolicy: BackoffPolicy): Int {
return when (backoffPolicy) {
BackoffPolicy.EXPONENTIAL -> BackoffPolicyIds.EXPONENTIAL
BackoffPolicy.LINEAR -> BackoffPolicyIds.LINEAR
}
}
/**
* TypeConverter for an int to a BackoffPolicy.
*
* @param value The input integer
* @return The associated BackoffPolicy enum value
*/
@JvmStatic
@TypeConverter
fun intToBackoffPolicy(value: Int): BackoffPolicy {
return when (value) {
BackoffPolicyIds.EXPONENTIAL -> BackoffPolicy.EXPONENTIAL
BackoffPolicyIds.LINEAR -> BackoffPolicy.LINEAR
else -> throw IllegalArgumentException("Could not convert $value to BackoffPolicy")
}
}
/**
* TypeConverter for a NetworkType to an int.
*
* @param networkType The input NetworkType
* @return The associated int constant
*/
@JvmStatic
@TypeConverter
fun networkTypeToInt(networkType: NetworkType): Int {
return when (networkType) {
NetworkType.NOT_REQUIRED -> NetworkTypeIds.NOT_REQUIRED
NetworkType.CONNECTED -> NetworkTypeIds.CONNECTED
NetworkType.UNMETERED -> NetworkTypeIds.UNMETERED
NetworkType.NOT_ROAMING -> NetworkTypeIds.NOT_ROAMING
NetworkType.METERED -> NetworkTypeIds.METERED
else -> {
if (Build.VERSION.SDK_INT >= 30 && networkType == NetworkType.TEMPORARILY_UNMETERED)
NetworkTypeIds.TEMPORARILY_UNMETERED
else
throw IllegalArgumentException("Could not convert $networkType to int")
}
}
}
/**
* TypeConverter for an int to a NetworkType.
*
* @param value The input integer
* @return The associated NetworkType enum value
*/
@JvmStatic
@TypeConverter
fun intToNetworkType(value: Int): NetworkType {
return when (value) {
NetworkTypeIds.NOT_REQUIRED -> NetworkType.NOT_REQUIRED
NetworkTypeIds.CONNECTED -> NetworkType.CONNECTED
NetworkTypeIds.UNMETERED -> NetworkType.UNMETERED
NetworkTypeIds.NOT_ROAMING -> NetworkType.NOT_ROAMING
NetworkTypeIds.METERED -> NetworkType.METERED
else -> {
if (Build.VERSION.SDK_INT >= 30 && value == NetworkTypeIds.TEMPORARILY_UNMETERED) {
return NetworkType.TEMPORARILY_UNMETERED
} else throw IllegalArgumentException("Could not convert $value to NetworkType")
}
}
}
/**
* Converts a [OutOfQuotaPolicy] to an int.
*
* @param policy The [OutOfQuotaPolicy] policy being used
* @return the corresponding int representation.
*/
@JvmStatic
@TypeConverter
fun outOfQuotaPolicyToInt(policy: OutOfQuotaPolicy): Int {
return when (policy) {
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST ->
OutOfPolicyIds.RUN_AS_NON_EXPEDITED_WORK_REQUEST
OutOfQuotaPolicy.DROP_WORK_REQUEST -> OutOfPolicyIds.DROP_WORK_REQUEST
}
}
/**
* Converter from an int to a [OutOfQuotaPolicy].
*
* @param value The input integer
* @return An [OutOfQuotaPolicy]
*/
@JvmStatic
@TypeConverter
fun intToOutOfQuotaPolicy(value: Int): OutOfQuotaPolicy {
return when (value) {
OutOfPolicyIds.RUN_AS_NON_EXPEDITED_WORK_REQUEST ->
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST
OutOfPolicyIds.DROP_WORK_REQUEST -> OutOfQuotaPolicy.DROP_WORK_REQUEST
else -> throw IllegalArgumentException("Could not convert $value to OutOfQuotaPolicy")
}
}
/**
* Converts a list of [ContentUriTriggers.Trigger]s to byte array representation
* @param triggers the list of [ContentUriTriggers.Trigger]s to convert
* @return corresponding byte array representation
*/
@JvmStatic
@TypeConverter
fun contentUriTriggersToByteArray(triggers: ContentUriTriggers): ByteArray? {
if (triggers.size() == 0) {
return null
}
val outputStream = ByteArrayOutputStream()
outputStream.use {
ObjectOutputStream(outputStream).use { objectOutputStream ->
objectOutputStream.writeInt(triggers.size())
for (trigger in triggers.triggers) {
objectOutputStream.writeUTF(trigger.uri.toString())
objectOutputStream.writeBoolean(trigger.shouldTriggerForDescendants())
}
}
}
return outputStream.toByteArray()
}
/**
* Converts a byte array to list of [ContentUriTriggers.Trigger]s
* @param bytes byte array representation to convert
* @return list of [ContentUriTriggers.Trigger]s
*/
@JvmStatic
@TypeConverter
fun byteArrayToContentUriTriggers(bytes: ByteArray?): ContentUriTriggers {
val triggers = ContentUriTriggers()
if (bytes == null) {
// bytes will be null if there are no Content Uri Triggers
return triggers
}
val inputStream = ByteArrayInputStream(bytes)
inputStream.use {
try {
ObjectInputStream(inputStream).use { objectInputStream ->
repeat(objectInputStream.readInt()) {
val uri = Uri.parse(objectInputStream.readUTF())
val triggersForDescendants = objectInputStream.readBoolean()
triggers.add(uri, triggersForDescendants)
}
}
} catch (e: IOException) {
e.printStackTrace()
}
}
return triggers
}
}