Compare commits
No commits in common. "12273bdd1713b481bbe9b896e7249f0993434d3a" and "19ab656b7c3b20fc023c23e867c9521efee14641" have entirely different histories.
12273bdd17
...
19ab656b7c
4 changed files with 35 additions and 101 deletions
|
@ -1,93 +1,28 @@
|
||||||
package eu.m724.vastapp.activity
|
package eu.m724.vastapp.activity
|
||||||
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import eu.m724.vastapp.activity.termux.TermuxSshActivity
|
|
||||||
|
|
||||||
class Opener {
|
class Opener {
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
|
||||||
* opens an url in another app
|
|
||||||
* usually a browser but can be another app if it's default
|
|
||||||
* @param url the url
|
|
||||||
* @param activity the activity that starts the browser activity
|
|
||||||
*/
|
|
||||||
fun openUrl(url: String, activity: ComponentActivity) {
|
fun openUrl(url: String, activity: ComponentActivity) {
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||||
activity.startActivity(intent)
|
activity.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* opens another app
|
|
||||||
* @param packageName package name like com.termux or eu.m724.vastapp
|
|
||||||
* @param activity the activity that starts the package
|
|
||||||
*/
|
|
||||||
fun openApp(packageName: String, activity: ComponentActivity) {
|
fun openApp(packageName: String, activity: ComponentActivity) {
|
||||||
val intent = activity.packageManager.getLaunchIntentForPackage(packageName)
|
val intent = activity.packageManager.getLaunchIntentForPackage(packageName)
|
||||||
activity.startActivity(intent)
|
activity.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* copies text to clipboard without toast
|
|
||||||
* @param text the text to copy
|
|
||||||
* @param label what are you copying
|
|
||||||
* @param context the context
|
|
||||||
*/
|
|
||||||
fun copyToClipboard(text: String, label: String, context: Context) {
|
fun copyToClipboard(text: String, label: String, context: Context) {
|
||||||
copyToClipboard(text, label, null, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* copies text to clipboard with toast
|
|
||||||
* @param text the text to copy
|
|
||||||
* @param label what are you copying
|
|
||||||
* @param toast text of toast that will appear if android < 12
|
|
||||||
* @param context the context
|
|
||||||
*/
|
|
||||||
fun copyToClipboard(text: String, label: String, toast: String?, context: Context) {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
|
||||||
Toast.makeText(
|
|
||||||
context,
|
|
||||||
toast,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clipData = ClipData.newPlainText(label, text)
|
val clipData = ClipData.newPlainText(label, text)
|
||||||
clipboardManager.setPrimaryClip(clipData)
|
clipboardManager.setPrimaryClip(clipData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* starts termux with command
|
|
||||||
* it will start [TermuxSshActivity] on finish
|
|
||||||
* @param context the context
|
|
||||||
* @param command the command, first entry is the executable and then arguments
|
|
||||||
*/
|
|
||||||
fun startTermux(context: Context, command: Array<String>) {
|
|
||||||
val noSshIntent = Intent(context, TermuxSshActivity::class.java)
|
|
||||||
val pendingIntent = PendingIntent.getActivity(
|
|
||||||
context,
|
|
||||||
0,
|
|
||||||
noSshIntent,
|
|
||||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_MUTABLE
|
|
||||||
)
|
|
||||||
|
|
||||||
val intent = Intent()
|
|
||||||
intent.setClassName("com.termux", "com.termux.app.RunCommandService")
|
|
||||||
intent.setAction("com.termux.RUN_COMMAND")
|
|
||||||
intent.putExtra("com.termux.RUN_COMMAND_PATH", command[0])
|
|
||||||
intent.putExtra("com.termux.RUN_COMMAND_ARGUMENTS", command.drop(1).toTypedArray())
|
|
||||||
intent.putExtra("com.termux.RUN_COMMAND_PENDING_INTENT", pendingIntent)
|
|
||||||
|
|
||||||
context.startForegroundService(intent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,17 @@
|
||||||
package eu.m724.vastapp.activity.dashboard
|
package eu.m724.vastapp.activity.dashboard
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import eu.m724.vastapp.R
|
import eu.m724.vastapp.R
|
||||||
import eu.m724.vastapp.activity.Opener
|
import eu.m724.vastapp.activity.Opener
|
||||||
import eu.m724.vastapp.activity.PermissionChecker
|
import eu.m724.vastapp.activity.PermissionChecker
|
||||||
|
import eu.m724.vastapp.activity.termux.TermuxSshActivity
|
||||||
import eu.m724.vastapp.vastai.ApiRoute
|
import eu.m724.vastapp.vastai.ApiRoute
|
||||||
import eu.m724.vastapp.vastai.VastApi
|
import eu.m724.vastapp.vastai.VastApi
|
||||||
import eu.m724.vastapp.vastai.api.InstancesUrlRequestCallback
|
import eu.m724.vastapp.vastai.api.InstancesUrlRequestCallback
|
||||||
|
@ -21,7 +25,7 @@ import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
|
|
||||||
class DashboardViewModel(
|
class DashboardViewModel(
|
||||||
initialUser: User,
|
private val initialUser: User,
|
||||||
private val vastApi: VastApi
|
private val vastApi: VastApi
|
||||||
) : ViewModel() { // TODO do something with the user
|
) : ViewModel() { // TODO do something with the user
|
||||||
|
|
||||||
|
@ -36,9 +40,6 @@ class DashboardViewModel(
|
||||||
private val _user: MutableStateFlow<User> = MutableStateFlow(initialUser)
|
private val _user: MutableStateFlow<User> = MutableStateFlow(initialUser)
|
||||||
val user: StateFlow<User> = _user.asStateFlow()
|
val user: StateFlow<User> = _user.asStateFlow()
|
||||||
|
|
||||||
private val _remainingTime: MutableStateFlow<Int> = MutableStateFlow(-1)
|
|
||||||
var remainingTime: StateFlow<Int> = _remainingTime.asStateFlow()
|
|
||||||
|
|
||||||
private val _refreshError: MutableStateFlow<List<String>> = MutableStateFlow(emptyList())
|
private val _refreshError: MutableStateFlow<List<String>> = MutableStateFlow(emptyList())
|
||||||
val refreshError: StateFlow<List<String>> = _refreshError.asStateFlow()
|
val refreshError: StateFlow<List<String>> = _refreshError.asStateFlow()
|
||||||
|
|
||||||
|
@ -71,22 +72,6 @@ class DashboardViewModel(
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(refreshing = it.refreshing - 1)
|
it.copy(refreshing = it.refreshing - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instances.isEmpty()) { // TODO move this
|
|
||||||
_remainingTime.value = -1
|
|
||||||
} else {
|
|
||||||
var totalDph = 0.0
|
|
||||||
|
|
||||||
instances.forEach {
|
|
||||||
if (it.status == "running")
|
|
||||||
totalDph += it.instance.pricing.dphTotal!!
|
|
||||||
else
|
|
||||||
totalDph += it.instance.pricing.dphTotal!! - it.instance.pricing.dphBase
|
|
||||||
// TODO make this ideal
|
|
||||||
}
|
|
||||||
|
|
||||||
_remainingTime.value = (user.value.credit / totalDph * 3600).toInt()
|
|
||||||
}
|
|
||||||
}, { apiFailure ->
|
}, { apiFailure ->
|
||||||
_refreshError.update { it + apiFailure.errorMessage!! }
|
_refreshError.update { it + apiFailure.errorMessage!! }
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
|
@ -114,7 +99,6 @@ class DashboardViewModel(
|
||||||
// TODO I don't like this function especially the last line
|
// TODO I don't like this function especially the last line
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SdCardPath")
|
|
||||||
fun sshButtonClick(activity: ComponentActivity, rentedInstance: RentedInstance) {
|
fun sshButtonClick(activity: ComponentActivity, rentedInstance: RentedInstance) {
|
||||||
val sshCommand = "ssh -p ${rentedInstance.sshProxyPort} root@${rentedInstance.sshProxyHost}"
|
val sshCommand = "ssh -p ${rentedInstance.sshProxyPort} root@${rentedInstance.sshProxyHost}"
|
||||||
val context = activity.applicationContext
|
val context = activity.applicationContext
|
||||||
|
@ -126,11 +110,10 @@ class DashboardViewModel(
|
||||||
) { granted, asked ->
|
) { granted, asked ->
|
||||||
if (granted) {
|
if (granted) {
|
||||||
val arguments = arrayOf(
|
val arguments = arrayOf(
|
||||||
"/data/data/com.termux/files/usr/bin/ssh",
|
|
||||||
"-p", rentedInstance.sshProxyPort.toString(),
|
"-p", rentedInstance.sshProxyPort.toString(),
|
||||||
"root@" + rentedInstance.sshProxyHost
|
"root@" + rentedInstance.sshProxyHost
|
||||||
)
|
)
|
||||||
Opener.startTermux(context, arguments)
|
startTermux(context, arguments)
|
||||||
Thread.sleep(100)
|
Thread.sleep(100)
|
||||||
println(activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED))
|
println(activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED))
|
||||||
} else {
|
} else {
|
||||||
|
@ -144,12 +127,33 @@ class DashboardViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyToClipboard(context: Context, text: String) {
|
private fun copyToClipboard(context: Context, text: String) {
|
||||||
Opener.copyToClipboard(
|
Toast.makeText(
|
||||||
text,
|
context,
|
||||||
"ssh command",
|
|
||||||
context.getString(R.string.copied_to_clipboard),
|
context.getString(R.string.copied_to_clipboard),
|
||||||
context
|
Toast.LENGTH_SHORT
|
||||||
|
).show() // TODO hide on a12
|
||||||
|
|
||||||
|
Opener.copyToClipboard(text, "ssh command", context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SdCardPath")
|
||||||
|
private fun startTermux(context: Context, arguments: Array<String>) {
|
||||||
|
val noSshIntent = Intent(context, TermuxSshActivity::class.java)
|
||||||
|
val pendingIntent = PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
noSshIntent,
|
||||||
|
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_MUTABLE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val intent = Intent()
|
||||||
|
intent.setClassName("com.termux", "com.termux.app.RunCommandService")
|
||||||
|
intent.setAction("com.termux.RUN_COMMAND")
|
||||||
|
intent.putExtra("com.termux.RUN_COMMAND_PATH", "/data/data/com.termux/files/usr/bin/ssh")
|
||||||
|
intent.putExtra("com.termux.RUN_COMMAND_ARGUMENTS", arguments)
|
||||||
|
intent.putExtra("com.termux.RUN_COMMAND_PENDING_INTENT", pendingIntent)
|
||||||
|
|
||||||
|
context.startForegroundService(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -26,7 +26,9 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
@ -46,7 +48,7 @@ fun DashboardScreen(dashboardViewModel: DashboardViewModel) {
|
||||||
|
|
||||||
val user by dashboardViewModel.user.collectAsState()
|
val user by dashboardViewModel.user.collectAsState()
|
||||||
val rentedInstances by dashboardViewModel.rentedInstances.collectAsState()
|
val rentedInstances by dashboardViewModel.rentedInstances.collectAsState()
|
||||||
val remainingTime by dashboardViewModel.remainingTime.collectAsState()
|
val remainingTime by rememberSaveable { mutableIntStateOf(6000000) }
|
||||||
val isRefreshing by remember(uiState) { derivedStateOf { uiState.refreshing > 0 } }
|
val isRefreshing by remember(uiState) { derivedStateOf { uiState.refreshing > 0 } }
|
||||||
|
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
@ -168,7 +170,7 @@ fun balanceColor(balance: Double, warningThreshold: Double): Color {
|
||||||
@Composable
|
@Composable
|
||||||
fun formatTime(seconds: Int): String {
|
fun formatTime(seconds: Int): String {
|
||||||
if (seconds <= 0)
|
if (seconds <= 0)
|
||||||
return "―"
|
return stringResource(id = R.string.time_minutes_short, 0.0)
|
||||||
|
|
||||||
val minutes: Double = seconds / 60.0
|
val minutes: Double = seconds / 60.0
|
||||||
if (minutes < 60)
|
if (minutes < 60)
|
||||||
|
|
|
@ -34,11 +34,6 @@ data class RentedInstance(
|
||||||
val sshProxyHost: String,
|
val sshProxyHost: String,
|
||||||
val sshProxyPort: Int,
|
val sshProxyPort: Int,
|
||||||
|
|
||||||
/** exited or running */
|
|
||||||
val status: String,
|
|
||||||
/** "running" if the machine is starting or "stopped" if stopping */
|
|
||||||
val targetStatus: String,
|
|
||||||
|
|
||||||
/** the docker image that runs on the instance */
|
/** the docker image that runs on the instance */
|
||||||
val image: String,
|
val image: String,
|
||||||
/** label set by user, null if not set */
|
/** label set by user, null if not set */
|
||||||
|
@ -64,8 +59,6 @@ data class RentedInstance(
|
||||||
json.optDouble("inet_up_billed").toInt(),
|
json.optDouble("inet_up_billed").toInt(),
|
||||||
"ssh${json.getInt("ssh_idx")}.vast.ai", // TODO
|
"ssh${json.getInt("ssh_idx")}.vast.ai", // TODO
|
||||||
json.getInt("ssh_port"),
|
json.getInt("ssh_port"),
|
||||||
json.getString("actual_status"),
|
|
||||||
json.getString("intended_status"),
|
|
||||||
json.getString("image_uuid"),
|
json.getString("image_uuid"),
|
||||||
json.optString("label").takeUnless { it == "null" || it.isBlank() },
|
json.optString("label").takeUnless { it == "null" || it.isBlank() },
|
||||||
json.getString("local_ipaddrs").split(" ").filterNot { it == "\n" }
|
json.getString("local_ipaddrs").split(" ").filterNot { it == "\n" }
|
||||||
|
|
Loading…
Reference in a new issue