Files
EhViewer/app/src/main/java/com/hippo/ehviewer/widget/GalleryHeader.java
T
2022-05-18 11:40:29 +08:00

211 lines
7.2 KiB
Java

/*
* Copyright 2019 Hippo Seven
*
* 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.hippo.ehviewer.widget;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.view.DisplayCutoutCompat;
import com.hippo.ehviewer.R;
import com.hippo.yorozuya.ObjectUtils;
public class GalleryHeader extends ViewGroup {
private final Rect batteryRect = new Rect();
private final Rect progressRect = new Rect();
private final Rect clockRect = new Rect();
private final int[] location = new int[2];
private DisplayCutoutCompat displayCutout;
private int topInsets = 0;
private View battery;
private View progress;
private View clock;
private int lastX = 0;
private int lastY = 0;
public GalleryHeader(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setDisplayCutout(@Nullable DisplayCutoutCompat displayCutout) {
if (!ObjectUtils.equal(this.displayCutout, displayCutout)) {
this.displayCutout = displayCutout;
requestLayout();
}
}
public void setTopInsets(int topInsets) {
if (this.topInsets != topInsets) {
this.topInsets = topInsets;
requestLayout();
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
battery = findViewById(R.id.battery);
progress = findViewById(R.id.progress);
clock = findViewById(R.id.clock);
}
private void measureChild(Rect rect, View view, int width, int paddingLeft, int paddingRight) {
int left;
MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
if (view == battery) {
left = paddingLeft + lp.leftMargin;
} else if (view == progress) {
left = paddingLeft + (width - paddingLeft - paddingRight) / 2 - view.getMeasuredWidth() / 2;
} else {
left = width - paddingRight - lp.rightMargin - view.getMeasuredWidth();
}
rect.set(left, lp.topMargin + topInsets, left + view.getMeasuredWidth(), lp.topMargin + topInsets + view.getMeasuredHeight());
}
@RequiresApi(api = Build.VERSION_CODES.P)
private boolean offsetVertically(Rect rect, View view, int width) {
int offset = 0;
measureChild(rect, view, width, 0, 0);
rect.offset(lastX, lastY);
for (Rect notch : displayCutout.getBoundingRects()) {
if (Rect.intersects(notch, rect)) {
offset = Math.max(offset, notch.bottom - lastY);
}
}
if (offset != 0) {
rect.offset(-lastX, -lastY);
rect.offset(0, offset);
return true;
} else {
return false;
}
}
@RequiresApi(api = Build.VERSION_CODES.P)
private int getOffsetLeft(Rect rect, View view, int width) {
int offset = 0;
measureChild(rect, view, width, 0, 0);
rect.offset(lastX, lastY);
for (Rect notch : displayCutout.getBoundingRects()) {
if (Rect.intersects(notch, rect)) {
offset = Math.max(offset, notch.right - lastX);
}
}
return offset;
}
@RequiresApi(api = Build.VERSION_CODES.P)
private int getOffsetRight(Rect rect, View view, int width) {
int offset = 0;
measureChild(rect, view, width, 0, 0);
rect.offset(lastX, lastY);
for (Rect notch : displayCutout.getBoundingRects()) {
if (Rect.intersects(notch, rect)) {
offset = Math.max(offset, lastX + width - notch.left);
}
}
return offset;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {
throw new IllegalStateException();
}
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
height = Math.max(height, child.getMeasuredHeight() + lp.topMargin + topInsets);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && displayCutout != null) {
// Check progress covered
if (offsetVertically(progressRect, progress, width)) {
offsetVertically(batteryRect, battery, width);
offsetVertically(clockRect, clock, width);
height = Math.max(progressRect.bottom, Math.max(batteryRect.bottom, clockRect.bottom));
} else {
// Clamp left and right
int paddingLeft = getOffsetLeft(batteryRect, battery, width);
int paddingRight = getOffsetRight(clockRect, clock, width);
measureChild(batteryRect, battery, width, paddingLeft, paddingRight);
measureChild(progressRect, progress, width, paddingLeft, paddingRight);
measureChild(clockRect, clock, width, paddingLeft, paddingRight);
}
} else {
measureChild(batteryRect, battery, width, 0, 0);
measureChild(progressRect, progress, width, 0, 0);
measureChild(clockRect, clock, width, 0, 0);
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
battery.layout(batteryRect.left, batteryRect.top, batteryRect.right, batteryRect.bottom);
progress.layout(progressRect.left, progressRect.top, progressRect.right, progressRect.bottom);
clock.layout(clockRect.left, clockRect.top, clockRect.right, clockRect.bottom);
getLocationOnScreen(location);
if (lastX != location[0] || lastY != location[1]) {
lastX = location[0];
lastY = location[1];
requestLayout();
}
}
@Override
public MarginLayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof MarginLayoutParams;
}
@Override
protected MarginLayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
if (lp instanceof MarginLayoutParams) {
return new MarginLayoutParams((MarginLayoutParams) lp);
}
return new MarginLayoutParams(lp);
}
}