• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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.intentresolver.icons
18 
19 import android.app.ActivityManager
20 import android.content.Context
21 import android.content.pm.ResolveInfo
22 import android.graphics.drawable.Drawable
23 import android.os.AsyncTask
24 import android.os.UserHandle
25 import android.util.SparseArray
26 import androidx.annotation.GuardedBy
27 import androidx.lifecycle.DefaultLifecycleObserver
28 import androidx.lifecycle.Lifecycle
29 import androidx.lifecycle.LifecycleOwner
30 import com.android.intentresolver.TargetPresentationGetter
31 import com.android.intentresolver.chooser.DisplayResolveInfo
32 import com.android.intentresolver.chooser.SelectableTargetInfo
33 import java.util.concurrent.atomic.AtomicInteger
34 import java.util.function.Consumer
35 import kotlinx.coroutines.Dispatchers
36 import kotlinx.coroutines.asExecutor
37 
38 /** An actual [TargetDataLoader] implementation. */
39 // TODO: replace async tasks with coroutines.
40 class DefaultTargetDataLoader(
41     private val context: Context,
42     private val lifecycle: Lifecycle,
43     private val isAudioCaptureDevice: Boolean,
44 ) : TargetDataLoader() {
45     private val presentationFactory =
46         TargetPresentationGetter.Factory(
47             context,
48             context.getSystemService(ActivityManager::class.java)?.launcherLargeIconDensity
49                 ?: error("Unable to access ActivityManager")
50         )
51     private val nextTaskId = AtomicInteger(0)
52     @GuardedBy("self") private val activeTasks = SparseArray<AsyncTask<*, *, *>>()
53     private val executor = Dispatchers.IO.asExecutor()
54 
55     init {
56         lifecycle.addObserver(
57             object : DefaultLifecycleObserver {
58                 override fun onDestroy(owner: LifecycleOwner) {
59                     lifecycle.removeObserver(this)
60                     destroy()
61                 }
62             }
63         )
64     }
65 
66     override fun loadAppTargetIcon(
67         info: DisplayResolveInfo,
68         userHandle: UserHandle,
69         callback: Consumer<Drawable>,
70     ) {
71         val taskId = nextTaskId.getAndIncrement()
72         LoadIconTask(context, info, userHandle, presentationFactory) { result ->
73                 removeTask(taskId)
74                 callback.accept(result)
75             }
76             .also { addTask(taskId, it) }
77             .executeOnExecutor(executor)
78     }
79 
80     override fun loadDirectShareIcon(
81         info: SelectableTargetInfo,
82         userHandle: UserHandle,
83         callback: Consumer<Drawable>,
84     ) {
85         val taskId = nextTaskId.getAndIncrement()
86         LoadDirectShareIconTask(
87                 context.createContextAsUser(userHandle, 0),
88                 info,
89                 presentationFactory,
90             ) { result ->
91                 removeTask(taskId)
92                 callback.accept(result)
93             }
94             .also { addTask(taskId, it) }
95             .executeOnExecutor(executor)
96     }
97 
98     override fun loadLabel(info: DisplayResolveInfo, callback: Consumer<Array<CharSequence?>>) {
99         val taskId = nextTaskId.getAndIncrement()
100         LoadLabelTask(context, info, isAudioCaptureDevice, presentationFactory) { result ->
101                 removeTask(taskId)
102                 callback.accept(result)
103             }
104             .also { addTask(taskId, it) }
105             .executeOnExecutor(executor)
106     }
107 
108     override fun createPresentationGetter(info: ResolveInfo): TargetPresentationGetter =
109         presentationFactory.makePresentationGetter(info)
110 
111     private fun addTask(id: Int, task: AsyncTask<*, *, *>) {
112         synchronized(activeTasks) { activeTasks.put(id, task) }
113     }
114 
115     private fun removeTask(id: Int) {
116         synchronized(activeTasks) { activeTasks.remove(id) }
117     }
118 
119     private fun destroy() {
120         synchronized(activeTasks) {
121             for (i in 0 until activeTasks.size()) {
122                 activeTasks.valueAt(i).cancel(false)
123             }
124             activeTasks.clear()
125         }
126     }
127 }
128