1 /*
2  * Copyright (C) 2018 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.settings.panel;
18 
19 import static com.android.settings.panel.PanelContent.VIEW_TYPE_SLIDER;
20 import static com.android.settings.panel.PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON;
21 import static com.android.settings.panel.PanelSlicesAdapter.MAX_NUM_OF_SLICES;
22 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_GROUP_SLICE_URI;
23 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI;
24 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI;
25 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_MEDIA_URI;
26 
27 import static com.google.common.truth.Truth.assertThat;
28 
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.ArgumentMatchers.eq;
31 import static org.mockito.Mockito.doReturn;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.spy;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.when;
36 
37 import android.content.Context;
38 import android.net.Uri;
39 import android.view.LayoutInflater;
40 import android.view.ViewGroup;
41 import android.widget.FrameLayout;
42 
43 import androidx.lifecycle.LiveData;
44 import androidx.slice.Slice;
45 
46 import com.android.settings.R;
47 import com.android.settings.panel.PanelSlicesAdapter.SliceRowViewHolder;
48 import com.android.settings.testutils.FakeFeatureFactory;
49 
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 import org.mockito.ArgumentCaptor;
54 import org.mockito.MockitoAnnotations;
55 import org.robolectric.Robolectric;
56 import org.robolectric.RobolectricTestRunner;
57 import org.robolectric.RuntimeEnvironment;
58 import org.robolectric.android.controller.ActivityController;
59 import org.robolectric.annotation.Config;
60 import org.robolectric.annotation.Implementation;
61 import org.robolectric.annotation.Implements;
62 
63 import java.util.LinkedHashMap;
64 import java.util.Map;
65 
66 @RunWith(RobolectricTestRunner.class)
67 @Config(shadows = PanelSlicesAdapterTest.ShadowLayoutInflater.class)
68 public class PanelSlicesAdapterTest {
69 
70     private static LayoutInflater sLayoutInflater;
71 
72     private Context mContext;
73     private PanelFragment mPanelFragment;
74     private PanelFeatureProvider mPanelFeatureProvider;
75     private FakeFeatureFactory mFakeFeatureFactory;
76     private FakePanelContent mFakePanelContent;
77     private Map<Uri, LiveData<Slice>> mData = new LinkedHashMap<>();
78 
79     @Before
setUp()80     public void setUp() {
81         MockitoAnnotations.initMocks(this);
82         mContext = RuntimeEnvironment.application;
83 
84         mPanelFeatureProvider = spy(new PanelFeatureProviderImpl());
85         mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
86         mFakeFeatureFactory.panelFeatureProvider = mPanelFeatureProvider;
87         mFakePanelContent = new FakePanelContent();
88         doReturn(mFakePanelContent).when(mPanelFeatureProvider).getPanel(any(), any());
89 
90         ActivityController<FakeSettingsPanelActivity> activityController =
91                 Robolectric.buildActivity(FakeSettingsPanelActivity.class);
92         activityController.setup();
93 
94         mPanelFragment =
95                 spy((PanelFragment)
96                         activityController
97                                 .get()
98                                 .getSupportFragmentManager()
99                                 .findFragmentById(R.id.main_content));
100 
101     }
102 
addTestLiveData(Uri uri)103     private void addTestLiveData(Uri uri) {
104         // Create a slice to return for the LiveData
105         final Slice slice = spy(new Slice());
106         doReturn(uri).when(slice).getUri();
107         final LiveData<Slice> liveData = mock(LiveData.class);
108         when(liveData.getValue()).thenReturn(slice);
109         mData.put(uri, liveData);
110     }
111 
112     @Test
sizeOfAdapter_shouldNotExceedMaxNum()113     public void sizeOfAdapter_shouldNotExceedMaxNum() {
114         for (int i = 0; i < MAX_NUM_OF_SLICES + 2; i++) {
115             addTestLiveData(Uri.parse("uri" + i));
116         }
117 
118         assertThat(mData.size()).isEqualTo(MAX_NUM_OF_SLICES + 2);
119 
120         final PanelSlicesAdapter adapter =
121                 new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
122         final ViewGroup view = new FrameLayout(mContext);
123         final SliceRowViewHolder viewHolder =
124                 adapter.onCreateViewHolder(view, 0);
125 
126         assertThat(adapter.getItemCount()).isEqualTo(MAX_NUM_OF_SLICES);
127         assertThat(adapter.getData().size()).isEqualTo(MAX_NUM_OF_SLICES);
128     }
129 
130     @Test
mediaOutputIndicatorSlice_shouldNotAllowDividerAbove()131     public void mediaOutputIndicatorSlice_shouldNotAllowDividerAbove() {
132         addTestLiveData(MEDIA_OUTPUT_INDICATOR_SLICE_URI);
133 
134         final PanelSlicesAdapter adapter =
135                 new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
136         final int position = 0;
137         final ViewGroup view = new FrameLayout(mContext);
138         final SliceRowViewHolder viewHolder =
139                 adapter.onCreateViewHolder(view, 0 /* view type*/);
140 
141         adapter.onBindViewHolder(viewHolder, position);
142 
143         assertThat(viewHolder.isDividerAllowedAbove()).isFalse();
144     }
145 
146     @Test
sliderLargeIconPanel_shouldNotAllowDividerBelow()147     public void sliderLargeIconPanel_shouldNotAllowDividerBelow() {
148         addTestLiveData(MEDIA_OUTPUT_SLICE_URI);
149         mFakePanelContent.setViewType(PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON);
150 
151         final PanelSlicesAdapter adapter =
152                 new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
153         final int position = 0;
154         final ViewGroup view = new FrameLayout(mContext);
155         final SliceRowViewHolder viewHolder =
156                 adapter.onCreateViewHolder(view, PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON);
157         adapter.onBindViewHolder(viewHolder, position);
158 
159         assertThat(viewHolder.isDividerAllowedBelow()).isFalse();
160     }
161 
162     @Test
sliderPanelType_shouldAllowDividerBelow()163     public void sliderPanelType_shouldAllowDividerBelow() {
164         addTestLiveData(VOLUME_MEDIA_URI);
165         mFakePanelContent.setViewType(PanelContent.VIEW_TYPE_SLIDER);
166 
167         final PanelSlicesAdapter adapter =
168                 new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
169         final int position = 0;
170         final ViewGroup view = new FrameLayout(mContext);
171         final SliceRowViewHolder viewHolder =
172                 adapter.onCreateViewHolder(view, PanelContent.VIEW_TYPE_SLIDER);
173         adapter.onBindViewHolder(viewHolder, position);
174 
175         assertThat(viewHolder.isDividerAllowedBelow()).isTrue();
176     }
177 
178     @Test
defaultPanelType_shouldAllowDividerBelow()179     public void defaultPanelType_shouldAllowDividerBelow() {
180         addTestLiveData(VOLUME_MEDIA_URI);
181         mFakePanelContent.setViewType(0 /* viewType */);
182 
183         final PanelSlicesAdapter adapter =
184                 new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
185         final int position = 0;
186         final ViewGroup view = new FrameLayout(mContext);
187         final SliceRowViewHolder viewHolder = adapter.onCreateViewHolder(view, 0/* viewType */);
188         adapter.onBindViewHolder(viewHolder, position);
189 
190         assertThat(viewHolder.isDividerAllowedBelow()).isTrue();
191     }
192 
193     @Test
outputSwitcherSlice_shouldAddFirstItemPadding()194     public void outputSwitcherSlice_shouldAddFirstItemPadding() {
195         addTestLiveData(MEDIA_OUTPUT_SLICE_URI);
196 
197         final PanelSlicesAdapter adapter =
198                 new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
199         final int position = 0;
200         final ViewGroup view = new FrameLayout(mContext);
201         final SliceRowViewHolder viewHolder =
202                 adapter.onCreateViewHolder(view, PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON);
203 
204         adapter.onBindViewHolder(viewHolder, position);
205 
206         assertThat(viewHolder.mSliceSliderLayout.getPaddingTop()).isEqualTo(
207                 mPanelFragment.getResources().getDimensionPixelSize(
208                         R.dimen.output_switcher_slice_padding_top));
209     }
210 
211     @Test
outputSwitcherGroupSlice_shouldAddFirstItemPadding()212     public void outputSwitcherGroupSlice_shouldAddFirstItemPadding() {
213         addTestLiveData(MEDIA_OUTPUT_GROUP_SLICE_URI);
214 
215         final PanelSlicesAdapter adapter =
216                 new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
217         final int position = 0;
218         final ViewGroup view = new FrameLayout(mContext);
219         final SliceRowViewHolder viewHolder =
220                 adapter.onCreateViewHolder(view, PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON);
221 
222         adapter.onBindViewHolder(viewHolder, position);
223 
224         assertThat(viewHolder.mSliceSliderLayout.getPaddingTop()).isEqualTo(
225                 mPanelFragment.getResources().getDimensionPixelSize(
226                         R.dimen.output_switcher_slice_padding_top));
227     }
228 
229     @Test
mediaOutputIndicatorSlice_notSliderPanel_noSliderLayout()230     public void mediaOutputIndicatorSlice_notSliderPanel_noSliderLayout() {
231         addTestLiveData(MEDIA_OUTPUT_INDICATOR_SLICE_URI);
232 
233         final PanelSlicesAdapter adapter =
234                 new PanelSlicesAdapter(mPanelFragment, mData, 0 /* metrics category */);
235         final int position = 0;
236         final ViewGroup view = new FrameLayout(mContext);
237         final SliceRowViewHolder viewHolder =
238                 adapter.onCreateViewHolder(view, 0 /* view type*/);
239 
240         adapter.onBindViewHolder(viewHolder, position);
241 
242         assertThat(viewHolder.mSliceSliderLayout).isNull();
243     }
244 
245     @Test
onCreateViewHolder_viewTypeSlider_verifyLayout()246     public void onCreateViewHolder_viewTypeSlider_verifyLayout() {
247         final PanelSlicesAdapter adapter =
248                 new PanelSlicesAdapter(mPanelFragment, mData, 0);
249         final ViewGroup view = new FrameLayout(mContext);
250         final ArgumentCaptor<Integer> intArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
251 
252         adapter.onCreateViewHolder(view, VIEW_TYPE_SLIDER);
253 
254         verify(sLayoutInflater).inflate(intArgumentCaptor.capture(), eq(view), eq(false));
255         assertThat(intArgumentCaptor.getValue()).isEqualTo(R.layout.panel_slice_slider_row);
256     }
257 
258     @Test
onCreateViewHolder_viewTypeSliderLargeIcon_verifyLayout()259     public void onCreateViewHolder_viewTypeSliderLargeIcon_verifyLayout() {
260         final PanelSlicesAdapter adapter = new PanelSlicesAdapter(mPanelFragment, mData, 0);
261         final ViewGroup view = new FrameLayout(mContext);
262         final ArgumentCaptor<Integer> intArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
263 
264         adapter.onCreateViewHolder(view, VIEW_TYPE_SLIDER_LARGE_ICON);
265 
266         verify(sLayoutInflater).inflate(intArgumentCaptor.capture(), eq(view), eq(false));
267         assertThat(intArgumentCaptor.getValue()).isEqualTo(
268                 R.layout.panel_slice_slider_row_large_icon);
269     }
270 
271     @Test
onCreateViewHolder_viewTypeDefault_verifyLayout()272     public void onCreateViewHolder_viewTypeDefault_verifyLayout() {
273         final PanelSlicesAdapter adapter =
274                 new PanelSlicesAdapter(mPanelFragment, mData, 0);
275         final ViewGroup view = new FrameLayout(mContext);
276         final ArgumentCaptor<Integer> intArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
277 
278         adapter.onCreateViewHolder(view, 0);
279 
280         verify(sLayoutInflater).inflate(intArgumentCaptor.capture(), eq(view), eq(false));
281         assertThat(intArgumentCaptor.getValue()).isEqualTo(R.layout.panel_slice_row);
282     }
283 
284     @Implements(LayoutInflater.class)
285     public static class ShadowLayoutInflater {
286 
287         @Implementation
from(Context context)288         public static LayoutInflater from(Context context) {
289             final LayoutInflater inflater =
290                     (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
291             if (inflater == null) {
292                 throw new AssertionError("LayoutInflater not found.");
293             }
294             sLayoutInflater = spy(inflater);
295             return sLayoutInflater;
296         }
297     }
298 }
299