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

@ -37,4 +37,4 @@
</activity> </activity>
</application> </application>
</manifest> </manifest>

View File

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

View File

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

View File

@ -34,12 +34,16 @@ class Helpers {
val split = docId.split(":".toRegex()).toTypedArray() val split = docId.split(":".toRegex()).toTypedArray()
val type = split[0] val type = split[0]
var contentUri: Uri? = null var contentUri: Uri? = null
if ("image" == type) { when (type) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI "image" -> {
} else if ("video" == type) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI }
} else if ("audio" == type) { "video" -> {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
}
"audio" -> {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
} }
val selection = "_id=?" val selection = "_id=?"
val selectionArgs = arrayOf(split[1]) val selectionArgs = arrayOf(split[1])
@ -58,13 +62,13 @@ class Helpers {
val column = "_data" val column = "_data"
val projection = arrayOf(column) val projection = arrayOf(column)
try { 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()) { if (cursor != null && cursor.moveToFirst()) {
val column_index: Int = cursor.getColumnIndexOrThrow(column) val column_index: Int = cursor.getColumnIndexOrThrow(column)
return cursor.getString(column_index) return cursor.getString(column_index)
} }
} finally { } finally {
if (cursor != null) cursor.close() cursor?.close()
} }
return null return null
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,7 @@
package org.ryujinx.android.viewmodels package org.ryujinx.android.viewmodels
import android.R.string
import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.ParcelFileDescriptor
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import com.anggrayudi.storage.file.extension import com.anggrayudi.storage.file.extension
import org.ryujinx.android.Helpers import org.ryujinx.android.Helpers
@ -22,8 +19,8 @@ class GameModel(var file: DocumentFile, val context: Context) {
init { init {
fileName = file.name fileName = file.name
var absPath = getPath() val absPath = getPath()
var gameInfo = RyujinxNative().deviceGetGameInfoFromPath(absPath ?: "") val gameInfo = RyujinxNative().deviceGetGameInfoFromPath(absPath ?: "")
fileSize = gameInfo.FileSize fileSize = gameInfo.FileSize
titleId = gameInfo.TitleId titleId = gameInfo.TitleId
@ -37,7 +34,7 @@ class GameModel(var file: DocumentFile, val context: Context) {
var uri = file.uri var uri = file.uri
if (uri.scheme != "file") if (uri.scheme != "file")
uri = Uri.parse("file://" + Helpers.getPath(context, file.uri)) uri = Uri.parse("file://" + Helpers.getPath(context, file.uri))
return uri.path; return uri.path
} }
fun getIsXci() : Boolean { 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.DocumentFileType
import com.anggrayudi.storage.file.FileFullPath import com.anggrayudi.storage.file.FileFullPath
import com.anggrayudi.storage.file.extension import com.anggrayudi.storage.file.extension
import com.anggrayudi.storage.file.fullName
import com.anggrayudi.storage.file.getAbsolutePath import com.anggrayudi.storage.file.getAbsolutePath
import com.anggrayudi.storage.file.search import com.anggrayudi.storage.file.search
import org.ryujinx.android.MainActivity import org.ryujinx.android.MainActivity
@ -20,7 +19,7 @@ class HomeViewModel(
private var gameList: SnapshotStateList<GameModel>? = null private var gameList: SnapshotStateList<GameModel>? = null
private var loadedCache: List<GameModel> = listOf() private var loadedCache: List<GameModel> = listOf()
private var gameFolderPath: DocumentFile? = null private var gameFolderPath: DocumentFile? = null
private var sharedPref: SharedPreferences? = null; private var sharedPref: SharedPreferences? = null
init { init {
if (activity != null) { if (activity != null) {
@ -28,15 +27,15 @@ class HomeViewModel(
activity.storageHelper!!.onFolderSelected = { requestCode, folder -> activity.storageHelper!!.onFolderSelected = { requestCode, folder ->
run { run {
gameFolderPath = folder gameFolderPath = folder
var p = folder.getAbsolutePath(activity!!) val p = folder.getAbsolutePath(activity!!)
var editor = sharedPref?.edit() val editor = sharedPref?.edit()
editor?.putString("gameFolder", p); editor?.putString("gameFolder", p)
editor?.apply() editor?.apply()
reloadGameList() reloadGameList()
} }
} }
var savedFolder = sharedPref?.getString("gameFolder", "") ?: "" val savedFolder = sharedPref?.getString("gameFolder", "") ?: ""
if (savedFolder.isNotEmpty()) { if (savedFolder.isNotEmpty()) {
try { try {
@ -56,26 +55,26 @@ class HomeViewModel(
} }
fun openGameFolder() { fun openGameFolder() {
var path = sharedPref?.getString("gameFolder", "") ?: "" val path = sharedPref?.getString("gameFolder", "") ?: ""
if (path.isNullOrEmpty()) if (path.isEmpty())
activity?.storageHelper?.storage?.openFolderPicker(); activity?.storageHelper?.storage?.openFolderPicker()
else else
activity?.storageHelper?.storage?.openFolderPicker( activity?.storageHelper?.storage?.openFolderPicker(
activity!!.storageHelper!!.storage.requestCodeFolderPicker, activity.storageHelper!!.storage.requestCodeFolderPicker,
FileFullPath(activity, path) FileFullPath(activity, path)
) )
} }
fun reloadGameList() { fun reloadGameList() {
var storage = activity?.storageHelper ?: return 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)) { for (file in folder.search(false, DocumentFileType.FILE)) {
if (file.extension == "xci" || file.extension == "nsp") if (file.extension == "xci" || file.extension == "nsp")
activity?.let { activity.let {
files.add(GameModel(file, it)) files.add(GameModel(file, it))
} }
} }
@ -91,7 +90,7 @@ class HomeViewModel(
} }
fun setViewList(list: SnapshotStateList<GameModel>) { fun setViewList(list: SnapshotStateList<GameModel>) {
gameList = list; gameList = list
applyFilter() applyFilter()
} }
} }

View File

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

View File

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

View File

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

View File

@ -12,22 +12,22 @@ class VulkanDriverViewModel(val activity: MainActivity) {
var selected: String = "" var selected: String = ""
companion object { companion object {
val DriverRequestCode: Int = 1003 const val DriverRequestCode: Int = 1003
const val DriverFolder: String = "drivers" const val DriverFolder: String = "drivers"
} }
private fun getAppPath() : String { private fun getAppPath() : String {
var appPath = var appPath =
(MainActivity.AppPath ?: activity.getExternalFilesDir(null)?.absolutePath ?: ""); MainActivity.AppPath
appPath += "/" appPath += "/"
return appPath return appPath
} }
fun ensureDriverPath() : File { fun ensureDriverPath() : File {
var driverPath = getAppPath() + DriverFolder val driverPath = getAppPath() + DriverFolder
var driverFolder = File(driverPath) val driverFolder = File(driverPath)
if(!driverFolder.exists()) if(!driverFolder.exists())
driverFolder.mkdirs() driverFolder.mkdirs()
@ -36,13 +36,13 @@ class VulkanDriverViewModel(val activity: MainActivity) {
} }
fun getAvailableDrivers() : MutableList<DriverMetadata> { 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()){ if(selectedDriverFile.exists()){
selected = selectedDriverFile.readText() selected = selectedDriverFile.readText()
@ -52,16 +52,16 @@ class VulkanDriverViewModel(val activity: MainActivity) {
} }
} }
var gson = Gson() val gson = Gson()
for (folder in folders){ for (folder in folders){
if(folder.isDirectory() && folder.parent == driverFolder.absolutePath){ if(folder.isDirectory() && folder.parent == driverFolder.absolutePath){
var meta = File(folder.absolutePath + "/meta.json") val meta = File(folder.absolutePath + "/meta.json")
if(meta.exists()){ 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()) { if(metadata.name.isNotEmpty()) {
var driver = folder.absolutePath + "/${metadata.libraryName}" val driver = folder.absolutePath + "/${metadata.libraryName}"
metadata.driverPath = driver metadata.driverPath = driver
if (File(driver).exists()) if (File(driver).exists())
drivers.add(metadata) drivers.add(metadata)
@ -74,18 +74,17 @@ class VulkanDriverViewModel(val activity: MainActivity) {
} }
fun saveSelected() { fun saveSelected() {
var driverFolder = ensureDriverPath() val driverFolder = ensureDriverPath()
var selectedDriverFile = File(driverFolder.absolutePath + "/selected") val selectedDriverFile = File(driverFolder.absolutePath + "/selected")
selectedDriverFile.writeText(selected) selectedDriverFile.writeText(selected)
} }
fun removeSelected(){ fun removeSelected(){
if(selected.isNotEmpty()){ if(selected.isNotEmpty()){
var sel = File(selected) val sel = File(selected)
if(sel.exists()) { if(sel.exists()) {
var parent = sel.parentFile sel.parentFile?.deleteRecursively()
parent.deleteRecursively()
} }
selected = "" selected = ""
@ -96,20 +95,20 @@ class VulkanDriverViewModel(val activity: MainActivity) {
fun add(refresh: MutableState<Boolean>) { fun add(refresh: MutableState<Boolean>) {
activity.storageHelper?.apply { activity.storageHelper?.apply {
var callBack = this.onFileSelected val callBack = this.onFileSelected
onFileSelected = { requestCode, files -> onFileSelected = { requestCode, files ->
run { run {
onFileSelected = callBack onFileSelected = callBack
if(requestCode == DriverRequestCode) if(requestCode == DriverRequestCode)
{ {
var file = files.firstOrNull() val file = files.firstOrNull()
file?.apply { file?.apply {
var path = Helpers.getPath(storage.context, file.uri) val path = Helpers.getPath(storage.context, file.uri)
if(!path.isNullOrEmpty()){ if(!path.isNullOrEmpty()){
var name = file.name?.removeSuffix("." + file.extension) ?: "" val name = file.name?.removeSuffix("." + file.extension) ?: ""
var driverFolder = ensureDriverPath() val driverFolder = ensureDriverPath()
var extractionFolder = File(driverFolder.absolutePath + "/${name}") val extractionFolder = File(driverFolder.absolutePath + "/${name}")
extractionFolder.mkdirs() extractionFolder.mkdirs()
ZipFile(path)?.use { zip -> ZipFile(path)?.use { zip ->
zip.entries().asSequence().forEach { entry -> zip.entries().asSequence().forEach { entry ->
@ -118,7 +117,7 @@ class VulkanDriverViewModel(val activity: MainActivity) {
val filePath = extractionFolder.absolutePath + File.separator + entry.name val filePath = extractionFolder.absolutePath + File.separator + entry.name
if (!entry.isDirectory) { if (!entry.isDirectory) {
var length = input.available() val length = input.available()
val bytesIn = ByteArray(length) val bytesIn = ByteArray(length)
input.read(bytesIn) input.read(bytesIn)
File(filePath).writeBytes(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.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.width
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth 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.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogWindowProvider import androidx.compose.ui.window.DialogWindowProvider
import androidx.compose.ui.window.Popup
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import androidx.navigation.NavController
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import coil.compose.AsyncImage import coil.compose.AsyncImage
import org.ryujinx.android.MainActivity import org.ryujinx.android.MainActivity
@ -78,11 +73,11 @@ class HomeViews {
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun MainTopBar(navController: NavHostController) { fun MainTopBar(navController: NavHostController) {
var topBarSize = remember { val topBarSize = remember {
mutableStateOf(0) mutableStateOf(0)
} }
Column { Column {
var showOptionsPopup = remember { val showOptionsPopup = remember {
mutableStateOf(false) mutableStateOf(false)
} }
TopAppBar( TopAppBar(
@ -116,7 +111,7 @@ class HomeViews {
actions = { actions = {
IconButton( IconButton(
onClick = { onClick = {
showOptionsPopup.value = true; showOptionsPopup.value = true
} }
) { ) {
Icon( Icon(
@ -171,7 +166,7 @@ class HomeViews {
fun Home(viewModel: HomeViewModel = HomeViewModel(), navController: NavHostController? = null) { fun Home(viewModel: HomeViewModel = HomeViewModel(), navController: NavHostController? = null) {
val sheetState = rememberModalBottomSheetState() val sheetState = rememberModalBottomSheetState()
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var showBottomSheet = remember { mutableStateOf(false) } val showBottomSheet = remember { mutableStateOf(false) }
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@ -195,7 +190,7 @@ class HomeViews {
) { contentPadding -> ) { contentPadding ->
Box(modifier = Modifier.padding(contentPadding)) { Box(modifier = Modifier.padding(contentPadding)) {
var list = remember { val list = remember {
mutableStateListOf<GameModel>() mutableStateListOf<GameModel>()
} }
viewModel.setViewList(list) viewModel.setViewList(list)
@ -227,8 +222,8 @@ class HomeViews {
shape = MaterialTheme.shapes.large, shape = MaterialTheme.shapes.large,
tonalElevation = AlertDialogDefaults.TonalElevation tonalElevation = AlertDialogDefaults.TonalElevation
) { ) {
var titleId = viewModel.mainViewModel?.selected?.titleId ?: "" val titleId = viewModel.mainViewModel?.selected?.titleId ?: ""
var name = viewModel.mainViewModel?.selected?.titleName ?: "" val name = viewModel.mainViewModel?.selected?.titleName ?: ""
TitleUpdateViews.Main(titleId, name, openDialog) TitleUpdateViews.Main(titleId, name, openDialog)
} }
@ -267,7 +262,7 @@ class HomeViews {
} }
} }
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun GameItem(gameModel: GameModel, viewModel: HomeViewModel, showSheet : MutableState<Boolean>) { fun GameItem(gameModel: GameModel, viewModel: HomeViewModel, showSheet : MutableState<Boolean>) {
Card(shape = MaterialTheme.shapes.medium, Card(shape = MaterialTheme.shapes.medium,
@ -291,8 +286,8 @@ class HomeViews {
Row { Row {
if(!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000") if(!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000")
{ {
var iconSource = MainActivity.AppPath + "/iconCache/" + gameModel.iconCache val iconSource = MainActivity.AppPath + "/iconCache/" + gameModel.iconCache
var imageFile = File(iconSource) val imageFile = File(iconSource)
if(imageFile.exists()) { if(imageFile.exists()) {
val size = ImageSize / Resources.getSystem().displayMetrics.density val size = ImageSize / Resources.getSystem().displayMetrics.density
AsyncImage(model = imageFile, AsyncImage(model = imageFile,
@ -320,7 +315,7 @@ class HomeViews {
@Composable @Composable
fun NotAvailableIcon() { fun NotAvailableIcon() {
var size = ImageSize / Resources.getSystem().displayMetrics.density val size = ImageSize / Resources.getSystem().displayMetrics.density
Icon( Icon(
Icons.Filled.Add, Icons.Filled.Add,
contentDescription = "Options", contentDescription = "Options",

View File

@ -1,6 +1,5 @@
package org.ryujinx.android.views package org.ryujinx.android.views
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -73,7 +72,7 @@ class MainView {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
GameStats(mainViewModel) GameStats(mainViewModel)
var ryujinxNative = RyujinxNative() val ryujinxNative = RyujinxNative()
// touch surface // touch surface
Surface(color = Color.Transparent, modifier = Modifier Surface(color = Color.Transparent, modifier = Modifier
@ -82,32 +81,36 @@ class MainView {
.pointerInput(Unit) { .pointerInput(Unit) {
awaitPointerEventScope { awaitPointerEventScope {
while (true) { while (true) {
Thread.sleep(2); Thread.sleep(2)
val event = awaitPointerEvent() val event = awaitPointerEvent()
if(controller.isVisible) if(controller.isVisible)
continue continue
var change = event val change = event
.component1() .component1()
.firstOrNull() .firstOrNull()
change?.apply { change?.apply {
var position = this.position val position = this.position
if (event.type == PointerEventType.Press) { when (event.type) {
ryujinxNative.inputSetTouchPoint( PointerEventType.Press -> {
position.x.roundToInt(), ryujinxNative.inputSetTouchPoint(
position.y.roundToInt() position.x.roundToInt(),
) position.y.roundToInt()
} else if (event.type == PointerEventType.Release) { )
ryujinxNative.inputReleaseTouchPoint() }
PointerEventType.Release -> {
ryujinxNative.inputReleaseTouchPoint()
} else if (event.type == PointerEventType.Move) { }
ryujinxNative.inputSetTouchPoint( PointerEventType.Move -> {
position.x.roundToInt(), ryujinxNative.inputSetTouchPoint(
position.y.roundToInt() position.x.roundToInt(),
) position.y.roundToInt()
)
}
} }
} }
} }
@ -128,7 +131,7 @@ class MainView {
} }
@Composable @Composable
fun rememberVideogameAsset(): ImageVector { fun rememberVideogameAsset(): ImageVector {
var primaryColor = MaterialTheme.colorScheme.primary val primaryColor = MaterialTheme.colorScheme.primary
return remember { return remember {
ImageVector.Builder( ImageVector.Builder(
name = "videogame_asset", name = "videogame_asset",
@ -224,20 +227,20 @@ class MainView {
@Composable @Composable
fun GameStats(mainViewModel: MainViewModel){ fun GameStats(mainViewModel: MainViewModel){
var fifo = remember { val fifo = remember {
mutableStateOf(0.0) mutableStateOf(0.0)
} }
var gameFps = remember { val gameFps = remember {
mutableStateOf(0.0) mutableStateOf(0.0)
} }
var gameTime = remember { val gameTime = remember {
mutableStateOf(0.0) mutableStateOf(0.0)
} }
Surface(modifier = Modifier.padding(16.dp), Surface(modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.surface.copy(0.4f)) { color = MaterialTheme.colorScheme.surface.copy(0.4f)) {
Column { Column {
var gameTimeVal = 0.0; var gameTimeVal = 0.0
if (!gameTime.value.isInfinite()) if (!gameTime.value.isInfinite())
gameTimeVal = gameTime.value gameTimeVal = gameTime.value
Text(text = "${String.format("%.3f", fifo.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.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding 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.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.KeyboardArrowUp 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.Card
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider import androidx.compose.material3.Slider
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue 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.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.ryujinx.android.viewmodels.SettingsViewModel import org.ryujinx.android.viewmodels.SettingsViewModel
import org.ryujinx.android.viewmodels.VulkanDriverViewModel
class SettingViews { class SettingViews {
companion object { companion object {
@ -63,38 +50,38 @@ class SettingViews {
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun Main(settingsViewModel: SettingsViewModel) { fun Main(settingsViewModel: SettingsViewModel) {
var loaded = remember { val loaded = remember {
mutableStateOf(false) mutableStateOf(false)
} }
var isHostMapped = remember { val isHostMapped = remember {
mutableStateOf(false) mutableStateOf(false)
} }
var useNce = remember { val useNce = remember {
mutableStateOf(false) mutableStateOf(false)
} }
var enableVsync = remember { val enableVsync = remember {
mutableStateOf(false) mutableStateOf(false)
} }
var enableDocked = remember { val enableDocked = remember {
mutableStateOf(false) mutableStateOf(false)
} }
var enablePtc = remember { val enablePtc = remember {
mutableStateOf(false) mutableStateOf(false)
} }
var ignoreMissingServices = remember { val ignoreMissingServices = remember {
mutableStateOf(false) mutableStateOf(false)
} }
var enableShaderCache = remember { val enableShaderCache = remember {
mutableStateOf(false) mutableStateOf(false)
} }
var enableTextureRecompression = remember { val enableTextureRecompression = remember {
mutableStateOf(false) mutableStateOf(false)
} }
var resScale = remember { val resScale = remember {
mutableStateOf(1f) mutableStateOf(1f)
} }
var useVirtualController = remember { val useVirtualController = remember {
mutableStateOf(true) mutableStateOf(true)
} }
@ -470,8 +457,8 @@ class SettingViews {
title: String, title: String,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
var expanded = false val expanded = false
var mutableExpanded = remember { val mutableExpanded = remember {
mutableStateOf(expanded) mutableStateOf(expanded)
} }
val transitionState = remember { 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.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
@ -35,7 +33,7 @@ class TitleUpdateViews {
fun Main(titleId: String, name: String, openDialog: MutableState<Boolean>) { fun Main(titleId: String, name: String, openDialog: MutableState<Boolean>) {
val viewModel = TitleUpdateViewModel(titleId) val viewModel = TitleUpdateViewModel(titleId)
var selected = remember { mutableStateOf(0) } val selected = remember { mutableStateOf(0) }
viewModel.data?.apply { viewModel.data?.apply {
selected.value = paths.indexOf(this.selected) + 1 selected.value = paths.indexOf(this.selected) + 1
} }
@ -67,14 +65,14 @@ class TitleUpdateViews {
) )
} }
var paths = remember { val paths = remember {
mutableStateListOf<String>() mutableStateListOf<String>()
} }
viewModel.setPaths(paths) viewModel.setPaths(paths)
var index = 1 var index = 1
for (path in paths) { for (path in paths) {
var i = index val i = index
Row(modifier = Modifier.padding(8.dp)) { Row(modifier = Modifier.padding(8.dp)) {
RadioButton( RadioButton(
selected = (selected.value == i), selected = (selected.value == i),