NekoX/TMessagesProj/src/main/java/org/telegram/ui/Charts/BaseChartView.java

1606 lines
59 KiB
Java

package org.telegram.ui.Charts;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.Bundle;
import android.text.TextPaint;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Charts.data.ChartData;
import org.telegram.ui.Charts.view_data.ChartBottomSignatureData;
import org.telegram.ui.Charts.view_data.ChartHeaderView;
import org.telegram.ui.Charts.view_data.ChartHorizontalLinesData;
import org.telegram.ui.Charts.view_data.LegendSignatureView;
import org.telegram.ui.Charts.view_data.LineViewData;
import org.telegram.ui.Charts.view_data.TransitionParams;
import org.telegram.ui.Components.CubicBezierInterpolator;
import java.util.ArrayList;
import java.util.Arrays;
public abstract class BaseChartView<T extends ChartData, L extends LineViewData> extends View implements ChartPickerDelegate.Listener {
public SharedUiComponents sharedUiComponents;
ArrayList<ChartHorizontalLinesData> horizontalLines = new ArrayList<>(10);
ArrayList<ChartBottomSignatureData> bottomSignatureDate = new ArrayList<>(25);
public ArrayList<L> lines = new ArrayList<>();
private final int ANIM_DURATION = 400;
public final static float HORIZONTAL_PADDING = AndroidUtilities.dpf2(16f);
private final static float LINE_WIDTH = 1;
private final static float SELECTED_LINE_WIDTH = AndroidUtilities.dpf2(1.5f);
private final static float SIGNATURE_TEXT_SIZE = AndroidUtilities.dpf2(12f);
public final static int SIGNATURE_TEXT_HEIGHT = AndroidUtilities.dp(18f);
private final static int BOTTOM_SIGNATURE_TEXT_HEIGHT = AndroidUtilities.dp(14f);
public final static int BOTTOM_SIGNATURE_START_ALPHA = AndroidUtilities.dp(10f);
protected final static int PICKER_PADDING = AndroidUtilities.dp(16f);
private final static int PICKER_CAPTURE_WIDTH = AndroidUtilities.dp(24);
private final static int LANDSCAPE_END_PADDING = AndroidUtilities.dp(16);
private final static int BOTTOM_SIGNATURE_OFFSET = AndroidUtilities.dp(10);
private final static int DP_12 = AndroidUtilities.dp(12);
private final static int DP_6 = AndroidUtilities.dp(6);
private final static int DP_5 = AndroidUtilities.dp(5);
private final static int DP_2 = AndroidUtilities.dp(2);
private final static int DP_1 = AndroidUtilities.dp(1);
protected boolean drawPointOnSelection = true;
float signaturePaintAlpha;
float bottomSignaturePaintAlpha;
int hintLinePaintAlpha;
int chartActiveLineAlpha;
public final static boolean USE_LINES = android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.P;
protected final static boolean ANIMATE_PICKER_SIZES = android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP;
public static FastOutSlowInInterpolator INTERPOLATOR = new FastOutSlowInInterpolator();
int chartBottom;
public float currentMaxHeight = 250;
public float currentMinHeight = 0;
float animateToMaxHeight = 0;
float animateToMinHeight = 0;
float thresholdMaxHeight = 0;
int startXIndex;
int endXIndex;
boolean invalidatePickerChart = true;
boolean landscape = false;
public boolean enabled = true;
Paint emptyPaint = new Paint();
Paint linePaint = new Paint();
Paint selectedLinePaint = new Paint();
Paint signaturePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
Paint signaturePaint2 = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
Paint bottomSignaturePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
Paint pickerSelectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint unactiveBottomChartPaint = new Paint();
Paint selectionBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint ripplePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint whiteLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Rect pickerRect = new Rect();
Path pathTmp = new Path();
Animator maxValueAnimator;
ValueAnimator alphaAnimator;
ValueAnimator alphaBottomAnimator;
Animator pickerAnimator;
ValueAnimator selectionAnimator;
boolean postTransition = false;
public ChartPickerDelegate pickerDelegate = new ChartPickerDelegate(this);
T chartData;
ChartBottomSignatureData currentBottomSignatures;
protected float pickerMaxHeight;
protected float pickerMinHeight;
protected float animatedToPickerMaxHeight;
protected float animatedToPickerMinHeight;
protected int tmpN;
protected int tmpI;
protected int bottomSignatureOffset;
private Bitmap bottomChartBitmap;
private Canvas bottomChartCanvas;
protected boolean chartCaptured = false;
protected int selectedIndex = -1;
protected float selectedCoordinate = -1;
public LegendSignatureView legendSignatureView;
public boolean legendShowing = false;
public float selectionA = 0f;
boolean superDraw = false;
boolean useAlphaSignature = false;
public int transitionMode = TRANSITION_MODE_NONE;
public TransitionParams transitionParams;
public final static int TRANSITION_MODE_CHILD = 1;
public final static int TRANSITION_MODE_PARENT = 2;
public final static int TRANSITION_MODE_ALPHA_ENTER = 3;
public final static int TRANSITION_MODE_NONE = 0;
private final int touchSlop;
public int pikerHeight = AndroidUtilities.dp(46);
public float pickerWidth;
public float chartStart;
public float chartEnd;
public float chartWidth;
public float chartFullWidth;
public RectF chartArea = new RectF();
private ValueAnimator.AnimatorUpdateListener pickerHeightUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
pickerMaxHeight = (float) animation.getAnimatedValue();
invalidatePickerChart = true;
invalidate();
}
};
private ValueAnimator.AnimatorUpdateListener pickerMinHeightUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
pickerMinHeight = (float) animation.getAnimatedValue();
invalidatePickerChart = true;
invalidate();
}
};
private ValueAnimator.AnimatorUpdateListener heightUpdateListener = animation -> {
currentMaxHeight = ((float) animation.getAnimatedValue());
invalidate();
};
private ValueAnimator.AnimatorUpdateListener minHeightUpdateListener = animation -> {
currentMinHeight = ((float) animation.getAnimatedValue());
invalidate();
};
private ValueAnimator.AnimatorUpdateListener selectionAnimatorListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
selectionA = (float) animation.getAnimatedValue();
legendSignatureView.setAlpha(selectionA);
invalidate();
}
};
private Animator.AnimatorListener selectorAnimatorEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (!animateLegentTo) {
legendShowing = false;
legendSignatureView.setVisibility(GONE);
invalidate();
}
postTransition = false;
}
};
protected boolean useMinHeight = false;
protected DateSelectionListener dateSelectionListener;
private float startFromMax;
private float startFromMin;
private float startFromMaxH;
private float startFromMinH;
private float minMaxUpdateStep;
public BaseChartView(Context context) {
super(context);
init();
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
protected void init() {
linePaint.setStrokeWidth(LINE_WIDTH);
selectedLinePaint.setStrokeWidth(SELECTED_LINE_WIDTH);
signaturePaint.setTextSize(SIGNATURE_TEXT_SIZE);
signaturePaint2.setTextSize(SIGNATURE_TEXT_SIZE);
signaturePaint2.setTextAlign(Paint.Align.RIGHT);
bottomSignaturePaint.setTextSize(SIGNATURE_TEXT_SIZE);
bottomSignaturePaint.setTextAlign(Paint.Align.CENTER);
selectionBackgroundPaint.setStrokeWidth(AndroidUtilities.dpf2(6f));
selectionBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
setLayerType(LAYER_TYPE_HARDWARE, null);
setWillNotDraw(false);
legendSignatureView = createLegendView();
legendSignatureView.setVisibility(GONE);
whiteLinePaint.setColor(Color.WHITE);
whiteLinePaint.setStrokeWidth(AndroidUtilities.dpf2(3));
whiteLinePaint.setStrokeCap(Paint.Cap.ROUND);
updateColors();
}
protected LegendSignatureView createLegendView() {
return new LegendSignatureView(getContext());
}
public void updateColors() {
if (useAlphaSignature) {
signaturePaint.setColor(Theme.getColor(Theme.key_statisticChartSignatureAlpha));
} else {
signaturePaint.setColor(Theme.getColor(Theme.key_statisticChartSignature));
}
bottomSignaturePaint.setColor(Theme.getColor(Theme.key_statisticChartSignature));
linePaint.setColor(Theme.getColor(Theme.key_statisticChartHintLine));
selectedLinePaint.setColor(Theme.getColor(Theme.key_statisticChartActiveLine));
pickerSelectorPaint.setColor(Theme.getColor(Theme.key_statisticChartActivePickerChart));
unactiveBottomChartPaint.setColor(Theme.getColor(Theme.key_statisticChartInactivePickerChart));
selectionBackgroundPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite));
ripplePaint.setColor(Theme.getColor(Theme.key_statisticChartRipple));
legendSignatureView.recolor();
hintLinePaintAlpha = linePaint.getAlpha();
chartActiveLineAlpha = selectedLinePaint.getAlpha();
signaturePaintAlpha = signaturePaint.getAlpha() / 255f;
bottomSignaturePaintAlpha = bottomSignaturePaint.getAlpha() / 255f;
for (LineViewData l : lines) {
l.updateColors();
}
if (legendShowing && selectedIndex < chartData.x.length) {
legendSignatureView.setData(selectedIndex, chartData.x[selectedIndex], (ArrayList<LineViewData>) lines, false);
}
invalidatePickerChart = true;
}
int lastW = 0;
int lastH = 0;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!landscape) {
setMeasuredDimension(
MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(widthMeasureSpec)
);
} else {
setMeasuredDimension(
MeasureSpec.getSize(widthMeasureSpec),
AndroidUtilities.displaySize.y - AndroidUtilities.dp(56)
);
}
if (getMeasuredWidth() != lastW || getMeasuredHeight() != lastH) {
lastW = getMeasuredWidth();
lastH = getMeasuredHeight();
bottomChartBitmap = Bitmap.createBitmap((int) (getMeasuredWidth() - (HORIZONTAL_PADDING * 2f)), pikerHeight, Bitmap.Config.ARGB_4444);
bottomChartCanvas = new Canvas(bottomChartBitmap);
sharedUiComponents.getPickerMaskBitmap(pikerHeight, (int) (getMeasuredWidth() - HORIZONTAL_PADDING * 2));
measureSizes();
if (legendShowing)
moveLegend(chartFullWidth * (pickerDelegate.pickerStart) - HORIZONTAL_PADDING);
onPickerDataChanged(false, true, false);
}
}
private void measureSizes() {
if (getMeasuredHeight() <= 0 || getMeasuredWidth() <= 0) {
return;
}
pickerWidth = getMeasuredWidth() - (HORIZONTAL_PADDING * 2);
chartStart = HORIZONTAL_PADDING;
chartEnd = getMeasuredWidth() - (landscape ? LANDSCAPE_END_PADDING : HORIZONTAL_PADDING);
chartWidth = chartEnd - chartStart;
chartFullWidth = (chartWidth / (pickerDelegate.pickerEnd - pickerDelegate.pickerStart));
updateLineSignature();
chartBottom = AndroidUtilities.dp(100f);
chartArea.set(chartStart - HORIZONTAL_PADDING, 0, chartEnd + HORIZONTAL_PADDING, getMeasuredHeight() - chartBottom);
if (chartData != null) {
bottomSignatureOffset = (int) (AndroidUtilities.dp(20) / ((float) pickerWidth / chartData.x.length));
}
measureHeightThreshold();
}
private void measureHeightThreshold() {
int chartHeight = getMeasuredHeight() - chartBottom;
if (animateToMaxHeight == 0 || chartHeight == 0) return;
thresholdMaxHeight = ((float) animateToMaxHeight / chartHeight) * SIGNATURE_TEXT_SIZE;
}
protected void drawPickerChart(Canvas canvas) {
}
@Override
protected void onDraw(Canvas canvas) {
if (superDraw) {
super.onDraw(canvas);
return;
}
tick();
int count = canvas.save();
canvas.clipRect(0, chartArea.top, getMeasuredWidth(), chartArea.bottom);
drawBottomLine(canvas);
tmpN = horizontalLines.size();
for (tmpI = 0; tmpI < tmpN; tmpI++) {
drawHorizontalLines(canvas, horizontalLines.get(tmpI));
}
drawChart(canvas);
for (tmpI = 0; tmpI < tmpN; tmpI++) {
drawSignaturesToHorizontalLines(canvas, horizontalLines.get(tmpI));
}
canvas.restoreToCount(count);
drawBottomSignature(canvas);
drawPicker(canvas);
drawSelection(canvas);
super.onDraw(canvas);
}
protected void tick() {
if (minMaxUpdateStep == 0) {
return;
}
if (currentMaxHeight != animateToMaxHeight) {
startFromMax += minMaxUpdateStep;
if (startFromMax > 1) {
startFromMax = 1;
currentMaxHeight = animateToMaxHeight;
} else {
currentMaxHeight = startFromMaxH + (animateToMaxHeight - startFromMaxH) * CubicBezierInterpolator.EASE_OUT.getInterpolation(startFromMax);
}
invalidate();
}
if (useMinHeight) {
if (currentMinHeight != animateToMinHeight) {
startFromMin += minMaxUpdateStep;
if (startFromMin > 1) {
startFromMin = 1;
currentMinHeight = animateToMinHeight;
} else {
currentMinHeight = startFromMinH + (animateToMinHeight - startFromMinH) * CubicBezierInterpolator.EASE_OUT.getInterpolation(startFromMin);
}
invalidate();
}
}
}
void drawBottomSignature(Canvas canvas) {
if (chartData == null) return;
tmpN = bottomSignatureDate.size();
float transitionAlpha = 1f;
if (transitionMode == TRANSITION_MODE_PARENT) {
transitionAlpha = 1f - transitionParams.progress;
} else if (transitionMode == TRANSITION_MODE_CHILD) {
transitionAlpha = transitionParams.progress;
} else if (transitionMode == TRANSITION_MODE_ALPHA_ENTER) {
transitionAlpha = transitionParams.progress;
}
for (tmpI = 0; tmpI < tmpN; tmpI++) {
int resultAlpha = bottomSignatureDate.get(tmpI).alpha;
int step = bottomSignatureDate.get(tmpI).step;
if (step == 0) step = 1;
int start = startXIndex - bottomSignatureOffset;
while (start % step != 0) {
start--;
}
int end = endXIndex - bottomSignatureOffset;
while (end % step != 0 || end < chartData.x.length - 1) {
end++;
}
start += bottomSignatureOffset;
end += bottomSignatureOffset;
float offset = chartFullWidth * (pickerDelegate.pickerStart) - HORIZONTAL_PADDING;
for (int i = start; i < end; i += step) {
if (i < 0 || i >= chartData.x.length - 1) continue;
float xPercentage = (float) (chartData.x[i] - chartData.x[0]) /
(float) ((chartData.x[chartData.x.length - 1] - chartData.x[0]));
float xPoint = xPercentage * chartFullWidth - offset;
float xPointOffset = xPoint - BOTTOM_SIGNATURE_OFFSET;
if (xPointOffset > 0 &&
xPointOffset <= chartWidth + HORIZONTAL_PADDING) {
if (xPointOffset < BOTTOM_SIGNATURE_START_ALPHA) {
float a = 1f - (BOTTOM_SIGNATURE_START_ALPHA - xPointOffset) / BOTTOM_SIGNATURE_START_ALPHA;
bottomSignaturePaint.setAlpha((int) (resultAlpha * a * bottomSignaturePaintAlpha * transitionAlpha));
} else if (xPointOffset > chartWidth) {
float a = 1f - (xPointOffset - chartWidth) / HORIZONTAL_PADDING;
bottomSignaturePaint.setAlpha((int) (resultAlpha * a * bottomSignaturePaintAlpha * transitionAlpha));
} else {
bottomSignaturePaint.setAlpha((int) (resultAlpha * bottomSignaturePaintAlpha * transitionAlpha));
}
canvas.drawText(chartData.getDayString(i), xPoint, getMeasuredHeight() - chartBottom + BOTTOM_SIGNATURE_TEXT_HEIGHT + AndroidUtilities.dp(3), bottomSignaturePaint);
}
}
}
}
protected void drawBottomLine(Canvas canvas) {
if (chartData == null) {
return;
}
float transitionAlpha = 1f;
if (transitionMode == TRANSITION_MODE_PARENT) {
transitionAlpha = 1f - transitionParams.progress;
} else if (transitionMode == TRANSITION_MODE_CHILD) {
transitionAlpha = transitionParams.progress;
} else if (transitionMode == TRANSITION_MODE_ALPHA_ENTER) {
transitionAlpha = transitionParams.progress;
}
linePaint.setAlpha((int) (hintLinePaintAlpha * transitionAlpha));
signaturePaint.setAlpha((int) (255 * signaturePaintAlpha * transitionAlpha));
int textOffset = (int) (SIGNATURE_TEXT_HEIGHT - signaturePaint.getTextSize());
int y = (getMeasuredHeight() - chartBottom - 1);
canvas.drawLine(
chartStart,
y,
chartEnd,
y,
linePaint);
if (useMinHeight) return;
canvas.drawText("0", HORIZONTAL_PADDING, y - textOffset, signaturePaint);
}
protected void drawSelection(Canvas canvas) {
if (selectedIndex < 0 || !legendShowing || chartData == null) return;
int alpha = (int) (chartActiveLineAlpha * selectionA);
float fullWidth = (chartWidth / (pickerDelegate.pickerEnd - pickerDelegate.pickerStart));
float offset = fullWidth * (pickerDelegate.pickerStart) - HORIZONTAL_PADDING;
float xPoint;
if (selectedIndex < chartData.xPercentage.length) {
xPoint = chartData.xPercentage[selectedIndex] * fullWidth - offset;
} else {
return;
}
selectedLinePaint.setAlpha(alpha);
canvas.drawLine(xPoint, 0, xPoint, chartArea.bottom, selectedLinePaint);
if (drawPointOnSelection) {
tmpN = lines.size();
for (tmpI = 0; tmpI < tmpN; tmpI++) {
LineViewData line = lines.get(tmpI);
if (!line.enabled && line.alpha == 0) continue;
float yPercentage = (line.line.y[selectedIndex] - currentMinHeight) / (currentMaxHeight - currentMinHeight);
float yPoint = getMeasuredHeight() - chartBottom - (yPercentage) * (getMeasuredHeight() - chartBottom - SIGNATURE_TEXT_HEIGHT);
line.selectionPaint.setAlpha((int) (255 * line.alpha * selectionA));
selectionBackgroundPaint.setAlpha((int) (255 * line.alpha * selectionA));
canvas.drawPoint(xPoint, yPoint, line.selectionPaint);
canvas.drawPoint(xPoint, yPoint, selectionBackgroundPaint);
}
}
}
protected void drawChart(Canvas canvas) {
}
protected void drawHorizontalLines(Canvas canvas, ChartHorizontalLinesData a) {
int n = a.values.length;
float additionalOutAlpha = 1f;
if (n > 2) {
float v = (a.values[1] - a.values[0]) / (float) (currentMaxHeight - currentMinHeight);
if (v < 0.1) {
additionalOutAlpha = v / 0.1f;
}
}
float transitionAlpha = 1f;
if (transitionMode == TRANSITION_MODE_PARENT) {
transitionAlpha = 1f - transitionParams.progress;
} else if (transitionMode == TRANSITION_MODE_CHILD) {
transitionAlpha = transitionParams.progress;
} else if (transitionMode == TRANSITION_MODE_ALPHA_ENTER) {
transitionAlpha = transitionParams.progress;
}
linePaint.setAlpha((int) (a.alpha * (hintLinePaintAlpha / 255f) * transitionAlpha * additionalOutAlpha));
signaturePaint.setAlpha((int) (a.alpha * signaturePaintAlpha * transitionAlpha * additionalOutAlpha));
int chartHeight = getMeasuredHeight() - chartBottom - SIGNATURE_TEXT_HEIGHT;
for (int i = useMinHeight ? 0 : 1; i < n; i++) {
int y = (int) ((getMeasuredHeight() - chartBottom) - chartHeight * ((a.values[i] - currentMinHeight) / (currentMaxHeight - currentMinHeight)));
canvas.drawRect(chartStart, y, chartEnd, y + 1, linePaint);
}
}
protected void drawSignaturesToHorizontalLines(Canvas canvas, ChartHorizontalLinesData a) {
int n = a.values.length;
float additionalOutAlpha = 1f;
if (n > 2) {
float v = (a.values[1] - a.values[0]) / (float) (currentMaxHeight - currentMinHeight);
if (v < 0.1) {
additionalOutAlpha = v / 0.1f;
}
}
float transitionAlpha = 1f;
if (transitionMode == TRANSITION_MODE_PARENT) {
transitionAlpha = 1f - transitionParams.progress;
} else if (transitionMode == TRANSITION_MODE_CHILD) {
transitionAlpha = transitionParams.progress;
} else if (transitionMode == TRANSITION_MODE_ALPHA_ENTER) {
transitionAlpha = transitionParams.progress;
}
linePaint.setAlpha((int) (a.alpha * (hintLinePaintAlpha / 255f) * transitionAlpha * additionalOutAlpha));
signaturePaint.setAlpha((int) (a.alpha * signaturePaintAlpha * transitionAlpha * additionalOutAlpha));
int chartHeight = getMeasuredHeight() - chartBottom - SIGNATURE_TEXT_HEIGHT;
int textOffset = (int) (SIGNATURE_TEXT_HEIGHT - signaturePaint.getTextSize());
for (int i = useMinHeight ? 0 : 1; i < n; i++) {
int y = (int) ((getMeasuredHeight() - chartBottom) - chartHeight * ((a.values[i] - currentMinHeight) / (currentMaxHeight - currentMinHeight)));
canvas.drawText(a.valuesStr[i], HORIZONTAL_PADDING, y - textOffset, signaturePaint);
}
}
void drawPicker(Canvas canvas) {
if (chartData == null) {
return;
}
pickerDelegate.pickerWidth = pickerWidth;
int bottom = getMeasuredHeight() - PICKER_PADDING;
int top = getMeasuredHeight() - pikerHeight - PICKER_PADDING;
int start = (int) (HORIZONTAL_PADDING + pickerWidth * pickerDelegate.pickerStart);
int end = (int) (HORIZONTAL_PADDING + pickerWidth * pickerDelegate.pickerEnd);
float transitionAlpha = 1f;
if (transitionMode == TRANSITION_MODE_CHILD) {
int startParent = (int) (HORIZONTAL_PADDING + pickerWidth * transitionParams.pickerStartOut);
int endParent = (int) (HORIZONTAL_PADDING + pickerWidth * transitionParams.pickerEndOut);
start += (startParent - start) * (1f - transitionParams.progress);
end += (endParent - end) * (1f - transitionParams.progress);
} else if (transitionMode == TRANSITION_MODE_ALPHA_ENTER) {
transitionAlpha = transitionParams.progress;
}
if (chartData != null) {
boolean instantDraw = false;
if (transitionMode == TRANSITION_MODE_NONE) {
for (int i = 0; i < lines.size(); i++) {
L l = lines.get(i);
if ((l.animatorIn != null && l.animatorIn.isRunning()) || (l.animatorOut != null && l.animatorOut.isRunning())) {
instantDraw = true;
break;
}
}
}
if (instantDraw) {
canvas.save();
canvas.clipRect(
HORIZONTAL_PADDING, getMeasuredHeight() - PICKER_PADDING - pikerHeight,
getMeasuredWidth() - HORIZONTAL_PADDING, getMeasuredHeight() - PICKER_PADDING
);
canvas.translate(HORIZONTAL_PADDING, getMeasuredHeight() - PICKER_PADDING - pikerHeight);
drawPickerChart(canvas);
canvas.restore();
} else if (invalidatePickerChart) {
bottomChartBitmap.eraseColor(0);
drawPickerChart(bottomChartCanvas);
invalidatePickerChart = false;
}
if (!instantDraw) {
if (transitionMode == TRANSITION_MODE_PARENT) {
float pY = top + (bottom - top) >> 1;
float pX = HORIZONTAL_PADDING + pickerWidth * transitionParams.xPercentage;
emptyPaint.setAlpha((int) ((1f - transitionParams.progress) * 255));
canvas.save();
canvas.clipRect(HORIZONTAL_PADDING, top, getMeasuredWidth() - HORIZONTAL_PADDING, bottom);
canvas.scale(1 + 2 * transitionParams.progress, 1f, pX, pY);
canvas.drawBitmap(bottomChartBitmap, HORIZONTAL_PADDING, getMeasuredHeight() - PICKER_PADDING - pikerHeight, emptyPaint);
canvas.restore();
} else if (transitionMode == TRANSITION_MODE_CHILD) {
float pY = top + (bottom - top) >> 1;
float pX = HORIZONTAL_PADDING + pickerWidth * transitionParams.xPercentage;
float dX = (transitionParams.xPercentage > 0.5f ? pickerWidth * transitionParams.xPercentage : pickerWidth * (1f - transitionParams.xPercentage)) * transitionParams.progress;
canvas.save();
canvas.clipRect(pX - dX, top, pX + dX, bottom);
emptyPaint.setAlpha((int) (transitionParams.progress * 255));
canvas.scale(transitionParams.progress, 1f, pX, pY);
canvas.drawBitmap(bottomChartBitmap, HORIZONTAL_PADDING, getMeasuredHeight() - PICKER_PADDING - pikerHeight, emptyPaint);
canvas.restore();
} else {
emptyPaint.setAlpha((int) ((transitionAlpha) * 255));
canvas.drawBitmap(bottomChartBitmap, HORIZONTAL_PADDING, getMeasuredHeight() - PICKER_PADDING - pikerHeight, emptyPaint);
}
}
if (transitionMode == TRANSITION_MODE_PARENT) {
return;
}
canvas.drawRect(HORIZONTAL_PADDING,
top,
start + DP_12,
bottom, unactiveBottomChartPaint);
canvas.drawRect(end - DP_12,
top,
getMeasuredWidth() - HORIZONTAL_PADDING,
bottom, unactiveBottomChartPaint);
} else {
canvas.drawRect(HORIZONTAL_PADDING,
top,
getMeasuredWidth() - HORIZONTAL_PADDING,
bottom, unactiveBottomChartPaint);
}
canvas.drawBitmap(
sharedUiComponents.getPickerMaskBitmap(pikerHeight, (int) (getMeasuredWidth() - HORIZONTAL_PADDING * 2)),
HORIZONTAL_PADDING, getMeasuredHeight() - PICKER_PADDING - pikerHeight, emptyPaint);
if (chartData != null) {
pickerRect.set(start,
top,
end,
bottom);
pickerDelegate.middlePickerArea.set(pickerRect);
canvas.drawPath(RoundedRect(pathTmp, pickerRect.left,
pickerRect.top - DP_1,
pickerRect.left + DP_12,
pickerRect.bottom + DP_1, DP_6, DP_6,
true, false, false, true), pickerSelectorPaint);
canvas.drawPath(RoundedRect(pathTmp, pickerRect.right - DP_12,
pickerRect.top - DP_1, pickerRect.right,
pickerRect.bottom + DP_1, DP_6, DP_6,
false, true, true, false), pickerSelectorPaint);
canvas.drawRect(pickerRect.left + DP_12,
pickerRect.bottom, pickerRect.right - DP_12,
pickerRect.bottom + DP_1, pickerSelectorPaint);
canvas.drawRect(pickerRect.left + DP_12,
pickerRect.top - DP_1, pickerRect.right - DP_12,
pickerRect.top, pickerSelectorPaint);
canvas.drawLine(pickerRect.left + DP_6, pickerRect.centerY() - DP_6,
pickerRect.left + DP_6, pickerRect.centerY() + DP_6, whiteLinePaint);
canvas.drawLine(pickerRect.right - DP_6, pickerRect.centerY() - DP_6,
pickerRect.right - DP_6, pickerRect.centerY() + DP_6, whiteLinePaint);
ChartPickerDelegate.CapturesData middleCap = pickerDelegate.getMiddleCaptured();
int r = ((pickerRect.bottom - pickerRect.top) >> 1);
int cY = pickerRect.top + r;
if (middleCap != null) {
// canvas.drawCircle(pickerRect.left + ((pickerRect.right - pickerRect.left) >> 1), cY, r * middleCap.aValue + HORIZONTAL_PADDING, ripplePaint);
} else {
ChartPickerDelegate.CapturesData lCap = pickerDelegate.getLeftCaptured();
ChartPickerDelegate.CapturesData rCap = pickerDelegate.getRightCaptured();
if (lCap != null)
canvas.drawCircle(pickerRect.left + DP_5, cY, r * lCap.aValue - DP_2, ripplePaint);
if (rCap != null)
canvas.drawCircle(pickerRect.right - DP_5, cY, r * rCap.aValue - DP_2, ripplePaint);
}
int cX = start;
pickerDelegate.leftPickerArea.set(
cX - PICKER_CAPTURE_WIDTH,
top,
cX + (PICKER_CAPTURE_WIDTH >> 1),
bottom
);
cX = end;
pickerDelegate.rightPickerArea.set(
cX - (PICKER_CAPTURE_WIDTH >> 1),
top,
cX + PICKER_CAPTURE_WIDTH,
bottom
);
}
}
long lastTime = 0;
private void setMaxMinValue(int newMaxHeight, int newMinHeight, boolean animated) {
setMaxMinValue(newMaxHeight, newMinHeight, animated, false, false);
}
protected void setMaxMinValue(int newMaxHeight, int newMinHeight, boolean animated, boolean force, boolean useAnimator) {
boolean heightChanged = true;
if ((Math.abs(ChartHorizontalLinesData.lookupHeight(newMaxHeight) - animateToMaxHeight) < thresholdMaxHeight) || newMaxHeight == 0) {
heightChanged = false;
}
if (!heightChanged && newMaxHeight == animateToMinHeight) return;
final ChartHorizontalLinesData newData = createHorizontalLinesData(newMaxHeight, newMinHeight);
newMaxHeight = newData.values[newData.values.length - 1];
newMinHeight = newData.values[0];
if (!useAnimator) {
float k = (currentMaxHeight - currentMinHeight) / (newMaxHeight - newMinHeight);
if (k > 1f) {
k = (newMaxHeight - newMinHeight) / (currentMaxHeight - currentMinHeight);
}
float s = 0.045f;
if (k > 0.7) {
s = 0.1f;
} else if (k < 0.1) {
s = 0.03f;
}
boolean update = false;
if (newMaxHeight != animateToMaxHeight) {
update = true;
}
if (useMinHeight && newMinHeight != animateToMinHeight) {
update = true;
}
if (update) {
if (maxValueAnimator != null) {
maxValueAnimator.removeAllListeners();
maxValueAnimator.cancel();
}
startFromMaxH = currentMaxHeight;
startFromMinH = currentMinHeight;
startFromMax = 0;
startFromMin = 0;
minMaxUpdateStep = s;
}
}
animateToMaxHeight = newMaxHeight;
animateToMinHeight = newMinHeight;
measureHeightThreshold();
long t = System.currentTimeMillis();
// debounce
if (t - lastTime < 320 && !force) {
return;
}
lastTime = t;
if (alphaAnimator != null) {
alphaAnimator.removeAllListeners();
alphaAnimator.cancel();
}
if (!animated) {
currentMaxHeight = newMaxHeight;
currentMinHeight = newMinHeight;
horizontalLines.clear();
horizontalLines.add(newData);
newData.alpha = 255;
return;
}
horizontalLines.add(newData);
if (useAnimator) {
if (maxValueAnimator != null) {
maxValueAnimator.removeAllListeners();
maxValueAnimator.cancel();
}
minMaxUpdateStep = 0;
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(createAnimator(currentMaxHeight, newMaxHeight, heightUpdateListener));
if (useMinHeight) {
animatorSet.playTogether(createAnimator(currentMinHeight, newMinHeight, minHeightUpdateListener));
}
maxValueAnimator = animatorSet;
maxValueAnimator.start();
}
int n = horizontalLines.size();
for (int i = 0; i < n; i++) {
ChartHorizontalLinesData a = horizontalLines.get(i);
if (a != newData) a.fixedAlpha = a.alpha;
}
alphaAnimator = createAnimator(0, 255, animation -> {
newData.alpha = (int) ((float) animation.getAnimatedValue());
for (ChartHorizontalLinesData a : horizontalLines) {
if (a != newData)
a.alpha = (int) ((a.fixedAlpha / 255f) * (255 - newData.alpha));
}
invalidate();
});
alphaAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
horizontalLines.clear();
horizontalLines.add(newData);
}
});
alphaAnimator.start();
}
protected ChartHorizontalLinesData createHorizontalLinesData(int newMaxHeight, int newMinHeight) {
return new ChartHorizontalLinesData(newMaxHeight, newMinHeight, useMinHeight);
}
ValueAnimator createAnimator(float f1, float f2, ValueAnimator.AnimatorUpdateListener l) {
ValueAnimator a = ValueAnimator.ofFloat(f1, f2);
a.setDuration(ANIM_DURATION);
a.setInterpolator(INTERPOLATOR);
a.addUpdateListener(l);
return a;
}
int lastX;
int lastY;
int capturedX;
int capturedY;
long capturedTime;
protected boolean canCaptureChartSelection;
@Override
public boolean onTouchEvent(MotionEvent event) {
if (chartData == null) {
return false;
}
if (!enabled) {
pickerDelegate.uncapture(event, event.getActionIndex());
getParent().requestDisallowInterceptTouchEvent(false);
chartCaptured = false;
return false;
}
int x = (int) event.getX(event.getActionIndex());
int y = (int) event.getY(event.getActionIndex());
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
capturedTime = System.currentTimeMillis();
getParent().requestDisallowInterceptTouchEvent(true);
boolean captured = pickerDelegate.capture(x, y, event.getActionIndex());
if (captured) {
return true;
}
capturedX = lastX = x;
capturedY = lastY = y;
if (chartArea.contains(x, y)) {
if (selectedIndex < 0 || !animateLegentTo) {
chartCaptured = true;
selectXOnChart(x, y);
}
return true;
}
return false;
case MotionEvent.ACTION_POINTER_DOWN:
return pickerDelegate.capture(x, y, event.getActionIndex());
case MotionEvent.ACTION_MOVE:
int dx = x - lastX;
int dy = y - lastY;
if (pickerDelegate.captured()) {
boolean rez = pickerDelegate.move(x, y, event.getActionIndex());
if (event.getPointerCount() > 1) {
x = (int) event.getX(1);
y = (int) event.getY(1);
pickerDelegate.move(x, y, 1);
}
getParent().requestDisallowInterceptTouchEvent(rez);
return true;
}
if (chartCaptured) {
boolean disable;
if (canCaptureChartSelection && System.currentTimeMillis() - capturedTime > 200) {
disable = true;
} else {
disable = Math.abs(dx) > Math.abs(dy) || Math.abs(dy) < touchSlop;
}
lastX = x;
lastY = y;
getParent().requestDisallowInterceptTouchEvent(disable);
selectXOnChart(x, y);
} else if (chartArea.contains(capturedX, capturedY)){
int dxCaptured = capturedX - x;
int dyCaptured = capturedY - y;
if (Math.sqrt(dxCaptured * dxCaptured + dyCaptured * dyCaptured) > touchSlop || System.currentTimeMillis() - capturedTime > 200) {
chartCaptured = true;
selectXOnChart(x, y);
}
}
return true;
case MotionEvent.ACTION_POINTER_UP:
pickerDelegate.uncapture(event, event.getActionIndex());
return true;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (pickerDelegate.uncapture(event, event.getActionIndex())) {
return true;
}
if (chartArea.contains(capturedX, capturedY) && !chartCaptured) {
animateLegend(false);
}
pickerDelegate.uncapture();
updateLineSignature();
getParent().requestDisallowInterceptTouchEvent(false);
chartCaptured = false;
onActionUp();
invalidate();
int min = 0;
if (useMinHeight) min = findMinValue(startXIndex, endXIndex);
setMaxMinValue(findMaxValue(startXIndex, endXIndex), min, true, true, false);
return true;
}
return false;
}
protected void onActionUp() {
}
protected void selectXOnChart(int x, int y) {
int oldSelectedX = selectedIndex;
if (chartData == null) return;
float offset = chartFullWidth * (pickerDelegate.pickerStart) - HORIZONTAL_PADDING;
float xP = (offset + x) / chartFullWidth;
selectedCoordinate = xP;
if (xP < 0) {
selectedIndex = 0;
selectedCoordinate = 0f;
} else if (xP > 1) {
selectedIndex = chartData.x.length - 1;
selectedCoordinate = 1f;
} else {
selectedIndex = chartData.findIndex(startXIndex, endXIndex, xP);
if (selectedIndex + 1 < chartData.xPercentage.length) {
float dx = Math.abs(chartData.xPercentage[selectedIndex] - xP);
float dx2 = Math.abs(chartData.xPercentage[selectedIndex + 1] - xP);
if (dx2 < dx) {
selectedIndex++;
}
}
}
if (selectedIndex > endXIndex) selectedIndex = endXIndex;
if (selectedIndex < startXIndex) selectedIndex = startXIndex;
legendShowing = true;
animateLegend(true);
moveLegend(offset);
if (dateSelectionListener != null) {
dateSelectionListener.onDateSelected(getSelectedDate());
}
invalidate();
}
public boolean animateLegentTo = false;
public void animateLegend(boolean show) {
moveLegend();
if (animateLegentTo == show) return;
animateLegentTo = show;
if (selectionAnimator != null) {
selectionAnimator.removeAllListeners();
selectionAnimator.cancel();
}
selectionAnimator = createAnimator(selectionA, show ? 1f : 0f, selectionAnimatorListener)
.setDuration(200);
selectionAnimator.addListener(selectorAnimatorEndListener);
selectionAnimator.start();
}
public void moveLegend(float offset) {
if (chartData == null || selectedIndex == -1 || !legendShowing) return;
legendSignatureView.setData(selectedIndex, chartData.x[selectedIndex], (ArrayList<LineViewData>) lines, false);
legendSignatureView.setVisibility(VISIBLE);
legendSignatureView.measure(
MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)
);
float lXPoint = chartData.xPercentage[selectedIndex] * chartFullWidth - offset;
if (lXPoint > (chartStart + chartWidth) / 2f) {
lXPoint -= (legendSignatureView.getWidth() + DP_5);
} else {
lXPoint += DP_5;
}
if (lXPoint < 0) {
lXPoint = 0;
} else if (lXPoint + legendSignatureView.getMeasuredWidth() > getMeasuredWidth()) {
lXPoint = getMeasuredWidth() - legendSignatureView.getMeasuredWidth();
}
legendSignatureView.setTranslationX(
lXPoint
);
}
public int findMaxValue(int startXIndex, int endXIndex) {
int linesSize = lines.size();
int maxValue = 0;
for (int j = 0; j < linesSize; j++) {
if (!lines.get(j).enabled) continue;
int lineMax = lines.get(j).line.segmentTree.rMaxQ(startXIndex, endXIndex);
if (lineMax > maxValue)
maxValue = lineMax;
}
return maxValue;
}
public int findMinValue(int startXIndex, int endXIndex) {
int linesSize = lines.size();
int minValue = Integer.MAX_VALUE;
for (int j = 0; j < linesSize; j++) {
if (!lines.get(j).enabled) continue;
int lineMin = lines.get(j).line.segmentTree.rMinQ(startXIndex, endXIndex);
if (lineMin < minValue)
minValue = lineMin;
}
return minValue;
}
public void setData(T chartData) {
if (this.chartData != chartData) {
invalidate();
lines.clear();
if (chartData != null && chartData.lines != null) {
for (int i = 0; i < chartData.lines.size(); i++) {
lines.add(createLineViewData(chartData.lines.get(i)));
}
}
clearSelection();
this.chartData = chartData;
if (chartData != null) {
if (chartData.x[0] == 0) {
pickerDelegate.pickerStart = 0f;
pickerDelegate.pickerEnd = 1f;
} else {
pickerDelegate.minDistance = getMinDistance();
if (pickerDelegate.pickerEnd - pickerDelegate.pickerStart < pickerDelegate.minDistance) {
pickerDelegate.pickerStart = pickerDelegate.pickerEnd - pickerDelegate.minDistance;
if (pickerDelegate.pickerStart < 0) {
pickerDelegate.pickerStart = 0f;
pickerDelegate.pickerEnd = 1f;
}
}
}
}
}
measureSizes();
if (chartData != null) {
updateIndexes();
int min = useMinHeight ? findMinValue(startXIndex, endXIndex) : 0;
setMaxMinValue(findMaxValue(startXIndex, endXIndex), min, false);
pickerMaxHeight = 0;
pickerMinHeight = Integer.MAX_VALUE;
initPickerMaxHeight();
legendSignatureView.setSize(lines.size());
invalidatePickerChart = true;
updateLineSignature();
} else {
pickerDelegate.pickerStart = 0.7f;
pickerDelegate.pickerEnd = 1f;
pickerMaxHeight = pickerMinHeight = 0;
horizontalLines.clear();
if (maxValueAnimator != null) {
maxValueAnimator.cancel();
}
if (alphaAnimator != null) {
alphaAnimator.removeAllListeners();
alphaAnimator.cancel();
}
}
}
protected float getMinDistance() {
if (chartData == null) {
return 0.1f;
}
int n = chartData.x.length;
if (n < 5) {
return 1f;
}
float r = 5f / n;
if (r < 0.1f) {
return 0.1f;
}
return r;
}
protected void initPickerMaxHeight() {
for (LineViewData l : lines) {
if (l.enabled && l.line.maxValue > pickerMaxHeight) pickerMaxHeight = l.line.maxValue;
if (l.enabled && l.line.minValue < pickerMinHeight) pickerMinHeight = l.line.minValue;
if (pickerMaxHeight == pickerMinHeight) {
pickerMaxHeight++;
pickerMinHeight--;
}
}
}
public abstract L createLineViewData(ChartData.Line line);
public void onPickerDataChanged() {
onPickerDataChanged(true, false, false);
}
public void onPickerDataChanged(boolean animated, boolean force, boolean useAniamtor) {
if (chartData == null) return;
chartFullWidth = (chartWidth / (pickerDelegate.pickerEnd - pickerDelegate.pickerStart));
updateIndexes();
int min = useMinHeight ? findMinValue(startXIndex, endXIndex) : 0;
setMaxMinValue(findMaxValue(startXIndex, endXIndex), min, animated, force, useAniamtor);
if (legendShowing && !force) {
animateLegend(false);
moveLegend(chartFullWidth * (pickerDelegate.pickerStart) - HORIZONTAL_PADDING);
}
invalidate();
}
public void onPickerJumpTo(float start, float end, boolean force) {
if (chartData == null) return;
if (force) {
int startXIndex = chartData.findStartIndex(Math.max(
start, 0f
));
int endXIndex = chartData.findEndIndex(startXIndex, Math.min(
end, 1f
));
setMaxMinValue(findMaxValue(startXIndex, endXIndex), findMinValue(startXIndex, endXIndex), true, true, false);
animateLegend(false);
} else {
updateIndexes();
invalidate();
}
}
protected void updateIndexes() {
if (chartData == null) return;
startXIndex = chartData.findStartIndex(Math.max(
pickerDelegate.pickerStart, 0f
));
endXIndex = chartData.findEndIndex(startXIndex, Math.min(
pickerDelegate.pickerEnd, 1f
));
if (endXIndex < startXIndex) {
endXIndex = startXIndex;
}
if (chartHeaderView != null) {
chartHeaderView.setDates(chartData.x[startXIndex], chartData.x[endXIndex]);
}
updateLineSignature();
}
private final static int BOTTOM_SIGNATURE_COUNT = 6;
private void updateLineSignature() {
if (chartData == null || chartWidth == 0) return;
float d = chartFullWidth * chartData.oneDayPercentage;
float k = chartWidth / d;
int step = (int) (k / BOTTOM_SIGNATURE_COUNT);
updateDates(step);
}
private void updateDates(int step) {
if (currentBottomSignatures == null || step >= currentBottomSignatures.stepMax || step <= currentBottomSignatures.stepMin) {
step = Integer.highestOneBit(step) << 1;
if (currentBottomSignatures != null && currentBottomSignatures.step == step) {
return;
}
if (alphaBottomAnimator != null) {
alphaBottomAnimator.removeAllListeners();
alphaBottomAnimator.cancel();
}
int stepMax = (int) (step + step * 0.2);
int stepMin = (int) (step - step * 0.2);
final ChartBottomSignatureData data = new ChartBottomSignatureData(step, stepMax, stepMin);
data.alpha = 255;
if (currentBottomSignatures == null) {
currentBottomSignatures = data;
data.alpha = 255;
bottomSignatureDate.add(data);
return;
}
currentBottomSignatures = data;
tmpN = bottomSignatureDate.size();
for (int i = 0; i < tmpN; i++) {
ChartBottomSignatureData a = bottomSignatureDate.get(i);
a.fixedAlpha = a.alpha;
}
bottomSignatureDate.add(data);
if (bottomSignatureDate.size() > 2) {
bottomSignatureDate.remove(0);
}
alphaBottomAnimator = createAnimator(0f, 1f, animation -> {
float alpha = (float) animation.getAnimatedValue();
for (ChartBottomSignatureData a : bottomSignatureDate) {
if (a == data) {
data.alpha = (int) (255 * alpha);
} else {
a.alpha = (int) ((1f - alpha) * (a.fixedAlpha));
}
}
invalidate();
}).setDuration(200);
alphaBottomAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
bottomSignatureDate.clear();
bottomSignatureDate.add(data);
}
});
alphaBottomAnimator.start();
}
}
public void onCheckChanged() {
onPickerDataChanged(true, true, true);
tmpN = lines.size();
for (tmpI = 0; tmpI < tmpN; tmpI++) {
final LineViewData lineViewData = lines.get(tmpI);
if (lineViewData.enabled && lineViewData.animatorOut != null) {
lineViewData.animatorOut.cancel();
}
if (!lineViewData.enabled && lineViewData.animatorIn != null) {
lineViewData.animatorIn.cancel();
}
if (lineViewData.enabled && lineViewData.alpha != 1f) {
if (lineViewData.animatorIn != null && lineViewData.animatorIn.isRunning()) {
continue;
}
lineViewData.animatorIn = createAnimator(lineViewData.alpha, 1f, animation -> {
lineViewData.alpha = ((float) animation.getAnimatedValue());
invalidatePickerChart = true;
invalidate();
});
lineViewData.animatorIn.start();
}
if (!lineViewData.enabled && lineViewData.alpha != 0) {
if (lineViewData.animatorOut != null && lineViewData.animatorOut.isRunning()) {
continue;
}
lineViewData.animatorOut = createAnimator(lineViewData.alpha, 0f, animation -> {
lineViewData.alpha = ((float) animation.getAnimatedValue());
invalidatePickerChart = true;
invalidate();
});
lineViewData.animatorOut.start();
}
}
updatePickerMinMaxHeight();
if (legendShowing)
legendSignatureView.setData(selectedIndex, chartData.x[selectedIndex], (ArrayList<LineViewData>) lines, true);
}
protected void updatePickerMinMaxHeight() {
if (!ANIMATE_PICKER_SIZES) return;
int max = 0;
int min = Integer.MAX_VALUE;
for (LineViewData l : lines) {
if (l.enabled && l.line.maxValue > max) max = l.line.maxValue;
if (l.enabled && l.line.minValue < min) min = l.line.minValue;
}
if ((min != Integer.MAX_VALUE && min != animatedToPickerMinHeight) || (max > 0 && max != animatedToPickerMaxHeight)) {
animatedToPickerMaxHeight = max;
if (pickerAnimator != null) pickerAnimator.cancel();
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
createAnimator(pickerMaxHeight, animatedToPickerMaxHeight, pickerHeightUpdateListener),
createAnimator(pickerMinHeight, animatedToPickerMinHeight, pickerMinHeightUpdateListener)
);
pickerAnimator = animatorSet;
pickerAnimator.start();
}
}
public void setLandscape(boolean b) {
landscape = b;
}
public void saveState(Bundle outState) {
if (outState == null) return;
outState.putFloat("chart_start", pickerDelegate.pickerStart);
outState.putFloat("chart_end", pickerDelegate.pickerEnd);
if (lines != null) {
int n = lines.size();
boolean[] bArray = new boolean[n];
for (int i = 0; i < n; i++) {
bArray[i] = lines.get(i).enabled;
}
outState.putBooleanArray("chart_line_enabled", bArray);
}
}
ChartHeaderView chartHeaderView;
public void setHeader(ChartHeaderView chartHeaderView) {
this.chartHeaderView = chartHeaderView;
}
public long getSelectedDate() {
if (selectedIndex < 0) {
return -1;
}
return chartData.x[selectedIndex];
}
public void clearSelection() {
selectedIndex = -1;
legendShowing = false;
animateLegentTo = false;
legendSignatureView.setVisibility(GONE);
selectionA = 0f;
}
public void selectDate(long activeZoom) {
selectedIndex = Arrays.binarySearch(chartData.x, activeZoom);
legendShowing = true;
legendSignatureView.setVisibility(VISIBLE);
selectionA = 1f;
moveLegend(chartFullWidth * (pickerDelegate.pickerStart) - HORIZONTAL_PADDING);
performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
public long getStartDate() {
return chartData.x[startXIndex];
}
public long getEndDate() {
return chartData.x[endXIndex];
}
public void updatePicker(ChartData chartData, long d) {
int n = chartData.x.length;
long startOfDay = d - d % 86400000L;
long endOfDay = startOfDay + 86400000L - 1;
int startIndex = 0;
int endIndex = 0;
for (int i = 0; i < n; i++) {
if (startOfDay > chartData.x[i]) startIndex = i;
if (endOfDay > chartData.x[i]) endIndex = i;
}
pickerDelegate.pickerStart = chartData.xPercentage[startIndex];
pickerDelegate.pickerEnd = chartData.xPercentage[endIndex];
}
public void moveLegend() {
moveLegend(chartFullWidth * (pickerDelegate.pickerStart) - HORIZONTAL_PADDING);
}
@Override
public void requestLayout() {
super.requestLayout();
}
public static Path RoundedRect(
Path path,
float left, float top, float right, float bottom, float rx, float ry,
boolean tl, boolean tr, boolean br, boolean bl
) {
path.reset();
if (rx < 0) rx = 0;
if (ry < 0) ry = 0;
float width = right - left;
float height = bottom - top;
if (rx > width / 2) rx = width / 2;
if (ry > height / 2) ry = height / 2;
float widthMinusCorners = (width - (2 * rx));
float heightMinusCorners = (height - (2 * ry));
path.moveTo(right, top + ry);
if (tr)
path.rQuadTo(0, -ry, -rx, -ry);
else {
path.rLineTo(0, -ry);
path.rLineTo(-rx, 0);
}
path.rLineTo(-widthMinusCorners, 0);
if (tl)
path.rQuadTo(-rx, 0, -rx, ry);
else {
path.rLineTo(-rx, 0);
path.rLineTo(0, ry);
}
path.rLineTo(0, heightMinusCorners);
if (bl)
path.rQuadTo(0, ry, rx, ry);
else {
path.rLineTo(0, ry);
path.rLineTo(rx, 0);
}
path.rLineTo(widthMinusCorners, 0);
if (br)
path.rQuadTo(rx, 0, rx, -ry);
else {
path.rLineTo(rx, 0);
path.rLineTo(0, -ry);
}
path.rLineTo(0, -heightMinusCorners);
path.close();
return path;
}
public void setDateSelectionListener(DateSelectionListener dateSelectionListener) {
this.dateSelectionListener = dateSelectionListener;
}
public interface DateSelectionListener {
void onDateSelected(long date);
}
public static class SharedUiComponents {
private Bitmap pickerRoundBitmap;
private Canvas canvas;
private RectF rectF = new RectF();
private Paint xRefP = new Paint(Paint.ANTI_ALIAS_FLAG);
public SharedUiComponents() {
xRefP.setColor(0);
xRefP.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
int k = 0;
private boolean invalidate = true;
Bitmap getPickerMaskBitmap(int h, int w) {
if (h + w << 10 != k || invalidate) {
invalidate = false;
k = h + w << 10;
pickerRoundBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvas = new Canvas(pickerRoundBitmap);
rectF.set(0, 0, w, h);
canvas.drawColor(Theme.getColor(Theme.key_windowBackgroundWhite));
canvas.drawRoundRect(rectF, AndroidUtilities.dp(4), AndroidUtilities.dp(4), xRefP);
}
return pickerRoundBitmap;
}
public void invalidate(){
invalidate = true;
}
}
public void fillTransitionParams(TransitionParams params) {
}
}