mirror of
https://github.com/ovrport/app.git
synced 2026-10-27 03:19:56 -04:00
feat: Compatibility status
This commit is contained in:
1 parent
479deff61a
commit
3fbd6b61bd
7 files changed
+247
-56
No files matched your search
@@ -8,4 +8,9 @@
|
|||||||
<string name="cant_update_overport">overport libraries cannot be downloaded.</string>
|
<string name="cant_update_overport">overport libraries cannot be downloaded.</string>
|
||||||
<string name="unknown_error">Unknown error has been occurred.</string>
|
<string name="unknown_error">Unknown error has been occurred.</string>
|
||||||
<string name="update_available">Update available!</string>
|
<string name="update_available">Update available!</string>
|
||||||
|
<string name="status_unknown">Unknown</string>
|
||||||
|
<string name="status_playable">Playable</string>
|
||||||
|
<string name="status_has_problems">Has problems</string>
|
||||||
|
<string name="status_not_working">Not working</string>
|
||||||
|
<string name="status_loading">Loading…</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -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<GithubIssueLabel>
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GithubIssues(
|
||||||
|
@SerialName("items")
|
||||||
|
val items: List<GithubIssue>
|
||||||
|
)
|
||||||
@@ -1,9 +1,130 @@
|
|||||||
package moe.crx.ovrport.patch
|
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 {
|
object Constants {
|
||||||
const val APK_MIMETYPE = "application/vnd.android.package-archive"
|
const val APK_MIMETYPE = "application/vnd.android.package-archive"
|
||||||
const val OVRPORT_VERSION = "3.2.2"
|
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_URL = "https://files.crx.moe/ovrport/tracker/releases/download/$OVRPORT_VERSION/libraries.zip"
|
||||||
const val LIBRARIES_DIR = "libraries/$OVRPORT_VERSION"
|
const val LIBRARIES_DIR = "libraries/$OVRPORT_VERSION"
|
||||||
const val RELEASES_URL = "https://api.github.com/repos/ovrport/app/releases"
|
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<GithubIssues>(
|
||||||
|
String(
|
||||||
|
download(
|
||||||
|
Constants.ISSUES_URL.format(
|
||||||
|
URLEncoder.encode(
|
||||||
|
pkg,
|
||||||
|
Charsets.UTF_8
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.items.firstOrNull { it.title.contains(pkg) || it.title.contains(name) }
|
||||||
|
?: json
|
||||||
|
.decodeFromString<GithubIssues>(
|
||||||
|
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
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.CloudDownload
|
import androidx.compose.material.icons.filled.CloudDownload
|
||||||
import androidx.compose.material.icons.filled.Download
|
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -79,7 +78,6 @@ fun AppContent(
|
|||||||
Constants.OVRPORT_VERSION,
|
Constants.OVRPORT_VERSION,
|
||||||
modifier = Modifier.padding(8.dp),
|
modifier = Modifier.padding(8.dp),
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
color = MaterialTheme.colorScheme.secondary
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -103,7 +101,6 @@ fun AppContent(
|
|||||||
Text(
|
Text(
|
||||||
stringResource(Res.string.update_available),
|
stringResource(Res.string.update_available),
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
|
||||||
modifier = Modifier.padding(0.dp, 8.dp, 8.dp, 8.dp),
|
modifier = Modifier.padding(0.dp, 8.dp, 8.dp, 8.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,43 +2,110 @@ package moe.crx.ovrport.ui
|
|||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.*
|
||||||
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.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.material.icons.filled.Widgets
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.unit.dp
|
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.Patch
|
||||||
import moe.crx.ovrport.patch.PatchStore
|
import moe.crx.ovrport.patch.PatchStore
|
||||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
import moe.crx.ovrport.patch.getCompatibilityStatus
|
||||||
import androidx.compose.material.icons.filled.Widgets
|
|
||||||
import androidx.compose.material3.*
|
|
||||||
import org.jetbrains.compose.resources.stringResource
|
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.Res
|
||||||
import overportapp.composeapp.generated.resources.unknown_package
|
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)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun ApplicationInfoCard(
|
fun ApplicationInfoCard(
|
||||||
innerPadding: PaddingValues = PaddingValues(16.dp),
|
innerPadding: PaddingValues = PaddingValues(16.dp),
|
||||||
applicationName: String? = "Application",
|
applicationName: String? = "Application",
|
||||||
applicationPackage: String? = "com.company.application",
|
applicationPackage: String? = "",
|
||||||
applicationVersion: String? = "1.0.0",
|
applicationVersion: String? = "1.0.0",
|
||||||
applicationIcon: ImageBitmap? = null,
|
applicationIcon: ImageBitmap? = null,
|
||||||
onCancel: () -> Unit = {},
|
onCancel: () -> Unit = {},
|
||||||
@@ -49,32 +116,11 @@ fun ApplicationInfoCard(
|
|||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.fillMaxWidth().padding(8.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
if (applicationIcon == null) {
|
ApplicationInfo(applicationName, applicationPackage, applicationVersion, applicationIcon)
|
||||||
Icon(
|
ApplicationStatus(applicationName, applicationPackage)
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val patches = remember { mutableStateListOf(*PatchStore.PATCHES.toTypedArray()) }
|
val patches = remember { mutableStateListOf(*PatchStore.PATCHES.toTypedArray()) }
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
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.Bolt
|
||||||
import androidx.compose.material.icons.filled.Download
|
import androidx.compose.material.icons.filled.Download
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.darkColorScheme
|
import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
private val DarkColorScheme = darkColorScheme(
|
private val DarkColorScheme = darkColorScheme(
|
||||||
primary = Purple80,
|
primary = Purple80,
|
||||||
@@ -16,16 +17,6 @@ private val LightColorScheme = lightColorScheme(
|
|||||||
primary = Purple40,
|
primary = Purple40,
|
||||||
secondary = PurpleGrey40,
|
secondary = PurpleGrey40,
|
||||||
tertiary = Pink40,
|
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
|
@Composable
|
||||||
|
|||||||
Reference in new issue
Block a user