1 /*
<lambda>null2  * Copyright (C) 2019 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 com.android.permissioncontroller.permission.data
18 
19 import android.os.Binder
20 import com.android.permissioncontroller.permission.utils.IPC
21 import kotlinx.coroutines.Dispatchers.Main
22 import kotlinx.coroutines.GlobalScope
23 import kotlinx.coroutines.Job
24 import kotlinx.coroutines.launch
25 
26 /**
27  * A LiveData which loads its data in a background AsyncTask. It will cancel current tasks, if new
28  * requests come during execution
29  */
30 abstract class SmartAsyncMediatorLiveData<T> : SmartUpdateMediatorLiveData<T>() {
31 
32     private var currentJob: Job? = null
33     @Volatile
34     private var jobQueued = false
35     @Volatile
36     private var jobRunning = false
37 
38     /**
39      * The main function which will load data. It should periodically check isCancelled to see if
40      * it should stop working. If data is loaded, it should call "postValue".
41      */
42     abstract suspend fun loadDataAndPostValue(job: Job)
43 
44     override fun onUpdate() {
45         updateAsync()
46     }
47 
48     open fun updateAsync() {
49         if (jobRunning) {
50             jobQueued = true
51             return
52         } else {
53             jobRunning = true
54         }
55 
56         GlobalScope.launch(IPC) {
57             currentJob = coroutineContext[Job]
58             loadDataAndPostValue(currentJob!!)
59             // TODO ntmyren: generalize this command to the IPC dispatcher
60             Binder.flushPendingCommands()
61             jobRunning = false
62             if (jobQueued) {
63                 jobQueued = false
64                 GlobalScope.launch(Main.immediate) {
65                     updateAsync()
66                 }
67             }
68         }
69     }
70 
71     override fun onInactive() {
72         cancelJobIfRunning()
73         jobQueued = false
74         super.onInactive()
75     }
76 
77     private fun cancelJobIfRunning() {
78         currentJob?.let { job ->
79             if (job.isActive) {
80                 job.cancel()
81             }
82         }
83     }
84 }