diff --git a/scripts/build.py b/scripts/build.py index 9a2980d..2b847cb 100644 --- a/scripts/build.py +++ b/scripts/build.py @@ -3,6 +3,7 @@ from .project_build import ProjectBuild from .project_copy import ProjectCopy from .project_end import ProjectEnd from .project_init import ProjectInit +from .project_proguard import ProjectProguard from .project_update import ProjectUpdate from .project_upload import ProjectUpload @@ -15,6 +16,7 @@ def run(context: Context): ProjectInit(context), ProjectCopy(context), ProjectUpdate(context), + ProjectProguard(context), ProjectBuild(context), ProjectUpload(context), ProjectEnd(context), diff --git a/scripts/context.py b/scripts/context.py index 9c3bc85..eea0b63 100644 --- a/scripts/context.py +++ b/scripts/context.py @@ -36,6 +36,8 @@ class Context: out_release_apk: str = "" out_release_aab: str = "" + proguard_dict: dict = None + @classmethod def from_json(cls, json_str: str): data = json.loads(json_str) diff --git a/scripts/project_proguard.py b/scripts/project_proguard.py new file mode 100644 index 0000000..bd84a85 --- /dev/null +++ b/scripts/project_proguard.py @@ -0,0 +1,114 @@ +import hashlib +import os +from pathlib import Path + +from scripts.context import Context +from scripts.task import Task +from utils import FileUtils + + +def string_to_md5(text): + # 将字符串编码为UTF-8字节 + text_bytes = text.encode('utf-8') + # 创建MD5哈希对象并更新字节数据 + md5_hash = hashlib.md5(text_bytes) + # 返回十六进制哈希字符串 + return md5_hash.hexdigest() + + +def generate_encryption_key(key: str, s_len: int = -1, target_package_name: str = "") -> str: + # if game_editor == "Cocos": + # return key + handle_key = target_package_name + key + target_package_name + processed_key = string_to_md5(handle_key) + while processed_key[0].isdigit(): + processed_key = processed_key[1:] # 移除首字符 + + # 计算目标长度 + if s_len > 0: + target_length = s_len + else: + target_length = len(key) + + # 取前N位(根据原始key长度),不足则全部保留 + # 如果处理后的key为空(极小概率),则返回空字符串 + return processed_key[:target_length] if processed_key else "" + + +layout_path = "LauncherCode/res/layout".replace("/", os.sep) +drawable_xxhdpi_path = "LauncherCode/res/drawable-xxhdpi".replace("/", os.sep) +drawable_path = "LauncherCode/res/drawable".replace("/", os.sep) +code_path = "LauncherCode/src".replace("/", os.sep) + +android_manifest_path = "lawnchair/AndroidManifest.xml".replace("/", os.sep) +xml_path = "lawnchair/res/xml".replace("/", os.sep) +launcher_code_path = "lawnchair/src".replace("/", os.sep) +src_code_path = "src".replace("/", os.sep) +launchercode = "launchercode" + + +class ProjectProguard(Task): + def __init__(self, context: Context): + super().__init__(context) + self.context.proguard_dict = { + launchercode: generate_encryption_key(launchercode, target_package_name=self.context.package_name)} + + def add_proguard_key(self, key: str) -> str: + value = self.context.proguard_dict.get(key) + if value: + return value + value = generate_encryption_key(key, target_package_name=self.context.package_name) + self.context.proguard_dict[key] = value + return value + + def rename_files_in_dir(self, dir_path: str): + for root, dirs, files in os.walk(dir_path): + for file in files: + file_path = os.path.join(root, file) + name = Path(file).stem + add_proguard_key = self.add_proguard_key(name) + FileUtils.move(file_path, + os.path.join(root, file.replace(name, add_proguard_key)) + .replace(launchercode, self.context.proguard_dict.get(launchercode)), + overwrite=True) + + def update(self, file_path: str): + text = open(file_path, "r", encoding="UTF-8").read() + for key, value in self.context.proguard_dict.items(): + text = text.replace(key, value) + pass + open(file_path, "w", encoding="UTF-8").write(text) + + def update_proguard(self, dir_path: str): + if os.path.isdir(dir_path): + for root, dirs, files in os.walk(dir_path): + for file in files: + if file.endswith(".xml") or file.endswith(".java") or file.endswith(".kt"): + file_path = os.path.join(root, file) + self.update(file_path) + else: + self.update(dir_path) + pass + + def execute(self): + self.rename_files_in_dir(os.path.join(self.context.temp_project_path, layout_path)) + self.rename_files_in_dir(os.path.join(self.context.temp_project_path, drawable_xxhdpi_path)) + self.rename_files_in_dir(os.path.join(self.context.temp_project_path, drawable_path)) + self.rename_files_in_dir(os.path.join(self.context.temp_project_path, code_path)) + + self.context.proguard_dict = { + k: v for k, v in sorted( + self.context.proguard_dict.items(), + key=lambda item: len(item[0]), + reverse=True + ) + } + self.update_proguard(os.path.join(self.context.temp_project_path, layout_path)) + self.update_proguard(os.path.join(self.context.temp_project_path, drawable_xxhdpi_path)) + self.update_proguard(os.path.join(self.context.temp_project_path, drawable_path)) + self.update_proguard(os.path.join(self.context.temp_project_path, code_path)) + self.update_proguard(os.path.join(self.context.temp_project_path, android_manifest_path)) + self.update_proguard(os.path.join(self.context.temp_project_path, xml_path)) + self.update_proguard(os.path.join(self.context.temp_project_path, launcher_code_path)) + self.update_proguard(os.path.join(self.context.temp_project_path, src_code_path)) + pass diff --git a/utils/file_utils.py b/utils/file_utils.py index 19c5eb5..e543fb7 100644 --- a/utils/file_utils.py +++ b/utils/file_utils.py @@ -273,6 +273,60 @@ class FileUtils: md5.update(chunk) return md5.hexdigest() + @staticmethod + def move( + src: Union[str, Path], + dst: Union[str, Path], + overwrite: bool = False, + ignore_patterns: Optional[List[str]] = None, + create_parents: bool = True + ) -> Optional[bool]: + """ + 移动文件或文件夹 + + :param create_parents: + :param src: 源路径 + :param dst: 目标路径 + :param overwrite: 是否覆盖已存在文件 + :param ignore_patterns: 忽略的文件模式列表 (如 ['*.tmp', '*.log']) + :return: 是否成功 + + 示例: + >>> FileUtils.move('a.txt', 'dir/b.txt') # 移动并重命名文件 + >>> FileUtils.move('dir1', 'dir2') # 移动文件夹 + """ + src, dst = Path(src), Path(dst) + + try: + # 处理目标已存在的情况 + if dst.exists(): + if not overwrite: + return False + FileUtils.delete(dst) # 先删除目标 + + if create_parents: + if src.is_file(): + dst.parent.mkdir(parents=True, exist_ok=True) + elif src.is_dir(): + dst.mkdir(parents=True, exist_ok=True) + + # 移动文件 + if src.is_file(): + shutil.move(str(src), str(dst)) + return True + + # 移动文件夹(带忽略模式) + elif src.is_dir(): + # 先拷贝再删除源目录 + if FileUtils.copy(src, dst, overwrite, ignore_patterns): + FileUtils.delete(src, recursive=True) + return True + return False + + except Exception as e: + print(f"移动失败: {e}") + return False + # 使用示例 if __name__ == "__main__":