mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-06-18 05:23:12 +10:00
Code Review for UI Rewrite
This commit is contained in:
@@ -57,7 +57,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
if [[ "$tidal_src" == *.apkm ]]; then
|
if [[ "$tidal_src" == *.apkm ]]; then
|
||||||
echo "Merging splits from $tidal_src via APKEditor"
|
echo "Merging splits from $tidal_src via APKEditor"
|
||||||
curl -sLo /tmp/APKEditor.jar https://github.com/REAndroid/APKEditor/releases/download/V1.4.3/APKEditor-1.4.3.jar
|
curl --fail --location --retry 3 --retry-delay 2 -sSo /tmp/APKEditor.jar \
|
||||||
|
https://github.com/REAndroid/APKEditor/releases/download/V1.4.3/APKEditor-1.4.3.jar
|
||||||
java -jar /tmp/APKEditor.jar m -i "$tidal_src" -o ./dist/tidal-stock.apk
|
java -jar /tmp/APKEditor.jar m -i "$tidal_src" -o ./dist/tidal-stock.apk
|
||||||
echo "Merged tidal-stock.apk:"
|
echo "Merged tidal-stock.apk:"
|
||||||
ls -la ./dist/tidal-stock.apk
|
ls -la ./dist/tidal-stock.apk
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.meowarex.rlmobile.ui.screens.settings.SettingsModel
|
|||||||
import com.meowarex.rlmobile.ui.widgets.updater.UpdaterViewModel
|
import com.meowarex.rlmobile.ui.widgets.updater.UpdaterViewModel
|
||||||
import com.meowarex.rlmobile.updatechecker.UpdateCheckWorker
|
import com.meowarex.rlmobile.updatechecker.UpdateCheckWorker
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import org.koin.android.ext.android.get
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import org.koin.core.module.dsl.*
|
import org.koin.core.module.dsl.*
|
||||||
@@ -101,7 +102,12 @@ class ManagerApplication : Application() {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule periodic update check
|
// Schedule periodic update check only when the user has opted in,
|
||||||
UpdateCheckWorker.schedule(this)
|
// so the disabled state survives app restarts instead of being re-enqueued.
|
||||||
|
if (get<PreferencesManager>().autoUpdateCheck) {
|
||||||
|
UpdateCheckWorker.schedule(this)
|
||||||
|
} else {
|
||||||
|
UpdateCheckWorker.cancel(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -10,7 +10,10 @@ class CommitsPagingSource(
|
|||||||
) : PagingSource<Int, GithubCommit>() {
|
) : PagingSource<Int, GithubCommit>() {
|
||||||
|
|
||||||
override fun getRefreshKey(state: PagingState<Int, GithubCommit>): Int? =
|
override fun getRefreshKey(state: PagingState<Int, GithubCommit>): Int? =
|
||||||
state.anchorPosition?.let { state.closestPageToPosition(it)?.prevKey }
|
state.anchorPosition?.let {
|
||||||
|
val page = state.closestPageToPosition(it) ?: return null
|
||||||
|
page.prevKey?.plus(1) ?: page.nextKey?.minus(1)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, GithubCommit> {
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, GithubCommit> {
|
||||||
val page = params.key ?: 0
|
val page = params.key ?: 0
|
||||||
|
|||||||
+10
-2
@@ -51,8 +51,16 @@ class SmaliPatchStep : Step(), IDexProvider, KoinComponent {
|
|||||||
if (patchFile.endsWith(".smali") && patchFile.startsWith("extension/")) {
|
if (patchFile.endsWith(".smali") && patchFile.startsWith("extension/")) {
|
||||||
val relative = patchFile.removePrefix("extension/")
|
val relative = patchFile.removePrefix("extension/")
|
||||||
val out = smaliDir.resolve(relative)
|
val out = smaliDir.resolve(relative)
|
||||||
out.parentFile?.mkdirs()
|
// Guard against zip-slip: a crafted entry could otherwise escape smaliDir.
|
||||||
out.writeBytes(zip.openEntry(patchFile)!!.read())
|
val baseCanonical = smaliDir.canonicalPath + File.separator
|
||||||
|
val outCanonical = out.canonicalPath
|
||||||
|
if (!outCanonical.startsWith(baseCanonical)) {
|
||||||
|
throw SecurityException("Zip entry escapes target directory: $patchFile")
|
||||||
|
}
|
||||||
|
val entry = zip.openEntry(patchFile)
|
||||||
|
?: throw FileNotFoundException("Missing zip entry: $patchFile")
|
||||||
|
out.canonicalFile.parentFile?.mkdirs()
|
||||||
|
out.writeBytes(entry.read())
|
||||||
container.log("Extracted extension smali: $relative")
|
container.log("Extracted extension smali: $relative")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ object ManifestPatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
val origPkg = originalPackage ?: "com.aspiro.tidal"
|
val origPkg = originalPackage
|
||||||
|
?: throw IllegalStateException("Manifest has no package attribute; refusing to rewrite authorities/permissions without a known originalPackage")
|
||||||
|
|
||||||
val reader = AxmlReader(manifestBytes)
|
val reader = AxmlReader(manifestBytes)
|
||||||
val writer = AxmlWriter()
|
val writer = AxmlWriter()
|
||||||
@@ -104,7 +105,7 @@ object ManifestPatcher {
|
|||||||
super.attr(
|
super.attr(
|
||||||
ns, name, resourceId, type,
|
ns, name, resourceId, type,
|
||||||
when (name) {
|
when (name) {
|
||||||
"name" -> (value as String).replace(origPkg, packageName)
|
"name" -> (value as? String)?.replace(origPkg, packageName) ?: value
|
||||||
else -> value
|
else -> value
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -155,7 +156,7 @@ object ManifestPatcher {
|
|||||||
super.attr(
|
super.attr(
|
||||||
ns, name, resourceId, type,
|
ns, name, resourceId, type,
|
||||||
if (name == "authorities") {
|
if (name == "authorities") {
|
||||||
(value as String).replace(origPkg, packageName)
|
(value as? String)?.replace(origPkg, packageName) ?: value
|
||||||
} else {
|
} else {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ class HomeModel(
|
|||||||
fun openApp(packageName: String) {
|
fun openApp(packageName: String) {
|
||||||
val launchIntent = application.packageManager.getLaunchIntentForPackage(packageName)
|
val launchIntent = application.packageManager.getLaunchIntentForPackage(packageName)
|
||||||
if (launchIntent != null) {
|
if (launchIntent != null) {
|
||||||
|
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
application.startActivity(launchIntent)
|
application.startActivity(launchIntent)
|
||||||
} else {
|
} else {
|
||||||
application.showToast(R.string.launch_app_fail)
|
application.showToast(R.string.launch_app_fail)
|
||||||
|
|||||||
@@ -12,10 +12,15 @@ import androidx.compose.runtime.*
|
|||||||
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.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
@@ -55,16 +60,28 @@ class HomeScreen : Screen, Parcelable {
|
|||||||
title = { Text(stringResource(R.string.navigation_home)) },
|
title = { Text(stringResource(R.string.navigation_home)) },
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(onClick = { model.refresh() }) {
|
IconButton(onClick = { model.refresh() }) {
|
||||||
Icon(painterResource(R.drawable.ic_refresh), contentDescription = null)
|
Icon(
|
||||||
|
painterResource(R.drawable.ic_refresh),
|
||||||
|
contentDescription = stringResource(R.string.navigation_refresh),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
IconButton(onClick = { navigator.push(AboutScreen()) }) {
|
IconButton(onClick = { navigator.push(AboutScreen()) }) {
|
||||||
Icon(painterResource(R.drawable.ic_info), contentDescription = null)
|
Icon(
|
||||||
|
painterResource(R.drawable.ic_info),
|
||||||
|
contentDescription = stringResource(R.string.navigation_about),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
IconButton(onClick = { navigator.push(LogsListScreen()) }) {
|
IconButton(onClick = { navigator.push(LogsListScreen()) }) {
|
||||||
Icon(painterResource(R.drawable.ic_receipt), contentDescription = null)
|
Icon(
|
||||||
|
painterResource(R.drawable.ic_receipt),
|
||||||
|
contentDescription = stringResource(R.string.navigation_logs),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
IconButton(onClick = { navigator.push(SettingsScreen()) }) {
|
IconButton(onClick = { navigator.push(SettingsScreen()) }) {
|
||||||
Icon(painterResource(R.drawable.ic_settings), contentDescription = null)
|
Icon(
|
||||||
|
painterResource(R.drawable.ic_settings),
|
||||||
|
contentDescription = stringResource(R.string.navigation_settings),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -117,17 +134,19 @@ private fun ColumnScope.HomeContent(
|
|||||||
val currentVersionName = install?.version?.let { "v${it.toString()}" }
|
val currentVersionName = install?.version?.let { "v${it.toString()}" }
|
||||||
val latestVersionName = state.latestTidalVersionCode?.let { "build $it" }
|
val latestVersionName = state.latestTidalVersionCode?.let { "build $it" }
|
||||||
|
|
||||||
if (install?.icon != null) {
|
val fallbackPainter = if (install?.icon == null) {
|
||||||
|
// R.mipmap.ic_launcher is an adaptive-icon XML on API 26+, which painterResource cannot decode.
|
||||||
|
val context = LocalContext.current
|
||||||
|
remember {
|
||||||
|
val drawable = ContextCompat.getDrawable(context, R.mipmap.ic_launcher)
|
||||||
|
drawable?.toBitmap()?.asImageBitmap()?.let(::BitmapPainter)
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
|
||||||
|
val iconPainter = install?.icon ?: fallbackPainter
|
||||||
|
if (iconPainter != null) {
|
||||||
Image(
|
Image(
|
||||||
painter = install.icon,
|
painter = iconPainter,
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(60.dp)
|
|
||||||
.clip(CircleShape),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(R.mipmap.ic_launcher),
|
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(60.dp)
|
.size(60.dp)
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ data class PatchOptions(
|
|||||||
companion object {
|
companion object {
|
||||||
val Default = PatchOptions(
|
val Default = PatchOptions(
|
||||||
appName = "TIDAL",
|
appName = "TIDAL",
|
||||||
packageName = "com.tidal.music",
|
packageName = "com.aspiro.tidal",
|
||||||
debuggable = false,
|
debuggable = false,
|
||||||
customInjector = null,
|
customInjector = null,
|
||||||
customPatches = null,
|
customPatches = null,
|
||||||
|
|||||||
+2
-2
@@ -7,6 +7,7 @@ import android.app.PendingIntent
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
@@ -14,7 +15,6 @@ import androidx.core.app.NotificationCompat
|
|||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import com.meowarex.rlmobile.BuildConfig
|
import com.meowarex.rlmobile.BuildConfig
|
||||||
import com.meowarex.rlmobile.MainActivity
|
|
||||||
import com.meowarex.rlmobile.R
|
import com.meowarex.rlmobile.R
|
||||||
import com.meowarex.rlmobile.manager.PreferencesManager
|
import com.meowarex.rlmobile.manager.PreferencesManager
|
||||||
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
|
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
|
||||||
@@ -81,7 +81,7 @@ class UpdateCheckWorker(
|
|||||||
val pendingIntent = PendingIntent.getActivity(
|
val pendingIntent = PendingIntent.getActivity(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
0,
|
0,
|
||||||
Intent(applicationContext, MainActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) },
|
Intent(Intent.ACTION_VIEW, Uri.parse(releaseUrl)).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) },
|
||||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,7 @@
|
|||||||
<string name="navigation_about">About</string>
|
<string name="navigation_about">About</string>
|
||||||
<string name="navigation_logs">Logs</string>
|
<string name="navigation_logs">Logs</string>
|
||||||
<string name="navigation_settings">Settings</string>
|
<string name="navigation_settings">Settings</string>
|
||||||
|
<string name="navigation_refresh">Refresh</string>
|
||||||
|
|
||||||
<string name="contributors_lead">Lead</string>
|
<string name="contributors_lead">Lead</string>
|
||||||
<string name="contributors">Contributors</string>
|
<string name="contributors">Contributors</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user