Compare commits
11 Commits
alpha-v0.0.1
...
v0.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 9453b3b50b | |||
| 48ea732d77 | |||
| 62e23a2eb0 | |||
| 5986d8b069 | |||
| cc49aad2aa | |||
| d6f616da7a | |||
| 3b2e5bee18 | |||
| 8a74245c9c | |||
| 0f53c836ae | |||
| d0744050d2 | |||
| 8f30061d44 |
@@ -0,0 +1,65 @@
|
||||
name: Build mod
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
#schedule: # раз в 36 часов
|
||||
# - cron: "0 0 */3 * *" # каждые 3 дня в 00:00
|
||||
# - cron: "0 12 */3 * *" # каждые 3 дня в 12:00
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download APK
|
||||
run: |
|
||||
curl -L -o app.apk "https://mirror-dl.anixart-app.com/anixart-beta.apk"
|
||||
|
||||
- name: Ensure aapt is installed
|
||||
run: |
|
||||
if ! command -v aapt &> /dev/null; then
|
||||
echo "aapt не найден, устанавливаем..."
|
||||
sudo apt-get update && sudo apt-get install -y --no-install-recommends android-sdk-build-tools
|
||||
fi
|
||||
|
||||
- name: Export secrets
|
||||
env:
|
||||
KEYSTORE: ${{ secrets.KEYSTORE }}
|
||||
KEYSTORE_PASS: ${{ secrets.KEYSTORE_PASS }}
|
||||
run: |
|
||||
# Export so later steps can reference them
|
||||
echo "$KEYSTORE" | base64 -d > keystore.jks
|
||||
echo "$KEYSTORE_PASS" > keystore.pass
|
||||
|
||||
- name: Build APK
|
||||
id: build
|
||||
run: |
|
||||
mkdir original
|
||||
mv app.apk original/
|
||||
python ./main.py -f
|
||||
|
||||
- name: Read title from report.log
|
||||
id: get_title
|
||||
run: |
|
||||
TITLE=$(head -n 1 modified/report.log)
|
||||
echo "title=${TITLE}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup go
|
||||
if: steps.build.outputs.BUILD_EXIT == '0'
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '>=1.20'
|
||||
|
||||
- name: Make release
|
||||
if: steps.build.outputs.BUILD_EXIT == '0'
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
title: ${{ steps.get_title.outputs.title }}
|
||||
body_path: modified/report.log
|
||||
draft: true
|
||||
api_key: '${{secrets.RELEASE_TOKEN}}'
|
||||
files: |-
|
||||
modified/**-mod.apk
|
||||
modified/report.log
|
||||
Vendored
+2
@@ -5,3 +5,5 @@ tools
|
||||
|
||||
__pycache__
|
||||
.venv
|
||||
*.jks
|
||||
*.pass
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
### Структура проекта:
|
||||
- `main.py` Главный файл
|
||||
- `patches` Модули патчей
|
||||
@@ -13,6 +14,37 @@
|
||||
- `patches/resources` Ресурсы, используемые патчами
|
||||
- `todo_drafts` Заметки для новых патчей(можно в любом формате)
|
||||
|
||||
### Схема
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Процесс модифицирования приложения
|
||||
---
|
||||
|
||||
flowchart TD
|
||||
A([Оригинальный apk]) f1@==> B[поиск и выбор apk]
|
||||
|
||||
B f2@==> p[Декомпиляция]
|
||||
|
||||
subgraph p["Применение патчей по возрастанию приоритета"]
|
||||
C[Патч 1] --> D
|
||||
D[Патч 2] --...--> E[Патч n]
|
||||
end
|
||||
|
||||
p f3@==> F[Сборка apk обратно]
|
||||
F f4@==> G[Выравнивание zipaling]
|
||||
G f5@==> H[Подпись V2+V3]
|
||||
|
||||
H f6@==> I([Модифицированый apk])
|
||||
|
||||
f1@{ animate: true }
|
||||
f2@{ animate: true }
|
||||
f3@{ animate: true }
|
||||
f4@{ animate: true }
|
||||
f5@{ animate: true }
|
||||
f6@{ animate: true }
|
||||
```
|
||||
|
||||
### Установка и использование:
|
||||
|
||||
1. Клонируйте репозиторий:
|
||||
|
||||
@@ -3,16 +3,17 @@
|
||||
"apktool_jar_url": "https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.12.0.jar",
|
||||
"apktool_wrapper_url": "https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool"
|
||||
},
|
||||
"new_package_name": "com.wowlikon.anixart",
|
||||
"new_package_name": "com.wowlikon.anixart2",
|
||||
"server": "https://anixarty.wowlikon.tech/modding",
|
||||
"theme": {
|
||||
"colors": {
|
||||
"primary": "#ccff00",
|
||||
"secondary": "#ffffd700",
|
||||
"background": "#FFFFFF",
|
||||
"background": "#ffffff",
|
||||
"text": "#000000"
|
||||
},
|
||||
"gradient": {
|
||||
"angle": "135.0",
|
||||
"from": "#ffff6060",
|
||||
"to": "#ffccff00"
|
||||
}
|
||||
@@ -29,10 +30,17 @@
|
||||
"icon": "@drawable/ic_custom_telegram",
|
||||
"icon_space_reserved": "false"
|
||||
},
|
||||
{
|
||||
"title": "Kentai Radiquum",
|
||||
"description": "Разработчик",
|
||||
"url": "https://t.me/radiquum",
|
||||
"icon": "@drawable/ic_custom_telegram",
|
||||
"icon_space_reserved": "false"
|
||||
},
|
||||
{
|
||||
"title": "Мы в Telegram",
|
||||
"description": "Подпишитесь на канал, чтобы быть в курсе последних новостей.",
|
||||
"url": "https://t.me/wowlikon",
|
||||
"url": "https://t.me/http_teapod",
|
||||
"icon": "@drawable/ic_custom_telegram",
|
||||
"icon_space_reserved": "false"
|
||||
}
|
||||
@@ -51,5 +59,5 @@
|
||||
"android": "http://schemas.android.com/apk/res/android",
|
||||
"app": "http://schemas.android.com/apk/res-auto"
|
||||
},
|
||||
"speeds": [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 9.0]
|
||||
"speeds": [9.0]
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import yaml
|
||||
import requests
|
||||
import argparse
|
||||
import colorama
|
||||
import importlib
|
||||
import traceback
|
||||
import subprocess
|
||||
from tqdm import tqdm
|
||||
|
||||
@@ -12,7 +15,7 @@ def init() -> dict:
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
with open("./patches/config.json", "r") as config_file:
|
||||
with open("./config.json", "r") as config_file:
|
||||
conf = json.load(config_file)
|
||||
|
||||
if not os.path.exists("./tools/apktool.jar"):
|
||||
@@ -61,6 +64,11 @@ def select_apk() -> str:
|
||||
print("Нет файлов .apk в текущей директории")
|
||||
sys.exit(1)
|
||||
|
||||
if len(apks) == 1:
|
||||
apk = apks[0]
|
||||
print(f"Выбран файл {apk}")
|
||||
return apk
|
||||
|
||||
while True:
|
||||
print("Выберете файл для модификации")
|
||||
for index, apk in enumerate(apks):
|
||||
@@ -101,6 +109,53 @@ def decompile_apk(apk: str):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def compile_apk(apk: str):
|
||||
print("Компилируем apk...")
|
||||
try:
|
||||
subprocess.run(
|
||||
"tools/apktool b decompiled -o " + os.path.join("modified", apk),
|
||||
shell=True,
|
||||
check=True,
|
||||
text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
subprocess.run(
|
||||
"zipalign -v 4 " + os.path.join("modified", apk) + " " + os.path.join("modified", apk.replace(".apk", "-aligned.apk")),
|
||||
shell=True,
|
||||
check=True,
|
||||
text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
subprocess.run(
|
||||
"apksigner sign " +
|
||||
"--v1-signing-enabled false " +
|
||||
"--v2-signing-enabled true " +
|
||||
"--v3-signing-enabled true " +
|
||||
"--ks keystore.jks " +
|
||||
"--ks-pass file:keystore.pass " +
|
||||
"--out " + os.path.join("modified", apk.replace(".apk", "-mod.apk")) +
|
||||
" " + os.path.join("modified", apk.replace(".apk", "-aligned.apk")),
|
||||
shell=True,
|
||||
check=True,
|
||||
text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
title = "anixart mod "
|
||||
with open('./decompiled/apktool.yml') as f:
|
||||
package = yaml.safe_load(f)
|
||||
title += ' '.join([f'{k}: {v}' for k, v in package['versionInfo'].items()])
|
||||
with open("./modified/report.log", "w") as log_file:
|
||||
log_file.write(title+'\n')
|
||||
log_file.write("\n".join([f"{patch.name}: {'applied' if patch.applied else 'failed'}" for patch in patches]))
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("Ошибка при выполнении команды:")
|
||||
print(e.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Patch:
|
||||
def __init__(self, name, pkg):
|
||||
self.name = name
|
||||
@@ -117,17 +172,34 @@ class Patch:
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Ошибка при применении патча {self.name}: {e}")
|
||||
print(e.args)
|
||||
print(type(e), e.args)
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Автоматический патчер anixart"
|
||||
)
|
||||
|
||||
parser.add_argument("-v", "--verbose",
|
||||
action="store_true",
|
||||
help="Выводить подробные сообщения")
|
||||
|
||||
parser.add_argument("-f", "--force",
|
||||
action="store_true",
|
||||
help="Принудительно собрать APK")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
conf = init()
|
||||
apk = select_apk()
|
||||
patch = decompile_apk(apk)
|
||||
|
||||
if args.verbose: conf["verbose"] = True
|
||||
|
||||
patches = []
|
||||
for filename in os.listdir("patches/"):
|
||||
if filename.endswith(".py") and filename != "__init__.py":
|
||||
if filename.endswith(".py") and filename != "__init__.py" and not filename.startswith("todo_"):
|
||||
module_name = filename[:-3]
|
||||
module = importlib.import_module(f"patches.{module_name}")
|
||||
patches.append(Patch(module_name, module))
|
||||
@@ -145,8 +217,14 @@ for patch in patches:
|
||||
print(f"{marker}{colorama.Style.RESET_ALL} {patch.name}")
|
||||
|
||||
if all(statuses.values()):
|
||||
print("Все патчи успешно применены")
|
||||
print(f"{colorama.Fore.GREEN}Все патчи успешно применены{colorama.Style.RESET_ALL}")
|
||||
compile_apk(apk)
|
||||
elif any(statuses.values()):
|
||||
print("Некоторые патчи не были успешно применены")
|
||||
print(f"{colorama.Fore.YELLOW}⚠{colorama.Style.RESET_ALL} Некоторые патчи не были успешно применены")
|
||||
if args.force or input("Продолжить? (y/n): ").lower() == "y":
|
||||
compile_apk(apk)
|
||||
else:
|
||||
print("Ни один патч не был успешно применен")
|
||||
print(colorama.Fore.RED + "Операция отменена" + colorama.Style.RESET_ALL)
|
||||
else:
|
||||
print(f"{colorama.Fore.RED}Ни один патч не был успешно применен{colorama.Style.RESET_ALL}")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -5,11 +5,36 @@ from tqdm import tqdm
|
||||
import json
|
||||
import requests
|
||||
|
||||
|
||||
def apply(config: dict) -> bool:
|
||||
response = requests.get(config['server'])
|
||||
if response.status_code == 200:
|
||||
for item in json.loads(response.text)["modding"]:
|
||||
tqdm.write(item)
|
||||
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']:
|
||||
tqdm.write(f"Изменение {item['file']}")
|
||||
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/network/api/'+item['file']
|
||||
with open(filepath, 'r') as f:
|
||||
content = f.read()
|
||||
with open(filepath, 'w') as f:
|
||||
if content.count(item['src']) == 0:
|
||||
tqdm.write(f"⚠ Не найдено {item['src']}")
|
||||
f.write(content.replace(item['src'], item['dst']))
|
||||
|
||||
tqdm.write(f"Изменение Github ссылки")
|
||||
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/utils/anixnet/GithubPagesNetFetcher.smali'
|
||||
with open(filepath, 'r') as f:
|
||||
content = f.read()
|
||||
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("Удаление динамического выбора сервера")
|
||||
filepath = './decompiled/smali_classes2/com/swiftsoft/anixartd/DaggerApp_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.smali'
|
||||
with open(filepath, 'r') as f:
|
||||
for line in f.readlines():
|
||||
if "addInterceptor" in line: continue
|
||||
content += line
|
||||
with open(filepath, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
return True
|
||||
tqdm.write(f"Failed to fetch data {response.status_code} {response.text}")
|
||||
return False
|
||||
|
||||
@@ -5,15 +5,18 @@ from tqdm import tqdm
|
||||
import os
|
||||
import shutil
|
||||
|
||||
|
||||
def apply(config: dict) -> bool:
|
||||
for item in os.listdir("./decompiled/unknown/"):
|
||||
item_path = os.path.join("./decompiled/unknown/", item)
|
||||
|
||||
if os.path.isfile(item_path):
|
||||
os.remove(item_path)
|
||||
if config.get("verbose", False):
|
||||
tqdm.write(f'Удалён файл: {item_path}')
|
||||
elif os.path.isdir(item_path):
|
||||
if item not in config["cleanup"]["keep_dirs"]:
|
||||
shutil.rmtree(item_path)
|
||||
if config.get("verbose", False):
|
||||
tqdm.write(f'Удалена папка: {item_path}')
|
||||
return True
|
||||
|
||||
+57
-2
@@ -1,12 +1,19 @@
|
||||
"""Change application theme"""
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from utils.public import (
|
||||
insert_after_public,
|
||||
insert_after_color,
|
||||
insert_after_id,
|
||||
change_color,
|
||||
)
|
||||
|
||||
def apply(config: dict) -> bool:
|
||||
main_color = config["theme"]["colors"]["primary"]
|
||||
splash_color = config["theme"]["colors"]["secondary"]
|
||||
gradient_angle = config["theme"]["gradient"]["angle"]
|
||||
gradient_from = config["theme"]["gradient"]["from"]
|
||||
gradient_to = config["theme"]["gradient"]["to"]
|
||||
|
||||
@@ -31,6 +38,7 @@ def apply(config: dict) -> bool:
|
||||
root = tree.getroot()
|
||||
|
||||
# Change attributes with namespace
|
||||
root.set(f"{{{config['xml_ns']['android']}}}angle", gradient_angle)
|
||||
root.set(f"{{{config['xml_ns']['android']}}}startColor", gradient_from)
|
||||
root.set(f"{{{config['xml_ns']['android']}}}endColor", gradient_to)
|
||||
|
||||
@@ -45,7 +53,7 @@ def apply(config: dict) -> bool:
|
||||
root = tree.getroot()
|
||||
|
||||
# Finding "path"
|
||||
for el in root.findall("path", namespaces=config['xml_ns']):
|
||||
for el in root.findall("path", namespaces=config["xml_ns"]):
|
||||
name = el.get(f"{{{config['xml_ns']['android']}}}name")
|
||||
if name == "path":
|
||||
el.set(f"{{{config['xml_ns']['android']}}}fillColor", splash_color)
|
||||
@@ -53,4 +61,51 @@ def apply(config: dict) -> bool:
|
||||
# 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"]:
|
||||
file_path = f"./decompiled/res/drawable-v24/{filename}.xml"
|
||||
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
tree = etree.parse(file_path, parser)
|
||||
root = tree.getroot()
|
||||
|
||||
# Change attributes with namespace
|
||||
root.set(f"{{{config['xml_ns']['android']}}}angle", gradient_angle)
|
||||
items = root.findall("item", namespaces=config['xml_ns'])
|
||||
assert len(items) == 2
|
||||
items[0].set(f"{{{config['xml_ns']['android']}}}color", gradient_from)
|
||||
items[1].set(f"{{{config['xml_ns']['android']}}}color", gradient_to)
|
||||
|
||||
# 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:])
|
||||
change_color("accent_alpha_70", main_color[0]+'b3'+main_color[1:])
|
||||
change_color("colorAccent", main_color[0]+'ff'+main_color[1:])
|
||||
change_color("link_color", main_color[0]+'ff'+main_color[1:])
|
||||
change_color("link_color_alpha_70", main_color[0]+'b3'+main_color[1:])
|
||||
change_color("refresh_progress", main_color[0]+'ff'+main_color[1:])
|
||||
|
||||
change_color("ic_launcher_background", "#ff000000")
|
||||
change_color("bottom_nav_indicator_active", "#ffffffff")
|
||||
change_color("bottom_nav_indicator_icon_checked", main_color[0]+'ff'+main_color[1:])
|
||||
change_color("bottom_nav_indicator_label_checked", main_color[0]+'ff'+main_color[1:])
|
||||
|
||||
insert_after_public("warning_error_counter_background", "ic_custom_telegram")
|
||||
insert_after_public("warning_error_counter_background", "ic_custom_crown")
|
||||
|
||||
try:
|
||||
last = "speed75"
|
||||
for speed in config.get("speeds", []):
|
||||
insert_after_public(last, f"speed{int(float(speed)*10)}")
|
||||
insert_after_id(last, f"speed{int(float(speed)*10)}")
|
||||
last = f"speed{int(float(speed)*10)}"
|
||||
except Exception as e:
|
||||
print(f"Error occurred while processing speeds: {e}")
|
||||
return True
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Compress PNGs"""
|
||||
|
||||
priority = -1
|
||||
from tqdm import tqdm
|
||||
|
||||
@@ -7,15 +6,15 @@ import os
|
||||
import subprocess
|
||||
|
||||
|
||||
def compress_pngs(root_dir):
|
||||
def compress_pngs(root_dir: str, verbose: bool = False):
|
||||
compressed_files = []
|
||||
for dirpath, _, filenames in os.walk(root_dir):
|
||||
for filename in filenames:
|
||||
if filename.lower().endswith(".png"):
|
||||
filepath = os.path.join(dirpath, filename)
|
||||
tqdm.write(f"Сжимаю: {filepath}")
|
||||
if verbose: tqdm.write(f"Сжимаю: {filepath}")
|
||||
try:
|
||||
subprocess.run(
|
||||
assert subprocess.run(
|
||||
[
|
||||
"pngquant",
|
||||
"--force",
|
||||
@@ -24,9 +23,8 @@ def compress_pngs(root_dir):
|
||||
"--quality=65-90",
|
||||
filepath,
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
).returncode in [0, 99]
|
||||
compressed_files.append(filepath)
|
||||
except subprocess.CalledProcessError as e:
|
||||
tqdm.write(f"Ошибка при сжатии {filepath}: {e}")
|
||||
@@ -34,5 +32,5 @@ def compress_pngs(root_dir):
|
||||
|
||||
|
||||
def apply(config: dict) -> bool:
|
||||
files = compress_pngs("./decompiled")
|
||||
files = compress_pngs("./decompiled", config.get("verbose", False))
|
||||
return len(files) > 0 and any(files)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Disable ad banners"""
|
||||
|
||||
priority = 0
|
||||
|
||||
from utils.smali_parser import (
|
||||
@@ -9,6 +8,7 @@ from utils.smali_parser import (
|
||||
replace_smali_method_body,
|
||||
)
|
||||
|
||||
|
||||
replace = """ .locals 0
|
||||
|
||||
const/4 p0, 0x1
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
"""Remove beta banner"""
|
||||
|
||||
priority = 0
|
||||
import os
|
||||
from tqdm import tqdm
|
||||
from lxml import etree
|
||||
|
||||
from typing import TypedDict
|
||||
import os
|
||||
from lxml import etree
|
||||
|
||||
|
||||
def apply(config) -> bool:
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Insert new files"""
|
||||
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
import shutil
|
||||
import os
|
||||
|
||||
+11
-2
@@ -1,9 +1,8 @@
|
||||
"""Change package name of apk"""
|
||||
|
||||
priority = -1
|
||||
from tqdm import tqdm
|
||||
|
||||
import os
|
||||
from lxml import etree
|
||||
|
||||
|
||||
def rename_dir(src, dst):
|
||||
@@ -88,6 +87,16 @@ def apply(config: dict) -> bool:
|
||||
except:
|
||||
pass
|
||||
|
||||
file_path = "./decompiled/res/layout/fragment_sign_in.xml"
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
tree = etree.parse(file_path, parser)
|
||||
root = tree.getroot()
|
||||
|
||||
last_linear = root.xpath("//LinearLayout/LinearLayout[4]")[0]
|
||||
last_linear.set(f"{{{config['xml_ns']['android']}}}visibility", "gone")
|
||||
|
||||
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"""Add new settings"""
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
from lxml import etree
|
||||
|
||||
# Generate PreferenceCategory
|
||||
|
||||
def make_category(ns, name, items):
|
||||
cat = etree.Element("PreferenceCategory", nsmap=ns)
|
||||
cat.set(f"{{{ns['android']}}}title", name)
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
"""Change application icon"""
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
import time
|
||||
|
||||
def apply(config: dict) -> bool:
|
||||
time.sleep(0.2)
|
||||
return False
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Change application icon"""
|
||||
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
import struct
|
||||
|
||||
+65
-7
@@ -2,15 +2,15 @@ from lxml import etree
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
def insert_after(anchor_name: str, elem_name: str):
|
||||
file_path = "../decompiled/res/values/public.xml"
|
||||
def insert_after_public(anchor_name: str, elem_name: str):
|
||||
file_path = "./decompiled/res/values/public.xml"
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
tree = etree.parse(file_path, parser)
|
||||
root = tree.getroot()
|
||||
anchor = None
|
||||
types = {}
|
||||
|
||||
for idx, elem in enumerate(root):
|
||||
for elem in root:
|
||||
assert elem.tag == "public"
|
||||
assert elem.keys() == ["type", "name", "id"]
|
||||
attrs = dict(zip(elem.keys(), elem.values()))
|
||||
@@ -25,7 +25,6 @@ def insert_after(anchor_name: str, elem_name: str):
|
||||
if i not in group:
|
||||
free_ids.add(i)
|
||||
|
||||
assert len(free_ids) > 0
|
||||
new_id = None
|
||||
for i in free_ids:
|
||||
if i > int(anchor[1]["id"], 16):
|
||||
@@ -38,12 +37,71 @@ def insert_after(anchor_name: str, elem_name: str):
|
||||
if name == anchor[1]["type"]:
|
||||
continue
|
||||
if new_id in group:
|
||||
new_id = max(group)
|
||||
assert False, f"ID {new_id} already exists in group {name}"
|
||||
|
||||
new_elem = deepcopy(anchor[0])
|
||||
new_elem.set("id", new_id)
|
||||
new_elem.set("id", hex(new_id))
|
||||
new_elem.set("name", elem_name)
|
||||
anchor[0].addnext(new_elem)
|
||||
|
||||
tree.write(file_path, pretty_print=True, xml_declaration=True, encoding="utf-8")
|
||||
return new_id
|
||||
|
||||
|
||||
def insert_after_id(anchor_name: str, elem_name: str):
|
||||
file_path = "./decompiled/res/values/ids.xml"
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
tree = etree.parse(file_path, parser)
|
||||
root = tree.getroot()
|
||||
anchor = None
|
||||
|
||||
for elem in root:
|
||||
assert elem.tag == "item"
|
||||
assert elem.keys() == ["type", "name"]
|
||||
attrs = dict(zip(elem.keys(), elem.values()))
|
||||
if attrs["name"] == anchor_name:
|
||||
assert anchor == None
|
||||
anchor = (elem, attrs)
|
||||
|
||||
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):
|
||||
file_path = "./decompiled/res/values/colors.xml"
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
tree = etree.parse(file_path, parser)
|
||||
root = tree.getroot()
|
||||
replacements = 0
|
||||
|
||||
for elem in root:
|
||||
assert elem.tag == "color"
|
||||
assert elem.keys() == ["name"]
|
||||
attrs = dict(zip(elem.keys(), elem.values()))
|
||||
if attrs["name"] == name:
|
||||
elem.set("name", name)
|
||||
elem.text = value
|
||||
replacements += 1
|
||||
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):
|
||||
file_path = "./decompiled/res/values/colors.xml"
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
tree = etree.parse(file_path, parser)
|
||||
root = tree.getroot()
|
||||
anchor = None
|
||||
|
||||
for elem in root:
|
||||
assert elem.tag == "color"
|
||||
assert elem.keys() == ["name"]
|
||||
attrs = dict(zip(elem.keys(), elem.values()))
|
||||
if attrs["name"] == anchor_name:
|
||||
assert anchor == None
|
||||
anchor = (elem, attrs)
|
||||
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user