1 /* 2 * Copyright (C) 2023 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.ArgumentMatchers.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 31 import android.app.PendingIntent; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.os.UserHandle; 36 import android.testing.AndroidTestingRunner; 37 import android.testing.TestableLooper; 38 import android.widget.ImageView; 39 40 import androidx.test.filters.SmallTest; 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.CarDeviceProvisionedController; 48 import com.android.systemui.car.CarServiceProvider; 49 import com.android.systemui.car.CarSystemUiTest; 50 import com.android.systemui.car.systembar.element.CarSystemBarElementInitializer; 51 import com.android.systemui.settings.UserTracker; 52 import com.android.systemui.statusbar.policy.ConfigurationController; 53 54 import org.junit.After; 55 import org.junit.Before; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 import org.mockito.ArgumentCaptor; 59 import org.mockito.Mock; 60 import org.mockito.MockitoAnnotations; 61 62 @CarSystemUiTest 63 @RunWith(AndroidTestingRunner.class) 64 @TestableLooper.RunWithLooper 65 @SmallTest 66 public class StatusIconPanelViewControllerTest extends SysuiTestCase { 67 private StatusIconPanelViewController mViewController; 68 private ImageView mAnchorView; 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 CarDeviceProvisionedController mDeviceProvisionedController; 81 @Mock 82 private CarSystemBarElementInitializer mCarSystemBarElementInitializer; 83 84 @Before setUp()85 public void setUp() { 86 MockitoAnnotations.initMocks(this); 87 88 mContext = spy(mContext); 89 mUserHandle = UserHandle.of(1000); 90 when(mUserTracker.getUserHandle()).thenReturn(mUserHandle); 91 92 mAnchorView = spy(new ImageView(mContext)); 93 mAnchorView.setImageDrawable(mContext.getDrawable(R.drawable.ic_bluetooth_status_off)); 94 mAnchorView.setColorFilter(mContext.getColor(R.color.car_status_icon_color)); 95 mViewController = new StatusIconPanelViewController.Builder(mContext, mUserTracker, 96 mCarServiceProvider, mBroadcastDispatcher, mConfigurationController, 97 mDeviceProvisionedController, mCarSystemBarElementInitializer).build(mAnchorView, 98 R.layout.qc_display_panel, R.dimen.car_status_icon_panel_default_width); 99 spyOn(mViewController); 100 reset(mAnchorView); 101 mViewController.init(); 102 } 103 104 @After tearDown()105 public void tearDown() { 106 // The view controller must be detached before we move on since it creates a real 107 // PopupWindow instance. Otherwise, the window will continue to be present and cause 108 // instability in future tests. 109 mViewController.onViewDetached(); 110 } 111 112 @Test onViewAttached_registersListeners()113 public void onViewAttached_registersListeners() { 114 mViewController.onViewAttached(); 115 verify(mBroadcastDispatcher).registerReceiver(any(), any(), any(), any()); 116 verify(mUserTracker).addCallback(any(), any()); 117 verify(mConfigurationController).addCallback(any()); 118 verify(mCarServiceProvider).addListener(any()); 119 } 120 121 @Test onViewDetached_unregistersListeners()122 public void onViewDetached_unregistersListeners() { 123 mViewController.onViewDetached(); 124 verify(mCarServiceProvider).removeListener(any()); 125 verify(mConfigurationController).removeCallback(any()); 126 verify(mUserTracker).removeCallback(any()); 127 verify(mBroadcastDispatcher).unregisterReceiver(any()); 128 assertThat(mViewController.getPanel()).isNull(); 129 } 130 131 @Test onPanelAnchorViewClicked_panelShowing()132 public void onPanelAnchorViewClicked_panelShowing() { 133 clickAnchorView(); 134 waitForIdleSync(); 135 136 assertThat(mViewController.getPanel().isShowing()).isTrue(); 137 } 138 139 @Test onPanelAnchorViewClicked_panelShowing_panelDismissed()140 public void onPanelAnchorViewClicked_panelShowing_panelDismissed() { 141 clickAnchorView(); 142 143 clickAnchorView(); 144 waitForIdleSync(); 145 146 assertThat(mViewController.getPanel().isShowing()).isFalse(); 147 } 148 149 @Test onPanelAnchorViewClicked_sendsIntentToDismissSystemDialogsWithIdentifier()150 public void onPanelAnchorViewClicked_sendsIntentToDismissSystemDialogsWithIdentifier() { 151 ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class); 152 153 clickAnchorView(); 154 waitForIdleSync(); 155 156 verify(mContext).sendBroadcastAsUser(argumentCaptor.capture(), eq(mUserHandle)); 157 assertThat(argumentCaptor.getValue().getAction()).isEqualTo( 158 Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 159 assertThat(argumentCaptor.getValue().getIdentifier()).isEqualTo( 160 mViewController.getIdentifier()); 161 } 162 163 @Test onDismissSystemDialogReceived_fromSelf_panelOpen_doesNotDismissPanel()164 public void onDismissSystemDialogReceived_fromSelf_panelOpen_doesNotDismissPanel() { 165 Intent intent = new Intent(); 166 intent.setAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 167 intent.setIdentifier(mViewController.getIdentifier()); 168 clickAnchorView(); 169 waitForIdleSync(); 170 171 mViewController.getBroadcastReceiver().onReceive(mContext, intent); 172 173 assertThat(mViewController.getPanel().isShowing()).isTrue(); 174 } 175 176 @Test onDismissSystemDialogReceived_notFromSelf_panelOpen_dismissesPanel()177 public void onDismissSystemDialogReceived_notFromSelf_panelOpen_dismissesPanel() { 178 Intent intent = new Intent(); 179 intent.setAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 180 clickAnchorView(); 181 waitForIdleSync(); 182 183 mViewController.getBroadcastReceiver().onReceive(mContext, intent); 184 185 assertThat(mViewController.getPanel().isShowing()).isFalse(); 186 } 187 188 @Test onLayoutDirectionChanged_recreatePanel()189 public void onLayoutDirectionChanged_recreatePanel() { 190 mViewController.getConfigurationListener() 191 .onLayoutDirectionChanged(/* isLayoutRtl= */ true); 192 193 assertThat(mViewController.getPanel()).isNotNull(); 194 } 195 196 @Test onUserChanged_unregisterRegisterReceiver()197 public void onUserChanged_unregisterRegisterReceiver() { 198 int newUser = 999; 199 Context userContext = mock(Context.class); 200 reset(mBroadcastDispatcher); 201 202 mViewController.getUserTrackerCallback() 203 .onUserChanged(newUser, userContext); 204 205 verify(mBroadcastDispatcher).unregisterReceiver( 206 eq(mViewController.getBroadcastReceiver())); 207 verify(mBroadcastDispatcher).registerReceiver( 208 eq(mViewController.getBroadcastReceiver()), 209 any(IntentFilter.class), eq(null), eq(mUserHandle)); 210 } 211 212 @Test onGlobalFocusChanged_panelShowing_panelDismissed()213 public void onGlobalFocusChanged_panelShowing_panelDismissed() { 214 FocusParkingView newFocusView = mock(FocusParkingView.class); 215 clickAnchorView(); 216 waitForIdleSync(); 217 218 mViewController.getFocusChangeListener() 219 .onGlobalFocusChanged(mAnchorView, newFocusView); 220 221 assertThat(mViewController.getPanel().isShowing()).isFalse(); 222 } 223 224 @Test onQCAction_pendingIntentAction_panelDismissed()225 public void onQCAction_pendingIntentAction_panelDismissed() { 226 QCItem qcItem = mock(QCItem.class); 227 PendingIntent action = mock(PendingIntent.class); 228 when(action.isActivity()).thenReturn(true); 229 clickAnchorView(); 230 waitForIdleSync(); 231 232 mViewController.getQCActionListener().onQCAction(qcItem, action); 233 234 assertThat(mViewController.getPanel().isShowing()).isFalse(); 235 } 236 237 @Test onQCAction_actionHandler_panelDismissed()238 public void onQCAction_actionHandler_panelDismissed() { 239 QCItem qcItem = mock(QCItem.class); 240 QCItem.ActionHandler action = mock(QCItem.ActionHandler.class); 241 when(action.isActivity()).thenReturn(true); 242 clickAnchorView(); 243 waitForIdleSync(); 244 245 mViewController.getQCActionListener().onQCAction(qcItem, action); 246 247 assertThat(mViewController.getPanel().isShowing()).isFalse(); 248 } 249 clickAnchorView()250 private void clickAnchorView() { 251 mViewController.getOnClickListener().onClick(mAnchorView); 252 } 253 } 254