Skip to content

Commit

Permalink
- Added ADB Setup
Browse files Browse the repository at this point in the history
- Added Command Executor for executing commands.
- Added Generic Loading Dialog
- Saved ADB path to storage
- TODO : Need to add Device options if ADB setup is done
- Updated kotlin_lint.yml
  • Loading branch information
iammohdzaki committed Mar 9, 2024
1 parent cd2c737 commit ee1dc9e
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 22 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/kotlin_lint.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: kotlin_lint

on:
push:
branches:
- "*"
pull_request:
paths:
- "**/*.kt"
Expand Down
87 changes: 71 additions & 16 deletions src/jvmMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.*
import command.CommandBuilder
import command.CommandExecutor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import local.FileStorageHelper
import ui.Styles
import ui.components.CheckboxWithText
import ui.components.ChooseFileTextField
import ui.components.CustomTextField
import ui.components.*
import utils.*
import java.awt.Desktop
import java.awt.FileDialog
Expand All @@ -35,7 +35,7 @@ import utils.Strings

@Composable
@Preview
fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
fun App(fileStorageHelper: FileStorageHelper, savedPath: String?, adbSavedPath: String?) {
val density = LocalDensity.current // to calculate the intrinsic size of vector images (SVG, XML)
val coroutineScope = rememberCoroutineScope()
var logs by remember { mutableStateOf("") }
Expand All @@ -57,28 +57,69 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
var keyPassword by remember { mutableStateOf("") }
var isAutoUnzip by remember { mutableStateOf(true) }
var savedJarPath by remember { mutableStateOf(savedPath) }
var isAdbSetupDone by remember { mutableStateOf(false) }
var adbPath by remember { mutableStateOf("") }
var showLoadingDialog by remember { mutableStateOf(Pair("", false)) }


//TODO: KNOWN ISSUE - Can't update file path once saved, For now Delete path.kb file inside storage directory.
//TODO: (Fixed this issue need to test more!) - Can't update file path once saved, For now Delete path.kb file inside storage directory.
savedJarPath?.let {
bundletoolPath = it
saveJarPath = true
}

//Check if ADB Setup is Done or Not
adbSavedPath?.let {
adbPath = it
isAdbSetupDone = true
}

if (isOpen && !isLoading) {
FileDialog { fileName, directory ->
isOpen = false
if (fileName.isNullOrEmpty() || directory.isNullOrEmpty()) {
return@FileDialog
}
when (fileDialogType) {
FileDialogType.BUNDLETOOL -> bundletoolPath = "${directory}$fileName"
FileDialogType.AAPT2 -> aapt2Path = "${directory}$fileName"
FileDialogType.KEY_STORE_PATH -> keyStorePath = "${directory}$fileName"
FileDialogType.ADB_PATH -> {
adbPath = "${directory}${fileName}"
//Show Loading Here
showLoadingDialog = Pair(Strings.VERIFYING_ADB_PATH, true)
CommandExecutor().executeCommand(
CommandBuilder()
.verifyAdbPath(true, adbPath)
.getAdbVerifyCommand(), coroutineScope,
onSuccess = {
logs += it
isAdbSetupDone = true
Log.i("Saving Path in DB $adbPath")
fileStorageHelper.save(DBConstants.ADB_PATH, adbPath)
//Hide Loading
Thread.sleep(1000L)
showLoadingDialog = Pair(Strings.VERIFYING_ADB_PATH, false)
},
onFailure = {
logs += it
isAdbSetupDone = false
//Hide Loading
showLoadingDialog = Pair(Strings.VERIFYING_ADB_PATH, false)
}
)
}

else -> {
aabFilePath = Pair(directory, fileName)
}
}
}
}

if (showLoadingDialog.second) {
LoadingDialog(showLoadingDialog.first)
}

if (isExecute) {
isExecute = false
//Get Command to Execute
Expand Down Expand Up @@ -158,13 +199,26 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start
) {

Spacer(modifier = Modifier.padding(12.dp))
Text(
text = Strings.APP_NAME,
style = Styles.TextStyleBold(28.sp),
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp)
)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = Strings.APP_NAME,
style = Styles.TextStyleBold(28.sp),
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp)
)
ButtonWithToolTip(
if (isAdbSetupDone) Strings.ABD_SETUP_DONE else Strings.SETUP_ADB,
onClick = {
fileDialogType = FileDialogType.ADB_PATH
isOpen = true
},
Strings.SETUP_ADB_INFO,
icon = if (isAdbSetupDone) "done" else "info",
)
}
Spacer(modifier = Modifier.padding(8.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
Expand Down Expand Up @@ -412,7 +466,7 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
@Composable
private fun FileDialog(
parent: Frame? = null,
onCloseRequest: (fileName: String, directory: String) -> Unit
onCloseRequest: (fileName: String?, directory: String?) -> Unit
) = AwtWindow(
create = {
object : FileDialog(parent, Strings.CHOOSE_FILE, LOAD) {
Expand All @@ -431,16 +485,17 @@ private fun FileDialog(
fun main() = application {
val fileStorageHelper = FileStorageHelper()
//Check if path for bundletool exists in local storage
val path = fileStorageHelper.read("path") as String?
val path = fileStorageHelper.read(DBConstants.BUNDLETOOL_PATH) as String?
val adbPath = fileStorageHelper.read(DBConstants.ADB_PATH) as String?
Log.showLogs = true
Window(
onCloseRequest = ::exitApplication,
state = rememberWindowState(
width = 1000.dp, height = 1000.dp,
width = 1200.dp, height = 1000.dp,
position = WindowPosition(Alignment.Center)
),
title = Strings.APP_NAME
) {
App(fileStorageHelper, path)
App(fileStorageHelper, path, adbPath)
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package command

import utils.SigningMode
import utils.Utils

Expand All @@ -13,6 +15,7 @@ class CommandBuilder {
private var keyStorePassword: String = ""
private var keyAlias: String = ""
private var keyPassword: String = ""
private var adbVerifyCommandExecute = Pair(false, "")

fun bundletoolPath(path: String) = apply { this.bundletoolPath = path }
fun aabFilePath(path: Pair<String, String>) = apply { this.aabFilePath = path }
Expand All @@ -26,6 +29,16 @@ class CommandBuilder {
fun keyAlias(alias: String) = apply { this.keyAlias = alias }
fun keyPassword(password: String) = apply { this.keyPassword = password }

fun verifyAdbPath(value: Boolean, path: String) = apply { this.adbVerifyCommandExecute = Pair(value,path) }

fun getAdbVerifyCommand(): String {
val (forVerify,path) = adbVerifyCommandExecute
if (forVerify){
return "\"${path}\" version"
}
return ""
}

fun validateAndGetCommand(): Pair<String, Boolean> {
if (bundletoolPath.isEmpty()) {
return Pair("bundletoolPath", false)
Expand Down Expand Up @@ -55,9 +68,15 @@ class CommandBuilder {
if (isAapt2PathEnabled) {
commandBuilder.append("--aapt2=\"$aapt2Path\" ")
}
commandBuilder.append("--bundle=\"${aabFilePath.first}${aabFilePath.second}\" --output=\"${aabFilePath.first}${aabFilePath.second.split(".")[0]}.apks\" ")
commandBuilder.append(
"--bundle=\"${aabFilePath.first}${aabFilePath.second}\" --output=\"${aabFilePath.first}${
aabFilePath.second.split(
"."
)[0]
}.apks\" "
)

if (signingMode == SigningMode.RELEASE){
if (signingMode == SigningMode.RELEASE) {
commandBuilder.append("--ks=$keyStorePath --ks-pass=pass:$keyStorePassword --ks-key-alias=$keyAlias --key-pass=pass:$keyPassword ")
}

Expand Down
55 changes: 55 additions & 0 deletions src/jvmMain/kotlin/command/CommandExecutor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package command

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.BufferedReader
import java.io.InputStreamReader

class CommandExecutor {

fun executeCommand(
cmd: String,
coroutineScope: CoroutineScope,
onSuccess: (String) -> Unit,
onFailure: (Throwable) -> Unit
) {
coroutineScope.launch(Dispatchers.IO){
try {
val runtime = Runtime.getRuntime()
val startTime = System.currentTimeMillis()

val process = runtime.exec(cmd)
val outputReader = BufferedReader(InputStreamReader(process.inputStream))
val errorReader = BufferedReader(InputStreamReader(process.errorStream))

var output = ""
var errorOutput = ""

// Read command output
var line: String?
while (outputReader.readLine().also { line = it } != null) {
output += line + "\n"
}

// Read error output
while (errorReader.readLine().also { line = it } != null) {
errorOutput += line + "\n"
}

process.waitFor()
val endTime = System.currentTimeMillis()
if (process.exitValue() == 0) {
val executionTime = ((endTime - startTime) / 1000).toString() + "s"
onSuccess("$cmd\n$output\nCommand Executed in $executionTime")
} else {
val exception = Exception("Command execution failed: $cmd$errorOutput")
onFailure(exception)
}
} catch (e: Exception) {
onFailure(e)
}
}
}

}
73 changes: 73 additions & 0 deletions src/jvmMain/kotlin/ui/components/ButtonWithToolTip.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package ui.components

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.TooltipArea
import androidx.compose.foundation.TooltipPlacement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.res.useResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import ui.Styles

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ButtonWithToolTip(label: String, onClick: () -> Unit, toolTipText: String = "", icon: String = "info", buttonColors: ButtonColors = ButtonDefaults.buttonColors()) {
val density = LocalDensity.current // to calculate the intrinsic size of vector images (SVG, XML)
Button(
onClick = {
onClick.invoke()
},
colors = buttonColors,
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp)
.wrapContentWidth(),
) {
Text(
text = label,
style = Styles.TextStyleMedium(16.sp),
color = Color.White,
fontWeight = FontWeight.Medium,
)
if (toolTipText.isNotEmpty()) {
TooltipArea(
tooltip = {
// composable tooltip content
Surface(
modifier = Modifier.shadow(4.dp),
color = Color(255, 255, 210),
shape = RoundedCornerShape(4.dp)
) {
Text(
text = toolTipText,
color = Color.Black,
style = Styles.TextStyleMedium(12.sp),
modifier = Modifier.padding(10.dp)
)
}
},
delayMillis = 200, // in milliseconds
tooltipPlacement = TooltipPlacement.CursorPoint(
alignment = Alignment.BottomEnd,
offset = DpOffset.Zero // tooltip offset
)
) {
Icon(
painter = useResource("$icon.svg") { loadSvgPainter(it, density) },
contentDescription = icon,
modifier = Modifier.padding(start = 8.dp)
)
}
}
}
}
1 change: 1 addition & 0 deletions src/jvmMain/kotlin/ui/components/CheckboxWithText.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ fun CheckboxWithText(label: String, isChecked: Boolean, onCheckedChange: (Boolea
) {
Text(
text = toolTipText,

style = Styles.TextStyleMedium(12.sp),
modifier = Modifier.padding(10.dp)
)
Expand Down
Loading

0 comments on commit ee1dc9e

Please sign in to comment.