1111import sys
1212import os
1313
14- from util .resource import get_root_dir , resource_path , resolve_platform_tool
14+ from util .resource import get_root_dir , resource_path
15+ from util .toolpaths import ToolPaths
1516root_dir = get_root_dir ()
1617if root_dir not in sys .path :
1718 sys .path .insert (0 , root_dir )
1819
1920import subprocess
2021import math
2122from PyQt6 .QtWidgets import (QFileDialog , QDialog , QVBoxLayout , QHBoxLayout , QLabel ,
22- QLineEdit , QPushButton , QMessageBox , QTextEdit )
23+ QLineEdit , QPushButton , QMessageBox , QTextEdit , QCheckBox )
2324from PyQt6 .QtCore import Qt , QThread , pyqtSignal
2425import threading
2526
@@ -100,7 +101,7 @@ class DeviceInfoWorker(QThread):
100101 def __init__ (self , platform_tools_path ):
101102 super ().__init__ ()
102103 self .platform_tools_path = platform_tools_path
103- self .adb_path = resolve_platform_tool ( platform_tools_path , "adb" )
104+ self .adb_path = ToolPaths . instance (). adb
104105
105106 def run (self ):
106107 commands = self ._get_device_commands ()
@@ -141,7 +142,9 @@ def run(self):
141142 self .info_ready .emit ("\n " .join (results ))
142143
143144 def _get_device_commands (self ):
144- adb_cmd = f'"{ self .adb_path } "'
145+ from util .devicemanager import DeviceManager
146+ serial_flag = DeviceManager .instance ().serial_flag ()
147+ adb_cmd = f'"{ self .adb_path } " { serial_flag } '
145148 return {
146149 "Fingerprint" : f"{ adb_cmd } shell getprop ro.build.fingerprint" ,
147150 "Board" : f"{ adb_cmd } shell getprop ro.product.board" ,
@@ -227,10 +230,90 @@ def _start_info_worker(self, platform_tools_path):
227230
228231# [Legacy WirelessADBDialog removed - now using modules.wirelessadb]
229232
233+ class InstallFlagsDialog (QDialog ):
234+ def __init__ (self , current_flags , parent = None ):
235+ super ().__init__ (parent )
236+ self .current_flags = current_flags .copy () if current_flags else {}
237+ self .result_flags = self .current_flags .copy ()
238+ self ._setup_ui ()
239+
240+ def _setup_ui (self ):
241+ self .setWindowTitle ("Install Flags" )
242+ self .setMinimumWidth (450 )
243+ self .setWindowFlags (self .windowFlags () | Qt .WindowType .WindowStaysOnTopHint )
244+ self .setModal (True )
245+
246+ layout = QVBoxLayout ()
247+
248+ self .checkboxes = {}
249+ flags_info = [
250+ ("-r" , "replace existing application" ),
251+ ("-t" , "allow test packages" ),
252+ ("-d" , "allow version code downgrade (debuggable packages only)" ),
253+ ("-p" , "partial application install (install-multiple only)" ),
254+ ("-g" , "grant all runtime permissions" ),
255+ ("--instant" , "cause the app to be installed as an ephemeral install app" ),
256+ ("--no-streaming" , "always push APK to device and invoke Package Manager as separate steps" ),
257+ ("--streaming" , "force streaming APK directly into Package Manager" )
258+ ]
259+
260+ for flag , desc in flags_info :
261+ cb = QCheckBox (f"{ flag } : { desc } " )
262+ if self .current_flags .get (flag , False ):
263+ cb .setChecked (True )
264+ self .checkboxes [flag ] = cb
265+ layout .addWidget (cb )
266+
267+ # ABI flag
268+ abi_layout = QHBoxLayout ()
269+ self .abi_cb = QCheckBox ("--abi: override platform's default ABI" )
270+ self .abi_cb .setChecked ("--abi" in self .current_flags )
271+
272+ self .abi_entry = QLineEdit ()
273+ self .abi_entry .setPlaceholderText ("e.g. arm64-v8a" )
274+ self .abi_entry .setEnabled (self .abi_cb .isChecked ())
275+ if "--abi" in self .current_flags :
276+ self .abi_entry .setText (self .current_flags ["--abi" ])
277+
278+ self .abi_cb .toggled .connect (self .abi_entry .setEnabled )
279+
280+ abi_layout .addWidget (self .abi_cb )
281+ abi_layout .addWidget (self .abi_entry )
282+ layout .addLayout (abi_layout )
283+
284+ # Buttons
285+ btn_layout = QHBoxLayout ()
286+ btn_layout .addStretch ()
287+ cancel_btn = QPushButton ("Cancel" )
288+ cancel_btn .clicked .connect (self .reject )
289+ apply_btn = QPushButton ("Apply" )
290+ apply_btn .clicked .connect (self ._apply )
291+ btn_layout .addWidget (cancel_btn )
292+ btn_layout .addWidget (apply_btn )
293+ layout .addLayout (btn_layout )
294+ self .setLayout (layout )
295+
296+ def _apply (self ):
297+ self .result_flags .clear ()
298+ for flag , cb in self .checkboxes .items ():
299+ if cb .isChecked ():
300+ self .result_flags [flag ] = True
301+
302+ if self .abi_cb .isChecked () and self .abi_entry .text ().strip ():
303+ self .result_flags ["--abi" ] = self .abi_entry .text ().strip ()
304+
305+ if self .result_flags .get ("--streaming" , False ) and self .result_flags .get ("--no-streaming" , False ):
306+ QMessageBox .warning (self , "Conflict" , "You cannot select both --streaming and --no-streaming." )
307+ return
308+
309+ self .accept ()
310+
311+
230312class InstallAPKDialog (QDialog ):
231313 def __init__ (self , parent = None ):
232314 super ().__init__ (parent )
233315 self .parent_app = parent
316+ self .install_flags = {}
234317 self ._setup_ui ()
235318
236319 def _setup_ui (self ):
@@ -256,14 +339,27 @@ def _setup_ui(self):
256339
257340 layout .addLayout (path_layout )
258341
259- # Install button
342+ # Setup custom buttons
343+ action_layout = QHBoxLayout ()
344+
345+ self .flags_btn = QPushButton ("Flags" )
346+ self .flags_btn .clicked .connect (self ._open_flags )
347+
260348 install_btn = QPushButton ("Install APK" )
261349 install_btn .clicked .connect (self ._install_apk )
262350 install_btn .setDefault (True )
263- layout .addWidget (install_btn )
351+
352+ action_layout .addWidget (self .flags_btn )
353+ action_layout .addWidget (install_btn )
354+ layout .addLayout (action_layout )
264355
265356 self .setLayout (layout )
266357
358+ def _open_flags (self ):
359+ dialog = InstallFlagsDialog (self .install_flags , self )
360+ if dialog .exec () == QDialog .DialogCode .Accepted :
361+ self .install_flags = dialog .result_flags
362+
267363 def _browse_apk (self ):
268364 file_path , _ = QFileDialog .getOpenFileName (
269365 self , "Select APK File" , "" , "APK Files (*.apk);;All Files (*)"
@@ -282,8 +378,18 @@ def _install_apk(self):
282378 return
283379
284380 if hasattr (self .parent_app , 'run_command_async' ):
381+ flag_str = ""
382+ for k , v in self .install_flags .items ():
383+ if k == "--abi" :
384+ flag_str += f" --abi { v } "
385+ else :
386+ flag_str += f" { k } "
387+ flag_str = flag_str .strip ()
388+
389+ cmd = f'adb install { flag_str } "{ apk_path } "' if flag_str else f'adb install "{ apk_path } "'
390+
285391 self .parent_app .run_command_async (
286- f'adb install " { apk_path } "' ,
392+ cmd ,
287393 f"Installing { os .path .basename (apk_path )} " ,
288394 "ADB"
289395 )
@@ -518,13 +624,9 @@ def sideload_file(self):
518624
519625
520626def show_wireless_adb_ui (self ):
521- import os
522627 from modules .wirelessadb import WirelessADBDialog
523628 # Show wireless ADB connection dialog (new QR pairing module)
524- # Passed parent and platform_tools_path dynamically since parent represents quickadb.py
525- adb_exe = "adb.exe" if os .name == 'nt' else "adb"
526- adb_path = os .path .join (self .platform_tools_path , adb_exe )
527- dialog = WirelessADBDialog (self , adb_path )
629+ dialog = WirelessADBDialog (self , ToolPaths .instance ().adb )
528630 dialog .exec ()
529631
530632
0 commit comments