Cleanup RyujinxAndroid

This commit is contained in:
TSR Berry 2023-07-22 07:37:11 +02:00 committed by Emmanuel Hansen
parent 300b23cf9b
commit 979db1def3
18 changed files with 200 additions and 254 deletions

View File

@ -6,40 +6,26 @@ import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.GraphicsLayerScope
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.flowWithLifecycle
import com.swordfish.radialgamepad.library.RadialGamePad
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.RadialGamePadTheme
import com.swordfish.radialgamepad.library.config.SecondaryDialConfig
import com.swordfish.radialgamepad.library.event.Event
import com.swordfish.radialgamepad.library.event.GestureType
import com.swordfish.radialgamepad.library.haptics.HapticConfig
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.subscribe
import kotlinx.coroutines.launch
typealias GamePad = RadialGamePad
@ -56,7 +42,7 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
return this.isVisible
}
return false;
return false
}
init {
@ -80,22 +66,22 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
lifecycleScope.apply {
lifecycleScope.launch {
var events = merge(leftGamePad.events(),rightGamePad.events())
val events = merge(leftGamePad.events(),rightGamePad.events())
.shareIn(lifecycleScope, SharingStarted.Lazily)
events.safeCollect {
handleEvent(it)
};
}
}
}
}
private fun Create(context: Context) : View
{
var inflator = LayoutInflater.from(context);
var view = inflator.inflate(R.layout.game_layout, null)
view.findViewById<FrameLayout>(R.id.leftcontainer)!!.addView(leftGamePad);
view.findViewById<FrameLayout>(R.id.rightcontainer)!!.addView(rightGamePad);
val inflator = LayoutInflater.from(context)
val view = inflator.inflate(R.layout.game_layout, null)
view.findViewById<FrameLayout>(R.id.leftcontainer)!!.addView(leftGamePad)
view.findViewById<FrameLayout>(R.id.rightcontainer)!!.addView(rightGamePad)
controllerView = view
@ -120,51 +106,43 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
if(controllerId == -1)
controllerId = ryujinxNative.inputConnectGamepad(0)
controllerId?.apply {
controllerId.apply {
when (ev) {
is Event.Button -> {
var action = ev.action
val action = ev.action
when (action) {
KeyEvent.ACTION_UP -> {
ryujinxNative.inputSetButtonReleased(ev.id, this)
}
KeyEvent.ACTION_DOWN -> {
ryujinxNative.inputSetButtonPressed(ev.id, this)
}
}
}
is Event.Direction -> {
var direction = ev.id
val direction = ev.id
when(direction) {
GamePadButtonInputId.DpadUp.ordinal -> {
if (ev.xAxis > 0)
{
if (ev.xAxis > 0) {
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadRight.ordinal, this)
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
}
else if (ev.xAxis < 0)
{
} else if (ev.xAxis < 0) {
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadLeft.ordinal, this)
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
}
else
{
} else {
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadLeft.ordinal, this)
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadRight.ordinal, this)
}
if (ev.yAxis < 0)
{
if (ev.yAxis < 0) {
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadUp.ordinal, this)
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
}
else if (ev.yAxis > 0)
{
} else if (ev.yAxis > 0) {
ryujinxNative.inputSetButtonPressed(GamePadButtonInputId.DpadDown.ordinal, this)
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
}
else
{
} else {
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadDown.ordinal, this)
ryujinxNative.inputSetButtonReleased(GamePadButtonInputId.DpadUp.ordinal, this)
}
@ -173,6 +151,7 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
GamePadButtonInputId.LeftStick.ordinal -> {
ryujinxNative.inputSetStickAxis(1, ev.xAxis, -ev.yAxis ,this)
}
GamePadButtonInputId.RightStick.ordinal -> {
ryujinxNative.inputSetStickAxis(2, ev.xAxis, -ev.yAxis ,this)
}
@ -193,7 +172,7 @@ suspend fun <T> Flow<T>.safeCollect(
}
private fun generateConfig(isLeft: Boolean): GamePadConfig {
var distance = 0.05f
val distance = 0.05f
if (isLeft) {
return GamePadConfig(

View File

@ -2,14 +2,12 @@ package org.ryujinx.android
import android.content.Context
import android.os.Build
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
import org.ryujinx.android.viewmodels.GameModel
import org.ryujinx.android.viewmodels.MainViewModel
import org.ryujinx.android.viewmodels.QuickSettings
import kotlin.concurrent.thread
import kotlin.math.roundToInt
class GameHost(context: Context?, val controller: GameController, val mainViewModel: MainViewModel) : SurfaceView(context), SurfaceHolder.Callback {
private var _renderingThreadWatcher: Thread? = null
@ -37,19 +35,19 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
var isStarted = _isStarted;
val isStarted = _isStarted
start(holder)
if(isStarted && (_width != width || _height != height))
{
var nativeHelpers = NativeHelpers()
var window = nativeHelpers.getNativeWindow(holder.surface);
_nativeRyujinx.graphicsSetSurface(window);
val nativeHelpers = NativeHelpers()
val window = nativeHelpers.getNativeWindow(holder.surface)
_nativeRyujinx.graphicsSetSurface(window)
}
_width = width;
_height = height;
_width = width
_height = height
if(_isStarted)
{
@ -61,15 +59,15 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
}
private fun start(surfaceHolder: SurfaceHolder) : Unit {
var game = gameModel ?: return
var path = game.getPath() ?: return
private fun start(surfaceHolder: SurfaceHolder) {
val game = gameModel ?: return
val path = game.getPath() ?: return
if (_isStarted)
return
var surface = surfaceHolder.surface;
var surface = surfaceHolder.surface
var settings = QuickSettings(mainViewModel.activity)
val settings = QuickSettings(mainViewModel.activity)
var success = _nativeRyujinx.graphicsInitialize(GraphicsConfiguration().apply {
EnableShaderCache = settings.enableShaderCache
@ -78,14 +76,14 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
})
var nativeHelpers = NativeHelpers()
var window = nativeHelpers.getNativeWindow(surfaceHolder.surface);
val nativeHelpers = NativeHelpers()
val window = nativeHelpers.getNativeWindow(surfaceHolder.surface)
nativeInterop = NativeGraphicsInterop()
nativeInterop!!.VkRequiredExtensions = arrayOf(
"VK_KHR_surface", "VK_KHR_android_surface"
);
)
nativeInterop!!.VkCreateSurface = nativeHelpers.getCreateSurfacePtr()
nativeInterop!!.SurfaceHandle = window;
nativeInterop!!.SurfaceHandle = window
success = _nativeRyujinx.graphicsInitializeRenderer(
nativeInterop!!.VkRequiredExtensions!!,
@ -104,7 +102,7 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
false,
"UTC",
settings.ignoreMissingServices
);
)
success = _nativeRyujinx.deviceLoad(path)
@ -124,12 +122,12 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
_nativeRyujinx.graphicsRendererSetSize(
surfaceHolder.surfaceFrame.width(),
surfaceHolder.surfaceFrame.height()
);
)
_guestThread = thread(start = true) {
runGame()
}
_isStarted = success;
_isStarted = success
_updateThread = thread(start = true) {
var c = 0
@ -145,20 +143,20 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
}
}
private fun runGame() : Unit{
private fun runGame() {
// RenderingThreadWatcher
_renderingThreadWatcher = thread(start = true) {
var threadId = 0L;
var threadId = 0L
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
mainViewModel.performanceManager?.enable()
while (_isStarted) {
Thread.sleep(1000)
var newthreadId = mainViewModel.activity.getRenderingThreadId()
val newthreadId = mainViewModel.activity.getRenderingThreadId()
if (threadId != newthreadId) {
mainViewModel.performanceManager?.closeCurrentRenderingSession()
}
threadId = newthreadId;
threadId = newthreadId
if (threadId != 0L) {
mainViewModel.performanceManager?.initializeRenderingSession(threadId)
}

View File

@ -34,13 +34,17 @@ class Helpers {
val split = docId.split(":".toRegex()).toTypedArray()
val type = split[0]
var contentUri: Uri? = null
if ("image" == type) {
when (type) {
"image" -> {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
} else if ("video" == type) {
}
"video" -> {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
} else if ("audio" == type) {
}
"audio" -> {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
}
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
return getDataColumn(context, contentUri, selection, selectionArgs)
@ -58,13 +62,13 @@ class Helpers {
val column = "_data"
val projection = arrayOf(column)
try {
cursor = uri?.let { context.getContentResolver().query(it, projection, selection, selectionArgs,null) }
cursor = uri?.let { context.contentResolver.query(it, projection, selection, selectionArgs,null) }
if (cursor != null && cursor.moveToFirst()) {
val column_index: Int = cursor.getColumnIndexOrThrow(column)
return cursor.getString(column_index)
}
} finally {
if (cursor != null) cursor.close()
cursor?.close()
}
return null
}

View File

@ -2,7 +2,6 @@ package org.ryujinx.android
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.media.AudioDeviceInfo
import android.media.AudioManager
@ -25,23 +24,19 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.anggrayudi.storage.SimpleStorageHelper
import org.ryujinx.android.ui.theme.RyujinxAndroidTheme
import org.ryujinx.android.viewmodels.HomeViewModel
import org.ryujinx.android.viewmodels.MainViewModel
import org.ryujinx.android.views.HomeViews
import org.ryujinx.android.views.MainView
class MainActivity : ComponentActivity() {
var physicalControllerManager: PhysicalControllerManager
var physicalControllerManager: PhysicalControllerManager = PhysicalControllerManager(this)
private var _isInit: Boolean = false
var storageHelper: SimpleStorageHelper? = null
companion object {
var mainViewModel: MainViewModel? = null
var AppPath : String?
var AppPath : String = ""
var StorageHelper: SimpleStorageHelper? = null
init {
AppPath = ""
}
@JvmStatic
fun updateRenderSessionPerformance(gameTime : Long)
@ -56,7 +51,6 @@ class MainActivity : ComponentActivity() {
}
init {
physicalControllerManager = PhysicalControllerManager(this)
storageHelper = SimpleStorageHelper(this)
StorageHelper = storageHelper
System.loadLibrary("ryujinxjni")
@ -66,29 +60,28 @@ class MainActivity : ComponentActivity() {
external fun getRenderingThreadId() : Long
external fun initVm()
fun setFullScreen() :Unit {
fun setFullScreen() {
requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
var insets = WindowCompat.getInsetsController(window, window.decorView)
val insets = WindowCompat.getInsetsController(window, window.decorView)
insets?.apply {
insets.apply {
insets.hide(WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.navigationBars())
insets.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
private fun getAudioDevice () : Int {
var audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
var devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
return if (devices.isEmpty())
0
else {
var speaker = devices.find { it.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER }
var earPiece = devices.find { it.type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES || it.type == AudioDeviceInfo.TYPE_WIRED_HEADSET }
val speaker = devices.find { it.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER }
val earPiece = devices.find { it.type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES || it.type == AudioDeviceInfo.TYPE_WIRED_HEADSET }
if(earPiece != null)
return earPiece.id
if(speaker != null)
@ -112,17 +105,23 @@ class MainActivity : ComponentActivity() {
return super.dispatchGenericMotionEvent(ev)
}
private fun initialize() : Unit
{
private fun initialize() {
if (_isInit)
return
var appPath: String = AppPath ?: return
var success = RyujinxNative().initialize(appPath, false)
val appPath: String = AppPath
val success = RyujinxNative().initialize(appPath, false)
_isInit = success
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(
!Environment.isExternalStorageManager()
) {
storageHelper?.storage?.requestFullStorageAccess()
}
AppPath = this.getExternalFilesDir(null)!!.absolutePath
initialize()
@ -130,14 +129,6 @@ class MainActivity : ComponentActivity() {
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
WindowCompat.setDecorFitsSystemWindows(window,false)
if(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
!Environment.isExternalStorageManager()
} else {
false
}
) {
storageHelper?.storage?.requestFullStorageAccess()
}
mainViewModel = MainViewModel(this)
mainViewModel?.apply {

View File

@ -14,7 +14,7 @@ class PerformanceManager(val performanceHintManager: PerformanceHintManager) {
if(!_isEnabled || renderingSession != null)
return
var threads = IntArray(1)
val threads = IntArray(1)
threads[0] = threadId.toInt()
renderingSession = performanceHintManager.createHintSession(threads, DEFAULT_TARGET_NS)
}

View File

@ -9,7 +9,7 @@ class PhysicalControllerManager(val activity: MainActivity) {
fun onKeyEvent(event: KeyEvent) : Boolean{
if(controllerId != -1) {
var id = GetGamePadButtonInputId(event.keyCode)
val id = GetGamePadButtonInputId(event.keyCode)
if(id != GamePadButtonInputId.None) {
when (event.action) {
@ -21,7 +21,7 @@ class PhysicalControllerManager(val activity: MainActivity) {
ryujinxNative.inputSetButtonPressed(id.ordinal, controllerId)
}
}
return true;
return true
}
}
@ -31,10 +31,10 @@ class PhysicalControllerManager(val activity: MainActivity) {
fun onMotionEvent(ev: MotionEvent) {
if(controllerId != -1) {
if(ev.action == MotionEvent.ACTION_MOVE) {
var leftStickX = ev.getAxisValue(MotionEvent.AXIS_X);
var leftStickY = ev.getAxisValue(MotionEvent.AXIS_Y);
var rightStickX = ev.getAxisValue(MotionEvent.AXIS_Z);
var rightStickY = ev.getAxisValue(MotionEvent.AXIS_RZ);
val leftStickX = ev.getAxisValue(MotionEvent.AXIS_X)
val leftStickY = ev.getAxisValue(MotionEvent.AXIS_Y)
val rightStickX = ev.getAxisValue(MotionEvent.AXIS_Z)
val rightStickY = ev.getAxisValue(MotionEvent.AXIS_RZ)
ryujinxNative.inputSetStickAxis(1, leftStickX, -leftStickY ,controllerId)
ryujinxNative.inputSetStickAxis(2, rightStickX, -rightStickY ,controllerId)
}

View File

@ -1,9 +1,8 @@
package org.ryujinx.android
import android.view.Surface
import org.ryujinx.android.viewmodels.GameInfo
import java.io.FileDescriptor
@Suppress("KotlinJniMissingFunction")
class RyujinxNative {
external fun initialize(appPath: String, enableDebugLogs : Boolean): Boolean

View File

@ -1,10 +1,7 @@
package org.ryujinx.android.viewmodels
import android.R.string
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
import android.os.ParcelFileDescriptor
import androidx.documentfile.provider.DocumentFile
import com.anggrayudi.storage.file.extension
import org.ryujinx.android.Helpers
@ -22,8 +19,8 @@ class GameModel(var file: DocumentFile, val context: Context) {
init {
fileName = file.name
var absPath = getPath()
var gameInfo = RyujinxNative().deviceGetGameInfoFromPath(absPath ?: "")
val absPath = getPath()
val gameInfo = RyujinxNative().deviceGetGameInfoFromPath(absPath ?: "")
fileSize = gameInfo.FileSize
titleId = gameInfo.TitleId
@ -37,7 +34,7 @@ class GameModel(var file: DocumentFile, val context: Context) {
var uri = file.uri
if (uri.scheme != "file")
uri = Uri.parse("file://" + Helpers.getPath(context, file.uri))
return uri.path;
return uri.path
}
fun getIsXci() : Boolean {

View File

@ -8,7 +8,6 @@ import com.anggrayudi.storage.file.DocumentFileCompat
import com.anggrayudi.storage.file.DocumentFileType
import com.anggrayudi.storage.file.FileFullPath
import com.anggrayudi.storage.file.extension
import com.anggrayudi.storage.file.fullName
import com.anggrayudi.storage.file.getAbsolutePath
import com.anggrayudi.storage.file.search
import org.ryujinx.android.MainActivity
@ -20,7 +19,7 @@ class HomeViewModel(
private var gameList: SnapshotStateList<GameModel>? = null
private var loadedCache: List<GameModel> = listOf()
private var gameFolderPath: DocumentFile? = null
private var sharedPref: SharedPreferences? = null;
private var sharedPref: SharedPreferences? = null
init {
if (activity != null) {
@ -28,15 +27,15 @@ class HomeViewModel(
activity.storageHelper!!.onFolderSelected = { requestCode, folder ->
run {
gameFolderPath = folder
var p = folder.getAbsolutePath(activity!!)
var editor = sharedPref?.edit()
editor?.putString("gameFolder", p);
val p = folder.getAbsolutePath(activity!!)
val editor = sharedPref?.edit()
editor?.putString("gameFolder", p)
editor?.apply()
reloadGameList()
}
}
var savedFolder = sharedPref?.getString("gameFolder", "") ?: ""
val savedFolder = sharedPref?.getString("gameFolder", "") ?: ""
if (savedFolder.isNotEmpty()) {
try {
@ -56,26 +55,26 @@ class HomeViewModel(
}
fun openGameFolder() {
var path = sharedPref?.getString("gameFolder", "") ?: ""
val path = sharedPref?.getString("gameFolder", "") ?: ""
if (path.isNullOrEmpty())
activity?.storageHelper?.storage?.openFolderPicker();
if (path.isEmpty())
activity?.storageHelper?.storage?.openFolderPicker()
else
activity?.storageHelper?.storage?.openFolderPicker(
activity!!.storageHelper!!.storage.requestCodeFolderPicker,
activity.storageHelper!!.storage.requestCodeFolderPicker,
FileFullPath(activity, path)
)
}
fun reloadGameList() {
var storage = activity?.storageHelper ?: return
var folder = gameFolderPath ?: return
val folder = gameFolderPath ?: return
var files = mutableListOf<GameModel>()
val files = mutableListOf<GameModel>()
for (file in folder.search(false, DocumentFileType.FILE)) {
if (file.extension == "xci" || file.extension == "nsp")
activity?.let {
activity.let {
files.add(GameModel(file, it))
}
}
@ -91,7 +90,7 @@ class HomeViewModel(
}
fun setViewList(list: SnapshotStateList<GameModel>) {
gameList = list;
gameList = list
applyFilter()
}
}

View File

@ -23,14 +23,14 @@ class MainViewModel(val activity: MainActivity) {
init {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
var hintService =
val hintService =
activity.getSystemService(Context.PERFORMANCE_HINT_SERVICE) as PerformanceHintManager
performanceManager = PerformanceManager(hintService)
}
}
fun loadGame(game:GameModel) {
var controller = navController?: return;
val controller = navController?: return
activity.setFullScreen()
GameHost.gameModel = game
controller.navigate("game")

View File

@ -55,18 +55,18 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
resScale: MutableState<Float>,
useVirtualController: MutableState<Boolean>
){
var editor = sharedPref.edit()
val editor = sharedPref.edit()
editor.putBoolean("isHostMapped", isHostMapped?.value ?: true)
editor.putBoolean("useNce", useNce?.value ?: true)
editor.putBoolean("enableVsync", enableVsync?.value ?: true)
editor.putBoolean("enableDocked", enableDocked?.value ?: true)
editor.putBoolean("enablePtc", enablePtc?.value ?: true)
editor.putBoolean("ignoreMissingServices", ignoreMissingServices?.value ?: false)
editor.putBoolean("enableShaderCache", enableShaderCache?.value ?: true)
editor.putBoolean("enableTextureRecompression", enableTextureRecompression?.value ?: false)
editor.putFloat("resScale", resScale?.value ?: 1f)
editor.putBoolean("useVirtualController", useVirtualController?.value ?: true)
editor.putBoolean("isHostMapped", isHostMapped.value)
editor.putBoolean("useNce", useNce.value)
editor.putBoolean("enableVsync", enableVsync.value)
editor.putBoolean("enableDocked", enableDocked.value)
editor.putBoolean("enablePtc", enablePtc.value)
editor.putBoolean("ignoreMissingServices", ignoreMissingServices.value)
editor.putBoolean("enableShaderCache", enableShaderCache.value)
editor.putBoolean("enableTextureRecompression", enableTextureRecompression.value)
editor.putFloat("resScale", resScale.value)
editor.putBoolean("useVirtualController", useVirtualController.value)
editor.apply()
}

View File

@ -1,6 +1,5 @@
package org.ryujinx.android.viewmodels
import androidx.appcompat.widget.ThemedSpinnerAdapter.Helper
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.toLowerCase
@ -24,23 +23,23 @@ class TitleUpdateViewModel(val titleId: String) {
return
data?.paths?.apply {
removeAt(index - 1);
removeAt(index - 1)
pathsState?.clear()
pathsState?.addAll(this)
}
}
fun Add() {
var callBack = storageHelper.onFileSelected
val callBack = storageHelper.onFileSelected
storageHelper.onFileSelected = { requestCode, files ->
run {
storageHelper.onFileSelected = callBack
if(requestCode == UpdateRequestCode)
{
var file = files.firstOrNull()
val file = files.firstOrNull()
file?.apply {
var path = Helpers.getPath(storageHelper.storage.context, file.uri)
val path = Helpers.getPath(storageHelper.storage.context, file.uri)
if(!path.isNullOrEmpty()){
data?.apply {
if(!paths.contains(path)) {
@ -62,20 +61,19 @@ class TitleUpdateViewModel(val titleId: String) {
this.selected = ""
if(paths.isNotEmpty() && index > 0)
{
var ind = max(index - 1, paths.count() - 1)
val ind = max(index - 1, paths.count() - 1)
this.selected = paths[ind]
}
var gson = Gson()
var json = gson.toJson(this)
jsonPath = (MainActivity.AppPath
?: "") + "/games/" + titleId.toLowerCase(Locale.current)
val gson = Gson()
val json = gson.toJson(this)
jsonPath = MainActivity.AppPath + "/games/" + titleId.toLowerCase(Locale.current)
File(jsonPath).mkdirs()
File(jsonPath + "/updates.json").writeText(json)
File("$jsonPath/updates.json").writeText(json)
}
}
fun setPaths(paths: SnapshotStateList<String>) {
pathsState = paths;
pathsState = paths
data?.apply {
pathsState?.clear()
pathsState?.addAll(this.paths)
@ -86,16 +84,15 @@ class TitleUpdateViewModel(val titleId: String) {
private var jsonPath: String
init {
jsonPath = (MainActivity.AppPath
?: "") + "/games/" + titleId.toLowerCase(Locale.current) + "/updates.json"
jsonPath = MainActivity.AppPath + "/games/" + titleId.toLowerCase(Locale.current) + "/updates.json"
data = TitleUpdateMetadata()
if (File(jsonPath).exists()) {
var gson = Gson()
val gson = Gson()
data = gson.fromJson(File(jsonPath).readText(), TitleUpdateMetadata::class.java)
data?.apply {
var existingPaths = mutableListOf<String>()
val existingPaths = mutableListOf<String>()
for (path in paths) {
if (File(path).exists()) {
existingPaths.add(path)

View File

@ -12,22 +12,22 @@ class VulkanDriverViewModel(val activity: MainActivity) {
var selected: String = ""
companion object {
val DriverRequestCode: Int = 1003
const val DriverRequestCode: Int = 1003
const val DriverFolder: String = "drivers"
}
private fun getAppPath() : String {
var appPath =
(MainActivity.AppPath ?: activity.getExternalFilesDir(null)?.absolutePath ?: "");
MainActivity.AppPath
appPath += "/"
return appPath
}
fun ensureDriverPath() : File {
var driverPath = getAppPath() + DriverFolder
val driverPath = getAppPath() + DriverFolder
var driverFolder = File(driverPath)
val driverFolder = File(driverPath)
if(!driverFolder.exists())
driverFolder.mkdirs()
@ -36,13 +36,13 @@ class VulkanDriverViewModel(val activity: MainActivity) {
}
fun getAvailableDrivers() : MutableList<DriverMetadata> {
var driverFolder = ensureDriverPath()
val driverFolder = ensureDriverPath()
var folders = driverFolder.walkTopDown()
val folders = driverFolder.walkTopDown()
var drivers = mutableListOf<DriverMetadata>()
val drivers = mutableListOf<DriverMetadata>()
var selectedDriverFile = File(driverFolder.absolutePath + "/selected");
val selectedDriverFile = File(driverFolder.absolutePath + "/selected")
if(selectedDriverFile.exists()){
selected = selectedDriverFile.readText()
@ -52,16 +52,16 @@ class VulkanDriverViewModel(val activity: MainActivity) {
}
}
var gson = Gson()
val gson = Gson()
for (folder in folders){
if(folder.isDirectory() && folder.parent == driverFolder.absolutePath){
var meta = File(folder.absolutePath + "/meta.json")
val meta = File(folder.absolutePath + "/meta.json")
if(meta.exists()){
var metadata = gson.fromJson(meta.readText(), DriverMetadata::class.java)
val metadata = gson.fromJson(meta.readText(), DriverMetadata::class.java)
if(metadata.name.isNotEmpty()) {
var driver = folder.absolutePath + "/${metadata.libraryName}"
val driver = folder.absolutePath + "/${metadata.libraryName}"
metadata.driverPath = driver
if (File(driver).exists())
drivers.add(metadata)
@ -74,18 +74,17 @@ class VulkanDriverViewModel(val activity: MainActivity) {
}
fun saveSelected() {
var driverFolder = ensureDriverPath()
val driverFolder = ensureDriverPath()
var selectedDriverFile = File(driverFolder.absolutePath + "/selected")
val selectedDriverFile = File(driverFolder.absolutePath + "/selected")
selectedDriverFile.writeText(selected)
}
fun removeSelected(){
if(selected.isNotEmpty()){
var sel = File(selected)
val sel = File(selected)
if(sel.exists()) {
var parent = sel.parentFile
parent.deleteRecursively()
sel.parentFile?.deleteRecursively()
}
selected = ""
@ -96,20 +95,20 @@ class VulkanDriverViewModel(val activity: MainActivity) {
fun add(refresh: MutableState<Boolean>) {
activity.storageHelper?.apply {
var callBack = this.onFileSelected
val callBack = this.onFileSelected
onFileSelected = { requestCode, files ->
run {
onFileSelected = callBack
if(requestCode == DriverRequestCode)
{
var file = files.firstOrNull()
val file = files.firstOrNull()
file?.apply {
var path = Helpers.getPath(storage.context, file.uri)
val path = Helpers.getPath(storage.context, file.uri)
if(!path.isNullOrEmpty()){
var name = file.name?.removeSuffix("." + file.extension) ?: ""
var driverFolder = ensureDriverPath()
var extractionFolder = File(driverFolder.absolutePath + "/${name}")
val name = file.name?.removeSuffix("." + file.extension) ?: ""
val driverFolder = ensureDriverPath()
val extractionFolder = File(driverFolder.absolutePath + "/${name}")
extractionFolder.mkdirs()
ZipFile(path)?.use { zip ->
zip.entries().asSequence().forEach { entry ->
@ -118,7 +117,7 @@ class VulkanDriverViewModel(val activity: MainActivity) {
val filePath = extractionFolder.absolutePath + File.separator + entry.name
if (!entry.isDirectory) {
var length = input.available()
val length = input.available()
val bytesIn = ByteArray(length)
input.read(bytesIn)
File(filePath).writeBytes(bytesIn)

View File

@ -12,8 +12,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
@ -56,12 +54,9 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogWindowProvider
import androidx.compose.ui.window.Popup
import androidx.compose.ui.zIndex
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import coil.compose.AsyncImage
import org.ryujinx.android.MainActivity
@ -78,11 +73,11 @@ class HomeViews {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainTopBar(navController: NavHostController) {
var topBarSize = remember {
val topBarSize = remember {
mutableStateOf(0)
}
Column {
var showOptionsPopup = remember {
val showOptionsPopup = remember {
mutableStateOf(false)
}
TopAppBar(
@ -116,7 +111,7 @@ class HomeViews {
actions = {
IconButton(
onClick = {
showOptionsPopup.value = true;
showOptionsPopup.value = true
}
) {
Icon(
@ -171,7 +166,7 @@ class HomeViews {
fun Home(viewModel: HomeViewModel = HomeViewModel(), navController: NavHostController? = null) {
val sheetState = rememberModalBottomSheetState()
val scope = rememberCoroutineScope()
var showBottomSheet = remember { mutableStateOf(false) }
val showBottomSheet = remember { mutableStateOf(false) }
Scaffold(
modifier = Modifier.fillMaxSize(),
@ -195,7 +190,7 @@ class HomeViews {
) { contentPadding ->
Box(modifier = Modifier.padding(contentPadding)) {
var list = remember {
val list = remember {
mutableStateListOf<GameModel>()
}
viewModel.setViewList(list)
@ -227,8 +222,8 @@ class HomeViews {
shape = MaterialTheme.shapes.large,
tonalElevation = AlertDialogDefaults.TonalElevation
) {
var titleId = viewModel.mainViewModel?.selected?.titleId ?: ""
var name = viewModel.mainViewModel?.selected?.titleName ?: ""
val titleId = viewModel.mainViewModel?.selected?.titleId ?: ""
val name = viewModel.mainViewModel?.selected?.titleName ?: ""
TitleUpdateViews.Main(titleId, name, openDialog)
}
@ -267,7 +262,7 @@ class HomeViews {
}
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun GameItem(gameModel: GameModel, viewModel: HomeViewModel, showSheet : MutableState<Boolean>) {
Card(shape = MaterialTheme.shapes.medium,
@ -291,8 +286,8 @@ class HomeViews {
Row {
if(!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000")
{
var iconSource = MainActivity.AppPath + "/iconCache/" + gameModel.iconCache
var imageFile = File(iconSource)
val iconSource = MainActivity.AppPath + "/iconCache/" + gameModel.iconCache
val imageFile = File(iconSource)
if(imageFile.exists()) {
val size = ImageSize / Resources.getSystem().displayMetrics.density
AsyncImage(model = imageFile,
@ -320,7 +315,7 @@ class HomeViews {
@Composable
fun NotAvailableIcon() {
var size = ImageSize / Resources.getSystem().displayMetrics.density
val size = ImageSize / Resources.getSystem().displayMetrics.density
Icon(
Icons.Filled.Add,
contentDescription = "Options",

View File

@ -1,6 +1,5 @@
package org.ryujinx.android.views
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -73,7 +72,7 @@ class MainView {
Box(modifier = Modifier.fillMaxSize()) {
GameStats(mainViewModel)
var ryujinxNative = RyujinxNative()
val ryujinxNative = RyujinxNative()
// touch surface
Surface(color = Color.Transparent, modifier = Modifier
@ -82,27 +81,30 @@ class MainView {
.pointerInput(Unit) {
awaitPointerEventScope {
while (true) {
Thread.sleep(2);
Thread.sleep(2)
val event = awaitPointerEvent()
if(controller.isVisible)
continue
var change = event
val change = event
.component1()
.firstOrNull()
change?.apply {
var position = this.position
val position = this.position
if (event.type == PointerEventType.Press) {
when (event.type) {
PointerEventType.Press -> {
ryujinxNative.inputSetTouchPoint(
position.x.roundToInt(),
position.y.roundToInt()
)
} else if (event.type == PointerEventType.Release) {
}
PointerEventType.Release -> {
ryujinxNative.inputReleaseTouchPoint()
} else if (event.type == PointerEventType.Move) {
}
PointerEventType.Move -> {
ryujinxNative.inputSetTouchPoint(
position.x.roundToInt(),
position.y.roundToInt()
@ -112,6 +114,7 @@ class MainView {
}
}
}
}
}) {
}
controller.Compose(mainViewModel.activity.lifecycleScope, mainViewModel.activity.lifecycle)
@ -128,7 +131,7 @@ class MainView {
}
@Composable
fun rememberVideogameAsset(): ImageVector {
var primaryColor = MaterialTheme.colorScheme.primary
val primaryColor = MaterialTheme.colorScheme.primary
return remember {
ImageVector.Builder(
name = "videogame_asset",
@ -224,20 +227,20 @@ class MainView {
@Composable
fun GameStats(mainViewModel: MainViewModel){
var fifo = remember {
val fifo = remember {
mutableStateOf(0.0)
}
var gameFps = remember {
val gameFps = remember {
mutableStateOf(0.0)
}
var gameTime = remember {
val gameTime = remember {
mutableStateOf(0.0)
}
Surface(modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.surface.copy(0.4f)) {
Column {
var gameTimeVal = 0.0;
var gameTimeVal = 0.0
if (!gameTime.value.isInfinite())
gameTimeVal = gameTime.value
Text(text = "${String.format("%.3f", fifo.value)} %")

View File

@ -13,36 +13,24 @@ import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -54,7 +42,6 @@ import androidx.compose.ui.draw.rotate
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import org.ryujinx.android.viewmodels.SettingsViewModel
import org.ryujinx.android.viewmodels.VulkanDriverViewModel
class SettingViews {
companion object {
@ -63,38 +50,38 @@ class SettingViews {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Main(settingsViewModel: SettingsViewModel) {
var loaded = remember {
val loaded = remember {
mutableStateOf(false)
}
var isHostMapped = remember {
val isHostMapped = remember {
mutableStateOf(false)
}
var useNce = remember {
val useNce = remember {
mutableStateOf(false)
}
var enableVsync = remember {
val enableVsync = remember {
mutableStateOf(false)
}
var enableDocked = remember {
val enableDocked = remember {
mutableStateOf(false)
}
var enablePtc = remember {
val enablePtc = remember {
mutableStateOf(false)
}
var ignoreMissingServices = remember {
val ignoreMissingServices = remember {
mutableStateOf(false)
}
var enableShaderCache = remember {
val enableShaderCache = remember {
mutableStateOf(false)
}
var enableTextureRecompression = remember {
val enableTextureRecompression = remember {
mutableStateOf(false)
}
var resScale = remember {
val resScale = remember {
mutableStateOf(1f)
}
var useVirtualController = remember {
val useVirtualController = remember {
mutableStateOf(true)
}
@ -470,8 +457,8 @@ class SettingViews {
title: String,
content: @Composable () -> Unit
) {
var expanded = false
var mutableExpanded = remember {
val expanded = false
val mutableExpanded = remember {
mutableStateOf(expanded)
}
val transitionState = remember {

View File

@ -3,11 +3,9 @@ package org.ryujinx.android.views
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
@ -35,7 +33,7 @@ class TitleUpdateViews {
fun Main(titleId: String, name: String, openDialog: MutableState<Boolean>) {
val viewModel = TitleUpdateViewModel(titleId)
var selected = remember { mutableStateOf(0) }
val selected = remember { mutableStateOf(0) }
viewModel.data?.apply {
selected.value = paths.indexOf(this.selected) + 1
}
@ -67,14 +65,14 @@ class TitleUpdateViews {
)
}
var paths = remember {
val paths = remember {
mutableStateListOf<String>()
}
viewModel.setPaths(paths)
var index = 1
for (path in paths) {
var i = index
val i = index
Row(modifier = Modifier.padding(8.dp)) {
RadioButton(
selected = (selected.value == i),