diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 18311db..5aad826 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,7 +14,19 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Vastapp"
+ android:name=".VastApplication"
tools:targetApi="34">
+
+
+
+
+
+
+
-
-
-
-
-
diff --git a/app/src/main/java/eu/m724/vastapp/VastApplication.kt b/app/src/main/java/eu/m724/vastapp/VastApplication.kt
new file mode 100644
index 0000000..2e40f7f
--- /dev/null
+++ b/app/src/main/java/eu/m724/vastapp/VastApplication.kt
@@ -0,0 +1,38 @@
+package eu.m724.vastapp
+
+import android.app.Application
+import android.content.Context
+import android.content.SharedPreferences
+import eu.m724.vastapp.vastai.Account
+import eu.m724.vastapp.vastai.VastApi
+import org.chromium.net.CronetEngine
+import java.util.concurrent.Executors
+
+class VastApplication : Application() {
+ private lateinit var cronetEngine: CronetEngine
+ lateinit var vastApi: VastApi // TODO maybe make private?
+
+ private lateinit var loginSharedPreferences: SharedPreferences
+
+ var account: Account? = null
+
+ override fun onCreate() {
+ super.onCreate()
+ cronetEngine = CronetEngine.Builder(baseContext)
+ .enableBrotli(true)
+ .build() // http3 is not supported on cloud.vast.ai
+ vastApi = VastApi("", cronetEngine, Executors.newSingleThreadExecutor())
+ loginSharedPreferences = applicationContext.getSharedPreferences("login", Context.MODE_PRIVATE)
+ }
+
+ fun submitKey(apiKey: String) {
+ with (loginSharedPreferences.edit()) {
+ putString("apiKey", apiKey) // TODO encrypt
+ apply()
+ }
+ }
+
+ fun loadKey(): String? {
+ return loginSharedPreferences.getString("apiKey", null)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardActivity.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardActivity.kt
index ecc2842..19ea987 100644
--- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardActivity.kt
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardActivity.kt
@@ -36,26 +36,18 @@ import eu.m724.vastapp.activity.dashboard.screen.HelpScreen
import eu.m724.vastapp.activity.dashboard.screen.InstancesScreen
import eu.m724.vastapp.activity.dashboard.screen.Screen
import eu.m724.vastapp.ui.theme.VastappTheme
-import eu.m724.vastapp.vastai.VastApi
-import eu.m724.vastapp.vastai.data.User
import kotlinx.coroutines.launch
-import org.chromium.net.CronetEngine
-import java.util.concurrent.Executors
class DashboardActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val user = intent.getParcelableExtra("user")!! // TODO null check
-
- val executor = Executors.newSingleThreadExecutor()
- val cronetEngine = CronetEngine.Builder(baseContext).enableBrotli(true).build()
- val vastApi = VastApi(user.apiKey, cronetEngine, executor) // TODO use that from login activity
-
- val dashboardViewModel = DashboardViewModel(user, vastApi)
+ val dashboardViewModel = DashboardViewModel(application)
+ if (intent.getBooleanExtra("direct", false).not()) {
+ dashboardViewModel.refresh(this)
+ }
lifecycleScope.launch {
- dashboardViewModel.refresh(this@DashboardActivity)
repeatOnLifecycle(Lifecycle.State.STARTED) {
dashboardViewModel.refreshError.collect {
it.forEach { errorMsg ->
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardViewModel.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardViewModel.kt
index 21b65de..fbb1ff8 100644
--- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardViewModel.kt
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardViewModel.kt
@@ -1,19 +1,19 @@
package eu.m724.vastapp.activity.dashboard
import android.annotation.SuppressLint
+import android.app.Application
import android.content.Context
import androidx.activity.ComponentActivity
+import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.ViewModel
import eu.m724.vastapp.R
+import eu.m724.vastapp.VastApplication
import eu.m724.vastapp.activity.Opener
import eu.m724.vastapp.activity.PermissionChecker
import eu.m724.vastapp.vastai.ApiRoute
-import eu.m724.vastapp.vastai.VastApi
import eu.m724.vastapp.vastai.api.InstancesUrlRequestCallback
import eu.m724.vastapp.vastai.api.UserUrlRequestCallback
import eu.m724.vastapp.vastai.data.RentedInstance
-import eu.m724.vastapp.vastai.data.User
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -21,30 +21,24 @@ import kotlinx.coroutines.flow.update
class DashboardViewModel(
- initialUser: User,
- private val vastApi: VastApi
-) : ViewModel() { // TODO do something with the user
+ application: Application
+) : AndroidViewModel(application) { // TODO do something with the user
private val _uiState: MutableStateFlow =
MutableStateFlow(DashboardUiState(0))
val uiState: StateFlow =
_uiState.asStateFlow()
- private val _rentedInstances: MutableStateFlow> = MutableStateFlow(emptyList())
- val rentedInstances: StateFlow> = _rentedInstances.asStateFlow()
-
- private val _user: MutableStateFlow = MutableStateFlow(initialUser)
- val user: StateFlow = _user.asStateFlow()
-
- private val _remainingTime: MutableStateFlow = MutableStateFlow(-1)
- var remainingTime: StateFlow = _remainingTime.asStateFlow()
-
private val _refreshError: MutableStateFlow> = MutableStateFlow(emptyList())
val refreshError: StateFlow> = _refreshError.asStateFlow()
private val _termuxAvailable: MutableStateFlow = MutableStateFlow(0)
val termuxAvailable: StateFlow = _termuxAvailable.asStateFlow()
+ private val application = getApplication()
+ private val vastApi = this.application.vastApi
+ val account = this.application.account!!
+
fun refresh(activity: ComponentActivity) {
_uiState.value = _uiState.value.copy(refreshing = 2)
_refreshError.value = emptyList()
@@ -52,7 +46,7 @@ class DashboardViewModel(
val userRequest = vastApi.buildRequest(
ApiRoute.SHOW_USER,
UserUrlRequestCallback({ newUser ->
- _user.value = newUser
+ account.updateUser(newUser)
_uiState.update {
it.copy(refreshing = it.refreshing - 1) // TODO I don't like how this looks
}
@@ -67,33 +61,17 @@ class DashboardViewModel(
val instancesRequest = vastApi.buildRequest(
ApiRoute.GET_INSTANCES,
InstancesUrlRequestCallback({ instances ->
- _rentedInstances.value = instances // TODO better way?
+ account.updateRentedInstances(instances)
_uiState.update {
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 ->
_refreshError.update { it + apiFailure.errorMessage!! }
_uiState.update {
it.copy(refreshing = it.refreshing - 1)
}
})
- )
+ ) // TODO move all that refreshing to some shared place
userRequest.start()
instancesRequest.start()
@@ -111,7 +89,7 @@ class DashboardViewModel(
}
} else -1 // not available
- // TODO I don't like this function especially the last line
+ // TODO I don't like this function especially the last line. I think it should be moved to application
}
@SuppressLint("SdCardPath")
@@ -123,7 +101,7 @@ class DashboardViewModel(
PermissionChecker.requestIfNoPermission(
"com.termux.permission.RUN_COMMAND",
activity, 0
- ) { granted, asked ->
+ ) { granted, _ ->
if (granted) {
val arguments = arrayOf(
"/data/data/com.termux/files/usr/bin/ssh",
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingActivity.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingActivity.kt
new file mode 100644
index 0000000..7f0f942
--- /dev/null
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingActivity.kt
@@ -0,0 +1,76 @@
+package eu.m724.vastapp.activity.dashboard.loading
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Icon
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import eu.m724.vastapp.R
+import eu.m724.vastapp.activity.dashboard.DashboardActivity
+import eu.m724.vastapp.activity.dashboard.loading.ui.theme.VastappTheme
+import eu.m724.vastapp.activity.login.LoginActivity
+
+class LoadingActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val viewModel = LoadingViewModel(application) { result ->
+ println(result.loggedIn)
+ val intent =
+ if (result.loggedIn) {
+ Intent(this, DashboardActivity::class.java)
+ .putExtra("direct", true)
+ } else {
+ Intent(this, LoginActivity::class.java)
+ .putExtra("error", result.error!!)
+ }
+
+ this.startActivity(intent)
+ finish()
+ }
+
+ viewModel.init()
+
+ enableEdgeToEdge()
+ setContent {
+ VastappTheme {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.fillMaxHeight().weight(1f))
+ Box(
+ modifier = Modifier
+ .fillMaxHeight()
+ .weight(1f),
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ modifier = Modifier.fillMaxSize(),
+ painter = painterResource(id = R.drawable.ic_launcher_foreground),
+ contentDescription = "app icon"
+ )
+ }
+ Box(
+ modifier = Modifier
+ .fillMaxHeight()
+ .weight(1f),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator()
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingResult.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingResult.kt
new file mode 100644
index 0000000..ff03242
--- /dev/null
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingResult.kt
@@ -0,0 +1,6 @@
+package eu.m724.vastapp.activity.dashboard.loading
+
+data class LoadingResult(
+ val loggedIn: Boolean,
+ val error: String?
+)
\ No newline at end of file
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingViewModel.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingViewModel.kt
new file mode 100644
index 0000000..5bbb4e0
--- /dev/null
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingViewModel.kt
@@ -0,0 +1,54 @@
+package eu.m724.vastapp.activity.dashboard.loading
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import eu.m724.vastapp.VastApplication
+import eu.m724.vastapp.vastai.Account
+import eu.m724.vastapp.vastai.ApiRoute
+import eu.m724.vastapp.vastai.api.InstancesUrlRequestCallback
+import eu.m724.vastapp.vastai.api.UserUrlRequestCallback
+
+class LoadingViewModel(
+ application: Application,
+ private val onEnded: (LoadingResult) -> Unit,
+) : AndroidViewModel(application) {
+ private val application = application as VastApplication
+
+ fun init() {
+ val vastApi = application.vastApi
+ val apiKey = application.loadKey()
+
+ if (apiKey != null) {
+ vastApi.apiKey = apiKey
+
+ val request = vastApi.buildRequest(
+ ApiRoute.SHOW_USER,
+ UserUrlRequestCallback({ user ->
+ application.account = Account(user)
+ loadInstances()
+ }, { apiFailure ->
+ onEnded(LoadingResult(false, apiFailure.errorMessage))
+ })
+ )
+
+ request.start()
+ }
+ }
+
+ private fun loadInstances() {
+ val vastApi = application.vastApi
+
+ val instancesRequest = vastApi.buildRequest(
+ ApiRoute.GET_INSTANCES,
+ InstancesUrlRequestCallback({ instances ->
+ application.account!!.updateRentedInstances(instances)
+ onEnded(LoadingResult(true, null))
+ }, { apiFailure ->
+ // TODO I don't know what to do yet
+ onEnded(LoadingResult(true, null))
+ })
+ )
+
+ instancesRequest.start()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/ui/theme/Color.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/ui/theme/Color.kt
new file mode 100644
index 0000000..b383a21
--- /dev/null
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/ui/theme/Color.kt
@@ -0,0 +1,11 @@
+package eu.m724.vastapp.activity.dashboard.loading.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/ui/theme/Theme.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/ui/theme/Theme.kt
new file mode 100644
index 0000000..10e5b06
--- /dev/null
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/ui/theme/Theme.kt
@@ -0,0 +1,58 @@
+package eu.m724.vastapp.activity.dashboard.loading.ui.theme
+
+import android.app.Activity
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+
+private val DarkColorScheme = darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80
+)
+
+private val LightColorScheme = lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40
+
+ /* Other default colors to override
+ background = Color(0xFFFFFBFE),
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+)
+
+@Composable
+fun VastappTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/ui/theme/Type.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/ui/theme/Type.kt
new file mode 100644
index 0000000..09e2472
--- /dev/null
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/ui/theme/Type.kt
@@ -0,0 +1,34 @@
+package eu.m724.vastapp.activity.dashboard.loading.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ bodyLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ )
+ /* Other default text styles to override
+ titleLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Billing.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Billing.kt
index eb5dd2b..c693e32 100644
--- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Billing.kt
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Billing.kt
@@ -29,7 +29,7 @@ import eu.m724.vastapp.activity.dashboard.DashboardViewModel
fun BillingScreen(dashboardViewModel: DashboardViewModel) {
val uiState by dashboardViewModel.uiState.collectAsState()
- val user by dashboardViewModel.user.collectAsState()
+ val user by dashboardViewModel.account.user.collectAsState()
Column(
modifier = Modifier.fillMaxWidth(),
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Dashboard.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Dashboard.kt
index fadbaed..ba882a5 100644
--- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Dashboard.kt
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Dashboard.kt
@@ -44,9 +44,9 @@ fun DashboardScreen(dashboardViewModel: DashboardViewModel) {
val context = LocalContext.current
val uiState by dashboardViewModel.uiState.collectAsState()
- val user by dashboardViewModel.user.collectAsState()
- val rentedInstances by dashboardViewModel.rentedInstances.collectAsState()
- val remainingTime by dashboardViewModel.remainingTime.collectAsState()
+ val user by dashboardViewModel.account.user.collectAsState()
+ val rentedInstances by dashboardViewModel.account.rentedInstances.collectAsState()
+ val remainingTime by dashboardViewModel.account.remainingTime.collectAsState()
val isRefreshing by remember(uiState) { derivedStateOf { uiState.refreshing > 0 } }
val scrollState = rememberScrollState()
diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Instances.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Instances.kt
index 882734c..3f7882d 100644
--- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Instances.kt
+++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Instances.kt
@@ -42,7 +42,7 @@ fun InstancesScreen(dashboardViewModel: DashboardViewModel) {
val activity = LocalContext.current as ComponentActivity
val uiState by dashboardViewModel.uiState.collectAsState()
- val rentedInstances by dashboardViewModel.rentedInstances.collectAsState()
+ val rentedInstances by dashboardViewModel.account.rentedInstances.collectAsState()
val termuxAvailable by dashboardViewModel.termuxAvailable.collectAsState()
// TODO actually get instances
diff --git a/app/src/main/java/eu/m724/vastapp/activity/login/LoginActivity.kt b/app/src/main/java/eu/m724/vastapp/activity/login/LoginActivity.kt
index 1045384..0aac63f 100644
--- a/app/src/main/java/eu/m724/vastapp/activity/login/LoginActivity.kt
+++ b/app/src/main/java/eu/m724/vastapp/activity/login/LoginActivity.kt
@@ -55,7 +55,6 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import androidx.lifecycle.lifecycleScope
-import eu.m724.vastapp.BuildConfig
import eu.m724.vastapp.R
import eu.m724.vastapp.activity.dashboard.DashboardActivity
import eu.m724.vastapp.ui.theme.VastappTheme
@@ -84,6 +83,11 @@ class LoginActivity : ComponentActivity() {
}
}
+ val loadingError = intent.getStringExtra("error")
+ if (loadingError != null) {
+ Toast.makeText(baseContext, loadingError, Toast.LENGTH_SHORT).show()
+ }
+
lifecycleScope.launch { // TODO I was suggested not to launch an activity from a lifecycle scope
loginViewModel.uiState.collect { state ->
if (state is LoginUiState.Success) {
@@ -92,9 +96,6 @@ class LoginActivity : ComponentActivity() {
}
}
- if (BuildConfig.AUTO_LOGIN)
- loginViewModel.loadKey()
-
enableEdgeToEdge()
setContent {
VastappTheme {
diff --git a/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt b/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt
index a079008..6be7c72 100644
--- a/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt
+++ b/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt
@@ -1,12 +1,11 @@
package eu.m724.vastapp.activity.login
import android.app.Application
-import android.content.Context
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
+import eu.m724.vastapp.VastApplication
import eu.m724.vastapp.vastai.ApiRoute
-import eu.m724.vastapp.vastai.VastApi
import eu.m724.vastapp.vastai.api.UserUrlRequestCallback
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -34,35 +33,16 @@ class LoginViewModel(
private val _fullscreenLoading = MutableStateFlow(false)
var fullscreenLoading: StateFlow = _fullscreenLoading.asStateFlow()
- private val applicationContext = getApplication().applicationContext
- private val sharedPreferences = applicationContext.getSharedPreferences("login", Context.MODE_PRIVATE)
-
-
- private fun saveKey() {
- with (sharedPreferences.edit()) {
- putString("apiKey", apiKey.value) // TODO encrypt
- apply()
- }
- }
-
- fun loadKey() {
- val apiKey = sharedPreferences.getString("apiKey", null)
-
- if (apiKey != null) {
- _apiKey.value = apiKey
- _fullscreenLoading.value = true
- tryLogin()
- }
- }
+ private val application = getApplication()
fun tryLogin() {
- val apiKey = apiKey.value
+ val vastApi = application.vastApi
+ vastApi.apiKey = apiKey.value
- val vastApi = VastApi(apiKey, cronetEngine, executor)
val request = vastApi.buildRequest(
ApiRoute.SHOW_USER,
UserUrlRequestCallback({ user ->
- saveKey() // TODO toggle for this
+ application.submitKey(apiKey.value) // TODO toggle for this
_uiState.value = LoginUiState.Success(user)
}, { apiFailure ->
_uiState.value = LoginUiState.Idle
diff --git a/app/src/main/java/eu/m724/vastapp/vastai/Account.kt b/app/src/main/java/eu/m724/vastapp/vastai/Account.kt
new file mode 100644
index 0000000..a846009
--- /dev/null
+++ b/app/src/main/java/eu/m724/vastapp/vastai/Account.kt
@@ -0,0 +1,49 @@
+package eu.m724.vastapp.vastai
+
+import eu.m724.vastapp.vastai.data.RentedInstance
+import eu.m724.vastapp.vastai.data.User
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class Account(
+ initialUser: User
+) {
+ private val _rentedInstances: MutableStateFlow> = MutableStateFlow(emptyList())
+ val rentedInstances: StateFlow> = _rentedInstances.asStateFlow()
+
+ private val _user: MutableStateFlow = MutableStateFlow(initialUser)
+ val user: StateFlow = _user.asStateFlow()
+
+ private val _remainingTime: MutableStateFlow = MutableStateFlow(-1)
+ var remainingTime: StateFlow = _remainingTime.asStateFlow()
+
+ fun updateUser(user: User) {
+ _user.value = user
+ }
+
+ fun updateRentedInstances(rentedInstances: List) { // TODO better way?
+ _rentedInstances.value = rentedInstances
+ calculateRemainingTime()
+ }
+
+ fun calculateRemainingTime() {
+ val rentedInstances = rentedInstances.value
+
+ if (rentedInstances.isEmpty()) {
+ _remainingTime.value = -1
+ } else {
+ var totalDph = 0.0
+
+ rentedInstances.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()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/m724/vastapp/vastai/VastApi.kt b/app/src/main/java/eu/m724/vastapp/vastai/VastApi.kt
index 9edb9a5..920a174 100644
--- a/app/src/main/java/eu/m724/vastapp/vastai/VastApi.kt
+++ b/app/src/main/java/eu/m724/vastapp/vastai/VastApi.kt
@@ -1,16 +1,14 @@
package eu.m724.vastapp.vastai
-import android.os.Parcel
-import android.os.Parcelable
import eu.m724.vastapp.BuildConfig
import org.chromium.net.CronetEngine
import org.chromium.net.UrlRequest
import java.util.concurrent.Executor
class VastApi(
- val apiKey: String,
- val cronetEngine: CronetEngine,
- val executor: Executor
+ var apiKey: String, // TODO make private?
+ private val cronetEngine: CronetEngine,
+ private val executor: Executor
) {
/**
* build an api request
diff --git a/app/src/main/java/eu/m724/vastapp/vastai/api/UserUrlRequestCallback.kt b/app/src/main/java/eu/m724/vastapp/vastai/api/UserUrlRequestCallback.kt
index 7bf693e..ddef734 100644
--- a/app/src/main/java/eu/m724/vastapp/vastai/api/UserUrlRequestCallback.kt
+++ b/app/src/main/java/eu/m724/vastapp/vastai/api/UserUrlRequestCallback.kt
@@ -43,16 +43,23 @@ class UserUrlRequestCallback(
override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
println(stringResponse) // TODO don't do that
if (info?.httpStatusCode == 200) {
- val jsonResponse = JSONObject(stringResponse.toString())
- onSuccess(User(
- id = jsonResponse.getString("id"),
- username = jsonResponse.getString("username"),
- email = jsonResponse.getString("email"),
- apiKey = jsonResponse.getString("api_key"),
- credit = jsonResponse.getDouble("credit"),
- balanceThreshold = jsonResponse.getDouble("balance_threshold"),
- balanceThresholdEnabled = jsonResponse.getBoolean("balance_threshold_enabled"),
- ))
+ try {
+ val jsonResponse = JSONObject(stringResponse.toString())
+ onSuccess(
+ User(
+ id = jsonResponse.getString("id"),
+ username = jsonResponse.getString("username"),
+ email = jsonResponse.getString("email"),
+ apiKey = jsonResponse.getString("api_key"),
+ credit = jsonResponse.getDouble("credit"),
+ balanceThreshold = jsonResponse.getDouble("balance_threshold"),
+ balanceThresholdEnabled = jsonResponse.getBoolean("balance_threshold_enabled"),
+ )
+ )
+ } catch (e: Exception) {
+ onFailure(ApiFailure(e.message))
+ println("API response error: $stringResponse")
+ }
} else {
onFailure(ApiFailure("${info?.httpStatusCode} ${info?.httpStatusText}"))
println("API error: $stringResponse")
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6adf639..7c12a4d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,7 +1,8 @@
vast.app
Dashboard
- vast.app
+ Login
+ vast.app
Dashboard
Billing
Instances
@@ -21,14 +22,13 @@
checkbox is angry
none yet sorry
If you change your mind, do so from settings
- Termuxn\'t
- TermuxSshActivity
+ Termux Error
No ssh client on termux, install dropbear or openssh package
Copied command to clipboard
Install Dropbear with:
Open Termux
Termux is not configured for usage with other apps.
Open instructions on github.com
- An error occured:
+ An error occurred:
(this will be a webview)
\ No newline at end of file