from __future__ import annotations

import threading
from typing import Any, List

""" ... """
"""
 _                       
| |__    ____  ____  ____
| '_ \  |_  / |_  / |_  /
| | | |  / /   / /   / / 
|_| |_| /___| /___| /___|
                         
"""
""" ... """

from java import dynamic_proxy, jarray, jclass, jfloat, jint
from java.lang import Integer, Runnable, Thread, Runtime
from java.lang.ref import WeakReference
from java.net import URL

from android.graphics import (
    Bitmap,
    BitmapFactory,
    BitmapShader,
    Canvas,
    Color,
    ColorMatrix,
    ColorMatrixColorFilter,
    LinearGradient,
    Matrix,
    Outline,
    Paint,
    PorterDuff,
    PorterDuffXfermode,
    RadialGradient,
    RectF,
    Shader,
)

from android.graphics.drawable import BitmapDrawable, GradientDrawable
from android.os import Build, Handler, Looper, SystemClock, PowerManager
from android.util import TypedValue
from android.view import Gravity, MotionEvent, View, ViewGroup, ViewOutlineProvider
from android.widget import FrameLayout, ImageView, LinearLayout, SeekBar, TextView, Toast

from org.telegram.messenger import AndroidUtilities, ApplicationLoader
from org.telegram.ui.ActionBar import AlertDialog, Theme
from org.telegram.ui.Components import BackupImageView, LayoutHelper

from android_utils import run_on_ui_thread
from base_plugin import BasePlugin, MethodHook
from client_utils import get_last_fragment
from hook_utils import find_class
from ui.settings import Custom, Header, SimpleSettingFactory, Switch, Text
from ui.alert import AlertDialogBuilder


__id__ = "visual_ecstasy"
__name__ = "Visual Ecstasy"
__description__ = "Различная визуальная кастомизация: разноцветный liquid glass, виньетка и тд.."
__author__ = "@exoseo -> @HzzzPlugins"
__version__ = "1.5"
__icon__ = "HzzzIcons/7"
__app_version__ = ">=12.5.1"
__sdk_version__ = ">=1.3.3.3"


DEFAULT_GLASS_COLOR = "B27AD7"
DEFAULT_GLASS_SAT = 75.0
DEFAULT_VIG_SIZE = 40.0
DEFAULT_VIG_SAT = 35.0
DEFAULT_VIG_SMOOTH = 10.0
DEFAULT_CINE = 25.0
DEFAULT_CYCLE_SPEED = 50.0
CYCLE_HSV_S = 0.86
CYCLE_HSV_V = 0.94

PREVIEW_NAME = "h z z z"
PREVIEW_STATUS = "Был(-а) хз когда"
PREVIEW_AVATAR_URL = "https://t.me/i/userpic/320/exoseo.jpg"
PREVIEW_GRADIENT = (
    0xFF2B0B8A,
    0xFF310969,
    0xFF52236B,
    0xFF1A0B32
)

OVERLAY_TAG = "visual_ecstasy.overlay.v3"
ANDROID_CONTENT_ID = 0x01020002
APPLY_DELAY_MS = 48
_UI_HANDLER = Handler(Looper.getMainLooper())

SKIP_ALWAYS = (
    "camera",
    "cameraview",
    "qr"
)

SKIP_SETTINGS = ("--setting", "--preference", "--bottomsheet")


def _signed(argb):
    argb = int(argb) & 0xFFFFFFFF
    if argb >= 0x80000000:
        return argb - 0x100000000
    return argb


def _u32(argb):
    return int(argb) & 0xFFFFFFFF


def _jcolor(argb):
    return _signed(argb)


def _jfloats(values):
    arr = jarray(jfloat)(len(values))
    for i, v in enumerate(values):
        arr[i] = float(v)
    return arr


def _jints(values):
    arr = jarray(jint)(len(values))
    for i, v in enumerate(values):
        arr[i] = _jcolor(int(v))
    return arr


def _argb(a, r, g, b):
    return _jcolor(
        ((int(a) & 255) << 24)
        | ((int(r) & 255) << 16)
        | ((int(g) & 255) << 8)
        | (int(b) & 255)
    )


def _clamp(v, lo, hi):
    return lo if v < lo else hi if v > hi else v


def _lerp(a, b, t):
    return int(a + (b - a) * t)


def _blend(original, light, t):
    if t <= 0.0:
        return _u32(original)
    if t >= 1.0:
        return _u32(light)
    o = _u32(original)
    l = _u32(light)
    oa, or_, og, ob = (o >> 24) & 255, (o >> 16) & 255, (o >> 8) & 255, o & 255
    la, lr, lg, lb = (l >> 24) & 255, (l >> 16) & 255, (l >> 8) & 255, l & 255
    a = _clamp(_lerp(oa, la, t), 0, 255)
    r = _clamp(_lerp(or_, lr, t), 0, 255)
    g = _clamp(_lerp(og, lg, t), 0, 255)
    b = _clamp(_lerp(ob, lb, t), 0, 255)
    return ((a << 24) | (r << 16) | (g << 8) | b) & 0xFFFFFFFF


def _to_int(val):
    try:
        if val is None:
            return None
        if isinstance(val, Integer):
            return int(val.intValue()) & 0xFFFFFFFF
        if isinstance(val, int):
            return val & 0xFFFFFFFF
        return int(val) & 0xFFFFFFFF
    except Exception:
        return None


def _parse_hex(raw, fallback=DEFAULT_GLASS_COLOR):
    try:
        s = str(raw or fallback).strip().replace("#", "").upper()
        if len(s) == 3:
            s = "".join(ch * 2 for ch in s)
        if len(s) >= 8:
            s = s[-6:]
        if len(s) != 6:
            s = fallback
        n = int(s, 16)
        return s, (n >> 16) & 255, (n >> 8) & 255, n & 255
    except Exception:
        n = int(fallback, 16)
        return fallback, (n >> 16) & 255, (n >> 8) & 255, n & 255


def _cycle_period_sec(speed):
    s = _clamp(float(speed), 1.0, 100.0)
    if s <= 50.0:
        t = (s - 1.0) / 49.0
        return 90.0 + (30.0 - 90.0) * t
    t = (s - 50.0) / 50.0
    return 30.0 + (4.0 - 30.0) * t


def _cycle_speed_label(speed):
    sec = _cycle_period_sec(speed)
    if sec >= 10.0:
        return "%dс" % int(round(sec))
    return "%.1fс" % sec


def _as_bool(val, default=False):
    if val is None:
        return bool(default)
    if isinstance(val, bool):
        return val
    try:
        if hasattr(val, "booleanValue"):
            return bool(val.booleanValue())
    except Exception:
        pass
    s = str(val).strip().lower()
    if s in ("1", "true", "yes", "on"):
        return True
    if s in ("0", "false", "no", "off", "", "none"):
        return False
    try:
        return bool(val)
    except Exception:
        return bool(default)


def _class_name(obj):
    try:
        return str(obj.getClass().getName())
    except Exception:
        return ""


def _mlp(w, h, l=0, t=0, r=0, b=0, weight=0.0):
    if weight:
        lp = LinearLayout.LayoutParams(int(w), int(h), float(weight))
    else:
        lp = LinearLayout.LayoutParams(int(w), int(h))
    lp.leftMargin = int(l)
    lp.topMargin = int(t)
    lp.rightMargin = int(r)
    lp.bottomMargin = int(b)
    return lp


class _UiRunnable(dynamic_proxy(Runnable)):
    def __init__(self, fn):
        super(_UiRunnable, self).__init__()
        self._fn = fn

    def run(self):
        fn = self._fn
        if fn is not None:
            try:
                fn()
            except Exception:
                pass


class _PassTouch(dynamic_proxy(jclass("android.view.View$OnTouchListener"))):
    def onTouch(self, v, event):
        return False


class LiquidGlassUpdateHook(MethodHook):
    def __init__(self, plugin):
        self.p = plugin

    def before_hooked_method(self, param):
        try:
            p = self.p
            if p._force_updating:
                return

            this_obj = param.thisObject
            method = param.method
            args = param.args
            if not args:
                return

            idx = len(args) - 1
            orig = _to_int(args[idx])
            if orig is None:
                return

            if this_obj is not None and method is not None:
                p._register_glass_updater(this_obj, method, args, orig, idx)

            if not p.glass_on:
                return

            param.args[idx] = Integer(_signed(p.mix_glass_fg(orig)))
        except Exception:
            pass


class SetLensColorHook(MethodHook):
    def __init__(self, plugin):
        self.p = plugin

    def before_hooked_method(self, param):
        try:
            p = self.p
            if p._applying_lens:
                return

            args = param.args
            if not args or len(args) < 2:
                return

            bg = _to_int(args[0])
            fg = _to_int(args[1])
            p._remember_lens(param.thisObject, bg, fg)

            if not p.glass_on:
                return
            if bg is not None:
                param.args[0] = Integer(_signed(p.mix_glass_bg(bg)))
            if fg is not None:
                param.args[1] = Integer(_signed(p.mix_glass_fg(fg)))
        except Exception:
            pass


class ActivityAttachHook(MethodHook):
    def __init__(self, plugin):
        self.plugin = plugin

    def after_hooked_method(self, param):
        try:
            self.plugin.attach_activity(param.thisObject)
            self.plugin._scan_tree_for_glass(param.thisObject)
        except Exception:
            pass


class ActivityDetachHook(MethodHook):
    def __init__(self, plugin):
        self.plugin = plugin

    def after_hooked_method(self, param):
        try:
            self.plugin.detach_activity(param.thisObject)
        except Exception:
            pass


class FragmentResumeHook(MethodHook):
    def __init__(self, plugin):
        self.plugin = plugin

    def after_hooked_method(self, param):
        try:
            frag = param.thisObject
            act = None
            try:
                act = frag.getParentActivity()
            except Exception:
                act = None
            if self.plugin._skip_obj(frag) or self.plugin._settings_surface(frag):
                if act is not None:
                    self.plugin.detach_activity(act)
                return
            if act is not None:
                self.plugin.attach_activity(act)
                self.plugin._scan_tree_for_glass(act)
        except Exception:
            pass


class VisualEcstasyPlugin(BasePlugin):
    def __init__(self):
        super(VisualEcstasyPlugin, self).__init__()
        self.unhook_list = []
        self._lock = threading.RLock()
        self._attached = {}
        self._overlay_bmp = None
        self._overlay_key = None
        self._preview_bmp = None
        self._preview_key = None
        self._retiring = []
        self._lifecycle = None
        self._factories = []
        self._slider_refs = []
        self._preview_hosts = []
        self._layout_listeners = []
        self._layout_bound = set()
        self._picker_refs = []
        self._touch_listener = None
        self._apply_runnable = None
        self._retire_runnable = None
        self._color_sig = {}
        self._grade_broken = False
        self._seekbar_view_cls = None
        self._seekbar_delegate_cls = None
        self._avatar_bmp = None
        self._avatar_loading = False
        self._round_outlines = []
        self.glass_on = True
        self.glass_t = 0.5
        self.glass_fg = 0x66C9B6E8
        self.glass_bg = 0x33C9B6E8
        self.vig_size = DEFAULT_VIG_SIZE
        self.vig_sat = DEFAULT_VIG_SAT
        self.vig_smooth = DEFAULT_VIG_SMOOTH
        self.cine_t = DEFAULT_CINE / 100.0
        self.grade_on = True
        self._welcome_shown = False
        self.cycle_on = False
        self.cycle_speed = DEFAULT_CYCLE_SPEED
        self._cycle_hue = 0.0
        self._cycle_last_ms = 0
        self._cycle_runnable = None
        self._cycle_running = False
        self._applying_lens = False
        self._force_updating = False
        self._lens_views = {}
        self._glass_updaters = {}
        self._cycle_hsv_arr = None

        self._hw_tier = 1
        self._cycle_tick_ms = 75
        self._vignette_max_edge = 280
        self._allow_rendereffect = True
        self._app_in_foreground = True

    def _probe_hardware_capabilities(self):
        try:
            ctx = ApplicationLoader.applicationContext
            am = ctx.getSystemService(jclass("android.content.Context").ACTIVITY_SERVICE)
            is_low_ram = False
            total_ram_gb = 4.0
            try:
                is_low_ram = bool(am.isLowRamDevice())
            except Exception:
                pass
            try:
                mem_info = jclass("android.app.ActivityManager$MemoryInfo")()
                am.getMemoryInfo(mem_info)
                total_ram_gb = float(mem_info.totalMem) / (1024.0 * 1024.0 * 1024.0)
            except Exception:
                pass

            cores = 4
            try:
                cores = int(Runtime.getRuntime().availableProcessors())
            except Exception:
                pass

            sdk = int(Build.VERSION.SDK_INT)

            if is_low_ram or total_ram_gb < 3.5 or cores <= 4 or sdk < 29:
                self._hw_tier = 0
                self._cycle_tick_ms = 380
                self._vignette_max_edge = 160
                self._allow_rendereffect = False
            elif total_ram_gb < 6.0 or cores < 8:
                self._hw_tier = 1
                self._cycle_tick_ms = 75
                self._vignette_max_edge = 280
                self._allow_rendereffect = (sdk >= 31)
            else:
                self._hw_tier = 2
                self._cycle_tick_ms = 32
                self._vignette_max_edge = 420
                self._allow_rendereffect = (sdk >= 31)
        except Exception:
            self._hw_tier = 1
            self._cycle_tick_ms = 75
            self._vignette_max_edge = 280
            self._allow_rendereffect = (int(Build.VERSION.SDK_INT) >= 31)

    def on_plugin_load(self):
        try:
            self._touch_listener = _PassTouch()
        except Exception:
            self._touch_listener = None
        self._probe_hardware_capabilities()
        self._resolve_seekbar()
        self._reload_cache()
        self._install_hooks()
        self._register_lifecycle()
        self._prefetch_avatar()
        self._schedule_apply(0)
        if self.cycle_on:
            self._ensure_cycle()

    def on_plugin_unload(self):
        try:
            self._unregister_lifecycle()
        except Exception:
            pass
        try:
            if self._apply_runnable is not None:
                _UI_HANDLER.removeCallbacks(self._apply_runnable)
        except Exception:
            pass
        try:
            self._stop_cycle()
        except Exception:
            pass
        for unhook in list(self.unhook_list):
            try:
                unhook.unhook()
            except Exception:
                try:
                    self.unhook_method(unhook)
                except Exception:
                    pass
        self.unhook_list = []
        run_on_ui_thread(self._detach_all)
        self._factories = []
        self._slider_refs = []
        self._preview_hosts = []
        self._picker_refs = []
        self._layout_listeners = []
        self._layout_bound = set()
        self._round_outlines = []
        self._lens_views = {}
        self._glass_updaters = {}

    def _resolve_seekbar(self):
        try:
            self._seekbar_view_cls = find_class("org.telegram.ui.Components.SeekBarView")
            self._seekbar_delegate_cls = find_class(
                "org.telegram.ui.Components.SeekBarView$SeekBarViewDelegate"
            )
        except Exception:
            self._seekbar_view_cls = None
            self._seekbar_delegate_cls = None

    def _reload_cache(self):
        try:
            sat = self._f("glass_sat", DEFAULT_GLASS_SAT)
            self.glass_t = _clamp((sat - 50.0) / 50.0, -1.0, 1.0)
            self.cycle_on = self._b("glass_cycle", False)
            self.cycle_speed = self._f("glass_cycle_speed", DEFAULT_CYCLE_SPEED, 1.0, 100.0)
            self.vig_size = self._f("vig_size", DEFAULT_VIG_SIZE)
            self.vig_sat = self._f("vig_sat", DEFAULT_VIG_SAT)
            self.vig_smooth = self._f("vig_smooth", DEFAULT_VIG_SMOOTH)
            self.cine_t = self._f("cine", DEFAULT_CINE) / 100.0
            self.glass_on = self.glass_t > 0.004
            self.grade_on = self.vig_sat > 0.4 or self.cine_t > 0.004
            if self.cycle_on:
                self._apply_cycle_rgb()
                self._ensure_cycle()
            else:
                hex_s, r, g, b = _parse_hex(
                    self.get_setting("glass_color", DEFAULT_GLASS_COLOR), DEFAULT_GLASS_COLOR
                )
                self.glass_fg = (0x66 << 24) | (r << 16) | (g << 8) | b
                self.glass_bg = (0x33 << 24) | (r << 16) | (g << 8) | b
                self._stop_cycle()
        except Exception:
            self.glass_on = True
            self.glass_t = 0.5
            self.grade_on = True

    def _f(self, key, default, lo=0.0, hi=100.0):
        try:
            v = float(self.get_setting(key, default))
        except Exception:
            v = float(default)
        return _clamp(v, lo, hi)

    def _b(self, key, default=False):
        try:
            return _as_bool(self.get_setting(key, default), default)
        except Exception:
            return bool(default)

    def mix_glass_fg(self, original):
        t = self.glass_t
        if t <= 0.0:
            return _u32(original)
        return _blend(original, self.glass_fg, t)

    def mix_glass_bg(self, original):
        t = self.glass_t
        if t <= 0.0:
            return _u32(original)
        return _blend(original, self.glass_bg, t)

    def _now_ms(self):
        try:
            return int(SystemClock.elapsedRealtime())
        except Exception:
            import time
            return int(time.time() * 1000.0)

    def _is_device_interactive(self):
        try:
            ctx = ApplicationLoader.applicationContext
            pm = ctx.getSystemService(jclass("android.content.Context").POWER_SERVICE)
            if pm is not None:
                if not bool(pm.isInteractive()):
                    return False
                if bool(pm.isPowerSaveMode()) and self._cycle_tick_ms < 350:
                    self._cycle_tick_ms = 380
        except Exception:
            pass
        return True

    def _seed_hue_from_color(self):
        try:
            _, r, g, b = _parse_hex(
                self.get_setting("glass_color", DEFAULT_GLASS_COLOR), DEFAULT_GLASS_COLOR
            )
            hsv = jarray(jfloat)(3)
            Color.colorToHSV(_argb(255, r, g, b), hsv)
            self._cycle_hue = float(hsv[0]) % 360.0
        except Exception:
            pass

    def _apply_cycle_rgb(self):
        try:
            arr = self._cycle_hsv_arr
            if arr is None:
                arr = _jfloats([0.0, float(CYCLE_HSV_S), float(CYCLE_HSV_V)])
                self._cycle_hsv_arr = arr
            arr[0] = float(self._cycle_hue % 360.0)
            c = int(Color.HSVToColor(arr)) & 0xFFFFFFFF
            r, g, b = (c >> 16) & 255, (c >> 8) & 255, c & 255
            self.glass_fg = (0x66 << 24) | (r << 16) | (g << 8) | b
            self.glass_bg = (0x33 << 24) | (r << 16) | (g << 8) | b
        except Exception:
            try:
                hsv = _jfloats([
                    float(self._cycle_hue % 360.0),
                    float(CYCLE_HSV_S),
                    float(CYCLE_HSV_V),
                ])
                c = int(Color.HSVToColor(hsv)) & 0xFFFFFFFF
                r, g, b = (c >> 16) & 255, (c >> 8) & 255, c & 255
                self.glass_fg = (0x66 << 24) | (r << 16) | (g << 8) | b
                self.glass_bg = (0x33 << 24) | (r << 16) | (g << 8) | b
            except Exception:
                pass

    def _find_views_in_object(self, obj):
        views = []
        if obj is None:
            return views
        try:
            cls = obj.getClass()
            curr = cls
            count = 0
            while curr is not None and curr.getName() != "java.lang.Object" and count < 3:
                count += 1
                for f in curr.getDeclaredFields():
                    try:
                        f.setAccessible(True)
                        val = f.get(obj)
                        if val is None:
                            continue
                        if isinstance(val, View):
                            views.append(val)
                        elif hasattr(val, "getCallback"):
                            cb = val.getCallback()
                            if isinstance(cb, View):
                                views.append(cb)
                    except Exception:
                        pass
                curr = curr.getSuperclass()
        except Exception:
            pass
        return views

    def _register_glass_updater(self, obj, method, args, orig_color, color_idx, extra_views=None):
        try:
            key = int(obj.hashCode())
        except Exception:
            key = id(obj)

        try:
            saved_args = list(args)
        except Exception:
            saved_args = [a for a in args]

        found_views = self._find_views_in_object(obj)
        if extra_views:
            found_views.extend(extra_views)

        with self._lock:
            try:
                ref = WeakReference(obj)
            except Exception:
                ref = obj
            self._glass_updaters[key] = {
                "ref": ref,
                "method": method,
                "args": saved_args,
                "orig_color": orig_color,
                "color_idx": color_idx,
                "views": [WeakReference(v) for v in found_views if v is not None],
            }

    def _scan_tree_for_glass(self, root_or_act):
        try:
            if root_or_act is None:
                return
            decor = None
            if hasattr(root_or_act, "getWindow"):
                w = root_or_act.getWindow()
                if w is not None:
                    decor = w.getDecorView()
            elif isinstance(root_or_act, View):
                decor = root_or_act
            if decor is not None:
                self._scan_view_recursive(decor, 0)
        except Exception:
            pass

    def _scan_view_recursive(self, view, depth):
        if view is None or depth > 7:
            return
        try:
            vname = _class_name(view)
            if "Glass" in vname or "Tabs" in vname or "Lens" in vname:
                key = int(view.hashCode())
                if key not in self._lens_views and hasattr(view, "setLensColor"):
                    self._remember_lens(view, 0x33C9B6E8, 0x66C9B6E8)

            cls = view.getClass()
            for f in cls.getDeclaredFields():
                try:
                    ftype = f.getType().getName()
                    if "LiquidGlass" in ftype or "GlassEffect" in ftype:
                        f.setAccessible(True)
                        effect = f.get(view)
                        if effect is not None:
                            self._extract_and_register_effect(effect, view)
                except Exception:
                    pass

            if isinstance(view, ViewGroup):
                n = view.getChildCount()
                for i in range(n):
                    self._scan_view_recursive(view.getChildAt(i), depth + 1)
        except Exception:
            pass

    def _extract_and_register_effect(self, effect, parent_view):
        try:
            key = int(effect.hashCode())
        except Exception:
            key = id(effect)
        if key in self._glass_updaters:
            return

        cls = effect.getClass()
        target_m = None
        for m in cls.getDeclaredMethods():
            mname = m.getName()
            if mname in ("update", "render", "setTint", "setColor"):
                pts = m.getParameterTypes()
                if len(pts) > 0 and pts[len(pts) - 1].getName() in ("int", "java.lang.Integer"):
                    target_m = m
                    break

        if target_m is not None:
            pts = target_m.getParameterTypes()
            dummy_args = []
            for ptype in pts:
                pname = ptype.getName()
                if pname in ("int", "java.lang.Integer"):
                    dummy_args.append(Integer(_signed(0x66C9B6E8)))
                elif pname in ("float", "java.lang.Float"):
                    dummy_args.append(jfloat(0.0))
                else:
                    dummy_args.append(None)
            self._register_glass_updater(
                effect,
                target_m,
                dummy_args,
                0x66C9B6E8,
                len(pts) - 1,
                [parent_view] if parent_view is not None else [],
            )

    def _remember_lens(self, view, bg, fg):
        if view is None:
            return
        try:
            key = int(view.hashCode())
        except Exception:
            return
        try:
            ref = WeakReference(view)
        except Exception:
            ref = view
        self._lens_views[key] = (ref, _to_int(bg), _to_int(fg))

    def _push_lens_colors(self):
        if not self._lens_views:
            return
        self._applying_lens = True
        alive = {}
        try:
            items = list(self._lens_views.items())
            if len(items) > 16:
                items = items[-16:]
            for key, packed in items:
                ref, orig_bg, orig_fg = packed
                view = None
                try:
                    view = ref.get() if hasattr(ref, "get") else ref
                except Exception:
                    view = None
                if view is None:
                    continue
                try:
                    if orig_bg is not None and orig_fg is not None:
                        view.setLensColor(
                            Integer(_signed(self.mix_glass_bg(orig_bg))),
                            Integer(_signed(self.mix_glass_fg(orig_fg))),
                        )
                    try:
                        view.postInvalidateOnAnimation()
                    except Exception:
                        view.invalidate()
                    alive[key] = packed
                except Exception:
                    pass
            self._lens_views = alive
        finally:
            self._applying_lens = False

    def _push_glass_updates(self):
        if not self.glass_on:
            return

        self._push_lens_colors()

        if self._glass_updaters:
            self._force_updating = True
            alive_updaters = {}
            try:
                with self._lock:
                    items = list(self._glass_updaters.items())

                for key, data in items:
                    ref = data["ref"]
                    obj = ref.get() if hasattr(ref, "get") else ref
                    if obj is None:
                        continue

                    method = data["method"]
                    saved_args = data["args"]
                    orig_color = data["orig_color"]
                    idx = data["color_idx"]

                    new_mixed = self.mix_glass_fg(orig_color)
                    saved_args[idx] = Integer(_signed(new_mixed))

                    try:
                        method.setAccessible(True)
                        arr = jarray(jclass("java.lang.Object"))(len(saved_args))
                        for i, a in enumerate(saved_args):
                            arr[i] = a
                        method.invoke(obj, arr)
                        alive_updaters[key] = data
                    except Exception:
                        try:
                            method.invoke(obj, *saved_args)
                            alive_updaters[key] = data
                        except Exception:
                            pass

                    for vref in data.get("views", []):
                        try:
                            v = vref.get() if hasattr(vref, "get") else vref
                            if v is not None:
                                v.postInvalidateOnAnimation()
                        except Exception:
                            pass

                with self._lock:
                    self._glass_updaters = alive_updaters
            finally:
                self._force_updating = False

        self._invalidate_all_surfaces()

    def _invalidate_all_surfaces(self):
        try:
            frag = get_last_fragment()
            if frag is not None:
                fv = frag.fragmentView
                if fv is not None:
                    fv.postInvalidateOnAnimation()
        except Exception:
            pass

        with self._lock:
            attached_items = list(self._attached.values())
        for root, _, _ in attached_items:
            try:
                root.postInvalidateOnAnimation()
            except Exception:
                try:
                    root.invalidate()
                except Exception:
                    pass

    def _ensure_cycle(self):
        if not self.cycle_on:
            self._stop_cycle()
            return
        if self._cycle_running:
            return
        if not self._cycle_last_ms:
            self._seed_hue_from_color()
        self._cycle_running = True
        self._cycle_last_ms = self._now_ms()
        self._apply_cycle_rgb()
        self._schedule_cycle()

    def _stop_cycle(self):
        self._cycle_running = False
        self._cycle_last_ms = 0
        try:
            if self._cycle_runnable is not None:
                _UI_HANDLER.removeCallbacks(self._cycle_runnable)
        except Exception:
            pass
        self._cycle_runnable = None

    def _schedule_cycle(self):
        if not self.cycle_on or not self._cycle_running:
            self._cycle_running = False
            return
        try:
            if self._cycle_runnable is not None:
                _UI_HANDLER.removeCallbacks(self._cycle_runnable)
        except Exception:
            pass

        def go():
            try:
                self._cycle_tick()
            except Exception:
                self._cycle_running = False

        self._cycle_runnable = _UiRunnable(go)
        try:
            _UI_HANDLER.postDelayed(self._cycle_runnable, int(self._cycle_tick_ms))
        except Exception:
            run_on_ui_thread(go)

    def _cycle_tick(self):
        if not self.cycle_on:
            self._stop_cycle()
            return

        if not self._app_in_foreground or not self._is_device_interactive():
            self._schedule_cycle()
            return

        now = self._now_ms()
        last = self._cycle_last_ms or now
        dt = now - last
        if dt < 0:
            dt = 0
        if dt > 500:
            dt = 500
        self._cycle_last_ms = now
        period = _cycle_period_sec(self.cycle_speed) * 1000.0
        if period < 2500.0:
            period = 2500.0
        self._cycle_hue = (self._cycle_hue + 360.0 * (float(dt) / period)) % 360.0
        self._apply_cycle_rgb()

        self._push_glass_updates()
        self._paint_preview_pills()
        self._schedule_cycle()

    def _on_cycle_change(self, new_value):
        enabled = _as_bool(new_value, False)
        self.cycle_on = enabled
        if enabled:
            self._seed_hue_from_color()
            self._apply_cycle_rgb()
            self._ensure_cycle()
        else:
            self._stop_cycle()
        try:
            self.set_setting("glass_cycle", enabled, reload_settings=True)
        except TypeError:
            try:
                self.set_setting("glass_cycle", enabled)
            except Exception:
                pass
        except Exception:
            try:
                self.set_setting("glass_cycle", enabled)
            except Exception:
                pass
        try:
            self._reload_cache()
        except Exception:
            pass
        self._schedule_apply(0)

    def _hook_all(self, clazz, name, hook):
        if not clazz:
            return
        try:
            hooks = self.hook_all_methods(clazz, name, hook)
            if hooks:
                self.unhook_list.extend(hooks)
        except Exception:
            pass

    def _install_hooks(self):
        glass_update_hook = LiquidGlassUpdateHook(self)
        lens_color_hook = SetLensColorHook(self)

        liquid_glass_classes = [
            "org.telegram.ui.Components.blur3.LiquidGlassEffect",
            "org.telegram.ui.Components.blur.LiquidGlassEffect",
            "org.telegram.ui.Components.glass.LiquidGlassEffect",
            "org.telegram.ui.ActionBar.LiquidGlassEffect",
            "org.telegram.ui.Components.LiquidGlassView",
            "org.telegram.ui.Components.blur3.LiquidGlassView",
        ]
        for cname in liquid_glass_classes:
            cls = find_class(cname)
            if cls:
                self._hook_all(cls, "update", glass_update_hook)
                self._hook_all(cls, "render", glass_update_hook)
                self._hook_all(cls, "setGlassColor", glass_update_hook)
                self._hook_all(cls, "setTint", glass_update_hook)

        lens_classes = [
            "org.telegram.ui.Components.glass.GlassTabsView",
            "org.telegram.ui.Components.GlassTabsView",
            "org.telegram.ui.Components.FilterTabsView",
            "org.telegram.ui.Components.glass.LiquidLensView",
        ]
        for cname in lens_classes:
            cls = find_class(cname)
            if cls:
                self._hook_all(cls, "setLensColor", lens_color_hook)
                self._hook_all(cls, "setGlassColor", lens_color_hook)
                self._hook_all(cls, "updateColors", lens_color_hook)

        attach = ActivityAttachHook(self)
        detach = ActivityDetachHook(self)
        Activity = find_class("android.app.Activity")
        self._hook_all(Activity, "onResume", attach)
        self._hook_all(Activity, "onStart", attach)
        self._hook_all(Activity, "onDestroy", detach)
        self._hook_all(find_class("org.telegram.ui.LaunchActivity"), "onResume", attach)

        frag = find_class("org.telegram.ui.ActionBar.BaseFragment")
        resume = FragmentResumeHook(self)
        self._hook_all(frag, "onResume", resume)
        self._hook_all(frag, "onBecomeFullyVisible", resume)

    def _register_lifecycle(self):
        try:
            Application = jclass("android.app.Application")
            Callbacks = jclass("android.app.Application$ActivityLifecycleCallbacks")
            plugin = self

            class Life(dynamic_proxy(Callbacks)):
                def onActivityCreated(self, activity, saved):
                    plugin.attach_activity(activity)

                def onActivityStarted(self, activity):
                    plugin._app_in_foreground = True
                    plugin.attach_activity(activity)
                    plugin._scan_tree_for_glass(activity)

                def onActivityResumed(self, activity):
                    plugin._app_in_foreground = True
                    plugin.attach_activity(activity)
                    plugin._scan_tree_for_glass(activity)
                    if plugin.cycle_on:
                        plugin._ensure_cycle()

                def onActivityPaused(self, activity):
                    return None

                def onActivityStopped(self, activity):
                    plugin._app_in_foreground = False
                    return None

                def onActivitySaveInstanceState(self, activity, out):
                    return None

                def onActivityDestroyed(self, activity):
                    plugin.detach_activity(activity)

            self._lifecycle = Life()
            ctx = ApplicationLoader.applicationContext
            app = ctx if isinstance(ctx, Application) else ctx.getApplicationContext()
            app.registerActivityLifecycleCallbacks(self._lifecycle)
        except Exception:
            pass

    def _unregister_lifecycle(self):
        if self._lifecycle is None:
            return
        try:
            Application = jclass("android.app.Application")
            ctx = ApplicationLoader.applicationContext
            app = ctx if isinstance(ctx, Application) else ctx.getApplicationContext()
            app.unregisterActivityLifecycleCallbacks(self._lifecycle)
        except Exception:
            pass
        self._lifecycle = None

    def _skip_obj(self, obj):
        name = _class_name(obj).lower()
        if not name:
            return False
        for token in SKIP_ALWAYS:
            if token in name:
                return True
        return False

    def _settings_surface(self, obj):
        name = _class_name(obj).lower()
        if not name:
            return False
        for token in SKIP_SETTINGS:
            if token in name:
                return True
        return False

    def _active_should_skip(self, activity=None):
        if activity is not None and self._skip_obj(activity):
            return True
        try:
            frag = get_last_fragment()
            if frag is None:
                return False
            if self._skip_obj(frag) or self._settings_surface(frag):
                return True
        except Exception:
            pass
        return False

    def _contrast_f(self):
        return 1.0 + self.cine_t * 0.32

    def _brightness_f(self):
        return -self.cine_t * 0.06

    def _saturation_f(self):
        return 1.0 + self.cine_t * 0.22

    def _warmth_f(self):
        return self.cine_t * 0.28

    def _fade_f(self):
        return self.cine_t * 0.10

    def _identity_grade(self):
        return self.cine_t < 0.004

    def _matrix_sig(self):
        return (
            round(self._contrast_f(), 3),
            round(self._brightness_f(), 3),
            round(self._saturation_f(), 3),
            round(self._warmth_f(), 3),
            round(self._fade_f(), 3),
            1 if self.grade_on else 0,
        )

    def _build_color_matrix(self):
        cm = ColorMatrix()
        sat = ColorMatrix()
        sat.setSaturation(float(self._saturation_f()))
        cm.postConcat(sat)
        c = float(self._contrast_f())
        b = float(self._brightness_f()) * 255.0
        t = (1.0 - c) * 127.5 + b
        contrast = ColorMatrix(
            _jfloats(
                [
                    c, 0, 0, 0, t,
                    0, c, 0, 0, t,
                    0, 0, c, 0, t,
                    0, 0, 0, 1, 0,
                ]
            )
        )
        cm.postConcat(contrast)
        w = float(self._warmth_f())
        rs = 1.0 + w * 0.10
        gs = 1.0 + w * 0.02
        bs = 1.0 - w * 0.12
        warmth = ColorMatrix(
            _jfloats(
                [
                    rs, 0, 0, 0, 0,
                    0, gs, 0, 0, 0,
                    0, 0, bs, 0, 0,
                    0, 0, 0, 1, 0,
                ]
            )
        )
        cm.postConcat(warmth)
        f = float(self._fade_f())
        if f > 0.001:
            o = 128.0 * f
            k = 1.0 - f
            fade = ColorMatrix(
                _jfloats(
                    [
                        k, 0, 0, 0, o,
                        0, k, 0, 0, o,
                        0, 0, k, 0, o,
                        0, 0, 0, 1, 0,
                    ]
                )
            )
            cm.postConcat(fade)
        return cm

    def _color_filter(self):
        return ColorMatrixColorFilter(self._build_color_matrix())

    def _vignette_geom(self, w, h, density):
        size = self.vig_size / 100.0
        sat = self.vig_sat / 100.0
        smooth = self.vig_smooth / 100.0
        density = density if density > 0.1 else 1.0
        shortest = min(w, h) / density
        tablet = shortest >= 600
        landscape = w > h
        inner = _clamp(0.90 - size * 0.52 + smooth * 0.10, 0.18, 0.92)
        edge_a = _clamp(sat * 0.58, 0.0, 0.72)
        if tablet:
            inner = min(0.93, inner + 0.08)
            edge_a *= 0.78
        if landscape and not tablet:
            inner = max(0.20, inner - 0.04)
            edge_a *= 1.04
        mid_a = edge_a * (0.22 + (1.0 - smooth) * 0.25)
        return inner, mid_a, edge_a, smooth

    def _retire(self, bmp):
        if bmp is None:
            return
        self._retiring.append(bmp)
        if self._retire_runnable is not None:
            try:
                _UI_HANDLER.removeCallbacks(self._retire_runnable)
            except Exception:
                pass

        def flush():
            dead = self._retiring
            self._retiring = []
            for item in dead:
                try:
                    if item is not None and not item.isRecycled():
                        item.recycle()
                except Exception:
                    pass

        self._retire_runnable = _UiRunnable(flush)
        _UI_HANDLER.postDelayed(self._retire_runnable, 400)

    def _soften(self, bmp):
        if self._hw_tier == 0:
            return bmp
        try:
            w = int(bmp.getWidth())
            h = int(bmp.getHeight())
            tw = max(18, w // 5)
            th = max(18, h // 5)
            tiny = Bitmap.createScaledBitmap(bmp, tw, th, True)
            out = Bitmap.createScaledBitmap(tiny, w, h, True)
            if tiny is not bmp and tiny is not out:
                try:
                    tiny.recycle()
                except Exception:
                    pass
            return out
        except Exception:
            return bmp

    def _draw_vignette(self, bw, bh, inner, mid_a, edge_a, smooth):
        bmp = Bitmap.createBitmap(bw, bh, Bitmap.Config.ARGB_8888)
        canvas = Canvas(bmp)
        canvas.drawColor(_jcolor(0x00000000))
        if edge_a < 0.012:
            return bmp
        cx = bw * 0.5
        cy = bh * 0.42
        radius = max(bw, bh) * 0.62
        colors = _jints(
            [
                0x00000000,
                0x00000000,
                _argb(int(mid_a * 0.45 * 255.0), 0, 0, 0),
                _argb(int(mid_a * 255.0), 0, 0, 0),
                _argb(int(edge_a * 255.0), 0, 0, 0),
            ]
        )
        spread = 0.10 + smooth * 0.16
        mid_stop = float(min(0.88, inner + 0.12 + (1.0 - inner) * 0.20))
        stops = _jfloats([0.0, float(inner), float(min(0.84, inner + spread)), mid_stop, 1.0])
        shader = RadialGradient(0.0, 0.0, float(radius), colors, stops, Shader.TileMode.CLAMP)
        matrix = Matrix()
        sx = (bw * 0.5) / radius * 1.08
        sy = (bh * 0.5) / radius * 1.08
        matrix.postScale(float(sx), float(sy))
        matrix.postTranslate(float(cx), float(cy))
        shader.setLocalMatrix(matrix)
        paint = Paint()
        paint.setAntiAlias(True)
        paint.setDither(True)
        paint.setShader(shader)
        canvas.drawRect(0.0, 0.0, float(bw), float(bh), paint)
        softened = self._soften(bmp)
        if softened is not bmp:
            try:
                bmp.recycle()
            except Exception:
                pass
            bmp = softened
        return bmp

    def _vignette_bitmap(self, w, h, density, preview=False):
        if w < 8 or h < 8:
            return None
        if self.vig_sat < 0.4:
            return None
        inner, mid_a, edge_a, smooth = self._vignette_geom(w, h, density)
        if edge_a < 0.012:
            return None
        max_edge = self._vignette_max_edge
        scale = min(1.0, float(max_edge) / float(max(w, h)))
        bw = max(36, int(w * scale))
        bh = max(36, int(h * scale))
        key = (
            bw,
            bh,
            round(inner, 3),
            round(mid_a, 3),
            round(edge_a, 3),
            round(smooth, 3),
            1 if preview else 0,
        )
        cache_bmp = self._preview_bmp if preview else self._overlay_bmp
        cache_key = self._preview_key if preview else self._overlay_key
        if cache_bmp is not None and cache_key == key:
            try:
                if not cache_bmp.isRecycled():
                    return cache_bmp
            except Exception:
                pass
        try:
            bmp = self._draw_vignette(bw, bh, inner, mid_a, edge_a, smooth)
        except Exception:
            return None
        if preview:
            old = self._preview_bmp
            self._preview_bmp = bmp
            self._preview_key = key
            if old is not None and old is not bmp:
                self._retire(old)
        else:
            old = self._overlay_bmp
            self._overlay_bmp = bmp
            self._overlay_key = key
            if old is not None and old is not bmp:
                self._retire(old)
        return bmp

    def _new_overlay_view(self, context):
        view = ImageView(context)
        try:
            view.setScaleType(ImageView.ScaleType.FIT_XY)
            view.setClickable(False)
            view.setFocusable(False)
            view.setLongClickable(False)
            view.setSoundEffectsEnabled(False)
            view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO)
            view.setTag(OVERLAY_TAG)
        except Exception:
            pass
        try:
            if self._touch_listener is not None:
                view.setOnTouchListener(self._touch_listener)
        except Exception:
            pass
        try:
            view.setElevation(2048.0)
        except Exception:
            pass
        return view

    def _content_view(self, root):
        try:
            found = root.findViewById(ANDROID_CONTENT_ID)
            if found is not None:
                return found
        except Exception:
            pass
        try:
            if isinstance(root, ViewGroup) and root.getChildCount() > 0:
                return root.getChildAt(0)
        except Exception:
            pass
        return root

    def _find_tagged(self, root, tag):
        try:
            if str(root.getTag()) == tag:
                return root
        except Exception:
            pass
        try:
            if not isinstance(root, ViewGroup):
                return None
            n = root.getChildCount()
            for i in range(n - 1, -1, -1):
                child = root.getChildAt(i)
                try:
                    if str(child.getTag()) == tag:
                        return child
                except Exception:
                    pass
        except Exception:
            pass
        return None

    def attach_activity(self, activity):
        if activity is None:
            return
        try:
            if activity.isFinishing():
                return
        except Exception:
            return
        try:
            window = activity.getWindow()
            if window is None:
                return
            decor = window.getDecorView()
        except Exception:
            return
        if self._active_should_skip(activity):
            self.detach_root(decor)
            return
        self.attach_root(decor, activity)

    def detach_activity(self, activity):
        try:
            window = activity.getWindow()
            if window is None:
                return
            self.detach_root(window.getDecorView())
        except Exception:
            pass

    def attach_root(self, root, context):
        if root is None or context is None:
            return

        def work():
            try:
                self._attach_root_ui(root, context)
            except Exception:
                pass

        run_on_ui_thread(work)

    def _attach_root_ui(self, root, context):
        self._reload_cache()
        if self._active_should_skip():
            self._detach_root_ui(root)
            return
        if not self.grade_on:
            self._detach_root_ui(root)
            return
        if not isinstance(root, ViewGroup):
            return
        content = self._content_view(root)
        self._apply_color_to(content)
        w = int(root.getWidth() or 0)
        h = int(root.getHeight() or 0)
        if w < 8 or h < 8:
            try:
                dm = context.getResources().getDisplayMetrics()
                w = int(dm.widthPixels)
                h = int(dm.heightPixels)
            except Exception:
                pass
        density = 1.0
        try:
            density = float(context.getResources().getDisplayMetrics().density)
        except Exception:
            pass
        overlay = self._find_tagged(root, OVERLAY_TAG)
        if overlay is None:
            overlay = self._new_overlay_view(context)
            lp = FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT,
            )
            try:
                root.addView(overlay, lp)
            except Exception:
                try:
                    root.addView(overlay)
                except Exception:
                    return
            self._bind_layout(root)
        bmp = self._vignette_bitmap(w, h, density, preview=False)
        self._paint_overlay(overlay, bmp, context)
        key = int(root.hashCode())
        with self._lock:
            self._attached[key] = (root, content, overlay)

    def _bind_layout(self, root):
        try:
            key = int(root.hashCode())
        except Exception:
            return
        if key in self._layout_bound:
            return
        plugin = self
        try:
            listener_cls = jclass("android.view.View$OnLayoutChangeListener")

            class Layout(dynamic_proxy(listener_cls)):
                def onLayoutChange(self, v, l, t, r, b, ol, ot, rr, ob):
                    if (r - l) == (rr - ol) and (b - t) == (ob - ot):
                        return
                    plugin._schedule_apply(80)

            listener = Layout()
            self._layout_listeners.append(listener)
            root.addOnLayoutChangeListener(listener)
            self._layout_bound.add(key)
        except Exception:
            pass

    def _paint_overlay(self, overlay, bmp, context):
        try:
            if bmp is None:
                overlay.setVisibility(View.GONE)
                try:
                    overlay.setImageDrawable(None)
                except Exception:
                    pass
                return
            overlay.setVisibility(View.VISIBLE)
            drawable = BitmapDrawable(context.getResources(), bmp)
            try:
                drawable.setFilterBitmap(True)
            except Exception:
                pass
            try:
                overlay.setImageDrawable(drawable)
            except Exception:
                overlay.setBackground(drawable)
        except Exception:
            pass

    def _clear_grade(self, content):
        if content is None:
            return
        try:
            if int(Build.VERSION.SDK_INT) >= 31:
                content.setRenderEffect(None)
        except Exception:
            pass
        try:
            content.setLayerType(View.LAYER_TYPE_NONE, None)
        except Exception:
            pass

    def _apply_color_to(self, content):
        if content is None:
            return
        identity = (
            self._identity_grade()
            or not self.grade_on
            or self._grade_broken
            or not self._allow_rendereffect
        )
        sig = self._matrix_sig()
        try:
            key = int(content.hashCode())
        except Exception:
            key = 0
        with self._lock:
            if self._color_sig.get(key) == sig:
                return
            self._color_sig[key] = sig
        if identity:
            self._clear_grade(content)
            return
        try:
            if int(Build.VERSION.SDK_INT) >= 31:
                RenderEffect = jclass("android.graphics.RenderEffect")
                effect = RenderEffect.createColorFilterEffect(self._color_filter())
                content.setRenderEffect(effect)
                return
        except Exception:
            self._grade_broken = True
            self._clear_grade(content)

    def detach_root(self, root):
        if root is None:
            return
        run_on_ui_thread(lambda: self._detach_root_ui(root))

    def _detach_root_ui(self, root):
        try:
            overlay = self._find_tagged(root, OVERLAY_TAG)
            if overlay is not None and isinstance(root, ViewGroup):
                root.removeView(overlay)
        except Exception:
            pass
        content = self._content_view(root)
        self._clear_grade(content)
        try:
            key = int(root.hashCode())
            with self._lock:
                self._attached.pop(key, None)
                self._color_sig.pop(key, None)
        except Exception:
            pass

    def _detach_all(self):
        with self._lock:
            items = list(self._attached.values())
            self._attached.clear()
            self._color_sig.clear()
        for root, content, overlay in items:
            try:
                if overlay is not None and isinstance(root, ViewGroup):
                    root.removeView(overlay)
            except Exception:
                pass
            self._clear_grade(content)
        old_a, old_b = self._overlay_bmp, self._preview_bmp
        self._overlay_bmp = None
        self._preview_bmp = None
        self._overlay_key = None
        self._preview_key = None
        self._retire(old_a)
        self._retire(old_b)

    def _current_activity(self):
        try:
            fragment = get_last_fragment()
            if fragment is not None:
                act = fragment.getParentActivity()
                if act is not None:
                    return act
        except Exception:
            pass
        return None

    def _schedule_apply(self, delay=APPLY_DELAY_MS):
        try:
            if self._apply_runnable is not None:
                _UI_HANDLER.removeCallbacks(self._apply_runnable)
        except Exception:
            pass

        def go():
            try:
                self.apply_now()
            except Exception:
                pass

        self._apply_runnable = _UiRunnable(go)
        try:
            _UI_HANDLER.postDelayed(self._apply_runnable, int(max(0, delay)))
        except Exception:
            run_on_ui_thread(go)

    def apply_now(self):
        self._reload_cache()
        act = self._current_activity()
        if self._active_should_skip(act):
            if act is not None:
                self.detach_activity(act)
            self._refresh_previews()
            return
        if act is not None:
            self.attach_activity(act)
            self._scan_tree_for_glass(act)
        with self._lock:
            roots = [triple[0] for triple in self._attached.values()]
        for root in roots:
            try:
                ctx = root.getContext()
                self._attach_root_ui(root, ctx)
            except Exception:
                pass
        self._refresh_previews()

    def _keep(self, factory):
        self._factories.append(factory)
        return factory()

    def _theme_color(self, key, resources_provider=None, fallback=0xFF111111):
        try:
            if resources_provider is not None:
                return _jcolor(Theme.getColor(key, resources_provider))
            return _jcolor(Theme.getColor(key))
        except Exception:
            return _jcolor(fallback)

    def _settings_text(self, context, text, size_dp, color, bold=False, gravity=None):
        view = TextView(context)
        view.setText(str(text))
        view.setTextSize(TypedValue.COMPLEX_UNIT_DIP, float(size_dp))
        view.setTextColor(_jcolor(color))
        if bold:
            try:
                view.setTypeface(AndroidUtilities.bold())
            except Exception:
                pass
        if gravity is not None:
            view.setGravity(gravity)
        return view

    def _round_rect(self, color, radius_dp):
        gd = GradientDrawable()
        gd.setColor(_jcolor(color))
        gd.setCornerRadius(float(AndroidUtilities.dp(radius_dp)))
        return gd

    def _clip_round(self, view, radius_dp):
        try:
            view.setClipToOutline(True)
        except Exception:
            pass

    def _crop_circle(self, src, size):
        w = int(src.getWidth())
        h = int(src.getHeight())
        side = min(w, h)
        x = (w - side) // 2
        y = (h - side) // 2
        square = Bitmap.createBitmap(src, x, y, side, side)
        scaled = Bitmap.createScaledBitmap(square, size, size, True)
        if square is not src and square is not scaled:
            try:
                square.recycle()
            except Exception:
                pass
        out = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
        canvas = Canvas(out)
        paint = Paint()
        paint.setAntiAlias(True)
        paint.setShader(BitmapShader(scaled, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP))
        r = size / 2.0
        canvas.drawCircle(r, r, r, paint)
        return out

    def _prefetch_avatar(self):
        if self._avatar_bmp is not None or self._avatar_loading:
            return
        self._avatar_loading = True
        plugin = self

        def work():
            bmp = None
            try:
                conn = URL(PREVIEW_AVATAR_URL).openConnection()
                conn.setConnectTimeout(8000)
                conn.setReadTimeout(8000)
                conn.setInstanceFollowRedirects(True)
                try:
                    conn.setRequestProperty("User-Agent", "Mozilla/5.0")
                except Exception:
                    pass
                stream = conn.getInputStream()
                raw = BitmapFactory.decodeStream(stream)
                try:
                    stream.close()
                except Exception:
                    pass
                if raw is not None:
                    size = AndroidUtilities.dp(50)
                    bmp = plugin._crop_circle(raw, size)
                    if raw is not bmp:
                        try:
                            raw.recycle()
                        except Exception:
                            pass
            except Exception:
                bmp = None

            def done():
                plugin._avatar_loading = False
                if bmp is not None:
                    plugin._avatar_bmp = bmp
                    plugin._refresh_previews()

            run_on_ui_thread(done)

        try:
            Thread(work).start()
        except Exception:
            self._avatar_loading = False

    def _bind_avatar(self, avatar):
        bmp = self._avatar_bmp
        if bmp is not None:
            try:
                avatar.setImageBitmap(bmp)
                return
            except Exception:
                try:
                    avatar.setBackground(BitmapDrawable(avatar.getResources(), bmp))
                    return
                except Exception:
                    pass
        try:
            if "BackupImageView" in _class_name(avatar):
                try:
                    avatar.setImage(PREVIEW_AVATAR_URL, "50_50", None, None, 0)
                    return
                except Exception:
                    avatar.setImage(PREVIEW_AVATAR_URL, "50_50", None)
                    return
        except Exception:
            pass
        self._prefetch_avatar()

    def _preview_glass_color(self):
        t = _clamp(self.glass_t, 0.0, 1.0)
        if self.cycle_on:
            c = _u32(self.glass_fg)
            r, g, b = (c >> 16) & 255, (c >> 8) & 255, c & 255
        else:
            _, r, g, b = _parse_hex(
                self.get_setting("glass_color", DEFAULT_GLASS_COLOR), DEFAULT_GLASS_COLOR
            )
        mix = 0.35 + t * 0.55
        rr = _clamp(_lerp(28, r, mix), 0, 255)
        gg = _clamp(_lerp(22, g, mix), 0, 255)
        bb = _clamp(_lerp(32, b, mix), 0, 255)
        a = _clamp(_lerp(100, 168, t), 0, 255)
        return _argb(a, rr, gg, bb)

    def _preview_gradient(self):
        gd = GradientDrawable()
        gd.setOrientation(GradientDrawable.Orientation.BL_TR)
        gd.setColors(_jints(list(PREVIEW_GRADIENT)))
        return gd

    def _preview_factory(self):
        plugin = self

        def create_view(context, list_view, current_account, class_guid, resources_provider):
            host = FrameLayout(context)
            host.setPadding(
                AndroidUtilities.dp(16),
                AndroidUtilities.dp(10),
                AndroidUtilities.dp(16),
                AndroidUtilities.dp(12),
            )
            stage = FrameLayout(context)
            plugin._clip_round(stage, 18)
            wall = View(context)
            wall.setBackground(plugin._preview_gradient())
            stage.addView(wall, FrameLayout.LayoutParams(-1, -1))

            header = LinearLayout(context)
            header.setOrientation(LinearLayout.HORIZONTAL)
            header.setGravity(Gravity.CENTER_VERTICAL)
            hp = AndroidUtilities.dp(10)
            header.setPadding(hp, 0, hp, 0)

            back = TextView(context)
            back.setText("‹")
            back.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 26)
            back.setGravity(Gravity.CENTER)
            back.setTextColor(_jcolor(0xFFFFFFFF))
            back.setBackground(plugin._round_rect(0xB80C0C0E, 20))
            header.addView(back, LinearLayout.LayoutParams(AndroidUtilities.dp(50), AndroidUtilities.dp(50)))

            pill = LinearLayout(context)
            pill.setOrientation(LinearLayout.VERTICAL)
            pill.setGravity(Gravity.CENTER)
            pp = AndroidUtilities.dp(12)
            pill.setPadding(pp, AndroidUtilities.dp(7), pp, AndroidUtilities.dp(7))
            pill.setBackground(plugin._round_rect(plugin._preview_glass_color(), 22))
            name = plugin._settings_text(context, PREVIEW_NAME, 16, 0xFFFFFFFF, True, Gravity.CENTER)
            status = plugin._settings_text(
                context, PREVIEW_STATUS, 12, 0x8CFFFFFF, False, Gravity.CENTER
            )
            pill.addView(name, LayoutHelper.createLinear(-2, -2, Gravity.CENTER))
            pill.addView(status, LayoutHelper.createLinear(-2, -2, Gravity.CENTER))
            lp_pill = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0)
            lp_pill.leftMargin = AndroidUtilities.dp(8)
            lp_pill.rightMargin = AndroidUtilities.dp(8)
            header.addView(pill, lp_pill)

            avatar = None
            try:
                avatar = BackupImageView(context)
                try:
                    avatar.setRoundRadius(AndroidUtilities.dp(20))
                except Exception:
                    pass
            except Exception:
                avatar = ImageView(context)
                try:
                    avatar.setScaleType(ImageView.ScaleType.CENTER_CROP)
                except Exception:
                    pass
            if avatar is None:
                avatar = View(context)
            header.addView(avatar, LinearLayout.LayoutParams(AndroidUtilities.dp(50), AndroidUtilities.dp(50)))
            plugin._bind_avatar(avatar)

            stage.addView(header, FrameLayout.LayoutParams(-1, -1))

            overlay = plugin._new_overlay_view(context)
            try:
                overlay.setElevation(8.0)
            except Exception:
                pass
            stage.addView(overlay, FrameLayout.LayoutParams(-1, -1))

            host.addView(stage, FrameLayout.LayoutParams(-1, AndroidUtilities.dp(104)))
            host.setTag(stage)
            plugin._preview_hosts.append(host)
            plugin._paint_preview_host(host)
            return host

        def bind_view(view, item, divider, adapter, list_view):
            plugin._paint_preview_host(view)

        return SimpleSettingFactory(create_view, bind_view, is_clickable=False, is_shadow=False)

    def _paint_preview_host(self, host):
        try:
            self._reload_cache()
            stage = host.getTag() if host.getTag() is not None else host.getChildAt(0)
            wall = stage.getChildAt(0)
            header = stage.getChildAt(1)
            overlay = stage.getChildAt(2)
            try:
                wall.setBackground(self._preview_gradient())
            except Exception:
                pass
            if (
                self.grade_on
                and not self._identity_grade()
                and not self._grade_broken
                and self._allow_rendereffect
            ):
                try:
                    if int(Build.VERSION.SDK_INT) >= 31:
                        RenderEffect = jclass("android.graphics.RenderEffect")
                        wall.setRenderEffect(RenderEffect.createColorFilterEffect(self._color_filter()))
                    else:
                        wall.setLayerType(View.LAYER_TYPE_NONE, None)
                except Exception:
                    wall.setLayerType(View.LAYER_TYPE_NONE, None)
            else:
                try:
                    if int(Build.VERSION.SDK_INT) >= 31:
                        wall.setRenderEffect(None)
                except Exception:
                    pass
                wall.setLayerType(View.LAYER_TYPE_NONE, None)
            try:
                pill = header.getChildAt(1)
                pill.setBackground(self._round_rect(self._preview_glass_color(), 22))
            except Exception:
                pass
            try:
                avatar = header.getChildAt(2)
                self._bind_avatar(avatar)
            except Exception:
                pass
            w = stage.getWidth() or AndroidUtilities.dp(280)
            h = stage.getHeight() or AndroidUtilities.dp(104)
            density = 1.0
            try:
                density = float(host.getResources().getDisplayMetrics().density)
            except Exception:
                pass
            bmp = self._vignette_bitmap(int(w), int(h), density, preview=True) if self.grade_on else None
            self._paint_overlay(overlay, bmp, host.getContext())
        except Exception:
            pass

    def _refresh_previews(self):
        alive = []
        for host in list(self._preview_hosts):
            try:
                self._paint_preview_host(host)
                alive.append(host)
            except Exception:
                pass
        self._preview_hosts = alive[-4:]

    def _paint_preview_pills(self):
        if not self._preview_hosts:
            return
        color = self._preview_glass_color()
        alive = []
        for host in list(self._preview_hosts):
            painted = False
            try:
                stage = host.getTag() if host.getTag() is not None else host.getChildAt(0)
                header = stage.getChildAt(1)
                pill = header.getChildAt(1)
                pill.setBackground(self._round_rect(color, 22))
                painted = True
            except Exception:
                pass
            if painted:
                alive.append(host)
            else:
                try:
                    if host.getParent() is not None:
                        alive.append(host)
                except Exception:
                    pass
        self._preview_hosts = alive[-4:]

    def _set_bar_progress(self, bar, value, max_value=100):
        try:
            name = _class_name(bar)
            if "SeekBarView" in name:
                bar.setProgress(float(value) / float(max_value), False)
            else:
                bar.setProgress(int(round(value)))
        except Exception:
            try:
                bar.setProgress(int(round(value)))
            except Exception:
                pass

    def _make_slider_bar(self, context, resources_provider, on_drag, max_value=100):
        max_value = float(max_value)
        if self._seekbar_view_cls is not None and self._seekbar_delegate_cls is not None:
            try:
                try:
                    bar = self._seekbar_view_cls(context, False, resources_provider)
                except Exception:
                    bar = self._seekbar_view_cls(context)
                try:
                    bar.setReportChanges(True)
                except Exception:
                    pass
                delegate_cls = self._seekbar_delegate_cls

                class _L(dynamic_proxy(delegate_cls)):
                    def onSeekBarDrag(self, stop, progress):
                        try:
                            on_drag(int(round(float(progress) * max_value)), bool(stop))
                        except Exception:
                            pass

                    def onSeekBarPressed(self, pressed):
                        return None

                    def getContentDescription(self):
                        return ""

                    def getStepsCount(self):
                        return 0

                    def needVisuallyDivideSteps(self):
                        return False

                listener = _L()
                bar.setDelegate(listener)
                return bar, listener
            except Exception:
                pass

        bar = SeekBar(context)
        bar.setMax(int(max_value))
        listener_cls = jclass("android.widget.SeekBar$OnSeekBarChangeListener")

        class _L(dynamic_proxy(listener_cls)):
            def onProgressChanged(self, s, progress, from_user):
                if not from_user:
                    return
                try:
                    on_drag(int(progress), False)
                except Exception:
                    pass

            def onStartTrackingTouch(self, s):
                return None

            def onStopTrackingTouch(self, s):
                try:
                    on_drag(int(s.getProgress()), True)
                except Exception:
                    pass

        listener = _L()
        bar.setOnSeekBarChangeListener(listener)
        return bar, listener

    def _slider_factory(self, key, title, default, formatter=None):
        plugin = self

        def current():
            return plugin._f(key, default, 0.0, 100.0)

        def label_for(value):
            if formatter is not None:
                try:
                    return str(formatter(value))
                except Exception:
                    pass
            return "%d" % int(round(value))

        def create_view(context, list_view, current_account, class_guid, resources_provider):
            root = LinearLayout(context)
            root.setOrientation(LinearLayout.VERTICAL)
            root.setPadding(
                AndroidUtilities.dp(21),
                AndroidUtilities.dp(8),
                AndroidUtilities.dp(21),
                AndroidUtilities.dp(6),
            )
            row = LinearLayout(context)
            row.setOrientation(LinearLayout.HORIZONTAL)
            row.setGravity(Gravity.CENTER_VERTICAL)
            title_view = plugin._settings_text(
                context,
                title,
                16,
                plugin._theme_color(Theme.key_windowBackgroundWhiteBlackText, resources_provider, 0xFFF5F5F7),
            )
            row.addView(title_view, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0))
            value_view = plugin._settings_text(
                context,
                label_for(current()),
                15,
                plugin._theme_color(Theme.key_windowBackgroundWhiteBlueText, resources_provider, 0xFF5AA0E6),
                True,
            )
            try:
                value_view.setMinWidth(AndroidUtilities.dp(40 if formatter is not None else 28))
                value_view.setGravity(Gravity.RIGHT)
            except Exception:
                pass
            row.addView(value_view, LayoutHelper.createLinear(-2, -2))
            root.addView(row, LayoutHelper.createLinear(-1, -2))

            def commit(value, immediate=False):
                plugin.set_setting(key, str(int(value)))
                try:
                    value_view.setText(label_for(value))
                except Exception:
                    pass
                plugin._reload_cache()
                plugin._schedule_apply(0 if immediate else APPLY_DELAY_MS)

            bar, listener = plugin._make_slider_bar(context, resources_provider, commit)
            plugin._slider_refs.append((listener, bar, value_view, key))
            plugin._set_bar_progress(bar, current())
            root.addView(bar, _mlp(-1, AndroidUtilities.dp(38), 0, AndroidUtilities.dp(2), 0, 0))
            root.setTag(bar)
            return root

        def bind_view(view, item, divider, adapter, list_view):
            value = int(round(current()))
            try:
                view.getChildAt(0).getChildAt(1).setText(label_for(value))
            except Exception:
                pass
            try:
                seek = view.getTag()
                if seek is None:
                    seek = view.getChildAt(1)
                plugin._set_bar_progress(seek, value)
            except Exception:
                pass

        return SimpleSettingFactory(create_view, bind_view, is_clickable=False, is_shadow=False)

    def _sv_bitmap(self, hue, w, h):
        w = max(8, int(w))
        h = max(8, int(h))
        bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        canvas = Canvas(bmp)
        hsv = _jfloats([float(hue), 1.0, 1.0])
        hue_color = Color.HSVToColor(hsv)
        paint = Paint()
        paint.setAntiAlias(False)
        paint.setShader(
            LinearGradient(0.0, 0.0, float(w), 0.0, _jcolor(0xFFFFFFFF), hue_color, Shader.TileMode.CLAMP)
        )
        canvas.drawRect(0.0, 0.0, float(w), float(h), paint)
        paint.setShader(
            LinearGradient(0.0, 0.0, 0.0, float(h), _jcolor(0x00000000), _jcolor(0xFF000000), Shader.TileMode.CLAMP)
        )
        paint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.SRC_OVER))
        canvas.drawRect(0.0, 0.0, float(w), float(h), paint)
        return bmp

    def _hue_bitmap(self, w, h):
        w = max(8, int(w))
        h = max(8, int(h))
        bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        canvas = Canvas(bmp)
        colors = _jints(
            [0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000]
        )
        paint = Paint()
        paint.setShader(LinearGradient(0.0, 0.0, float(w), 0.0, colors, None, Shader.TileMode.CLAMP))
        rr = RectF(0.0, 0.0, float(w), float(h))
        canvas.drawRoundRect(rr, h / 2.0, h / 2.0, paint)
        return bmp

    def _commit_glass_color(self, argb, immediate=True):
        c = _to_int(argb)
        if c is None:
            return
        r, g, b = (c >> 16) & 255, (c >> 8) & 255, c & 255
        self.set_setting("glass_color", "%02X%02X%02X" % (r, g, b))
        self._reload_cache()
        self._schedule_apply(0 if immediate else APPLY_DELAY_MS)

    def _try_native_picker(self, context, r, g, b):
        color = _argb(255, r, g, b)
        names = (
            "org.telegram.ui.Components.ColorPickerBottomSheet",
            "org.telegram.ui.ActionBar.ColorPickerBottomSheet",
        )
        for name in names:
            try:
                Cls = find_class(name)
                if Cls is None:
                    continue
                try:
                    sheet = Cls(context)
                except Exception:
                    sheet = Cls(context, True)
                try:
                    sheet.setColor(color)
                except Exception:
                    pass
                plugin = self
                Callback = jclass("org.telegram.messenger.Utilities$Callback")

                class L(dynamic_proxy(Callback)):
                    def run(self, value):
                        plugin._commit_glass_color(value, True)

                lis = L()
                self._picker_refs.append(lis)
                try:
                    sheet.setColorListener(lis)
                except Exception:
                    sheet.setListener(lis)
                sheet.show()
                return True
            except Exception:
                continue
        return False

    def _knob(self, context):
        v = View(context)
        gd = GradientDrawable()
        gd.setShape(GradientDrawable.OVAL)
        gd.setColor(_jcolor(0xFFFFFFFF))
        try:
            gd.setStroke(AndroidUtilities.dp(2), _jcolor(0xFF111111))
        except Exception:
            pass
        v.setBackground(gd)
        return v

    def _open_color_picker(self, view=None):
        plugin = self
        try:
            fragment = get_last_fragment()
            activity = fragment.getParentActivity() if fragment is not None else None
            context = activity if activity is not None else (view.getContext() if view is not None else None)
            if context is None:
                return
            hex_s, cr, cg, cb = _parse_hex(
                self.get_setting("glass_color", DEFAULT_GLASS_COLOR), DEFAULT_GLASS_COLOR
            )
            if self._try_native_picker(context, cr, cg, cb):
                return
            hsv = jarray(jfloat)(3)
            Color.colorToHSV(_argb(255, cr, cg, cb), hsv)
            state = {"h": float(hsv[0]), "s": float(hsv[1]), "v": float(hsv[2])}

            def current_rgb():
                arr = _jfloats([state["h"], state["s"], state["v"]])
                c = Color.HSVToColor(arr) & 0xFFFFFFFF
                return (c >> 16) & 255, (c >> 8) & 255, c & 255

            def hex_now():
                r, g, b = current_rgb()
                return "%02X%02X%02X" % (r, g, b)

            def apply_color(immediate=False):
                r, g, b = current_rgb()
                plugin.set_setting("glass_color", "%02X%02X%02X" % (r, g, b))
                plugin._reload_cache()
                plugin._schedule_apply(0 if immediate else APPLY_DELAY_MS)

            root = LinearLayout(context)
            root.setOrientation(LinearLayout.VERTICAL)
            pad = AndroidUtilities.dp(16)
            root.setPadding(pad, AndroidUtilities.dp(4), pad, pad)
            root.setLayoutParams(ViewGroup.LayoutParams(-1, -2))

            swatch = View(context)
            swatch.setBackground(plugin._round_rect(0xFF000000 | (cr << 16) | (cg << 8) | cb, 14))
            root.addView(swatch, _mlp(-1, AndroidUtilities.dp(44), 0, 0, 0, AndroidUtilities.dp(12)))

            sv_wrap = FrameLayout(context)
            plugin._clip_round(sv_wrap, 14)
            sv = ImageView(context)
            sv.setScaleType(ImageView.ScaleType.FIT_XY)
            sv_wrap.addView(sv, FrameLayout.LayoutParams(-1, -1))
            sv_knob = plugin._knob(context)
            knob_sz = AndroidUtilities.dp(18)
            sv_wrap.addView(sv_knob, FrameLayout.LayoutParams(knob_sz, knob_sz))
            root.addView(sv_wrap, _mlp(-1, AndroidUtilities.dp(168), 0, 0, 0, AndroidUtilities.dp(12)))

            hue_wrap = FrameLayout(context)
            hue = ImageView(context)
            hue.setScaleType(ImageView.ScaleType.FIT_XY)
            hue_wrap.addView(hue, FrameLayout.LayoutParams(-1, -1))
            hue_knob = plugin._knob(context)
            hue_wrap.addView(hue_knob, FrameLayout.LayoutParams(knob_sz, knob_sz))
            root.addView(hue_wrap, _mlp(-1, AndroidUtilities.dp(22), 0, 0, 0, AndroidUtilities.dp(14)))

            r_bar = [None]
            g_bar = [None]
            b_bar = [None]

            def place_sv_knob():
                try:
                    ww = max(1, sv_wrap.getWidth())
                    hh = max(1, sv_wrap.getHeight())
                    x = int(state["s"] * ww - knob_sz / 2)
                    y = int((1.0 - state["v"]) * hh - knob_sz / 2)
                    sv_knob.setX(float(_clamp(x, 0, ww - knob_sz)))
                    sv_knob.setY(float(_clamp(y, 0, hh - knob_sz)))
                except Exception:
                    pass

            def place_hue_knob():
                try:
                    ww = max(1, hue_wrap.getWidth())
                    hh = max(1, hue_wrap.getHeight())
                    x = int((state["h"] / 360.0) * ww - knob_sz / 2)
                    y = int((hh - knob_sz) / 2)
                    hue_knob.setX(float(_clamp(x, 0, ww - knob_sz)))
                    hue_knob.setY(float(y))
                except Exception:
                    pass

            def paint_sv():
                try:
                    w = sv_wrap.getWidth() or AndroidUtilities.dp(280)
                    h = sv_wrap.getHeight() or AndroidUtilities.dp(168)
                    sv.setImageBitmap(plugin._sv_bitmap(state["h"], w, h))
                except Exception:
                    pass

            def paint_hue():
                try:
                    w = hue_wrap.getWidth() or AndroidUtilities.dp(280)
                    h = hue_wrap.getHeight() or AndroidUtilities.dp(22)
                    hue.setImageBitmap(plugin._hue_bitmap(w, h))
                except Exception:
                    pass

            def set_from_rgb(r, g, b):
                Color.colorToHSV(_argb(255, r, g, b), hsv)
                state["h"] = float(hsv[0])
                state["s"] = float(hsv[1])
                state["v"] = float(hsv[2])
                paint_sv()
                place_sv_knob()
                place_hue_knob()

            def channel_row(label, getter, setter):
                row = LinearLayout(context)
                row.setOrientation(LinearLayout.HORIZONTAL)
                row.setGravity(Gravity.CENTER_VERTICAL)
                lab = plugin._settings_text(context, label, 14, 0xFF8E8E93, True)
                lab.setMinWidth(AndroidUtilities.dp(18))
                row.addView(lab, LayoutHelper.createLinear(-2, -2))

                def on_drag(value, stop):
                    setter(int(value))
                    try:
                        val.setText("%d" % int(value))
                    except Exception:
                        pass
                    r, g, b = current_rgb()
                    try:
                        swatch.setBackground(plugin._round_rect(0xFF000000 | (r << 16) | (g << 8) | b, 14))
                        hex_row.setText("#" + hex_now())
                    except Exception:
                        pass
                    apply_color(bool(stop))

                bar, lis = plugin._make_slider_bar(context, None, on_drag, 255)
                plugin._picker_refs.append(lis)
                try:
                    name = _class_name(bar)
                    if "SeekBarView" in name:
                        bar.setProgress(float(getter()) / 255.0, False)
                    else:
                        bar.setMax(255)
                        bar.setProgress(int(getter()))
                except Exception:
                    try:
                        bar.setMax(255)
                        bar.setProgress(int(getter()))
                    except Exception:
                        pass
                val = plugin._settings_text(context, "%d" % getter(), 13, 0xFFF5F5F7, True)
                val.setMinWidth(AndroidUtilities.dp(32))
                val.setGravity(Gravity.RIGHT)
                row.addView(bar, LinearLayout.LayoutParams(0, AndroidUtilities.dp(38), 1.0))
                row.addView(val, LayoutHelper.createLinear(-2, -2))
                root.addView(row, _mlp(-1, -2, 0, 0, 0, AndroidUtilities.dp(2)))
                return bar

            def wrap_setter(index):
                def set_v(v):
                    r, g, b = current_rgb()
                    rgb = [r, g, b]
                    rgb[index] = int(v)
                    set_from_rgb(rgb[0], rgb[1], rgb[2])

                return set_v

            r_bar[0] = channel_row("R", lambda: current_rgb()[0], wrap_setter(0))
            g_bar[0] = channel_row("G", lambda: current_rgb()[1], wrap_setter(1))
            b_bar[0] = channel_row("B", lambda: current_rgb()[2], wrap_setter(2))

            hex_row = plugin._settings_text(context, "#" + hex_now(), 14, 0xFF8E8E93, False, Gravity.CENTER)
            root.addView(hex_row, _mlp(-1, -2, 0, AndroidUtilities.dp(8), 0, 0))

            def refresh_channels():
                r, g, b = current_rgb()
                try:
                    plugin._set_bar_progress(r_bar[0], r, 255)
                    plugin._set_bar_progress(g_bar[0], g, 255)
                    plugin._set_bar_progress(b_bar[0], b, 255)
                    swatch.setBackground(plugin._round_rect(0xFF000000 | (r << 16) | (g << 8) | b, 14))
                    hex_row.setText("#" + hex_now())
                except Exception:
                    pass
                place_sv_knob()
                place_hue_knob()
                apply_color(False)

            touch_cls = jclass("android.view.View$OnTouchListener")

            class SvTouch(dynamic_proxy(touch_cls)):
                def onTouch(self, v, event):
                    try:
                        act = int(event.getActionMasked())
                        if act not in (
                            int(MotionEvent.ACTION_DOWN),
                            int(MotionEvent.ACTION_MOVE),
                            int(MotionEvent.ACTION_UP),
                        ):
                            return True
                        ww = max(1, v.getWidth())
                        hh = max(1, v.getHeight())
                        x = _clamp(float(event.getX()) / float(ww), 0.0, 1.0)
                        y = _clamp(float(event.getY()) / float(hh), 0.0, 1.0)
                        state["s"] = x
                        state["v"] = 1.0 - y
                        refresh_channels()
                        if act == int(MotionEvent.ACTION_UP):
                            apply_color(True)
                    except Exception:
                        pass
                    return True

            class HueTouch(dynamic_proxy(touch_cls)):
                def onTouch(self, v, event):
                    try:
                        act = int(event.getActionMasked())
                        if act not in (
                            int(MotionEvent.ACTION_DOWN),
                            int(MotionEvent.ACTION_MOVE),
                            int(MotionEvent.ACTION_UP),
                        ):
                            return True
                        ww = max(1, v.getWidth())
                        x = _clamp(float(event.getX()) / float(ww), 0.0, 1.0)
                        state["h"] = x * 360.0
                        paint_sv()
                        refresh_channels()
                        if act == int(MotionEvent.ACTION_UP):
                            apply_color(True)
                    except Exception:
                        pass
                    return True

            sv_touch = SvTouch()
            hue_touch = HueTouch()
            plugin._picker_refs.extend([sv_touch, hue_touch])
            sv_wrap.setOnTouchListener(sv_touch)
            hue_wrap.setOnTouchListener(hue_touch)

            listener_cls = jclass("android.view.View$OnLayoutChangeListener")

            class Lay(dynamic_proxy(listener_cls)):
                def onLayoutChange(self, v, l, t, r, b, ol, ot, rr, ob):
                    if r - l <= 0 or b - t <= 0:
                        return
                    paint_sv()
                    paint_hue()
                    place_sv_knob()
                    place_hue_knob()

            lay = Lay()
            plugin._picker_refs.append(lay)
            sv_wrap.addOnLayoutChangeListener(lay)
            hue_wrap.addOnLayoutChangeListener(lay)

            shown = False
            try:
                Builder = jclass("org.telegram.ui.ActionBar.BottomSheet$Builder")
                builder = Builder(context)
                try:
                    builder.setTitle("Цвет стекла", True)
                except Exception:
                    try:
                        builder.setTitle("Цвет стекла")
                    except Exception:
                        pass
                try:
                    builder.setCustomView(root, Gravity.LEFT | Gravity.TOP)
                except Exception:
                    builder.setCustomView(root)
                builder.show()
                shown = True
            except Exception:
                shown = False
            if not shown:
                builder = AlertDialog.Builder(context)
                builder.setTitle("Цвет стекла")
                builder.setView(root)
                click_cls = jclass("android.content.DialogInterface$OnClickListener")

                class Ok(dynamic_proxy(click_cls)):
                    def onClick(self, dialog, which):
                        apply_color(True)

                ok = Ok()
                plugin._picker_refs.append(ok)
                builder.setPositiveButton("Готово", ok)
                builder.show()
        except Exception:
            pass

    def _color_factory(self):
        plugin = self

        def current_hex():
            return _parse_hex(
                plugin.get_setting("glass_color", DEFAULT_GLASS_COLOR), DEFAULT_GLASS_COLOR
            )

        def create_view(context, list_view, current_account, class_guid, resources_provider):
            root = LinearLayout(context)
            root.setOrientation(LinearLayout.HORIZONTAL)
            root.setGravity(Gravity.CENTER_VERTICAL)
            root.setPadding(
                AndroidUtilities.dp(21),
                AndroidUtilities.dp(12),
                AndroidUtilities.dp(21),
                AndroidUtilities.dp(12),
            )
            label = plugin._settings_text(
                context,
                "Цвет",
                16,
                plugin._theme_color(Theme.key_windowBackgroundWhiteBlackText, resources_provider, 0xFFF5F5F7),
                False,
                Gravity.LEFT | Gravity.CENTER_VERTICAL,
            )
            root.addView(label, LayoutHelper.createLinear(-2, -2))
            hex_s, r, g, b = current_hex()
            hex_tv = plugin._settings_text(
                context,
                "#" + hex_s,
                13,
                plugin._theme_color(Theme.key_windowBackgroundWhiteGrayText, resources_provider, 0xFF8E8E93),
                True,
                Gravity.RIGHT | Gravity.CENTER_VERTICAL,
            )
            lp_hex = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0)
            lp_hex.leftMargin = AndroidUtilities.dp(10)
            lp_hex.rightMargin = AndroidUtilities.dp(10)
            root.addView(hex_tv, lp_hex)
            swatch = View(context)
            swatch.setBackground(plugin._round_rect(0xFF000000 | (r << 16) | (g << 8) | b, 8))
            root.addView(swatch, LinearLayout.LayoutParams(AndroidUtilities.dp(36), AndroidUtilities.dp(28)))
            root.setTag(swatch)
            return root

        def bind_view(view, item, divider, adapter, list_view):
            try:
                hex_s, r, g, b = current_hex()
                fill = view.getTag()
                fill.setBackground(plugin._round_rect(0xFF000000 | (r << 16) | (g << 8) | b, 8))
                view.getChildAt(1).setText("#" + hex_s)
            except Exception:
                pass

        def on_click(plugin_ref, item, view):
            target = plugin_ref if hasattr(plugin_ref, "_open_color_picker") else plugin
            target._open_color_picker(view)

        return SimpleSettingFactory(
            create_view, bind_view, is_clickable=True, is_shadow=False, on_click=on_click
        )

    def _open_tg(self, username, view=None):
        url = "https://t.me/" + username
        try:
            fragment = get_last_fragment()
            ctx = None
            if fragment is not None:
                ctx = fragment.getParentActivity()
            if ctx is None and view is not None:
                ctx = view.getContext()
            Browser = jclass("org.telegram.messenger.browser.Browser")
            Browser.openUrl(ctx, url)
        except Exception:
            try:
                Intent = jclass("android.content.Intent")
                Uri = jclass("android.net.Uri")
                ctx.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
            except Exception:
                pass

    def _show_welcome_dialog(self):
        try:
            current_fragment = get_last_fragment()
            if not current_fragment:
                return
            activity = current_fragment.getParentActivity()
            if not activity:
                return

            builder = AlertDialogBuilder(activity)
            builder.set_title(__name__)
            builder.set_message("«Заманчивое описание канала!»")

            def on_hide(bld, which):
                self.set_setting("welcome_shown", True)
                bld.dismiss()
                try:
                    Toast.makeText(
                        ApplicationLoader.applicationContext,
                        "куда заманчивее =(",
                        Toast.LENGTH_SHORT,
                    ).show()
                except Exception:
                    pass

            def on_channel(bld, which):
                self.set_setting("welcome_shown", True)
                self._open_tg("HzzzPlugins")
                bld.dismiss()

            builder.set_positive_button("🆘 Перейти туда", on_channel)
            builder.set_negative_button("Скрыть", on_hide)
            builder.show()
        except Exception:
            pass

    def create_settings(self):
        if not self._welcome_shown and not self.get_setting("welcome_shown", False):
            self._welcome_shown = True
            run_on_ui_thread(self._show_welcome_dialog)

        self._factories = []
        self._slider_refs = []
        self._preview_hosts = []
        cycle_on = self._b("glass_cycle", False)
        items = [
            Header(text="1.5-public"),
            self._keep(self._preview_factory()),
            Header(text="ᅠ"),
            Header(text="Liquid glass"),
            self._keep(self._slider_factory("glass_sat", "Насыщенность цвета", DEFAULT_GLASS_SAT)),
        ]
        if not cycle_on:
            items.append(self._keep(self._color_factory()))
        items.append(
            Switch(
                key="glass_cycle",
                text="Переливающийся градиент !beta2",
                default=False,
                on_change=self._on_cycle_change,
            )
        )
        if cycle_on:
            items.append(
                self._keep(
                    self._slider_factory(
                        "glass_cycle_speed",
                        "Скорость",
                        DEFAULT_CYCLE_SPEED,
                        formatter=_cycle_speed_label,
                    )
                )
            )
        items.extend(
            [
                Header(text="Виньетка"),
                self._keep(self._slider_factory("vig_size", "Размер", DEFAULT_VIG_SIZE)),
                self._keep(self._slider_factory("vig_sat", "Насыщенность", DEFAULT_VIG_SAT)),
                self._keep(self._slider_factory("vig_smooth", "Резкость", DEFAULT_VIG_SMOOTH)),
                Header(text="Киношный эффект"),
                self._keep(self._slider_factory("cine", "Интенсивность", DEFAULT_CINE)),
                Header(text="ᅠ"),
                Text(
                    text="Сделал: @exoseo",
                    on_click=lambda v=None: self._open_tg("exoseo", v),
                ),
                Text(
                    text="Канал: @HzzzPlugins",
                    accent=True,
                    on_click=lambda v=None: self._open_tg("HzzzPlugins", v),
                ),
            ]
        )
        return items
