1 /* <lambda>null2 * 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.impl.model 17 18 import android.net.Uri 19 import android.os.Build 20 import androidx.room.TypeConverter 21 import androidx.work.BackoffPolicy 22 import androidx.work.Constraints 23 import androidx.work.Constraints.ContentUriTrigger 24 import androidx.work.NetworkType 25 import androidx.work.OutOfQuotaPolicy 26 import androidx.work.WorkInfo 27 import androidx.work.impl.utils.NetworkRequest28 28 import androidx.work.impl.utils.NetworkRequestCompat 29 import androidx.work.impl.utils.capabilitiesCompat 30 import androidx.work.impl.utils.transportTypesCompat 31 import java.io.ByteArrayInputStream 32 import java.io.ByteArrayOutputStream 33 import java.io.IOException 34 import java.io.ObjectInputStream 35 import java.io.ObjectOutputStream 36 37 /** TypeConverters for WorkManager enums and classes. */ 38 object WorkTypeConverters { 39 /** Integer identifiers that map to [WorkInfo.State]. */ 40 object StateIds { 41 const val ENQUEUED = 0 42 const val RUNNING = 1 43 const val SUCCEEDED = 2 44 const val FAILED = 3 45 const val BLOCKED = 4 46 const val CANCELLED = 5 47 const val COMPLETED_STATES = "($SUCCEEDED, $FAILED, $CANCELLED)" 48 } 49 50 /** Integer identifiers that map to [BackoffPolicy]. */ 51 private object BackoffPolicyIds { 52 const val EXPONENTIAL = 0 53 const val LINEAR = 1 54 } 55 56 /** Integer identifiers that map to [NetworkType]. */ 57 private object NetworkTypeIds { 58 const val NOT_REQUIRED = 0 59 const val CONNECTED = 1 60 const val UNMETERED = 2 61 const val NOT_ROAMING = 3 62 const val METERED = 4 63 const val TEMPORARILY_UNMETERED = 5 64 } 65 66 /** Integer identifiers that map to [OutOfQuotaPolicy]. */ 67 private object OutOfPolicyIds { 68 const val RUN_AS_NON_EXPEDITED_WORK_REQUEST = 0 69 const val DROP_WORK_REQUEST = 1 70 } 71 72 /** 73 * TypeConverter for a State to an int. 74 * 75 * @param state The input State 76 * @return The associated int constant 77 */ 78 @JvmStatic 79 @TypeConverter 80 fun stateToInt(state: WorkInfo.State): Int { 81 return when (state) { 82 WorkInfo.State.ENQUEUED -> StateIds.ENQUEUED 83 WorkInfo.State.RUNNING -> StateIds.RUNNING 84 WorkInfo.State.SUCCEEDED -> StateIds.SUCCEEDED 85 WorkInfo.State.FAILED -> StateIds.FAILED 86 WorkInfo.State.BLOCKED -> StateIds.BLOCKED 87 WorkInfo.State.CANCELLED -> StateIds.CANCELLED 88 } 89 } 90 91 /** 92 * TypeConverter for an int to a State. 93 * 94 * @param value The input integer 95 * @return The associated State enum value 96 */ 97 @JvmStatic 98 @TypeConverter 99 fun intToState(value: Int): WorkInfo.State { 100 return when (value) { 101 StateIds.ENQUEUED -> WorkInfo.State.ENQUEUED 102 StateIds.RUNNING -> WorkInfo.State.RUNNING 103 StateIds.SUCCEEDED -> WorkInfo.State.SUCCEEDED 104 StateIds.FAILED -> WorkInfo.State.FAILED 105 StateIds.BLOCKED -> WorkInfo.State.BLOCKED 106 StateIds.CANCELLED -> WorkInfo.State.CANCELLED 107 else -> throw IllegalArgumentException("Could not convert $value to State") 108 } 109 } 110 111 /** 112 * TypeConverter for a BackoffPolicy to an int. 113 * 114 * @param backoffPolicy The input BackoffPolicy 115 * @return The associated int constant 116 */ 117 @JvmStatic 118 @TypeConverter 119 fun backoffPolicyToInt(backoffPolicy: BackoffPolicy): Int { 120 return when (backoffPolicy) { 121 BackoffPolicy.EXPONENTIAL -> BackoffPolicyIds.EXPONENTIAL 122 BackoffPolicy.LINEAR -> BackoffPolicyIds.LINEAR 123 } 124 } 125 126 /** 127 * TypeConverter for an int to a BackoffPolicy. 128 * 129 * @param value The input integer 130 * @return The associated BackoffPolicy enum value 131 */ 132 @JvmStatic 133 @TypeConverter 134 fun intToBackoffPolicy(value: Int): BackoffPolicy { 135 return when (value) { 136 BackoffPolicyIds.EXPONENTIAL -> BackoffPolicy.EXPONENTIAL 137 BackoffPolicyIds.LINEAR -> BackoffPolicy.LINEAR 138 else -> throw IllegalArgumentException("Could not convert $value to BackoffPolicy") 139 } 140 } 141 142 /** 143 * TypeConverter for a NetworkType to an int. 144 * 145 * @param networkType The input NetworkType 146 * @return The associated int constant 147 */ 148 @JvmStatic 149 @TypeConverter 150 fun networkTypeToInt(networkType: NetworkType): Int { 151 return when (networkType) { 152 NetworkType.NOT_REQUIRED -> NetworkTypeIds.NOT_REQUIRED 153 NetworkType.CONNECTED -> NetworkTypeIds.CONNECTED 154 NetworkType.UNMETERED -> NetworkTypeIds.UNMETERED 155 NetworkType.NOT_ROAMING -> NetworkTypeIds.NOT_ROAMING 156 NetworkType.METERED -> NetworkTypeIds.METERED 157 else -> { 158 if (Build.VERSION.SDK_INT >= 30 && networkType == NetworkType.TEMPORARILY_UNMETERED) 159 NetworkTypeIds.TEMPORARILY_UNMETERED 160 else throw IllegalArgumentException("Could not convert $networkType to int") 161 } 162 } 163 } 164 165 /** 166 * TypeConverter for an int to a NetworkType. 167 * 168 * @param value The input integer 169 * @return The associated NetworkType enum value 170 */ 171 @JvmStatic 172 @TypeConverter 173 fun intToNetworkType(value: Int): NetworkType { 174 return when (value) { 175 NetworkTypeIds.NOT_REQUIRED -> NetworkType.NOT_REQUIRED 176 NetworkTypeIds.CONNECTED -> NetworkType.CONNECTED 177 NetworkTypeIds.UNMETERED -> NetworkType.UNMETERED 178 NetworkTypeIds.NOT_ROAMING -> NetworkType.NOT_ROAMING 179 NetworkTypeIds.METERED -> NetworkType.METERED 180 else -> { 181 if (Build.VERSION.SDK_INT >= 30 && value == NetworkTypeIds.TEMPORARILY_UNMETERED) { 182 return NetworkType.TEMPORARILY_UNMETERED 183 } else throw IllegalArgumentException("Could not convert $value to NetworkType") 184 } 185 } 186 } 187 188 /** 189 * Converts a [OutOfQuotaPolicy] to an int. 190 * 191 * @param policy The [OutOfQuotaPolicy] policy being used 192 * @return the corresponding int representation. 193 */ 194 @JvmStatic 195 @TypeConverter 196 fun outOfQuotaPolicyToInt(policy: OutOfQuotaPolicy): Int { 197 return when (policy) { 198 OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST -> 199 OutOfPolicyIds.RUN_AS_NON_EXPEDITED_WORK_REQUEST 200 OutOfQuotaPolicy.DROP_WORK_REQUEST -> OutOfPolicyIds.DROP_WORK_REQUEST 201 } 202 } 203 204 /** 205 * Converter from an int to a [OutOfQuotaPolicy]. 206 * 207 * @param value The input integer 208 * @return An [OutOfQuotaPolicy] 209 */ 210 @JvmStatic 211 @TypeConverter 212 fun intToOutOfQuotaPolicy(value: Int): OutOfQuotaPolicy { 213 return when (value) { 214 OutOfPolicyIds.RUN_AS_NON_EXPEDITED_WORK_REQUEST -> 215 OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST 216 OutOfPolicyIds.DROP_WORK_REQUEST -> OutOfQuotaPolicy.DROP_WORK_REQUEST 217 else -> throw IllegalArgumentException("Could not convert $value to OutOfQuotaPolicy") 218 } 219 } 220 221 /** 222 * Converts a set of [Constraints.ContentUriTrigger]s to byte array representation 223 * 224 * @param triggers the list of [Constraints.ContentUriTrigger]s to convert 225 * @return corresponding byte array representation 226 */ 227 @JvmStatic 228 @TypeConverter 229 fun setOfTriggersToByteArray(triggers: Set<ContentUriTrigger>): ByteArray { 230 if (triggers.isEmpty()) { 231 return ByteArray(0) 232 } 233 val outputStream = ByteArrayOutputStream() 234 outputStream.use { 235 ObjectOutputStream(outputStream).use { objectOutputStream -> 236 objectOutputStream.writeInt(triggers.size) 237 for (trigger in triggers) { 238 objectOutputStream.writeUTF(trigger.uri.toString()) 239 objectOutputStream.writeBoolean(trigger.isTriggeredForDescendants) 240 } 241 } 242 } 243 return outputStream.toByteArray() 244 } 245 246 /** 247 * Converts a byte array to set of [ContentUriTrigger]s 248 * 249 * @param bytes byte array representation to convert 250 * @return set of [ContentUriTrigger] 251 */ 252 @JvmStatic 253 @TypeConverter 254 fun byteArrayToSetOfTriggers(bytes: ByteArray): Set<ContentUriTrigger> { 255 val triggers = mutableSetOf<ContentUriTrigger>() 256 if (bytes.isEmpty()) { 257 // bytes will be null if there are no Content Uri Triggers 258 return triggers 259 } 260 val inputStream = ByteArrayInputStream(bytes) 261 inputStream.use { 262 try { 263 ObjectInputStream(inputStream).use { objectInputStream -> 264 repeat(objectInputStream.readInt()) { 265 val uri = Uri.parse(objectInputStream.readUTF()) 266 val triggersForDescendants = objectInputStream.readBoolean() 267 triggers.add(ContentUriTrigger(uri, triggersForDescendants)) 268 } 269 } 270 } catch (e: IOException) { 271 e.printStackTrace() 272 } 273 } 274 return triggers 275 } 276 277 @JvmStatic 278 @TypeConverter 279 internal fun toNetworkRequest(bytes: ByteArray): NetworkRequestCompat { 280 if (Build.VERSION.SDK_INT < 28 || bytes.isEmpty()) { 281 return NetworkRequestCompat(null) 282 } 283 return ByteArrayInputStream(bytes).use { 284 ObjectInputStream(it).use { inputStream -> 285 val transports = IntArray(inputStream.readInt()) 286 repeat(transports.size) { i -> transports[i] = inputStream.readInt() } 287 val capabilities = IntArray(inputStream.readInt()) 288 repeat(capabilities.size) { i -> capabilities[i] = inputStream.readInt() } 289 NetworkRequest28.createNetworkRequestCompat(capabilities, transports) 290 } 291 } 292 } 293 294 @JvmStatic 295 @TypeConverter 296 internal fun fromNetworkRequest(requestCompat: NetworkRequestCompat): ByteArray { 297 if (Build.VERSION.SDK_INT < 28) { 298 return ByteArray(0) 299 } 300 val request = requestCompat.networkRequest ?: return ByteArray(0) 301 val outputStream = ByteArrayOutputStream() 302 outputStream.use { 303 ObjectOutputStream(it).use { outputStream -> 304 val transports = request.transportTypesCompat 305 val capabilities = request.capabilitiesCompat 306 outputStream.writeInt(transports.size) 307 transports.forEach { t -> outputStream.writeInt(t) } 308 outputStream.writeInt(capabilities.size) 309 capabilities.forEach { c -> outputStream.writeInt(c) } 310 } 311 } 312 return outputStream.toByteArray() 313 } 314 } 315