Merge pull request #9 from iammohdzaki/feature/device-options

Feature/device options
This commit is contained in:
Mohammad Zaki
2024-03-10 20:12:59 +05:30
committed by GitHub
24 changed files with 697 additions and 118 deletions
+3 -1
View File
@@ -1,6 +1,9 @@
name: kotlin_lint
on:
push:
branches-ignore:
- "*"
pull_request:
paths:
- "**/*.kt"
@@ -9,7 +12,6 @@ on:
jobs:
ktlint:
runs-on: ubuntu-latest
steps:
- name: "checkout"
uses: actions/checkout@v2
+185 -61
View File
@@ -1,8 +1,28 @@
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -16,26 +36,35 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import androidx.compose.ui.window.AwtWindow
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import command.CommandBuilder
import command.CommandExecutor
import local.FileStorageHelper
import ui.Styles
import ui.components.ButtonWithToolTip
import ui.components.CheckboxWithText
import ui.components.ChooseFileTextField
import ui.components.CustomTextField
import utils.*
import ui.components.LoadingDialog
import utils.Constant
import utils.DBConstants
import utils.FileDialogType
import utils.FileHelper
import utils.Log
import utils.SigningMode
import utils.Strings
import java.awt.Desktop
import java.awt.FileDialog
import java.awt.Frame
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.URI
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("") }
@@ -57,21 +86,60 @@ 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)) }
var isDeviceIdEnabled by remember { mutableStateOf(false) }
var deviceSerialId by remember { mutableStateOf("") }
//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.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)
}
@@ -79,9 +147,13 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
}
}
if (showLoadingDialog.second) {
LoadingDialog(showLoadingDialog.first)
}
if (isExecute) {
isExecute = false
//Get Command to Execute
// Get Command to Execute
val (cmd, isValid) = CommandBuilder()
.bundletoolPath(bundletoolPath)
.aabFilePath(aabFilePath)
@@ -97,24 +169,13 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
.validateAndGetCommand()
if (isValid) {
Log.i("Command $cmd")
logs += "Executing Command : \n$cmd\n"
coroutineScope.launch(Dispatchers.IO) {
val runtime = Runtime.getRuntime()
val startTime = System.currentTimeMillis()
try {
//Launch Runtime to execute command
val process = runtime.exec(cmd)
// Read and log error output
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
Log.i("Process Error Output:")
while (errorReader.readLine().also { logs += "\n ERROR -> $it" } != null) {
isLoading = false
}
process.waitFor()
val endTime = System.currentTimeMillis()
if (process.exitValue() == 0) {
Log.i("Command Executed in ${((endTime - startTime) / 1000)}s")
logs += "\nCommand Executed in ${((endTime - startTime) / 1000)}s\n"
// logs += "Executing Command : \n$cmd\n"
CommandExecutor()
.executeCommand(
cmd,
coroutineScope,
onSuccess = {
logs += "$it\n"
// Do further file operation after new apks is generated
// From Auto Zip you can control further file operations.
if (isAutoUnzip) {
@@ -128,8 +189,8 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
isLoading = false
}
//Save Path in Storage
//If We don't have any saved path in file storage and save jar path option is checked.Then,we can save new value in storage.
// Save Path in Storage
// If We don't have any saved path in file storage and save jar path option is checked.Then,we can save new value in storage.
if (savedJarPath == null && saveJarPath) {
fileStorageHelper.save("path", bundletoolPath)
}
@@ -140,12 +201,12 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
if (fileStorageHelper.delete("path"))
saveJarPath = false
}
},
onFailure = {
isLoading = false
logs += "Failed -> ${it.printStackTrace()}"
}
} catch (e: Exception) {
isLoading = false
logs += "Failed -> ${e.printStackTrace()}"
}
}
)
} else {
Log.i("Error $cmd")
logs += "\nError -> $cmd"
@@ -158,20 +219,33 @@ 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,
modifier = Modifier.wrapContentSize(),
verticalAlignment = Alignment.CenterVertically
) {
//Bundle tool select flow
// Bundle tool select flow
ChooseFileTextField(
bundletoolPath,
Strings.SELECT_BUNDLETOOL_JAR,
@@ -348,6 +422,56 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
Strings.AUTO_UNZIP
)
Spacer(modifier = Modifier.padding(8.dp))
if (isAdbSetupDone) {
Text(
text = Strings.DEVICE_OPTIONS,
style = Styles.TextStyleBold(16.sp),
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp)
)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
CheckboxWithText(
Strings.DEVICE_ID,
isDeviceIdEnabled,
onCheckedChange = {
isDeviceIdEnabled = it
},
Strings.DEVICE_ID_INFO
)
if (isDeviceIdEnabled) {
CustomTextField(
deviceSerialId,
Strings.SERIAL_ID,
forPassword = false,
onValueChange = {
deviceSerialId = it
}
)
ButtonWithToolTip(
Strings.FETCH_DEVICES,
onClick = {
CommandExecutor()
.executeCommand(
CommandBuilder()
.getAdbFetchCommand(adbSavedPath!!),
coroutineScope,
onSuccess = {
logs += it
},
onFailure = {
logs += it.printStackTrace()
}
)
},
Strings.FETCH_DEVICES_INFO,
icon = "device_fetch"
)
}
}
Spacer(modifier = Modifier.padding(8.dp))
}
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier.size(size = 40.dp)
@@ -361,13 +485,13 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
isExecute = true
},
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp)
.wrapContentWidth(),
.wrapContentWidth()
) {
Text(
text = Strings.EXECUTE,
style = Styles.TextStyleMedium(16.sp),
color = Color.White,
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Medium
)
}
}
@@ -385,17 +509,17 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
modifier = Modifier.padding(end = 0.dp, bottom = 8.dp),
onClick = {
logs = ""
},
}
) {
Text(
text = Strings.CLEAR_LOGS,
style = Styles.TextStyleBold(13.sp),
style = Styles.TextStyleBold(13.sp)
)
Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing))
Icon(
painter = useResource("clear.svg") { loadSvgPainter(it, density) },
contentDescription = "Clear",
modifier = Modifier.size(ButtonDefaults.IconSize),
modifier = Modifier.size(ButtonDefaults.IconSize)
)
}
}
@@ -403,7 +527,7 @@ fun App(fileStorageHelper: FileStorageHelper, savedPath: String?) {
modifier = Modifier.fillMaxSize().padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
value = logs,
textStyle = Styles.TextStyleMedium(16.sp),
onValueChange = {},
onValueChange = {}
)
}
}
@@ -412,7 +536,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) {
@@ -427,20 +551,20 @@ private fun FileDialog(
dispose = FileDialog::dispose
)
fun main() = application {
val fileStorageHelper = FileStorageHelper()
//Check if path for bundletool exists in local storage
val path = fileStorageHelper.read("path") as String?
// Check if path for bundletool exists in local storage
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)
}
}
@@ -1,5 +1,6 @@
package command
import utils.SigningMode
import utils.Utils
class CommandBuilder {
private var bundletoolPath: String = ""
@@ -13,6 +14,9 @@ class CommandBuilder {
private var keyStorePassword: String = ""
private var keyAlias: String = ""
private var keyPassword: String = ""
private var adbVerifyCommandExecute = Pair(false, "")
private var isDeviceIdEnabled: Boolean = false
private var adbSerialId: String = ""
fun bundletoolPath(path: String) = apply { this.bundletoolPath = path }
fun aabFilePath(path: Pair<String, String>) = apply { this.aabFilePath = path }
@@ -26,6 +30,24 @@ 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 isDeviceSerialIdEnabled(value: Boolean) = apply { this.isDeviceIdEnabled = value }
fun adbSerialId(value: String) = apply { this.adbSerialId = value }
fun getAdbVerifyCommand(): String {
val (forVerify, path) = adbVerifyCommandExecute
if (forVerify) {
return "\"${path}\" version"
}
return ""
}
fun getAdbFetchCommand(adbPath: String): String {
return "\"${adbPath}\" devices"
}
fun validateAndGetCommand(): Pair<String, Boolean> {
if (bundletoolPath.isEmpty()) {
return Pair("bundletoolPath", false)
@@ -39,30 +61,40 @@ class CommandBuilder {
if (signingMode == SigningMode.RELEASE && (keyStorePath.isEmpty() || keyStorePassword.isEmpty() || keyAlias.isEmpty() || keyPassword.isEmpty())) {
return Pair("Check Keystore Info!", false)
}
if (isDeviceIdEnabled && adbSerialId.isEmpty()) {
return Pair("Invalid Serial ID", false)
}
return Pair(getCommand(), true)
}
private fun getCommand(): String {
val commandBuilder = StringBuilder()
if (Utils.isWindowsOS()) {
commandBuilder.append("java -jar \"${bundletoolPath}\" build-apks ")
if (isUniversalMode) {
commandBuilder.append("--mode=universal ")
}
if (isOverwrite) {
commandBuilder.append("--overwrite ")
}
if (isAapt2PathEnabled) {
commandBuilder.append("--aapt2=\"$aapt2Path\" ")
}
commandBuilder.append("--bundle=\"${aabFilePath.first}${aabFilePath.second}\" --output=\"${aabFilePath.first}${aabFilePath.second.split(".")[0]}.apks\" ")
if (signingMode == SigningMode.RELEASE){
commandBuilder.append("--ks=$keyStorePath --ks-pass=pass:$keyStorePassword --ks-key-alias=$keyAlias --key-pass=pass:$keyPassword ")
}
return commandBuilder.toString()
commandBuilder.append("java -jar \"${bundletoolPath}\" build-apks ")
if (isUniversalMode) {
commandBuilder.append("--mode=universal ")
}
return ""
if (isOverwrite) {
commandBuilder.append("--overwrite ")
}
if (isAapt2PathEnabled) {
commandBuilder.append("--aapt2=\"$aapt2Path\" ")
}
commandBuilder.append(
"--bundle=\"${aabFilePath.first}${aabFilePath.second}\" --output=\"${aabFilePath.first}${
aabFilePath.second.split(
"."
)[0]
}.apks\" "
)
if (signingMode == SigningMode.RELEASE) {
commandBuilder.append("--ks=$keyStorePath --ks-pass=pass:$keyStorePassword --ks-key-alias=$keyAlias --key-pass=pass:$keyPassword ")
}
if (isDeviceIdEnabled) {
commandBuilder.append("--device-id=$adbSerialId ")
}
return commandBuilder.toString()
}
}
@@ -0,0 +1,54 @@
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)
}
}
}
}
@@ -17,6 +17,7 @@ class FileStorageHelper {
init {
initializeDir()
}
private fun getKryo(): Kryo {
return if (this::kryo.isInitialized) {
kryo
@@ -71,5 +72,4 @@ class FileStorageHelper {
false
}
}
}
@@ -5,5 +5,4 @@ class KiteDbException : RuntimeException {
constructor(detailMessage: String?) : super(detailMessage)
constructor(detailMessage: String?, throwable: Throwable?) : super(detailMessage, throwable)
}
+4 -5
View File
@@ -10,28 +10,27 @@ object Styles {
TextStyle(
fontWeight = FontWeight.Normal,
fontFamily = codeFontFamily,
fontSize = size,
fontSize = size
)
fun TextStyleMedium(size: TextUnit) =
TextStyle(
fontWeight = FontWeight.Medium,
fontFamily = codeFontFamily,
fontSize = size,
fontSize = size
)
fun TextStyleSemiBold(size: TextUnit) =
TextStyle(
fontWeight = FontWeight.SemiBold,
fontFamily = codeFontFamily,
fontSize = size,
fontSize = size
)
fun TextStyleBold(size: TextUnit) =
TextStyle(
fontWeight = FontWeight.Bold,
fontFamily = codeFontFamily,
fontSize = size,
fontSize = size
)
}
+3 -3
View File
@@ -9,9 +9,9 @@ import androidx.compose.ui.unit.sp
val codeFontFamily = FontFamily(
Font(resource = "fonts/sans_regular.ttf", weight = FontWeight.Light),
Font( resource = "fonts/sans_thin.ttf", weight = FontWeight.Normal),
Font( resource = "fonts/sans_medium.ttf", weight = FontWeight.Medium),
Font( resource = "fonts/sans_bold.ttf", weight = FontWeight.Bold)
Font(resource = "fonts/sans_thin.ttf", weight = FontWeight.Normal),
Font(resource = "fonts/sans_medium.ttf", weight = FontWeight.Medium),
Font(resource = "fonts/sans_bold.ttf", weight = FontWeight.Bold)
)
val typography = Typography(
@@ -0,0 +1,84 @@
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.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.Surface
import androidx.compose.material.Button
import androidx.compose.material.ButtonColors
import androidx.compose.material.Text
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)
)
}
}
}
}
@@ -36,12 +36,12 @@ fun CheckboxWithText(label: String, isChecked: Boolean, onCheckedChange: (Boolea
Checkbox(
modifier = Modifier.testTag(TestTags.CHECKBOX_TAG),
checked = isChecked,
onCheckedChange = { onCheckedChange.invoke(it) },
onCheckedChange = { onCheckedChange.invoke(it) }
)
Text(
modifier = Modifier.testTag(TestTags.TEXT_TAG),
text = label,
style = Styles.TextStyleMedium(14.sp),
style = Styles.TextStyleMedium(14.sp)
)
if (toolTipText.isNotEmpty()) {
TooltipArea(
@@ -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)
)
@@ -2,9 +2,18 @@ package ui.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.MaterialTheme
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -42,11 +51,11 @@ fun ChooseFileTextField(value: String, label: String, onSelect: () -> Unit) {
Text(
text = label,
style = Styles.TextStyleMedium(16.sp),
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Medium
)
},
textStyle = Styles.TextStyleMedium(16.sp),
onValueChange = {},
onValueChange = {}
)
Button(
onClick = {
@@ -54,11 +63,11 @@ fun ChooseFileTextField(value: String, label: String, onSelect: () -> Unit) {
},
modifier = Modifier
.height(50.dp)
.wrapContentWidth(),
.wrapContentWidth()
) {
Icon(
painter = useResource("open_folder.svg") { loadSvgPainter(it, density) },
contentDescription = "",
contentDescription = ""
)
}
}
@@ -49,13 +49,13 @@ fun CustomTextField(value: String, label: String, forPassword: Boolean = true, o
Text(
text = label,
style = Styles.TextStyleMedium(16.sp),
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.Medium
)
},
textStyle = Styles.TextStyleMedium(16.sp),
onValueChange = {
onValueChange.invoke(it)
},
}
)
}
}
@@ -0,0 +1,60 @@
package ui.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import ui.Styles
@Composable
fun LoadingDialog(text: String) {
Surface(
shape = RoundedCornerShape(8.dp),
elevation = 8.dp
) {
Box(
modifier = Modifier
.padding(4.dp)
.border(
BorderStroke(1.dp, Color.LightGray),
shape = RoundedCornerShape(8.dp)
)
) {
Dialog(
title = text,
onCloseRequest = { },
undecorated = false,
resizable = false,
enabled = false
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.padding(16.dp).fillMaxWidth().fillMaxHeight()
) {
Text(
text = text,
style = Styles.TextStyleBold(20.sp),
modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)
)
CircularProgressIndicator()
}
}
}
}
}
@@ -0,0 +1,36 @@
package ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
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.dp
import androidx.compose.ui.unit.sp
import ui.Styles
@Composable
fun TextWithIcon(label: String, onIconClick: () -> Unit) {
val density = LocalDensity.current // to calculate the intrinsic size of vector images (SVG, XML)
Row {
Text(
text = label,
style = Styles.TextStyleMedium(16.sp),
color = Color.Black,
fontWeight = FontWeight.Medium
)
Icon(
painter = useResource("info.svg") { loadSvgPainter(it, density) },
contentDescription = "Info",
modifier = Modifier.padding(start = 8.dp)
.clickable { onIconClick.invoke() }
)
}
}
+9 -4
View File
@@ -6,15 +6,20 @@ object Constant {
const val BUNDLE_DOWNLOAD_LINK = "https://github.com/google/bundletool/releases"
}
object FileDialogType{
object DBConstants {
const val BUNDLETOOL_PATH = "bundletool_path"
const val ADB_PATH = "adb_path"
}
object FileDialogType {
const val AAB = -1
const val BUNDLETOOL = 1
const val AAPT2 = 2
const val KEY_STORE_PATH = 3
const val ADB_PATH = 4
}
object SigningMode{
object SigningMode {
const val DEBUG = 1
const val RELEASE = 2
}
}
+4 -2
View File
@@ -12,7 +12,10 @@ object FileHelper {
Log.i("CHANGED NAME\n Unzipping...")
try {
FileUtils.unzip(newFile, directory)
fileStatus.invoke(Constant.SUCCESS, "Rename and Unzip Successful\nFile Saved at ${directory.removeSuffix("\\")}.")
fileStatus.invoke(
Constant.SUCCESS,
"Rename and Unzip Successful\nFile Saved at ${directory.removeSuffix("\\")}."
)
Log.i("TRYING DELETING FILE")
val value = FileUtils.deleteFile(newFile)
Log.i("DELETE STATUS : $value")
@@ -25,5 +28,4 @@ object FileHelper {
fileStatus.invoke(Constant.FAILURE, "FAILED RENAMING THE FILE!!")
}
}
}
+5 -3
View File
@@ -1,6 +1,10 @@
package utils
import java.io.*
import java.io.BufferedOutputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.FileOutputStream
import java.util.zip.ZipFile
object FileUtils {
@@ -13,7 +17,6 @@ object FileUtils {
fun deleteFile(file: File) =
file.delete()
/**
* @param oldFile
* @param newFile
@@ -72,5 +75,4 @@ object FileUtils {
}
bos.close()
}
}
+10 -1
View File
@@ -28,8 +28,17 @@ object Strings {
const val KEY_PASSWORD = "Key Password"
const val EXECUTE = "Execute"
const val FILE_OPTIONS = "File Options"
const val DEVICE_OPTIONS = "Device Options"
const val AUTO_UNZIP = "Automatically Unzip and Delete Apks File"
const val AUTO_UNZIP_INFO = "Automatically Unzip and Delete Apks File"
const val CLEAR_LOGS = "Clear Logs"
const val LOGS_VIEW = "Logs View"
const val SETUP_ADB = "Set up Adb"
const val ABD_SETUP_DONE = "Adb Connected"
const val SETUP_ADB_INFO = "Setup ADB path to create builds based on connected device"
const val DEVICE_ID = "Device Id"
const val DEVICE_ID_INFO = "Device Id based on the Serial Number. Use with Mode Universal to get one apk."
const val VERIFYING_ADB_PATH = "Verifying ADB Path.."
const val SERIAL_ID = "Serial Id"
const val FETCH_DEVICES = "Fetch Devices"
const val FETCH_DEVICES_INFO = "Fetch Connected Devices Info in LogView"
}
+2 -3
View File
@@ -1,11 +1,10 @@
package utils
import java.util.*
import java.util.Locale
object Utils {
fun isWindowsOS(): Boolean{
fun isWindowsOS(): Boolean {
return System.getProperty("os.name").lowercase(Locale.getDefault()).contains("windows")
}
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-120q-138 0-240.5-91.5T122-440h82q14 104 92.5 172T480-200q117 0 198.5-81.5T760-480q0-117-81.5-198.5T480-760q-69 0-129 32t-101 88h110v80H120v-240h80v94q51-64 124.5-99T480-840q75 0 140.5 28.5t114 77q48.5 48.5 77 114T840-480q0 75-28.5 140.5t-77 114q-48.5 48.5-114 77T480-120Zm112-192L440-464v-216h80v184l128 128-56 56Z"/></svg>

After

Width:  |  Height:  |  Size: 425 B

Some files were not shown because too many files have changed in this diff Show More