1 /*
<lambda>null2  * Copyright (C) 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.profiles
18 
19 import android.os.UserHandle
20 import android.view.LayoutInflater
21 import android.view.View
22 import android.view.ViewGroup
23 import android.widget.ListView
24 import androidx.test.platform.app.InstrumentationRegistry
25 import com.android.intentresolver.R
26 import com.android.intentresolver.ResolverListAdapter
27 import com.android.intentresolver.emptystate.EmptyStateProvider
28 import com.android.intentresolver.profiles.MultiProfilePagerAdapter.PROFILE_PERSONAL
29 import com.android.intentresolver.profiles.MultiProfilePagerAdapter.PROFILE_WORK
30 import com.google.common.collect.ImmutableList
31 import com.google.common.truth.Truth.assertThat
32 import java.util.Optional
33 import java.util.function.Supplier
34 import org.junit.Test
35 import org.mockito.kotlin.doReturn
36 import org.mockito.kotlin.mock
37 
38 class MultiProfilePagerAdapterTest {
39     private val PERSONAL_USER_HANDLE = UserHandle.of(10)
40     private val WORK_USER_HANDLE = UserHandle.of(20)
41 
42     private val context = InstrumentationRegistry.getInstrumentation().getContext()
43     private val inflater = Supplier {
44         LayoutInflater.from(context).inflate(R.layout.resolver_list_per_profile, null, false)
45             as ViewGroup
46     }
47 
48     @Test
49     fun testSinglePageProfileAdapter() {
50         val personalListAdapter =
51             mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE }
52         val pagerAdapter =
53             MultiProfilePagerAdapter(
54                 { listAdapter: ResolverListAdapter -> listAdapter },
55                 { listView: ListView, bindAdapter: ResolverListAdapter ->
56                     listView.setAdapter(bindAdapter)
57                 },
58                 ImmutableList.of(
59                     TabConfig(
60                         PROFILE_PERSONAL,
61                         "personal",
62                         "personal_a11y",
63                         "TAG_PERSONAL",
64                         personalListAdapter
65                     )
66                 ),
67                 object : EmptyStateProvider {},
68                 { false },
69                 PROFILE_PERSONAL,
70                 null,
71                 null,
72                 inflater,
73                 { Optional.empty() }
74             )
75         assertThat(pagerAdapter.count).isEqualTo(1)
76         assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_PERSONAL)
77         assertThat(pagerAdapter.currentUserHandle).isEqualTo(PERSONAL_USER_HANDLE)
78         assertThat(pagerAdapter.getPageAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
79         assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(personalListAdapter)
80         assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
81         assertThat(pagerAdapter.workListAdapter).isNull()
82         assertThat(pagerAdapter.itemCount).isEqualTo(1)
83         // TODO: consider covering some of the package-private methods (and making them public?).
84         // TODO: consider exercising responsibilities as an implementation of a ViewPager adapter.
85     }
86 
87     @Test
88     fun testTwoProfilePagerAdapter() {
89         val personalListAdapter =
90             mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE }
91         val workListAdapter =
92             mock<ResolverListAdapter> { on { userHandle } doReturn WORK_USER_HANDLE }
93         val pagerAdapter =
94             MultiProfilePagerAdapter(
95                 { listAdapter: ResolverListAdapter -> listAdapter },
96                 { listView: ListView, bindAdapter: ResolverListAdapter ->
97                     listView.setAdapter(bindAdapter)
98                 },
99                 ImmutableList.of(
100                     TabConfig(
101                         PROFILE_PERSONAL,
102                         "personal",
103                         "personal_a11y",
104                         "TAG_PERSONAL",
105                         personalListAdapter
106                     ),
107                     TabConfig(PROFILE_WORK, "work", "work_a11y", "TAG_WORK", workListAdapter)
108                 ),
109                 object : EmptyStateProvider {},
110                 { false },
111                 PROFILE_PERSONAL,
112                 WORK_USER_HANDLE, // TODO: why does this test pass even if this is null?
113                 null,
114                 inflater,
115                 { Optional.empty() }
116             )
117         assertThat(pagerAdapter.count).isEqualTo(2)
118         assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_PERSONAL)
119         assertThat(pagerAdapter.currentUserHandle).isEqualTo(PERSONAL_USER_HANDLE)
120         assertThat(pagerAdapter.getPageAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
121         assertThat(pagerAdapter.getPageAdapterForIndex(1)).isSameInstanceAs(workListAdapter)
122         assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(personalListAdapter)
123         assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
124         assertThat(pagerAdapter.workListAdapter).isSameInstanceAs(workListAdapter)
125         assertThat(pagerAdapter.itemCount).isEqualTo(2)
126         // TODO: consider covering some of the package-private methods (and making them public?).
127         // TODO: consider exercising responsibilities as an implementation of a ViewPager adapter;
128         // especially matching profiles to ListViews?
129         // TODO: test ProfileSelectedListener (and getters for "current" state) as the selected
130         // page changes. Currently there's no API to change the selected page directly; that's
131         // only possible through manipulation of the bound ViewPager.
132     }
133 
134     @Test
135     fun testTwoProfilePagerAdapter_workIsDefault() {
136         val personalListAdapter =
137             mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE }
138         val workListAdapter =
139             mock<ResolverListAdapter> { on { userHandle } doReturn WORK_USER_HANDLE }
140         val pagerAdapter =
141             MultiProfilePagerAdapter(
142                 { listAdapter: ResolverListAdapter -> listAdapter },
143                 { listView: ListView, bindAdapter: ResolverListAdapter ->
144                     listView.setAdapter(bindAdapter)
145                 },
146                 ImmutableList.of(
147                     TabConfig(
148                         PROFILE_PERSONAL,
149                         "personal",
150                         "personal_a11y",
151                         "TAG_PERSONAL",
152                         personalListAdapter
153                     ),
154                     TabConfig(PROFILE_WORK, "work", "work_a11y", "TAG_WORK", workListAdapter)
155                 ),
156                 object : EmptyStateProvider {},
157                 { false },
158                 PROFILE_WORK, // <-- This test specifically requests we start on work profile.
159                 WORK_USER_HANDLE, // TODO: why does this test pass even if this is null?
160                 null,
161                 inflater,
162                 { Optional.empty() }
163             )
164         assertThat(pagerAdapter.count).isEqualTo(2)
165         assertThat(pagerAdapter.currentPage).isEqualTo(PROFILE_WORK)
166         assertThat(pagerAdapter.currentUserHandle).isEqualTo(WORK_USER_HANDLE)
167         assertThat(pagerAdapter.getPageAdapterForIndex(0)).isSameInstanceAs(personalListAdapter)
168         assertThat(pagerAdapter.getPageAdapterForIndex(1)).isSameInstanceAs(workListAdapter)
169         assertThat(pagerAdapter.activeListAdapter).isSameInstanceAs(workListAdapter)
170         assertThat(pagerAdapter.personalListAdapter).isSameInstanceAs(personalListAdapter)
171         assertThat(pagerAdapter.workListAdapter).isSameInstanceAs(workListAdapter)
172         assertThat(pagerAdapter.itemCount).isEqualTo(2)
173         // TODO: consider covering some of the package-private methods (and making them public?).
174         // TODO: test ProfileSelectedListener (and getters for "current" state) as the selected
175         // page changes. Currently there's no API to change the selected page directly; that's
176         // only possible through manipulation of the bound ViewPager.
177     }
178 
179     @Test
180     fun testBottomPaddingDelegate_default() {
181         val personalListAdapter =
182             mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE }
183         val pagerAdapter =
184             MultiProfilePagerAdapter(
185                 { listAdapter: ResolverListAdapter -> listAdapter },
186                 { listView: ListView, bindAdapter: ResolverListAdapter ->
187                     listView.setAdapter(bindAdapter)
188                 },
189                 ImmutableList.of(
190                     TabConfig(
191                         PROFILE_PERSONAL,
192                         "personal",
193                         "personal_a11y",
194                         "TAG_PERSONAL",
195                         personalListAdapter
196                     )
197                 ),
198                 object : EmptyStateProvider {},
199                 { false },
200                 PROFILE_PERSONAL,
201                 null,
202                 null,
203                 inflater,
204                 { Optional.empty() }
205             )
206         val container =
207             pagerAdapter.activeEmptyStateView.requireViewById<View>(
208                 com.android.internal.R.id.resolver_empty_state_container
209             )
210         container.setPadding(1, 2, 3, 4)
211         pagerAdapter.setupContainerPadding()
212         assertThat(container.paddingLeft).isEqualTo(1)
213         assertThat(container.paddingTop).isEqualTo(2)
214         assertThat(container.paddingRight).isEqualTo(3)
215         assertThat(container.paddingBottom).isEqualTo(4)
216     }
217 
218     @Test
219     fun testBottomPaddingDelegate_override() {
220         val personalListAdapter =
221             mock<ResolverListAdapter> { on { userHandle } doReturn PERSONAL_USER_HANDLE }
222         val pagerAdapter =
223             MultiProfilePagerAdapter(
224                 { listAdapter: ResolverListAdapter -> listAdapter },
225                 { listView: ListView, bindAdapter: ResolverListAdapter ->
226                     listView.setAdapter(bindAdapter)
227                 },
228                 ImmutableList.of(
229                     TabConfig(
230                         PROFILE_PERSONAL,
231                         "personal",
232                         "personal_a11y",
233                         "TAG_PERSONAL",
234                         personalListAdapter
235                     )
236                 ),
237                 object : EmptyStateProvider {},
238                 { false },
239                 PROFILE_PERSONAL,
240                 null,
241                 null,
242                 inflater,
243                 { Optional.of(42) }
244             )
245         val container =
246             pagerAdapter.activeEmptyStateView.requireViewById<View>(
247                 com.android.internal.R.id.resolver_empty_state_container
248             )
249         container.setPadding(1, 2, 3, 4)
250         pagerAdapter.setupContainerPadding()
251         assertThat(container.paddingLeft).isEqualTo(1)
252         assertThat(container.paddingTop).isEqualTo(2)
253         assertThat(container.paddingRight).isEqualTo(3)
254         assertThat(container.paddingBottom).isEqualTo(42)
255     }
256 
257     @Test
258     fun testPresumedQuietModeEmptyStateForWorkProfile_whenQuiet() {
259         // TODO: this is "presumed" because the conditions to determine whether we "should" show an
260         // empty state aren't enforced to align with the conditions when we actually *would* -- I
261         // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider?
262         val personalListAdapter =
263             mock<ResolverListAdapter> {
264                 on { userHandle } doReturn PERSONAL_USER_HANDLE
265                 on { unfilteredCount } doReturn 1
266             }
267         val workListAdapter =
268             mock<ResolverListAdapter> {
269                 on { userHandle } doReturn WORK_USER_HANDLE
270                 on { unfilteredCount } doReturn 1
271             }
272         val pagerAdapter =
273             MultiProfilePagerAdapter(
274                 { listAdapter: ResolverListAdapter -> listAdapter },
275                 { listView: ListView, bindAdapter: ResolverListAdapter ->
276                     listView.setAdapter(bindAdapter)
277                 },
278                 ImmutableList.of(
279                     TabConfig(
280                         PROFILE_PERSONAL,
281                         "personal",
282                         "personal_a11y",
283                         "TAG_PERSONAL",
284                         personalListAdapter
285                     ),
286                     TabConfig(PROFILE_WORK, "work", "work_a11y", "TAG_WORK", workListAdapter)
287                 ),
288                 object : EmptyStateProvider {},
289                 { true }, // <-- Work mode is quiet.
290                 PROFILE_WORK,
291                 WORK_USER_HANDLE,
292                 null,
293                 inflater,
294                 { Optional.empty() }
295             )
296         assertThat(pagerAdapter.shouldShowEmptyStateScreen(workListAdapter)).isTrue()
297         assertThat(pagerAdapter.shouldShowEmptyStateScreen(personalListAdapter)).isFalse()
298     }
299 
300     @Test
301     fun testPresumedQuietModeEmptyStateForWorkProfile_notWhenNotQuiet() {
302         // TODO: this is "presumed" because the conditions to determine whether we "should" show an
303         // empty state aren't enforced to align with the conditions when we actually *would* -- I
304         // believe `shouldShowEmptyStateScreen` should be implemented in terms of the provider?
305         val personalListAdapter =
306             mock<ResolverListAdapter> {
307                 on { userHandle } doReturn PERSONAL_USER_HANDLE
308                 on { unfilteredCount } doReturn 1
309             }
310         val workListAdapter =
311             mock<ResolverListAdapter> {
312                 on { userHandle } doReturn WORK_USER_HANDLE
313                 on { unfilteredCount } doReturn 1
314             }
315         val pagerAdapter =
316             MultiProfilePagerAdapter(
317                 { listAdapter: ResolverListAdapter -> listAdapter },
318                 { listView: ListView, bindAdapter: ResolverListAdapter ->
319                     listView.setAdapter(bindAdapter)
320                 },
321                 ImmutableList.of(
322                     TabConfig(
323                         PROFILE_PERSONAL,
324                         "personal",
325                         "personal_a11y",
326                         "TAG_PERSONAL",
327                         personalListAdapter
328                     ),
329                     TabConfig(PROFILE_WORK, "work", "work_a11y", "TAG_WORK", workListAdapter)
330                 ),
331                 object : EmptyStateProvider {},
332                 { false }, // <-- Work mode is not quiet.
333                 PROFILE_WORK,
334                 WORK_USER_HANDLE,
335                 null,
336                 inflater,
337                 { Optional.empty() }
338             )
339         assertThat(pagerAdapter.shouldShowEmptyStateScreen(workListAdapter)).isFalse()
340         assertThat(pagerAdapter.shouldShowEmptyStateScreen(personalListAdapter)).isFalse()
341     }
342 }
343