• 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.graphics.drawable.Drawable
22 import android.os.AsyncTask
23 import android.os.UserHandle
24 import android.util.SparseArray
25 import androidx.annotation.GuardedBy
26 import androidx.lifecycle.DefaultLifecycleObserver
27 import androidx.lifecycle.Lifecycle
28 import androidx.lifecycle.LifecycleOwner
29 import com.android.intentresolver.TargetPresentationGetter
30 import com.android.intentresolver.chooser.DisplayResolveInfo
31 import com.android.intentresolver.chooser.SelectableTargetInfo
32 import java.util.concurrent.atomic.AtomicInteger
33 import java.util.function.Consumer
34 import kotlinx.coroutines.Dispatchers
35 import kotlinx.coroutines.asExecutor
36 
37 /** An actual [TargetDataLoader] implementation. */
38 // TODO: replace async tasks with coroutines.
39 class DefaultTargetDataLoader(
40     private val context: Context,
41     private val lifecycle: Lifecycle,
42     private val isAudioCaptureDevice: Boolean,
43 ) : TargetDataLoader() {
44     private val presentationFactory =
45         TargetPresentationGetter.Factory(
46             context,
47             context.getSystemService(ActivityManager::class.java)?.launcherLargeIconDensity
48                 ?: error("Unable to access ActivityManager")
49         )
50     private val nextTaskId = AtomicInteger(0)
51     @GuardedBy("self") private val activeTasks = SparseArray<AsyncTask<*, *, *>>()
52     private val executor = Dispatchers.IO.asExecutor()
53 
54     init {
55         lifecycle.addObserver(
56             object : DefaultLifecycleObserver {
57                 override fun onDestroy(owner: LifecycleOwner) {
58                     lifecycle.removeObserver(this)
59                     destroy()
60                 }
61             }
62         )
63     }
64 
65     override fun getOrLoadAppTargetIcon(
66         info: DisplayResolveInfo,
67         userHandle: UserHandle,
68         callback: Consumer<Drawable>,
69     ): Drawable? {
70         val taskId = nextTaskId.getAndIncrement()
71         LoadIconTask(context, info, userHandle, presentationFactory) { result ->
72                 removeTask(taskId)
73                 callback.accept(result)
74             }
75             .also { addTask(taskId, it) }
76             .executeOnExecutor(executor)
77         return null
78     }
79 
80     override fun getOrLoadDirectShareIcon(
81         info: SelectableTargetInfo,
82         userHandle: UserHandle,
83         callback: Consumer<Drawable>,
84     ): Drawable? {
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         return null
97     }
98 
99     override fun loadLabel(info: DisplayResolveInfo, callback: Consumer<LabelInfo>) {
100         val taskId = nextTaskId.getAndIncrement()
101         LoadLabelTask(context, info, isAudioCaptureDevice, presentationFactory) { result ->
102                 removeTask(taskId)
103                 callback.accept(result)
104             }
105             .also { addTask(taskId, it) }
106             .executeOnExecutor(executor)
107     }
108 
109     override fun getOrLoadLabel(info: DisplayResolveInfo) {
110         if (!info.hasDisplayLabel()) {
111             val result =
112                 LoadLabelTask.loadLabel(context, info, isAudioCaptureDevice, presentationFactory)
113             info.displayLabel = result.label
114             info.extendedInfo = result.subLabel
115         }
116     }
117 
118     private fun addTask(id: Int, task: AsyncTask<*, *, *>) {
119         synchronized(activeTasks) { activeTasks.put(id, task) }
120     }
121 
122     private fun removeTask(id: Int) {
123         synchronized(activeTasks) { activeTasks.remove(id) }
124     }
125 
126     private fun destroy() {
127         synchronized(activeTasks) {
128             for (i in 0 until activeTasks.size()) {
129                 activeTasks.valueAt(i).cancel(false)
130             }
131             activeTasks.clear()
132         }
133     }
134 }
135