Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b5fc6637 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Add border to indicate side stage split region" into sc-v2-dev am: a8424ea5 am: ea91f2ed

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15048194

Change-Id: I7f776e3af2aee3c46b398e826bac014139544894
parents 76e74444 ea91f2ed
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<com.android.wm.shell.splitscreen.OutlineRoot
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.android.wm.shell.splitscreen.OutlineView
        android:id="@+id/split_outline"
        android:layout_height="match_parent"
        android:layout_width="match_parent" />

</com.android.wm.shell.splitscreen.OutlineRoot>
+2 −3
Original line number Diff line number Diff line
@@ -311,9 +311,8 @@ public final class SplitLayout {
        return context.getSystemService(WindowManager.class)
                .getMaximumWindowMetrics()
                .getWindowInsets()
                .getInsets(WindowInsets.Type.navigationBars()
                        | WindowInsets.Type.statusBars()
                        | WindowInsets.Type.displayCutout()).toRect();
                .getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())
                .toRect();
    }

    private static boolean isLandscape(Rect bounds) {
+125 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 com.android.wm.shell.splitscreen;

import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
import android.view.IWindow;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.WindowlessWindowManager;

import com.android.wm.shell.R;

import java.util.function.Supplier;

/**
 * Handles drawing outline of the bounds of provided root surface. The outline will be drown with
 * the consideration of display insets like status bar, navigation bar and display cutout.
 */
class OutlineManager extends WindowlessWindowManager {
    private static final String WINDOW_NAME = "SplitOutlineLayer";
    private final Context mContext;
    private final int mOutlineColor;
    private final Rect mOutlineBounds = new Rect();
    private final Rect mTmpBounds = new Rect();
    private final Supplier<SurfaceControl> mOutlineSurfaceSupplier;
    private SurfaceControlViewHost mViewHost;

    /**
     * Constructs {@link #OutlineManager} with indicated outline color for the provided root
     * surface.
     */
    OutlineManager(Context context, Configuration configuration,
            Supplier<SurfaceControl> outlineSurfaceSupplier, int color) {
        super(configuration, null /* rootSurface */, null /* hostInputToken */);
        mContext = context.createDisplayContext(context.getDisplay());
        mOutlineSurfaceSupplier = outlineSurfaceSupplier;
        mOutlineColor = color;
    }

    @Override
    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
        b.setParent(mOutlineSurfaceSupplier.get());
    }

    boolean updateOutlineBounds(Rect rootBounds) {
        computeOutlineBounds(mContext, rootBounds, mTmpBounds);
        if (mOutlineBounds.equals(mTmpBounds)) {
            return false;
        }
        mOutlineBounds.set(mTmpBounds);

        if (mViewHost == null) {
            mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
        }
        if (mViewHost.getView() == null) {
            final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext)
                    .inflate(R.layout.split_outline, null);
            rootView.updateOutlineBounds(mOutlineBounds, mOutlineColor);

            final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                    rootBounds.width(), rootBounds.height(),
                    TYPE_APPLICATION_OVERLAY,
                    FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE,
                    PixelFormat.TRANSLUCENT);
            lp.token = new Binder();
            lp.setTitle(WINDOW_NAME);
            lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
            // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
            //  TRUSTED_OVERLAY for windowless window without input channel.
            mViewHost.setView(rootView, lp);
        } else {
            ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor);
            final WindowManager.LayoutParams lp =
                    (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
            lp.width = rootBounds.width();
            lp.height = rootBounds.height();
            mViewHost.relayout(lp);
        }

        return true;
    }

    private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) {
        computeDisplayStableBounds(context, outBounds);
        outBounds.intersect(rootBounds);
        // Offset the coordinate from screen based to surface based.
        outBounds.offset(-rootBounds.left, -rootBounds.top);
    }

    private static void computeDisplayStableBounds(Context context, Rect outBounds) {
        final WindowMetrics windowMetrics =
                context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
        outBounds.set(windowMetrics.getBounds());
        outBounds.inset(windowMetrics.getWindowInsets().getInsets(
                WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()));
    }
}
+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 com.android.wm.shell.splitscreen;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.wm.shell.R;

/** Root layout for holding split outline. */
public class OutlineRoot extends FrameLayout {
    public OutlineRoot(@NonNull Context context) {
        super(context);
    }

    public OutlineRoot(@NonNull Context context,
            @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
            int defStyleAttr,
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    private OutlineView mOutlineView;

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mOutlineView = findViewById(R.id.split_outline);
    }

    void updateOutlineBounds(Rect bounds, int color) {
        mOutlineView.updateOutlineBounds(bounds, color);
    }
}
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 com.android.wm.shell.splitscreen;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.R;

/** View for drawing split outline. */
public class OutlineView extends View {
    private final Paint mPaint = new Paint();
    private final Rect mBounds = new Rect();

    public OutlineView(@NonNull Context context) {
        super(context);
    }

    public OutlineView(@NonNull Context context,
            @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(getResources()
                .getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
    }

    void updateOutlineBounds(Rect bounds, int color) {
        if (mBounds.equals(bounds) && mPaint.getColor() == color) return;
        mBounds.set(bounds);
        mPaint.setColor(color);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mBounds.isEmpty()) return;
        final Path path = new Region(mBounds).getBoundaryPath();
        canvas.drawPath(path, mPaint);
    }
}
Loading