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