"""Keeps the ghost icon at menu-icon size. exteraGram's `ghost` drawable is 32dp (its icon-pack variants 30dp), while menu and drawer icons are 24dp, and both views show icons at their intrinsic size (ImageView.ScaleType.CENTER). The menus only take a resource id, so after a view has set the ghost icon, the drawable it holds (already swapped for the active icon pack by the app's resources) is wrapped in a LayerDrawable sized to 24dp: ActionBarMenuSubItem.setTextAndIcon(CharSequence, int, ...) chat/profile menus DrawerMenuItemView.setMenuItem(int, int, int, CharSequence) drawer rows """ from base_plugin import MethodHook from hooking import field, hook_all, unhook_all GHOST = "ghost" SIZE_DP = 24 SUB_ITEM = "org.telegram.ui.ActionBar.ActionBarMenuSubItem" DRAWER_ITEM = "com.exteragram.messenger.drawer.DrawerMenuItemView" _ids = {} def drawable_id(name): """A drawable's resource id by name (the app's R isn't importable).""" if name not in _ids: from org.telegram.messenger import ApplicationLoader context = ApplicationLoader.applicationContext _ids[name] = int(context.getResources().getIdentifier(name, "drawable", context.getPackageName())) return _ids[name] def fit(drawable, size_dp=SIZE_DP): """`drawable` drawn into a size_dp square, or as is when it isn't bigger.""" if drawable is None: return None from android.graphics.drawable import LayerDrawable from org.telegram.messenger import AndroidUtilities size = int(AndroidUtilities.dp(size_dp)) if max(int(drawable.getIntrinsicWidth()), int(drawable.getIntrinsicHeight())) <= size: return drawable layer = LayerDrawable([drawable]) layer.setLayerSize(0, size, size) return layer def shrink(image_view): if image_view is not None: image_view.setImageDrawable(fit(image_view.getDrawable())) class Icons: def __init__(self, plugin): self.plugin = plugin self.handles = [] def install(self): icons = self class SubItemHook(MethodHook): # setTextAndIcon(CharSequence text, int icon, ...) def after_hooked_method(self, param): icons._after(param, 1, "imageView") class DrawerItemHook(MethodHook): # setMenuItem(int layoutId, int account, int icon, CharSequence text) def after_hooked_method(self, param): icons._after(param, 2, "iconView") self.handles += hook_all(self.plugin, SUB_ITEM, "setTextAndIcon", SubItemHook()) self.handles += hook_all(self.plugin, DRAWER_ITEM, "setMenuItem", DrawerItemHook()) def uninstall(self): unhook_all(self.plugin, self.handles) def _after(self, param, icon_arg, view_field): try: args = param.args if len(args) <= icon_arg or not isinstance(args[icon_arg], int): return if int(args[icon_arg]) != drawable_id(GHOST): return shrink(field(param.thisObject, view_field)) except Exception as exc: self.plugin.log(f"ghost icon size: {exc}")