@@ -666,6 +666,119 @@ class DownloadService extends EventEmitter implements DownloadAPI {
666666 throw error // Re-throw so the IPC handler logs it
667667 }
668668 }
669+
670+ public async installManualFile ( filePath : string , deviceId : string ) : Promise < boolean > {
671+ console . log ( `[Service] Manual install requested for ${ filePath } on device ${ deviceId } ` )
672+
673+ // Check if the app is connected to the target device
674+ const targetDeviceForInstall = this . getTargetDeviceForInstallation ( )
675+ if ( ! targetDeviceForInstall ) {
676+ console . error (
677+ `[Service installManualFile] App is not connected to any device. Cannot install ${ filePath } .`
678+ )
679+ return false
680+ }
681+
682+ if ( targetDeviceForInstall !== deviceId ) {
683+ console . error (
684+ `[Service installManualFile] App is connected to ${ targetDeviceForInstall } but installation requested for ${ deviceId } .`
685+ )
686+ return false
687+ }
688+
689+ // Check if the target device is still connected and authorized at the ADB level
690+ try {
691+ const devices = await this . adbService . listDevices ( )
692+ const targetDevice = devices . find ( ( d ) => d . id === deviceId && d . type === 'device' )
693+ if ( ! targetDevice ) {
694+ console . error (
695+ `[Service installManualFile] Target device ${ deviceId } not found or not authorized at ADB level.`
696+ )
697+ return false
698+ }
699+ } catch ( err ) {
700+ console . error ( `[Service installManualFile] Error verifying target device ${ deviceId } :` , err )
701+ return false
702+ }
703+
704+ // Check if the file/folder exists
705+ if ( ! existsSync ( filePath ) ) {
706+ console . error ( `[Service installManualFile] File/folder not found: ${ filePath } ` )
707+ return false
708+ }
709+
710+ try {
711+ const stats = await fs . stat ( filePath )
712+
713+ if ( stats . isFile ( ) && filePath . toLowerCase ( ) . endsWith ( '.apk' ) ) {
714+ // Single APK file installation
715+ console . log ( `[Service installManualFile] Installing single APK: ${ filePath } ` )
716+ const success = await this . adbService . installPackage ( deviceId , filePath , {
717+ flags : [ '-r' , '-g' ]
718+ } )
719+ if ( success ) {
720+ console . log ( `[Service installManualFile] Successfully installed APK: ${ filePath } ` )
721+ this . emit ( 'installation:success' , deviceId )
722+ }
723+ return success
724+ } else if ( stats . isDirectory ( ) ) {
725+ // Folder installation - create a temporary DownloadItem to use existing installation logic
726+ console . log ( `[Service installManualFile] Installing folder: ${ filePath } ` )
727+
728+ // Generate a unique identifier for this manual installation
729+ const manualId = `manual-${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 9 ) } `
730+
731+ // Try to extract package name from folder structure if possible
732+ let packageName = ''
733+ try {
734+ const folderContents = await fs . readdir ( filePath )
735+ const apkFiles = folderContents . filter ( ( f ) => f . toLowerCase ( ) . endsWith ( '.apk' ) )
736+ if ( apkFiles . length > 0 ) {
737+ // Look for potential package directory (common pattern in extracted games)
738+ const potentialPackageDirs = folderContents . filter ( ( item ) => {
739+ // Common package name patterns
740+ return item . includes ( '.' ) && ! item . includes ( ' ' ) && item . length > 5
741+ } )
742+ if ( potentialPackageDirs . length === 1 ) {
743+ packageName = potentialPackageDirs [ 0 ]
744+ }
745+ }
746+ } catch ( error ) {
747+ console . log ( `[Service installManualFile] Could not analyze folder structure: ${ error } ` )
748+ }
749+
750+ // Create a temporary DownloadItem
751+ const tempItem : DownloadItem = {
752+ gameId : manualId ,
753+ releaseName : manualId ,
754+ packageName : packageName ,
755+ gameName : `Manual Install: ${ filePath . split ( / [ / \\ ] / ) . pop ( ) } ` ,
756+ status : 'Completed' ,
757+ progress : 100 ,
758+ extractProgress : 100 ,
759+ addedDate : Date . now ( ) ,
760+ downloadPath : filePath
761+ }
762+
763+ // Use the installation processor to handle the folder
764+ const success = await this . installationProcessor . startInstallation ( tempItem , deviceId )
765+ if ( success ) {
766+ console . log ( `[Service installManualFile] Successfully installed folder: ${ filePath } ` )
767+ this . emit ( 'installation:success' , deviceId )
768+ }
769+ return success
770+ } else {
771+ console . error ( `[Service installManualFile] Unsupported file type: ${ filePath } ` )
772+ return false
773+ }
774+ } catch ( error ) {
775+ console . error (
776+ `[Service installManualFile] Error during manual installation of ${ filePath } :` ,
777+ error
778+ )
779+ return false
780+ }
781+ }
669782}
670783
671784export default new DownloadService ( )
0 commit comments