import a.fs
import a.os
import a.time
import ..consts::_template_dir
class SDK {
r###"
Logically wraps the Android SDK
"###
[android_sdk_path]
static def __call() = {
"Do some minimal validity check on the sdk path"
__malloc(SDK, [_guess_sdk_root()])
}
def _emulator(self) = self.android_sdk_path.join('emulator', 'emulator')
def _adb(self) = self.android_sdk_path.join('platform-tools', 'adb')
def materialize(self, outpath, project) {
"""
Materialize an android project to the given path
"""
fs::copy(_template_dir.join('base'), outpath)
}
def _run_args(self) = Table(
encoding=:utf8,
clear_envs=true,
envs=[
:ANDROID_SDK_ROOT: self.android_sdk_path,
:PATH: os::env('PATH'),
],
)
def _run(self, *args, **kwargs) = {
kwargs = (self._run_args() + kwargs)
os::run(
*args,
**kwargs,
)
}
def _gradlew(self, project_path) = {
if os::name == "windows" {
"""
process::Command can't really deal with UNC paths...
So as a workaround, just strip the prefix for now
"""
path_str = str(project_path.canon())
path_str = path_str.lstrip(r'\\?\')
path_str + r'\gradlew.bat'
} else {
project_path.canon().join('gradlew')
}
}
def _run_gradlew(self, project_path, cmdargs, *args, **kwargs) = {
if os::name == 'windows' {
self._run(
self._gradlew(project_path),
cmdargs,
dir=project_path,
*args,
**kwargs,
)
} else {
r###"
We call sh instead of the script directly so that
gradlew doesn't need executable permissions
"###
self._run(
'sh',
[self._gradlew(project_path)] + cmdargs,
dir=project_path,
*args,
**kwargs,
)
}
}
def _start(self, *args, **kwargs) = {
os::Process(
*args,
**(self._run_args() + kwargs),
)
}
def build(self, project_path) {
r###"
Builds the android app at the given path
"###
self._run_gradlew(
project_path,
['build'],
)
}
def install(self, project_path) {
r###"
Installs the android app at the given path
"###
self._run_gradlew(
project_path,
['installDebug'],
)
}
def _get_emulator_name(self) = {
r###"
Tries to get the name of some emulator available
on the system
"###
self._run(
self._emulator(),
['-list-avds'],
stdout=:pipe,
)[1].trim().lines().list()[-1]
}
def _start_emulator(self) = {
emulator_name = self._get_emulator_name()
self._start(
self._emulator(),
['-avd', emulator_name, '-port', '5554'],
)
self._wait_for_emulator_ready()
}
def _wait_for_emulator_ready(self) {
self._run(self._adb(), ['wait-for-device'])
}
def _emulator_started(self) = {
[_status, stdout, _stderr] = self._run(
self._adb(),
['devices', '-l'],
stdout=:pipe,
encoding=:utf8,
)
stdout.trim().lines().list().len() > 1
}
def start_emulator(self) = {
r###"
Starts an emulator if none found,
and waits for it to be ready
"###
if self._emulator_started() {
self._wait_for_emulator_ready()
nil
} else {
self._start_emulator()
}
}
}
def _guess_sdk_root() = {
def _failure() {
__raise(
RuntimeError,
'Could not find android sdk root (maybe set ANDROID_SDK_ROOT?)',
)
}
path = os::env(:ANDROID_SDK_ROOT)
if path {
Path(path)
} elif os::name == 'windows' {
path = Path((os::home() or _failure()).join(r'AppData\Local\Android\Sdk'))
if path.is_dir() {
path
} else {
_failure()
}
} elif os::name == 'macos' {
path = Path((os::home() or _failure()).join(r'Library/Android/sdk'))
if path.is_dir() {
path
} else {
_failure()
}
} else {
_failure()
}
}