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