blob: 364bb651d55dc15c500152662b1d50e973871fb4 [file] [log] [blame]
/*
* 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.common.split;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
import static android.view.RoundedCorner.POSITION_TOP_LEFT;
import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.RoundedCorner;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.wm.shell.R;
/**
* Draws inverted rounded corners beside divider bar to keep splitting tasks cropped with proper
* rounded corners.
*/
public class DividerRoundedCorner extends View {
private final int mDividerWidth;
private final Paint mDividerBarBackground;
private final Point mStartPos = new Point();
private InvertedRoundedCornerDrawInfo mTopLeftCorner;
private InvertedRoundedCornerDrawInfo mTopRightCorner;
private InvertedRoundedCornerDrawInfo mBottomLeftCorner;
private InvertedRoundedCornerDrawInfo mBottomRightCorner;
public DividerRoundedCorner(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mDividerWidth = getResources().getDimensionPixelSize(R.dimen.split_divider_bar_width);
mDividerBarBackground = new Paint();
mDividerBarBackground.setColor(
getResources().getColor(R.color.split_divider_background, null));
mDividerBarBackground.setFlags(Paint.ANTI_ALIAS_FLAG);
mDividerBarBackground.setStyle(Paint.Style.FILL);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mTopLeftCorner = new InvertedRoundedCornerDrawInfo(POSITION_TOP_LEFT);
mTopRightCorner = new InvertedRoundedCornerDrawInfo(POSITION_TOP_RIGHT);
mBottomLeftCorner = new InvertedRoundedCornerDrawInfo(POSITION_BOTTOM_LEFT);
mBottomRightCorner = new InvertedRoundedCornerDrawInfo(POSITION_BOTTOM_RIGHT);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
mTopLeftCorner.calculateStartPos(mStartPos);
canvas.translate(mStartPos.x, mStartPos.y);
canvas.drawPath(mTopLeftCorner.mPath, mDividerBarBackground);
canvas.translate(-mStartPos.x, -mStartPos.y);
mTopRightCorner.calculateStartPos(mStartPos);
canvas.translate(mStartPos.x, mStartPos.y);
canvas.drawPath(mTopRightCorner.mPath, mDividerBarBackground);
canvas.translate(-mStartPos.x, -mStartPos.y);
mBottomLeftCorner.calculateStartPos(mStartPos);
canvas.translate(mStartPos.x, mStartPos.y);
canvas.drawPath(mBottomLeftCorner.mPath, mDividerBarBackground);
canvas.translate(-mStartPos.x, -mStartPos.y);
mBottomRightCorner.calculateStartPos(mStartPos);
canvas.translate(mStartPos.x, mStartPos.y);
canvas.drawPath(mBottomRightCorner.mPath, mDividerBarBackground);
canvas.restore();
}
@Override
public boolean hasOverlappingRendering() {
return false;
}
private boolean isLandscape() {
return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
}
/**
* Holds draw information of the inverted rounded corner at a specific position.
*
* @see {@link com.android.launcher3.taskbar.TaskbarDragLayer}
*/
private class InvertedRoundedCornerDrawInfo {
@RoundedCorner.Position
private final int mCornerPosition;
private final int mRadius;
private final Path mPath = new Path();
InvertedRoundedCornerDrawInfo(@RoundedCorner.Position int cornerPosition) {
mCornerPosition = cornerPosition;
final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(cornerPosition);
mRadius = roundedCorner == null ? 0 : roundedCorner.getRadius();
// Starts with a filled square, and then subtracting out a circle from the appropriate
// corner.
final Path square = new Path();
square.addRect(0, 0, mRadius, mRadius, Path.Direction.CW);
final Path circle = new Path();
circle.addCircle(
isLeftCorner() ? mRadius : 0 /* x */,
isTopCorner() ? mRadius : 0 /* y */,
mRadius, Path.Direction.CW);
mPath.op(square, circle, Path.Op.DIFFERENCE);
}
private void calculateStartPos(Point outPos) {
if (isLandscape()) {
// Place left corner at the right side of the divider bar.
outPos.x = isLeftCorner()
? getWidth() / 2 + mDividerWidth / 2
: getWidth() / 2 - mDividerWidth / 2 - mRadius;
outPos.y = isTopCorner() ? 0 : getHeight() - mRadius;
} else {
outPos.x = isLeftCorner() ? 0 : getWidth() - mRadius;
// Place top corner at the bottom of the divider bar.
outPos.y = isTopCorner()
? getHeight() / 2 + mDividerWidth / 2
: getHeight() / 2 - mDividerWidth / 2 - mRadius;
}
}
private boolean isLeftCorner() {
return mCornerPosition == POSITION_TOP_LEFT || mCornerPosition == POSITION_BOTTOM_LEFT;
}
private boolean isTopCorner() {
return mCornerPosition == POSITION_TOP_LEFT || mCornerPosition == POSITION_TOP_RIGHT;
}
}
}