From 6a593021048bb158c5795074f5a43ecd9ad5a812 Mon Sep 17 00:00:00 2001 From: KeatonTheBot Date: Fri, 23 Jan 2026 19:43:56 -0600 Subject: [PATCH] Kotlin: Code cleanup --- .../app/src/main/AndroidManifest.xml | 6 +-- .../org/kenjinx/android/GameController2.kt | 7 ++- .../org/kenjinx/android/GameController3.kt | 7 ++- .../org/kenjinx/android/GameController4.kt | 7 ++- .../org/kenjinx/android/GameController5.kt | 7 ++- .../org/kenjinx/android/GameController6.kt | 1 - .../main/java/org/kenjinx/android/GameHost.kt | 11 ++--- .../java/org/kenjinx/android/KenjinxNative.kt | 7 ++- .../java/org/kenjinx/android/MainActivity.kt | 49 +++++++++---------- .../org/kenjinx/android/PerformanceManager.kt | 4 ++ .../org/kenjinx/android/ShortcutHelper.kt | 4 +- .../java/org/kenjinx/android/ShortcutUtils.kt | 7 ++- .../kenjinx/android/ShortcutWizardActivity.kt | 2 + .../java/org/kenjinx/android/UiHandler.kt | 4 +- .../org/kenjinx/android/cheats/CheatFs.kt | 2 +- .../org/kenjinx/android/cheats/CheatPrefs.kt | 3 +- .../java/org/kenjinx/android/cheats/ModFs.kt | 2 - .../java/org/kenjinx/android/saves/SaveFs.kt | 2 +- .../android/service/EmulationService.kt | 25 ++++------ .../android/viewmodels/DlcViewModel.kt | 4 +- .../kenjinx/android/viewmodels/GameModel.kt | 24 ++++----- .../android/viewmodels/MainViewModel.kt | 6 ++- .../viewmodels/TitleUpdateViewModel.kt | 6 +-- .../org/kenjinx/android/views/GameViews.kt | 24 ++++----- .../org/kenjinx/android/views/HomeViews.kt | 36 +++++++------- 25 files changed, 120 insertions(+), 137 deletions(-) diff --git a/src/KenjinxAndroid/app/src/main/AndroidManifest.xml b/src/KenjinxAndroid/app/src/main/AndroidManifest.xml index 1bf2ee21c..597fb8baa 100644 --- a/src/KenjinxAndroid/app/src/main/AndroidManifest.xml +++ b/src/KenjinxAndroid/app/src/main/AndroidManifest.xml @@ -2,12 +2,12 @@ - + - + - + diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController2.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController2.kt index 191d6b696..c65ef0d2d 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController2.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController2.kt @@ -18,7 +18,6 @@ import com.swordfish.radialgamepad.library.config.ButtonConfig import com.swordfish.radialgamepad.library.config.CrossConfig import com.swordfish.radialgamepad.library.config.CrossContentDescription import com.swordfish.radialgamepad.library.config.PrimaryDialConfig -import com.swordfish.radialgamepad.library.config.RadialGamePadConfig import com.swordfish.radialgamepad.library.config.SecondaryDialConfig import com.swordfish.radialgamepad.library.event.Event import kotlinx.coroutines.flow.merge @@ -189,12 +188,12 @@ class GameController2(var activity: Activity) : IGameController { } // --- Local copy of the config generation (name = generateConfig2, to avoid conflicts) --- -private fun generateConfig2(isLeft: Boolean, useSwitchLayout: Boolean): RadialGamePadConfig { +private fun generateConfig2(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig { val distance = 0.3f val buttonScale = 1f if (isLeft) { - return RadialGamePadConfig( + return GamePadConfig( /* ringSegments = */ 12, /* Primary (Stick) */ // IMPORTANT: pressButtonId -> DUMMY_LEFT_STICK_PRESS_ID, so that double tap does not trigger L3 @@ -300,7 +299,7 @@ private fun generateConfig2(isLeft: Boolean, useSwitchLayout: Boolean): RadialGa ) ) } else { - return RadialGamePadConfig( + return GamePadConfig( /* ringSegments = */ 12, /* Primary (ABXY) */ PrimaryDialConfig.PrimaryButtons( diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController3.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController3.kt index 86987848d..51bb8031d 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController3.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController3.kt @@ -18,7 +18,6 @@ import com.swordfish.radialgamepad.library.config.ButtonConfig import com.swordfish.radialgamepad.library.config.CrossConfig import com.swordfish.radialgamepad.library.config.CrossContentDescription import com.swordfish.radialgamepad.library.config.PrimaryDialConfig -import com.swordfish.radialgamepad.library.config.RadialGamePadConfig import com.swordfish.radialgamepad.library.config.SecondaryDialConfig import com.swordfish.radialgamepad.library.event.Event import kotlinx.coroutines.flow.merge @@ -182,12 +181,12 @@ class GameController3(var activity: Activity) : IGameController { } } -private fun generateConfig3(isLeft: Boolean, useSwitchLayout: Boolean): RadialGamePadConfig { +private fun generateConfig3(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig { val distance = 0.3f val buttonScale = 1f if (isLeft) { - return RadialGamePadConfig( + return GamePadConfig( /* ringSegments = */ 12, /* Primary (D-Pad) */ PrimaryDialConfig.Cross( @@ -293,7 +292,7 @@ private fun generateConfig3(isLeft: Boolean, useSwitchLayout: Boolean): RadialGa ) ) } else { - return RadialGamePadConfig( + return GamePadConfig( /* ringSegments = */ 12, /* Primary (ABXY) */ PrimaryDialConfig.PrimaryButtons( diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController4.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController4.kt index 5367214c3..68dd54fda 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController4.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController4.kt @@ -18,7 +18,6 @@ import com.swordfish.radialgamepad.library.config.ButtonConfig import com.swordfish.radialgamepad.library.config.CrossConfig import com.swordfish.radialgamepad.library.config.CrossContentDescription import com.swordfish.radialgamepad.library.config.PrimaryDialConfig -import com.swordfish.radialgamepad.library.config.RadialGamePadConfig import com.swordfish.radialgamepad.library.config.SecondaryDialConfig import com.swordfish.radialgamepad.library.event.Event import kotlinx.coroutines.flow.merge @@ -178,12 +177,12 @@ class GameController4(var activity: Activity) : IGameController { } } -private fun generateConfig4(isLeft: Boolean, useSwitchLayout: Boolean): RadialGamePadConfig { +private fun generateConfig4(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig { val distance = 0.3f val buttonScale = 1f if (isLeft) { - return RadialGamePadConfig( + return GamePadConfig( /* ringSegments = */ 12, /* Primary (Stick) */ // IMPORTANT: pressButtonId -> DUMMY_LEFT_STICK_PRESS_ID, so that double tap does not trigger L3 @@ -271,7 +270,7 @@ private fun generateConfig4(isLeft: Boolean, useSwitchLayout: Boolean): RadialGa ) ) } else { - return RadialGamePadConfig( + return GamePadConfig( /* ringSegments = */ 12, /* Primary (ABXY) */ PrimaryDialConfig.PrimaryButtons( diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController5.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController5.kt index 30d13512c..6ad7b0feb 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController5.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController5.kt @@ -18,7 +18,6 @@ import com.swordfish.radialgamepad.library.config.ButtonConfig import com.swordfish.radialgamepad.library.config.CrossConfig import com.swordfish.radialgamepad.library.config.CrossContentDescription import com.swordfish.radialgamepad.library.config.PrimaryDialConfig -import com.swordfish.radialgamepad.library.config.RadialGamePadConfig import com.swordfish.radialgamepad.library.config.SecondaryDialConfig import com.swordfish.radialgamepad.library.event.Event import kotlinx.coroutines.flow.merge @@ -178,12 +177,12 @@ class GameController5(var activity: Activity) : IGameController { } } -private fun generateConfig5(isLeft: Boolean, useSwitchLayout: Boolean): RadialGamePadConfig { +private fun generateConfig5(isLeft: Boolean, useSwitchLayout: Boolean): GamePadConfig { val distance = 0.3f val buttonScale = 1f if (isLeft) { - return RadialGamePadConfig( + return GamePadConfig( /* ringSegments = */ 12, /* Primary (Stick) */ // IMPORTANT: pressButtonId -> DUMMY_LEFT_STICK_PRESS_ID, so that double tap does not trigger L3 @@ -273,7 +272,7 @@ private fun generateConfig5(isLeft: Boolean, useSwitchLayout: Boolean): RadialGa ) ) } else { - return RadialGamePadConfig( + return GamePadConfig( /* ringSegments = */ 12, /* Primary (ABXY) */ PrimaryDialConfig.PrimaryButtons( diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController6.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController6.kt index 6581ff7d2..40a44ac30 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController6.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameController6.kt @@ -18,7 +18,6 @@ import com.swordfish.radialgamepad.library.config.ButtonConfig import com.swordfish.radialgamepad.library.config.CrossConfig import com.swordfish.radialgamepad.library.config.CrossContentDescription import com.swordfish.radialgamepad.library.config.PrimaryDialConfig -import com.swordfish.radialgamepad.library.config.RadialGamePadConfig import com.swordfish.radialgamepad.library.config.SecondaryDialConfig import com.swordfish.radialgamepad.library.event.Event import kotlinx.coroutines.flow.merge diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameHost.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameHost.kt index 8aa165ea1..d0836065c 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameHost.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/GameHost.kt @@ -177,7 +177,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su override fun onWindowVisibilityChanged(visibility: Int) { super.onWindowVisibilityChanged(visibility) - if (visibility != android.view.View.VISIBLE) { + if (visibility != VISIBLE) { ghLog("window not visible → shutdownBinding()") try { KenjinxNative.detachWindow() } catch (_: Throwable) {} shutdownBinding() @@ -370,7 +370,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su val isSideFlip = (old == 1 && rotation == 3) || (old == 3 && rotation == 1) if (isSideFlip) { - try { KenjinxNative.setSurfaceRotationByAndroidRotation(rotation ?: 0) } catch (_: Throwable) {} + try { KenjinxNative.setSurfaceRotationByAndroidRotation(rotation) } catch (_: Throwable) {} rebindNativeWindow(force = true) val now = android.os.SystemClock.uptimeMillis() if (now - lastKickAt >= 300L && _inputInitialized && MainActivity.mainViewModel?.rendererReady == true) { @@ -466,12 +466,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su val act = mainViewModel.activity val intent = Intent(act, EmulationService::class.java) try { - if (Build.VERSION.SDK_INT >= 26) { - act.startForegroundService(intent) - } else { - @Suppress("DEPRECATION") - act.startService(intent) - } + act.startForegroundService(intent) } catch (_: Throwable) { } try { diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt index e9f0e7a2c..ce7507644 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt @@ -1,5 +1,6 @@ package org.kenjinx.android +import android.os.Build import com.sun.jna.JNIEnv import com.sun.jna.Library import com.sun.jna.Native @@ -7,6 +8,7 @@ import org.kenjinx.android.viewmodels.GameInfo import java.util.Collections import android.view.Surface import android.util.Log +import androidx.annotation.RequiresApi interface KenjinxNativeJna : Library { fun deviceInitialize( @@ -173,7 +175,7 @@ object KenjinxNative : KenjinxNativeJna by jnaInstance { backendThreading: Int ): Boolean { val requested = backendThreading - val isQcom = "qcom".equals(android.os.Build.HARDWARE, true) + val isQcom = "qcom".equals(Build.HARDWARE, true) // Heuristic: On QCOM, when "Auto" is selected, first try single-threaded mode; // explicitly set values remain unchanged. @@ -182,7 +184,7 @@ object KenjinxNative : KenjinxNativeJna by jnaInstance { Log.i( "KenjinxNative", - "graphicsInitialize: request=$requested firstChoice=$firstChoice hw=${android.os.Build.HARDWARE}" + "graphicsInitialize: request=$requested firstChoice=$firstChoice hw=${Build.HARDWARE}" ) return try { @@ -234,6 +236,7 @@ object KenjinxNative : KenjinxNativeJna by jnaInstance { } // ---------------------------------------------------- + @RequiresApi(Build.VERSION_CODES.R) @JvmStatic fun frameEnded() = MainActivity.frameEnded() diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt index 2066b717c..52d3991bc 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt @@ -7,7 +7,6 @@ import android.view.KeyEvent import android.view.MotionEvent import android.view.WindowManager import android.content.Intent -import android.os.Build import android.os.Handler import android.os.Looper import android.util.Log @@ -43,7 +42,10 @@ import androidx.lifecycle.Lifecycle import android.content.BroadcastReceiver import android.content.IntentFilter import android.content.Context +import android.os.Build +import androidx.annotation.RequiresApi import org.kenjinx.android.service.EmulationService +import androidx.core.content.edit class MainActivity : BaseActivity() { private var physicalControllerManager: PhysicalControllerManager = @@ -151,7 +153,7 @@ class MainActivity : BaseActivity() { override fun run() { val isReallyResumed = lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) && isActive val hasFocusNow = hasWindowFocus() - val rendererReady = MainActivity.mainViewModel?.rendererReady == true + val rendererReady = mainViewModel?.rendererReady == true if (!isReallyResumed || !hasFocusNow || !rendererReady) { handler.postDelayed(this, ENABLE_PRESENT_DELAY_MS) @@ -231,6 +233,7 @@ class MainActivity : BaseActivity() { const val EXTRA_TITLE_ID = "titleId" const val EXTRA_TITLE_NAME = "titleName" + @RequiresApi(Build.VERSION_CODES.R) @JvmStatic fun frameEnded() { mainViewModel?.activity?.apply { @@ -276,6 +279,7 @@ class MainActivity : BaseActivity() { _isInit = KenjinxNative.javaInitialize(appPath, JNIEnv.CURRENT) } + @RequiresApi(Build.VERSION_CODES.R) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ensureNotificationPermission() @@ -283,15 +287,9 @@ class MainActivity : BaseActivity() { motionSensorManager = MotionSensorManager(this) Thread.setDefaultUncaughtExceptionHandler(crashHandler) - if (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - !Environment.isExternalStorageManager() - } else { - !Environment.isExternalStorageLegacy() - } + if (!Environment.isExternalStorageManager() ) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - storageHelper?.storage?.requestFullStorageAccess() - } + storageHelper?.storage?.requestFullStorageAccess() } AppPath = this.getExternalFilesDir(null)!!.absolutePath @@ -335,9 +333,7 @@ class MainActivity : BaseActivity() { } storedIntent = intent - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - rotLog("onCreate: initial display.rotation=${display?.rotation} → ${deg(display?.rotation)}°") - } + rotLog("onCreate: initial display.rotation=${display?.rotation} → ${deg(display?.rotation)}°") } override fun onNewIntent(intent: Intent) { @@ -379,13 +375,13 @@ class MainActivity : BaseActivity() { super.onStart() coldResetIfZombie("onStart") - if (isGameRunning && MainActivity.mainViewModel?.rendererReady == true) { + if (isGameRunning && mainViewModel?.rendererReady == true) { try { KenjinxNative.graphicsSetPresentEnabled(true) Log.d(TAG_FG, "present=ENABLED (onStart)") } catch (_: Throwable) {} } else { - Log.d(TAG_FG, "skip enable present (onStart) — rendererReady=${MainActivity.mainViewModel?.rendererReady}") + Log.d(TAG_FG, "skip enable present (onStart) — rendererReady=${mainViewModel?.rendererReady}") setPresentEnabled(false, "cold reset: onStart (no game)") } } @@ -405,15 +401,15 @@ class MainActivity : BaseActivity() { override fun onTrimMemory(level: Int) { super.onTrimMemory(level) - if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN && isGameRunning) { + if (level >= TRIM_MEMORY_UI_HIDDEN && isGameRunning) { setAudioForegroundState(false) - if (MainActivity.mainViewModel?.rendererReady == true) { + if (mainViewModel?.rendererReady == true) { try { setPresentEnabled(false, "onTrimMemory:$level") KenjinxNative.detachWindow() } catch (_: Throwable) {} } else { - Log.d(TAG_FG, "skip disable present (onTrimMemory) — rendererReady=${MainActivity.mainViewModel?.rendererReady}") + Log.d(TAG_FG, "skip disable present (onTrimMemory) — rendererReady=${mainViewModel?.rendererReady}") } } } @@ -441,7 +437,7 @@ class MainActivity : BaseActivity() { registerReceiver( serviceStopReceiver, IntentFilter(EmulationService.ACTION_STOPPED), - Context.RECEIVER_EXPORTED + RECEIVER_EXPORTED ) } else { @Suppress("DEPRECATION") @@ -495,6 +491,7 @@ class MainActivity : BaseActivity() { } } + @RequiresApi(Build.VERSION_CODES.R) override fun onPause() { super.onPause() isActive = false @@ -583,6 +580,7 @@ class MainActivity : BaseActivity() { try { KenjinxNative.setSurfaceRotationByAndroidRotation(rot) } catch (_: Throwable) {} } + @RequiresApi(Build.VERSION_CODES.R) override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) val rot = this.display?.rotation @@ -614,7 +612,7 @@ class MainActivity : BaseActivity() { ) { /* optional: Log/Toast */ } private fun ensureNotificationPermission() { - if (android.os.Build.VERSION.SDK_INT >= 33) { + if (Build.VERSION.SDK_INT >= 33) { val granted = ContextCompat.checkSelfPermission( this, Manifest.permission.POST_NOTIFICATIONS ) == PackageManager.PERMISSION_GRANTED @@ -674,6 +672,7 @@ class MainActivity : BaseActivity() { } catch (_: Exception) { null } } + @RequiresApi(Build.VERSION_CODES.R) fun shutdownAndRestart() { val packageManager = packageManager val intent = packageManager.getLaunchIntentForPackage(packageName) @@ -696,10 +695,10 @@ class MainActivity : BaseActivity() { private fun setEmuRunningFlag(value: Boolean) { try { - getSharedPreferences(PREFS, Context.MODE_PRIVATE) - .edit() - .putBoolean(KEY_EMU_RUNNING, value) - .apply() + getSharedPreferences(PREFS, MODE_PRIVATE) + .edit { + putBoolean(KEY_EMU_RUNNING, value) + } } catch (_: Throwable) { } } @@ -723,7 +722,7 @@ class MainActivity : BaseActivity() { private fun coldResetIfZombie(phase: String) { try { - val zombie = getSharedPreferences(PREFS, Context.MODE_PRIVATE) + val zombie = getSharedPreferences(PREFS, MODE_PRIVATE) .getBoolean(KEY_EMU_RUNNING, false) if (zombie) { clearEmuRunningFlag() diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/PerformanceManager.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/PerformanceManager.kt index a62b87d6d..65f6e532f 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/PerformanceManager.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/PerformanceManager.kt @@ -1,10 +1,13 @@ package org.kenjinx.android import android.content.Intent +import android.os.Build +import androidx.annotation.RequiresApi import kotlin.math.abs class PerformanceManager(private val activity: MainActivity) { companion object { + @RequiresApi(Build.VERSION_CODES.R) fun force60HzRefreshRate(enable: Boolean, activity: MainActivity) { // Hack for MIUI devices since they don't support the standard Android APIs try { @@ -25,6 +28,7 @@ class PerformanceManager(private val activity: MainActivity) { } } + @RequiresApi(Build.VERSION_CODES.R) fun setTurboMode(enable: Boolean) { NativeHelpers.instance.setTurboMode(enable) force60HzRefreshRate(enable, activity) diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutHelper.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutHelper.kt index 968ce0e55..bba1aa696 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutHelper.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutHelper.kt @@ -6,12 +6,12 @@ import android.content.Context import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory -import android.net.Uri import android.util.Base64 import android.widget.Toast import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat +import androidx.core.net.toUri object ShortcutHelper { @@ -33,7 +33,7 @@ object ShortcutHelper { gridIconBitmap: Bitmap? = null, gridIconBase64: String? = null ) { - val uri = runCatching { Uri.parse(bootPathUri) }.getOrNull() + val uri = runCatching { bootPathUri.toUri() }.getOrNull() // --- Select icon (Grid bitmap > Base64 > App icon) var icon = IconCompat.createWithResource(context, R.mipmap.ic_launcher) diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutUtils.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutUtils.kt index 998a066ce..055bbd517 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutUtils.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutUtils.kt @@ -1,5 +1,6 @@ package org.kenjinx.android +import android.annotation.SuppressLint import android.app.Activity import android.app.PendingIntent import android.content.BroadcastReceiver @@ -16,9 +17,8 @@ import android.net.Uri import android.os.Build import android.os.Handler import android.os.Looper -import android.view.MotionEvent -import android.view.View +@SuppressLint("SourceLockedOrientationActivity") object ShortcutUtils { fun suggestLabelFromUri(uri: Uri): String { @@ -47,7 +47,6 @@ object ShortcutUtils { iconBitmap: Bitmap? = null, onCompleted: (() -> Unit)? = null ): Boolean { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false val sm = activity.getSystemService(ShortcutManager::class.java) ?: return false val rw = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION @@ -118,7 +117,7 @@ object ShortcutUtils { } val filter = IntentFilter(ACTION_PIN_RESULT) if (Build.VERSION.SDK_INT >= 33) { - activity.registerReceiver(pinResultReceiver!!, filter, Context.RECEIVER_NOT_EXPORTED) + activity.registerReceiver(pinResultReceiver, filter, Context.RECEIVER_NOT_EXPORTED) } else { @Suppress("UnspecifiedRegisterReceiverFlag") activity.registerReceiver(pinResultReceiver, filter) diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutWizardActivity.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutWizardActivity.kt index fc544fcf7..d9929f608 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutWizardActivity.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/ShortcutWizardActivity.kt @@ -1,5 +1,6 @@ package org.kenjinx.android +import android.annotation.SuppressLint import android.app.Activity import android.app.AlertDialog import android.content.Intent @@ -14,6 +15,7 @@ import android.util.Base64 import androidx.documentfile.provider.DocumentFile import org.kenjinx.android.viewmodels.GameInfo +@SuppressLint("SourceLockedOrientationActivity") class ShortcutWizardActivity : Activity() { private val REQ_PICK_GAME = 1 diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/UiHandler.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/UiHandler.kt index 76ea399bf..60e17d94f 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/UiHandler.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/UiHandler.kt @@ -100,7 +100,7 @@ class UiHandler { if (inputText.value.isEmpty()) { validation.value = "Must be between $minLength and $maxLength characters" } else { - return inputText.value.length < minLength || inputText.value.length > maxLength + return inputText.value.length !in minLength..maxLength } return false } @@ -116,7 +116,7 @@ class UiHandler { fun submit() { if (type == 2) { - if (inputListener.value.length < minLength || inputListener.value.length > maxLength) return + if (inputListener.value.length !in minLength..maxLength) return } KenjinxNative.uiHandlerSetResponse(true, if (type == 2) inputListener.value else "") showMessageListener.value = false diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/CheatFs.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/CheatFs.kt index bd0fb2a73..1398a1ab9 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/CheatFs.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/CheatFs.kt @@ -30,7 +30,7 @@ private fun allCheatDirs(activity: Activity, titleId: String): List { private fun parseCheatNames(text: String): List { // Trim BOM, CRLF tolerant val clean = text.replace("\uFEFF", "") - val rx = Regex("""(?m)^\s*\[(.+?)\]\s*$""") + val rx = Regex("""(?m)^\s*\[(.+?)]\s*$""") return rx.findAll(clean) .map { it.groupValues[1].trim() } .filter { it.isNotEmpty() } diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/CheatPrefs.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/CheatPrefs.kt index 667efd61f..377a98fa8 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/CheatPrefs.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/CheatPrefs.kt @@ -1,6 +1,7 @@ package org.kenjinx.android.cheats import android.content.Context +import androidx.core.content.edit class CheatPrefs(private val context: Context) { private fun key(titleId: String) = "cheats_$titleId" @@ -11,6 +12,6 @@ class CheatPrefs(private val context: Context) { } fun setEnabled(titleId: String, keys: Set) { - prefs.edit().putStringSet(key(titleId), keys.toSet()).apply() + prefs.edit { putStringSet(key(titleId), keys.toSet()) } } } diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/ModFs.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/ModFs.kt index 2a873b849..b8c213b6b 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/ModFs.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/cheats/ModFs.kt @@ -6,8 +6,6 @@ import android.net.Uri import android.provider.OpenableColumns import android.util.Log import java.io.File -import java.io.InputStream -import java.util.zip.ZipEntry import java.util.zip.ZipInputStream /* -------- Paths -------- */ diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/saves/SaveFs.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/saves/SaveFs.kt index d087b7ccb..089612ded 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/saves/SaveFs.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/saves/SaveFs.kt @@ -246,7 +246,7 @@ fun importSaveFromZip( } if (titleIdFromZip == null) return ImportResult(false, "error importing save. missing TITLEID folder") - val tidZip = titleIdFromZip!!.lowercase(Locale.ROOT) + val tidZip = titleIdFromZip.lowercase(Locale.ROOT) // Sum the size only under //… runCatching { diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/service/EmulationService.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/service/EmulationService.kt index 8f782a15d..daff96d4a 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/service/EmulationService.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/service/EmulationService.kt @@ -5,10 +5,8 @@ import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.app.Service -import android.content.Context import android.content.Intent import android.os.Binder -import android.os.Build import android.os.IBinder import androidx.core.app.NotificationCompat import org.kenjinx.android.MainActivity @@ -138,10 +136,7 @@ class EmulationService : Service() { private fun buildNotification(): Notification { val openIntent = Intent(this, MainActivity::class.java) - val flags = if (Build.VERSION.SDK_INT >= 23) - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - else - PendingIntent.FLAG_UPDATE_CURRENT + val flags = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT val pi = PendingIntent.getActivity(this, 0, openIntent, flags) return NotificationCompat.Builder(this, CHANNEL_ID) @@ -156,15 +151,13 @@ class EmulationService : Service() { } private fun createNotificationChannel() { - if (Build.VERSION.SDK_INT >= 26) { - val mgr = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val ch = NotificationChannel( - CHANNEL_ID, - "Emulation", - NotificationManager.IMPORTANCE_LOW - ) - ch.description = "Keeps the Emulation active." - mgr.createNotificationChannel(ch) - } + val mgr = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + val ch = NotificationChannel( + CHANNEL_ID, + "Emulation", + NotificationManager.IMPORTANCE_LOW + ) + ch.description = "Keeps the Emulation active." + mgr.createNotificationChannel(ch) } } diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/DlcViewModel.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/DlcViewModel.kt index 92fedaddd..04d0adc23 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/DlcViewModel.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/DlcViewModel.kt @@ -212,11 +212,9 @@ class DlcViewModel(val titleId: String) { } var data: MutableList? = null - private var jsonPath: String + private var jsonPath: String = MainActivity.AppPath + "/games/" + titleId.toLowerCase(Locale.current) + "/dlc.json" init { - jsonPath = - MainActivity.AppPath + "/games/" + titleId.toLowerCase(Locale.current) + "/dlc.json" storageHelper = MainActivity.StorageHelper!! reloadFromDisk() diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/GameModel.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/GameModel.kt index 24001526e..7cbe23d50 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/GameModel.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/GameModel.kt @@ -1,5 +1,6 @@ package org.kenjinx.android.viewmodels +import android.annotation.SuppressLint import android.content.Context import android.net.Uri import android.os.ParcelFileDescriptor @@ -10,10 +11,15 @@ import org.kenjinx.android.KenjinxNative class GameModel(var file: DocumentFile, val context: Context) { private var updateDescriptor: ParcelFileDescriptor? = null - var type: FileType + var type: FileType = when { + (file.extension == "xci") -> FileType.Xci + (file.extension == "nsp") -> FileType.Nsp + (file.extension == "nro") -> FileType.Nro + else -> FileType.None + } var descriptor: ParcelFileDescriptor? = null - var fileName: String? - var fileBaseName: String? + var fileName = file.name + var fileBaseName: String? = fileName?.substringBeforeLast(".", fileName!!) var fileSize = 0.0 var titleName: String? = null var titleId: String? = null @@ -21,17 +27,6 @@ class GameModel(var file: DocumentFile, val context: Context) { var version: String? = null var icon: String? = null - init { - fileName = file.name - fileBaseName = fileName?.substringBeforeLast(".", fileName!!) - type = when { - (file.extension == "xci") -> FileType.Xci - (file.extension == "nsp") -> FileType.Nsp - (file.extension == "nro") -> FileType.Nro - else -> FileType.None - } - } - fun getGameInfo() { val pid = open() val gameInfo = GameInfo() @@ -60,6 +55,7 @@ class GameModel(var file: DocumentFile, val context: Context) { return descriptor?.fd ?: 0 } + @SuppressLint("UseKtx") fun openUpdate(): Int { if (titleId?.isNotEmpty() == true) { val vm = TitleUpdateViewModel(titleId ?: "") diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt index e8d725b3e..11f3bc6da 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt @@ -27,6 +27,8 @@ import org.kenjinx.android.UiHandler import android.util.Log import java.io.File import java.util.TimeZone +import androidx.core.content.edit +import androidx.core.net.toUri @SuppressLint("WrongConstant") class MainViewModel(val activity: MainActivity) { @@ -62,7 +64,7 @@ class MainViewModel(val activity: MainActivity) { set(value) { field = value val prefs = PreferenceManager.getDefaultSharedPreferences(activity) - prefs.edit().putString("defaultGameFolderUri", value?.toString() ?: "").apply() + prefs.edit { putString("defaultGameFolderUri", value?.toString() ?: "") } } var gameHost: GameHost? = null set(value) { @@ -79,7 +81,7 @@ class MainViewModel(val activity: MainActivity) { val prefs = PreferenceManager.getDefaultSharedPreferences(activity) val saved = prefs.getString("defaultGameFolderUri", "") ?: "" if (saved.isNotEmpty()) { - defaultGameFolderUri = Uri.parse(saved) + defaultGameFolderUri = saved.toUri() } } diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/TitleUpdateViewModel.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/TitleUpdateViewModel.kt index 67972b1b9..28a2eaebe 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/TitleUpdateViewModel.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/TitleUpdateViewModel.kt @@ -16,7 +16,7 @@ import java.io.File class TitleUpdateViewModel(val titleId: String) { private var canClose: MutableState? = null - private var basePath: String + private var basePath: String = MainActivity.AppPath + "/games/" + titleId.toLowerCase(Locale.current) private var updateJsonName = "updates.json" private var storageHelper: SimpleStorageHelper private var currentPaths: MutableList = mutableListOf() @@ -187,11 +187,9 @@ class TitleUpdateViewModel(val titleId: String) { } var data: TitleUpdateMetadata? = null - private var jsonPath: String + private var jsonPath: String = "${basePath}/${updateJsonName}" init { - basePath = MainActivity.AppPath + "/games/" + titleId.toLowerCase(Locale.current) - jsonPath = "${basePath}/${updateJsonName}" data = TitleUpdateMetadata() if (File(jsonPath).exists()) { diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/GameViews.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/GameViews.kt index 5ce54e356..0323fa646 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/GameViews.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/GameViews.kt @@ -53,7 +53,6 @@ import org.kenjinx.android.viewmodels.VSyncMode import org.kenjinx.android.widgets.SimpleAlertDialog import java.util.Locale import kotlin.math.roundToInt -import android.net.Uri import android.widget.Toast import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth @@ -61,6 +60,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.ui.platform.LocalConfiguration import org.kenjinx.android.viewmodels.QuickSettings.VirtualControllerPreset +import androidx.core.net.toUri class GameViews { @@ -338,7 +338,7 @@ class GameViews { Toast.makeText(mainViewModel.activity, "Slot 1 is empty.", Toast.LENGTH_SHORT).show() } else { try { - val bytes = mainViewModel.activity.contentResolver.openInputStream(Uri.parse(u))?.use { it.readBytes() } + val bytes = mainViewModel.activity.contentResolver.openInputStream(u.toUri())?.use { it.readBytes() } if (bytes != null && bytes.isNotEmpty()) { val ok = KenjinxNative.amiiboLoadBin(bytes, bytes.size) if (ok) Toast.makeText(mainViewModel.activity, "Loaded: $name", Toast.LENGTH_SHORT).show() @@ -346,7 +346,7 @@ class GameViews { } else Toast.makeText(mainViewModel.activity, "File not readable.", Toast.LENGTH_SHORT).show() } catch (t: Throwable) { Toast.makeText(mainViewModel.activity, "Error: ${t.message}", Toast.LENGTH_SHORT).show() } } - }) { androidx.compose.material3.Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo1Name, 1)) } + }) { Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo1Name, 1)) } androidx.compose.material3.Button(onClick = { val qs = QuickSettings(mainViewModel.activity) @@ -356,7 +356,7 @@ class GameViews { Toast.makeText(mainViewModel.activity, "Slot 2 is empty.", Toast.LENGTH_SHORT).show() } else { try { - val bytes = mainViewModel.activity.contentResolver.openInputStream(Uri.parse(u))?.use { it.readBytes() } + val bytes = mainViewModel.activity.contentResolver.openInputStream(u.toUri())?.use { it.readBytes() } if (bytes != null && bytes.isNotEmpty()) { val ok = KenjinxNative.amiiboLoadBin(bytes, bytes.size) if (ok) Toast.makeText(mainViewModel.activity, "Loaded: $name", Toast.LENGTH_SHORT).show() @@ -364,7 +364,7 @@ class GameViews { } else Toast.makeText(mainViewModel.activity, "File not readable.", Toast.LENGTH_SHORT).show() } catch (t: Throwable) { Toast.makeText(mainViewModel.activity, "Error: ${t.message}", Toast.LENGTH_SHORT).show() } } - }) { androidx.compose.material3.Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo2Name, 2)) } + }) { Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo2Name, 2)) } androidx.compose.material3.Button(onClick = { val qs = QuickSettings(mainViewModel.activity) @@ -374,7 +374,7 @@ class GameViews { Toast.makeText(mainViewModel.activity, "Slot 3 is empty.", Toast.LENGTH_SHORT).show() } else { try { - val bytes = mainViewModel.activity.contentResolver.openInputStream(Uri.parse(u))?.use { it.readBytes() } + val bytes = mainViewModel.activity.contentResolver.openInputStream(u.toUri())?.use { it.readBytes() } if (bytes != null && bytes.isNotEmpty()) { val ok = KenjinxNative.amiiboLoadBin(bytes, bytes.size) if (ok) Toast.makeText(mainViewModel.activity, "Loaded: $name", Toast.LENGTH_SHORT).show() @@ -382,7 +382,7 @@ class GameViews { } else Toast.makeText(mainViewModel.activity, "File not readable.", Toast.LENGTH_SHORT).show() } catch (t: Throwable) { Toast.makeText(mainViewModel.activity, "Error: ${t.message}", Toast.LENGTH_SHORT).show() } } - }) { androidx.compose.material3.Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo3Name, 3)) } + }) { Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo3Name, 3)) } } Row(modifier = Modifier.padding(top = 8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) { @@ -394,7 +394,7 @@ class GameViews { Toast.makeText(mainViewModel.activity, "Slot 4 is empty.", Toast.LENGTH_SHORT).show() } else { try { - val bytes = mainViewModel.activity.contentResolver.openInputStream(Uri.parse(u))?.use { it.readBytes() } + val bytes = mainViewModel.activity.contentResolver.openInputStream(u.toUri())?.use { it.readBytes() } if (bytes != null && bytes.isNotEmpty()) { val ok = KenjinxNative.amiiboLoadBin(bytes, bytes.size) if (ok) Toast.makeText(mainViewModel.activity, "Loaded: $name", Toast.LENGTH_SHORT).show() @@ -402,7 +402,7 @@ class GameViews { } else Toast.makeText(mainViewModel.activity, "File not readable.", Toast.LENGTH_SHORT).show() } catch (t: Throwable) { Toast.makeText(mainViewModel.activity, "Error: ${t.message}", Toast.LENGTH_SHORT).show() } } - }) { androidx.compose.material3.Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo4Name, 4)) } + }) { Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo4Name, 4)) } androidx.compose.material3.Button(onClick = { val qs = QuickSettings(mainViewModel.activity) @@ -412,7 +412,7 @@ class GameViews { Toast.makeText(mainViewModel.activity, "Slot 5 is empty.", Toast.LENGTH_SHORT).show() } else { try { - val bytes = mainViewModel.activity.contentResolver.openInputStream(Uri.parse(u))?.use { it.readBytes() } + val bytes = mainViewModel.activity.contentResolver.openInputStream(u.toUri())?.use { it.readBytes() } if (bytes != null && bytes.isNotEmpty()) { val ok = KenjinxNative.amiiboLoadBin(bytes, bytes.size) if (ok) Toast.makeText(mainViewModel.activity, "Loaded: $name", Toast.LENGTH_SHORT).show() @@ -420,12 +420,12 @@ class GameViews { } else Toast.makeText(mainViewModel.activity, "File not readable.", Toast.LENGTH_SHORT).show() } catch (t: Throwable) { Toast.makeText(mainViewModel.activity, "Error: ${t.message}", Toast.LENGTH_SHORT).show() } } - }) { androidx.compose.material3.Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo5Name, 5)) } + }) { Text(qsLabel(QuickSettings(mainViewModel.activity).amiibo5Name, 5)) } androidx.compose.material3.OutlinedButton(onClick = { KenjinxNative.amiiboClear() Toast.makeText(mainViewModel.activity, "Amiibo cleared", Toast.LENGTH_SHORT).show() - }) { androidx.compose.material3.Text("Clear") } + }) { Text("Clear") } } } } diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/HomeViews.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/HomeViews.kt index 96a5c20fa..3e967a397 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/HomeViews.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/HomeViews.kt @@ -92,12 +92,12 @@ import org.kenjinx.android.viewmodels.HomeViewModel import org.kenjinx.android.viewmodels.QuickSettings import org.kenjinx.android.widgets.SimpleAlertDialog import org.kenjinx.android.ShortcutUtils -import org.kenjinx.android.ShortcutWizardActivity import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.material3.AlertDialog import androidx.compose.material3.TextButton +import androidx.compose.runtime.mutableFloatStateOf import androidx.documentfile.provider.DocumentFile // Cheats @@ -162,19 +162,19 @@ class HomeViews { var isFabVisible by remember { mutableStateOf(true) } val isNavigating = remember { mutableStateOf(false) } - // NEW: Amiibo slot picker state + // Amiibo slot picker state val showAmiiboSlotDialog = remember { mutableStateOf(false) } val pendingSlot = remember { mutableStateOf(1) } - // NEW: Cheats UI state + // Cheats UI state val openCheatsDialog = remember { mutableStateOf(false) } val cheatsForSelected = remember { mutableStateOf(listOf()) } val enabledCheatKeys = remember { mutableStateOf(mutableSetOf()) } - // NEW: Mods UI state + // Mods UI state val openModsDialog = remember { mutableStateOf(false) } val modsForSelected = remember { mutableStateOf(listOf()) } - val modsImportProgress = remember { mutableStateOf(0f) } + val modsImportProgress = remember { mutableFloatStateOf(0f) } val modsImportBusy = remember { mutableStateOf(false) } val modsImportStatusText = remember { mutableStateOf("") } @@ -182,8 +182,8 @@ class HomeViews { val openSavesDialog = remember { mutableStateOf(false) } val saveImportBusy = remember { mutableStateOf(false) } val saveExportBusy = remember { mutableStateOf(false) } - val saveImportProgress = remember { mutableStateOf(0f) } - val saveExportProgress = remember { mutableStateOf(0f) } + val saveImportProgress = remember { mutableFloatStateOf(0f) } + val saveExportProgress = remember { mutableFloatStateOf(0f) } val saveImportStatus = remember { mutableStateOf("") } val saveExportStatus = remember { mutableStateOf("") } @@ -198,13 +198,13 @@ class HomeViews { val tIdNow = viewModel.mainViewModel?.selected?.titleId.orEmpty() if (uri != null && act != null && tIdNow.isNotEmpty()) { saveImportBusy.value = true - saveImportProgress.value = 0f + saveImportProgress.floatValue = 0f saveImportStatus.value = "Starting…" thread { val res = importSaveFromZip(act, uri) { prog -> val frac = if (prog.total > 0) prog.bytes.toFloat() / prog.total else 0f - saveImportProgress.value = frac.coerceIn(0f, 1f) + saveImportProgress.floatValue = frac.coerceIn(0f, 1f) saveImportStatus.value = "Importing: ${prog.currentEntry}" } saveImportBusy.value = false @@ -223,13 +223,13 @@ class HomeViews { val tIdNow = viewModel.mainViewModel?.selected?.titleId.orEmpty() if (uri != null && act != null && tIdNow.isNotEmpty()) { saveExportBusy.value = true - saveExportProgress.value = 0f + saveExportProgress.floatValue = 0f saveExportStatus.value = "Starting…" thread { val res = exportSaveToZip(act, tIdNow, uri) { prog -> val frac = if (prog.total > 0) prog.bytes.toFloat() / prog.total else 0f - saveExportProgress.value = frac.coerceIn(0f, 1f) + saveExportProgress.floatValue = frac.coerceIn(0f, 1f) saveExportStatus.value = "Exporting: ${prog.currentPath}" } saveExportBusy.value = false @@ -250,7 +250,7 @@ class HomeViews { val context = LocalContext.current - // NEW: Amiibo Launcher (OpenDocument) + // Amiibo Launcher (OpenDocument) val pickAmiiboLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenDocument() ) { uri: Uri? -> @@ -275,7 +275,7 @@ class HomeViews { } } - // NEW: Cheats Import (.txt) + // Cheats Import (.txt) val importCheatLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenDocument() ) { uri: Uri? -> @@ -316,7 +316,7 @@ class HomeViews { } catch (_: Exception) {} modsImportBusy.value = true - modsImportProgress.value = 0f + modsImportProgress.floatValue = 0f modsImportStatusText.value = "Starting…" thread { @@ -325,7 +325,7 @@ class HomeViews { titleId, uri ) { prog -> - modsImportProgress.value = prog.fraction + modsImportProgress.floatValue = prog.fraction modsImportStatusText.value = if (prog.currentEntry.isNotEmpty()) "Copying: ${prog.currentEntry}" else @@ -642,7 +642,7 @@ class HomeViews { // Amiibo slot chooser dialog (outside of game) if (showAmiiboSlotDialog.value) { - androidx.compose.material3.AlertDialog( + AlertDialog( onDismissRequest = { showAmiiboSlotDialog.value = false }, title = { Text("Import Amiibo") }, text = { @@ -732,7 +732,7 @@ class HomeViews { if (viewModel.mainViewModel?.selected != null) { // Press the cheat buttons before starting (Run button). - val gmSel = viewModel.mainViewModel!!.selected!! + val gmSel = viewModel.mainViewModel.selected!! val tId = gmSel.titleId ?: "" val act = viewModel.activity @@ -1125,7 +1125,7 @@ class HomeViews { .padding(top = 12.dp), horizontalArrangement = Arrangement.End ) { - androidx.compose.material3.TextButton( + TextButton( onClick = { openSavesDialog.value = false } ) { Text("Close") } }