mirror of
https://github.com/ovrport/app.git
synced 2026-10-28 03:40:01 -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">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -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>
|
||||||
@@ -11,4 +11,4 @@ class AndroidPlatform(override val name: String) : Platform
|
|||||||
expect fun getPlatform(): Platform
|
expect fun getPlatform(): Platform
|
||||||
|
|
||||||
// TODO This should be controlled by Gradle.
|
// 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
|
@Serializable
|
||||||
data class FrdaAsset(
|
data class FrdaAsset(
|
||||||
@SerialName("ID")
|
@SerialName("ID")
|
||||||
val id: Long,
|
val id: Long = 0,
|
||||||
val path: String,
|
val path: String = "",
|
||||||
val type: String = "store"
|
val type: String = "store"
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FrdaParameters(
|
data class FrdaParameters(
|
||||||
val assets: List<FrdaAsset>,
|
val assets: List<FrdaAsset> = listOf(),
|
||||||
val skus: List<String>
|
val skus: List<String> = listOf()
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FrdaInteraction(
|
data class FrdaInteraction(
|
||||||
val parameters: FrdaParameters
|
val parameters: FrdaParameters = FrdaParameters(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class FrdaConfig(
|
data class FrdaConfig(
|
||||||
val interaction: FrdaInteraction
|
val interaction: FrdaInteraction = FrdaInteraction(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val json = Json { ignoreUnknownKeys = true }
|
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
|
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
|
||||||
}
|
}
|
||||||
@@ -14,8 +14,8 @@ class FileTransformer(val file: File) {
|
|||||||
file.writeText(Regex(input, RegexOption.DOT_MATCHES_ALL).replace(file.readText(), output))
|
file.writeText(Regex(input, RegexOption.DOT_MATCHES_ALL).replace(file.readText(), output))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun find(input: String): Boolean {
|
fun readText(): String {
|
||||||
return Regex(input, RegexOption.DOT_MATCHES_ALL).find(file.readText()) != null
|
return file.readText()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun append(line: String) {
|
fun append(line: String) {
|
||||||
@@ -158,7 +158,10 @@ class PatchExecutor(private val workingDir: File) {
|
|||||||
it ?: workingDir.resolve("smali/classes").apply { mkdirs() }
|
it ?: workingDir.resolve("smali/classes").apply { mkdirs() }
|
||||||
}
|
}
|
||||||
.resolve("${fileName}.smali")
|
.resolve("${fileName}.smali")
|
||||||
.apply { createNewFile() }
|
.apply {
|
||||||
|
parentFile?.mkdirs()
|
||||||
|
createNewFile()
|
||||||
|
}
|
||||||
.run {
|
.run {
|
||||||
if (exists()) {
|
if (exists()) {
|
||||||
FileTransformer(this).block()
|
FileTransformer(this).block()
|
||||||
|
|||||||
@@ -184,15 +184,22 @@ object PatchStore {
|
|||||||
takeNodesEach({ named("application") }) {
|
takeNodesEach({ named("application") }) {
|
||||||
takeNodesEach({ named("activity") }) {
|
takeNodesEach({ named("activity") }) {
|
||||||
takeNodesEach({ named("intent-filter") }) {
|
takeNodesEach({ named("intent-filter") }) {
|
||||||
takeNodesEach({ named("category") }) {
|
val hasIntent = elem<JSONArray>("nodes").elemEach<JSONObject> { named("category") }.map {
|
||||||
when (attributeName()) {
|
when (it.attributeName()) {
|
||||||
"android.intent.category.INFO" -> null
|
"com.oculus.intent.category.VR" -> true
|
||||||
"android.intent.category.LAUNCHER" -> null
|
"android.intent.category.LAUNCHER" -> true
|
||||||
else -> this
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}.reduceOrNull { l, r -> l || r } ?: false
|
||||||
|
|
||||||
takeNodes {
|
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") {
|
PATCHES += Patch("Fix Unreal Engine 4 crash") {
|
||||||
createSmali("com/unity3d/player/UnityPlayer") {
|
createSmali("com/unity3d/player/UnityPlayer") {
|
||||||
if (!find(".*")) {
|
if (readText().isBlank()) {
|
||||||
append(".class public Lcom/unity3d/player/UnityPlayer;")
|
append(".class public Lcom/unity3d/player/UnityPlayer;")
|
||||||
append(".super Ljava/lang/Object;")
|
append(".super Ljava/lang/Object;")
|
||||||
append(".field public static currentActivity:Landroid/app/Activity;")
|
append(".field public static currentActivity:Landroid/app/Activity;")
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package moe.crx.ovrport.ui
|
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.clickable
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
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.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
|
||||||
@@ -22,16 +18,14 @@ 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 androidx.compose.ui.unit.sp
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import moe.crx.ovrport.AndroidPlatform
|
import moe.crx.ovrport.AndroidPlatform
|
||||||
import moe.crx.ovrport.getCurrentVersion
|
import moe.crx.ovrport.getCurrentVersion
|
||||||
import moe.crx.ovrport.patch.Patch
|
|
||||||
import moe.crx.ovrport.getPlatform
|
import moe.crx.ovrport.getPlatform
|
||||||
import moe.crx.ovrport.model.GithubRelease
|
import moe.crx.ovrport.model.GithubRelease
|
||||||
import moe.crx.ovrport.patch.Constants
|
import moe.crx.ovrport.patch.Constants
|
||||||
|
import moe.crx.ovrport.patch.Patch
|
||||||
import moe.crx.ovrport.ui.theme.OverportTheme
|
import moe.crx.ovrport.ui.theme.OverportTheme
|
||||||
import moe.crx.ovrport.utils.HttpUtil
|
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
import org.jetbrains.compose.resources.stringResource
|
import org.jetbrains.compose.resources.stringResource
|
||||||
import overportapp.composeapp.generated.resources.Res
|
import overportapp.composeapp.generated.resources.Res
|
||||||
@@ -79,16 +73,11 @@ 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
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
AnimatedVisibility(
|
FadeVisibility(versionToUpdate?.name != null && versionToUpdate?.name != getCurrentVersion()) {
|
||||||
versionToUpdate?.name != null && versionToUpdate?.name != getCurrentVersion(),
|
|
||||||
enter = fadeIn(),
|
|
||||||
exit = fadeOut()
|
|
||||||
) {
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clip(
|
verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clip(
|
||||||
RoundedCornerShape(16.dp)
|
RoundedCornerShape(16.dp)
|
||||||
@@ -103,7 +92,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),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -118,21 +106,13 @@ fun AppContent(
|
|||||||
val inProgress = viewModel.working
|
val inProgress = viewModel.working
|
||||||
val isPatcherVisible = viewModel.isApkLoaded() && !inProgress
|
val isPatcherVisible = viewModel.isApkLoaded() && !inProgress
|
||||||
|
|
||||||
AnimatedVisibility(
|
FadeVisibility(!isPatcherVisible) {
|
||||||
!isPatcherVisible,
|
|
||||||
enter = fadeIn(),
|
|
||||||
exit = fadeOut()
|
|
||||||
) {
|
|
||||||
StartingCard(innerPadding, inProgress) {
|
StartingCard(innerPadding, inProgress) {
|
||||||
onOpen()
|
onOpen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility(
|
FadeVisibility(isPatcherVisible) {
|
||||||
isPatcherVisible,
|
|
||||||
enter = fadeIn(),
|
|
||||||
exit = fadeOut()
|
|
||||||
) {
|
|
||||||
ApplicationInfoCard(
|
ApplicationInfoCard(
|
||||||
innerPadding,
|
innerPadding,
|
||||||
viewModel.currentAppName(),
|
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
|
package moe.crx.ovrport.ui
|
||||||
|
|
||||||
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.material3.Button
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
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 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)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -38,7 +24,7 @@ import overportapp.composeapp.generated.resources.unknown_package
|
|||||||
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 +35,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()) }
|
||||||
|
|||||||
@@ -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
|
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.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.*
|
||||||
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.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.Download
|
import androidx.compose.material.icons.filled.Download
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.LocalContentColor
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
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.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.drawBehind
|
import androidx.compose.ui.draw.drawBehind
|
||||||
import androidx.compose.ui.draw.rotate
|
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.PathEffect
|
||||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import moe.crx.ovrport.AndroidPlatform
|
import moe.crx.ovrport.AndroidPlatform
|
||||||
import moe.crx.ovrport.getPlatform
|
import moe.crx.ovrport.getPlatform
|
||||||
import org.jetbrains.compose.resources.stringResource
|
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.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
|
||||||
|
|||||||
@@ -85,4 +85,23 @@ fun createCategory(value: String): JSONObject {
|
|||||||
.put("data", value)
|
.put("data", value)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
android.useAndroidX=true
|
||||||
|
|
||||||
#Application
|
#Application
|
||||||
appVersion=1.0.0
|
appVersion=1.1.0
|
||||||
Reference in new issue
Block a user