1 /* 2 * Copyright (C) 2021 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.systemui.car.statusicon; 18 19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.Mockito.eq; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.reset; 27 import static org.mockito.Mockito.spy; 28 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.when; 30 import static org.testng.Assert.assertThrows; 31 32 import android.app.PendingIntent; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.IntentFilter; 36 import android.os.UserHandle; 37 import android.test.suitebuilder.annotation.SmallTest; 38 import android.testing.AndroidTestingRunner; 39 import android.testing.TestableLooper; 40 import android.widget.ImageView; 41 42 import com.android.car.qc.QCItem; 43 import com.android.car.ui.FocusParkingView; 44 import com.android.systemui.R; 45 import com.android.systemui.SysuiTestCase; 46 import com.android.systemui.broadcast.BroadcastDispatcher; 47 import com.android.systemui.car.CarServiceProvider; 48 import com.android.systemui.car.CarSystemUiTest; 49 import com.android.systemui.car.qc.SystemUIQCViewController; 50 import com.android.systemui.settings.UserTracker; 51 import com.android.systemui.statusbar.policy.ConfigurationController; 52 53 import org.junit.After; 54 import org.junit.Before; 55 import org.junit.Test; 56 import org.junit.runner.RunWith; 57 import org.mockito.ArgumentCaptor; 58 import org.mockito.Mock; 59 import org.mockito.MockitoAnnotations; 60 61 @CarSystemUiTest 62 @RunWith(AndroidTestingRunner.class) 63 @TestableLooper.RunWithLooper 64 @SmallTest 65 public class StatusIconPanelControllerTest extends SysuiTestCase { 66 private StatusIconPanelController mStatusIconPanelController; 67 private ImageView mAnchorView; 68 private String mIconTag; 69 private UserHandle mUserHandle; 70 71 @Mock 72 private UserTracker mUserTracker; 73 @Mock 74 private CarServiceProvider mCarServiceProvider; 75 @Mock 76 private BroadcastDispatcher mBroadcastDispatcher; 77 @Mock 78 private ConfigurationController mConfigurationController; 79 @Mock 80 private SystemUIQCViewController mSystemUIQCViewController; 81 82 @Before setUp()83 public void setUp() { 84 MockitoAnnotations.initMocks(this); 85 86 mContext = spy(mContext); 87 mIconTag = mContext.getResources().getString(R.string.qc_icon_tag); 88 mUserHandle = UserHandle.of(1000); 89 when(mUserTracker.getUserHandle()).thenReturn(mUserHandle); 90 91 mStatusIconPanelController = new StatusIconPanelController(mContext, mUserTracker, 92 mCarServiceProvider, mBroadcastDispatcher, mConfigurationController, 93 () -> mSystemUIQCViewController); 94 spyOn(mStatusIconPanelController); 95 mAnchorView = spy(new ImageView(mContext)); 96 mAnchorView.setTag(mIconTag); 97 mAnchorView.setImageDrawable(mContext.getDrawable(R.drawable.ic_bluetooth_status_off)); 98 mAnchorView.setColorFilter(mStatusIconPanelController.getIconHighlightedColor()); 99 reset(mAnchorView); 100 mStatusIconPanelController.attachPanel(mAnchorView, R.layout.qc_display_panel, 101 R.dimen.car_status_icon_panel_default_width); 102 } 103 104 @After tearDown()105 public void tearDown() { 106 mStatusIconPanelController.destroyPanel(); 107 } 108 109 @Test onPanelAnchorViewClicked_panelShowing()110 public void onPanelAnchorViewClicked_panelShowing() { 111 clickAnchorView(); 112 waitForIdleSync(); 113 114 assertThat(mStatusIconPanelController.getPanel().isShowing()).isTrue(); 115 } 116 117 @Test onPanelAnchorViewClicked_statusIconHighlighted()118 public void onPanelAnchorViewClicked_statusIconHighlighted() { 119 clickAnchorView(); 120 waitForIdleSync(); 121 122 verify(mAnchorView).setColorFilter(mStatusIconPanelController.getIconHighlightedColor()); 123 } 124 125 @Test onPanelAnchorViewClicked_panelShowing_panelDismissed()126 public void onPanelAnchorViewClicked_panelShowing_panelDismissed() { 127 clickAnchorView(); 128 129 clickAnchorView(); 130 waitForIdleSync(); 131 132 assertThat(mStatusIconPanelController.getPanel().isShowing()).isFalse(); 133 } 134 135 @Test onPanelAnchorViewClicked_panelShowing_statusIconNotHighlighted()136 public void onPanelAnchorViewClicked_panelShowing_statusIconNotHighlighted() { 137 clickAnchorView(); 138 139 clickAnchorView(); 140 waitForIdleSync(); 141 142 verify(mAnchorView).setColorFilter(mStatusIconPanelController.getIconNotHighlightedColor()); 143 } 144 145 @Test onPanelAnchorViewClicked_sendsIntentToDismissSystemDialogsWithIdentifier()146 public void onPanelAnchorViewClicked_sendsIntentToDismissSystemDialogsWithIdentifier() { 147 ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class); 148 149 clickAnchorView(); 150 waitForIdleSync(); 151 152 verify(mContext).sendBroadcastAsUser(argumentCaptor.capture(), eq(mUserHandle)); 153 assertThat(argumentCaptor.getValue().getAction()).isEqualTo( 154 Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 155 assertThat(argumentCaptor.getValue().getIdentifier()).isEqualTo( 156 mStatusIconPanelController.getIdentifier()); 157 } 158 159 @Test onDismissSystemDialogReceived_fromSelf_panelOpen_doesNotDismissPanel()160 public void onDismissSystemDialogReceived_fromSelf_panelOpen_doesNotDismissPanel() { 161 Intent intent = new Intent(); 162 intent.setAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 163 intent.setIdentifier(mStatusIconPanelController.getIdentifier()); 164 clickAnchorView(); 165 waitForIdleSync(); 166 167 mStatusIconPanelController.getBroadcastReceiver().onReceive(mContext, intent); 168 169 assertThat(mStatusIconPanelController.getPanel().isShowing()).isTrue(); 170 } 171 172 @Test onDismissSystemDialogReceived_fromSelf_panelOpen_statusIconHighlighted()173 public void onDismissSystemDialogReceived_fromSelf_panelOpen_statusIconHighlighted() { 174 Intent intent = new Intent(); 175 intent.setAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 176 intent.setIdentifier(mStatusIconPanelController.getIdentifier()); 177 clickAnchorView(); 178 waitForIdleSync(); 179 180 mStatusIconPanelController.getBroadcastReceiver().onReceive(mContext, intent); 181 182 verify(mAnchorView).setColorFilter(mStatusIconPanelController.getIconHighlightedColor()); 183 } 184 185 @Test onDismissSystemDialogReceived_notFromSelf_panelOpen_dismissesPanel()186 public void onDismissSystemDialogReceived_notFromSelf_panelOpen_dismissesPanel() { 187 Intent intent = new Intent(); 188 intent.setAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 189 clickAnchorView(); 190 waitForIdleSync(); 191 192 mStatusIconPanelController.getBroadcastReceiver().onReceive(mContext, intent); 193 194 assertThat(mStatusIconPanelController.getPanel().isShowing()).isFalse(); 195 } 196 197 @Test onDismissSystemDialogReceived_notFromSelf_panelOpen_statusIconNotHighlighted()198 public void onDismissSystemDialogReceived_notFromSelf_panelOpen_statusIconNotHighlighted() { 199 Intent intent = new Intent(); 200 intent.setAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 201 clickAnchorView(); 202 waitForIdleSync(); 203 204 mStatusIconPanelController.getBroadcastReceiver().onReceive(mContext, intent); 205 206 verify(mAnchorView).setColorFilter(mStatusIconPanelController.getIconNotHighlightedColor()); 207 } 208 209 @Test onDestroy_unregistersListeners()210 public void onDestroy_unregistersListeners() { 211 mStatusIconPanelController.destroyPanel(); 212 213 verify(mConfigurationController).removeCallback(any()); 214 verify(mBroadcastDispatcher).unregisterReceiver(any()); 215 } 216 217 @Test onDestroy_reAttach_throwsException()218 public void onDestroy_reAttach_throwsException() { 219 mStatusIconPanelController.destroyPanel(); 220 221 assertThrows(IllegalStateException.class, () -> mStatusIconPanelController.attachPanel( 222 mAnchorView, R.layout.qc_display_panel, 223 R.dimen.car_status_icon_panel_default_width)); 224 } 225 226 @Test onLayoutDirectionChanged_recreatePanel()227 public void onLayoutDirectionChanged_recreatePanel() { 228 mStatusIconPanelController.getConfigurationListener() 229 .onLayoutDirectionChanged(/* isLayoutRtl= */ true); 230 231 assertThat(mStatusIconPanelController.getPanel()).isNotNull(); 232 } 233 234 @Test onUserChanged_unregisterRegisterReceiver()235 public void onUserChanged_unregisterRegisterReceiver() { 236 int newUser = 999; 237 Context userContext = mock(Context.class); 238 reset(mBroadcastDispatcher); 239 240 mStatusIconPanelController.getUserTrackerCallback() 241 .onUserChanged(newUser, userContext); 242 243 verify(mBroadcastDispatcher).unregisterReceiver( 244 eq(mStatusIconPanelController.getBroadcastReceiver())); 245 verify(mBroadcastDispatcher).registerReceiver( 246 eq(mStatusIconPanelController.getBroadcastReceiver()), 247 any(IntentFilter.class), eq(null), eq(mUserHandle)); 248 } 249 250 @Test onGlobalFocusChanged_panelShowing_panelDismissed()251 public void onGlobalFocusChanged_panelShowing_panelDismissed() { 252 FocusParkingView newFocusView = mock(FocusParkingView.class); 253 clickAnchorView(); 254 waitForIdleSync(); 255 256 mStatusIconPanelController.getFocusChangeListener() 257 .onGlobalFocusChanged(mAnchorView, newFocusView); 258 259 assertThat(mStatusIconPanelController.getPanel().isShowing()).isFalse(); 260 } 261 262 @Test onQCAction_pendingIntentAction_panelDismissed()263 public void onQCAction_pendingIntentAction_panelDismissed() { 264 QCItem qcItem = mock(QCItem.class); 265 PendingIntent action = mock(PendingIntent.class); 266 when(action.isActivity()).thenReturn(true); 267 clickAnchorView(); 268 waitForIdleSync(); 269 270 mStatusIconPanelController.getQCActionListener().onQCAction(qcItem, action); 271 272 assertThat(mStatusIconPanelController.getPanel().isShowing()).isFalse(); 273 } 274 275 @Test onQCAction_actionHandler_panelDismissed()276 public void onQCAction_actionHandler_panelDismissed() { 277 QCItem qcItem = mock(QCItem.class); 278 QCItem.ActionHandler action = mock(QCItem.ActionHandler.class); 279 when(action.isActivity()).thenReturn(true); 280 clickAnchorView(); 281 waitForIdleSync(); 282 283 mStatusIconPanelController.getQCActionListener().onQCAction(qcItem, action); 284 285 assertThat(mStatusIconPanelController.getPanel().isShowing()).isFalse(); 286 } 287 clickAnchorView()288 private void clickAnchorView() { 289 mStatusIconPanelController.getOnClickListener().onClick(mAnchorView); 290 } 291 } 292