import json import os from typing import TextIO from model.custom_struct import EmulatorInfo from tools.emulator.emulator import Emulator from tools.utils import Utils class LD(Emulator): def __init__(self, system_type: int): self.install_path = self.get_ld_install_dir(system_type) super().__init__(self.install_path) self.emulator_type = 'ld' self.system_type = system_type if not os.path.exists(self.install_path + r'\dnconsole.exe'): raise ValueError(f"{self.emulator_type}-{system_type},安装路径错误") def set_install_path(self, path: str): self.install_path = path def get_install_path(self) -> str: return self.install_path def dnc(self, cmd: str) -> str: return self._execute_cmd('dnconsole.exe" ' + cmd) def shell(self, index: int, cmd: str) -> str: return self._execute_cmd(f'ld.exe" -s {index} "{cmd}"').rstrip('\n') def start(self, index: int): self.dnc(f'launch --index {index}') def is_started(self, index) -> tuple[bool, EmulatorInfo | None]: emulator_info = self.get_info(index) if emulator_info : return emulator_info.is_enter_android == 1 , emulator_info return False, None def close(self, index: int): self.dnc(f'quit --index {index}') def close_all(self): self.dnc(f'quitall') def add(self, name: str): self.dnc(f'add --name {name}') def remove(self, index: int): self.dnc(f'remove --index {index}') def rename(self, index: int, name: str): self.dnc(f'rename --index {index} --title {name}') def restore(self, index: int, backup_file: str): self.dnc(f'restore --index {index} --file {backup_file}') def is_exists(self, index: int) -> bool: return os.path.exists(os.path.join(self.get_install_path(),'vms',f'leidian{index}')) def is_running(self, index: int) -> bool: return self.dnc(f'isrunning --index {index}') == 'running' def modify(self,index,cpu=None, memory=None, resolution=None, manufacturer=None, model=None, pnumber=None, imei=None, imsi=None, simserial=None, androidid=None, mac=None, autorotate=None, lockwindow=None, root=None): """ 修改模拟器机型 :param index: 模拟器索引 :param args: [--resolution ] [--cpu <1 | 2 | 3 | 4>] [--memory <256 | 512 | 768 | 1024 | 1536 | 2048 | 4096 | 8192>] [--manufacturer asus] [--model ASUS_Z00DUO] [--pnumber 13800000000] [--imei ] [--imsi ] [--simserial ] [--androidid ] [--mac ] [--autorotate <1 | 0> 自动旋转 [--lockwindow <1 | 0> 锁定窗口大小 [--root <1 | 0> """ formatted_args = " ".join([f"--{key} {value}" for key, value in locals().items() if value and key not in ['index', 'self']]) self.dnc(f'modify --index {index} {formatted_args}') def get_manufacturer(self, index:int): return self.shell(index, 'getprop ro.product.manufacturer') def get_model(self,index:int): return self.shell(index, 'getprop ro.product.model') def get_android_version(self,index:int): return self.shell(index, 'getprop ro.build.version.release') def get_android_id(self, index:int): return self.shell(index, 'getprop phone.androidid') def get_IMEI(self, index:int): return self.shell(index, 'getprop phone.imei') def get_IMSI(self, index:int): return self.shell(index, 'getprop phone.imsi') def get_mac(self, index:int): if self.system_type == 9: return self.shell(index, 'cat /sys/class/net/wlan0/address') else: return self.shell(index, 'cat /sys/class/net/eth0/address') def get_resolution(self, index:int): return self.shell(index, 'wm size') def get_sim_serial(self, index:int): return self.shell(index, 'getprop phone.simserial') def get_phone_number(self, index:int): return self.shell(index, 'getprop phone.number') def get_ip(self, index:int): tmp_str = self.shell(index, 'ifconfig eth0') return Utils.extract_between_strings(tmp_str, 'ip ', ' ') def read_emulator_file(self, index: int, file_path: str) -> str: """ 读取模拟器文本文件 :param index: 模拟器索引 :param file_path:模拟器文件路径 :return: """ return super().read_emulator_file(index, file_path) def write_emulator_file(self, index: int, file_path: str, content: str) -> bool: """ 写入模拟器文本文件 :param index: 模拟器索引 :param file_path:模拟器文件路径 :param content: 写入内容 :return: """ return super().write_emulator_file(index, file_path, content) def get_list(self) -> list[EmulatorInfo]: emu_list = [] info_str = self.dnc('list2').strip() for line in info_str.split("\n"): tmp_list = line.split(',') if len(tmp_list) == 7: info = EmulatorInfo( index=int(tmp_list[0]), name=tmp_list[1], top_handle=int(tmp_list[2]), bind_handle=int(tmp_list[3]), is_enter_android=int(tmp_list[4]), pid=int(tmp_list[5]), vbox_pid=int(tmp_list[6]), width=None, height=None, dpi=None ) emu_list.append(info) elif len(tmp_list) == 10: info = EmulatorInfo( index=int(tmp_list[0]), name=tmp_list[1], top_handle=int(tmp_list[2]), bind_handle=int(tmp_list[3]), is_enter_android=int(tmp_list[4]), pid=int(tmp_list[5]), vbox_pid=int(tmp_list[6]), width=int(tmp_list[7]), height=int(tmp_list[8]), dpi=int(tmp_list[9]) ) emu_list.append(info) return emu_list def get_info(self, index: int) -> EmulatorInfo | None: info_str = self.dnc('list2').strip() for line in info_str.split("\n"): if line: tmp_list = line.split(',') if int(tmp_list[0]) == index: if len(tmp_list) == 7: return EmulatorInfo( index=int(tmp_list[0]), name=tmp_list[1], top_handle=int(tmp_list[2]), bind_handle=int(tmp_list[3]), is_enter_android=int(tmp_list[4]), pid=int(tmp_list[5]), vbox_pid=int(tmp_list[6]), width=None, height=None, dpi=None ) elif len(tmp_list) == 10: return EmulatorInfo( index=int(tmp_list[0]), name=tmp_list[1], top_handle=int(tmp_list[2]), bind_handle=int(tmp_list[3]), is_enter_android=int(tmp_list[4]), pid=int(tmp_list[5]), vbox_pid=int(tmp_list[6]), width=int(tmp_list[7]), height=int(tmp_list[8]), dpi=int(tmp_list[9]) ) raise f"Emulator {index} not found" return None def set_share_dir(self, index: int, path: str, dir_type: int = 1) -> bool: """ 设置模拟器的共享目录。 :param index: 模拟器索引 :param path: 共享目录路径 :param dir_type: 共享目录类型(1: 图片, 2: 应用, 3: 杂项) :return: 成功返回 True,失败返回 False """ # 共享目录类型映射 key_map = { 1: 'statusSettings.sharedPictures', 2: 'statusSettings.sharedApplications', 3: 'statusSettings.sharedMisc' } # 获取对应的 key key = key_map.get(dir_type, 'statusSettings.sharedPictures') # 处理路径分隔符 path = path.replace("\\", "/") # 拼接配置文件路径 config_file = os.path.join(self.install_path, 'vms', 'config', f'leidian{index}.config') if not os.path.exists(config_file): raise FileNotFoundError(f"Config file not found: {config_file}") try: # 读取配置文件 with open(config_file, 'r', encoding='utf-8') as file: config = json.load(file) # 设置共享目录 config[key] = path # 写回配置文件 with open(config_file, 'w', encoding='utf-8') as file: # type: TextIO json.dump(config, file, ensure_ascii=False, indent=4) return True except FileNotFoundError: raise FileNotFoundError(f"Config file not found: {config_file}") except json.JSONDecodeError: raise json.JSONDecodeError(f"Error decoding JSON from {config_file}") except Exception as e: raise Exception(f"An unexpected error occurred: {e}") def get_share_dir(self, index: int, dir_type: int = 1): key_map = { 1: 'statusSettings.sharedPictures', 2: 'statusSettings.sharedApplications', 3: 'statusSettings.sharedMisc' } key = key_map.get(dir_type, 'statusSettings.sharedPictures') config_file = self.install_path + f'\\vms\\config\\leidian{index}.config' try: with open(config_file, 'r', encoding='utf-8') as file: config = json.load(file) return True, config[key] except FileNotFoundError: raise FileNotFoundError(f"Config file not found: {config_file}") except json.JSONDecodeError: raise json.JSONDecodeError(f"Error decoding JSON from {config_file}") except Exception as e: raise Exception(f"An unexpected error occurred: {e}") def start_app(self, index, packagename): return self.dnc(f"runapp --index {index} --packagename {packagename}") @staticmethod def get_ld_install_dir(_type: int) -> str: """ 获取雷电模拟器的安装路径。 :param _type: 模拟器类型 (4 表示雷电模拟器4, 9 表示雷电模拟器9) :return: 安装路径字符串,若未找到则返回空字符串 """ install_path = "" if _type == 4: install_path = Utils.get_registry_value(r"SOFTWARE\leidian\ldplayer", "InstallDir") if not install_path: install_path = Utils.get_install_dir_from_shortcut("雷电模拟器4.lnk").replace("dnplayer.exe", "") if _type == 64: install_path = Utils.get_registry_value(r"SOFTWARE\leidian\ldplayer64", "InstallDir") if not install_path: install_path = Utils.get_install_dir_from_shortcut("雷电模拟器64.lnk").replace("dnplayer.exe", "") elif _type == 9: install_path = Utils.get_registry_value(r"SOFTWARE\leidian\ldplayer9", "InstallDir") if not install_path: install_path = Utils.get_install_dir_from_shortcut("雷电模拟器9.lnk").replace("dnplayer.exe", "") if not install_path: raise FileNotFoundError(f"雷电模拟器-{_type}:安装路径未找到") return install_path @staticmethod def get_device_ip_by_index(index: int): return 'emulator-' + str(5554 + 2 * index) @staticmethod def get_index_by_device_ip(device_ip: str): return (int(device_ip.replace('emulator-', '')) - 5554) // 2