Created
November 17, 2025 13:44
-
-
Save samuelcaldas/58abc3fdd350b82dba11bc7cdd11b569 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Script PowerShell para duplicar aplicativos instalados em dispositivos Samsung Android usando segundo usuário. | |
| .DESCRIPTION | |
| Este script permite instalar uma cópia de aplicativos já instalados no dispositivo Android | |
| em um segundo usuário, incluindo suporte para split APKs. | |
| .NOTES | |
| Autor: Script gerado para duplicação de apps Android | |
| Requisitos: ADB no PATH do sistema | |
| #> | |
| # Configurações de cores e interface | |
| $Host.UI.RawUI.WindowTitle = "Android App Duplicator" | |
| $ErrorActionPreference = "Stop" | |
| # Função para verificar se ADB está disponível | |
| function Test-AdbAvailable { | |
| try { | |
| $adbVersion = adb version 2>&1 | |
| return $true | |
| } | |
| catch { | |
| Write-Host "`n[ERRO] ADB não encontrado no PATH do sistema." -ForegroundColor Red | |
| Write-Host "Por favor, instale o ADB e adicione ao PATH." -ForegroundColor Yellow | |
| return $false | |
| } | |
| } | |
| # Função para listar dispositivos conectados | |
| function Get-AdbDevices { | |
| $devices = adb devices | Select-Object -Skip 1 | Where-Object { $_ -match "\t" } | |
| $deviceList = @() | |
| foreach ($device in $devices) { | |
| if ($device -match "^([^\s]+)\s+device") { | |
| $deviceList += $matches[1] | |
| } | |
| } | |
| return $deviceList | |
| } | |
| # Função para selecionar dispositivo | |
| function Select-AdbDevice { | |
| param ( | |
| [string[]]$Devices | |
| ) | |
| if ($Devices.Count -eq 0) { | |
| Write-Host "`n[ERRO] Nenhum dispositivo Android conectado." -ForegroundColor Red | |
| Write-Host "Por favor, conecte um dispositivo com USB Debugging habilitado." -ForegroundColor Yellow | |
| return $null | |
| } | |
| if ($Devices.Count -eq 1) { | |
| Write-Host "`n[INFO] Dispositivo detectado: $($Devices[0])" -ForegroundColor Cyan | |
| return $Devices[0] | |
| } | |
| Write-Host "`n========================================" -ForegroundColor Cyan | |
| Write-Host " MÚLTIPLOS DISPOSITIVOS DETECTADOS" -ForegroundColor Cyan | |
| Write-Host "========================================" -ForegroundColor Cyan | |
| for ($i = 0; $i -lt $Devices.Count; $i++) { | |
| Write-Host " [$($i + 1)] $($Devices[$i])" -ForegroundColor White | |
| } | |
| Write-Host " [0] Cancelar" -ForegroundColor Yellow | |
| Write-Host "" | |
| do { | |
| $selection = Read-Host "Selecione o dispositivo (0-$($Devices.Count))" | |
| $selectionNum = 0 | |
| $validInput = [int]::TryParse($selection, [ref]$selectionNum) | |
| } while (-not $validInput -or $selectionNum -lt 0 -or $selectionNum -gt $Devices.Count) | |
| if ($selectionNum -eq 0) { | |
| return $null | |
| } | |
| return $Devices[$selectionNum - 1] | |
| } | |
| # Função para obter usuários disponíveis no dispositivo | |
| function Get-AndroidUsers { | |
| param ( | |
| [string]$DeviceSerial | |
| ) | |
| try { | |
| $usersOutput = adb -s $DeviceSerial shell pm list users 2>&1 | |
| $users = @() | |
| foreach ($line in $usersOutput) { | |
| if ($line -match "UserInfo\{(\d+):[^:]+:") { | |
| $userId = $matches[1] | |
| $users += [PSCustomObject]@{ | |
| Id = [int]$userId | |
| Info = $line | |
| } | |
| } | |
| } | |
| return $users | |
| } | |
| catch { | |
| Write-Host "`n[ERRO] Falha ao listar usuários: $($_.Exception.Message)" -ForegroundColor Red | |
| return @() | |
| } | |
| } | |
| # Função para selecionar usuário secundário automaticamente | |
| function Select-SecondaryUser { | |
| param ( | |
| [string]$DeviceSerial | |
| ) | |
| $users = Get-AndroidUsers -DeviceSerial $DeviceSerial | |
| if ($users.Count -eq 0) { | |
| Write-Host "`n[ERRO] Não foi possível listar usuários do dispositivo." -ForegroundColor Red | |
| return $null | |
| } | |
| # Filtra usuários secundários (ID > 0) | |
| $secondaryUsers = $users | Where-Object { $_.Id -gt 0 } | |
| if ($secondaryUsers.Count -eq 0) { | |
| Write-Host "`n[AVISO] Nenhum usuário secundário encontrado no dispositivo." -ForegroundColor Yellow | |
| Write-Host "Por favor, crie um segundo usuário nas configurações do Android." -ForegroundColor Yellow | |
| return $null | |
| } | |
| Write-Host "`n[INFO] Usuários encontrados:" -ForegroundColor Cyan | |
| foreach ($user in $users) { | |
| $userType = if ($user.Id -eq 0) { "Principal" } else { "Secundário" } | |
| Write-Host " - $($user.Info) [$userType]" -ForegroundColor White | |
| } | |
| # Seleciona automaticamente o primeiro usuário secundário | |
| $selectedUser = $secondaryUsers[0] | |
| Write-Host "`n[INFO] Usuário secundário selecionado: ID $($selectedUser.Id)" -ForegroundColor Green | |
| return $selectedUser.Id | |
| } | |
| # Função para obter lista de aplicativos com nomes amigáveis | |
| function Get-InstalledApps { | |
| param ( | |
| [string]$DeviceSerial | |
| ) | |
| Write-Host "`n[INFO] Carregando lista de aplicativos instalados..." -ForegroundColor Cyan | |
| try { | |
| # Lista pacotes de terceiros com paths | |
| $packagesOutput = adb -s $DeviceSerial shell pm list packages -3 -f --user 0 2>&1 | |
| $apps = @() | |
| foreach ($line in $packagesOutput) { | |
| if ($line -match "package:(.+)=(.+)$") { | |
| $apkPath = $matches[1] | |
| $packageName = $matches[2] | |
| # Tenta obter o nome amigável usando dumpsys | |
| $appLabel = $null | |
| try { | |
| $dumpsysOutput = adb -s $DeviceSerial shell "dumpsys package $packageName | grep -A 1 'labelRes='" 2>&1 | |
| # Fallback: usa o nome do pacote | |
| $appLabel = $packageName | |
| # Se não conseguir o nome amigável, usa o nome do pacote | |
| if ([string]::IsNullOrWhiteSpace($appLabel) -or $appLabel -eq $packageName) { | |
| # Tenta extrair o último componente do pacote como nome | |
| $parts = $packageName -split "\." | |
| if ($parts.Count -gt 0) { | |
| $appLabel = $parts[-1] | |
| } | |
| } | |
| } | |
| catch { | |
| $appLabel = $packageName | |
| } | |
| $apps += [PSCustomObject]@{ | |
| Index = $apps.Count + 1 | |
| Label = $appLabel | |
| Package = $packageName | |
| BasePath = $apkPath | |
| } | |
| } | |
| } | |
| return $apps | Sort-Object -Property Label | |
| } | |
| catch { | |
| Write-Host "`n[ERRO] Falha ao listar aplicativos: $($_.Exception.Message)" -ForegroundColor Red | |
| return @() | |
| } | |
| } | |
| # Função para exibir menu de seleção de app | |
| function Show-AppSelectionMenu { | |
| param ( | |
| [array]$Apps | |
| ) | |
| $pageSize = 15 | |
| $currentPage = 0 | |
| $totalPages = [Math]::Ceiling($Apps.Count / $pageSize) | |
| while ($true) { | |
| Clear-Host | |
| Write-Host "`n========================================" -ForegroundColor Cyan | |
| Write-Host " SELECIONE O APLICATIVO PARA DUPLICAR" -ForegroundColor Cyan | |
| Write-Host "========================================" -ForegroundColor Cyan | |
| Write-Host " Página $($currentPage + 1) de $totalPages" -ForegroundColor Yellow | |
| Write-Host "" | |
| $startIndex = $currentPage * $pageSize | |
| $endIndex = [Math]::Min($startIndex + $pageSize, $Apps.Count) | |
| for ($i = $startIndex; $i -lt $endIndex; $i++) { | |
| Write-Host " [$($i + 1)] $($Apps[$i].Label)" -ForegroundColor White | |
| Write-Host " Pacote: $($Apps[$i].Package)" -ForegroundColor DarkGray | |
| } | |
| Write-Host "" | |
| Write-Host " [N] Próxima página [P] Página anterior [0] Cancelar" -ForegroundColor Yellow | |
| Write-Host "" | |
| $selection = Read-Host "Selecione uma opção" | |
| if ($selection -eq "0") { | |
| return $null | |
| } | |
| elseif ($selection -eq "N" -or $selection -eq "n") { | |
| if ($currentPage -lt $totalPages - 1) { | |
| $currentPage++ | |
| } | |
| continue | |
| } | |
| elseif ($selection -eq "P" -or $selection -eq "p") { | |
| if ($currentPage -gt 0) { | |
| $currentPage-- | |
| } | |
| continue | |
| } | |
| else { | |
| $selectionNum = 0 | |
| $validInput = [int]::TryParse($selection, [ref]$selectionNum) | |
| if ($validInput -and $selectionNum -gt 0 -and $selectionNum -le $Apps.Count) { | |
| return $Apps[$selectionNum - 1] | |
| } | |
| else { | |
| Write-Host "`n[ERRO] Seleção inválida. Pressione Enter para continuar..." -ForegroundColor Red | |
| Read-Host | |
| } | |
| } | |
| } | |
| } | |
| # Função para obter todos os APKs (base + splits) | |
| function Get-AppApkPaths { | |
| param ( | |
| [string]$DeviceSerial, | |
| [string]$PackageName | |
| ) | |
| Write-Host "`n[INFO] Obtendo caminhos dos APKs..." -ForegroundColor Cyan | |
| try { | |
| $pathOutput = adb -s $DeviceSerial shell pm path $PackageName 2>&1 | |
| $apkPaths = @() | |
| foreach ($line in $pathOutput) { | |
| if ($line -match "package:(.+)$") { | |
| $apkPaths += $matches[1].Trim() | |
| } | |
| } | |
| if ($apkPaths.Count -eq 0) { | |
| Write-Host "[ERRO] Nenhum APK encontrado para o pacote." -ForegroundColor Red | |
| return $null | |
| } | |
| Write-Host "[INFO] Encontrados $($apkPaths.Count) arquivo(s) APK:" -ForegroundColor Green | |
| foreach ($path in $apkPaths) { | |
| $fileName = Split-Path -Leaf $path | |
| Write-Host " - $fileName" -ForegroundColor White | |
| } | |
| return $apkPaths | |
| } | |
| catch { | |
| Write-Host "`n[ERRO] Falha ao obter caminhos dos APKs: $($_.Exception.Message)" -ForegroundColor Red | |
| return $null | |
| } | |
| } | |
| # Função para instalar app no usuário secundário | |
| function Install-AppForUser { | |
| param ( | |
| [string]$DeviceSerial, | |
| [int]$UserId, | |
| [string[]]$ApkPaths | |
| ) | |
| Write-Host "`n[INFO] Iniciando instalação no usuário $UserId..." -ForegroundColor Cyan | |
| try { | |
| # Monta o comando de instalação | |
| $installCmd = "adb -s $DeviceSerial shell pm install --user $UserId" | |
| foreach ($apk in $ApkPaths) { | |
| $installCmd += " `"$apk`"" | |
| } | |
| Write-Host "[INFO] Executando instalação..." -ForegroundColor Cyan | |
| # Executa a instalação | |
| $result = Invoke-Expression $installCmd 2>&1 | |
| # Verifica o resultado | |
| $success = $false | |
| foreach ($line in $result) { | |
| if ($line -match "Success") { | |
| $success = $true | |
| break | |
| } | |
| } | |
| if ($success) { | |
| Write-Host "`n[SUCESSO] Aplicativo instalado com sucesso no usuário secundário!" -ForegroundColor Green | |
| return $true | |
| } | |
| else { | |
| Write-Host "`n[ERRO] Falha na instalação." -ForegroundColor Red | |
| Write-Host "Detalhes: $result" -ForegroundColor Yellow | |
| return $false | |
| } | |
| } | |
| catch { | |
| Write-Host "`n[ERRO] Exceção durante instalação: $($_.Exception.Message)" -ForegroundColor Red | |
| return $false | |
| } | |
| } | |
| # Função principal | |
| function Main { | |
| Clear-Host | |
| Write-Host "========================================" -ForegroundColor Cyan | |
| Write-Host " ANDROID APP DUPLICATOR" -ForegroundColor Cyan | |
| Write-Host "========================================" -ForegroundColor Cyan | |
| # Verifica se ADB está disponível | |
| if (-not (Test-AdbAvailable)) { | |
| Read-Host "`nPressione Enter para sair" | |
| exit 1 | |
| } | |
| # Lista e seleciona dispositivo | |
| Write-Host "`n[INFO] Verificando dispositivos conectados..." -ForegroundColor Cyan | |
| $devices = Get-AdbDevices | |
| $selectedDevice = Select-AdbDevice -Devices $devices | |
| if ($null -eq $selectedDevice) { | |
| Write-Host "`nOperação cancelada pelo usuário." -ForegroundColor Yellow | |
| Read-Host "`nPressione Enter para sair" | |
| exit 0 | |
| } | |
| # Seleciona usuário secundário | |
| $secondaryUser = Select-SecondaryUser -DeviceSerial $selectedDevice | |
| if ($null -eq $secondaryUser) { | |
| Read-Host "`nPressione Enter para sair" | |
| exit 1 | |
| } | |
| # Lista aplicativos instalados | |
| $apps = Get-InstalledApps -DeviceSerial $selectedDevice | |
| if ($apps.Count -eq 0) { | |
| Write-Host "`n[ERRO] Nenhum aplicativo encontrado." -ForegroundColor Red | |
| Read-Host "`nPressione Enter para sair" | |
| exit 1 | |
| } | |
| Write-Host "[SUCESSO] $($apps.Count) aplicativo(s) encontrado(s)." -ForegroundColor Green | |
| Read-Host "`nPressione Enter para continuar" | |
| # Exibe menu de seleção | |
| $selectedApp = Show-AppSelectionMenu -Apps $apps | |
| if ($null -eq $selectedApp) { | |
| Write-Host "`nOperação cancelada pelo usuário." -ForegroundColor Yellow | |
| Read-Host "`nPressione Enter para sair" | |
| exit 0 | |
| } | |
| Clear-Host | |
| Write-Host "`n========================================" -ForegroundColor Cyan | |
| Write-Host " INSTALANDO APLICATIVO" -ForegroundColor Cyan | |
| Write-Host "========================================" -ForegroundColor Cyan | |
| Write-Host " App: $($selectedApp.Label)" -ForegroundColor White | |
| Write-Host " Pacote: $($selectedApp.Package)" -ForegroundColor White | |
| Write-Host " Usuário: $secondaryUser" -ForegroundColor White | |
| Write-Host "" | |
| # Obtém caminhos dos APKs (incluindo splits) | |
| $apkPaths = Get-AppApkPaths -DeviceSerial $selectedDevice -PackageName $selectedApp.Package | |
| if ($null -eq $apkPaths) { | |
| Read-Host "`nPressione Enter para sair" | |
| exit 1 | |
| } | |
| # Instala o aplicativo | |
| $installSuccess = Install-AppForUser -DeviceSerial $selectedDevice -UserId $secondaryUser -ApkPaths $apkPaths | |
| if ($installSuccess) { | |
| Write-Host "`n========================================" -ForegroundColor Green | |
| Write-Host " INSTALAÇÃO CONCLUÍDA!" -ForegroundColor Green | |
| Write-Host "========================================" -ForegroundColor Green | |
| Write-Host "`nO aplicativo foi duplicado para o usuário secundário." -ForegroundColor White | |
| Write-Host "Troque para o usuário secundário no dispositivo para acessar." -ForegroundColor Yellow | |
| } | |
| else { | |
| Write-Host "`n========================================" -ForegroundColor Red | |
| Write-Host " INSTALAÇÃO FALHOU" -ForegroundColor Red | |
| Write-Host "========================================" -ForegroundColor Red | |
| Write-Host "`nVerifique as mensagens de erro acima." -ForegroundColor Yellow | |
| } | |
| Read-Host "`nPressione Enter para sair" | |
| } | |
| # Executa a função principal | |
| Main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment