mirror of https://github.com/NekoX-Dev/NekoX.git
529 lines
19 KiB
Java
529 lines
19 KiB
Java
/*
|
|
* This is the source code of Telegram for Android v. 5.x.x.
|
|
* It is licensed under GNU GPL v. 2 or later.
|
|
* You should have received a copy of the license in this archive (see LICENSE).
|
|
*
|
|
* Copyright Nikolai Kudashov, 2013-2018.
|
|
*/
|
|
|
|
package org.telegram.ui.Components;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorListenerAdapter;
|
|
import android.animation.ObjectAnimator;
|
|
import android.content.Context;
|
|
import android.content.res.ColorStateList;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Color;
|
|
import android.graphics.ColorFilter;
|
|
import android.graphics.Paint;
|
|
import android.graphics.PixelFormat;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.PorterDuffColorFilter;
|
|
import android.graphics.PorterDuffXfermode;
|
|
import android.graphics.RectF;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.graphics.drawable.RippleDrawable;
|
|
import android.os.Build;
|
|
import androidx.annotation.Keep;
|
|
import android.util.StateSet;
|
|
import android.view.View;
|
|
import android.view.accessibility.AccessibilityNodeInfo;
|
|
|
|
import org.telegram.messenger.AndroidUtilities;
|
|
import org.telegram.ui.ActionBar.Theme;
|
|
|
|
public class Switch extends View {
|
|
|
|
private RectF rectF;
|
|
|
|
private float progress;
|
|
private ObjectAnimator checkAnimator;
|
|
private ObjectAnimator iconAnimator;
|
|
|
|
private boolean attachedToWindow;
|
|
private boolean isChecked;
|
|
private Paint paint;
|
|
private Paint paint2;
|
|
|
|
private int drawIconType;
|
|
private float iconProgress = 1.0f;
|
|
|
|
private OnCheckedChangeListener onCheckedChangeListener;
|
|
|
|
private String trackColorKey = Theme.key_switch2Track;
|
|
private String trackCheckedColorKey = Theme.key_switch2TrackChecked;
|
|
private String thumbColorKey = Theme.key_windowBackgroundWhite;
|
|
private String thumbCheckedColorKey = Theme.key_windowBackgroundWhite;
|
|
|
|
private Drawable iconDrawable;
|
|
private int lastIconColor;
|
|
|
|
private boolean drawRipple;
|
|
private RippleDrawable rippleDrawable;
|
|
private Paint ripplePaint;
|
|
private int[] pressedState = new int[]{android.R.attr.state_enabled, android.R.attr.state_pressed};
|
|
private int colorSet;
|
|
|
|
private boolean bitmapsCreated;
|
|
private Bitmap[] overlayBitmap;
|
|
private Canvas[] overlayCanvas;
|
|
private Bitmap overlayMaskBitmap;
|
|
private Canvas overlayMaskCanvas;
|
|
private float overlayCx;
|
|
private float overlayCy;
|
|
private float overlayRad;
|
|
private Paint overlayEraserPaint;
|
|
private Paint overlayMaskPaint;
|
|
|
|
private int overrideColorProgress;
|
|
|
|
public interface OnCheckedChangeListener {
|
|
void onCheckedChanged(Switch view, boolean isChecked);
|
|
}
|
|
|
|
public Switch(Context context) {
|
|
super(context);
|
|
rectF = new RectF();
|
|
|
|
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
paint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
paint2.setStyle(Paint.Style.STROKE);
|
|
paint2.setStrokeCap(Paint.Cap.ROUND);
|
|
paint2.setStrokeWidth(AndroidUtilities.dp(2));
|
|
}
|
|
|
|
@Keep
|
|
public void setProgress(float value) {
|
|
if (progress == value) {
|
|
return;
|
|
}
|
|
progress = value;
|
|
invalidate();
|
|
}
|
|
|
|
@Keep
|
|
public float getProgress() {
|
|
return progress;
|
|
}
|
|
|
|
@Keep
|
|
public void setIconProgress(float value) {
|
|
if (iconProgress == value) {
|
|
return;
|
|
}
|
|
iconProgress = value;
|
|
invalidate();
|
|
}
|
|
|
|
@Keep
|
|
public float getIconProgress() {
|
|
return iconProgress;
|
|
}
|
|
|
|
private void cancelCheckAnimator() {
|
|
if (checkAnimator != null) {
|
|
checkAnimator.cancel();
|
|
checkAnimator = null;
|
|
}
|
|
}
|
|
|
|
private void cancelIconAnimator() {
|
|
if (iconAnimator != null) {
|
|
iconAnimator.cancel();
|
|
iconAnimator = null;
|
|
}
|
|
}
|
|
|
|
public void setDrawIconType(int type) {
|
|
drawIconType = type;
|
|
}
|
|
|
|
public void setDrawRipple(boolean value) {
|
|
if (Build.VERSION.SDK_INT < 21 || value == drawRipple) {
|
|
return;
|
|
}
|
|
drawRipple = value;
|
|
|
|
if (rippleDrawable == null) {
|
|
ripplePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
ripplePaint.setColor(0xffffffff);
|
|
Drawable maskDrawable;
|
|
if (Build.VERSION.SDK_INT >= 23) {
|
|
maskDrawable = null;
|
|
} else {
|
|
maskDrawable = new Drawable() {
|
|
@Override
|
|
public void draw(Canvas canvas) {
|
|
android.graphics.Rect bounds = getBounds();
|
|
canvas.drawCircle(bounds.centerX(), bounds.centerY(), AndroidUtilities.dp(18), ripplePaint);
|
|
}
|
|
|
|
@Override
|
|
public void setAlpha(int alpha) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void setColorFilter(ColorFilter colorFilter) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public int getOpacity() {
|
|
return PixelFormat.UNKNOWN;
|
|
}
|
|
};
|
|
}
|
|
ColorStateList colorStateList = new ColorStateList(
|
|
new int[][]{StateSet.WILD_CARD},
|
|
new int[]{0}
|
|
);
|
|
rippleDrawable = new RippleDrawable(colorStateList, null, maskDrawable);
|
|
if (Build.VERSION.SDK_INT >= 23) {
|
|
rippleDrawable.setRadius(AndroidUtilities.dp(18));
|
|
}
|
|
rippleDrawable.setCallback(this);
|
|
}
|
|
if (isChecked && colorSet != 2 || !isChecked && colorSet != 1) {
|
|
int color = isChecked ? Theme.getColor(Theme.key_switchTrackBlueSelectorChecked) : Theme.getColor(Theme.key_switchTrackBlueSelector);
|
|
/*if (Build.VERSION.SDK_INT < 28) {
|
|
color = Color.argb(Color.alpha(color) * 2, Color.red(color), Color.green(color), Color.blue(color));
|
|
}*/
|
|
ColorStateList colorStateList = new ColorStateList(
|
|
new int[][]{StateSet.WILD_CARD},
|
|
new int[]{color}
|
|
);
|
|
rippleDrawable.setColor(colorStateList);
|
|
colorSet = isChecked ? 2 : 1;
|
|
}
|
|
if (Build.VERSION.SDK_INT >= 28 && value) {
|
|
rippleDrawable.setHotspot(isChecked ? 0 : AndroidUtilities.dp(100), AndroidUtilities.dp(18));
|
|
}
|
|
rippleDrawable.setState(value ? pressedState : StateSet.NOTHING);
|
|
invalidate();
|
|
}
|
|
|
|
@Override
|
|
protected boolean verifyDrawable(Drawable who) {
|
|
return super.verifyDrawable(who) || rippleDrawable != null && who == rippleDrawable;
|
|
}
|
|
|
|
public void setColors(String track, String trackChecked, String thumb, String thumbChecked) {
|
|
trackColorKey = track;
|
|
trackCheckedColorKey = trackChecked;
|
|
thumbColorKey = thumb;
|
|
thumbCheckedColorKey = thumbChecked;
|
|
}
|
|
|
|
private void animateToCheckedState(boolean newCheckedState) {
|
|
checkAnimator = ObjectAnimator.ofFloat(this, "progress", newCheckedState ? 1 : 0);
|
|
checkAnimator.setDuration(250);
|
|
checkAnimator.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
checkAnimator = null;
|
|
}
|
|
});
|
|
checkAnimator.start();
|
|
}
|
|
|
|
private void animateIcon(boolean newCheckedState) {
|
|
iconAnimator = ObjectAnimator.ofFloat(this, "iconProgress", newCheckedState ? 1 : 0);
|
|
iconAnimator.setDuration(250);
|
|
iconAnimator.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
iconAnimator = null;
|
|
}
|
|
});
|
|
iconAnimator.start();
|
|
}
|
|
|
|
@Override
|
|
protected void onAttachedToWindow() {
|
|
super.onAttachedToWindow();
|
|
attachedToWindow = true;
|
|
}
|
|
|
|
@Override
|
|
protected void onDetachedFromWindow() {
|
|
super.onDetachedFromWindow();
|
|
attachedToWindow = false;
|
|
}
|
|
|
|
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
|
|
onCheckedChangeListener = listener;
|
|
}
|
|
|
|
public void setChecked(boolean checked, boolean animated) {
|
|
setChecked(checked, drawIconType, animated);
|
|
}
|
|
|
|
public void setChecked(boolean checked, int iconType, boolean animated) {
|
|
if (checked != isChecked) {
|
|
isChecked = checked;
|
|
if (attachedToWindow && animated) {
|
|
animateToCheckedState(checked);
|
|
} else {
|
|
cancelCheckAnimator();
|
|
setProgress(checked ? 1.0f : 0.0f);
|
|
}
|
|
if (onCheckedChangeListener != null) {
|
|
onCheckedChangeListener.onCheckedChanged(this, checked);
|
|
}
|
|
}
|
|
if (drawIconType != iconType) {
|
|
drawIconType = iconType;
|
|
if (attachedToWindow && animated) {
|
|
animateIcon(iconType == 0);
|
|
} else {
|
|
cancelIconAnimator();
|
|
setIconProgress(iconType == 0 ? 1.0f : 0.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setIcon(int icon) {
|
|
if (icon != 0) {
|
|
iconDrawable = getResources().getDrawable(icon).mutate();
|
|
if (iconDrawable != null) {
|
|
iconDrawable.setColorFilter(new PorterDuffColorFilter(lastIconColor = Theme.getColor(isChecked ? trackCheckedColorKey : trackColorKey), PorterDuff.Mode.MULTIPLY));
|
|
}
|
|
} else {
|
|
iconDrawable = null;
|
|
}
|
|
}
|
|
|
|
public boolean hasIcon() {
|
|
return iconDrawable != null;
|
|
}
|
|
|
|
public boolean isChecked() {
|
|
return isChecked;
|
|
}
|
|
|
|
public void setOverrideColor(int override) {
|
|
if (overrideColorProgress == override) {
|
|
return;
|
|
}
|
|
if (overlayBitmap == null) {
|
|
try {
|
|
overlayBitmap = new Bitmap[2];
|
|
overlayCanvas = new Canvas[2];
|
|
for (int a = 0; a < 2; a++) {
|
|
overlayBitmap[a] = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
|
|
overlayCanvas[a] = new Canvas(overlayBitmap[a]);
|
|
}
|
|
overlayMaskBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
|
|
overlayMaskCanvas = new Canvas(overlayMaskBitmap);
|
|
|
|
overlayEraserPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
overlayEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
|
|
|
overlayMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
overlayMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
|
|
bitmapsCreated = true;
|
|
} catch (Throwable e) {
|
|
return;
|
|
}
|
|
}
|
|
if (!bitmapsCreated) {
|
|
return;
|
|
}
|
|
overrideColorProgress = override;
|
|
overlayCx = 0;
|
|
overlayCy = 0;
|
|
overlayRad = 0;
|
|
invalidate();
|
|
}
|
|
|
|
public void setOverrideColorProgress(float cx, float cy, float rad) {
|
|
overlayCx = cx;
|
|
overlayCy = cy;
|
|
overlayRad = rad;
|
|
invalidate();
|
|
}
|
|
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
if (getVisibility() != VISIBLE) {
|
|
return;
|
|
}
|
|
|
|
int width = AndroidUtilities.dp(31);
|
|
int thumb = AndroidUtilities.dp(20);
|
|
int x = (getMeasuredWidth() - width) / 2;
|
|
float y = (getMeasuredHeight() - AndroidUtilities.dpf2(14)) / 2;
|
|
int tx = x + AndroidUtilities.dp(7) + (int) (AndroidUtilities.dp(17) * progress);
|
|
int ty = getMeasuredHeight() / 2;
|
|
|
|
|
|
int color1;
|
|
int color2;
|
|
float colorProgress;
|
|
int r1;
|
|
int r2;
|
|
int g1;
|
|
int g2;
|
|
int b1;
|
|
int b2;
|
|
int a1;
|
|
int a2;
|
|
int red;
|
|
int green;
|
|
int blue;
|
|
int alpha;
|
|
int color;
|
|
|
|
for (int a = 0; a < 2; a++) {
|
|
if (a == 1 && overrideColorProgress == 0) {
|
|
continue;
|
|
}
|
|
Canvas canvasToDraw = a == 0 ? canvas : overlayCanvas[0];
|
|
|
|
if (a == 1) {
|
|
overlayBitmap[0].eraseColor(0);
|
|
paint.setColor(0xff000000);
|
|
overlayMaskCanvas.drawRect(0, 0, overlayMaskBitmap.getWidth(), overlayMaskBitmap.getHeight(), paint);
|
|
overlayMaskCanvas.drawCircle(overlayCx - getX(), overlayCy - getY(), overlayRad, overlayEraserPaint);
|
|
}
|
|
if (overrideColorProgress == 1) {
|
|
colorProgress = a == 0 ? 0 : 1;
|
|
} else if (overrideColorProgress == 2) {
|
|
colorProgress = a == 0 ? 1 : 0;
|
|
} else {
|
|
colorProgress = progress;
|
|
}
|
|
|
|
color1 = Theme.getColor(trackColorKey);
|
|
color2 = Theme.getColor(trackCheckedColorKey);
|
|
if (a == 0 && iconDrawable != null && lastIconColor != (isChecked ? color2 : color1)) {
|
|
iconDrawable.setColorFilter(new PorterDuffColorFilter(lastIconColor = (isChecked ? color2 : color1), PorterDuff.Mode.MULTIPLY));
|
|
}
|
|
|
|
r1 = Color.red(color1);
|
|
r2 = Color.red(color2);
|
|
g1 = Color.green(color1);
|
|
g2 = Color.green(color2);
|
|
b1 = Color.blue(color1);
|
|
b2 = Color.blue(color2);
|
|
a1 = Color.alpha(color1);
|
|
a2 = Color.alpha(color2);
|
|
|
|
red = (int) (r1 + (r2 - r1) * colorProgress);
|
|
green = (int) (g1 + (g2 - g1) * colorProgress);
|
|
blue = (int) (b1 + (b2 - b1) * colorProgress);
|
|
alpha = (int) (a1 + (a2 - a1) * colorProgress);
|
|
color = ((alpha & 0xff) << 24) | ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff);
|
|
paint.setColor(color);
|
|
paint2.setColor(color);
|
|
|
|
rectF.set(x, y, x + width, y + AndroidUtilities.dpf2(14));
|
|
canvasToDraw.drawRoundRect(rectF, AndroidUtilities.dpf2(7), AndroidUtilities.dpf2(7), paint);
|
|
canvasToDraw.drawCircle(tx, ty, AndroidUtilities.dpf2(10), paint);
|
|
|
|
if (a == 0 && rippleDrawable != null) {
|
|
rippleDrawable.setBounds(tx - AndroidUtilities.dp(18), ty - AndroidUtilities.dp(18), tx + AndroidUtilities.dp(18), ty + AndroidUtilities.dp(18));
|
|
rippleDrawable.draw(canvasToDraw);
|
|
} else if (a == 1) {
|
|
canvasToDraw.drawBitmap(overlayMaskBitmap, 0, 0, overlayMaskPaint);
|
|
}
|
|
}
|
|
if (overrideColorProgress != 0) {
|
|
canvas.drawBitmap(overlayBitmap[0], 0, 0, null);
|
|
}
|
|
|
|
for (int a = 0; a < 2; a++) {
|
|
if (a == 1 && overrideColorProgress == 0) {
|
|
continue;
|
|
}
|
|
Canvas canvasToDraw = a == 0 ? canvas : overlayCanvas[1];
|
|
|
|
if (a == 1) {
|
|
overlayBitmap[1].eraseColor(0);
|
|
}
|
|
if (overrideColorProgress == 1) {
|
|
colorProgress = a == 0 ? 0 : 1;
|
|
} else if (overrideColorProgress == 2) {
|
|
colorProgress = a == 0 ? 1 : 0;
|
|
} else {
|
|
colorProgress = progress;
|
|
}
|
|
|
|
color1 = Theme.getColor(thumbColorKey);
|
|
color2 = Theme.getColor(thumbCheckedColorKey);
|
|
r1 = Color.red(color1);
|
|
r2 = Color.red(color2);
|
|
g1 = Color.green(color1);
|
|
g2 = Color.green(color2);
|
|
b1 = Color.blue(color1);
|
|
b2 = Color.blue(color2);
|
|
a1 = Color.alpha(color1);
|
|
a2 = Color.alpha(color2);
|
|
|
|
red = (int) (r1 + (r2 - r1) * colorProgress);
|
|
green = (int) (g1 + (g2 - g1) * colorProgress);
|
|
blue = (int) (b1 + (b2 - b1) * colorProgress);
|
|
alpha = (int) (a1 + (a2 - a1) * colorProgress);
|
|
paint.setColor(((alpha & 0xff) << 24) | ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff));
|
|
|
|
canvasToDraw.drawCircle(tx, ty, AndroidUtilities.dp(8), paint);
|
|
|
|
if (a == 0) {
|
|
if (iconDrawable != null) {
|
|
iconDrawable.setBounds(tx - iconDrawable.getIntrinsicWidth() / 2, ty - iconDrawable.getIntrinsicHeight() / 2, tx + iconDrawable.getIntrinsicWidth() / 2, ty + iconDrawable.getIntrinsicHeight() / 2);
|
|
iconDrawable.draw(canvasToDraw);
|
|
} else if (drawIconType == 1) {
|
|
tx -= AndroidUtilities.dp(10.8f) - AndroidUtilities.dp(1.3f) * progress;
|
|
ty -= AndroidUtilities.dp(8.5f) - AndroidUtilities.dp(0.5f) * progress;
|
|
int startX2 = (int) AndroidUtilities.dpf2(4.6f) + tx;
|
|
int startY2 = (int) (AndroidUtilities.dpf2(9.5f) + ty);
|
|
int endX2 = startX2 + AndroidUtilities.dp(2);
|
|
int endY2 = startY2 + AndroidUtilities.dp(2);
|
|
|
|
int startX = (int) AndroidUtilities.dpf2(7.5f) + tx;
|
|
int startY = (int) AndroidUtilities.dpf2(5.4f) + ty;
|
|
int endX = startX + AndroidUtilities.dp(7);
|
|
int endY = startY + AndroidUtilities.dp(7);
|
|
|
|
startX = (int) (startX + (startX2 - startX) * progress);
|
|
startY = (int) (startY + (startY2 - startY) * progress);
|
|
endX = (int) (endX + (endX2 - endX) * progress);
|
|
endY = (int) (endY + (endY2 - endY) * progress);
|
|
canvasToDraw.drawLine(startX, startY, endX, endY, paint2);
|
|
|
|
startX = (int) AndroidUtilities.dpf2(7.5f) + tx;
|
|
startY = (int) AndroidUtilities.dpf2(12.5f) + ty;
|
|
endX = startX + AndroidUtilities.dp(7);
|
|
endY = startY - AndroidUtilities.dp(7);
|
|
canvasToDraw.drawLine(startX, startY, endX, endY, paint2);
|
|
} else if (drawIconType == 2 || iconAnimator != null) {
|
|
paint2.setAlpha((int) (255 * (1.0f - iconProgress)));
|
|
canvasToDraw.drawLine(tx, ty, tx, ty - AndroidUtilities.dp(5), paint2);
|
|
canvasToDraw.save();
|
|
canvasToDraw.rotate(-90 * iconProgress, tx, ty);
|
|
canvasToDraw.drawLine(tx, ty, tx + AndroidUtilities.dp(4), ty, paint2);
|
|
canvasToDraw.restore();
|
|
}
|
|
}
|
|
if (a == 1) {
|
|
canvasToDraw.drawBitmap(overlayMaskBitmap, 0, 0, overlayMaskPaint);
|
|
}
|
|
}
|
|
if (overrideColorProgress != 0) {
|
|
canvas.drawBitmap(overlayBitmap[1], 0, 0, null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
|
super.onInitializeAccessibilityNodeInfo(info);
|
|
info.setClassName("android.widget.Switch");
|
|
info.setCheckable(true);
|
|
info.setChecked(isChecked);
|
|
//info.setContentDescription(isChecked ? LocaleController.getString("NotificationsOn", R.string.NotificationsOn) : LocaleController.getString("NotificationsOff", R.string.NotificationsOff));
|
|
}
|
|
}
|