1 /*
2  * Copyright 2024 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.content.ComponentName
20 import android.content.Context
21 import android.content.Intent
22 import android.content.pm.ShortcutInfo
23 import android.graphics.Bitmap
24 import android.graphics.Color
25 import android.graphics.drawable.BitmapDrawable
26 import android.graphics.drawable.ColorDrawable
27 import android.graphics.drawable.Drawable
28 import android.graphics.drawable.Icon
29 import android.os.UserHandle
30 import com.android.intentresolver.ResolverDataProvider.createResolveInfo
31 import com.android.intentresolver.chooser.DisplayResolveInfo
32 import com.android.intentresolver.chooser.SelectableTargetInfo
33 import com.android.intentresolver.chooser.TargetInfo
34 import java.util.function.Consumer
35 import org.junit.Test
36 import org.mockito.kotlin.any
37 import org.mockito.kotlin.doAnswer
38 import org.mockito.kotlin.doReturn
39 import org.mockito.kotlin.eq
40 import org.mockito.kotlin.mock
41 import org.mockito.kotlin.verify
42 import org.mockito.kotlin.whenever
43 
44 class CachingTargetDataLoaderTest {
45     private val context = mock<Context>()
46     private val userHandle = UserHandle.of(1)
47 
48     @Test
doNotCacheCallerProvidedShortcutsnull49     fun doNotCacheCallerProvidedShortcuts() {
50         val callerTarget =
51             SelectableTargetInfo.newSelectableTargetInfo(
52                 /* sourceInfo = */ null,
53                 /* backupResolveInfo = */ null,
54                 /* resolvedIntent = */ Intent(),
55                 /* chooserTargetComponentName =*/ ComponentName("package", "Activity"),
56                 "chooserTargetUninitializedTitle",
57                 /* chooserTargetIcon =*/ Icon.createWithContentUri("content://package/icon.png"),
58                 /* chooserTargetIntentExtras =*/ null,
59                 /* modifiedScore =*/ 1f,
60                 /* shortcutInfo = */ null,
61                 /* appTarget = */ null,
62                 /* referrerFillInIntent = */ Intent(),
63             ) as SelectableTargetInfo
64 
65         val targetDataLoader =
66             mock<TargetDataLoader> {
67                 on { getOrLoadDirectShareIcon(eq(callerTarget), eq(userHandle), any()) } doReturn
68                     null
69             }
70         val testSubject = CachingTargetDataLoader(context, targetDataLoader)
71         val callback = Consumer<Drawable> {}
72 
73         testSubject.getOrLoadDirectShareIcon(callerTarget, userHandle, callback)
74         testSubject.getOrLoadDirectShareIcon(callerTarget, userHandle, callback)
75 
76         verify(targetDataLoader) {
77             2 * { getOrLoadDirectShareIcon(eq(callerTarget), eq(userHandle), any()) }
78         }
79     }
80 
81     @Test
serviceShortcutsAreCachednull82     fun serviceShortcutsAreCached() {
83         val context =
84             mock<Context> {
85                 on { userId } doReturn 1
86                 on { packageName } doReturn "package"
87             }
88         val targetInfo =
89             SelectableTargetInfo.newSelectableTargetInfo(
90                 /* sourceInfo = */ null,
91                 /* backupResolveInfo = */ null,
92                 /* resolvedIntent = */ Intent(),
93                 /* chooserTargetComponentName =*/ ComponentName("package", "Activity"),
94                 "chooserTargetUninitializedTitle",
95                 /* chooserTargetIcon =*/ null,
96                 /* chooserTargetIntentExtras =*/ null,
97                 /* modifiedScore =*/ 1f,
98                 /* shortcutInfo = */ ShortcutInfo.Builder(context, "1").build(),
99                 /* appTarget = */ null,
100                 /* referrerFillInIntent = */ Intent(),
101             ) as SelectableTargetInfo
102 
103         val targetDataLoader = mock<TargetDataLoader>()
104         doAnswer {
105                 val callback = it.arguments[2] as Consumer<Drawable>
106                 callback.accept(BitmapDrawable(createBitmap()))
107                 null
108             }
109             .whenever(targetDataLoader)
110             .getOrLoadDirectShareIcon(eq(targetInfo), eq(userHandle), any())
111         val testSubject = CachingTargetDataLoader(context, targetDataLoader)
112         val callback = Consumer<Drawable> {}
113 
114         testSubject.getOrLoadDirectShareIcon(targetInfo, userHandle, callback)
115         testSubject.getOrLoadDirectShareIcon(targetInfo, userHandle, callback)
116 
117         verify(targetDataLoader) {
118             1 * { getOrLoadDirectShareIcon(eq(targetInfo), eq(userHandle), any()) }
119         }
120     }
121 
122     @Test
onlyBitmapsAreCachednull123     fun onlyBitmapsAreCached() {
124         val context =
125             mock<Context> {
126                 on { userId } doReturn 1
127                 on { packageName } doReturn "package"
128             }
129         val colorTargetInfo =
130             DisplayResolveInfo.newDisplayResolveInfo(
131                 Intent(),
132                 createResolveInfo(1, userHandle.identifier),
133                 Intent(),
134             ) as DisplayResolveInfo
135         val bitmapTargetInfo =
136             DisplayResolveInfo.newDisplayResolveInfo(
137                 Intent(),
138                 createResolveInfo(2, userHandle.identifier),
139                 Intent(),
140             ) as DisplayResolveInfo
141         val hoverBitmapTargetInfo =
142             DisplayResolveInfo.newDisplayResolveInfo(
143                 Intent(),
144                 createResolveInfo(3, userHandle.identifier),
145                 Intent(),
146             ) as DisplayResolveInfo
147 
148         val targetDataLoader = mock<TargetDataLoader>()
149         doAnswer {
150                 val target = it.arguments[0] as TargetInfo
151                 val callback = it.arguments[2] as Consumer<Drawable>
152                 val drawable =
153                     if (target === bitmapTargetInfo) {
154                         BitmapDrawable(createBitmap())
155                     } else if (target === hoverBitmapTargetInfo) {
156                         HoverBitmapDrawable(createBitmap())
157                     } else {
158                         ColorDrawable(Color.RED)
159                     }
160                 callback.accept(drawable)
161                 null
162             }
163             .whenever(targetDataLoader)
164             .getOrLoadAppTargetIcon(any(), eq(userHandle), any())
165         val testSubject = CachingTargetDataLoader(context, targetDataLoader)
166         val callback = Consumer<Drawable> {}
167 
168         testSubject.getOrLoadAppTargetIcon(colorTargetInfo, userHandle, callback)
169         testSubject.getOrLoadAppTargetIcon(colorTargetInfo, userHandle, callback)
170         testSubject.getOrLoadAppTargetIcon(bitmapTargetInfo, userHandle, callback)
171         testSubject.getOrLoadAppTargetIcon(bitmapTargetInfo, userHandle, callback)
172         testSubject.getOrLoadAppTargetIcon(hoverBitmapTargetInfo, userHandle, callback)
173         testSubject.getOrLoadAppTargetIcon(hoverBitmapTargetInfo, userHandle, callback)
174 
175         verify(targetDataLoader) {
176             2 * { getOrLoadAppTargetIcon(eq(colorTargetInfo), eq(userHandle), any()) }
177         }
178         verify(targetDataLoader) {
179             1 * { getOrLoadAppTargetIcon(eq(bitmapTargetInfo), eq(userHandle), any()) }
180         }
181         verify(targetDataLoader) {
182             1 * { getOrLoadAppTargetIcon(eq(hoverBitmapTargetInfo), eq(userHandle), any()) }
183         }
184     }
185 }
186 
createBitmapnull187 private fun createBitmap() = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
188