From 0dd7858097f327a4307c582023d6c3a4c7fa3255 Mon Sep 17 00:00:00 2001 From: gfdgd xi <3025613752@qq.com> Date: Fri, 2 Jul 2021 20:05:01 +0800 Subject: [PATCH] 1.2.1 --- README.md | 26 +- build/DEBIAN/control | 4 +- build/DEBIAN/postinst | 8 +- build/opt/apps/uengine-runner/uengine-runner | 308 +++++++++---------- main.py | 308 +++++++++---------- spark-uengine-runner.deb | Bin 17572 -> 17556 bytes 6 files changed, 318 insertions(+), 336 deletions(-) diff --git a/README.md b/README.md index 8e137e2..4181035 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# uengine 运行器 1.2.0 +# uengine 运行器 1.2.1 #### 介绍 使用 Python3 的 tkinter 构建 -(测试平台:UOS 家庭版) +(测试平台:UOS 家庭版,deepin 20.2.2 待测试) (自己美术功底太差,图标直接用 anbox 的了) @@ -13,10 +13,24 @@ i386 和 amd64 #### 更新内容 +1.2.1更新内容: + +※1、进行了安装方式的修改(不使用 adb),修复原无法安装和卸载的问题; + +2、进行了部分优化; + +3、进行了功能缩水; + +4、修复 deb 打包错误。 + 1.2.0更新内容: + 1、支持安装自动添加快捷方式、卸载删除快捷方式; + 2、支持使用包名或 APK 文件卸载程序; + 3、支持查看安装的所有包名; + 4、进行了部分优化 @@ -25,7 +39,7 @@ i386 和 amd64 1. 安装所需依赖 ``` -sudo apt install python3 python3-tk git adb python3-pip aapt +sudo apt install python3 python3-tk git python3-pip aapt uengine pip3 install pillow pip3 install ttkthemes pip3 install pillow -U @@ -58,9 +72,11 @@ pip3 uninstall ttkthemes #### 使用说明 提示: -1、先连接设备再进行操作 +1、需要你有使用 root 权限的能力; -2、支持连接其他 Android 系统操作(需要进行设置) +2、需要安装 uengine 才能使用。 + +如果想要连接其他手机,请使用 1.2.0 以前的版本,可以使用 adb 连接。 #### 特技 diff --git a/build/DEBIAN/control b/build/DEBIAN/control index be3db3c..75fb7d0 100644 --- a/build/DEBIAN/control +++ b/build/DEBIAN/control @@ -1,9 +1,9 @@ Package: spark-uengine-runner -Version: 1.2.0 +Version: 1.2.1 Maintainer: gfdgd xi <3025613752@qq.com> Homepage: https://gitee.com/gfdgd-xi/uengine-runner Architecture: all Priority: optional -Depends: python3, python3-tk, adb, python3-pip, aapt +Depends: python3, python3-tk, python3-pip, aapt, uengine Description: gfdgd xi make's uengine runner diff --git a/build/DEBIAN/postinst b/build/DEBIAN/postinst index 658f2be..7f05c0f 100755 --- a/build/DEBIAN/postinst +++ b/build/DEBIAN/postinst @@ -1,5 +1,3 @@ -# !/bin/sh -sudo pip3 install --upgrade pip -sudo pip3 install --upgrade virtualenv -pip3 install pillow -pip3 install ttkthemes \ No newline at end of file +# !/bin/sh +pip3 install --upgrade pillow +pip3 install --upgrade ttkthemes diff --git a/build/opt/apps/uengine-runner/uengine-runner b/build/opt/apps/uengine-runner/uengine-runner index 5586b36..ea691b6 100755 --- a/build/opt/apps/uengine-runner/uengine-runner +++ b/build/opt/apps/uengine-runner/uengine-runner @@ -4,7 +4,7 @@ # 作者:gfdgd xi # 版本:1.2.0 # 更新时间:2021年5月30日 -# 感谢:anbox 和 统信 +# 感谢:anbox、deepin 和 统信 # 基于 Python3 的 tkinter 构建 ########################################################################################### ################# @@ -28,39 +28,37 @@ import tkinter.filedialog as filedialog import PIL.Image as Image import PIL.ImageTk as ImageTk -def KillAdbProgress(): +def UninstallProgram(package): + global fineUninstallApkHistory + #setting = {True: "", False: "-k"} + Return = GetCommandReturn("pkexec /usr/bin/uengine-session-launch-helper -- uengine uninstall --pkg='{}'".format(package)) + #Return = GetCommandReturn("adb shell pm uninstall {} {}".format(setting[cleanData], package)) + if os.path.exists("{}/.local/share/applications/{}.desktop".format(get_home(), package)): + os.remove("{}/.local/share/applications/{}.desktop".format(get_home(), package)) + if os.path.exists("{}/{}.desktop".format(get_desktop_path(), package)): + os.remove("{}/{}.desktop".format(get_desktop_path(), package)) + fineUninstallApkHistory.append(combobox3.get()) + combobox3['value'] = fineUninstallApkHistory + write_txt(get_home() + "/.config/uengine-runner/FindUninstallApkHistory.json", str(json.dumps(ListToDictionary(fineUninstallApkHistory)))) # 将历史记录的数组转换为字典并写入 + return Return + +def ButtonClick7(): + path = filedialog.askopenfilename(title="选择 Apk", filetypes=[("APK 文件", "*.apk"), ("所有文件", "*.*")], initialdir=json.loads(readtxt(get_home() + "/.config/uengine-runner/FindUninstallApk.json"))["path"]) + if path != "" and path != "()": + try: + combobox3.set(path) + write_txt(get_home() + "/.config/uengine-runner/FindUninstallApk.json", json.dumps({"path": os.path.dirname(path)})) # 写入配置文件 + except: + pass + +def ButtonClick8(): DisabledAndEnbled(True) - Return = GetCommandReturn("killall adb") - if Return is "": - Return = "进程已经杀死!" - messagebox.showinfo(title="tips", message=Return) - DisabledAndEnbled(False) - -def Button1Click(): - if combobox2.get() is "": - messagebox.showerror(title="提示", message="信息没有填写完整,无法继续连接 IP") - return - DisabledAndEnbled(True) - threading.Thread(target=ConnectPhoneIp).start() - -def ConnectPhoneIp(): - global phoneIp - messagebox.showinfo(title="提示", message=GetCommandReturn("adb connect '{}'".format(combobox2.get()))) - phoneIp.append(combobox2.get()) - combobox2['value'] = phoneIp - write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", str(json.dumps(ListToDictionary(phoneIp)))) # 将历史记录的数组转换为字典并写入 - DisabledAndEnbled(False) - -def ConnectPhoneIpDefult(quit = False): - global phoneIp - Return = GetCommandReturn("adb connect '192.168.250.2'") - if quit: - print(Return) - return - messagebox.showinfo(title="提示", message=Return) - phoneIp.append("192.168.250.2") - combobox2['value'] = phoneIp - write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", str(json.dumps(ListToDictionary(phoneIp)))) # 将历史记录的数组转换为字典并写入 + if os.path.exists(combobox3.get()): + path = GetApkPackageName(combobox3.get()) + else: + path = combobox3.get() + UninstallProgram(path) + messagebox.showinfo(message="操作执行完毕!", title="提示") DisabledAndEnbled(False) def FindApk(): @@ -79,24 +77,16 @@ def Button3Install(): DisabledAndEnbled(True) threading.Thread(target=InstallApk, args=(combobox1.get(),)).start() -def AdbRun(): - Return = GetCommandReturn("adb devices").replace("\n", "").replace("List of devices attached", "").replace("* daemon not running; starting now at tcp:5037", "").replace("* daemon started successfully", "") - if Return is "": - return False - return True - -def AdbConnect(): - return GetCommandReturn("adb devices") - def InstallApk(path, quit = False): global findApkHistory - if not AdbRun(): + '''if not AdbRun(): if quit: return messagebox.showinfo(title="提示", message="你没有使用 adb 连接任何设备") DisabledAndEnbled(False) - return - commandReturn = GetCommandReturn("adb install '{}'".format(path)) + return''' + #commandReturn = GetCommandReturn("adb install '{}'".format(path)) + commandReturn = GetCommandReturn("pkexec /usr/bin/uengine-session-launch-helper -- uengine install --apk='{}'".format(path)) iconSavePath = "{}/.local/share/icons/hicolor/256x256/apps/{}.desktop".format(get_home(), GetApkPackageName(path)) SaveApkIcon(path, iconSavePath) BuildUengineDesktop(GetApkPackageName(path), GetApkActivityName(path), GetApkChineseLabel(path), iconSavePath, @@ -116,17 +106,17 @@ def DisabledAndEnbled(choose): userChoose = {True: tk.DISABLED, False: tk.NORMAL} a = userChoose[choose] combobox1.configure(state=a) - combobox2.configure(state=a) + #combobox2.configure(state=a) combobox3.configure(state=a) - checkButton1.configure(state=a) - button1.configure(state=a) + #checkButton1.configure(state=a) + #button1.configure(state=a) button2.configure(state=a) button3.configure(state=a) - button4.configure(state=a) + #button4.configure(state=a) button5.configure(state=a) - button6.configure(state=a) - button7.configure(state=a) - button8.configure(state=a) + #button6.configure(state=a) + #button7.configure(state=a) + #button8.configure(state=a) # 需引入 subprocess def GetCommandReturn(cmd): @@ -139,9 +129,6 @@ def Button5Click(): def OpenUengineProgramList(): os.system("/usr/bin/uengine-launch.sh --package=org.anbox.appmgr --component=org.anbox.appmgr.AppViewActivity") -def ShowAdbConnect(): - messagebox.showinfo(title="提示", message=AdbConnect()) - # 显示“关于这个程序”窗口 def about_this_program(): global about @@ -266,9 +253,6 @@ def ShowUseProgram(): global useProgram messagebox.showinfo(title="{} 使用的程序列表(部分)".format(title), message=useProgram) -def AboutAdb(): - messagebox.showinfo(message=GetCommandReturn("adb version"), title="关于 adb") - def GetApkPath(packetName): return GetCommandReturn("adb shell pm path {}".format(packetName)).replace("package:", "") @@ -365,59 +349,11 @@ def get_desktop_path(): def get_home(): return os.path.expanduser('~') -def UninstallProgram(package, cleanData): - global fineUninstallApkHistory - setting = {True: "", False: "-k"} - Return = GetCommandReturn("adb shell pm uninstall {} {}".format(setting[cleanData], package)) - if os.path.exists("{}/.local/share/applications/{}.desktop".format(get_home(), package)): - os.remove("{}/.local/share/applications/{}.desktop".format(get_home(), package)) - if os.path.exists("{}/{}.desktop".format(get_desktop_path(), package)): - os.remove("{}/{}.desktop".format(get_desktop_path(), package)) - fineUninstallApkHistory.append(combobox3.get()) - combobox3['value'] = fineUninstallApkHistory - write_txt(get_home() + "/.config/uengine-runner/FindUninstallApkHistory.json", str(json.dumps(ListToDictionary(fineUninstallApkHistory)))) # 将历史记录的数组转换为字典并写入 - return Return - -def ButtonClick7(): - path = filedialog.askopenfilename(title="选择 Apk", filetypes=[("APK 文件", "*.apk"), ("所有文件", "*.*")], initialdir=json.loads(readtxt(get_home() + "/.config/uengine-runner/FindUninstallApk.json"))["path"]) - if path != "" and path != "()": - try: - combobox3.set(path) - write_txt(get_home() + "/.config/uengine-runner/FindUninstallApk.json", json.dumps({"path": os.path.dirname(path)})) # 写入配置文件 - except: - pass - -def ButtonClick8(): - DisabledAndEnbled(True) - if os.path.exists(combobox3.get()): - path = GetApkPackageName(combobox3.get()) - else: - path = combobox3.get() - messagebox.showinfo(message=UninstallProgram(path, not checkButtonBool1.get())) - DisabledAndEnbled(False) - -def GetAllPackageName(): - return GetCommandReturn("adb shell pm list packages") - -def ShowAdbInstallPackage(): - mess = tk.Toplevel() - message = ttk.Frame(mess) - mess.resizable(0, 0) - mess.title("所有软件包") - textbox1 = tk.Text(message, width=100) - button1 = ttk.Button(message, text="确定", command=mess.withdraw) - textbox1.insert("0.0", GetAllPackageName()) - textbox1.configure(state=tk.DISABLED) - textbox1.pack() - button1.pack(side="bottom") - message.pack() - mess.mainloop() - ########################### # 程序信息 ########################### programUrl = "https://gitee.com/gfdgd-xi/uengine-runner" -version = "1.2.0" +version = "1.2.1" goodRunSystem = "Linux" about = '''一个基于 Python3 的 tkinter 制作的 uengine APK 安装器 版本:{} @@ -426,15 +362,15 @@ tkinter 版本:{} 程序官网:{} ©2021-{} gfdgd xi'''.format(version, goodRunSystem, tk.TkVersion, programUrl, time.strftime("%Y")) tips = '''提示: -1、先连接设备再安装应用 -2、支持连接其他 Android 系统操作(需要进行设置)''' -updateThingsString = '''1、支持安装自动添加快捷方式、卸载删除快捷方式; -2、支持使用包名或 APK 文件卸载程序; -3、支持查看安装的所有包名; -4、支持终端连接默认 IP 和安装 APK 文件; -5、进行了部分优化''' +1、需要你有使用 root 权限的能力; +2、需要安装 uengine 才能使用。 +如果想要连接其他手机,请使用 1.2.0 以前的版本,可以使用 adb 连接。''' +updateThingsString = '''※1、进行了安装方式的修改(不使用 adb),修复原无法安装和卸载的问题; +2、进行了部分优化; +3、进行了功能缩水; +4、修复 deb 打包错误。''' title = "uengine 运行器 {}".format(version) -updateTime = "2021年6月6日" +updateTime = "2021年7月2日(考试结束了)" updateThings = "{} 更新内容:\n{}\n更新时间:{}".format(version, updateThingsString, updateTime, time.strftime("%Y")) iconPath = "{}/icon.png".format(os.path.split(os.path.realpath(__file__))[0]) desktop = "/opt/apps/uengine-runner/UengineAndroidProgramList.desktop" @@ -442,8 +378,7 @@ desktopName = "UengineAndroidProgramList.desktop" useProgram = '''1、uengine(anbox) 2、Python3 3、tkinter(tkinter.tk、ttkthemes 和 tkinter.ttk) -4、adb -5、aapt +4、aapt ……''' ########################### @@ -451,8 +386,8 @@ useProgram = '''1、uengine(anbox) ########################### if not os.path.exists(get_home() + "/.config/uengine-runner"): # 如果没有配置文件夹 os.mkdir(get_home() + "/.config/uengine-runner") # 创建配置文件夹 -if not os.path.exists(get_home() + "/.config/uengine-runner/PhoneIp.json"): # 如果没有配置文件 - write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", json.dumps({})) # 创建配置文件 +#if not os.path.exists(get_home() + "/.config/uengine-runner/PhoneIp.json"): # 如果没有配置文件 + #write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", json.dumps({})) # 创建配置文件 if not os.path.exists(get_home() + "/.config/uengine-runner/FindApkHistory.json"): # 如果没有配置文件 write_txt(get_home() + "/.config/uengine-runner/FindApkHistory.json", json.dumps({})) # 创建配置文件 if not os.path.exists(get_home() + "/.config/uengine-runner/FindUninstallApkHistory.json"): # 如果没有配置文件 @@ -466,34 +401,13 @@ if not os.path.exists(get_home() + "/.config/uengine-runner/FindUninstallApk.jso # 设置变量 ########################### findApkHistory = list(json.loads(readtxt(get_home() + "/.config/uengine-runner/FindApkHistory.json")).values()) -phoneIp = list(json.loads(readtxt(get_home() + "/.config/uengine-runner/PhoneIp.json")).values()) +#phoneIp = list(json.loads(readtxt(get_home() + "/.config/uengine-runner/PhoneIp.json")).values()) fineUninstallApkHistory = list(json.loads(readtxt(get_home() + "/.config/uengine-runner/FindUninstallApkHistory.json")).values()) ########################### # 判断参数 ########################### -quit = True -if len(sys.argv) > 1: # 有参数 - if "-q" in sys.argv: - quit = True - if "-i" in sys.argv: - if len(sys.argv) >= sys.argv.index("-i") + 2: - InstallApk(sys.argv[sys.argv.index("-i") + 1], quit) - sys.exit() - if "-ci" in sys.argv: - if len(sys.argv) >= sys.argv.index("-ci") + 2: - ConnectPhoneIpDefult(quit) - InstallApk(sys.argv[sys.argv.index("-ci") + 1], quit) - sys.exit() - if "-c" in sys.argv: - ConnectPhoneIpDefult(quit) - sys.exit() - print("帮助:") - print("-c\t连接默认 IP 地址(192.168.250.2)") - print("-ci APK文件路径\t连接默认 IP 地址并安装 APK 软件包") - print("-i APK文件路径\t安装 APK 软件包") - print("--help 查看帮助") - sys.exit() +# None ########################### # 窗口创建 @@ -511,34 +425,34 @@ frame1 = ttk.Frame(window) frame2 = ttk.Frame(window) frame3 = ttk.Frame(window) label1 = ttk.Label(window, text="要安装的 apk 路径:") -label2 = ttk.Label(window, text="要连接的设备的 IP(默认 IP 为 192.168.250.2):") +#label2 = ttk.Label(window, text="要连接的设备的 IP(默认 IP 为 192.168.250.2):") label3 = ttk.Label(window, text="要卸载的包名或程序对应的 APK 文件:") combobox1 = ttk.Combobox(window, width=100) -combobox2 = ttk.Combobox(window, width=100) +#combobox2 = ttk.Combobox(window, width=100) combobox3 = ttk.Combobox(window, width=100) -button1 = ttk.Button(frame1, text="连接设备", command=ConnectPhoneIp) +#button1 = ttk.Button(frame1, text="连接设备", command=ConnectPhoneIp) button2 = ttk.Button(window, text="浏览", command=FindApk) button3 = ttk.Button(frame2, text="安装", command=Button3Install) -button4 = ttk.Button(frame1, text="关闭 adb 软件进程", command=KillAdbProgress) +#button4 = ttk.Button(frame1, text="关闭 adb 软件进程", command=KillAdbProgress) button5 = ttk.Button(frame2, text="打开 uengine 应用列表", command=Button5Click) -button6 = ttk.Button(frame1, text="连接默认 IP", command=ConnectPhoneIpDefult) +#button6 = ttk.Button(frame1, text="连接默认 IP", command=ConnectPhoneIpDefult) button7 = ttk.Button(window, text="浏览", command=ButtonClick7) button8 = ttk.Button(frame3, text="卸载", command=ButtonClick8) -checkButton1 = ttk.Checkbutton(frame3, text="保留软件数据", variable=checkButtonBool1) +#checkButton1 = ttk.Checkbutton(frame3, text="保留软件数据", variable=checkButtonBool1) menu = tk.Menu(window, background="white") # 设置菜单栏 programmenu = tk.Menu(menu, tearoff=0, background="white") # 设置“程序”菜单栏 -adb = tk.Menu(menu, tearoff=0, background="white") +#adb = tk.Menu(menu, tearoff=0, background="white") uengine = tk.Menu(menu, tearoff=0, background="white") help = tk.Menu(menu, tearoff=0, background="white") # 设置“帮助”菜单栏 menu.add_cascade(label="程序", menu=programmenu) -menu.add_cascade(label="adb", menu=adb) +#menu.add_cascade(label="adb", menu=adb) menu.add_cascade(label="uengine", menu=uengine) menu.add_cascade(label="帮助", menu=help) programmenu.add_command(label="清空软件历史记录", command=CleanProgramHistory) programmenu.add_separator() # 设置分界线 programmenu.add_command(label="退出程序", command=window.quit) # 设置“退出程序”项 -adb.add_command(label="adb 连接的设备", command=ShowAdbConnect) -adb.add_command(label="adb 连接的设备的所有软件包", command=ShowAdbInstallPackage) +#adb.add_command(label="adb 连接的设备", command=ShowAdbConnect) +#adb.add_command(label="adb 连接的设备的所有软件包", command=ShowAdbInstallPackage) uengine.add_command(label="发送 uengine 应用列表到桌面", command=SendUengineAndroidListForDesktop) uengine.add_command(label="发送 uengine 应用列表到启动器", command=SendUengineAndroidListForLauncher) help.add_command(label="程序官网", command=OpenProgramURL) # 设置“程序官网”项 @@ -546,36 +460,106 @@ help.add_separator() help.add_command(label="小提示", command=helps) # 设置“小提示”项 help.add_command(label="更新内容", command=UpdateThings) # 设置“更新内容”项 help.add_command(label="这个程序使用的程序列表(部分)", command=ShowUseProgram) # 设置“更新内容”项 -help.add_command(label="关于 adb", command=AboutAdb) # 设置“关于这个程序”项 +#help.add_command(label="关于 adb", command=AboutAdb) # 设置“关于这个程序”项 help.add_command(label="关于这个程序", command=about_this_program) # 设置“关于这个程序”项 menu.configure(activebackground="white") help.configure(activebackground="white") uengine.configure(activebackground="white") -adb.configure(activebackground="white") +#adb.configure(activebackground="white") programmenu.configure(activebackground="white") # 设置控件 combobox3['value'] = fineUninstallApkHistory -combobox2['value'] = phoneIp +#combobox2['value'] = phoneIp combobox1['value'] = findApkHistory # win.config(menu=menu) # 显示菜单栏 label1.grid(row=2, column=0) -label2.grid(row=0, column=0) +#label2.grid(row=0, column=0) label3.grid(row=4, column=0) combobox1.grid(row=2, column=1) -combobox2.grid(row=0, column=1) +#combobox2.grid(row=0, column=1) combobox3.grid(row=4, column=1) -button1.grid(column=0, row=0) +#button1.grid(column=0, row=0) button2.grid(row=2, column=2) button3.grid(row=0, column=0) -button4.grid(column=1, row=0) +#button4.grid(column=1, row=0) button5.grid(row=0, column=1) -button6.grid(row=0, column=3) +#button6.grid(row=0, column=3) button7.grid(row=4, column=2) button8.grid(row=0, column=1) -checkButton1.grid(row=0, column=0) +#checkButton1.grid(row=0, column=0) frame1.grid(row=1, columnspa=3) frame2.grid(row=3, columnspa=3) frame3.grid(row=5, columnspa=3) window.pack() -win.mainloop() \ No newline at end of file +win.mainloop() + +######################################### +# 废弃的代码 +######################################### +'''def KillAdbProgress(): + DisabledAndEnbled(True) + Return = GetCommandReturn("killall adb") + if Return is "": + Return = "进程已经杀死!" + messagebox.showinfo(title="tips", message=Return) + DisabledAndEnbled(False)''' + +'''def Button1Click(): + if combobox2.get() is "": + messagebox.showerror(title="提示", message="信息没有填写完整,无法继续连接 IP") + return + DisabledAndEnbled(True) + threading.Thread(target=ConnectPhoneIp).start()''' + +'''def ConnectPhoneIp(): + global phoneIp + messagebox.showinfo(title="提示", message=GetCommandReturn("adb connect '{}'".format(combobox2.get()))) + phoneIp.append(combobox2.get()) + combobox2['value'] = phoneIp + write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", str(json.dumps(ListToDictionary(phoneIp)))) # 将历史记录的数组转换为字典并写入 + DisabledAndEnbled(False)''' + +'''def ConnectPhoneIpDefult(quit = False): + global phoneIp + Return = GetCommandReturn("adb connect '192.168.250.2'") + if quit: + print(Return) + return + messagebox.showinfo(title="提示", message=Return) + phoneIp.append("192.168.250.2") + combobox2['value'] = phoneIp + write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", str(json.dumps(ListToDictionary(phoneIp)))) # 将历史记录的数组转换为字典并写入 + DisabledAndEnbled(False)''' + +'''def AdbRun(): + Return = GetCommandReturn("adb devices").replace("\n", "").replace("List of devices attached", "").replace("* daemon not running; starting now at tcp:5037", "").replace("* daemon started successfully", "") + if Return is "": + return False + return True''' + +'''def AdbConnect(): + return GetCommandReturn("adb devices")''' + +'''def ShowAdbConnect(): + messagebox.showinfo(title="提示", message=AdbConnect())''' + +'''def AboutAdb(): + messagebox.showinfo(message=GetCommandReturn("adb version"), title="关于 adb")''' + +'''def GetAllPackageName(): + return GetCommandReturn("adb shell pm list packages")''' + +'''def ShowAdbInstallPackage(): + mess = tk.Toplevel() + message = ttk.Frame(mess) + mess.resizable(0, 0) + mess.title("所有软件包") + textbox1 = tk.Text(message, width=100) + button1 = ttk.Button(message, text="确定", command=mess.withdraw) + textbox1.insert("0.0", GetAllPackageName()) + textbox1.configure(state=tk.DISABLED) + textbox1.pack() + button1.pack(side="bottom") + message.pack() + mess.mainloop()''' \ No newline at end of file diff --git a/main.py b/main.py index 5586b36..ea691b6 100755 --- a/main.py +++ b/main.py @@ -4,7 +4,7 @@ # 作者:gfdgd xi # 版本:1.2.0 # 更新时间:2021年5月30日 -# 感谢:anbox 和 统信 +# 感谢:anbox、deepin 和 统信 # 基于 Python3 的 tkinter 构建 ########################################################################################### ################# @@ -28,39 +28,37 @@ import tkinter.filedialog as filedialog import PIL.Image as Image import PIL.ImageTk as ImageTk -def KillAdbProgress(): +def UninstallProgram(package): + global fineUninstallApkHistory + #setting = {True: "", False: "-k"} + Return = GetCommandReturn("pkexec /usr/bin/uengine-session-launch-helper -- uengine uninstall --pkg='{}'".format(package)) + #Return = GetCommandReturn("adb shell pm uninstall {} {}".format(setting[cleanData], package)) + if os.path.exists("{}/.local/share/applications/{}.desktop".format(get_home(), package)): + os.remove("{}/.local/share/applications/{}.desktop".format(get_home(), package)) + if os.path.exists("{}/{}.desktop".format(get_desktop_path(), package)): + os.remove("{}/{}.desktop".format(get_desktop_path(), package)) + fineUninstallApkHistory.append(combobox3.get()) + combobox3['value'] = fineUninstallApkHistory + write_txt(get_home() + "/.config/uengine-runner/FindUninstallApkHistory.json", str(json.dumps(ListToDictionary(fineUninstallApkHistory)))) # 将历史记录的数组转换为字典并写入 + return Return + +def ButtonClick7(): + path = filedialog.askopenfilename(title="选择 Apk", filetypes=[("APK 文件", "*.apk"), ("所有文件", "*.*")], initialdir=json.loads(readtxt(get_home() + "/.config/uengine-runner/FindUninstallApk.json"))["path"]) + if path != "" and path != "()": + try: + combobox3.set(path) + write_txt(get_home() + "/.config/uengine-runner/FindUninstallApk.json", json.dumps({"path": os.path.dirname(path)})) # 写入配置文件 + except: + pass + +def ButtonClick8(): DisabledAndEnbled(True) - Return = GetCommandReturn("killall adb") - if Return is "": - Return = "进程已经杀死!" - messagebox.showinfo(title="tips", message=Return) - DisabledAndEnbled(False) - -def Button1Click(): - if combobox2.get() is "": - messagebox.showerror(title="提示", message="信息没有填写完整,无法继续连接 IP") - return - DisabledAndEnbled(True) - threading.Thread(target=ConnectPhoneIp).start() - -def ConnectPhoneIp(): - global phoneIp - messagebox.showinfo(title="提示", message=GetCommandReturn("adb connect '{}'".format(combobox2.get()))) - phoneIp.append(combobox2.get()) - combobox2['value'] = phoneIp - write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", str(json.dumps(ListToDictionary(phoneIp)))) # 将历史记录的数组转换为字典并写入 - DisabledAndEnbled(False) - -def ConnectPhoneIpDefult(quit = False): - global phoneIp - Return = GetCommandReturn("adb connect '192.168.250.2'") - if quit: - print(Return) - return - messagebox.showinfo(title="提示", message=Return) - phoneIp.append("192.168.250.2") - combobox2['value'] = phoneIp - write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", str(json.dumps(ListToDictionary(phoneIp)))) # 将历史记录的数组转换为字典并写入 + if os.path.exists(combobox3.get()): + path = GetApkPackageName(combobox3.get()) + else: + path = combobox3.get() + UninstallProgram(path) + messagebox.showinfo(message="操作执行完毕!", title="提示") DisabledAndEnbled(False) def FindApk(): @@ -79,24 +77,16 @@ def Button3Install(): DisabledAndEnbled(True) threading.Thread(target=InstallApk, args=(combobox1.get(),)).start() -def AdbRun(): - Return = GetCommandReturn("adb devices").replace("\n", "").replace("List of devices attached", "").replace("* daemon not running; starting now at tcp:5037", "").replace("* daemon started successfully", "") - if Return is "": - return False - return True - -def AdbConnect(): - return GetCommandReturn("adb devices") - def InstallApk(path, quit = False): global findApkHistory - if not AdbRun(): + '''if not AdbRun(): if quit: return messagebox.showinfo(title="提示", message="你没有使用 adb 连接任何设备") DisabledAndEnbled(False) - return - commandReturn = GetCommandReturn("adb install '{}'".format(path)) + return''' + #commandReturn = GetCommandReturn("adb install '{}'".format(path)) + commandReturn = GetCommandReturn("pkexec /usr/bin/uengine-session-launch-helper -- uengine install --apk='{}'".format(path)) iconSavePath = "{}/.local/share/icons/hicolor/256x256/apps/{}.desktop".format(get_home(), GetApkPackageName(path)) SaveApkIcon(path, iconSavePath) BuildUengineDesktop(GetApkPackageName(path), GetApkActivityName(path), GetApkChineseLabel(path), iconSavePath, @@ -116,17 +106,17 @@ def DisabledAndEnbled(choose): userChoose = {True: tk.DISABLED, False: tk.NORMAL} a = userChoose[choose] combobox1.configure(state=a) - combobox2.configure(state=a) + #combobox2.configure(state=a) combobox3.configure(state=a) - checkButton1.configure(state=a) - button1.configure(state=a) + #checkButton1.configure(state=a) + #button1.configure(state=a) button2.configure(state=a) button3.configure(state=a) - button4.configure(state=a) + #button4.configure(state=a) button5.configure(state=a) - button6.configure(state=a) - button7.configure(state=a) - button8.configure(state=a) + #button6.configure(state=a) + #button7.configure(state=a) + #button8.configure(state=a) # 需引入 subprocess def GetCommandReturn(cmd): @@ -139,9 +129,6 @@ def Button5Click(): def OpenUengineProgramList(): os.system("/usr/bin/uengine-launch.sh --package=org.anbox.appmgr --component=org.anbox.appmgr.AppViewActivity") -def ShowAdbConnect(): - messagebox.showinfo(title="提示", message=AdbConnect()) - # 显示“关于这个程序”窗口 def about_this_program(): global about @@ -266,9 +253,6 @@ def ShowUseProgram(): global useProgram messagebox.showinfo(title="{} 使用的程序列表(部分)".format(title), message=useProgram) -def AboutAdb(): - messagebox.showinfo(message=GetCommandReturn("adb version"), title="关于 adb") - def GetApkPath(packetName): return GetCommandReturn("adb shell pm path {}".format(packetName)).replace("package:", "") @@ -365,59 +349,11 @@ def get_desktop_path(): def get_home(): return os.path.expanduser('~') -def UninstallProgram(package, cleanData): - global fineUninstallApkHistory - setting = {True: "", False: "-k"} - Return = GetCommandReturn("adb shell pm uninstall {} {}".format(setting[cleanData], package)) - if os.path.exists("{}/.local/share/applications/{}.desktop".format(get_home(), package)): - os.remove("{}/.local/share/applications/{}.desktop".format(get_home(), package)) - if os.path.exists("{}/{}.desktop".format(get_desktop_path(), package)): - os.remove("{}/{}.desktop".format(get_desktop_path(), package)) - fineUninstallApkHistory.append(combobox3.get()) - combobox3['value'] = fineUninstallApkHistory - write_txt(get_home() + "/.config/uengine-runner/FindUninstallApkHistory.json", str(json.dumps(ListToDictionary(fineUninstallApkHistory)))) # 将历史记录的数组转换为字典并写入 - return Return - -def ButtonClick7(): - path = filedialog.askopenfilename(title="选择 Apk", filetypes=[("APK 文件", "*.apk"), ("所有文件", "*.*")], initialdir=json.loads(readtxt(get_home() + "/.config/uengine-runner/FindUninstallApk.json"))["path"]) - if path != "" and path != "()": - try: - combobox3.set(path) - write_txt(get_home() + "/.config/uengine-runner/FindUninstallApk.json", json.dumps({"path": os.path.dirname(path)})) # 写入配置文件 - except: - pass - -def ButtonClick8(): - DisabledAndEnbled(True) - if os.path.exists(combobox3.get()): - path = GetApkPackageName(combobox3.get()) - else: - path = combobox3.get() - messagebox.showinfo(message=UninstallProgram(path, not checkButtonBool1.get())) - DisabledAndEnbled(False) - -def GetAllPackageName(): - return GetCommandReturn("adb shell pm list packages") - -def ShowAdbInstallPackage(): - mess = tk.Toplevel() - message = ttk.Frame(mess) - mess.resizable(0, 0) - mess.title("所有软件包") - textbox1 = tk.Text(message, width=100) - button1 = ttk.Button(message, text="确定", command=mess.withdraw) - textbox1.insert("0.0", GetAllPackageName()) - textbox1.configure(state=tk.DISABLED) - textbox1.pack() - button1.pack(side="bottom") - message.pack() - mess.mainloop() - ########################### # 程序信息 ########################### programUrl = "https://gitee.com/gfdgd-xi/uengine-runner" -version = "1.2.0" +version = "1.2.1" goodRunSystem = "Linux" about = '''一个基于 Python3 的 tkinter 制作的 uengine APK 安装器 版本:{} @@ -426,15 +362,15 @@ tkinter 版本:{} 程序官网:{} ©2021-{} gfdgd xi'''.format(version, goodRunSystem, tk.TkVersion, programUrl, time.strftime("%Y")) tips = '''提示: -1、先连接设备再安装应用 -2、支持连接其他 Android 系统操作(需要进行设置)''' -updateThingsString = '''1、支持安装自动添加快捷方式、卸载删除快捷方式; -2、支持使用包名或 APK 文件卸载程序; -3、支持查看安装的所有包名; -4、支持终端连接默认 IP 和安装 APK 文件; -5、进行了部分优化''' +1、需要你有使用 root 权限的能力; +2、需要安装 uengine 才能使用。 +如果想要连接其他手机,请使用 1.2.0 以前的版本,可以使用 adb 连接。''' +updateThingsString = '''※1、进行了安装方式的修改(不使用 adb),修复原无法安装和卸载的问题; +2、进行了部分优化; +3、进行了功能缩水; +4、修复 deb 打包错误。''' title = "uengine 运行器 {}".format(version) -updateTime = "2021年6月6日" +updateTime = "2021年7月2日(考试结束了)" updateThings = "{} 更新内容:\n{}\n更新时间:{}".format(version, updateThingsString, updateTime, time.strftime("%Y")) iconPath = "{}/icon.png".format(os.path.split(os.path.realpath(__file__))[0]) desktop = "/opt/apps/uengine-runner/UengineAndroidProgramList.desktop" @@ -442,8 +378,7 @@ desktopName = "UengineAndroidProgramList.desktop" useProgram = '''1、uengine(anbox) 2、Python3 3、tkinter(tkinter.tk、ttkthemes 和 tkinter.ttk) -4、adb -5、aapt +4、aapt ……''' ########################### @@ -451,8 +386,8 @@ useProgram = '''1、uengine(anbox) ########################### if not os.path.exists(get_home() + "/.config/uengine-runner"): # 如果没有配置文件夹 os.mkdir(get_home() + "/.config/uengine-runner") # 创建配置文件夹 -if not os.path.exists(get_home() + "/.config/uengine-runner/PhoneIp.json"): # 如果没有配置文件 - write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", json.dumps({})) # 创建配置文件 +#if not os.path.exists(get_home() + "/.config/uengine-runner/PhoneIp.json"): # 如果没有配置文件 + #write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", json.dumps({})) # 创建配置文件 if not os.path.exists(get_home() + "/.config/uengine-runner/FindApkHistory.json"): # 如果没有配置文件 write_txt(get_home() + "/.config/uengine-runner/FindApkHistory.json", json.dumps({})) # 创建配置文件 if not os.path.exists(get_home() + "/.config/uengine-runner/FindUninstallApkHistory.json"): # 如果没有配置文件 @@ -466,34 +401,13 @@ if not os.path.exists(get_home() + "/.config/uengine-runner/FindUninstallApk.jso # 设置变量 ########################### findApkHistory = list(json.loads(readtxt(get_home() + "/.config/uengine-runner/FindApkHistory.json")).values()) -phoneIp = list(json.loads(readtxt(get_home() + "/.config/uengine-runner/PhoneIp.json")).values()) +#phoneIp = list(json.loads(readtxt(get_home() + "/.config/uengine-runner/PhoneIp.json")).values()) fineUninstallApkHistory = list(json.loads(readtxt(get_home() + "/.config/uengine-runner/FindUninstallApkHistory.json")).values()) ########################### # 判断参数 ########################### -quit = True -if len(sys.argv) > 1: # 有参数 - if "-q" in sys.argv: - quit = True - if "-i" in sys.argv: - if len(sys.argv) >= sys.argv.index("-i") + 2: - InstallApk(sys.argv[sys.argv.index("-i") + 1], quit) - sys.exit() - if "-ci" in sys.argv: - if len(sys.argv) >= sys.argv.index("-ci") + 2: - ConnectPhoneIpDefult(quit) - InstallApk(sys.argv[sys.argv.index("-ci") + 1], quit) - sys.exit() - if "-c" in sys.argv: - ConnectPhoneIpDefult(quit) - sys.exit() - print("帮助:") - print("-c\t连接默认 IP 地址(192.168.250.2)") - print("-ci APK文件路径\t连接默认 IP 地址并安装 APK 软件包") - print("-i APK文件路径\t安装 APK 软件包") - print("--help 查看帮助") - sys.exit() +# None ########################### # 窗口创建 @@ -511,34 +425,34 @@ frame1 = ttk.Frame(window) frame2 = ttk.Frame(window) frame3 = ttk.Frame(window) label1 = ttk.Label(window, text="要安装的 apk 路径:") -label2 = ttk.Label(window, text="要连接的设备的 IP(默认 IP 为 192.168.250.2):") +#label2 = ttk.Label(window, text="要连接的设备的 IP(默认 IP 为 192.168.250.2):") label3 = ttk.Label(window, text="要卸载的包名或程序对应的 APK 文件:") combobox1 = ttk.Combobox(window, width=100) -combobox2 = ttk.Combobox(window, width=100) +#combobox2 = ttk.Combobox(window, width=100) combobox3 = ttk.Combobox(window, width=100) -button1 = ttk.Button(frame1, text="连接设备", command=ConnectPhoneIp) +#button1 = ttk.Button(frame1, text="连接设备", command=ConnectPhoneIp) button2 = ttk.Button(window, text="浏览", command=FindApk) button3 = ttk.Button(frame2, text="安装", command=Button3Install) -button4 = ttk.Button(frame1, text="关闭 adb 软件进程", command=KillAdbProgress) +#button4 = ttk.Button(frame1, text="关闭 adb 软件进程", command=KillAdbProgress) button5 = ttk.Button(frame2, text="打开 uengine 应用列表", command=Button5Click) -button6 = ttk.Button(frame1, text="连接默认 IP", command=ConnectPhoneIpDefult) +#button6 = ttk.Button(frame1, text="连接默认 IP", command=ConnectPhoneIpDefult) button7 = ttk.Button(window, text="浏览", command=ButtonClick7) button8 = ttk.Button(frame3, text="卸载", command=ButtonClick8) -checkButton1 = ttk.Checkbutton(frame3, text="保留软件数据", variable=checkButtonBool1) +#checkButton1 = ttk.Checkbutton(frame3, text="保留软件数据", variable=checkButtonBool1) menu = tk.Menu(window, background="white") # 设置菜单栏 programmenu = tk.Menu(menu, tearoff=0, background="white") # 设置“程序”菜单栏 -adb = tk.Menu(menu, tearoff=0, background="white") +#adb = tk.Menu(menu, tearoff=0, background="white") uengine = tk.Menu(menu, tearoff=0, background="white") help = tk.Menu(menu, tearoff=0, background="white") # 设置“帮助”菜单栏 menu.add_cascade(label="程序", menu=programmenu) -menu.add_cascade(label="adb", menu=adb) +#menu.add_cascade(label="adb", menu=adb) menu.add_cascade(label="uengine", menu=uengine) menu.add_cascade(label="帮助", menu=help) programmenu.add_command(label="清空软件历史记录", command=CleanProgramHistory) programmenu.add_separator() # 设置分界线 programmenu.add_command(label="退出程序", command=window.quit) # 设置“退出程序”项 -adb.add_command(label="adb 连接的设备", command=ShowAdbConnect) -adb.add_command(label="adb 连接的设备的所有软件包", command=ShowAdbInstallPackage) +#adb.add_command(label="adb 连接的设备", command=ShowAdbConnect) +#adb.add_command(label="adb 连接的设备的所有软件包", command=ShowAdbInstallPackage) uengine.add_command(label="发送 uengine 应用列表到桌面", command=SendUengineAndroidListForDesktop) uengine.add_command(label="发送 uengine 应用列表到启动器", command=SendUengineAndroidListForLauncher) help.add_command(label="程序官网", command=OpenProgramURL) # 设置“程序官网”项 @@ -546,36 +460,106 @@ help.add_separator() help.add_command(label="小提示", command=helps) # 设置“小提示”项 help.add_command(label="更新内容", command=UpdateThings) # 设置“更新内容”项 help.add_command(label="这个程序使用的程序列表(部分)", command=ShowUseProgram) # 设置“更新内容”项 -help.add_command(label="关于 adb", command=AboutAdb) # 设置“关于这个程序”项 +#help.add_command(label="关于 adb", command=AboutAdb) # 设置“关于这个程序”项 help.add_command(label="关于这个程序", command=about_this_program) # 设置“关于这个程序”项 menu.configure(activebackground="white") help.configure(activebackground="white") uengine.configure(activebackground="white") -adb.configure(activebackground="white") +#adb.configure(activebackground="white") programmenu.configure(activebackground="white") # 设置控件 combobox3['value'] = fineUninstallApkHistory -combobox2['value'] = phoneIp +#combobox2['value'] = phoneIp combobox1['value'] = findApkHistory # win.config(menu=menu) # 显示菜单栏 label1.grid(row=2, column=0) -label2.grid(row=0, column=0) +#label2.grid(row=0, column=0) label3.grid(row=4, column=0) combobox1.grid(row=2, column=1) -combobox2.grid(row=0, column=1) +#combobox2.grid(row=0, column=1) combobox3.grid(row=4, column=1) -button1.grid(column=0, row=0) +#button1.grid(column=0, row=0) button2.grid(row=2, column=2) button3.grid(row=0, column=0) -button4.grid(column=1, row=0) +#button4.grid(column=1, row=0) button5.grid(row=0, column=1) -button6.grid(row=0, column=3) +#button6.grid(row=0, column=3) button7.grid(row=4, column=2) button8.grid(row=0, column=1) -checkButton1.grid(row=0, column=0) +#checkButton1.grid(row=0, column=0) frame1.grid(row=1, columnspa=3) frame2.grid(row=3, columnspa=3) frame3.grid(row=5, columnspa=3) window.pack() -win.mainloop() \ No newline at end of file +win.mainloop() + +######################################### +# 废弃的代码 +######################################### +'''def KillAdbProgress(): + DisabledAndEnbled(True) + Return = GetCommandReturn("killall adb") + if Return is "": + Return = "进程已经杀死!" + messagebox.showinfo(title="tips", message=Return) + DisabledAndEnbled(False)''' + +'''def Button1Click(): + if combobox2.get() is "": + messagebox.showerror(title="提示", message="信息没有填写完整,无法继续连接 IP") + return + DisabledAndEnbled(True) + threading.Thread(target=ConnectPhoneIp).start()''' + +'''def ConnectPhoneIp(): + global phoneIp + messagebox.showinfo(title="提示", message=GetCommandReturn("adb connect '{}'".format(combobox2.get()))) + phoneIp.append(combobox2.get()) + combobox2['value'] = phoneIp + write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", str(json.dumps(ListToDictionary(phoneIp)))) # 将历史记录的数组转换为字典并写入 + DisabledAndEnbled(False)''' + +'''def ConnectPhoneIpDefult(quit = False): + global phoneIp + Return = GetCommandReturn("adb connect '192.168.250.2'") + if quit: + print(Return) + return + messagebox.showinfo(title="提示", message=Return) + phoneIp.append("192.168.250.2") + combobox2['value'] = phoneIp + write_txt(get_home() + "/.config/uengine-runner/PhoneIp.json", str(json.dumps(ListToDictionary(phoneIp)))) # 将历史记录的数组转换为字典并写入 + DisabledAndEnbled(False)''' + +'''def AdbRun(): + Return = GetCommandReturn("adb devices").replace("\n", "").replace("List of devices attached", "").replace("* daemon not running; starting now at tcp:5037", "").replace("* daemon started successfully", "") + if Return is "": + return False + return True''' + +'''def AdbConnect(): + return GetCommandReturn("adb devices")''' + +'''def ShowAdbConnect(): + messagebox.showinfo(title="提示", message=AdbConnect())''' + +'''def AboutAdb(): + messagebox.showinfo(message=GetCommandReturn("adb version"), title="关于 adb")''' + +'''def GetAllPackageName(): + return GetCommandReturn("adb shell pm list packages")''' + +'''def ShowAdbInstallPackage(): + mess = tk.Toplevel() + message = ttk.Frame(mess) + mess.resizable(0, 0) + mess.title("所有软件包") + textbox1 = tk.Text(message, width=100) + button1 = ttk.Button(message, text="确定", command=mess.withdraw) + textbox1.insert("0.0", GetAllPackageName()) + textbox1.configure(state=tk.DISABLED) + textbox1.pack() + button1.pack(side="bottom") + message.pack() + mess.mainloop()''' \ No newline at end of file diff --git a/spark-uengine-runner.deb b/spark-uengine-runner.deb index 6d750aca5f6876d8bb1a38a0e1d2f6f3395dc585..8cf59906772069deb9144111484cec605c41900e 100644 GIT binary patch literal 17556 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%0DS6#oL|htN$cmX+838F2$Z3qN?R!?$U*x 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 q7b)(