211 lines
7.2 KiB
Java
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);
|
|
}
|
|
}
|