blob: ee800689bb62c501a1be9cf42f8b606b15c1ba95 [file] [log] [blame]
/*
* Copyright (C) 2008 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 android.server.wm;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.Instrumentation;
import android.app.Presentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.server.wm.cts.R;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.PollingCheck;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@Presubmit
@MediumTest
@RunWith(AndroidJUnit4.class)
public class WindowTest {
private static final String TAG = "WindowTest";
private static final int VIEWGROUP_LAYOUT_HEIGHT = 100;
private static final int VIEWGROUP_LAYOUT_WIDTH = 200;
private Instrumentation mInstrumentation;
private WindowCtsActivity mActivity;
private Window mWindow;
private Window.Callback mWindowCallback;
private SurfaceView mSurfaceView;
// for testing setLocalFocus
private ProjectedPresentation mPresentation;
private VirtualDisplay mVirtualDisplay;
@Rule
public ActivityTestRule<WindowCtsActivity> mActivityRule =
new ActivityTestRule<>(WindowCtsActivity.class);
@Before
public void setup() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
mWindow = mActivity.getWindow();
mWindowCallback = mock(Window.Callback.class);
doReturn(true).when(mWindowCallback).dispatchKeyEvent(any());
doReturn(true).when(mWindowCallback).dispatchTouchEvent(any());
doReturn(true).when(mWindowCallback).dispatchTrackballEvent(any());
doReturn(true).when(mWindowCallback).dispatchGenericMotionEvent(any());
doReturn(true).when(mWindowCallback).dispatchPopulateAccessibilityEvent(any());
doReturn(true).when(mWindowCallback).onMenuItemSelected(anyInt(), any());
}
@After
public void teardown() {
if (mActivity != null) {
mActivity.setFlagFalse();
}
}
@UiThreadTest
@Test
public void testConstructor() {
mWindow = new MockWindow(mActivity);
assertSame(mActivity, mWindow.getContext());
}
/**
* Test flags related methods:
* 1. addFlags: add the given flag to WindowManager.LayoutParams.flags, if add more than one
* in sequence, flags will be set to formerFlag | latterFlag.
* 2. setFlags: _1. set the flags of the window.
* _2. test invocation of Window.Callback#onWindowAttributesChanged.
* 3. clearFlags: clear the flag bits as specified in flags.
*/
@Test
public void testOpFlags() {
mWindow = new MockWindow(mActivity);
final WindowManager.LayoutParams attrs = mWindow.getAttributes();
assertEquals(0, attrs.flags);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
assertEquals(WindowManager.LayoutParams.FLAG_FULLSCREEN, attrs.flags);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_DITHER);
assertEquals(WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_DITHER, attrs.flags);
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
assertEquals(WindowManager.LayoutParams.FLAG_DITHER, attrs.flags);
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DITHER);
assertEquals(0, attrs.flags);
mWindow.setCallback(mWindowCallback);
verify(mWindowCallback, never()).onWindowAttributesChanged(any());
// mask == flag, no bit of flag need to be modified.
mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
assertEquals(WindowManager.LayoutParams.FLAG_FULLSCREEN, attrs.flags);
// Test if the callback method is called by system
verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DITHER);
}
@Test
public void testFindViewById() {
TextView v = mWindow.findViewById(R.id.listview_window);
assertNotNull(v);
assertEquals(R.id.listview_window, v.getId());
}
@Test
public void testRequireViewById() {
TextView v = mWindow.requireViewById(R.id.listview_window);
assertNotNull(v);
assertEquals(R.id.listview_window, v.getId());
}
@Test(expected = IllegalArgumentException.class)
public void testRequireViewByIdNoId() {
TextView v = mWindow.requireViewById(View.NO_ID);
}
@Test(expected = IllegalArgumentException.class)
public void testRequireViewByIdInvalid() {
TextView v = mWindow.requireViewById(R.id.view); // not present in layout
}
/**
* getAttributes: Retrieve the current window attributes associated with this panel.
* Return is 1.the existing window attributes object.
* 2.a freshly created one if there is none.
* setAttributes: Specify custom window attributes.
* Here we just set some parameters to test if it can set, and the window is just
* available in this method. But it's not proper to setAttributes arbitrarily.
* setCallback: Set the Callback interface for this window. In Window.java,
* there is just one method, onWindowAttributesChanged, used.
* getCallback: Return the current Callback interface for this window.
*/
@Test
public void testAccessAttributes() {
mWindow = new MockWindow(mActivity);
// default attributes
WindowManager.LayoutParams attr = mWindow.getAttributes();
assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attr.width);
assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attr.height);
assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION, attr.type);
assertEquals(PixelFormat.OPAQUE, attr.format);
int width = 200;
int height = 300;
WindowManager.LayoutParams param = new WindowManager.LayoutParams(width, height,
WindowManager.LayoutParams.TYPE_BASE_APPLICATION,
WindowManager.LayoutParams.FLAG_DITHER, PixelFormat.RGBA_8888);
mWindow.setCallback(mWindowCallback);
assertSame(mWindowCallback, mWindow.getCallback());
verify(mWindowCallback, never()).onWindowAttributesChanged(any());
mWindow.setAttributes(param);
attr = mWindow.getAttributes();
assertEquals(width, attr.width);
assertEquals(height, attr.height);
assertEquals(WindowManager.LayoutParams.TYPE_BASE_APPLICATION, attr.type);
assertEquals(PixelFormat.RGBA_8888, attr.format);
assertEquals(WindowManager.LayoutParams.FLAG_DITHER, attr.flags);
verify(mWindowCallback, times(1)).onWindowAttributesChanged(attr);
}
/**
* If not set container, the DecorWindow operates as a top-level window, the mHasChildren of
* container is false;
* Otherwise, it will display itself meanwhile container's mHasChildren is true.
*/
@Test
public void testAccessContainer() {
mWindow = new MockWindow(mActivity);
assertNull(mWindow.getContainer());
assertFalse(mWindow.hasChildren());
MockWindow container = new MockWindow(mActivity);
mWindow.setContainer(container);
assertSame(container, mWindow.getContainer());
assertTrue(container.hasChildren());
}
/**
* addContentView: add an additional content view to the screen.
* 1.Added after any existing ones in the screen.
* 2.Existing views are NOT removed.
* getLayoutInflater: Quick access to the {@link LayoutInflater} instance that this Window
* retrieved from its Context.
*/
@UiThreadTest
@Test
public void testAddContentView() {
final ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(VIEWGROUP_LAYOUT_WIDTH,
VIEWGROUP_LAYOUT_HEIGHT);
// The LayoutInflater instance will be inflated to a view and used by
// addContentView,
// id of this view should be same with inflated id.
final LayoutInflater inflater = mActivity.getLayoutInflater();
TextView addedView = (TextView) mWindow.findViewById(R.id.listview_addwindow);
assertNull(addedView);
mWindow.addContentView(inflater.inflate(R.layout.windowstub_addlayout, null), lp);
TextView view = (TextView) mWindow.findViewById(R.id.listview_window);
addedView = (TextView) mWindow.findViewById(R.id.listview_addwindow);
assertNotNull(view);
assertNotNull(addedView);
assertEquals(R.id.listview_window, view.getId());
assertEquals(R.id.listview_addwindow, addedView.getId());
}
/**
* getCurrentFocus: Return the view in this Window that currently has focus, or null if
* there are none.
* The test will be:
* 1. Set focus view to null, get current focus, it should be null
* 2. Set listview_window as focus view, get it and compare.
*/
@UiThreadTest
@Test
public void testGetCurrentFocus() {
TextView v = (TextView) mWindow.findViewById(R.id.listview_window);
v.clearFocus();
assertNull(mWindow.getCurrentFocus());
v.setFocusable(true);
assertTrue(v.isFocusable());
assertTrue(v.requestFocus());
View focus = mWindow.getCurrentFocus();
assertNotNull(focus);
assertEquals(R.id.listview_window, focus.getId());
}
/**
* 1. getDecorView() retrieves the top-level window decor view, which contains the standard
* window frame/decorations and the client's content inside of that, we should check the
* primary components of this view which have no relationship concrete realization of Window.
* Therefore we should check if the size of this view equals to the screen size and the
* ontext is same as Window's context.
* 2. Return null if decor view is not created, else the same with detDecorView.
*/
@Test
public void testDecorView() {
mInstrumentation.waitForIdleSync();
View decor = mWindow.getDecorView();
assertNotNull(decor);
verifyDecorView(decor);
decor = mWindow.peekDecorView();
if (decor != null) {
verifyDecorView(decor);
}
}
private void verifyDecorView(View decor) {
DisplayMetrics dm = new DisplayMetrics();
mActivity.getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenWidth = dm.widthPixels;
int screenHeight = dm.heightPixels;
assertTrue(decor.getWidth() >= screenWidth);
assertTrue(decor.getHeight() >= screenHeight);
assertTrue(decor.getContext() instanceof ContextThemeWrapper);
}
/**
* setVolumeControlStream: Suggests an audio stream whose volume should be changed by
* the hardware volume controls.
* getVolumeControlStream: Gets the suggested audio stream whose volume should be changed by
* the harwdare volume controls.
*/
@Test
public void testAccessVolumeControlStream() {
// Default value is AudioManager.USE_DEFAULT_STREAM_TYPE, see javadoc of
// {@link Activity#setVolumeControlStream}.
assertEquals(AudioManager.USE_DEFAULT_STREAM_TYPE, mWindow.getVolumeControlStream());
mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC);
assertEquals(AudioManager.STREAM_MUSIC, mWindow.getVolumeControlStream());
}
/**
* setWindowManager: Set the window manager for use by this Window.
* getWindowManager: Return the window manager allowing this Window to display its own
* windows.
*/
@Test
public void testAccessWindowManager() {
mWindow = new MockWindow(mActivity);
WindowManager expected = (WindowManager) mActivity.getSystemService(
Context.WINDOW_SERVICE);
assertNull(mWindow.getWindowManager());
mWindow.setWindowManager(expected, null,
mActivity.getApplicationInfo().loadLabel(mActivity.getPackageManager()).toString());
// No way to compare the expected and actual directly, they are
// different object
assertNotNull(mWindow.getWindowManager());
}
/**
* Return the {@link android.R.styleable#Window} attributes from this
* window's theme. It's invisible.
*/
@Test
public void testGetWindowStyle() {
mWindow = new MockWindow(mActivity);
final TypedArray windowStyle = mWindow.getWindowStyle();
// the windowStyle is obtained from
// com.android.internal.R.styleable.Window whose details
// are invisible for user.
assertNotNull(windowStyle);
}
@Test
public void testIsActive() {
MockWindow window = new MockWindow(mActivity);
assertFalse(window.isActive());
window.makeActive();
assertTrue(window.isActive());
assertTrue(window.mIsOnActiveCalled);
}
/**
* isFloating: Return whether this window is being displayed with a floating style
* (based on the {@link android.R.attr#windowIsFloating} attribute in the style/theme).
*/
@Test
public void testIsFloating() {
// Default system theme defined by themes.xml, the windowIsFloating is set false.
assertFalse(mWindow.isFloating());
}
/**
* Change the background of this window to a custom Drawable.
* Setting the background to null will make the window be opaque(No way to get the window
* attribute of PixelFormat to check if the window is opaque). To make the window
* transparent, you can use an empty drawable(eg. ColorDrawable with the color 0).
*/
@Test
public void testSetBackgroundDrawable() throws Throwable {
// DecorView holds the background
View decor = mWindow.getDecorView();
assertEquals(PixelFormat.OPAQUE, decor.getBackground().getOpacity());
// setBackgroundDrawableResource(int resId) has the same
// functionality with setBackgroundDrawable(Drawable drawable), just different in
// parameter.
mActivityRule.runOnUiThread(() -> mWindow.setBackgroundDrawableResource(R.drawable.faces));
mInstrumentation.waitForIdleSync();
mActivityRule.runOnUiThread(() -> {
ColorDrawable drawable = new ColorDrawable(0);
mWindow.setBackgroundDrawable(drawable);
});
mInstrumentation.waitForIdleSync();
decor = mWindow.getDecorView();
// Color 0 with one alpha bit
assertEquals(PixelFormat.TRANSPARENT, decor.getBackground().getOpacity());
mActivityRule.runOnUiThread(() -> mWindow.setBackgroundDrawable(null));
mInstrumentation.waitForIdleSync();
decor = mWindow.getDecorView();
assertNull(decor.getBackground());
}
/**
* setContentView(int): set the screen content from a layout resource.
* setContentView(View): set the screen content to an explicit view.
* setContentView(View, LayoutParams): Set the screen content to an explicit view.
*
* Note that calling this function "locks in" various characteristics
* of the window that can not, from this point forward, be changed: the
* features that have been requested with {@link #requestFeature(int)},
* and certain window flags as described in {@link #setFlags(int, int)}.
* This functionality point is hard to test:
* 1. can't get the features requested because the getter is protected final.
* 2. certain window flags are not clear to concrete one.
*/
@Test
public void testSetContentView() throws Throwable {
final ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(VIEWGROUP_LAYOUT_WIDTH,
VIEWGROUP_LAYOUT_HEIGHT);
final LayoutInflater inflate = mActivity.getLayoutInflater();
mActivityRule.runOnUiThread(() -> {
TextView view;
View setView;
// Test setContentView(int layoutResID)
mWindow.setContentView(R.layout.windowstub_layout);
view = (TextView) mWindow.findViewById(R.id.listview_window);
assertNotNull(view);
assertEquals(R.id.listview_window, view.getId());
// Test setContentView(View view)
setView = inflate.inflate(R.layout.windowstub_addlayout, null);
mWindow.setContentView(setView);
view = (TextView) mWindow.findViewById(R.id.listview_addwindow);
assertNotNull(view);
assertEquals(R.id.listview_addwindow, view.getId());
// Test setContentView(View view, ViewGroup.LayoutParams params)
setView = inflate.inflate(R.layout.windowstub_layout, null);
mWindow.setContentView(setView, lp);
assertEquals(VIEWGROUP_LAYOUT_WIDTH, setView.getLayoutParams().width);
assertEquals(VIEWGROUP_LAYOUT_HEIGHT, setView.getLayoutParams().height);
view = (TextView) mWindow.findViewById(R.id.listview_window);
assertNotNull(view);
assertEquals(R.id.listview_window, view.getId());
});
mInstrumentation.waitForIdleSync();
}
@Test
public void testSetTitle() throws Throwable {
final String title = "Android Window Test";
mActivityRule.runOnUiThread(() -> {
mWindow.setTitle(title);
mWindow.setTitleColor(Color.BLUE);
});
mInstrumentation.waitForIdleSync();
// No way to get title and title color
}
/**
* takeKeyEvents: Request that key events come to this activity. Use this if your activity
* has no views with focus, but the activity still wants a chance to process key events.
*/
@Test
public void testTakeKeyEvents() throws Throwable {
mActivityRule.runOnUiThread(() -> {
View v = mWindow.findViewById(R.id.listview_window);
v.clearFocus();
assertNull(mWindow.getCurrentFocus());
mWindow.takeKeyEvents(false);
});
mInstrumentation.waitForIdleSync();
}
/**
* setDefaultWindowFormat: Set the format of window, as per the PixelFormat types. This
* is the format that will be used unless the client specifies in explicit format with
* setFormat().
* setFormat: Set the format of window, as per the PixelFormat types.
* param format: The new window format (see PixelFormat). Use
* PixelFormat.UNKNOWN to allow the Window to select
* the format.
*/
@Test
public void testSetDefaultWindowFormat() {
MockWindow window = new MockWindow(mActivity);
// mHaveWindowFormat will be true after set PixelFormat.OPAQUE and
// setDefaultWindowFormat is invalid
window.setFormat(PixelFormat.OPAQUE);
window.setCallback(mWindowCallback);
verify(mWindowCallback, never()).onWindowAttributesChanged(any());
window.setDefaultWindowFormat(PixelFormat.JPEG);
assertEquals(PixelFormat.OPAQUE, window.getAttributes().format);
verify(mWindowCallback, never()).onWindowAttributesChanged(any());
// mHaveWindowFormat will be false after set PixelFormat.UNKNOWN and
// setDefaultWindowFormat is valid
window.setFormat(PixelFormat.UNKNOWN);
reset(mWindowCallback);
window.setDefaultWindowFormat(PixelFormat.JPEG);
assertEquals(PixelFormat.JPEG, window.getAttributes().format);
verify(mWindowCallback, times(1)).onWindowAttributesChanged(window.getAttributes());
}
/**
* Set the gravity of the window
*/
@Test
public void testSetGravity() {
mWindow = new MockWindow(mActivity);
WindowManager.LayoutParams attrs = mWindow.getAttributes();
assertEquals(0, attrs.gravity);
mWindow.setCallback(mWindowCallback);
verify(mWindowCallback, never()).onWindowAttributesChanged(any());
mWindow.setGravity(Gravity.TOP);
attrs = mWindow.getAttributes();
assertEquals(Gravity.TOP, attrs.gravity);
verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
}
/**
* Set the width and height layout parameters of the window.
* 1.The default for both of these is MATCH_PARENT;
* 2.You can change them to WRAP_CONTENT to make a window that is not full-screen.
*/
@Test
public void testSetLayout() {
mWindow = new MockWindow(mActivity);
WindowManager.LayoutParams attrs = mWindow.getAttributes();
assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attrs.width);
assertEquals(WindowManager.LayoutParams.MATCH_PARENT, attrs.height);
mWindow.setCallback(mWindowCallback);
verify(mWindowCallback, never()).onWindowAttributesChanged(any());
mWindow.setLayout(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT);
attrs = mWindow.getAttributes();
assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, attrs.width);
assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, attrs.height);
verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
}
/**
* Set the type of the window, as per the WindowManager.LayoutParams types.
*/
@Test
public void testSetType() {
mWindow = new MockWindow(mActivity);
WindowManager.LayoutParams attrs = mWindow.getAttributes();
assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION, attrs.type);
mWindow.setCallback(mWindowCallback);
verify(mWindowCallback, never()).onWindowAttributesChanged(any());
mWindow.setType(WindowManager.LayoutParams.TYPE_BASE_APPLICATION);
attrs = mWindow.getAttributes();
assertEquals(WindowManager.LayoutParams.TYPE_BASE_APPLICATION, attrs.type);
verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
}
/**
* Specify an explicit soft input mode to use for the window, as per
* WindowManager.LayoutParams#softInputMode.
* 1.Providing "unspecified" here will NOT override the input mode the window.
* 2.Providing "unspecified" here will override the input mode the window.
*/
@Test
public void testSetSoftInputMode() {
mWindow = new MockWindow(mActivity);
assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,
mWindow.getAttributes().softInputMode);
mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE,
mWindow.getAttributes().softInputMode);
mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED);
assertEquals(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE,
mWindow.getAttributes().softInputMode);
}
/**
* Specify custom animations to use for the window, as per
* WindowManager.LayoutParams#windowAnimations.
* 1.Providing 0 here will NOT override the animations the window(But here we can't check
* it because the getter is in WindowManagerService and is private)
* 2.Providing 0 here will override the animations the window.
*/
@Test
public void testSetWindowAnimations() {
mWindow = new MockWindow(mActivity);
mWindow.setCallback(mWindowCallback);
verify(mWindowCallback, never()).onWindowAttributesChanged(any());
mWindow.setWindowAnimations(R.anim.alpha);
WindowManager.LayoutParams attrs = mWindow.getAttributes();
assertEquals(R.anim.alpha, attrs.windowAnimations);
verify(mWindowCallback, times(1)).onWindowAttributesChanged(attrs);
}
@Test
public void testSetFitsContentForInsets_false() throws Throwable {
mActivityRule.runOnUiThread(() -> mWindow.setDecorFitsSystemWindows(false));
mInstrumentation.waitForIdleSync();
assertEquals(mActivity.getContentView().getRootWindowInsets().getSystemWindowInsets(),
mActivity.getLastInsets().getSystemWindowInsets());
}
@Test
public void testSetFitsContentForInsets_defaultLegacy_sysuiFlags()
throws Throwable {
mActivityRule.runOnUiThread(() -> {
mWindow.getDecorView().setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
mWindow.getDecorView().setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
});
mInstrumentation.waitForIdleSync();
assertEquals(mActivity.getContentView().getRootWindowInsets().getSystemWindowInsets(),
mActivity.getLastInsets().getSystemWindowInsets());
}
@Test
public void testSetFitsContentForInsets_displayCutoutInsets_areApplied()
throws Throwable {
mActivityRule.runOnUiThread(() -> {
mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mWindow.setDecorFitsSystemWindows(true);
WindowManager.LayoutParams attrs = mWindow.getAttributes();
attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindow.setAttributes(attrs);
});
mInstrumentation.waitForIdleSync();
assertEquals(mActivity.getContentView().getRootWindowInsets().getSystemWindowInsets(),
mActivity.getAppliedInsets());
}
@Test
public void testSetFitsContentForInsets_defaultLegacy_none()
throws Throwable {
mInstrumentation.waitForIdleSync();
// We don't expect that we even got called.
assertNull(mActivity.getLastInsets());
}
@Test
public void testSetFitsContentForInsets_true()
throws Throwable {
mActivityRule.runOnUiThread(() -> {
mWindow.setDecorFitsSystemWindows(true);
});
mInstrumentation.waitForIdleSync();
// We don't expect that we even got called.
assertNull(mActivity.getLastInsets());
}
/**
* Test setLocalFocus together with injectInputEvent.
*/
@Test
public void testSetLocalFocus() throws Throwable {
mActivityRule.runOnUiThread(() -> mSurfaceView = new SurfaceView(mActivity));
mInstrumentation.waitForIdleSync();
final Semaphore waitingSemaphore = new Semaphore(0);
mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
destroyPresentation();
createPresentation(holder.getSurface(), width, height);
waitingSemaphore.release();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
destroyPresentation();
}
});
mActivityRule.runOnUiThread(() -> mWindow.setContentView(mSurfaceView));
mInstrumentation.waitForIdleSync();
assertTrue(waitingSemaphore.tryAcquire(5, TimeUnit.SECONDS));
assertNotNull(mVirtualDisplay);
assertNotNull(mPresentation);
PollingCheck.waitFor(() -> (mPresentation.button1 != null)
&& (mPresentation.button2 != null) && (mPresentation.button3 != null)
&& mPresentation.ready);
assertTrue(mPresentation.button1.isFocusable() && mPresentation.button2.isFocusable() &&
mPresentation.button3.isFocusable());
// currently it is only for debugging
View.OnFocusChangeListener listener = (View v, boolean hasFocus) ->
Log.d(TAG, "view " + v + " focus " + hasFocus);
// check key event focus
mPresentation.button1.setOnFocusChangeListener(listener);
mPresentation.button2.setOnFocusChangeListener(listener);
mPresentation.button3.setOnFocusChangeListener(listener);
final Window presentationWindow = mPresentation.getWindow();
presentationWindow.setLocalFocus(true, false);
PollingCheck.waitFor(() -> mPresentation.button1.hasWindowFocus());
checkPresentationButtonFocus(true, false, false);
assertFalse(mPresentation.button1.isInTouchMode());
injectKeyEvent(presentationWindow, KeyEvent.KEYCODE_TAB);
checkPresentationButtonFocus(false, true, false);
injectKeyEvent(presentationWindow, KeyEvent.KEYCODE_TAB);
checkPresentationButtonFocus(false, false, true);
// check touch input injection
presentationWindow.setLocalFocus(true, true);
PollingCheck.waitFor(() -> mPresentation.button1.isInTouchMode());
View.OnClickListener clickListener = (View v) -> {
Log.d(TAG, "onClick " + v);
if (v == mPresentation.button1) {
waitingSemaphore.release();
}
};
mPresentation.button1.setOnClickListener(clickListener);
mPresentation.button2.setOnClickListener(clickListener);
mPresentation.button3.setOnClickListener(clickListener);
injectTouchEvent(presentationWindow, mPresentation.button1.getX() +
mPresentation.button1.getWidth() / 2,
mPresentation.button1.getY() + mPresentation.button1.getHeight() / 2);
assertTrue(waitingSemaphore.tryAcquire(5, TimeUnit.SECONDS));
}
private void checkPresentationButtonFocus(final boolean button1Focused,
final boolean button2Focused, final boolean button3Focused) {
PollingCheck.waitFor(() -> (mPresentation.button1.isFocused() == button1Focused) &&
(mPresentation.button2.isFocused() == button2Focused) &&
(mPresentation.button3.isFocused() == button3Focused));
}
private void injectKeyEvent(Window window, int keyCode) {
KeyEvent downEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
window.injectInputEvent(downEvent);
KeyEvent upEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
window.injectInputEvent(upEvent);
}
private void injectTouchEvent(Window window, float x, float y) {
Log.d(TAG, "injectTouchEvent " + x + "," + y);
long downTime = SystemClock.uptimeMillis();
MotionEvent downEvent = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
x, y, 0);
downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
window.injectInputEvent(downEvent);
long upTime = SystemClock.uptimeMillis();
MotionEvent upEvent = MotionEvent.obtain(downTime, upTime, MotionEvent.ACTION_UP,
x, y, 0);
upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
window.injectInputEvent(upEvent);
}
private void createPresentation(final Surface surface, final int width,
final int height) {
DisplayManager displayManager =
(DisplayManager) mActivity.getSystemService(Context.DISPLAY_SERVICE);
mVirtualDisplay = displayManager.createVirtualDisplay("localFocusTest",
width, height, 300, surface, 0);
mPresentation = new ProjectedPresentation(mActivity, mVirtualDisplay.getDisplay());
mPresentation.show();
}
private void destroyPresentation() {
if (mPresentation != null) {
mPresentation.dismiss();
}
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
}
}
private class ProjectedPresentation extends Presentation {
public Button button1 = null;
public Button button2 = null;
public Button button3 = null;
public volatile boolean ready = false;
public ProjectedPresentation(Context outerContext, Display display) {
super(outerContext, display);
getWindow().setType(WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.windowstub_presentation);
button1 = (Button) findViewById(R.id.presentation_button1);
button2 = (Button) findViewById(R.id.presentation_button2);
button3 = (Button) findViewById(R.id.presentation_button3);
}
@Override
public void show() {
super.show();
new Handler().post(() -> ready = true);
}
}
public class MockWindow extends Window {
public boolean mIsOnConfigurationChangedCalled = false;
public boolean mIsOnActiveCalled = false;
public MockWindow(Context context) {
super(context);
}
public boolean isFloating() {
return false;
}
public void setContentView(int layoutResID) {
}
public void setContentView(View view) {
}
public void setContentView(View view, ViewGroup.LayoutParams params) {
}
public void addContentView(View view, ViewGroup.LayoutParams params) {
}
public void clearContentView() {
}
public View getCurrentFocus() {
return null;
}
public LayoutInflater getLayoutInflater() {
return null;
}
public void setTitle(CharSequence title) {
}
public void setTitleColor(int textColor) {
}
public void openPanel(int featureId, KeyEvent event) {
}
public void closePanel(int featureId) {
}
public void togglePanel(int featureId, KeyEvent event) {
}
public void invalidatePanelMenu(int featureId) {
}
public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
return true;
}
public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
return true;
}
public void closeAllPanels() {
}
public boolean performContextMenuIdentifierAction(int id, int flags) {
return true;
}
public void onConfigurationChanged(Configuration newConfig) {
mIsOnConfigurationChangedCalled = true;
}
public void setBackgroundDrawable(Drawable drawable) {
}
public void setFeatureDrawableResource(int featureId, int resId) {
}
public void setFeatureDrawableUri(int featureId, Uri uri) {
}
public void setFeatureDrawable(int featureId, Drawable drawable) {
}
public void setFeatureDrawableAlpha(int featureId, int alpha) {
}
public void setFeatureInt(int featureId, int value) {
}
public void takeKeyEvents(boolean get) {
}
public boolean superDispatchKeyEvent(KeyEvent event) {
return true;
}
public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
return false;
}
public boolean superDispatchTouchEvent(MotionEvent event) {
return true;
}
public boolean superDispatchTrackballEvent(MotionEvent event) {
return true;
}
public boolean superDispatchGenericMotionEvent(MotionEvent event) {
return true;
}
public View getDecorView() {
return null;
}
public void alwaysReadCloseOnTouchAttr() {
}
public View peekDecorView() {
return null;
}
public Bundle saveHierarchyState() {
return null;
}
public void restoreHierarchyState(Bundle savedInstanceState) {
}
protected void onActive() {
mIsOnActiveCalled = true;
}
public void setChildDrawable(int featureId, Drawable drawable) {
}
public void setChildInt(int featureId, int value) {
}
public boolean isShortcutKey(int keyCode, KeyEvent event) {
return false;
}
public void setVolumeControlStream(int streamType) {
}
public int getVolumeControlStream() {
return 0;
}
public void setDefaultWindowFormatFake(int format) {
super.setDefaultWindowFormat(format);
}
@Override
public void setDefaultWindowFormat(int format) {
super.setDefaultWindowFormat(format);
}
@Override
public void takeSurface(SurfaceHolder.Callback2 callback) {
}
@Override
public void takeInputQueue(InputQueue.Callback callback) {
}
@Override
public void setStatusBarColor(int color) {
}
@Override
public int getStatusBarColor() {
return 0;
}
@Override
public void setNavigationBarColor(int color) {
}
@Override
public void setDecorCaptionShade(int decorCaptionShade) {
}
@Override
public void setResizingCaptionDrawable(Drawable drawable) {
}
@Override
public int getNavigationBarColor() {
return 0;
}
}
}