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