blob: 114ed2edd5752e9d9a2db1b3404c67fd40a7eaec [file] [log] [blame]
/*
* Copyright (C) 2023 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.launcher3.apppairs;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import androidx.annotation.NonNull;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.FastBitmapDrawable;
/**
* A composed Drawable consisting of the two app pair icons and the background behind them (looks
* like two rectangles).
*/
public class AppPairIconDrawable extends Drawable {
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final AppPairIconDrawingParams mP;
private final FastBitmapDrawable mIcon1;
private final FastBitmapDrawable mIcon2;
/**
* Null values to use with
* {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}, since there
* doesn't seem to be any other API for drawing rectangles with 4 different corner radii.
*/
private static final RectF EMPTY_RECT = new RectF();
private static final float[] ARRAY_OF_ZEROES = new float[8];
AppPairIconDrawable(
AppPairIconDrawingParams p, FastBitmapDrawable icon1, FastBitmapDrawable icon2) {
mP = p;
mBackgroundPaint.setStyle(Paint.Style.FILL);
mBackgroundPaint.setColor(p.getBgColor());
mIcon1 = icon1;
mIcon2 = icon2;
}
@Override
public void draw(@NonNull Canvas canvas) {
if (mP.isLeftRightSplit()) {
drawLeftRightSplit(canvas);
} else {
drawTopBottomSplit(canvas);
}
canvas.translate(
mP.getStandardIconPadding() + mP.getOuterPadding(),
mP.getStandardIconPadding() + mP.getOuterPadding()
);
// Draw first icon.
canvas.save();
// The app icons are placed differently depending on device orientation.
if (mP.isLeftRightSplit()) {
canvas.translate(
mP.getInnerPadding(),
mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f
);
} else {
canvas.translate(
mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f,
mP.getInnerPadding()
);
}
mIcon1.draw(canvas);
canvas.restore();
// Draw second icon.
canvas.save();
// The app icons are placed differently depending on device orientation.
if (mP.isLeftRightSplit()) {
canvas.translate(
mP.getBackgroundSize() - (mP.getInnerPadding() + mP.getMemberIconSize()),
mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f
);
} else {
canvas.translate(
mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f,
mP.getBackgroundSize() - (mP.getInnerPadding() + mP.getMemberIconSize())
);
}
mIcon2.draw(canvas);
canvas.restore();
}
/**
* When device is in landscape, we draw the rectangles with a left-right split.
*/
private void drawLeftRightSplit(Canvas canvas) {
// Get the bounds where we will draw the background image
int width = mP.getIconSize();
int height = mP.getIconSize();
// The left half of the background image, excluding center channel
RectF leftSide = new RectF(
mP.getStandardIconPadding() + mP.getOuterPadding(),
mP.getStandardIconPadding() + mP.getOuterPadding(),
(width / 2f) - (mP.getCenterChannelSize() / 2f),
height - (mP.getStandardIconPadding() + mP.getOuterPadding())
);
// The right half of the background image, excluding center channel
RectF rightSide = new RectF(
(width / 2f) + (mP.getCenterChannelSize() / 2f),
(mP.getStandardIconPadding() + mP.getOuterPadding()),
width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
height - (mP.getStandardIconPadding() + mP.getOuterPadding())
);
// Scale each background from its center edge closest to the center channel.
Utilities.scaleRectFAboutPivot(
leftSide,
leftSide.left + leftSide.width(),
leftSide.top + leftSide.centerY(),
mP.getHoverScale());
Utilities.scaleRectFAboutPivot(
rightSide,
rightSide.left,
rightSide.top + rightSide.centerY(),
mP.getHoverScale());
drawCustomRoundedRect(canvas, leftSide, new float[]{
mP.getBigRadius(), mP.getBigRadius(),
mP.getSmallRadius(), mP.getSmallRadius(),
mP.getSmallRadius(), mP.getSmallRadius(),
mP.getBigRadius(), mP.getBigRadius()});
drawCustomRoundedRect(canvas, rightSide, new float[]{
mP.getSmallRadius(), mP.getSmallRadius(),
mP.getBigRadius(), mP.getBigRadius(),
mP.getBigRadius(), mP.getBigRadius(),
mP.getSmallRadius(), mP.getSmallRadius()});
}
/**
* When device is in portrait, we draw the rectangles with a top-bottom split.
*/
private void drawTopBottomSplit(Canvas canvas) {
// Get the bounds where we will draw the background image
int width = mP.getIconSize();
int height = mP.getIconSize();
// The top half of the background image, excluding center channel
RectF topSide = new RectF(
(mP.getStandardIconPadding() + mP.getOuterPadding()),
(mP.getStandardIconPadding() + mP.getOuterPadding()),
width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
(height / 2f) - (mP.getCenterChannelSize() / 2f)
);
// The bottom half of the background image, excluding center channel
RectF bottomSide = new RectF(
(mP.getStandardIconPadding() + mP.getOuterPadding()),
(height / 2f) + (mP.getCenterChannelSize() / 2f),
width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
height - (mP.getStandardIconPadding() + mP.getOuterPadding())
);
// Scale each background from its center edge closest to the center channel.
Utilities.scaleRectFAboutPivot(
topSide,
topSide.left + topSide.centerX(),
topSide.top + topSide.height(),
mP.getHoverScale());
Utilities.scaleRectFAboutPivot(
bottomSide,
bottomSide.left + bottomSide.centerX(),
bottomSide.top,
mP.getHoverScale());
drawCustomRoundedRect(canvas, topSide, new float[]{
mP.getBigRadius(), mP.getBigRadius(),
mP.getBigRadius(), mP.getBigRadius(),
mP.getSmallRadius(), mP.getSmallRadius(),
mP.getSmallRadius(), mP.getSmallRadius()});
drawCustomRoundedRect(canvas, bottomSide, new float[]{
mP.getSmallRadius(), mP.getSmallRadius(),
mP.getSmallRadius(), mP.getSmallRadius(),
mP.getBigRadius(), mP.getBigRadius(),
mP.getBigRadius(), mP.getBigRadius()});
}
/**
* Draws a rectangle with custom rounded corners.
* @param c The Canvas to draw on.
* @param rect The bounds of the rectangle.
* @param radii An array of 8 radii for the corners: top left x, top left y, top right x, top
* right y, bottom right x, and so on.
*/
private void drawCustomRoundedRect(Canvas c, RectF rect, float[] radii) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Canvas.drawDoubleRoundRect is supported from Q onward
c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, mBackgroundPaint);
} else {
// Fallback rectangle with uniform rounded corners
c.drawRoundRect(rect, mP.getBigRadius(), mP.getBigRadius(), mBackgroundPaint);
}
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
@Override
public void setAlpha(int i) {
mBackgroundPaint.setAlpha(i);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mBackgroundPaint.setColorFilter(colorFilter);
}
@Override
public int getIntrinsicWidth() {
return mP.getIconSize();
}
@Override
public int getIntrinsicHeight() {
return mP.getIconSize();
}
}