blob: e2aff1610e89ea886cfe9b162cf44989e83d0a20 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.policy;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Handler;
import android.provider.Settings;
import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.policy.AccessibilityShortcutController.FrameworkObjectProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.internal.util.reflection.Whitebox;
import java.util.Collections;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@RunWith(AndroidJUnit4.class)
public class AccessibilityShortcutControllerTest {
private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
private @Mock Context mContext;
private @Mock FrameworkObjectProvider mFrameworkObjectProvider;
private @Mock IAccessibilityManager mAccessibilityManagerService;
private @Mock Handler mHandler;
private @Mock AlertDialog.Builder mAlertDialogBuilder;
private @Mock AlertDialog mAlertDialog;
private @Mock AccessibilityServiceInfo mServiceInfo;
private @Mock Resources mResources;
private @Mock Toast mToast;
private MockContentResolver mContentResolver;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContentResolver = new MockContentResolver(mContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getResources()).thenReturn(mResources);
when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt()))
.thenReturn(Collections.singletonList(mServiceInfo));
// Use the extra level of indirection in the object to mock framework objects
AccessibilityManager accessibilityManager =
new AccessibilityManager(mHandler, mAccessibilityManagerService, 0);
when(mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext))
.thenReturn(accessibilityManager);
when(mFrameworkObjectProvider.getAlertDialogBuilder(mContext))
.thenReturn(mAlertDialogBuilder);
when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt()))
.thenReturn(mToast);
when(mResources.getString(anyInt())).thenReturn("Howdy %s");
ResolveInfo resolveInfo = mock(ResolveInfo.class);
when(resolveInfo.loadLabel(anyObject())).thenReturn("Service name");
when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
when(mServiceInfo.getComponentName())
.thenReturn(ComponentName.unflattenFromString(SERVICE_NAME_STRING));
when(mAlertDialogBuilder.setTitle(anyInt())).thenReturn(mAlertDialogBuilder);
when(mAlertDialogBuilder.setCancelable(anyBoolean())).thenReturn(mAlertDialogBuilder);
when(mAlertDialogBuilder.setMessage(anyObject())).thenReturn(mAlertDialogBuilder);
when(mAlertDialogBuilder.setPositiveButton(anyInt(), anyObject()))
.thenReturn(mAlertDialogBuilder);
when(mAlertDialogBuilder.setNegativeButton(anyInt(), anyObject()))
.thenReturn(mAlertDialogBuilder);
when(mAlertDialogBuilder.setOnCancelListener(anyObject())).thenReturn(mAlertDialogBuilder);
when(mAlertDialogBuilder.create()).thenReturn(mAlertDialog);
Window window = mock(Window.class);
Whitebox.setInternalState(window, "mWindowAttributes", new WindowManager.LayoutParams());
when(mAlertDialog.getWindow()).thenReturn(window);
}
@After
public void tearDown() {
}
@Test
public void testShortcutAvailable_withNullServiceIdWhenCreated_shouldReturnFalse() {
configureShortcutDisabled();
assertFalse(getController().isAccessibilityShortcutAvailable());
}
@Test
public void testShortcutAvailable_withNonNullServiceIdWhenCreated_shouldReturnTrue() {
configureShortcutEnabled();
assertTrue(getController().isAccessibilityShortcutAvailable());
}
@Test
public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse() {
configureShortcutEnabled();
AccessibilityShortcutController accessibilityShortcutController = getController();
Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
accessibilityShortcutController.onSettingsChanged();
assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable());
}
@Test
public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue() {
configureShortcutDisabled();
AccessibilityShortcutController accessibilityShortcutController = getController();
configureShortcutEnabled();
accessibilityShortcutController.onSettingsChanged();
assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable());
}
@Test
public void testOnAccessibilityShortcut_firstTime_showsWarningDialog()
throws Exception {
configureShortcutEnabled();
AccessibilityShortcutController accessibilityShortcutController = getController();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
accessibilityShortcutController.performAccessibilityShortcut();
assertEquals(1, Settings.Secure.getInt(
mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0));
verify(mResources).getString(R.string.accessibility_shortcut_toogle_warning);
verify(mAlertDialog).show();
verify(mAccessibilityManagerService).getInstalledAccessibilityServiceList(anyInt());
verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut();
}
@Test
public void testOnAccessibilityShortcut_withDialogShowing_callsServer()
throws Exception {
configureShortcutEnabled();
AccessibilityShortcutController accessibilityShortcutController = getController();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
accessibilityShortcutController.performAccessibilityShortcut();
accessibilityShortcutController.performAccessibilityShortcut();
verify(mToast).show();
verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut();
}
@Test
public void testOnAccessibilityShortcut_ifCanceledFirstTime_showsWarningDialog()
throws Exception {
configureShortcutEnabled();
AccessibilityShortcutController accessibilityShortcutController = getController();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
accessibilityShortcutController.performAccessibilityShortcut();
ArgumentCaptor<AlertDialog.OnCancelListener> cancelListenerCaptor =
ArgumentCaptor.forClass(AlertDialog.OnCancelListener.class);
verify(mAlertDialogBuilder).setOnCancelListener(cancelListenerCaptor.capture());
// Call the cancel callback
cancelListenerCaptor.getValue().onCancel(null);
accessibilityShortcutController.performAccessibilityShortcut();
verify(mAlertDialog, times(2)).show();
}
@Test
public void testClickingDisableButtonInDialog_shouldClearShortcutId() {
configureShortcutEnabled();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
getController().performAccessibilityShortcut();
ArgumentCaptor<DialogInterface.OnClickListener> captor =
ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.disable_accessibility_shortcut),
captor.capture());
// Call the button callback
captor.getValue().onClick(null, 0);
assertTrue(TextUtils.isEmpty(
Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)));
}
@Test
public void testClickingLeaveOnButtonInDialog_shouldLeaveShortcutReady() throws Exception {
configureShortcutEnabled();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
getController().performAccessibilityShortcut();
ArgumentCaptor<DialogInterface.OnClickListener> captor =
ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.leave_accessibility_shortcut_on),
captor.capture());
// Call the button callback, if one exists
if (captor.getValue() != null) {
captor.getValue().onClick(null, 0);
}
assertEquals(SERVICE_NAME_STRING,
Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
assertEquals(1, Settings.Secure.getInt(
mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN));
}
@Test
public void testOnAccessibilityShortcut_afterDialogShown_shouldCallServer() throws Exception {
configureShortcutEnabled();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
getController().performAccessibilityShortcut();
verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
verify(mToast).show();
verify(mAccessibilityManagerService).performAccessibilityShortcut();
}
private void configureShortcutDisabled() {
Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
}
private void configureShortcutEnabled() {
Settings.Secure.putString(
mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING);
}
private AccessibilityShortcutController getController() {
AccessibilityShortcutController accessibilityShortcutController =
new AccessibilityShortcutController(mContext, mHandler);
accessibilityShortcutController.mFrameworkObjectProvider = mFrameworkObjectProvider;
return accessibilityShortcutController;
}
}