Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2016 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 android.view.cts;
     18 
     19 import static org.junit.Assert.assertFalse;
     20 import static org.junit.Assert.assertNotNull;
     21 import static org.junit.Assert.assertTrue;
     22 
     23 import android.app.ActionBar;
     24 import android.app.Activity;
     25 import android.app.Instrumentation;
     26 import android.content.pm.PackageManager;
     27 import android.os.SystemClock;
     28 import android.support.test.InstrumentationRegistry;
     29 import android.support.test.filters.MediumTest;
     30 import android.support.test.rule.ActivityTestRule;
     31 import android.support.test.runner.AndroidJUnit4;
     32 import android.view.KeyEvent;
     33 import android.view.View;
     34 import android.view.ViewGroup;
     35 import android.view.ViewParent;
     36 
     37 import org.junit.Before;
     38 import org.junit.Rule;
     39 import org.junit.Test;
     40 import org.junit.runner.RunWith;
     41 
     42 @MediumTest
     43 @RunWith(AndroidJUnit4.class)
     44 public class ContentPaneFocusTest {
     45     private Instrumentation mInstrumentation;
     46     private Activity mActivity;
     47 
     48     @Rule
     49     public ActivityTestRule<ContentPaneCtsActivity> mActivityRule =
     50             new ActivityTestRule<>(ContentPaneCtsActivity.class);
     51 
     52     @Before
     53     public void setup() {
     54         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     55         mActivity = mActivityRule.getActivity();
     56     }
     57 
     58     @Test
     59     public void testAccessActionBar() throws Throwable {
     60         final View v1 = mActivity.findViewById(R.id.view1);
     61         mActivityRule.runOnUiThread(v1::requestFocus);
     62 
     63         mInstrumentation.waitForIdleSync();
     64         sendMetaHotkey(KeyEvent.KEYCODE_TAB);
     65         mInstrumentation.waitForIdleSync();
     66 
     67         ActionBar action = mActivity.getActionBar();
     68         if (action == null || !action.isShowing()) {
     69             // No action bar, so we only needed to make sure that the shortcut didn't cause
     70             // the framework to crash.
     71             return;
     72         }
     73 
     74         final View actionBar = getActionBarView();
     75         // Should jump to the action bar after meta+tab
     76         mActivityRule.runOnUiThread(() -> {
     77             assertFalse(v1.hasFocus());
     78             assertTrue(actionBar.hasFocus());
     79         });
     80 
     81         boolean isTouchScreen = mActivity.getPackageManager()
     82                 .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
     83         if (isTouchScreen) {
     84             mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
     85             mInstrumentation.waitForIdleSync();
     86 
     87             // Shouldn't leave actionbar with normal keyboard navigation on touchscreens.
     88             mActivityRule.runOnUiThread(() -> assertTrue(actionBar.hasFocus()));
     89         }
     90 
     91         sendMetaHotkey(KeyEvent.KEYCODE_TAB);
     92         mInstrumentation.waitForIdleSync();
     93 
     94         // Should jump to the first view again.
     95         mActivityRule.runOnUiThread(() -> assertTrue(v1.hasFocus()));
     96 
     97         if (isTouchScreen) {
     98             mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_UP);
     99             mInstrumentation.waitForIdleSync();
    100             // Now it shouldn't go up to action bar -- it doesn't allow taking focus once left
    101             // but only for touch screens.
    102             mActivityRule.runOnUiThread(() -> assertTrue(v1.hasFocus()));
    103         }
    104     }
    105 
    106     @Test
    107     public void testNoFocusablesInContent() throws Throwable {
    108         ViewGroup top = mActivity.findViewById(R.id.linearlayout);
    109         top.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
    110         mActivityRule.runOnUiThread(top::clearFocus);
    111         mInstrumentation.waitForIdleSync();
    112         top.clearFocus();
    113         final View content = mActivity.findViewById(android.R.id.content);
    114         assertTrue(content.findFocus() == null);
    115         sendMetaHotkey(KeyEvent.KEYCODE_TAB);
    116         mInstrumentation.waitForIdleSync();
    117 
    118         ActionBar action = mActivity.getActionBar();
    119         if (action == null || !action.isShowing()) {
    120             // No action bar, so we only needed to make sure that the shortcut didn't cause
    121             // the framework to crash.
    122             return;
    123         }
    124 
    125         assertTrue(getActionBarView().hasFocus());
    126     }
    127 
    128     private View getActionBarView() {
    129         final View content = mActivity.findViewById(android.R.id.content);
    130         assertNotNull(content);
    131         final ViewParent viewParent = content.getParent();
    132         assertNotNull(viewParent);
    133         assertTrue(viewParent instanceof ViewGroup);
    134         ViewGroup parent = (ViewGroup) viewParent;
    135         View actionBarView = null;
    136         for (int i = 0; i < parent.getChildCount(); i++) {
    137             View child = parent.getChildAt(i);
    138             if ("android:action_bar".equals(child.getTransitionName())) {
    139                 actionBarView = child;
    140                 break;
    141             }
    142         }
    143         assertNotNull(actionBarView);
    144         return actionBarView;
    145     }
    146 
    147     private void sendMetaHotkey(int keyCode) throws Throwable {
    148         sendMetaKey(KeyEvent.ACTION_DOWN);
    149         long time = SystemClock.uptimeMillis();
    150         KeyEvent metaHotkey = new KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode,
    151                 0, KeyEvent.META_META_ON | KeyEvent.META_META_LEFT_ON);
    152         mInstrumentation.sendKeySync(metaHotkey);
    153         time = SystemClock.uptimeMillis();
    154         metaHotkey = new KeyEvent(time, time, KeyEvent.ACTION_UP, keyCode,
    155                 0, KeyEvent.META_META_ON | KeyEvent.META_META_LEFT_ON);
    156         mInstrumentation.sendKeySync(metaHotkey);
    157         Thread.sleep(2);
    158         sendMetaKey(KeyEvent.ACTION_UP);
    159     }
    160 
    161     private void sendMetaKey(int action) throws Throwable {
    162         long time = SystemClock.uptimeMillis();
    163         KeyEvent keyEvent = new KeyEvent(time, time, action, KeyEvent.KEYCODE_META_LEFT, 0,
    164                 KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_ON);
    165         mInstrumentation.sendKeySync(keyEvent);
    166         Thread.sleep(2);
    167     }
    168 }
    169