mirror of
https://github.com/ovrport/app.git
synced 2026-10-27 19:29:57 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b081964d5 | ||
|
|
47a2f5815d | ||
|
|
0aad2646fe | ||
|
|
3fbd6b61bd | ||
|
|
479deff61a | ||
|
|
3c6b910233 | ||
|
|
c4fc748024 |
No files matched your search
@@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -8,4 +8,9 @@
|
||||
<string name="cant_update_overport">overport libraries cannot be downloaded.</string>
|
||||
<string name="unknown_error">Unknown error has been occurred.</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>
|
||||
@@ -11,4 +11,4 @@ class AndroidPlatform(override val name: String) : Platform
|
||||
expect fun getPlatform(): Platform
|
||||
|
||||
// TODO This should be controlled by Gradle.
|
||||
fun getCurrentVersion(): String = "1.0.0"
|
||||
fun getCurrentVersion(): String = "1.1.0"
|
||||
@@ -7,25 +7,25 @@ import kotlinx.serialization.json.Json
|
||||
@Serializable
|
||||
data class FrdaAsset(
|
||||
@SerialName("ID")
|
||||
val id: Long,
|
||||
val path: String,
|
||||
val id: Long = 0,
|
||||
val path: String = "",
|
||||
val type: String = "store"
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FrdaParameters(
|
||||
val assets: List<FrdaAsset>,
|
||||
val skus: List<String>
|
||||
val assets: List<FrdaAsset> = listOf(),
|
||||
val skus: List<String> = listOf()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FrdaInteraction(
|
||||
val parameters: FrdaParameters
|
||||
val parameters: FrdaParameters = FrdaParameters(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FrdaConfig(
|
||||
val interaction: FrdaInteraction
|
||||
val interaction: FrdaInteraction = FrdaInteraction(),
|
||||
)
|
||||
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
@@ -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
|
||||
|
||||
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<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
|
||||
}
|
||||
@@ -14,8 +14,8 @@ class FileTransformer(val file: File) {
|
||||
file.writeText(Regex(input, RegexOption.DOT_MATCHES_ALL).replace(file.readText(), output))
|
||||
}
|
||||
|
||||
fun find(input: String): Boolean {
|
||||
return Regex(input, RegexOption.DOT_MATCHES_ALL).find(file.readText()) != null
|
||||
fun readText(): String {
|
||||
return file.readText()
|
||||
}
|
||||
|
||||
fun append(line: String) {
|
||||
@@ -158,7 +158,10 @@ class PatchExecutor(private val workingDir: File) {
|
||||
it ?: workingDir.resolve("smali/classes").apply { mkdirs() }
|
||||
}
|
||||
.resolve("${fileName}.smali")
|
||||
.apply { createNewFile() }
|
||||
.apply {
|
||||
parentFile?.mkdirs()
|
||||
createNewFile()
|
||||
}
|
||||
.run {
|
||||
if (exists()) {
|
||||
FileTransformer(this).block()
|
||||
|
||||
@@ -184,15 +184,22 @@ object PatchStore {
|
||||
takeNodesEach({ named("application") }) {
|
||||
takeNodesEach({ named("activity") }) {
|
||||
takeNodesEach({ named("intent-filter") }) {
|
||||
takeNodesEach({ named("category") }) {
|
||||
when (attributeName()) {
|
||||
"android.intent.category.INFO" -> null
|
||||
"android.intent.category.LAUNCHER" -> null
|
||||
else -> this
|
||||
}
|
||||
val hasIntent = elem<JSONArray>("nodes").elemEach<JSONObject> { named("category") }.map {
|
||||
when (it.attributeName()) {
|
||||
"com.oculus.intent.category.VR" -> true
|
||||
"android.intent.category.LAUNCHER" -> true
|
||||
else -> false
|
||||
}
|
||||
}.reduceOrNull { l, r -> l || r } ?: false
|
||||
|
||||
takeNodes {
|
||||
this?.put(createCategory("android.intent.category.LAUNCHER"))
|
||||
if (!hasIntent) this else {
|
||||
JSONArray()
|
||||
.put(createAction("android.intent.action.MAIN"))
|
||||
.put(createCategory("android.intent.category.LAUNCHER"))
|
||||
.put(createCategory("com.oculus.intent.category.VR"))
|
||||
.put(createCategory("com.yvr.intent.category.VR"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,7 +223,7 @@ object PatchStore {
|
||||
}
|
||||
PATCHES += Patch("Fix Unreal Engine 4 crash") {
|
||||
createSmali("com/unity3d/player/UnityPlayer") {
|
||||
if (!find(".*")) {
|
||||
if (readText().isBlank()) {
|
||||
append(".class public Lcom/unity3d/player/UnityPlayer;")
|
||||
append(".super Ljava/lang/Object;")
|
||||
append(".field public static currentActivity:Landroid/app/Activity;")
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package moe.crx.ovrport.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -11,7 +8,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
|
||||
@@ -22,16 +18,14 @@ 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.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
import moe.crx.ovrport.AndroidPlatform
|
||||
import moe.crx.ovrport.getCurrentVersion
|
||||
import moe.crx.ovrport.patch.Patch
|
||||
import moe.crx.ovrport.getPlatform
|
||||
import moe.crx.ovrport.model.GithubRelease
|
||||
import moe.crx.ovrport.patch.Constants
|
||||
import moe.crx.ovrport.patch.Patch
|
||||
import moe.crx.ovrport.ui.theme.OverportTheme
|
||||
import moe.crx.ovrport.utils.HttpUtil
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import overportapp.composeapp.generated.resources.Res
|
||||
@@ -79,16 +73,11 @@ fun AppContent(
|
||||
Constants.OVRPORT_VERSION,
|
||||
modifier = Modifier.padding(8.dp),
|
||||
fontSize = 18.sp,
|
||||
color = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
AnimatedVisibility(
|
||||
versionToUpdate?.name != null && versionToUpdate?.name != getCurrentVersion(),
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
FadeVisibility(versionToUpdate?.name != null && versionToUpdate?.name != getCurrentVersion()) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clip(
|
||||
RoundedCornerShape(16.dp)
|
||||
@@ -103,7 +92,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),
|
||||
)
|
||||
}
|
||||
@@ -118,21 +106,13 @@ fun AppContent(
|
||||
val inProgress = viewModel.working
|
||||
val isPatcherVisible = viewModel.isApkLoaded() && !inProgress
|
||||
|
||||
AnimatedVisibility(
|
||||
!isPatcherVisible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
FadeVisibility(!isPatcherVisible) {
|
||||
StartingCard(innerPadding, inProgress) {
|
||||
onOpen()
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
isPatcherVisible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
FadeVisibility(isPatcherVisible) {
|
||||
ApplicationInfoCard(
|
||||
innerPadding,
|
||||
viewModel.currentAppName(),
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package moe.crx.ovrport.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Widgets
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,22 @@
|
||||
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.material3.Button
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.unit.dp
|
||||
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 org.jetbrains.compose.resources.stringResource
|
||||
import overportapp.composeapp.generated.resources.Res
|
||||
import overportapp.composeapp.generated.resources.unknown_package
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
@@ -38,7 +24,7 @@ import overportapp.composeapp.generated.resources.unknown_package
|
||||
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 +35,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()) }
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package moe.crx.ovrport.ui
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
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.getCompatibilityStatus
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package moe.crx.ovrport.ui
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Bolt
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun BreathingIcon(modifier: Modifier, duration: Int = 1500) {
|
||||
var isFaded by remember { mutableStateOf(false) }
|
||||
|
||||
val alpha by animateFloatAsState(
|
||||
targetValue = if (isFaded) 0.2f else 1f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(durationMillis = duration, easing = LinearEasing),
|
||||
repeatMode = RepeatMode.Reverse
|
||||
),
|
||||
label = ""
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
while (true) {
|
||||
isFaded = !isFaded
|
||||
delay(duration.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
Icon(
|
||||
Icons.Default.Bolt,
|
||||
contentDescription = null,
|
||||
modifier = modifier.alpha(alpha)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package moe.crx.ovrport.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.AnimatedVisibilityScope
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
fun FadeVisibility(
|
||||
condition: Boolean,
|
||||
block: @Composable AnimatedVisibilityScope.() -> Unit
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
condition,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
content = block
|
||||
)
|
||||
}
|
||||
@@ -1,37 +1,14 @@
|
||||
package moe.crx.ovrport.ui
|
||||
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.*
|
||||
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
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.draw.rotate
|
||||
@@ -40,7 +17,6 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.PathEffect
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.delay
|
||||
import moe.crx.ovrport.AndroidPlatform
|
||||
import moe.crx.ovrport.getPlatform
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
@@ -126,30 +102,3 @@ fun StartingCard(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BreathingIcon(modifier: Modifier, duration: Int = 1500) {
|
||||
var isFaded by remember { mutableStateOf(false) }
|
||||
|
||||
val alpha by animateFloatAsState(
|
||||
targetValue = if (isFaded) 0.2f else 1f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(durationMillis = duration, easing = LinearEasing),
|
||||
repeatMode = RepeatMode.Reverse
|
||||
),
|
||||
label = ""
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
while (true) {
|
||||
isFaded = !isFaded
|
||||
delay(duration.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
Icon(
|
||||
Icons.Default.Bolt,
|
||||
contentDescription = null,
|
||||
modifier = modifier.alpha(alpha)
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -86,3 +86,22 @@ fun createCategory(value: String): JSONObject {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun createAction(value: String): JSONObject {
|
||||
return JSONObject()
|
||||
.put("node_type", "element")
|
||||
.put("name", "action")
|
||||
.put(
|
||||
"attributes",
|
||||
JSONArray()
|
||||
.put(
|
||||
JSONObject()
|
||||
.put("name", "name")
|
||||
.put("id", 16842755)
|
||||
.put("uri", "http://schemas.android.com/apk/res/android")
|
||||
.put("prefix", "android")
|
||||
.put("value_type", "STRING")
|
||||
.put("data", value)
|
||||
)
|
||||
)
|
||||
}
|
||||
+1
-1
@@ -10,4 +10,4 @@ android.nonTransitiveRClass=true
|
||||
android.useAndroidX=true
|
||||
|
||||
#Application
|
||||
appVersion=1.0.0
|
||||
appVersion=1.1.0
|
||||
Reference in new issue
Block a user