blob: 7543c204d918e321531090f512296040dadd2ea4 [file] [log] [blame]
/*
* Copyright (C) 2019 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.systemui.cts;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import android.annotation.MainThread;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class WindowInsetsActivity extends LightBarBaseActivity implements View.OnClickListener,
View.OnApplyWindowInsetsListener {
private static final int DISPLAY_CUTOUT_SLACK_DP = 20;
private TextView mContent;
private boolean mIsSetViewBound;
private WindowInsets mContentWindowInsets;
private WindowInsets mDecorViewWindowInsets;
private Rect mDecorBound;
private Rect mContentBound;
private Consumer<Boolean> mInitialFinishCallBack;
private int mClickCount;
private Consumer<View> mClickConsumer;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
/**
* To setup the Activity to get better diagnoise.
* To setup WindowInsetPresenterDrawable that will show the boundary of the windowInset and
* the touch track.
*/
@Override
protected void onCreate(Bundle bundle) {
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
super.onCreate(bundle);
mContent = new TextView(this);
mContent.setTextSize(10);
mContent.setGravity(Gravity.CENTER);
mContent.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
WindowInsetsPresenterDrawable presenterDrawable =
new WindowInsetsPresenterDrawable(getWindow().getDecorView().getRootWindowInsets());
mContent.setOnTouchListener(presenterDrawable);
mContent.setBackground(presenterDrawable);
mContent.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
mContent.getRootView().setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
getWindow().getDecorView().setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().getAttributes().layoutInDisplayCutoutMode
= LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setStatusBarColor(0x22ff0000);
getWindow().setNavigationBarColor(0x22ff0000);
mContent.setOnClickListener(this);
mContent.requestApplyInsets();
setContentView(mContent);
mContentWindowInsets = getWindow().getDecorView().getRootWindowInsets();
mInitialFinishCallBack = null;
getDisplay().getRealMetrics(mDisplayMetrics);
}
@Override
protected void onResume() {
super.onResume();
mClickCount = 0;
}
private void showVisualBoundary(WindowInsets insets) {
if (insets != null) {
WindowInsetsPresenterDrawable presenterDrawable =
(WindowInsetsPresenterDrawable) mContent.getBackground();
presenterDrawable.setWindowInsets(insets);
}
}
/**
* To get the WindowInsets that comes from onApplyWindowInsets(mContent, insets).
*/
WindowInsets getContentViewWindowInsets() {
showVisualBoundary(mContentWindowInsets);
return mContentWindowInsets;
}
/**
* To get the WindowInsets that comes from onApplyWindowInsets(DecorView, insets).
*/
WindowInsets getDecorViewWindowInsets() {
showVisualBoundary(mDecorViewWindowInsets);
return mDecorViewWindowInsets;
}
/**
* To catch the WindowInsets that passwd to the content view.
* This WindowInset should have already consumed the SystemWindowInset.
*/
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
if (insets != null) {
if (v == mContent) {
mContentWindowInsets = new WindowInsets.Builder(insets).build();
} else {
mDecorViewWindowInsets = new WindowInsets.Builder(insets).build();
}
showInfoInTextView();
showVisualBoundary(mDecorViewWindowInsets);
}
return insets;
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mContent.post(() -> {
mContentBound = getViewBound(mContent);
mDecorBound = getViewBound(getWindow().getDecorView());
showInfoInTextView();
if (mInitialFinishCallBack != null) {
mInitialFinishCallBack.accept(true);
}
});
}
/**
* To present the WindowInsets information to mContent.
* To show all of results of getSystemWindowInsets(), getMandatorySytemGestureInsets(),
* getSystemGestureInsets(), getTappableElementsInsets() and the exclude rects
*/
@MainThread
public void setSystemGestureExclusion(boolean isSetViewBoundary) {
mIsSetViewBound = isSetViewBoundary;
List<Rect> rects = new ArrayList<>();
if (mIsSetViewBound) {
rects.add(new Rect(0 /* content view full match activity's width*/,
0 /* content view full match activity's height */,
mContent.getWidth(),
mContent.getHeight()));
}
getContentView().setSystemGestureExclusionRects(rects);
showInfoInTextView();
}
private void showInfoInTextView() {
StringBuilder sb = new StringBuilder();
sb.append(mIsSetViewBound ? "setSystemGestureExclusionRects" : "no set").append("\n");
sb.append("exclude rect list = " + Arrays.deepToString(mContent
.getSystemGestureExclusionRects().toArray())).append("\n");
if (mDecorViewWindowInsets != null) {
sb.append("onApplyWindowInsets mDecorViewWindowInsets = " + mDecorViewWindowInsets);
sb.append("\n");
sb.append("getSystemWindowInsets = " + mDecorViewWindowInsets.getSystemWindowInsets());
sb.append("\n");
sb.append("getSystemGestureInsets = "
+ mDecorViewWindowInsets.getSystemGestureInsets()).append("\n");
sb.append("getMandatorySystemGestureInsets = "
+ mDecorViewWindowInsets.getMandatorySystemGestureInsets()).append("\n");
sb.append("getTappableElementInsets = "
+ mDecorViewWindowInsets.getTappableElementInsets()).append("\n");
sb.append("decor boundary = ").append(mDecorBound).append("\n");
}
if (mContentWindowInsets != null) {
sb.append("------------------------").append("\n");
sb.append("onApplyWindowInsets mContentWindowInsets = " + mContentWindowInsets);
sb.append("\n");
sb.append("getSystemWindowInsets = " + mContentWindowInsets.getSystemWindowInsets());
sb.append("\n");
sb.append("getSystemGestureInsets = "
+ mContentWindowInsets.getSystemGestureInsets()).append("\n");
sb.append("getMandatorySystemGestureInsets = "
+ mContentWindowInsets.getMandatorySystemGestureInsets()).append("\n");
sb.append("getTappableElementInsets = "
+ mContentWindowInsets.getTappableElementInsets()).append("\n");
sb.append("content boundary = ").append(mContentBound).append("\n");
}
Display display = getDisplay();
if (display != null) {
sb.append("------------------------").append("\n");
DisplayCutout displayCutout = display.getCutout();
if (displayCutout != null) {
sb.append("displayCutout = ").append(displayCutout.toString()).append("\n");
} else {
sb.append("Display cut out = null\n");
}
sb.append("real size = (").append(mDisplayMetrics.widthPixels).append(",")
.append(mDisplayMetrics.heightPixels).append(")\n");
}
mContent.setText(sb.toString());
}
@MainThread
public View getContentView() {
return mContent;
}
Rect getViewBound(View view) {
int [] screenlocation = new int[2];
view.getLocationOnScreen(screenlocation);
return new Rect(screenlocation[0], screenlocation[1],
screenlocation[0] + view.getWidth(),
screenlocation[1] + view.getHeight());
}
/**
* To count the draggable boundary that has consume the related insets.
**/
@MainThread
public Rect getOperationArea(Insets insets, WindowInsets windowInsets) {
int left = insets.left;
int top = insets.top;
int right = insets.right;
int bottom = insets.bottom;
final DisplayCutout cutout = windowInsets.getDisplayCutout();
if (cutout != null) {
int slack = (int) (DISPLAY_CUTOUT_SLACK_DP * mDisplayMetrics.density);
if (cutout.getSafeInsetLeft() > 0) {
left = Math.max(left, cutout.getSafeInsetLeft() + slack);
}
if (cutout.getSafeInsetTop() > 0) {
top = Math.max(top, cutout.getSafeInsetTop() + slack);
}
if (cutout.getSafeInsetRight() > 0) {
right = Math.max(right, cutout.getSafeInsetRight() + slack);
}
if (cutout.getSafeInsetBottom() > 0) {
bottom = Math.max(bottom, cutout.getSafeInsetBottom() + slack);
}
}
Rect windowBoundary = getViewBound(getContentView());
Rect rect = new Rect(windowBoundary);
rect.left += left;
rect.top += top;
rect.right -= right;
rect.bottom -= bottom;
return rect;
}
@MainThread
public List<Point> getActionCancelPoints() {
return ((WindowInsetsPresenterDrawable) mContent.getBackground()).getActionCancelPoints();
}
@MainThread
public List<Point> getActionDownPoints() {
return ((WindowInsetsPresenterDrawable) mContent.getBackground()).getActionDownPoints();
}
@MainThread
public List<Point> getActionUpPoints() {
return ((WindowInsetsPresenterDrawable) mContent.getBackground()).getActionUpPoints();
}
/**
* To set the callback to notify the onClickListener is triggered.
* @param clickConsumer trigger the callback after clicking view.
*/
public void setOnClickConsumer(
Consumer<View> clickConsumer) {
mClickConsumer = clickConsumer;
}
/**
* Because the view needs the focus to catch the ACTION_DOWN otherwise do nothing.
* Only for the focus to receive the ACTION_DOWN, ACTION_MOVE, ACTION_UP and ACTION_CANCEL.
**/
@Override
public void onClick(View v) {
mClickCount++;
if (mClickConsumer != null) {
mClickConsumer.accept(v);
}
}
public int getClickCount() {
return mClickCount;
}
/**
* To set the callback to notify the test with the initial finish.
* @param initialFinishCallBack trigger the callback after initial finish.
*/
public void setInitialFinishCallBack(
Consumer<Boolean> initialFinishCallBack) {
mInitialFinishCallBack = initialFinishCallBack;
}
}