diff --git a/configs/comment_vote.json b/configs/comment_vote.json
new file mode 100644
index 0000000..e0caaa6
--- /dev/null
+++ b/configs/comment_vote.json
@@ -0,0 +1 @@
+{"enabled":true,"replace":true,"custom_icons":true,"icon_size":"18.0dip"}
\ No newline at end of file
diff --git a/configs/insert_new.json b/configs/insert_new.json
deleted file mode 100644
index 310f75e..0000000
--- a/configs/insert_new.json
+++ /dev/null
@@ -1 +0,0 @@
-{"enabled":true}
\ No newline at end of file
diff --git a/configs/welcome.json b/configs/welcome.json
new file mode 100644
index 0000000..a87e7c6
--- /dev/null
+++ b/configs/welcome.json
@@ -0,0 +1 @@
+{"enabled":true,"title":"Anixarty","description":"Описание","link_text":"МЫ В TELEGRAM","link_url":"https://t.me/http_teapod","skip_text":"Пропустить","title_bg_color":"#FFFFFF"}
\ No newline at end of file
diff --git a/main.py b/main.py
index cdde703..968d3cb 100644
--- a/main.py
+++ b/main.py
@@ -13,7 +13,6 @@ from rich.prompt import Prompt
from utils.config import *
from utils.tools import *
-# --- Paths ---
console = Console()
app = typer.Typer()
@@ -30,6 +29,7 @@ class Patch:
self.config = module.Config.model_validate_json((CONFIGS / f"{name}.json").read_text())
except Exception as e:
console.print(f"[red]Ошибка при загрузке конфигурации патча {name}: {e}")
+ console.print(f"[yellow]Используются значения по умолчанию")
self.config = module.Config()
def apply(self, conf: Dict[str, Any]) -> bool:
@@ -42,7 +42,7 @@ class Patch:
return False
-# ======================= INIT =========================
+# ========================= INIT =========================
@app.command()
def init():
"""Создание директорий и скачивание инструментов"""
@@ -71,7 +71,7 @@ def init():
raise typer.Exit(1)
-# ======================= INFO =========================
+# ========================= INFO =========================
@app.command()
def info(patch_name: str = ""):
"""Вывод информации о патче"""
@@ -81,18 +81,36 @@ def info(patch_name: str = ""):
console.print(f"[green]Информация о патче {patch.name}:")
console.print(f" [yellow]Приоритет: {patch.priority}")
console.print(f" [yellow]Описание: {patch.module.__doc__}")
+
+ console.print(f"[blue]Поля конфигурации")
+ for field_name, field_info in type(patch.config).model_fields.items():
+ field_data = {
+ 'type': field_info.annotation.__name__,
+ 'description': field_info.description,
+ 'default': field_info.default,
+ 'json_schema_extra': field_info.json_schema_extra,
+ }
+ console.print(f'{field_name} {field_data}')
+ console.print("\n[blue]" + "="*50 + "\n")
+
else:
+ conf = load_config(console)
console.print("[cyan]Список патчей:")
+ patch_list = []
for f in PATCHES.glob("*.py"):
- if f.name.startswith("todo_") or f.name == "__init__.py":
+ if f.name == "__init__.py": continue
+ if f.name.startswith("todo_"):
+ try: priority = __import__(f"patches.{f.stem}.priority", fromlist=[""])
+ except: priority = None
+ patch_list.append((priority, f" [{priority}] [yellow]{f.stem}: [yellow]⚠ в разработке"))
continue
- name = f.stem
- if conf["patches"].get(name, {}).get("enabled", True):
- console.print(f" [yellow]{name}: [green]✔ enabled")
- else:
- console.print(f" [yellow]{name}: [red]✘ disabled")
+ patch = Patch(f.stem, __import__(f"patches.{f.stem}", fromlist=[""]))
+ if patch.config.enabled: patch_list.append((patch.priority, f" [{patch.priority}] [yellow]{f.stem}: [green]✔ включен"))
+ else: patch_list.append((patch.priority, f" [{patch.priority}] [yellow]{f.stem}: [red]✘ выключен"))
+ for _, patch in sorted(patch_list, key=lambda x: (x[0] is None, x[0]), reverse=True): console.print(patch)
+# ========================= UTIL =========================
def select_apk() -> Path:
apks = [f for f in ORIGINAL.glob("*.apk")]
if not apks:
@@ -174,6 +192,7 @@ def compile(apk: Path, patches: List[Patch]):
f.write(f"{'✔' if p.applied else '✘'} {p.name}\n")
+# ========================= BUILD =========================
@app.command()
def build(
force: bool = typer.Option(False, "--force", "-f", help="Принудительная сборка"),
@@ -219,5 +238,4 @@ def build(
raise typer.Exit(1)
-if __name__ == "__main__":
- app()
+if __name__ == "__main__": app()
diff --git a/patches/change_server.py b/patches/change_server.py
index 668dff9..543d716 100644
--- a/patches/change_server.py
+++ b/patches/change_server.py
@@ -1,5 +1,4 @@
-"""
- Заменяет сервер api
+"""Заменяет сервер api
"change_server": {
"enabled": true,
@@ -24,11 +23,11 @@ class Config(PatchConfig):
# Patch
def apply(config: Config, base: Dict[str, Any]) -> bool:
- response = requests.get(config.server)
+ response = requests.get(config.server) # Получаем данные для патча
assert response.status_code == 200, f"Failed to fetch data {response.status_code} {response.text}"
new_api = json.loads(response.text)
- for item in new_api['modifications']:
+ for item in new_api['modifications']: # Применяем замены API
tqdm.write(f"Изменение {item['file']}")
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/network/api/'+item['file']
@@ -40,7 +39,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
tqdm.write(f"⚠ Не найдено {item['src']}")
f.write(content.replace(item['src'], item['dst']))
- tqdm.write(f"Изменение Github ссылки")
+ tqdm.write(f"Изменение Github ссылки") # Обновление ссылки на поиск серверов в Github
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/utils/anixnet/GithubPagesNetFetcher.smali'
with open(filepath, 'r') as f:
@@ -49,10 +48,10 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
with open(filepath, 'w') as f:
f.write(content.replace('const-string v1, "https://anixhelper.github.io/pages/urls.json"', f'const-string v1, "{new_api["gh"]}"'))
- content = ""
- tqdm.write("Удаление динамического выбора сервера")
+ tqdm.write("Удаление динамического выбора сервера") # Отключение автовыбора сервера
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/DaggerApp_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.smali'
+ content = ""
with open(filepath, 'r') as f:
for line in f.readlines():
if "addInterceptor" in line: continue
diff --git a/patches/color_theme.py b/patches/color_theme.py
index 9add593..ed8f1cb 100644
--- a/patches/color_theme.py
+++ b/patches/color_theme.py
@@ -1,5 +1,4 @@
-"""
- Изменяет цветовую тему приложения и иконку
+"""Изменяет цветовую тему приложения и иконку
"color_theme": {
"enabled": true,
@@ -59,7 +58,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
main_color = config.colors.primary
splash_color = config.colors.secondary
- # No connection alert coolor
+ # Обновление сообщения об отсутствии подключения
with open("./decompiled/assets/no_connection.html", "r", encoding="utf-8") as file:
file_contents = file.read()
@@ -68,33 +67,32 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
with open("./decompiled/assets/no_connection.html", "w", encoding="utf-8") as file:
file.write(new_contents)
- # For logo
+ # Суффиксы лого
drawable_types = ["", "-night"]
for drawable_type in drawable_types:
- # Application logo gradient colors
+ # Градиент лого приложения
file_path = f"./decompiled/res/drawable{drawable_type}/$logo__0.xml"
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser)
root = tree.getroot()
- # Change attributes with namespace
+ # Замена атрибутов значениями из конфигурации
root.set(f"{{{base['xml_ns']['android']}}}angle", str(config.logo.gradient.angle))
root.set(f"{{{base['xml_ns']['android']}}}startColor", config.logo.gradient.start_color)
root.set(f"{{{base['xml_ns']['android']}}}endColor", config.logo.gradient.end_color)
- # Save back
+ # Сохранение
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
- # Application logo anim color
+ # Замена анимации лого
file_path = f"./decompiled/res/drawable{drawable_type}/$logo_splash_anim__0.xml"
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser)
root = tree.getroot()
- # Finding "path"
for el in root.findall("path", namespaces=base["xml_ns"]):
name = el.get(f"{{{base['xml_ns']['android']}}}name")
if name == "path":
@@ -102,7 +100,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
elif name in ["path_1", "path_2"]:
el.set(f"{{{base['xml_ns']['android']}}}fillColor", config.logo.ears_color)
- # Save back
+ # Сохранение
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
for filename in ["$ic_launcher_foreground__0", "$ic_banner_foreground__0"]:
@@ -112,21 +110,23 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
tree = etree.parse(file_path, parser)
root = tree.getroot()
- # Change attributes with namespace
+ # Замена атрибутов значениями из конфигурации
root.set(f"{{{base['xml_ns']['android']}}}angle", str(config.logo.gradient.angle))
items = root.findall("item", namespaces=base['xml_ns'])
assert len(items) == 2
items[0].set(f"{{{base['xml_ns']['android']}}}color", config.logo.gradient.start_color)
items[1].set(f"{{{base['xml_ns']['android']}}}color", config.logo.gradient.end_color)
- # Save back
+ # Сохранение
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
+ # Добаление новых цветов для темы
insert_after_public("carmine", "custom_color")
insert_after_public("carmine_alpha_10", "custom_color_alpha_10")
insert_after_color("carmine", "custom_color", main_color[0]+'ff'+main_color[1:])
insert_after_color("carmine_alpha_10", "custom_color_alpha_10", main_color[0]+'1a'+main_color[1:])
+ # Замена цветов
change_color("accent_alpha_10", main_color[0]+'1a'+main_color[1:])
change_color("accent_alpha_20", main_color[0]+'33'+main_color[1:])
change_color("accent_alpha_50", main_color[0]+'80'+main_color[1:])
diff --git a/patches/comment_vote.py b/patches/comment_vote.py
new file mode 100644
index 0000000..1bc75a6
--- /dev/null
+++ b/patches/comment_vote.py
@@ -0,0 +1,99 @@
+"""Меняет местами кнопки лайка и дизлайка у коментария и иконки
+
+"comment_vote": {
+ "enabled": true,
+ "replace": true,
+ "custom_icons": true,
+ "icons_size": "14.0dip"
+}
+"""
+
+priority = 0
+
+# imports
+import os
+import shutil
+from tqdm import tqdm
+from lxml import etree
+from pydantic import Field
+from typing import Dict, Any
+from utils.config import PatchConfig
+
+#Config
+class Config(PatchConfig):
+ replace: bool = Field(True, description="Менять местами лайк/дизлайк")
+ custom_icons: bool = Field(True, description="Кастомные иконки")
+ icon_size: str = Field("18.0dip", description="Размер иконки")
+
+# Patch
+def apply(config, base: Dict[str, Any]) -> bool:
+ file_path = "./decompiled/res/layout/item_comment.xml"
+ parser = etree.XMLParser(remove_blank_text=True)
+ tree = etree.parse(file_path, parser)
+ root = tree.getroot()
+
+ tqdm.write("Меняем размер иконок лайка и дизлайка...")
+ for icon in root.xpath(
+ ".//*[@android:id='@id/votePlusInactive']//ImageView | "
+ ".//*[@android:id='@id/votePlusActive']//ImageView | "
+ ".//*[@android:id='@id/voteMinusInactive']//ImageView | "
+ ".//*[@android:id='@id/voteMinusActive']//ImageView",
+ namespaces=base['xml_ns'],
+ ):
+ icon.set(f"{{{base['xml_ns']['android']}}}layout_width", config.icon_size)
+ # icon.set(f"{{{base['xml_ns']['android']}}}layout_height", config.icon_size)
+
+ if config.replace:
+ tqdm.write("Меняем местами лайк и дизлайк комментария...")
+
+ containers = root.xpath(
+ ".//LinearLayout[.//*[@android:id='@id/voteMinus'] and .//*[@android:id='@id/votePlus']]",
+ namespaces=base["xml_ns"],
+ )
+
+ found = False
+ for container in containers:
+ children = list(container)
+ vote_plus = None
+ vote_minus = None
+
+ for ch in children:
+ cid = ch.get(f'{{{base["xml_ns"]["android"]}}}id')
+ if cid == "@id/votePlus":
+ vote_plus = ch
+ elif cid == "@id/voteMinus":
+ vote_minus = ch
+
+ if vote_plus is not None and vote_minus is not None:
+ found = True
+ i_plus = children.index(vote_plus)
+ i_minus = children.index(vote_minus)
+ children[i_plus], children[i_minus] = children[i_minus], children[i_plus]
+ container[:] = children
+ tqdm.write("Кнопки лайк и дизлайк поменялись местами.")
+ break
+
+ if not found:
+ tqdm.write("Не удалось найти оба узла votePlus/voteMinus даже в общих LinearLayout.")
+
+ if config.custom_icons:
+ tqdm.write("Заменяем иконки лайка и дизлайка на кастомные...")
+ for suffix in ["up", "up_40", "down", "down_40"]:
+ shutil.copy(
+ f"./resources/ic_chevron_{suffix}.xml",
+ f"./decompiled/res/drawable/ic_chevron_{suffix}.xml",
+ )
+
+ for inactive in root.xpath(
+ ".//*[@android:id='@id/votePlusInactive'] | .//*[@android:id='@id/voteMinusInactive']",
+ namespaces=base["xml_ns"],
+ ):
+ for img in inactive.xpath(".//ImageView[@android:src]", namespaces=base["xml_ns"]):
+ src = img.get(f'{{{base["xml_ns"]["android"]}}}src', "")
+ if src.startswith("@drawable/") and not src.endswith("_40"):
+ img.set(f'{{{base["xml_ns"]["android"]}}}src', src + "_40")
+
+ # Сохраняем
+ tree.write(file_path, encoding="utf-8", xml_declaration=True, pretty_print=True)
+
+ return True
diff --git a/patches/compress.py b/patches/compress.py
index a534f6e..ae7d1b2 100644
--- a/patches/compress.py
+++ b/patches/compress.py
@@ -1,19 +1,18 @@
-"""
- Удаляет ненужное и сжимает ресурсы что-бы уменьшить размер АПК
+"""Удаляет ненужное и сжимает ресурсы что-бы уменьшить размер АПК
Эффективность на проверена на версии 9.0 Beta 7
разница = оригинальный размер апк - патченный размер апк, не учитывая другие патчи
-| Настройка | Размер файла | Разница | % |
-| :----------- | :-------------------: | :-----------------: | :-: |
-| None | 17092 bytes - 17.1 MB | - | - |
-| Compress PNG | 17072 bytes - 17.1 MB | 20 bytes - 0.0 MB | 0.11% |
-| Remove files | 17020 bytes - 17.0 MB | 72 bytes - 0.1 MB | 0.42% |
-| Remove draws | 16940 bytes - 16.9 MB | 152 bytes - 0.2 MB | 0.89% |
-| Remove lines | 16444 bytes - 16.4 MB | 648 bytes - 0.7 MB | 3.79% |
-| Remove ai vo | 15812 bytes - 15.8 MB | 1280 bytes - 1.3 MB | 7.49% |
-| Remove langs | 15764 bytes - 15.7 MB | 1328 bytes - 1.3 MB | 7.76% |
-| Все включены | 13592 bytes - 13.6 MB | 3500 bytes - 4.8 MB | 20.5% |
+| Настройка | Размер файла | Разница | % |
+| :--------------: | :-------------------: | :-----------------: | :-: |
+| Ничего | 17092 bytes - 17.1 MB | - | - |
+| Сжатие PNG | 17072 bytes - 17.1 MB | 20 bytes - 0.0 MB | 0.11% |
+| Удалить unknown | 17020 bytes - 17.0 MB | 72 bytes - 0.1 MB | 0.42% |
+| Удалить draws | 16940 bytes - 16.9 MB | 152 bytes - 0.2 MB | 0.89% |
+| Удалить lines | 16444 bytes - 16.4 MB | 648 bytes - 0.7 MB | 3.79% |
+| Удалить ai voice | 15812 bytes - 15.8 MB | 1280 bytes - 1.3 MB | 7.49% |
+| Удалить языки | 15764 bytes - 15.7 MB | 1328 bytes - 1.3 MB | 7.76% |
+| Все включены | 13592 bytes - 13.6 MB | 3500 bytes - 4.8 MB | 20.5% |
"compress": {
"enabled": true,
@@ -34,8 +33,8 @@ import os
import shutil
import subprocess
from tqdm import tqdm
-from typing import Dict, List, Any
from pydantic import Field
+from typing import Dict, List, Any
from utils.config import PatchConfig
from utils.smali_parser import get_smali_lines, save_smali_lines
diff --git a/patches/disable_ad.py b/patches/disable_ad.py
index 69f9a37..ffe7a90 100644
--- a/patches/disable_ad.py
+++ b/patches/disable_ad.py
@@ -1,5 +1,4 @@
-"""
- Удаляет баннеры рекламы
+"""Удаляет баннеры рекламы
"disable_ad": {
"enabled": true
diff --git a/patches/disable_beta_banner.py b/patches/disable_beta_banner.py
index 9ab00d9..08fbc97 100644
--- a/patches/disable_beta_banner.py
+++ b/patches/disable_beta_banner.py
@@ -1,5 +1,4 @@
-"""
- Удаляет баннеры бета-версии
+"""Удаляет баннеры бета-версии
"disable_beta_banner": {
"enabled": true
diff --git a/patches/insert_new.py b/patches/insert_new.py
deleted file mode 100644
index 552111f..0000000
--- a/patches/insert_new.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""
- Вставляет новые файлы в проект
-
-"insert_new": {
- "enabled": true
-}
-"""
-
-priority = 0
-
-# imports
-import os
-import shutil
-from typing import Dict, Any
-from utils.config import PatchConfig
-from utils.public import insert_after_public
-
-
-#Config
-class Config(PatchConfig): ...
-
-# Patch
-def apply(config: Config, base: Dict[str, Any]) -> bool:
- # Mod first launch window
- shutil.copy("./resources/avatar.png", "./decompiled/assets/avatar.png")
- shutil.copy(
- "./resources/OpenSans-Regular.ttf",
- "./decompiled/assets/OpenSans-Regular.ttf",
- )
- shutil.copytree(
- "./resources/smali_classes4/", "./decompiled/smali_classes4/"
- )
-
- return True
diff --git a/patches/package_name.py b/patches/package_name.py
index 1496995..ed160ff 100644
--- a/patches/package_name.py
+++ b/patches/package_name.py
@@ -1,5 +1,4 @@
-"""
- Изменяет имя пакета в apk, удаляет вход по google и vk
+"""Изменяет имя пакета в apk, удаляет вход по google и vk
"package_name": {
"enabled": true,
@@ -34,7 +33,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
file_path = os.path.join(root, filename)
if os.path.isfile(file_path):
- try:
+ try: # Изменяем имя пакета в файлах
with open(file_path, "r", encoding="utf-8") as file:
file_contents = file.read()
@@ -44,12 +43,16 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
new_contents = new_contents.replace(
"com/swiftsoft/anixartd",
config.package_name.replace(".", "/"),
+ ).replace(
+ "com/swiftsoft",
+ "/".join(config.package_name.split(".")[:2]),
)
with open(file_path, "w", encoding="utf-8") as file:
file.write(new_contents)
except:
pass
+ # Изменяем названия папок
if os.path.exists("./decompiled/smali/com/swiftsoft/anixartd"):
rename_dir(
"./decompiled/smali/com/swiftsoft/anixartd",
@@ -72,7 +75,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
os.path.join(
"./decompiled",
"smali_classes4",
- "/".join(config.package_name.split(".")[:-1]),
+ "/".join(config.package_name.split(".")[:2]),
),
)
@@ -85,6 +88,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
# ),
# )
+ # Замена названия пакета для smali_classes4
for root, dirs, files in os.walk("./decompiled/smali_classes4/"):
for filename in files:
file_path = os.path.join(root, filename)
@@ -103,6 +107,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
except:
pass
+ # Скрытие входа по Google и VK (НЕ РАБОТАЮТ В МОДАХ)
file_path = "./decompiled/res/layout/fragment_sign_in.xml"
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser)
diff --git a/patches/replace_navbar.py b/patches/replace_navbar.py
index 8d3f1be..e273458 100644
--- a/patches/replace_navbar.py
+++ b/patches/replace_navbar.py
@@ -29,6 +29,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
tree = etree.parse(file_path, parser)
root = tree.getroot()
+ # Получение элементов панели навигации
items = root.findall("item", namespaces=base['xml_ns'])
def get_id_suffix(item):
@@ -38,11 +39,13 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
items_by_id = {get_id_suffix(item): item for item in items}
existing_order = [get_id_suffix(item) for item in items]
+ # Размещение в новом порядке
ordered_items = []
for key in config.items:
if key in items_by_id:
ordered_items.append(items_by_id[key])
+ # Если есть не указанные в конфиге они помещаются в конец списка
extra = [i for i in items if get_id_suffix(i) not in config.items]
if extra:
tqdm.write("⚠Найдены лишние элементы: " + str([get_id_suffix(i) for i in extra]))
diff --git a/patches/selectable_text.py b/patches/selectable_text.py
index 029093e..3ff4511 100644
--- a/patches/selectable_text.py
+++ b/patches/selectable_text.py
@@ -1,5 +1,4 @@
-"""
- Делает текст в описании аниме копируемым
+"""Делает текст в описании аниме копируемым
"selectable_text": {
"enabled": true
@@ -37,7 +36,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
if f"{{{base['xml_ns']['android']}}}textIsSelectable" not in element.attrib:
element.set(f"{{{base['xml_ns']['android']}}}textIsSelectable", "true")
- # Сохраняем обратно
+ # Сохраняем
tree.write(file_path, encoding="utf-8", xml_declaration=True, pretty_print=True)
return True
diff --git a/patches/settings_urls.py b/patches/settings_urls.py
index 5f6dfd4..0ca43a0 100644
--- a/patches/settings_urls.py
+++ b/patches/settings_urls.py
@@ -1,5 +1,4 @@
-"""
- Добавляет в настройки ссылки и добвляет текст к версии приложения
+"""Добавляет в настройки ссылки и добвляет текст к версии приложения
"settings_urls": {
"enabled": true,
@@ -61,7 +60,7 @@ DEFAULT_MENU = {
"Прочее": [
{
"title": "Помочь проекту",
- "description": "Вы можете помочь нам в разработке мода, написании кода или тестировании.",
+ "description": "Вы можете помочь нам с идеями, написанием кода или тестированием.",
"url": "https://git.wowlikon.tech/anixart-mod",
"icon": "@drawable/ic_custom_crown",
"icon_space_reserved": "false"
@@ -94,6 +93,7 @@ def make_category(ns, name, items):
return cat
def apply(config: Config, base: Dict[str, Any]) -> bool:
+ # Добавление кастомных иконок
shutil.copy(
"./resources/ic_custom_crown.xml",
"./decompiled/res/drawable/ic_custom_crown.xml",
@@ -111,16 +111,16 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
tree = etree.parse(file_path, parser)
root = tree.getroot()
- # Insert new PreferenceCategory before the last element
- last = root[-1] # last element
- pos = root.index(last)
+ # Вставка новых пунктов перед последним
+ pos = root.index(root[-1])
for section, items in config.menu.items():
root.insert(pos, make_category(base["xml_ns"], section, items))
pos += 1
- # Save back
+ # Сохранение
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
+ # Добавление суффикса версии
filepaths = [
"./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/activity/UpdateActivity.smali",
"./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/fragment/main/preference/MainPreferenceFragment.smali",
diff --git a/patches/share_links.py b/patches/share_links.py
index bfc4620..5dbb1d3 100644
--- a/patches/share_links.py
+++ b/patches/share_links.py
@@ -1,5 +1,4 @@
-"""
- Изменяет формат "поделиться"
+"""Изменяет формат "поделиться"
"selectable_text": {
"enabled": true,
@@ -31,7 +30,7 @@ DEFAULT_FORMATS = {
}
class Config(PatchConfig):
- format: Dict[str, str] = Field(DEFAULT_FORMATS)
+ format: Dict[str, str] = Field(DEFAULT_FORMATS, description="Строки для замены в `strings.xml`")
# Patch
def apply(config: Config, base: Dict[str, Any]) -> bool:
@@ -42,6 +41,7 @@ def apply(config: Config, base: Dict[str, Any]) -> bool:
tree = etree.parse(file_path, parser)
root = tree.getroot()
+ # Обновляем значения
for string in root.findall("string"):
name = string.get("name")
if name in config.format:
diff --git a/patches/todo_custom_speed.py b/patches/todo_custom_speed.py
index 84859b8..d7f6f59 100644
--- a/patches/todo_custom_speed.py
+++ b/patches/todo_custom_speed.py
@@ -1,5 +1,4 @@
-"""
- Добавляет пользовательские скорости воспроизведения видео
+"""Добавляет пользовательские скорости воспроизведения видео
"custom_speed": {
"enabled": true,
diff --git a/patches/todo_example.py b/patches/todo_example.py
index d7922f6..ece6039 100644
--- a/patches/todo_example.py
+++ b/patches/todo_example.py
@@ -1,5 +1,4 @@
-"""
- Шаблон патча
+"""Шаблон патча
Здесь вы можете добавить описание патча, его назначение и другие детали.
diff --git a/patches/welcome.py b/patches/welcome.py
new file mode 100644
index 0000000..508f9d4
--- /dev/null
+++ b/patches/welcome.py
@@ -0,0 +1,56 @@
+"""Добавляет всплывающее окно при первом входе
+
+"welcome": {
+ "enabled": true,
+ "title": "Anixarty",
+ "description": "Описание",
+ "link_text": "МЫ В TELEGRAM",
+ "link_url": "https://t.me/http_teapod",
+ "skip_text": "Пропустить",
+ "title_bg_color": "#FFFFFF"
+}
+"""
+
+priority = 0
+
+# imports
+import os
+import shutil
+from pydantic import Field
+from typing import Dict, Any
+from utils.config import PatchConfig
+from utils.smali_parser import (
+ find_and_replace_smali_line,
+ get_smali_lines,
+ save_smali_lines
+)
+
+#Config
+class Config(PatchConfig):
+ title: str = Field("Anixarty", description="Заголовок")
+ description: str = Field("Описание", description="Описание")
+ link_text: str = Field("МЫ В TELEGRAM", description="Текст ссылки")
+ link_url: str = Field("https://t.me/http_teapod", description="Ссылка")
+ skip_text: str = Field("Пропустить", description="Текст кнопки пропуска")
+ title_bg_color: str = Field("#FFFFFF", description="Цвет фона заголовка")
+
+
+# Patch
+def apply(config: Config, base: Dict[str, Any]) -> bool:
+ # Добавление ресурсов окна первого входа
+ shutil.copy("./resources/avatar.png", "./decompiled/assets/avatar.png")
+ shutil.copy(
+ "./resources/OpenSans-Regular.ttf",
+ "./decompiled/assets/OpenSans-Regular.ttf",
+ )
+ shutil.copytree(
+ "./resources/smali_classes4/", "./decompiled/smali_classes4/"
+ )
+
+ file_path = "./decompiled/smali_classes2/com/swiftsoft/anixartd/ui/activity/MainActivity.smali"
+ method = "invoke-super {p0}, Lmoxy/MvpAppCompatActivity;->onResume()V"
+ lines = get_smali_lines(file_path)
+ lines = find_and_replace_smali_line(lines, method, method+"\ninvoke-static {p0}, Lcom/swiftsoft/about/$2;->oooooo(Landroid/content/Context;)Ljava/lang/Object;")
+ save_smali_lines(file_path, lines)
+
+ return True
diff --git a/resources/ic_chevron_down.xml b/resources/ic_chevron_down.xml
new file mode 100644
index 0000000..6da7775
--- /dev/null
+++ b/resources/ic_chevron_down.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/resources/ic_chevron_down_40.xml b/resources/ic_chevron_down_40.xml
new file mode 100644
index 0000000..087a2f1
--- /dev/null
+++ b/resources/ic_chevron_down_40.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/resources/ic_chevron_up.xml b/resources/ic_chevron_up.xml
new file mode 100644
index 0000000..066f1e6
--- /dev/null
+++ b/resources/ic_chevron_up.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/resources/ic_chevron_up_40.xml b/resources/ic_chevron_up_40.xml
new file mode 100644
index 0000000..e7ad29b
--- /dev/null
+++ b/resources/ic_chevron_up_40.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/utils/hex.py b/utils/hex.py
deleted file mode 100644
index 03f1f0a..0000000
--- a/utils/hex.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import struct
-
-
-def float_to_hex(f):
- b = struct.pack(">f", f)
- return b.hex()
diff --git a/utils/public.py b/utils/public.py
index c517384..1d663c2 100644
--- a/utils/public.py
+++ b/utils/public.py
@@ -1,8 +1,9 @@
-from lxml import etree
+from typing_extensions import Optional
from copy import deepcopy
+from lxml import etree
-def insert_after_public(anchor_name: str, elem_name: str):
+def insert_after_public(anchor_name: str, elem_name: str) -> Optional[int]:
file_path = "./decompiled/res/values/public.xml"
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser)
@@ -19,6 +20,8 @@ def insert_after_public(anchor_name: str, elem_name: str):
anchor = (elem, attrs)
types[attrs["type"]] = types.get(attrs["type"], []) + [int(attrs["id"], 16)]
+ assert anchor != None
+
free_ids = set()
group = types[anchor[1]["type"]]
for i in range(min(group), max(group) + 1):
@@ -47,7 +50,7 @@ def insert_after_public(anchor_name: str, elem_name: str):
return new_id
-def insert_after_id(anchor_name: str, elem_name: str):
+def insert_after_id(anchor_name: str, elem_name: str) -> None:
file_path = "./decompiled/res/values/ids.xml"
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser)
@@ -62,13 +65,15 @@ def insert_after_id(anchor_name: str, elem_name: str):
assert anchor == None
anchor = (elem, attrs)
+ assert anchor != None
+
new_elem = deepcopy(anchor[0])
new_elem.set("name", elem_name)
anchor[0].addnext(new_elem)
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
-def change_color(name: str, value: str):
+def change_color(name: str, value: str) -> None:
file_path = "./decompiled/res/values/colors.xml"
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser)
@@ -86,7 +91,8 @@ def change_color(name: str, value: str):
assert replacements >= 1
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
-def insert_after_color(anchor_name: str, elem_name: str, elem_value: str):
+
+def insert_after_color(anchor_name: str, elem_name: str, elem_value: str) -> None:
file_path = "./decompiled/res/values/colors.xml"
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(file_path, parser)
@@ -101,6 +107,8 @@ def insert_after_color(anchor_name: str, elem_name: str, elem_value: str):
assert anchor == None
anchor = (elem, attrs)
+ assert anchor != None
+
new_elem = deepcopy(anchor[0])
new_elem.set("name", elem_name)
anchor[0].addnext(new_elem)
diff --git a/utils/smali_parser.py b/utils/smali_parser.py
index 7cc6a12..8778d69 100644
--- a/utils/smali_parser.py
+++ b/utils/smali_parser.py
@@ -67,3 +67,11 @@ def find_and_replace_smali_line(
def float_to_hex(f):
b = struct.pack(">f", f)
return b.hex()
+
+
+def quick_replace(file: str) -> None:
+ content = ""
+ with open(file, "r", encoding="utf-8") as smali:
+ content = smali.read()
+ with open(file, "w", encoding="utf-8") as f:
+ f.writelines(content)
diff --git a/utils/tools.py b/utils/tools.py
index 9731e23..454751f 100644
--- a/utils/tools.py
+++ b/utils/tools.py
@@ -19,6 +19,7 @@ def ensure_dirs():
for d in [TOOLS, ORIGINAL, MODIFIED, DECOMPILED, PATCHES, CONFIGS]:
d.mkdir(exist_ok=True)
+
def run(console: Console, cmd: List[str], hide_output=True):
prog = local[cmd[0]][cmd[1:]]
try:
@@ -28,6 +29,7 @@ def run(console: Console, cmd: List[str], hide_output=True):
console.print(e.stderr)
raise typer.Exit(1)
+
def download(console: Console, url: str, dest: Path):
console.print(f"[cyan]Скачивание {url} → {dest.name}")