KeyboardShortcutsTestCaseWithToolbar.java revision b31c3281d870e9abb673db239234d580dcc4feff
1/* 2 * Copyright (C) 2015 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 17package androidx.appcompat.app; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertFalse; 21import static org.junit.Assert.assertTrue; 22 23import android.os.Build; 24import android.os.SystemClock; 25import android.support.test.InstrumentationRegistry; 26import android.support.test.filters.SmallTest; 27import android.support.test.rule.ActivityTestRule; 28import android.support.test.runner.AndroidJUnit4; 29import androidx.appcompat.testutils.BaseTestActivity; 30import androidx.appcompat.widget.Toolbar; 31import android.view.KeyEvent; 32import android.view.MenuItem; 33import android.view.View; 34import android.view.Window; 35 36import org.junit.Rule; 37import org.junit.Test; 38import org.junit.runner.RunWith; 39 40@RunWith(AndroidJUnit4.class) 41public class KeyboardShortcutsTestCaseWithToolbar { 42 @Rule 43 public final ActivityTestRule<ToolbarAppCompatActivity> mActivityTestRule = 44 new ActivityTestRule<>(ToolbarAppCompatActivity.class); 45 46 @Test 47 @SmallTest 48 public void testAccessActionBar() throws Throwable { 49 // Since O, we rely on keyboard navigation clusters for jumping to actionbar 50 if (Build.VERSION.SDK_INT <= 25) { 51 return; 52 } 53 final BaseTestActivity activity = mActivityTestRule.getActivity(); 54 55 final View editText = activity.findViewById(androidx.appcompat.test.R.id.editText); 56 mActivityTestRule.runOnUiThread(new Runnable() { 57 @Override 58 public void run() { 59 editText.requestFocus(); 60 } 61 }); 62 63 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 64 sendMetaKey(KeyEvent.KEYCODE_TAB); 65 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 66 67 mActivityTestRule.runOnUiThread(new Runnable() { 68 @Override 69 public void run() { 70 assertFalse(editText.hasFocus()); 71 final View toolbar = activity.findViewById(androidx.appcompat.test.R.id.toolbar); 72 assertTrue(toolbar.hasFocus()); 73 } 74 }); 75 // We rely on keyboard navigation clusters for jumping out of actionbar since normal 76 // navigation won't leaves it. 77 sendMetaKey(KeyEvent.KEYCODE_TAB); 78 79 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 80 81 // Should jump to the first view again. 82 mActivityTestRule.runOnUiThread(new Runnable() { 83 @Override 84 public void run() { 85 assertTrue(editText.hasFocus()); 86 } 87 }); 88 } 89 90 @Test 91 @SmallTest 92 public void testKeyShortcuts() throws Throwable { 93 final ToolbarAppCompatActivity activity = mActivityTestRule.getActivity(); 94 95 final Toolbar toolbar = 96 activity.findViewById(androidx.appcompat.test.R.id.toolbar); 97 98 mActivityTestRule.runOnUiThread(new Runnable() { 99 @Override 100 public void run() { 101 toolbar.inflateMenu(androidx.appcompat.test.R.menu.sample_actions); 102 } 103 }); 104 105 final Boolean[] shareItemClicked = new Boolean[]{false}; 106 toolbar.getMenu().findItem(androidx.appcompat.test.R.id.action_alpha_shortcut) 107 .setOnMenuItemClickListener( 108 new MenuItem.OnMenuItemClickListener() { 109 @Override 110 public boolean onMenuItemClick(MenuItem item) { 111 return shareItemClicked[0] = true; 112 } 113 }); 114 115 final Window.Callback cb = activity.getWindow().getCallback(); 116 117 // Make sure valid menu shortcuts get handled by toolbar menu 118 long now = SystemClock.uptimeMillis(); 119 final KeyEvent handledShortcutKey = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, 120 KeyEvent.KEYCODE_A, 0, KeyEvent.META_CTRL_ON); 121 mActivityTestRule.runOnUiThread(new Runnable() { 122 @Override 123 public void run() { 124 assertTrue(cb.dispatchKeyShortcutEvent(handledShortcutKey)); 125 } 126 }); 127 assertTrue(shareItemClicked[0]); 128 129 final KeyEvent unhandledShortcutKey = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, 130 KeyEvent.KEYCODE_D, 0, KeyEvent.META_CTRL_ON); 131 132 // Make sure we aren't eating unused shortcuts. 133 mActivityTestRule.runOnUiThread(new Runnable() { 134 @Override 135 public void run() { 136 assertFalse(cb.dispatchKeyShortcutEvent(unhandledShortcutKey)); 137 } 138 }); 139 140 activity.resetCounters(); 141 142 // Make sure that unhandled shortcuts don't prepare menus (since toolbar is handling that). 143 InstrumentationRegistry.getInstrumentation().sendKeySync(unhandledShortcutKey); 144 assertEquals(1, activity.mKeyShortcutCount); 145 assertEquals(0, activity.mPrepareMenuCount); 146 assertEquals(0, activity.mCreateMenuCount); 147 } 148 149 private void sendMetaKey(int keyCode) throws Throwable { 150 long time = SystemClock.uptimeMillis(); 151 KeyEvent keyDown = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode, 152 0, KeyEvent.META_META_ON); 153 InstrumentationRegistry.getInstrumentation().sendKeySync(keyDown); 154 time = SystemClock.uptimeMillis(); 155 KeyEvent keyUp = new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode, 156 0, KeyEvent.META_META_ON); 157 InstrumentationRegistry.getInstrumentation().sendKeySync(keyUp); 158 } 159} 160