add start stop delete

This commit is contained in:
Minecon724 2024-08-06 15:45:23 +02:00
parent 6da971ef21
commit 784879393f
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
8 changed files with 183 additions and 20 deletions

View file

@ -49,7 +49,7 @@ fun InstancesScreen(dashboardViewModel: DashboardViewModel) {
// TODO actually get instances // TODO actually get instances
if (rentedInstances.size > 0) { if (rentedInstances.isNotEmpty()) {
ContextualFlowRow( ContextualFlowRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -66,6 +66,12 @@ fun InstancesScreen(dashboardViewModel: DashboardViewModel) {
sshButtonClick = { sshButtonClick = {
dashboardViewModel.sshButtonClick(activity, it) dashboardViewModel.sshButtonClick(activity, it)
}, },
actionButtonClick = {
dashboardViewModel.toggleInstance(it)
},
deleteButtonClick = {
dashboardViewModel.deleteInstance(it)
}
) )
} }
} else { } else {
@ -89,6 +95,8 @@ fun RentedInstanceCard(
rentedInstance: RentedInstance, rentedInstance: RentedInstance,
termuxAvailable: Int, termuxAvailable: Int,
sshButtonClick: (RentedInstance) -> Unit, sshButtonClick: (RentedInstance) -> Unit,
actionButtonClick: (RentedInstance) -> Unit,
deleteButtonClick: (RentedInstance) -> Unit,
) { ) {
val instance by remember(rentedInstance) { derivedStateOf { rentedInstance.instance } } val instance by remember(rentedInstance) { derivedStateOf { rentedInstance.instance } }
val label by remember(instance) { derivedStateOf { val label by remember(instance) { derivedStateOf {
@ -103,27 +111,66 @@ fun RentedInstanceCard(
Column( Column(
horizontalAlignment = Alignment.End horizontalAlignment = Alignment.End
) { ) {
Button( // TODO consider other buttons Row {
modifier = Modifier.height(24.dp), Button(
contentPadding = PaddingValues(0.dp), modifier = Modifier.size(24.dp),
onClick = { sshButtonClick(rentedInstance) } contentPadding = PaddingValues(0.dp),
) { onClick = { deleteButtonClick(rentedInstance) },
if (termuxAvailable > -1) { ) {
Icon( Icon(
painter = painterResource(id = R.drawable.termux_icon), modifier = Modifier.size(16.dp),
contentDescription = "Run in Termux" painter = painterResource(id = R.drawable.baseline_delete_24),
contentDescription = "Delete instance"
) )
Text("ssh") }
Spacer(modifier = Modifier.size(4.dp)) // necessary because TODO the termux icon has padding
} else { Spacer(modifier = Modifier.width(4.dp))
Spacer(modifier = Modifier.size(1.dp)) // TODO make this not needed?
Icon( Button(
modifier = Modifier.size(12.dp), modifier = Modifier.size(24.dp),
painter = painterResource(id = R.drawable.copy_regular), // TODO copy icon here contentPadding = PaddingValues(0.dp),
contentDescription = "Copy command" onClick = { actionButtonClick(rentedInstance) },
) enabled = rentedInstance.status == rentedInstance.targetStatus
Spacer(modifier = Modifier.size(6.dp)) ) {
Text("ssh") if (rentedInstance.status == "running") {
Icon(
modifier = Modifier.size(16.dp),
painter = painterResource(id = R.drawable.baseline_stop_24),
contentDescription = "Stop instance"
)
} else {
Icon(
modifier = Modifier.size(16.dp),
painter = painterResource(id = R.drawable.baseline_play_arrow_24),
contentDescription = "Start instance"
)
}
}
Spacer(modifier = Modifier.width(4.dp))
Button(
modifier = Modifier.height(24.dp),
contentPadding = PaddingValues(0.dp),
onClick = { sshButtonClick(rentedInstance) }
) {
if (termuxAvailable > -1) {
Icon(
painter = painterResource(id = R.drawable.termux_icon),
contentDescription = "Run in Termux"
)
Text("ssh")
Spacer(modifier = Modifier.size(4.dp)) // necessary because TODO the termux icon has padding
} else {
Spacer(modifier = Modifier.size(1.dp)) // TODO make this not needed?
Icon(
modifier = Modifier.size(12.dp),
painter = painterResource(id = R.drawable.copy_regular), // TODO copy icon here
contentDescription = "Copy command"
)
Spacer(modifier = Modifier.size(6.dp))
Text("ssh")
}
} }
} }

View file

@ -0,0 +1,16 @@
package eu.m724.vastapp.vastai.api
import eu.m724.vastapp.vastai.ApiFailure
import org.json.JSONObject
class JsonUrlRequestCallback(
onSuccess: (JSONObject) -> Unit,
onFailure: (ApiFailure) -> Unit
) : StringUrlRequestCallback({ stringResponse ->
try {
val jsonResponse = JSONObject(stringResponse)
onSuccess(jsonResponse)
} catch (e: Exception) {
onFailure(ApiFailure(e.message))
}
}, onFailure)

View file

@ -0,0 +1,51 @@
package eu.m724.vastapp.vastai.api
import eu.m724.vastapp.vastai.ApiFailure
import org.chromium.net.CronetException
import org.chromium.net.UrlRequest
import org.chromium.net.UrlResponseInfo
import java.nio.ByteBuffer
import java.nio.charset.CodingErrorAction
open class StringUrlRequestCallback(
val onSuccess: (String) -> Unit,
val onFailure: (ApiFailure) -> Unit
) : UrlRequest.Callback() {
protected val stringResponse = StringBuilder()
override fun onRedirectReceived(
request: UrlRequest?,
info: UrlResponseInfo?,
newLocationUrl: String?
) {
request?.followRedirect()
}
override fun onResponseStarted(request: UrlRequest?, info: UrlResponseInfo?) {
request?.read(ByteBuffer.allocateDirect(102400))
}
override fun onReadCompleted(
request: UrlRequest?,
info: UrlResponseInfo?,
byteBuffer: ByteBuffer?
) {
byteBuffer?.clear()
request?.read(byteBuffer)
stringResponse.append(Charsets.UTF_8.newDecoder().onUnmappableCharacter(CodingErrorAction.IGNORE).decode(byteBuffer))
}
override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
if (info?.httpStatusCode == 200) {
onSuccess(stringResponse.toString())
} else {
onFailure(ApiFailure("${info?.httpStatusCode} ${info?.httpStatusText}"))
println("API error: ${stringResponse.toString()}")
}
}
override fun onFailed(request: UrlRequest?, info: UrlResponseInfo?, error: CronetException?) {
onFailure(ApiFailure("Network error: ${error?.message ?: "Unknown"}"))
}
}

View file

@ -0,0 +1,9 @@
package eu.m724.vastapp.vastai.api.upload
import org.json.JSONObject
class JsonUploadDataProvider(
jsonObject: JSONObject
) : StringUploadDataProvider(
jsonObject.toString()
)

View file

@ -0,0 +1,25 @@
package eu.m724.vastapp.vastai.api.upload
import org.chromium.net.UploadDataProvider
import org.chromium.net.UploadDataSink
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
open class StringUploadDataProvider(
val data: String
) : UploadDataProvider() {
override fun getLength(): Long {
return data.length.toLong()
}
override fun read(uploadDataSink: UploadDataSink?, byteBuffer: ByteBuffer?) {
byteBuffer!!.put(data.toByteArray(StandardCharsets.UTF_8))
uploadDataSink!!.onReadSucceeded(false)
}
override fun rewind(uploadDataSink: UploadDataSink?) {
// TODO look into it
uploadDataSink!!.onRewindSucceeded()
}
}

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M6,6h12v12H6z"/>
</vector>