/* * Copyright (C) 2015 Google Inc. * * 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.talkback.formatter; import android.app.Instrumentation; import android.os.SystemClock; import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.widget.EditText; import com.android.talkback.R; import com.android.talkback.SpeechCleanupUtils; import com.android.talkback.SpeechController; import com.google.android.marvin.talkback.TalkBackService; import com.android.talkback.Utterance; import com.googlecode.eyesfree.testing.CharSequenceFilter; import com.googlecode.eyesfree.testing.TalkBackInstrumentationTestCase; import com.googlecode.eyesfree.testing.UtteranceFilter; import java.lang.CharSequence; import java.util.List; public class TextFormattersTest extends TalkBackInstrumentationTestCase { private TalkBackService mTalkBack; private Instrumentation mInstrumentation; @Override protected void setUp() throws Exception { super.setUp(); mTalkBack = getService(); mInstrumentation = getInstrumentation(); assertNotNull("Obtained TalkBack instance", mTalkBack); } @MediumTest public void testChangedTextFormatter_deleteCharacter() throws Exception { internalTestChangedTextFormatter_deleteCharacter("hello"); internalTestChangedTextFormatter_deleteCharacter("hello "); } private void internalTestChangedTextFormatter_deleteCharacter(String text) throws Exception { if (text.length() < 1) { throw new IllegalArgumentException("This test case can only handle non-empty strings"); } // Set up the test. setContentView(R.layout.text_activity); getViewForId(R.id.username).requestFocus(); mInstrumentation.waitForIdleSync(); mInstrumentation.sendStringSync(text); waitForAccessibilityIdleSync(); // Delete the last character. startRecordingUtterances(); mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DEL); waitForAccessibilityIdleSync(); final CharSequence lastChar = text.substring(text.length() - 1); final CharSequence expectedSpeech = SpeechCleanupUtils.cleanUp(mAppCtx, lastChar) + " " + mAppCtx.getString(R.string.value_text_removed); final CharSequenceFilter textFilter = new CharSequenceFilter().addContainsIgnoreCase( expectedSpeech); final UtteranceFilter utteranceFilter = new UtteranceFilter().addTextFilter(textFilter); final Utterance utterance = stopRecordingUtterancesAfterMatch(utteranceFilter); assertNotNull("Saw matching utterance", utterance); } @MediumTest public void testSelectedTextFormatter_moveCursorWithKeyboard() throws Exception { setContentView(R.layout.text_activity); getViewForId(R.id.username).requestFocus(); mInstrumentation.waitForIdleSync(); mInstrumentation.sendStringSync("abc"); waitForAccessibilityIdleSync(); checkKeyEventProcessing(KeyEvent.KEYCODE_DPAD_LEFT, "c"); checkKeyEventProcessing(KeyEvent.KEYCODE_DPAD_LEFT, "b"); checkKeyEventProcessing(KeyEvent.KEYCODE_DPAD_RIGHT, "b"); } private void checkKeyEventProcessing(int keyCode, String expectedUtterance) { startRecordingUtterances(); mInstrumentation.sendKeyDownUpSync(keyCode); waitForAccessibilityIdleSync(); CharSequenceFilter textFilter = new CharSequenceFilter().addContainsIgnoreCase( expectedUtterance); UtteranceFilter utteranceFilter = new UtteranceFilter().addTextFilter(textFilter); Utterance utterance = stopRecordingUtterancesAfterMatch(utteranceFilter); assertNotNull("Saw matching utterance", utterance); } @MediumTest public void testSelectedTextFormatter_moveCursorWithVolumeButtons() { setContentView(R.layout.text_activity); getViewForId(R.id.username).requestFocus(); mInstrumentation.waitForIdleSync(); mInstrumentation.sendStringSync("abc"); waitForAccessibilityIdleSync(); mTalkBack.getCursorController().setCursor(getNodeForId(R.id.username)); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_DOWN, "c"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_DOWN, "b"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_UP, "b"); } /** * Volume button simulation needs to be handled separately from key event simulation because the * normal Android system instrumentation will just change the system volume instead of * delivering the volume key events to TalkBack. */ private void sendVolumeKey(int keyCode) { assertTrue(keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN); long down = SystemClock.uptimeMillis(); KeyEvent keyDown = new KeyEvent(down, down, KeyEvent.ACTION_DOWN, keyCode, 0, 0); mTalkBack.onKeyEventShared(keyDown); long up = SystemClock.uptimeMillis(); KeyEvent keyUp = new KeyEvent(down, up, KeyEvent.ACTION_UP, keyCode, 0, 0); mTalkBack.onKeyEventShared(keyUp); } private void checkVolumeButtonProcessing(int keyCode, String expectedUtterance) { startRecordingUtterances(); sendVolumeKey(keyCode); waitForAccessibilityIdleSync(); CharSequenceFilter textFilter = new CharSequenceFilter().addContainsIgnoreCase( expectedUtterance); UtteranceFilter utteranceFilter = new UtteranceFilter().addTextFilter(textFilter); Utterance utterance = stopRecordingUtterancesAfterMatch(utteranceFilter); assertNotNull("Saw matching utterance", utterance); } @MediumTest public void testSelectedTextFormatter_volumeButtonFeedbackNotDuplicated() { setContentView(R.layout.text_activity); getViewForId(R.id.username).requestFocus(); mInstrumentation.waitForIdleSync(); mInstrumentation.sendStringSync("abc"); waitForAccessibilityIdleSync(); mTalkBack.getCursorController().setCursor(getNodeForId(R.id.username)); waitForAccessibilityIdleSync(); // Wait after the ["Edit text", "abc"] feedback. startRecordingUtterances(); sendVolumeKey(KeyEvent.KEYCODE_VOLUME_DOWN); mInstrumentation.waitForIdleSync(); waitForAccessibilityIdleSync(); sendVolumeKey(KeyEvent.KEYCODE_VOLUME_DOWN); mInstrumentation.waitForIdleSync(); waitForAccessibilityIdleSync(); sendVolumeKey(KeyEvent.KEYCODE_VOLUME_DOWN); mInstrumentation.waitForIdleSync(); waitForAccessibilityIdleSync(); CharSequenceFilter textFilter = new CharSequenceFilter().addContainsIgnoreCase("a"); UtteranceFilter utteranceFilter = new UtteranceFilter().addTextFilter(textFilter); stopRecordingUtterancesAfterMatch(utteranceFilter); List<Utterance> utteranceHistory = getUtteranceHistory(); int countB = 0; int countC = 0; for (Utterance utterance : utteranceHistory) { for (CharSequence spoken : utterance.getSpoken()) { if (spoken.toString().contains("b")) { countB++; } if (spoken.toString().contains("c")) { countC++; } } } assertEquals(1, countB); assertEquals(1, countC); } @MediumTest public void testInputShortText_uninterruptibleUtterance() throws Exception { setContentView(R.layout.text_activity); startRecordingUtterances(); getViewForId(R.id.username).requestFocus(); mInstrumentation.waitForIdleSync(); mInstrumentation.sendStringSync("a"); waitForAccessibilityIdleSync(); final CharSequenceFilter textFilter = new CharSequenceFilter().addContainsIgnoreCase("a"); final UtteranceFilter utteranceFilter = new UtteranceFilter().addTextFilter(textFilter); Utterance utterance = stopRecordingUtterancesAfterMatch(utteranceFilter); assertNotNull("Saw matching utterance", utterance); assertEquals(SpeechController.QUEUE_MODE_UNINTERRUPTIBLE, utterance.getMetadata(). getInt(Utterance.KEY_METADATA_QUEUING)); } @MediumTest public void testPasswordField_moveCursor() { setContentView(R.layout.text_activity); waitForAccessibilityIdleSync(); final EditText passwordTextView = (EditText) getViewForId(R.id.password); mInstrumentation.runOnMainSync(new Runnable() { @Override public void run() { passwordTextView.setText("abcdefg"); passwordTextView.requestFocus(); } }); waitForAccessibilityIdleSync(); mInstrumentation.runOnMainSync(new Runnable() { @Override public void run() { passwordTextView.setSelection(1, 1); } }); waitForAccessibilityIdleSync(); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_UP, "Character 2"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_UP, "Character 3"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_UP, "Character 4"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_DOWN, "Character 4"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_DOWN, "Character 3"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_DOWN, "Character 2"); } @MediumTest public void testPasswordField_moveCursorWithKeyboard() { setContentView(R.layout.text_activity); waitForAccessibilityIdleSync(); final EditText passwordTextView = (EditText) getViewForId(R.id.password); mInstrumentation.runOnMainSync(new Runnable() { @Override public void run() { passwordTextView.setText("abcdefg"); passwordTextView.requestFocus(); } }); waitForAccessibilityIdleSync(); mInstrumentation.runOnMainSync(new Runnable() { @Override public void run() { passwordTextView.setSelection(1, 1); } }); waitForAccessibilityIdleSync(); checkKeyEventProcessing(KeyEvent.KEYCODE_DPAD_RIGHT, "Character 2"); checkKeyEventProcessing(KeyEvent.KEYCODE_DPAD_RIGHT, "Character 3"); checkKeyEventProcessing(KeyEvent.KEYCODE_DPAD_RIGHT, "Character 4"); checkKeyEventProcessing(KeyEvent.KEYCODE_DPAD_LEFT, "Character 4"); checkKeyEventProcessing(KeyEvent.KEYCODE_DPAD_LEFT, "Character 3"); checkKeyEventProcessing(KeyEvent.KEYCODE_DPAD_LEFT, "Character 2"); } @MediumTest public void testPassword_select() { setContentView(R.layout.text_activity); waitForAccessibilityIdleSync(); final EditText passwordTextView = (EditText) getViewForId(R.id.password); mInstrumentation.runOnMainSync(new Runnable() { @Override public void run() { passwordTextView.setText("abcdefg"); passwordTextView.requestFocus(); } }); waitForAccessibilityIdleSync(); mInstrumentation.runOnMainSync(new Runnable() { @Override public void run() { passwordTextView.setSelection(1, 1); } }); waitForAccessibilityIdleSync(); mTalkBack.getCursorController().setSelectionModeActive(getNodeForView(passwordTextView), true); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_UP, "Character 2, selected"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_UP, "Character 3, selected"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_UP, "Character 4, selected"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_DOWN, "Character 4, unselected"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_DOWN, "Character 3, unselected"); checkVolumeButtonProcessing(KeyEvent.KEYCODE_VOLUME_DOWN, "Character 2, unselected"); } }