• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * @param isStaticVal Whether or not this LiveData value is expected to change
31  * @param alwaysUpdateOnActive Whether or not this LiveData should update upon going active
32  */
33 abstract class SmartAsyncMediatorLiveData<T>(
34     isStaticVal: Boolean = false,
35     private val alwaysUpdateOnActive: Boolean = true
36 ) : SmartUpdateMediatorLiveData<T>(isStaticVal) {
37 
38     private var currentJob: Job? = null
39     @Volatile
40     private var jobQueued = false
41     @Volatile
42     private var jobRunning = false
43 
44     /**
45      * The main function which will load data. It should periodically check isCancelled to see if
46      * it should stop working. If data is loaded, it should call "postValue".
47      */
48     abstract suspend fun loadDataAndPostValue(job: Job)
49 
50     override fun onUpdate() {
51         updateAsync()
52     }
53 
54     open fun updateAsync() {
55         if (jobRunning) {
56             jobQueued = true
57             return
58         } else {
59             jobRunning = true
60         }
61 
62         GlobalScope.launch(IPC) {
63             currentJob = coroutineContext[Job]
64             loadDataAndPostValue(currentJob!!)
65             // TODO ntmyren: generalize this command to the IPC dispatcher
66             Binder.flushPendingCommands()
67             jobRunning = false
68             if (jobQueued) {
69                 jobQueued = false
70                 GlobalScope.launch(Main.immediate) {
71                     updateAsync()
72                 }
73             }
74         }
75     }
76 
77     override fun onActive() {
78         super.onActive()
79 
80         if (alwaysUpdateOnActive) {
81             updateAsync()
82         }
83     }
84 
85     override fun onInactive() {
86         cancelJobIfRunning()
87         jobQueued = false
88         super.onInactive()
89     }
90 
91     private fun cancelJobIfRunning() {
92         currentJob?.let { job ->
93             if (job.isActive) {
94                 job.cancel()
95             }
96         }
97     }
98 }