Compare commits
7 commits
127a31841e
...
5f1787d6ff
Author | SHA1 | Date | |
---|---|---|---|
5f1787d6ff | |||
3d405adc6e | |||
8d7268f194 | |||
fbde1a502b | |||
8c9e4c0328 | |||
3e49fc30bc | |||
d3d3c9f59e |
7 changed files with 145 additions and 36 deletions
|
@ -2,6 +2,8 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
|
|
@ -29,6 +29,7 @@ import androidx.navigation.compose.rememberNavController
|
||||||
import eu.m724.vastapp.activity.dashboard.screen.Screen
|
import eu.m724.vastapp.activity.dashboard.screen.Screen
|
||||||
import eu.m724.vastapp.activity.dashboard.screen.BillingScreen
|
import eu.m724.vastapp.activity.dashboard.screen.BillingScreen
|
||||||
import eu.m724.vastapp.activity.dashboard.screen.DashboardScreen
|
import eu.m724.vastapp.activity.dashboard.screen.DashboardScreen
|
||||||
|
import eu.m724.vastapp.activity.dashboard.screen.HelpScreen
|
||||||
import eu.m724.vastapp.activity.dashboard.screen.InstancesScreen
|
import eu.m724.vastapp.activity.dashboard.screen.InstancesScreen
|
||||||
import eu.m724.vastapp.ui.theme.VastappTheme
|
import eu.m724.vastapp.ui.theme.VastappTheme
|
||||||
import eu.m724.vastapp.vastai.VastApi
|
import eu.m724.vastapp.vastai.VastApi
|
||||||
|
@ -54,7 +55,8 @@ class DashboardActivity : ComponentActivity() {
|
||||||
val items = listOf(
|
val items = listOf(
|
||||||
Screen.Dashboard,
|
Screen.Dashboard,
|
||||||
Screen.Instances,
|
Screen.Instances,
|
||||||
Screen.Billing
|
Screen.Billing,
|
||||||
|
Screen.Help
|
||||||
)
|
)
|
||||||
|
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
@ -70,6 +72,7 @@ class DashboardActivity : ComponentActivity() {
|
||||||
composable("dashboard") { DashboardScreen(dashboardViewModel) }
|
composable("dashboard") { DashboardScreen(dashboardViewModel) }
|
||||||
composable("instances") { InstancesScreen(dashboardViewModel) }
|
composable("instances") { InstancesScreen(dashboardViewModel) }
|
||||||
composable("billing") { BillingScreen(dashboardViewModel) }
|
composable("billing") { BillingScreen(dashboardViewModel) }
|
||||||
|
composable("help") { HelpScreen(dashboardViewModel) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,21 @@
|
||||||
package eu.m724.vastapp.activity.dashboard
|
package eu.m724.vastapp.activity.dashboard
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import eu.m724.vastapp.activity.login.LoginUiState
|
|
||||||
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.UserUrlRequestCallback
|
import eu.m724.vastapp.vastai.api.UserUrlRequestCallback
|
||||||
import eu.m724.vastapp.vastai.data.User
|
import eu.m724.vastapp.vastai.data.User
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.isActive
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class DashboardViewModel(private val _user: User, val vastApi: VastApi) : ViewModel() {
|
|
||||||
|
class DashboardViewModel(initialUser: User, private val vastApi: VastApi) : ViewModel() { // TODO do something with the user
|
||||||
private val _uiState: MutableStateFlow<DashboardUiState> =
|
private val _uiState: MutableStateFlow<DashboardUiState> =
|
||||||
MutableStateFlow(DashboardUiState(false, _user, null))
|
MutableStateFlow(DashboardUiState(false, initialUser, null))
|
||||||
val uiState: StateFlow<DashboardUiState> =
|
val uiState: StateFlow<DashboardUiState> =
|
||||||
_uiState.asStateFlow()
|
_uiState.asStateFlow()
|
||||||
|
|
||||||
private val _navigationEvent = MutableSharedFlow<String>()
|
|
||||||
val navigationEvent: SharedFlow<String> = _navigationEvent.asSharedFlow()
|
|
||||||
|
|
||||||
fun navigateTo(route: String) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
_navigationEvent.emit(route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
val request = vastApi.buildRequest(
|
val request = vastApi.buildRequest(
|
||||||
ApiRoute.SHOW_USER,
|
ApiRoute.SHOW_USER,
|
||||||
|
|
|
@ -1,12 +1,70 @@
|
||||||
package eu.m724.vastapp.activity.dashboard.screen
|
package eu.m724.vastapp.activity.dashboard.screen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import eu.m724.vastapp.R
|
||||||
import eu.m724.vastapp.activity.dashboard.DashboardViewModel
|
import eu.m724.vastapp.activity.dashboard.DashboardViewModel
|
||||||
|
|
||||||
class Billing {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BillingScreen(dashboardViewModel: DashboardViewModel) {
|
fun BillingScreen(dashboardViewModel: DashboardViewModel) {
|
||||||
|
val uiState by dashboardViewModel.uiState.collectAsState()
|
||||||
|
|
||||||
|
val user by remember(uiState) { derivedStateOf { uiState.user } }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.height(30.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.width(160.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.padding(16.dp, 8.dp).height(IntrinsicSize.Min)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.fillMaxHeight().aspectRatio(1f),
|
||||||
|
painter = painterResource(id = R.drawable.baseline_monetization_on_24),
|
||||||
|
contentDescription = "Balance"
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "$%.2f".format(user.credit),
|
||||||
|
fontSize = 24.sp,
|
||||||
|
color = balanceColor(user.credit, user.balanceThreshold)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package eu.m724.vastapp.activity.dashboard.screen
|
package eu.m724.vastapp.activity.dashboard.screen
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
@ -18,17 +17,14 @@ import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
import androidx.compose.material3.pulltorefresh.pullToRefresh
|
|
||||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||||
import androidx.compose.runtime.Composable
|
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.mutableDoubleStateOf
|
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
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
|
||||||
|
@ -37,13 +33,8 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import eu.m724.vastapp.R
|
import eu.m724.vastapp.R
|
||||||
import eu.m724.vastapp.activity.dashboard.DashboardViewModel
|
import eu.m724.vastapp.activity.dashboard.DashboardViewModel
|
||||||
import eu.m724.vastapp.vastai.data.User
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class Dashboard {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class) // for pullRefresh
|
@OptIn(ExperimentalMaterial3Api::class) // for pullRefresh
|
||||||
@Composable
|
@Composable
|
||||||
fun DashboardScreen(dashboardViewModel: DashboardViewModel) {
|
fun DashboardScreen(dashboardViewModel: DashboardViewModel) {
|
||||||
|
@ -130,6 +121,8 @@ fun DashboardScreen(dashboardViewModel: DashboardViewModel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO maybe reuse that from Instances?
|
||||||
|
|
||||||
val instance = JSONObject()
|
val instance = JSONObject()
|
||||||
instance.put("id", 234523)
|
instance.put("id", 234523)
|
||||||
instance.put("machine_id", 1121323)
|
instance.put("machine_id", 1121323)
|
||||||
|
@ -140,7 +133,6 @@ fun DashboardScreen(dashboardViewModel: DashboardViewModel) {
|
||||||
instance.put("gpu_ram", 24564)
|
instance.put("gpu_ram", 24564)
|
||||||
instance.put("vmem_usage", 0.339843)
|
instance.put("vmem_usage", 0.339843)
|
||||||
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.width(340.dp)
|
modifier = Modifier.width(340.dp)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package eu.m724.vastapp.activity.dashboard.screen
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import eu.m724.vastapp.activity.dashboard.DashboardViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HelpScreen(dashboardViewModel: DashboardViewModel) { // TODO make this a webview
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Button(onClick = {
|
||||||
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"))
|
||||||
|
context.startActivity(browserIntent)
|
||||||
|
}) {
|
||||||
|
Text(text = "https://vast.ai/docs")
|
||||||
|
}
|
||||||
|
Text(text = "(this will be a webview)", fontSize = 12.sp)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,16 +3,26 @@ package eu.m724.vastapp.activity.dashboard.screen
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ContextualFlowRow
|
||||||
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
@ -24,13 +34,35 @@ import org.json.JSONObject
|
||||||
class Instances {
|
class Instances {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun InstancesScreen(dashboardViewModel: DashboardViewModel) {
|
fun InstancesScreen(dashboardViewModel: DashboardViewModel) {
|
||||||
Column {
|
val uiState by dashboardViewModel.uiState.collectAsState()
|
||||||
|
|
||||||
|
// TODO actually get instances
|
||||||
|
|
||||||
|
ContextualFlowRow(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
itemCount = 10,
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
val instance = JSONObject()
|
||||||
|
instance.put("id", 234523)
|
||||||
|
instance.put("machine_id", 1121323)
|
||||||
|
instance.put("host_id", 5924)
|
||||||
|
instance.put("gpu_name", "RTX 4090")
|
||||||
|
instance.put("num_gpus", 2)
|
||||||
|
instance.put("gpu_util", 70)
|
||||||
|
instance.put("gpu_ram", 24564)
|
||||||
|
instance.put("vmem_usage", 0.339843)
|
||||||
|
|
||||||
|
InstanceCard(instance = instance, modifier = Modifier.width(340.dp).padding(8.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO maybe move this?
|
||||||
@Composable
|
@Composable
|
||||||
fun InstanceCard(instance: JSONObject, modifier: Modifier = Modifier) {
|
fun InstanceCard(instance: JSONObject, modifier: Modifier = Modifier) {
|
||||||
val gpuUsage = instance.getInt("gpu_util")
|
val gpuUsage = instance.getInt("gpu_util")
|
||||||
|
@ -68,7 +100,9 @@ fun InstanceCard(instance: JSONObject, modifier: Modifier = Modifier) {
|
||||||
modifier = Modifier.fillMaxWidth(0.5f)
|
modifier = Modifier.fillMaxWidth(0.5f)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth().weight(1f)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
) {
|
) {
|
||||||
Text(text = "GPU: $gpuUsage%", fontSize = 12.sp)
|
Text(text = "GPU: $gpuUsage%", fontSize = 12.sp)
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
|
@ -77,7 +111,9 @@ fun InstanceCard(instance: JSONObject, modifier: Modifier = Modifier) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth().weight(1f)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
) {
|
) {
|
||||||
Text(text = "%.1f / %.1f G".format(vramGbUsed, vramGb), fontSize = 12.sp)
|
Text(text = "%.1f / %.1f G".format(vramGbUsed, vramGb), fontSize = 12.sp)
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
|
|
Loading…
Reference in a new issue