Android.psm1


<#
# Android utilities
#>



$global:RD_SD = 'sdcard/'

#region ADB completion

$script:AdbCommands = @(
    'push', 'pull', 'connect', 'disconnect', 'tcpip',
    'start-server', 'kill-server', 'shell', 'usb',
    'devices', 'install', 'uninstall'
    #todo...
)

Register-ArgumentCompleter -Native -CommandName adb -ScriptBlock {
    param (
        $wordToComplete,
        $commandAst,
        $fakeBoundParameters
    )
    
    $script:AdbCommands | Where-Object {
        $_ -like "$wordToComplete*"
    } | ForEach-Object {
        "$_"
    }
}


#endregion

#region [IO]

enum AdbDestination {
    Remote
    Local
}

enum Direction {
    Up
    Down
}

<#
function Adb-SyncItems {
    param (
        [Parameter(Mandatory = $true)]
        [string]$remote,
        [Parameter(Mandatory = $false)]
        [string]$local,
        [Parameter(Mandatory = $true)]
        [AdbDestination]$d
    )
     
    #$remoteItems | ?{($localItems -notcontains $_)}
     
    #| sed "s/ /' '/g"
    #https://stackoverflow.com/questions/45041320/adb-shell-input-text-with-space
     
    $localItems = Get-ChildItem -Name
    $remoteItems = Adb-GetItems $remote
    $remote = Adb-Escape $remote Exchange
     
    #wh $remote
    switch ($d) {
        Remote {
            $m = Get-Difference $remoteItems $localItems
             
            foreach ($x in $m) {
                (adb push $x $remote)
            }
        }
        Local {
            $m = Get-Difference $localItems $remoteItems
             
            foreach ($x in $m) {
                (adb pull "$remote/$x")
            }
        }
        Default {
        }
    }
    return $m
} #>


<#
.Description
ADB enhanced passthru
#>

function adb {
    
    $argBuf = @()
    $argBuf += $args
    
    Write-Verbose "Original args: $(Write-Quick $($argBuf -join ' '))`n"
    
    switch ($argBuf[0]) {
        'push' {
            if ($argBuf.Length -lt 3) {
                $argBuf += 'sdcard/'
            }
        }
        Default {
        }
    }
    
    Write-Verbose "Final args: $(Write-Quick $($argBuf -join ' '))"
    
    adb.exe @argBuf
}

<#
.Description
Deletes file
#>

function Adb-RemoveItem {
    param (
        [Parameter(Mandatory = $true)]
        [string]$src
    )
    $a = @(Remove-Item "$src")
    Invoke-AdbCommand @a
}

function Adb-Copy {
    param (
        $v
    )
    $s = "am start-activity -a android.intent.action.SEND -e android.intent.extra.TEXT '$v' -t 'text/plain' com.tengu.sharetoclipboard"
    $vv=@('shell',$s)
    return adb @vv

}



function Adb-GetDevices {
    $d = (adb devices) -as [string[]]
    $d = $d[1..($d.Length)]
    return $d
}

function Adb-QPush {
    
    param (
        $f,
        [parameter(Mandatory = $false)]
        $d = 'sdcard/'
    )

    if ($f -is [array]) {
        $f | Invoke-Parallel -Parameter $d -ImportVariables -Quiet -ScriptBlock {
            adb push "$_" $parameter
        }
    }
    else {
        adb push $f $d
    }
    
}


function Adb-QPull {
    param(
        $r, 
        [parameter(Mandatory = $false)] 
        $d = $(Get-Location), 
        [switch]$retain
    )
    
    
    if (-not (Test-Path $d)) {
        mkdir $d
    }
    $d = Resolve-Path $d

    Write-Host "$d"
    Read-Host -Prompt "..."
    $r = Adb-GetItems $r -t 'f'


    $st1 = Get-Date

    $r | Invoke-Parallel -Parameter $d -ImportVariables -Quiet -ScriptBlock {
        
        $vx = adb pull $_ "$parameter"
        Write-Verbose "adb >>> $vx"
        

        # $sz = (Get-ChildItem $parameter).count
        $global:SyncState.Counter++
        Write-Host "`r$($global:SyncState.Counter)" -NoNewline

        # $SyncTable.c++
        # Write-Host "`r $($SyncTable.c)/$($SyncTable.l)" -NoNewline

        <# Write-Progress -Activity g -PercentComplete (($i / $l) * 100.0) #>
    }
    
    Write-Host
    $st2 = Get-Date
    $delta = $st2 - $st1
    Write-Host "$($delta.totalseconds)"
    #todo
    
    $global:SyncState = $global:SyncStateEmpty.psobject.copy()
}

$global:SyncStateEmpty = [hashtable]::Synchronized(
    @{
        c       = 0 
        l       = 0
        Counter = 0
    }
)

$global:SyncState = $global:SyncStateEmpty.psobject.copy()

function Adb-GetItems {
    [CmdletBinding()]
    [outputtype([string[]])]
    param (
        [Parameter()]
        $x,
        [Parameter(Mandatory = $false)]
        $t = 'f',
        [parameter(Mandatory = $false)]
        $Pattern
    )
    
    $r = Adb-Find -x $x -type $t
    $r = [string[]] ($r | Sort-Object)

    if ($pattern) {
        $r = $r | Where-Object {
            $_ -match $pattern 
        }
    }
    
    return $r
}


#endregion

Set-Alias Adb-Shell Invoke-AdbCommand
Set-Alias Adb-GetItem Adb-Stat

function Invoke-AdbCommand {
    $rg = @('shell', $args)
    adb @rg
}

function Adb-SendInput {
    param([parameter(ValueFromRemainingArguments)] $a)
    $x = @('input', $a)
    Invoke-AdbCommand @x
}

function Adb-SendFastSwipe {
    param (
        [Parameter(Mandatory = $false)]
        [Direction]$d,
        [Parameter(Mandatory = $false)]
        [int]$t,
        [Parameter(Mandatory = $false)]
        [int]$c
    )
    
    if (!($t)) {
        $t = 25
    }
    
    if (!($d)) {
        $d = [Direction]::Down
    }
    
    while ($c-- -gt 0) {
        switch ($d) {
            Down {
                Adb-SendInput "swipe 500 1000 300 300 $t"
            }
            Up {
                Adb-SendInput "swipe 300 300 500 1000 $t"
            }
        }
        Start-Sleep -Milliseconds $t
    }
}


function Adb-Find {
    [CmdletBinding()]
    [outputtype([string[]])]
    param (
        # [Parameter(Mandatory = $true)]
        $x,
        
        [Parameter(Mandatory = $false)]
        $name,

        [parameter(Mandatory = $false)]
        $type,

        [parameter(Mandatory = $false)]
        $maxdepth = -1
    )
    #find . ! -name . -prune -type f -exec ls -ldi {} +
    #adb shell "find sdcard/* ! -name . -prune -type f -exec ls -ldi {} +"
    #adb shell "find sdcard/* ! -name . -prune -type f -exec ls -la {} \;"
    #adb shell "find sdcard/* ! -name . -prune -type f -exec ls -F {} \;"

    $fa = "%p\\n"
    # $ig = "2>&1 | grep -v `"Permission denied`""
    $a = @("find $x")
    #find 'sdcard/*' -type 'f' -maxdepth 0
    if ($name) {
        $a += '-name', $name
    }
    if ($type) {
        $a += '-type', $type
    }
    if ($maxdepth -ne -1) {
        $a += '-maxdepth', $maxdepth
    }
    $a += '-printf', $fa
    
    $r = Invoke-AdbCommand @a
    
    #TODO

    # $r = $r[1..$r.Length]
    
    return $r
}

function Adb-Stat {
    param (
        $x
    )
    
    $x = Adb-Escape -x $x -e Shell
    
    $d = " "
    $a = "%n", "%N", "%F", "%w", "%x", "%y", "%z", "%s" -join $d
    $cmd = @("stat -c '$a' $x")
    $out = [string] (Invoke-AdbCommand @cmd)
    $rg = $out -split $d
    $i = 0

    $obj = [PSCustomObject]@{
        Name             = $rg[$i++]
        FullName         = $rg[$i++]
        Type             = $rg[$i++]
        TimeOfBirth      = $rg[$i++]
        LastAccess       = $rg[$i++]
        LastModification = $rg[$i++]
        LastStatusChange = $rg[$i++]
        Size             = $rg[$i++]
        
        IsDirectory      = $null
        IsFile           = $null
        
        Raw              = $out
        Input            = $cmd
    }

    $obj.IsDirectory = $obj.Type -match 'directory'
    $obj.IsFile = $obj.Type -match 'file'
    
    <# $cmd = @('shell', "stat $x")
    $out = [string] (adb @cmd)
    $rg = $out -split "`n" | ForEach-Object { $_.Trim() } #>


    
    return $obj
}

<#
function Adb-GetItem {
     
    param($x)
    return Adb-Stat $x
 
    # param (
    # $x,
    # [Parameter(Mandatory = $false)]
    # $x2
    # )
         
    # $a = @('shell', "wc -c $x", '2>&1')
    # $x = adb @a
    # $x = [string]$x
    # Write-Debug "$x| $(typeof $x)"
 
    # if ($x -match 'Is a directory') {
    # $isDir = $true
    # $isFile = $false
    # }
    # elseif ($x -match ('No such')) {
    # #...
    # return
    # }
    # else {
    # $isDir = $false
    # $isFile = $true
    # $size = [int]$x.Split(' ')[0]
    # }
     
    # $buf = @{
    # IsFile = $isFile
    # IsDirectory = $isDir
    # Size = $size
    # Orig = $x
    # }
     
     
    # return $buf
} #>


function Adb-GetPackages {
    $aa = @( 'pm list packages -f')
    return (Invoke-AdbCommand @aa)
}


enum EscapeType {
    Shell
    Exchange
    Simple
}


function Adb-Escape {
    param (
        [Parameter(Mandatory = $true)]
        [string]$x,
        [Parameter(Mandatory = $false)]
        [EscapeType]$e = 'Simple'
    )
    
    switch ($e) {
        Shell {
            # $x = $x.Replace('`', [string]::Empty)
            $x = $x.Replace(' ', '\ ')
            .Replace('(', '\(')
            .Replace(')', '\)')
            .Replace('"', '\"')
            
            return $x
        }
        Exchange {
            $s = $x.Split('/')
            $x3 = New-List 'string'
            
            foreach ($b in $s) {
                if ($b.Contains(' ')) {
                    $b2 = "`"$b/`""
                }
                else {
                    $b2 = $b
                }
                $x3.Add($b2)
            }
            return PathJoin($x3, '/')
        }
        Simple {
            return $x.Replace('"', '\"')
        }
        default {

        }
    }
}

function Adb-HandleSettings {
    param (
        $Operation,    $Scope, $Name
    )

    return adb shell settings $Operation $Scope $Name @args
}

<# function Adb-GetAccessibility {
    [outputtype([string[]])]
    $s = Adb-HandleSettings 'get' 'secure' 'enabled_accessibility_services'
 
    $s2 = ($s -split '/') -as [string[]]
    return $s2
}
 
function Adb-AddAccessibility {
    param($n)
    $s2 = Adb-GetAccessibility
    $s2 += $n
    Adb-SetAccessibility $s2
}
 
function Adb-SetAccessibility {
    param($s2)
    $v = $s2 -join '/'
    Adb-HandleSettings 'put' 'secure' 'enabled_accessibility_services' $v
}
 
function Adb-RemoveAccessibility {
    param($n)
    $s2 = (Adb-GetAccessibility | Where-Object { $_ -ne $n })
    Adb-SetAccessibility $s2
} #>



# region Bluetooth

function Blt-Send {
    param($name, $f)

    return Start-Job -Name 'Blt' -ScriptBlock { 
        btobex.exe -n $using:name $using:f
    }
}

# endregion