Compare commits

..

No commits in common. "046e3147d919cc0d0c18e83f55fff568936e9857" and "09f727c643c5470cb1fdaaf77d2d9f85b0f34420" have entirely different histories.

12 changed files with 295 additions and 90 deletions

1
.gitignore vendored
View file

@ -17,4 +17,3 @@ local.properties
# useless
/.idea/deploymentTargetSelector.xml
/.idea/other.xml

263
.idea/other.xml Normal file
View file

@ -0,0 +1,263 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="direct_access_persist.xml">
<option name="deviceSelectionList">
<list>
<PersistentDeviceSelectionData>
<option name="api" value="27" />
<option name="brand" value="DOCOMO" />
<option name="codename" value="F01L" />
<option name="id" value="F01L" />
<option name="manufacturer" value="FUJITSU" />
<option name="name" value="F-01L" />
<option name="screenDensity" value="360" />
<option name="screenX" value="720" />
<option name="screenY" value="1280" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="28" />
<option name="brand" value="DOCOMO" />
<option name="codename" value="SH-01L" />
<option name="id" value="SH-01L" />
<option name="manufacturer" value="SHARP" />
<option name="name" value="AQUOS sense2 SH-01L" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1080" />
<option name="screenY" value="2160" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="samsung" />
<option name="codename" value="a51" />
<option name="id" value="a51" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy A51" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="akita" />
<option name="id" value="akita" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="b0q" />
<option name="id" value="b0q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S22 Ultra" />
<option name="screenDensity" value="600" />
<option name="screenX" value="1440" />
<option name="screenY" value="3088" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="32" />
<option name="brand" value="google" />
<option name="codename" value="bluejay" />
<option name="id" value="bluejay" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 6a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="29" />
<option name="brand" value="samsung" />
<option name="codename" value="crownqlteue" />
<option name="id" value="crownqlteue" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Note9" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2220" />
<option name="screenY" value="1080" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="dm3q" />
<option name="id" value="dm3q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S23 Ultra" />
<option name="screenDensity" value="600" />
<option name="screenX" value="1440" />
<option name="screenY" value="3088" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="felix" />
<option name="id" value="felix" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="felix_camera" />
<option name="id" value="felix_camera" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold (Camera-enabled)" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="gts8uwifi" />
<option name="id" value="gts8uwifi" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Tab S8 Ultra" />
<option name="screenDensity" value="320" />
<option name="screenX" value="1848" />
<option name="screenY" value="2960" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="husky" />
<option name="id" value="husky" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8 Pro" />
<option name="screenDensity" value="390" />
<option name="screenX" value="1008" />
<option name="screenY" value="2244" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="motorola" />
<option name="codename" value="java" />
<option name="id" value="java" />
<option name="manufacturer" value="Motorola" />
<option name="name" value="G20" />
<option name="screenDensity" value="280" />
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="lynx" />
<option name="id" value="lynx" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 7a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="google" />
<option name="codename" value="oriole" />
<option name="id" value="oriole" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 6" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="panther" />
<option name="id" value="panther" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 7" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="samsung" />
<option name="codename" value="q2q" />
<option name="id" value="q2q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Z Fold3" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1768" />
<option name="screenY" value="2208" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="q5q" />
<option name="id" value="q5q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Z Fold5" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1812" />
<option name="screenY" value="2176" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="google" />
<option name="codename" value="r11" />
<option name="id" value="r11" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Watch" />
<option name="screenDensity" value="320" />
<option name="screenX" value="384" />
<option name="screenY" value="384" />
<option name="type" value="WEAR_OS" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="google" />
<option name="codename" value="redfin" />
<option name="id" value="redfin" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 5" />
<option name="screenDensity" value="440" />
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="shiba" />
<option name="id" value="shiba" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="tangorpro" />
<option name="id" value="tangorpro" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Tablet" />
<option name="screenDensity" value="320" />
<option name="screenX" value="1600" />
<option name="screenY" value="2560" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="29" />
<option name="brand" value="samsung" />
<option name="codename" value="x1q" />
<option name="id" value="x1q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S20" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1440" />
<option name="screenY" value="3200" />
</PersistentDeviceSelectionData>
</list>
</option>
</component>
</project>

View file

@ -15,8 +15,4 @@ I'm making this to learn stuff please don't rely on this app
home and instances icons are from font awesome
### TODOs
- move todos to issues
- readme
- figure out the api
- gb gib mb mib
TODO

View file

@ -1,7 +1,6 @@
package eu.m724.vastapp.activity.dashboard
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
@ -20,9 +19,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
@ -30,15 +26,14 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import eu.m724.vastapp.activity.dashboard.screen.Screen
import eu.m724.vastapp.activity.dashboard.screen.BillingScreen
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.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
@ -54,17 +49,6 @@ class DashboardActivity : ComponentActivity() {
val dashboardViewModel = DashboardViewModel(user, vastApi)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
dashboardViewModel.refreshError.collect {
it.forEach { errorMsg ->
Toast.makeText(baseContext, errorMsg, Toast.LENGTH_SHORT).show()
}
}
}
}
enableEdgeToEdge()
setContent {
VastappTheme {

View file

@ -1,6 +1,9 @@
package eu.m724.vastapp.activity.dashboard
import eu.m724.vastapp.vastai.data.User
data class DashboardUiState(
val refreshing: Int = 0
) {
}
val isRefreshing: Boolean,
val user: User,
val error: String?
) { }

View file

@ -3,69 +3,30 @@ package eu.m724.vastapp.activity.dashboard
import androidx.lifecycle.ViewModel
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.Instance
import eu.m724.vastapp.vastai.data.User
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
class DashboardViewModel(initialUser: User, private val vastApi: VastApi) : ViewModel() { // TODO do something with the user
private val _uiState: MutableStateFlow<DashboardUiState> =
MutableStateFlow(DashboardUiState(0))
MutableStateFlow(DashboardUiState(false, initialUser, null))
val uiState: StateFlow<DashboardUiState> =
_uiState.asStateFlow()
private val _rentedInstances: MutableStateFlow<List<Instance>> = MutableStateFlow(emptyList())
val rentedInstances: StateFlow<List<Instance>> = _rentedInstances.asStateFlow()
private val _user: MutableStateFlow<User> = MutableStateFlow(initialUser)
val user: StateFlow<User> = _user.asStateFlow()
private val _refreshError: MutableStateFlow<List<String>> = MutableStateFlow(emptyList())
val refreshError: StateFlow<List<String>> = _refreshError.asStateFlow()
fun refresh() {
_uiState.value = _uiState.value.copy(refreshing = 2)
_refreshError.value = emptyList()
val userRequest = vastApi.buildRequest(
val request = vastApi.buildRequest(
ApiRoute.SHOW_USER,
UserUrlRequestCallback({ newUser ->
_user.value = newUser
_uiState.update {
it.copy(refreshing = it.refreshing - 1) // TODO I don't like how this looks
}
UserUrlRequestCallback({ user ->
_uiState.value = _uiState.value.copy(isRefreshing = false, user = user)
}, { apiFailure ->
_refreshError.update { it + apiFailure.errorMessage!! }
_uiState.update {
it.copy(refreshing = it.refreshing - 1)
}
_uiState.value = _uiState.value.copy(isRefreshing = false, error = apiFailure.errorMessage)
})
)
val instancesRequest = vastApi.buildRequest(
ApiRoute.GET_INSTANCES,
InstancesUrlRequestCallback({ instances ->
_rentedInstances.value = instances // TODO better way?
_uiState.update {
it.copy(refreshing = it.refreshing - 1)
_uiState.value = _uiState.value.copy(isRefreshing = true)
request.start()
}
}, { apiFailure ->
_refreshError.update { it + apiFailure.errorMessage!! }
_uiState.update {
it.copy(refreshing = it.refreshing - 1)
}
})
)
userRequest.start()
instancesRequest.start()
// TODO I don't like this function especially the last line
}
}

View file

@ -9,13 +9,16 @@ 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.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
@ -29,7 +32,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 remember(uiState) { derivedStateOf { uiState.user } }
Column(
modifier = Modifier.fillMaxWidth(),

View file

@ -25,7 +25,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
@ -44,15 +43,13 @@ import eu.m724.vastapp.activity.dashboard.DashboardViewModel
fun DashboardScreen(dashboardViewModel: DashboardViewModel) {
val uiState by dashboardViewModel.uiState.collectAsState()
val user by dashboardViewModel.user.collectAsState()
val rentedInstances by dashboardViewModel.rentedInstances.collectAsState()
val user by remember(uiState) { derivedStateOf { uiState.user } }
val remainingTime by rememberSaveable { mutableIntStateOf(6000000) }
val isRefreshing by remember(uiState) { derivedStateOf { uiState.refreshing > 0 } }
val scrollState = rememberScrollState()
PullToRefreshBox(
isRefreshing = isRefreshing,
isRefreshing = uiState.isRefreshing,
state = rememberPullToRefreshState(),
onRefresh = { dashboardViewModel.refresh() }
) {
@ -145,7 +142,7 @@ fun DashboardScreen(dashboardViewModel: DashboardViewModel) {
modifier = Modifier.width(12.dp)
)
Text(
text = rentedInstances.size.toString(),
text = "4",
fontSize = 22.sp
)
}

View file

@ -1,7 +1,7 @@
package eu.m724.vastapp.vastai.api
import eu.m724.vastapp.vastai.ApiFailure
import eu.m724.vastapp.vastai.data.Instance
import eu.m724.vastapp.vastai.data.User
import org.chromium.net.CronetException
import org.chromium.net.UrlRequest
import org.chromium.net.UrlResponseInfo
@ -10,7 +10,7 @@ import java.nio.ByteBuffer
import java.nio.charset.CodingErrorAction
class InstancesUrlRequestCallback(
val onSuccess: (List<Instance>) -> Unit,
val onSuccess: (List<JSONObject>) -> Unit,
val onFailure: (ApiFailure) -> Unit
) : UrlRequest.Callback() {
@ -40,17 +40,16 @@ class InstancesUrlRequestCallback(
}
override fun onSucceeded(request: UrlRequest?, info: UrlResponseInfo?) {
println(stringResponse) // TODO don't do that
if (info?.httpStatusCode == 200) {
val jsonResponse = JSONObject(stringResponse.toString())
val instances = ArrayList<Instance>()
val instances = ArrayList<JSONObject>()
val instancesJson = jsonResponse.getJSONArray("instances")
for (i in 0..<instancesJson.length()) {
instances.add(Instance.fromJson(instancesJson.getJSONObject(i)))
instances.add(instancesJson.getJSONObject(i))
}
onSuccess(instances) // TODO handle json errors
onSuccess(instances) // TODO make it better
} else {
onFailure(ApiFailure("${info?.httpStatusCode} ${info?.httpStatusText}"))
println("API error: $stringResponse")

View file

@ -41,9 +41,9 @@ 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())
println(jsonResponse)
onSuccess(User(
id = jsonResponse.getString("id"),
username = jsonResponse.getString("username"),

View file

@ -24,7 +24,7 @@ data class Instance(
* at least I think because it's recent and unavailable machines don't have that */
val startDate: Long,
/** when instance will expire as unix seconds, like 1861891577 */
val endDate: Long?
val endDate: Long
) {
companion object {
fun fromJson(json: JSONObject): Instance {
@ -36,7 +36,7 @@ data class Instance(
json.getInt("num_gpus"),
json.getDouble("dlperf"),
json.getDouble("start_date").toLong(),
json.optLong("end_date").takeIf { it > 0 }
json.getDouble("end_date").toLong()
)
}
}

View file

@ -65,7 +65,7 @@ data class Machine(
json.getString("mobo_name"),
json.getInt("cpu_ram"),
json.getDouble("reliability2"),
HostingClass.entries.getOrElse(json.optInt("hosting_type", 0)) { HostingClass.PRIVATE },
HostingClass.entries.getOrElse(json.getInt("hostingType")) { HostingClass.PRIVATE },
MachineVerification.fromString(json.getString("verification"))
)
}