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 package com.android.tv.tests.ui; 17 18 import static junit.framework.Assert.assertTrue; 19 20 import android.app.Instrumentation; 21 import android.app.UiAutomation; 22 import android.content.Context; 23 import android.content.res.Resources; 24 import android.os.Build; 25 import android.os.SystemClock; 26 import android.support.test.uiautomator.BySelector; 27 import android.support.test.uiautomator.Configurator; 28 import android.support.test.uiautomator.SearchCondition; 29 import android.support.test.uiautomator.UiDevice; 30 import android.support.test.uiautomator.Until; 31 import android.view.InputDevice; 32 import android.view.KeyEvent; 33 import androidx.test.InstrumentationRegistry; 34 import com.android.tv.testing.data.ChannelInfo; 35 import com.android.tv.testing.testinput.ChannelStateData; 36 import com.android.tv.testing.testinput.TestInputControlConnection; 37 import com.android.tv.testing.testinput.TestInputControlUtils; 38 import com.android.tv.testing.uihelper.Constants; 39 import com.android.tv.testing.uihelper.LiveChannelsUiDeviceHelper; 40 import com.android.tv.testing.uihelper.MenuHelper; 41 import com.android.tv.testing.uihelper.SidePanelHelper; 42 import com.android.tv.testing.uihelper.UiDeviceAsserts; 43 import com.android.tv.testing.uihelper.UiDeviceUtils; 44 import org.junit.rules.ExternalResource; 45 46 /** UIHelpers and TestInputController for LiveChannels. */ 47 public class LiveChannelsTestController extends ExternalResource { 48 49 private final TestInputControlConnection mConnection = new TestInputControlConnection(); 50 51 public MenuHelper menuHelper; 52 public SidePanelHelper sidePanelHelper; 53 public LiveChannelsUiDeviceHelper liveChannelsHelper; 54 55 private UiDevice mDevice; 56 private Resources mTargetResources; 57 private Instrumentation mInstrumentation; 58 assertPressKeyUp(int keyCode, boolean sync)59 private void assertPressKeyUp(int keyCode, boolean sync) { 60 assertPressKey(KeyEvent.ACTION_UP, keyCode, sync); 61 } 62 assertPressKey(int action, int keyCode, boolean sync)63 private void assertPressKey(int action, int keyCode, boolean sync) { 64 long eventTime = SystemClock.uptimeMillis(); 65 KeyEvent event = 66 new KeyEvent( 67 eventTime, 68 eventTime, 69 action, 70 keyCode, 71 0, 72 0, 73 -1, 74 0, 75 0, 76 InputDevice.SOURCE_KEYBOARD); 77 assertTrue("Failed to inject key up event:" + event, injectEvent(event, sync)); 78 } 79 getUiAutomation()80 private UiAutomation getUiAutomation() { 81 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 82 int flags = Configurator.getInstance().getUiAutomationFlags(); 83 return mInstrumentation.getUiAutomation(flags); 84 } else { 85 return mInstrumentation.getUiAutomation(); 86 } 87 } 88 89 @Override before()90 protected void before() throws Exception { 91 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 92 Context context = mInstrumentation.getContext(); 93 context.bindService( 94 TestInputControlUtils.createIntent(), mConnection, Context.BIND_AUTO_CREATE); 95 mDevice = UiDevice.getInstance(mInstrumentation); 96 mTargetResources = mInstrumentation.getTargetContext().getResources(); 97 menuHelper = new MenuHelper(mDevice, mTargetResources); 98 sidePanelHelper = new SidePanelHelper(mDevice, mTargetResources); 99 liveChannelsHelper = new LiveChannelsUiDeviceHelper(mDevice, mTargetResources, context); 100 } 101 102 @Override after()103 protected void after() { 104 if (mConnection.isBound()) { 105 mInstrumentation.getContext().unbindService(mConnection); 106 } 107 108 // TODO: robustly handle left over pops from failed tests. 109 // Clear any side panel, menu, ... 110 // Scene container should not be checked here because pressing the BACK key in some scenes 111 // might launch the home screen. 112 if (mDevice.hasObject(Constants.SIDE_PANEL) 113 || mDevice.hasObject(Constants.MENU) 114 || mDevice.hasObject(Constants.PROGRAM_GUIDE)) { 115 mDevice.pressBack(); 116 } 117 // To destroy the activity to make sure next test case's activity launch check works well. 118 mDevice.pressBack(); 119 } 120 121 /** 122 * Update the channel state to {@code data} then tune to that channel. 123 * 124 * @param data the state to update the channel with. 125 * @param channel the channel to tune to 126 */ updateThenTune(ChannelStateData data, ChannelInfo channel)127 protected void updateThenTune(ChannelStateData data, ChannelInfo channel) { 128 mConnection.updateChannelState(channel, data); 129 pressKeysForChannel(channel); 130 } 131 132 /** 133 * Send the keys for the channel number of {@code channel} and press the DPAD center. 134 * 135 * <p>Usually this will tune to the given channel. 136 */ pressKeysForChannel(ChannelInfo channel)137 public void pressKeysForChannel(ChannelInfo channel) { 138 UiDeviceUtils.pressKeys(mDevice, channel.number); 139 UiDeviceAsserts.assertWaitForCondition( 140 mDevice, Until.hasObject(Constants.KEYPAD_CHANNEL_SWITCH)); 141 mDevice.pressDPadCenter(); 142 } 143 assertHas(BySelector bySelector, boolean expected)144 public void assertHas(BySelector bySelector, boolean expected) { 145 UiDeviceAsserts.assertHas(mDevice, bySelector, expected); 146 } 147 assertWaitForCondition(SearchCondition<Boolean> booleanSearchCondition)148 public void assertWaitForCondition(SearchCondition<Boolean> booleanSearchCondition) { 149 UiDeviceAsserts.assertWaitForCondition(mDevice, booleanSearchCondition); 150 } 151 assertWaitForCondition(SearchCondition<Boolean> gone, long timeout)152 public void assertWaitForCondition(SearchCondition<Boolean> gone, long timeout) { 153 UiDeviceAsserts.assertWaitForCondition(mDevice, gone, timeout); 154 } 155 assertWaitUntilFocused(BySelector bySelector)156 public void assertWaitUntilFocused(BySelector bySelector) { 157 UiDeviceAsserts.assertWaitUntilFocused(mDevice, bySelector); 158 } 159 getTargetResources()160 public Resources getTargetResources() { 161 return mTargetResources; 162 } 163 getUiDevice()164 public UiDevice getUiDevice() { 165 return mDevice; 166 } 167 injectEvent(KeyEvent event, boolean sync)168 public boolean injectEvent(KeyEvent event, boolean sync) { 169 return getUiAutomation().injectInputEvent(event, sync); 170 } 171 pressBack()172 public void pressBack() { 173 mDevice.pressBack(); 174 } 175 pressDPadCenter()176 public void pressDPadCenter() { 177 pressDPadCenter(1); 178 } 179 pressDPadCenter(int repeat)180 public void pressDPadCenter(int repeat) { 181 UiDevice.getInstance(mInstrumentation).waitForIdle(); 182 for (int i = 0; i < repeat; ++i) { 183 assertPressKey(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, false); 184 if (i < repeat - 1) { 185 assertPressKeyUp(KeyEvent.KEYCODE_DPAD_CENTER, false); 186 } 187 } 188 // Send last key event synchronously. 189 assertPressKeyUp(KeyEvent.KEYCODE_DPAD_CENTER, true); 190 } 191 pressDPadDown()192 public void pressDPadDown() { 193 mDevice.pressDPadDown(); 194 } 195 pressDPadLeft()196 public void pressDPadLeft() { 197 mDevice.pressDPadLeft(); 198 } 199 pressDPadRight()200 public void pressDPadRight() { 201 mDevice.pressDPadRight(); 202 } 203 pressDPadUp()204 public void pressDPadUp() { 205 mDevice.pressDPadUp(); 206 } 207 pressEnter()208 public void pressEnter() { 209 mDevice.pressEnter(); 210 } 211 pressHome()212 public void pressHome() { 213 mDevice.pressHome(); 214 } 215 pressKeyCode(int keyCode)216 public void pressKeyCode(int keyCode) { 217 mDevice.pressKeyCode(keyCode); 218 } 219 pressMenu()220 public void pressMenu() { 221 mDevice.pressMenu(); 222 } 223 waitForIdle()224 public void waitForIdle() { 225 mDevice.waitForIdle(); 226 } 227 waitForIdleSync()228 public void waitForIdleSync() { 229 mInstrumentation.waitForIdleSync(); 230 } 231 } 232