Files
Firefly_Apk_Tools/page1.py
2025-06-01 21:45:11 +07:00

225 lines
8.8 KiB
Python

import customtkinter as ctk
from tkinter import filedialog
import os
import subprocess
import threading
CREATE_NO_WINDOW = 0x08000000
class Page1:
def __init__(self, parent):
self.parent = parent
self.frame = ctk.CTkFrame(parent)
self.frame.pack(fill=ctk.BOTH, expand=True)
self.work_dir = "apk_work"
rootPath = os.path.dirname(os.path.abspath(__file__))
self.apktool_jar_path = os.path.join(rootPath, "apktool_2.11.1.jar")
self.uber_jar_path = os.path.join(rootPath, "uber-apk-signer.jar")
self.jdk_path = os.path.join(rootPath, r"jdk-21.0.7\bin\java.exe")
self.apk_path = ""
ctk.CTkLabel(self.frame, text="Page 1: Extract and compress APK").pack(pady=10)
file_frame = ctk.CTkFrame(self.frame)
file_frame.pack(pady=5)
self.label_apk = ctk.CTkLabel(file_frame, text="Not selected APK file")
self.label_apk.pack(pady=3)
self.btn_choose_apk = ctk.CTkButton(file_frame, text="Choose APK file", command=self.choose_apk)
self.btn_choose_apk.pack(pady=3)
self.btn_extract = ctk.CTkButton(file_frame, text="Extract APK", command=self.extract_apk, state=ctk.DISABLED)
self.btn_extract.pack(pady=10)
name_frame = ctk.CTkFrame(self.frame)
name_frame.pack(pady=10)
ctk.CTkLabel(name_frame, text="APK output name:").pack(side=ctk.LEFT)
self.name_entry = ctk.CTkEntry(name_frame, width=400)
self.name_entry.insert(0, "StarRail")
self.name_entry.pack(side=ctk.LEFT, padx=5)
self.btn_compress = ctk.CTkButton(self.frame, text="Compress and sign APK", command=self.compress_apk)
self.btn_compress.pack(pady=5)
self.loading_label = ctk.CTkLabel(self.frame, text="")
self.loading_label.pack(pady=5)
self.log_output = ctk.CTkTextbox(self.frame, width=600, height=200, corner_radius=0)
self.log_output.pack(pady=5)
def compress_apk(self):
if not self.work_dir:
self.log_output.insert(ctk.END, "Not found work directory\n")
return
apk_name = self.name_entry.get()
if not apk_name:
self.log_output.insert(ctk.END, "Not found apk name\n")
return
self.loading_label.configure(text="Compressing APK...")
self.btn_compress.configure(state=ctk.DISABLED)
self.log_output.insert(ctk.END, f"Compressing APK {apk_name}...\n")
threading.Thread(target=self.compress_apk_thread, daemon=True, args=(apk_name,)).start()
def compress_apk_thread(self, apk_name):
try:
decompiled_dir = os.path.join(self.work_dir)
compressed_apk = f"{apk_name}.apk"
cmd = [self.jdk_path,
"-jar", self.apktool_jar_path,
"b", decompiled_dir,
"-o", compressed_apk]
self.log_output.insert(ctk.END, "Running apktool to compress...\n")
process = subprocess.Popen(cmd, creationflags=CREATE_NO_WINDOW, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
while True:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
break
if output:
self.log(output.strip())
rc = process.poll()
if rc != 0:
self.log(f"Error when compress APK: Process returned code {rc}")
return False
self.log("Signing APK using Uber...")
if not os.path.exists(self.uber_jar_path):
self.log(f"Error: Cannot find uber-apk-signer at {self.uber_jar_path}")
self.log("Please make sure uber-apk-signer.jar is in the correct location")
return False
uber_cmd = [self.jdk_path,
"-jar", self.uber_jar_path,
"-a", compressed_apk]
process = subprocess.Popen(uber_cmd, creationflags=CREATE_NO_WINDOW, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
while True:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
break
if output:
self.log(output.strip())
rc = process.poll()
if rc != 0:
self.log(f"Error when sign APK: Process returned code {rc}")
return False
signed_apk = f"{apk_name}-aligned-debugSigned.apk"
signed_id = f"{apk_name}-aligned-debugSigned.apk.idsig"
if os.path.exists(signed_apk):
if os.path.exists(compressed_apk):
os.remove(compressed_apk)
os.rename(signed_apk, compressed_apk)
self.log(f"Successfully renamed signed APK to {compressed_apk}")
else:
self.log("Warning: Signed APK not found")
if os.path.exists(signed_id):
os.remove(signed_id)
self.loading_label.configure(text="Completed!")
self.log("Completed compressing and signing APK!")
except Exception as e:
self.log(f"Error: {str(e)}")
finally:
self.btn_compress.configure(state=ctk.NORMAL)
def choose_apk(self):
file_path = filedialog.askopenfilename(
title="Choose APK file",
filetypes=[("APK files", "*.apk")]
)
if file_path:
self.apk_path = file_path
self.label_apk.configure(text=f"File APK: {file_path}")
self.btn_extract.configure(state=ctk.NORMAL)
self.log(f"Selected APK file: {file_path}")
def extract_apk(self):
threading.Thread(target=self.extract_apk_thread, daemon=True).start()
def extract_apk_thread(self):
if not self.apk_path:
self.log_output.insert(ctk.END, "Not found apk path\n")
return
try:
self.loading_label.configure(text="Extracting APK...")
self.btn_extract.configure(state=ctk.DISABLED)
self.btn_choose_apk.configure(state=ctk.DISABLED)
self.log_output.delete(1.0, ctk.END)
if os.path.exists(self.work_dir):
try:
import time
time.sleep(1)
for root, dirs, files in os.walk(self.work_dir, topdown=False):
for name in files:
try:
file_path = os.path.join(root, name)
os.chmod(file_path, 0o777)
os.remove(file_path)
except Exception as e:
self.log(f"Cannot remove file {file_path}: {e}")
for name in dirs:
try:
dir_path = os.path.join(root, name)
os.rmdir(dir_path)
except Exception as e:
self.log(f"Cannot remove directory {dir_path}: {e}")
try:
os.rmdir(self.work_dir)
except Exception as e:
self.log(f"Cannot remove directory {self.work_dir}: {e}")
self.log(f"Deleted old folder {self.work_dir}")
except Exception as e:
self.log(f"Error when removing folder {self.work_dir}: {e}")
return False
decode_cmd = [self.jdk_path, "-jar", self.apktool_jar_path, "d", self.apk_path, "-f", "-o", self.work_dir]
process = subprocess.Popen(decode_cmd, creationflags=CREATE_NO_WINDOW, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
while True:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
break
if output:
self.log(output.strip())
rc = process.poll()
if rc == 0:
self.log(f"Extracted APK to folder {self.work_dir}")
else:
self.log(f"Error when extracting APK: Process returned code {rc}")
return False
except Exception as e:
self.log(f"Error when extracting APK: {e}")
return False
finally:
self.loading_label.configure(text="")
self.btn_extract.configure(state=ctk.NORMAL)
self.btn_choose_apk.configure(state=ctk.NORMAL)
def log(self, msg):
self.log_output.insert(ctk.END, f"{msg}\n")
self.log_output.see(ctk.END)