diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 657436f..173506d 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -8,4 +8,9 @@ overport libraries cannot be downloaded. Unknown error has been occurred. Update available! + Unknown + Playable + Has problems + Not working + Loading… \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/model/GithubIssues.kt b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/model/GithubIssues.kt new file mode 100644 index 0000000..b36e937 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/model/GithubIssues.kt @@ -0,0 +1,32 @@ +package moe.crx.ovrport.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GithubIssueLabel( + @SerialName("id") + val id: Long, + @SerialName("name") + val name: String, + @SerialName("color") + val color: String, +) + +@Serializable +data class GithubIssue( + @SerialName("html_url") + var htmlUrl: String, + @SerialName("title") + val title: String, + @SerialName("created_at") + val createdAt: String, + @SerialName("labels") + val labels: List +) + +@Serializable +data class GithubIssues( + @SerialName("items") + val items: List +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/patch/Constants.kt b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/patch/Constants.kt index 4bb9bfc..24d004e 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/patch/Constants.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/patch/Constants.kt @@ -1,9 +1,130 @@ package moe.crx.ovrport.patch +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import kotlinx.serialization.json.Json +import moe.crx.ovrport.model.GithubIssues +import moe.crx.ovrport.patch.Constants.UNKNOWN_COMPATIBILITY +import moe.crx.ovrport.utils.HttpUtil.download +import org.jetbrains.compose.resources.StringResource +import overportapp.composeapp.generated.resources.* +import overportapp.composeapp.generated.resources.Res +import overportapp.composeapp.generated.resources.status_has_problems +import overportapp.composeapp.generated.resources.status_not_working +import overportapp.composeapp.generated.resources.status_unknown +import java.net.URLEncoder + +private val json = Json { ignoreUnknownKeys = true } + object Constants { const val APK_MIMETYPE = "application/vnd.android.package-archive" const val OVRPORT_VERSION = "3.2.2" const val LIBRARIES_URL = "https://files.crx.moe/ovrport/tracker/releases/download/$OVRPORT_VERSION/libraries.zip" const val LIBRARIES_DIR = "libraries/$OVRPORT_VERSION" const val RELEASES_URL = "https://api.github.com/repos/ovrport/app/releases" + const val ISSUES_URL = "https://api.github.com/search/issues?q=%s+repo:ovrport/tracker" + private const val TRACKER_URL = "https://github.com/ovrport/tracker/issues" + val UNKNOWN_COMPATIBILITY = CompatibilityStatusInfo(CompatibilityStatus.UNKNOWN, TRACKER_URL) + val LOADING_COMPATIBILITY = CompatibilityStatusInfo(CompatibilityStatus.LOADING, TRACKER_URL) +} + +enum class CompatibilityStatus( + val labelName: String, + val icon: ImageVector, + val nameResource: StringResource, + val cardColor: Color, + val contentColor: Color, +) { + LOADING( + "Loading", + Icons.Default.Sync, + Res.string.status_loading, + Color(0xFF483f77), + Color(0xFFe6deff), + ), + UNKNOWN( + "Unknown", + Icons.Default.QuestionMark, + Res.string.status_unknown, + Color(0xFF354479), + Color(0xFFDCE1FF), + ), + NOT_WORKING( + "Not working", + Icons.Default.Close, + Res.string.status_not_working, + Color(0xFF73332f), + Color(0xFFffdad7), + ), + HAS_PROBLEMS( + "Has problems", + Icons.Default.Warning, + Res.string.status_has_problems, + Color(0xFF5b4300), + Color(0xFFffdf9f), + ), + PLAYABLE( + "Playable", + Icons.Default.Check, + Res.string.status_playable, + Color(0xFF2d4f1d), + Color(0xFFc4efac), + ), +} + +data class CompatibilityStatusInfo( + val status: CompatibilityStatus, + val url: String, +) + + +fun getCompatibilityStatus( + applicationName: String?, + applicationPackage: String? +): CompatibilityStatusInfo { + val pkg = applicationName ?: "" + val name = applicationPackage ?: "" + + runCatching { + val issue = json + .decodeFromString( + String( + download( + Constants.ISSUES_URL.format( + URLEncoder.encode( + pkg, + Charsets.UTF_8 + ) + ) + ) + ) + ) + .items.firstOrNull { it.title.contains(pkg) || it.title.contains(name) } + ?: json + .decodeFromString( + String( + download( + Constants.ISSUES_URL.format( + URLEncoder.encode( + name, + Charsets.UTF_8 + ) + ) + ) + ) + ) + .items.firstOrNull { it.title.contains(pkg) || it.title.contains(name) } + + issue?.labels?.forEach { label -> + CompatibilityStatus.entries.forEach { status -> + if (status.labelName == label.name) { + return CompatibilityStatusInfo(status, issue.htmlUrl) + } + } + } + } + + return UNKNOWN_COMPATIBILITY } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/AppContent.kt b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/AppContent.kt index 7f727a5..23c1254 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/AppContent.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/AppContent.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CloudDownload -import androidx.compose.material.icons.filled.Download import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -79,7 +78,6 @@ fun AppContent( Constants.OVRPORT_VERSION, modifier = Modifier.padding(8.dp), fontSize = 18.sp, - color = MaterialTheme.colorScheme.secondary ) } }, @@ -103,7 +101,6 @@ fun AppContent( Text( stringResource(Res.string.update_available), fontSize = 18.sp, - color = MaterialTheme.colorScheme.secondary, modifier = Modifier.padding(0.dp, 8.dp, 8.dp, 8.dp), ) } diff --git a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/ApplicationInfoCard.kt b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/ApplicationInfoCard.kt index fca4a2e..b4d29d2 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/ApplicationInfoCard.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/ApplicationInfoCard.kt @@ -2,43 +2,110 @@ package moe.crx.ovrport.ui import androidx.compose.foundation.Image import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -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.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember +import androidx.compose.material.icons.filled.Widgets +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import moe.crx.ovrport.patch.Constants.LOADING_COMPATIBILITY import moe.crx.ovrport.patch.Patch import moe.crx.ovrport.patch.PatchStore -import org.jetbrains.compose.ui.tooling.preview.Preview -import androidx.compose.material.icons.filled.Widgets -import androidx.compose.material3.* +import moe.crx.ovrport.patch.getCompatibilityStatus import org.jetbrains.compose.resources.stringResource +import org.jetbrains.compose.ui.tooling.preview.Preview import overportapp.composeapp.generated.resources.Res import overportapp.composeapp.generated.resources.unknown_package +@Composable +fun ApplicationInfo( + applicationName: String? = "Application", + applicationPackage: String? = "com.company.application", + applicationVersion: String? = "1.0.0", + applicationIcon: ImageBitmap? = null, +) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + if (applicationIcon == null) { + Icon( + Icons.Default.Widgets, + contentDescription = null, + modifier = Modifier.size(48.dp) + ) + } else { + Image( + applicationIcon, + contentDescription = null, + modifier = Modifier.size(48.dp).clip(RoundedCornerShape(8.dp)) + ) + } + Column( + modifier = Modifier.height(48.dp), + verticalArrangement = Arrangement.Center + ) { + Text("$applicationName ($applicationVersion)") + Text( + applicationPackage ?: stringResource(Res.string.unknown_package), + ) + } + } +} + +@Composable +fun ApplicationStatus( + applicationName: String? = "Application", + applicationPackage: String? = "", +) { + var statusInfo by remember(applicationPackage) { mutableStateOf(LOADING_COMPATIBILITY) } + val urlHandler = LocalUriHandler.current + + LaunchedEffect(applicationPackage) { + withContext(Dispatchers.IO) { + statusInfo = getCompatibilityStatus(applicationName, applicationPackage) + } + } + + + Card(colors = CardDefaults.cardColors(statusInfo.status.cardColor, statusInfo.status.contentColor)) { + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clip( + RoundedCornerShape(16.dp) + ).clickable { + urlHandler.openUri(statusInfo.url) + }) { + Icon( + statusInfo.status.icon, + contentDescription = null, + modifier = Modifier.padding(16.dp), + ) + Text( + stringResource(statusInfo.status.nameResource), + fontSize = 18.sp, + modifier = Modifier.padding(0.dp, 16.dp, 16.dp, 16.dp), + ) + } + } +} + @OptIn(ExperimentalLayoutApi::class) @Composable @Preview fun ApplicationInfoCard( innerPadding: PaddingValues = PaddingValues(16.dp), applicationName: String? = "Application", - applicationPackage: String? = "com.company.application", + applicationPackage: String? = "", applicationVersion: String? = "1.0.0", applicationIcon: ImageBitmap? = null, onCancel: () -> Unit = {}, @@ -49,32 +116,11 @@ fun ApplicationInfoCard( verticalArrangement = Arrangement.spacedBy(8.dp), ) { FlowRow( - modifier = Modifier.padding(16.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp) + modifier = Modifier.fillMaxWidth().padding(8.dp), + horizontalArrangement = Arrangement.SpaceBetween ) { - if (applicationIcon == null) { - Icon( - Icons.Default.Widgets, - contentDescription = null, - modifier = Modifier.size(48.dp) - ) - } else { - Image( - applicationIcon, - contentDescription = null, - modifier = Modifier.size(48.dp).clip(RoundedCornerShape(8.dp)) - ) - } - Column( - modifier = Modifier.height(48.dp), - verticalArrangement = Arrangement.Center - ) { - Text("$applicationName ($applicationVersion)") - Text( - applicationPackage ?: stringResource(Res.string.unknown_package), - color = MaterialTheme.colorScheme.secondary - ) - } + ApplicationInfo(applicationName, applicationPackage, applicationVersion, applicationIcon) + ApplicationStatus(applicationName, applicationPackage) } val patches = remember { mutableStateListOf(*PatchStore.PATCHES.toTypedArray()) } diff --git a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/StartingCard.kt b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/StartingCard.kt index 77162f7..28c32c9 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/StartingCard.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/StartingCard.kt @@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AddCircle import androidx.compose.material.icons.filled.Bolt import androidx.compose.material.icons.filled.Download import androidx.compose.material3.Card diff --git a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/theme/Theme.kt b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/theme/Theme.kt index 5767524..33a4d51 100644 --- a/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/theme/Theme.kt +++ b/composeApp/src/commonMain/kotlin/moe/crx/ovrport/ui/theme/Theme.kt @@ -5,6 +5,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color private val DarkColorScheme = darkColorScheme( primary = Purple80, @@ -16,16 +17,6 @@ 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