android: Apply ktlint codestyle
This commit is contained in:
		@@ -14,18 +14,18 @@ import android.widget.TextView
 | 
			
		||||
import androidx.annotation.Keep
 | 
			
		||||
import androidx.fragment.app.DialogFragment
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import java.lang.ref.WeakReference
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
 | 
			
		||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil.exists
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.Log.error
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.Log.verbose
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.Log.warning
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
 | 
			
		||||
import java.lang.ref.WeakReference
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class which contains methods that interact
 | 
			
		||||
@@ -76,7 +76,9 @@ object NativeLibrary {
 | 
			
		||||
    fun openContentUri(path: String?, openmode: String?): Int {
 | 
			
		||||
        return if (isNativePath(path!!)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.openContentUri(path, openmode)
 | 
			
		||||
        } else openContentUri(appContext, path, openmode)
 | 
			
		||||
        } else {
 | 
			
		||||
            openContentUri(appContext, path, openmode)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Keep
 | 
			
		||||
@@ -84,7 +86,9 @@ object NativeLibrary {
 | 
			
		||||
    fun getSize(path: String?): Long {
 | 
			
		||||
        return if (isNativePath(path!!)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.getFileSize(path)
 | 
			
		||||
        } else getFileSize(appContext, path)
 | 
			
		||||
        } else {
 | 
			
		||||
            getFileSize(appContext, path)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Keep
 | 
			
		||||
@@ -92,7 +96,9 @@ object NativeLibrary {
 | 
			
		||||
    fun exists(path: String?): Boolean {
 | 
			
		||||
        return if (isNativePath(path!!)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.exists(path)
 | 
			
		||||
        } else exists(appContext, path)
 | 
			
		||||
        } else {
 | 
			
		||||
            exists(appContext, path)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Keep
 | 
			
		||||
@@ -100,7 +106,9 @@ object NativeLibrary {
 | 
			
		||||
    fun isDirectory(path: String?): Boolean {
 | 
			
		||||
        return if (isNativePath(path!!)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.isDirectory(path)
 | 
			
		||||
        } else isDirectory(appContext, path)
 | 
			
		||||
        } else {
 | 
			
		||||
            isDirectory(appContext, path)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -454,7 +462,9 @@ object NativeLibrary {
 | 
			
		||||
                    Html.FROM_HTML_MODE_LEGACY
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationActivity.finish() }
 | 
			
		||||
            .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
 | 
			
		||||
                emulationActivity.finish()
 | 
			
		||||
            }
 | 
			
		||||
            .setOnDismissListener { emulationActivity.finish() }
 | 
			
		||||
        emulationActivity.runOnUiThread {
 | 
			
		||||
            val alert = builder.create()
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,12 @@ import android.app.Application
 | 
			
		||||
import android.app.NotificationChannel
 | 
			
		||||
import android.app.NotificationManager
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import java.io.File
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DocumentsTree
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
 | 
			
		||||
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
 | 
			
		||||
 | 
			
		||||
class YuzuApplication : Application() {
 | 
			
		||||
    private fun createNotificationChannels() {
 | 
			
		||||
@@ -21,7 +21,9 @@ class YuzuApplication : Application() {
 | 
			
		||||
            getString(R.string.emulation_notification_channel_name),
 | 
			
		||||
            NotificationManager.IMPORTANCE_LOW
 | 
			
		||||
        )
 | 
			
		||||
        emulationChannel.description = getString(R.string.emulation_notification_channel_description)
 | 
			
		||||
        emulationChannel.description = getString(
 | 
			
		||||
            R.string.emulation_notification_channel_description
 | 
			
		||||
        )
 | 
			
		||||
        emulationChannel.setSound(null, null)
 | 
			
		||||
        emulationChannel.vibrationPattern = null
 | 
			
		||||
 | 
			
		||||
@@ -48,7 +50,7 @@ class YuzuApplication : Application() {
 | 
			
		||||
        GpuDriverHelper.initializeDriverParameters(applicationContext)
 | 
			
		||||
        NativeLibrary.logDeviceInfo()
 | 
			
		||||
 | 
			
		||||
        createNotificationChannels();
 | 
			
		||||
        createNotificationChannels()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ import androidx.core.view.WindowCompat
 | 
			
		||||
import androidx.core.view.WindowInsetsCompat
 | 
			
		||||
import androidx.core.view.WindowInsetsControllerCompat
 | 
			
		||||
import androidx.navigation.fragment.NavHostFragment
 | 
			
		||||
import kotlin.math.roundToInt
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
 | 
			
		||||
@@ -45,7 +46,6 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.InputHandler
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.NfcReader
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.ThemeHelper
 | 
			
		||||
import kotlin.math.roundToInt
 | 
			
		||||
 | 
			
		||||
class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
    private lateinit var binding: ActivityEmulationBinding
 | 
			
		||||
@@ -256,7 +256,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder(): PictureInPictureParams.Builder {
 | 
			
		||||
    private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder():
 | 
			
		||||
        PictureInPictureParams.Builder {
 | 
			
		||||
        val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.int) {
 | 
			
		||||
            0 -> Rational(16, 9)
 | 
			
		||||
            1 -> Rational(4, 3)
 | 
			
		||||
@@ -267,7 +268,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
        return this.apply { aspectRatio?.let { setAspectRatio(it) } }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder(): PictureInPictureParams.Builder {
 | 
			
		||||
    private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder():
 | 
			
		||||
        PictureInPictureParams.Builder {
 | 
			
		||||
        val pictureInPictureActions: MutableList<RemoteAction> = mutableListOf()
 | 
			
		||||
        val pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
 | 
			
		||||
 | 
			
		||||
@@ -310,7 +312,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
        val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
 | 
			
		||||
            .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
 | 
			
		||||
            pictureInPictureParamsBuilder.setAutoEnterEnabled(BooleanSetting.PICTURE_IN_PICTURE.boolean)
 | 
			
		||||
            pictureInPictureParamsBuilder.setAutoEnterEnabled(
 | 
			
		||||
                BooleanSetting.PICTURE_IN_PICTURE.boolean
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        setPictureInPictureParams(pictureInPictureParamsBuilder.build())
 | 
			
		||||
    }
 | 
			
		||||
@@ -341,7 +345,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
 | 
			
		||||
        } else {
 | 
			
		||||
            try {
 | 
			
		||||
                unregisterReceiver(pictureInPictureReceiver)
 | 
			
		||||
            } catch (ignored : Exception) {
 | 
			
		||||
            } catch (ignored: Exception) {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,10 +28,9 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.CardGameBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.CardGameBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
import org.yuzu.yuzu_emu.model.GamesViewModel
 | 
			
		||||
 | 
			
		||||
class GameAdapter(private val activity: AppCompatActivity) :
 | 
			
		||||
@@ -60,7 +59,10 @@ class GameAdapter(private val activity: AppCompatActivity) :
 | 
			
		||||
    override fun onClick(view: View) {
 | 
			
		||||
        val holder = view.tag as GameViewHolder
 | 
			
		||||
 | 
			
		||||
        val gameExists = DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(holder.game.path))?.exists() == true
 | 
			
		||||
        val gameExists = DocumentFile.fromSingleUri(
 | 
			
		||||
            YuzuApplication.appContext,
 | 
			
		||||
            Uri.parse(holder.game.path)
 | 
			
		||||
        )?.exists() == true
 | 
			
		||||
        if (!gameExists) {
 | 
			
		||||
            Toast.makeText(
 | 
			
		||||
                YuzuApplication.appContext,
 | 
			
		||||
 
 | 
			
		||||
@@ -58,11 +58,12 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            when (option.titleId) {
 | 
			
		||||
                R.string.get_early_access -> binding.optionLayout.background =
 | 
			
		||||
                    ContextCompat.getDrawable(
 | 
			
		||||
                        binding.optionCard.context,
 | 
			
		||||
                        R.drawable.premium_background
 | 
			
		||||
                    )
 | 
			
		||||
                R.string.get_early_access ->
 | 
			
		||||
                    binding.optionLayout.background =
 | 
			
		||||
                        ContextCompat.getDrawable(
 | 
			
		||||
                            binding.optionCard.context,
 | 
			
		||||
                            R.drawable.premium_background
 | 
			
		||||
                        )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,10 +12,10 @@ import android.view.WindowInsets
 | 
			
		||||
import android.view.inputmethod.InputMethodManager
 | 
			
		||||
import androidx.annotation.Keep
 | 
			
		||||
import androidx.core.view.ViewCompat
 | 
			
		||||
import java.io.Serializable
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment
 | 
			
		||||
import java.io.Serializable
 | 
			
		||||
 | 
			
		||||
@Keep
 | 
			
		||||
object SoftwareKeyboard {
 | 
			
		||||
@@ -40,19 +40,22 @@ object SoftwareKeyboard {
 | 
			
		||||
        // There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
 | 
			
		||||
        val handler = Handler(Looper.myLooper()!!)
 | 
			
		||||
        val delayMs = 500
 | 
			
		||||
        handler.postDelayed(object : Runnable {
 | 
			
		||||
            override fun run() {
 | 
			
		||||
                val insets = ViewCompat.getRootWindowInsets(overlayView)
 | 
			
		||||
                val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
 | 
			
		||||
                if (isKeyboardVisible) {
 | 
			
		||||
                    handler.postDelayed(this, delayMs.toLong())
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
        handler.postDelayed(
 | 
			
		||||
            object : Runnable {
 | 
			
		||||
                override fun run() {
 | 
			
		||||
                    val insets = ViewCompat.getRootWindowInsets(overlayView)
 | 
			
		||||
                    val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
 | 
			
		||||
                    if (isKeyboardVisible) {
 | 
			
		||||
                        handler.postDelayed(this, delayMs.toLong())
 | 
			
		||||
                        return
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                // No longer visible, submit the result.
 | 
			
		||||
                NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
 | 
			
		||||
            }
 | 
			
		||||
        }, delayMs.toLong())
 | 
			
		||||
                    // No longer visible, submit the result.
 | 
			
		||||
                    NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            delayMs.toLong()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @JvmStatic
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,10 @@ object DiskShaderCacheProgress {
 | 
			
		||||
                emulationActivity.getString(R.string.loading),
 | 
			
		||||
                emulationActivity.getString(R.string.preparing_shaders)
 | 
			
		||||
            )
 | 
			
		||||
            fragment.show(emulationActivity.supportFragmentManager, ShaderProgressDialogFragment.TAG)
 | 
			
		||||
            fragment.show(
 | 
			
		||||
                emulationActivity.supportFragmentManager,
 | 
			
		||||
                ShaderProgressDialogFragment.TAG
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        synchronized(finishLock) { finishLock.wait() }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,9 @@ class ShaderProgressDialogFragment : DialogFragment() {
 | 
			
		||||
        shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg ->
 | 
			
		||||
            alertDialog.setMessage(msg)
 | 
			
		||||
        }
 | 
			
		||||
        synchronized(DiskShaderCacheProgress.finishLock) { DiskShaderCacheProgress.finishLock.notifyAll() }
 | 
			
		||||
        synchronized(DiskShaderCacheProgress.finishLock) {
 | 
			
		||||
            DiskShaderCacheProgress.finishLock.notifyAll()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroyView() {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,11 +13,11 @@ import android.os.ParcelFileDescriptor
 | 
			
		||||
import android.provider.DocumentsContract
 | 
			
		||||
import android.provider.DocumentsProvider
 | 
			
		||||
import android.webkit.MimeTypeMap
 | 
			
		||||
import java.io.*
 | 
			
		||||
import org.yuzu.yuzu_emu.BuildConfig
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.getPublicFilesDir
 | 
			
		||||
import java.io.*
 | 
			
		||||
 | 
			
		||||
class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
    private val baseDirectory: File
 | 
			
		||||
@@ -44,7 +44,7 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
            DocumentsContract.Document.COLUMN_SIZE
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        const val AUTHORITY : String = BuildConfig.APPLICATION_ID + ".user"
 | 
			
		||||
        const val AUTHORITY: String = BuildConfig.APPLICATION_ID + ".user"
 | 
			
		||||
        const val ROOT_ID: String = "root"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -58,7 +58,11 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
    private fun getFile(documentId: String): File {
 | 
			
		||||
        if (documentId.startsWith(ROOT_ID)) {
 | 
			
		||||
            val file = baseDirectory.resolve(documentId.drop(ROOT_ID.length + 1))
 | 
			
		||||
            if (!file.exists()) throw FileNotFoundException("${file.absolutePath} ($documentId) not found")
 | 
			
		||||
            if (!file.exists()) {
 | 
			
		||||
                throw FileNotFoundException(
 | 
			
		||||
                    "${file.absolutePath} ($documentId) not found"
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            return file
 | 
			
		||||
        } else {
 | 
			
		||||
            throw FileNotFoundException("'$documentId' is not in any known root")
 | 
			
		||||
@@ -80,7 +84,8 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
            add(DocumentsContract.Root.COLUMN_SUMMARY, null)
 | 
			
		||||
            add(
 | 
			
		||||
                DocumentsContract.Root.COLUMN_FLAGS,
 | 
			
		||||
                DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
 | 
			
		||||
                DocumentsContract.Root.FLAG_SUPPORTS_CREATE or
 | 
			
		||||
                    DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
 | 
			
		||||
            )
 | 
			
		||||
            add(DocumentsContract.Root.COLUMN_TITLE, context!!.getString(R.string.app_name))
 | 
			
		||||
            add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory))
 | 
			
		||||
@@ -127,11 +132,13 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (DocumentsContract.Document.MIME_TYPE_DIR == mimeType) {
 | 
			
		||||
                if (!newFile.mkdir())
 | 
			
		||||
                if (!newFile.mkdir()) {
 | 
			
		||||
                    throw IOException("Failed to create directory")
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (!newFile.createNewFile())
 | 
			
		||||
                if (!newFile.createNewFile()) {
 | 
			
		||||
                    throw IOException("Failed to create file")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: IOException) {
 | 
			
		||||
            throw FileNotFoundException("Couldn't create document '${newFile.path}': ${e.message}")
 | 
			
		||||
@@ -142,8 +149,9 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
 | 
			
		||||
    override fun deleteDocument(documentId: String?) {
 | 
			
		||||
        val file = getFile(documentId!!)
 | 
			
		||||
        if (!file.delete())
 | 
			
		||||
        if (!file.delete()) {
 | 
			
		||||
            throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun removeDocument(documentId: String, parentDocumentId: String?) {
 | 
			
		||||
@@ -151,38 +159,55 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
        val file = getFile(documentId)
 | 
			
		||||
 | 
			
		||||
        if (parent == file || file.parentFile == null || file.parentFile!! == parent) {
 | 
			
		||||
            if (!file.delete())
 | 
			
		||||
            if (!file.delete()) {
 | 
			
		||||
                throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun renameDocument(documentId: String?, displayName: String?): String {
 | 
			
		||||
        if (displayName == null)
 | 
			
		||||
            throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null")
 | 
			
		||||
        if (displayName == null) {
 | 
			
		||||
            throw FileNotFoundException(
 | 
			
		||||
                "Couldn't rename document '$documentId' as the new name is null"
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val sourceFile = getFile(documentId!!)
 | 
			
		||||
        val sourceParentFile = sourceFile.parentFile
 | 
			
		||||
            ?: throw FileNotFoundException("Couldn't rename document '$documentId' as it has no parent")
 | 
			
		||||
            ?: throw FileNotFoundException(
 | 
			
		||||
                "Couldn't rename document '$documentId' as it has no parent"
 | 
			
		||||
            )
 | 
			
		||||
        val destFile = sourceParentFile.resolve(displayName)
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (!sourceFile.renameTo(destFile))
 | 
			
		||||
                throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'")
 | 
			
		||||
            if (!sourceFile.renameTo(destFile)) {
 | 
			
		||||
                throw FileNotFoundException(
 | 
			
		||||
                    "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'"
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': ${e.message}")
 | 
			
		||||
            throw FileNotFoundException(
 | 
			
		||||
                "Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': " +
 | 
			
		||||
                    "${e.message}"
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return getDocumentId(destFile)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun copyDocument(
 | 
			
		||||
        sourceDocumentId: String, sourceParentDocumentId: String,
 | 
			
		||||
        sourceDocumentId: String,
 | 
			
		||||
        sourceParentDocumentId: String,
 | 
			
		||||
        targetParentDocumentId: String?
 | 
			
		||||
    ): String {
 | 
			
		||||
        if (!isChildDocument(sourceParentDocumentId, sourceDocumentId))
 | 
			
		||||
            throw FileNotFoundException("Couldn't copy document '$sourceDocumentId' as its parent is not '$sourceParentDocumentId'")
 | 
			
		||||
        if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) {
 | 
			
		||||
            throw FileNotFoundException(
 | 
			
		||||
                "Couldn't copy document '$sourceDocumentId' as its parent is not " +
 | 
			
		||||
                    "'$sourceParentDocumentId'"
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return copyDocument(sourceDocumentId, targetParentDocumentId)
 | 
			
		||||
    }
 | 
			
		||||
@@ -193,8 +218,13 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
        val newFile = parent.resolveWithoutConflict(oldFile.name)
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if (!(newFile.createNewFile() && newFile.setWritable(true) && newFile.setReadable(true)))
 | 
			
		||||
            if (!(
 | 
			
		||||
                newFile.createNewFile() && newFile.setWritable(true) &&
 | 
			
		||||
                    newFile.setReadable(true)
 | 
			
		||||
                )
 | 
			
		||||
            ) {
 | 
			
		||||
                throw IOException("Couldn't create new file")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            FileInputStream(oldFile).use { inStream ->
 | 
			
		||||
                FileOutputStream(newFile).use { outStream ->
 | 
			
		||||
@@ -209,12 +239,14 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun moveDocument(
 | 
			
		||||
        sourceDocumentId: String, sourceParentDocumentId: String?,
 | 
			
		||||
        sourceDocumentId: String,
 | 
			
		||||
        sourceParentDocumentId: String?,
 | 
			
		||||
        targetParentDocumentId: String?
 | 
			
		||||
    ): String {
 | 
			
		||||
        try {
 | 
			
		||||
            val newDocumentId = copyDocument(
 | 
			
		||||
                sourceDocumentId, sourceParentDocumentId!!,
 | 
			
		||||
                sourceDocumentId,
 | 
			
		||||
                sourceParentDocumentId!!,
 | 
			
		||||
                targetParentDocumentId
 | 
			
		||||
            )
 | 
			
		||||
            removeDocument(sourceDocumentId, sourceParentDocumentId)
 | 
			
		||||
@@ -245,24 +277,30 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
            add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId)
 | 
			
		||||
            add(
 | 
			
		||||
                DocumentsContract.Document.COLUMN_DISPLAY_NAME,
 | 
			
		||||
                if (localFile == baseDirectory) context!!.getString(R.string.app_name) else localFile.name
 | 
			
		||||
                if (localFile == baseDirectory) {
 | 
			
		||||
                    context!!.getString(R.string.app_name)
 | 
			
		||||
                } else {
 | 
			
		||||
                    localFile.name
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
            add(DocumentsContract.Document.COLUMN_SIZE, localFile.length())
 | 
			
		||||
            add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile))
 | 
			
		||||
            add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified())
 | 
			
		||||
            add(DocumentsContract.Document.COLUMN_FLAGS, flags)
 | 
			
		||||
            if (localFile == baseDirectory)
 | 
			
		||||
            if (localFile == baseDirectory) {
 | 
			
		||||
                add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_yuzu)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return cursor
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getTypeForFile(file: File): Any {
 | 
			
		||||
        return if (file.isDirectory)
 | 
			
		||||
        return if (file.isDirectory) {
 | 
			
		||||
            DocumentsContract.Document.MIME_TYPE_DIR
 | 
			
		||||
        else
 | 
			
		||||
        } else {
 | 
			
		||||
            getTypeForName(file.name)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getTypeForName(name: String): Any {
 | 
			
		||||
@@ -270,8 +308,9 @@ class DocumentProvider : DocumentsProvider() {
 | 
			
		||||
        if (lastDot >= 0) {
 | 
			
		||||
            val extension = name.substring(lastDot + 1)
 | 
			
		||||
            val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
 | 
			
		||||
            if (mime != null)
 | 
			
		||||
            if (mime != null) {
 | 
			
		||||
                return mime
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return "application/octect-stream"
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@
 | 
			
		||||
package org.yuzu.yuzu_emu.features.settings.model
 | 
			
		||||
 | 
			
		||||
import android.text.TextUtils
 | 
			
		||||
import java.util.*
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class Settings {
 | 
			
		||||
    private var gameId: String? = null
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@
 | 
			
		||||
package org.yuzu.yuzu_emu.features.settings.model.view
 | 
			
		||||
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
 | 
			
		||||
 | 
			
		||||
class SingleChoiceSetting(
 | 
			
		||||
    setting: AbstractIntSetting?,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,11 @@
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.features.settings.model.view
 | 
			
		||||
 | 
			
		||||
import kotlin.math.roundToInt
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.Log
 | 
			
		||||
import kotlin.math.roundToInt
 | 
			
		||||
 | 
			
		||||
class SliderSetting(
 | 
			
		||||
    setting: AbstractSetting?,
 | 
			
		||||
@@ -19,7 +17,7 @@ class SliderSetting(
 | 
			
		||||
    val max: Int,
 | 
			
		||||
    val units: String,
 | 
			
		||||
    val key: String? = null,
 | 
			
		||||
    val defaultValue: Int? = null,
 | 
			
		||||
    val defaultValue: Int? = null
 | 
			
		||||
) : SettingsItem(setting, titleId, descriptionId) {
 | 
			
		||||
    override val type = TYPE_SLIDER
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.features.settings.model.view
 | 
			
		||||
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
 | 
			
		||||
 | 
			
		||||
class StringSingleChoiceSetting(
 | 
			
		||||
    val key: String? = null,
 | 
			
		||||
@@ -22,7 +21,9 @@ class StringSingleChoiceSetting(
 | 
			
		||||
        if (valuesId == null) return null
 | 
			
		||||
        return if (index >= 0 && index < valuesId.size) {
 | 
			
		||||
            valuesId[index]
 | 
			
		||||
        } else ""
 | 
			
		||||
        } else {
 | 
			
		||||
            ""
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val selectedValue: String
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,6 @@
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.features.settings.model.view
 | 
			
		||||
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
 | 
			
		||||
 | 
			
		||||
class SubmenuSetting(
 | 
			
		||||
    titleId: Int,
 | 
			
		||||
    descriptionId: Int,
 | 
			
		||||
 
 | 
			
		||||
@@ -8,18 +8,18 @@ import android.content.Intent
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.view.Menu
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup.MarginLayoutParams
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.activity.OnBackPressedCallback
 | 
			
		||||
import androidx.activity.result.ActivityResultLauncher
 | 
			
		||||
import androidx.activity.viewModels
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import androidx.core.view.ViewCompat
 | 
			
		||||
import androidx.core.view.WindowCompat
 | 
			
		||||
import androidx.core.view.WindowInsetsCompat
 | 
			
		||||
import android.view.ViewGroup.MarginLayoutParams
 | 
			
		||||
import androidx.activity.OnBackPressedCallback
 | 
			
		||||
import androidx.activity.result.ActivityResultLauncher
 | 
			
		||||
import androidx.core.view.updatePadding
 | 
			
		||||
import com.google.android.material.color.MaterialColors
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
 | 
			
		||||
@@ -30,7 +30,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.*
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
 | 
			
		||||
class SettingsActivity : AppCompatActivity(), SettingsActivityView {
 | 
			
		||||
    private val presenter = SettingsActivityPresenter(this)
 | 
			
		||||
@@ -60,7 +59,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
 | 
			
		||||
        setSupportActionBar(binding.toolbarSettings)
 | 
			
		||||
        supportActionBar!!.setDisplayHomeAsUpEnabled(true)
 | 
			
		||||
 | 
			
		||||
        if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
 | 
			
		||||
        if (InsetsHelper.getSystemGestureType(applicationContext) !=
 | 
			
		||||
            InsetsHelper.GESTURE_NAVIGATION
 | 
			
		||||
        ) {
 | 
			
		||||
            binding.navigationBarShade.setBackgroundColor(
 | 
			
		||||
                ThemeHelper.getColorWithOpacity(
 | 
			
		||||
                    MaterialColors.getColor(
 | 
			
		||||
@@ -76,7 +77,8 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
 | 
			
		||||
            this,
 | 
			
		||||
            object : OnBackPressedCallback(true) {
 | 
			
		||||
                override fun handleOnBackPressed() = navigateBack()
 | 
			
		||||
            })
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        setInsets()
 | 
			
		||||
    }
 | 
			
		||||
@@ -149,11 +151,13 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
 | 
			
		||||
    private fun areSystemAnimationsEnabled(): Boolean {
 | 
			
		||||
        val duration = android.provider.Settings.Global.getFloat(
 | 
			
		||||
            contentResolver,
 | 
			
		||||
            android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, 1f
 | 
			
		||||
            android.provider.Settings.Global.ANIMATOR_DURATION_SCALE,
 | 
			
		||||
            1f
 | 
			
		||||
        )
 | 
			
		||||
        val transition = android.provider.Settings.Global.getFloat(
 | 
			
		||||
            contentResolver,
 | 
			
		||||
            android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, 1f
 | 
			
		||||
            android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE,
 | 
			
		||||
            1f
 | 
			
		||||
        )
 | 
			
		||||
        return duration != 0f && transition != 0f
 | 
			
		||||
    }
 | 
			
		||||
@@ -208,7 +212,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
 | 
			
		||||
        get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() {
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.frameContent) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.frameContent
 | 
			
		||||
        ) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
 | 
			
		||||
            view.updatePadding(
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,12 @@ package org.yuzu.yuzu_emu.features.settings.ui
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.text.TextUtils
 | 
			
		||||
import java.io.File
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.Log
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
 | 
			
		||||
    val settings: Settings get() = activityView.settings
 | 
			
		||||
@@ -46,9 +46,15 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
 | 
			
		||||
 | 
			
		||||
    private fun prepareDirectoriesIfNeeded() {
 | 
			
		||||
        val configFile =
 | 
			
		||||
            File(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
 | 
			
		||||
            File(
 | 
			
		||||
                "${DirectoryInitialization.userDirectory}/config/" +
 | 
			
		||||
                    "${SettingsFile.FILE_NAME_CONFIG}.ini"
 | 
			
		||||
            )
 | 
			
		||||
        if (!configFile.exists()) {
 | 
			
		||||
            Log.error(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
 | 
			
		||||
            Log.error(
 | 
			
		||||
                "${DirectoryInitialization.userDirectory}/config/" +
 | 
			
		||||
                    "${SettingsFile.FILE_NAME_CONFIG}.ini"
 | 
			
		||||
            )
 | 
			
		||||
            Log.error("yuzu config file could not be found!")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ import android.view.ViewGroup
 | 
			
		||||
import android.widget.TextView
 | 
			
		||||
import androidx.appcompat.app.AlertDialog
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import androidx.fragment.app.setFragmentResultListener
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import com.google.android.material.datepicker.MaterialDatePicker
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,10 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
			
		||||
        settingsAdapter = SettingsAdapter(this, requireActivity())
 | 
			
		||||
        val dividerDecoration = MaterialDividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
 | 
			
		||||
        val dividerDecoration = MaterialDividerItemDecoration(
 | 
			
		||||
            requireContext(),
 | 
			
		||||
            LinearLayoutManager.VERTICAL
 | 
			
		||||
        )
 | 
			
		||||
        dividerDecoration.isLastItemDecorated = false
 | 
			
		||||
        binding.listSettings.apply {
 | 
			
		||||
            adapter = settingsAdapter
 | 
			
		||||
@@ -99,7 +102,9 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() {
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.listSettings) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.listSettings
 | 
			
		||||
        ) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            view.updatePadding(bottom = insets.bottom)
 | 
			
		||||
            windowInsets
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ import android.content.SharedPreferences
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.text.TextUtils
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
 | 
			
		||||
@@ -236,7 +235,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
 | 
			
		||||
    private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
 | 
			
		||||
        settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics))
 | 
			
		||||
        sl.apply {
 | 
			
		||||
 | 
			
		||||
            add(
 | 
			
		||||
                SingleChoiceSetting(
 | 
			
		||||
                    IntSetting.RENDERER_ACCURACY,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,15 +4,15 @@
 | 
			
		||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
 | 
			
		||||
 | 
			
		||||
import android.view.View
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
 | 
			
		||||
import java.time.Instant
 | 
			
		||||
import java.time.ZoneId
 | 
			
		||||
import java.time.ZonedDateTime
 | 
			
		||||
import java.time.format.DateTimeFormatter
 | 
			
		||||
import java.time.format.FormatStyle
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
 | 
			
		||||
 | 
			
		||||
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
 | 
			
		||||
    SettingViewHolder(binding.root, adapter) {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.CompoundButton
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
 | 
			
		||||
 | 
			
		||||
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.features.settings.utils
 | 
			
		||||
 | 
			
		||||
import java.io.*
 | 
			
		||||
import java.util.*
 | 
			
		||||
import org.ini4j.Wini
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
@@ -13,8 +15,6 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.BiMap
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.Log
 | 
			
		||||
import java.io.*
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Contains static methods for interacting with .ini files in which settings are stored.
 | 
			
		||||
@@ -137,9 +137,12 @@ object SettingsFile {
 | 
			
		||||
            for (settingKey in sortedKeySet) {
 | 
			
		||||
                val setting = settings[settingKey]
 | 
			
		||||
                NativeLibrary.setUserSetting(
 | 
			
		||||
                    gameId, mapSectionNameFromIni(
 | 
			
		||||
                    gameId,
 | 
			
		||||
                    mapSectionNameFromIni(
 | 
			
		||||
                        section.name
 | 
			
		||||
                    ), setting!!.key, setting.valueAsString
 | 
			
		||||
                    ),
 | 
			
		||||
                    setting!!.key,
 | 
			
		||||
                    setting.valueAsString
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -148,13 +151,17 @@ object SettingsFile {
 | 
			
		||||
    private fun mapSectionNameFromIni(generalSectionName: String): String? {
 | 
			
		||||
        return if (sectionsMap.getForward(generalSectionName) != null) {
 | 
			
		||||
            sectionsMap.getForward(generalSectionName)
 | 
			
		||||
        } else generalSectionName
 | 
			
		||||
        } else {
 | 
			
		||||
            generalSectionName
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun mapSectionNameToIni(generalSectionName: String): String {
 | 
			
		||||
        return if (sectionsMap.getBackward(generalSectionName) != null) {
 | 
			
		||||
            sectionsMap.getBackward(generalSectionName).toString()
 | 
			
		||||
        } else generalSectionName
 | 
			
		||||
        } else {
 | 
			
		||||
            generalSectionName
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getSettingsFile(fileName: String): File {
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,11 @@ class AboutFragment : Fragment() {
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        binding.buttonContributors.setOnClickListener { openLink(getString(R.string.contributors_link)) }
 | 
			
		||||
        binding.buttonContributors.setOnClickListener {
 | 
			
		||||
            openLink(
 | 
			
		||||
                getString(R.string.contributors_link)
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        binding.buttonLicenses.setOnClickListener {
 | 
			
		||||
            exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
 | 
			
		||||
            binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
 | 
			
		||||
@@ -101,7 +105,9 @@ class AboutFragment : Fragment() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() =
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.root
 | 
			
		||||
        ) { _: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,11 @@ class EarlyAccessFragment : Fragment() {
 | 
			
		||||
            parentFragmentManager.primaryNavigationFragment?.findNavController()?.popBackStack()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        binding.getEarlyAccessButton.setOnClickListener { openLink(getString(R.string.play_store_link)) }
 | 
			
		||||
        binding.getEarlyAccessButton.setOnClickListener {
 | 
			
		||||
            openLink(
 | 
			
		||||
                getString(R.string.play_store_link)
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setInsets()
 | 
			
		||||
    }
 | 
			
		||||
@@ -60,7 +64,9 @@ class EarlyAccessFragment : Fragment() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() =
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.root
 | 
			
		||||
        ) { _: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -83,7 +83,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            onReturnFromSettings = context.activityResultRegistry.register(
 | 
			
		||||
                "SettingsResult", ActivityResultContracts.StartActivityForResult()
 | 
			
		||||
                "SettingsResult",
 | 
			
		||||
                ActivityResultContracts.StartActivityForResult()
 | 
			
		||||
            ) {
 | 
			
		||||
                binding.surfaceEmulation.setAspectRatio(
 | 
			
		||||
                    when (IntSetting.RENDERER_ASPECT_RATIO.int) {
 | 
			
		||||
@@ -191,9 +192,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
            requireActivity(),
 | 
			
		||||
            object : OnBackPressedCallback(true) {
 | 
			
		||||
                override fun handleOnBackPressed() {
 | 
			
		||||
                    if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open()
 | 
			
		||||
                    if (binding.drawerLayout.isOpen) {
 | 
			
		||||
                        binding.drawerLayout.close()
 | 
			
		||||
                    } else {
 | 
			
		||||
                        binding.drawerLayout.open()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
 | 
			
		||||
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
 | 
			
		||||
@@ -312,8 +318,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
    private fun updateScreenLayout() {
 | 
			
		||||
        emulationActivity?.let {
 | 
			
		||||
            it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.int) {
 | 
			
		||||
                Settings.LayoutOption_MobileLandscape -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
 | 
			
		||||
                Settings.LayoutOption_MobilePortrait -> ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
 | 
			
		||||
                Settings.LayoutOption_MobileLandscape ->
 | 
			
		||||
                    ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
 | 
			
		||||
                Settings.LayoutOption_MobilePortrait ->
 | 
			
		||||
                    ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
 | 
			
		||||
                Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
 | 
			
		||||
                else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
 | 
			
		||||
            }
 | 
			
		||||
@@ -321,25 +329,30 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
        onConfigurationChanged(resources.configuration)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun updateFoldableLayout(emulationActivity: EmulationActivity, newLayoutInfo: WindowLayoutInfo) {
 | 
			
		||||
        val isFolding = (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let {
 | 
			
		||||
            if (it.isSeparating) {
 | 
			
		||||
                emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
 | 
			
		||||
                if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
 | 
			
		||||
                    // Restrict emulation and overlays to the top of the screen
 | 
			
		||||
                    binding.emulationContainer.layoutParams.height = it.bounds.top
 | 
			
		||||
                    binding.overlayContainer.layoutParams.height = it.bounds.top
 | 
			
		||||
                    // Restrict input and menu drawer to the bottom of the screen
 | 
			
		||||
                    binding.inputContainer.layoutParams.height = it.bounds.bottom
 | 
			
		||||
                    binding.inGameMenu.layoutParams.height = it.bounds.bottom
 | 
			
		||||
    private fun updateFoldableLayout(
 | 
			
		||||
        emulationActivity: EmulationActivity,
 | 
			
		||||
        newLayoutInfo: WindowLayoutInfo
 | 
			
		||||
    ) {
 | 
			
		||||
        val isFolding =
 | 
			
		||||
            (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let {
 | 
			
		||||
                if (it.isSeparating) {
 | 
			
		||||
                    emulationActivity.requestedOrientation =
 | 
			
		||||
                        ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
 | 
			
		||||
                    if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
 | 
			
		||||
                        // Restrict emulation and overlays to the top of the screen
 | 
			
		||||
                        binding.emulationContainer.layoutParams.height = it.bounds.top
 | 
			
		||||
                        binding.overlayContainer.layoutParams.height = it.bounds.top
 | 
			
		||||
                        // Restrict input and menu drawer to the bottom of the screen
 | 
			
		||||
                        binding.inputContainer.layoutParams.height = it.bounds.bottom
 | 
			
		||||
                        binding.inGameMenu.layoutParams.height = it.bounds.bottom
 | 
			
		||||
 | 
			
		||||
                    isInFoldableLayout = true
 | 
			
		||||
                    binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE
 | 
			
		||||
                    refreshInputOverlay()
 | 
			
		||||
                        isInFoldableLayout = true
 | 
			
		||||
                        binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE
 | 
			
		||||
                        refreshInputOverlay()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            it.isSeparating
 | 
			
		||||
        } ?: false
 | 
			
		||||
                it.isSeparating
 | 
			
		||||
            } ?: false
 | 
			
		||||
        if (!isFolding) {
 | 
			
		||||
            binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
 | 
			
		||||
            binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
 | 
			
		||||
@@ -516,18 +529,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
            inputScaleSlider.apply {
 | 
			
		||||
                valueTo = 150F
 | 
			
		||||
                value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
 | 
			
		||||
                addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
 | 
			
		||||
                    inputScaleValue.text = "${value.toInt()}%"
 | 
			
		||||
                    setControlScale(value.toInt())
 | 
			
		||||
                })
 | 
			
		||||
                addOnChangeListener(
 | 
			
		||||
                    Slider.OnChangeListener { _, value, _ ->
 | 
			
		||||
                        inputScaleValue.text = "${value.toInt()}%"
 | 
			
		||||
                        setControlScale(value.toInt())
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            inputOpacitySlider.apply {
 | 
			
		||||
                valueTo = 100F
 | 
			
		||||
                value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat()
 | 
			
		||||
                addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
 | 
			
		||||
                    inputOpacityValue.text = "${value.toInt()}%"
 | 
			
		||||
                    setControlOpacity(value.toInt())
 | 
			
		||||
                })
 | 
			
		||||
                addOnChangeListener(
 | 
			
		||||
                    Slider.OnChangeListener { _, value, _ ->
 | 
			
		||||
                        inputOpacityValue.text = "${value.toInt()}%"
 | 
			
		||||
                        setControlOpacity(value.toInt())
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            inputScaleValue.text = "${inputScaleSlider.value.toInt()}%"
 | 
			
		||||
            inputOpacityValue.text = "${inputOpacitySlider.value.toInt()}%"
 | 
			
		||||
@@ -559,7 +576,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() {
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.inGameMenu) { v: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.inGameMenu
 | 
			
		||||
        ) { v: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
 | 
			
		||||
            var left = 0
 | 
			
		||||
            var right = 0
 | 
			
		||||
@@ -679,8 +698,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
 | 
			
		||||
                        state = State.PAUSED
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    State.PAUSED -> Log.warning("[EmulationFragment] Surface cleared while emulation paused.")
 | 
			
		||||
                    else -> Log.warning("[EmulationFragment] Surface cleared while emulation stopped.")
 | 
			
		||||
                    State.PAUSED -> Log.warning(
 | 
			
		||||
                        "[EmulationFragment] Surface cleared while emulation paused."
 | 
			
		||||
                    )
 | 
			
		||||
                    else -> Log.warning(
 | 
			
		||||
                        "[EmulationFragment] Surface cleared while emulation stopped."
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,9 @@ class HomeSettingsFragment : Fragment() {
 | 
			
		||||
                R.string.select_games_folder,
 | 
			
		||||
                R.string.select_games_folder_description,
 | 
			
		||||
                R.drawable.ic_add
 | 
			
		||||
            ) { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) },
 | 
			
		||||
            ) {
 | 
			
		||||
                mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
 | 
			
		||||
            },
 | 
			
		||||
            HomeSetting(
 | 
			
		||||
                R.string.manage_save_data,
 | 
			
		||||
                R.string.import_export_saves_description,
 | 
			
		||||
@@ -225,7 +227,11 @@ class HomeSettingsFragment : Fragment() {
 | 
			
		||||
        val intent = Intent(action)
 | 
			
		||||
        intent.addCategory(Intent.CATEGORY_DEFAULT)
 | 
			
		||||
        intent.data = DocumentsContract.buildRootUri(authority, DocumentProvider.ROOT_ID)
 | 
			
		||||
        intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
 | 
			
		||||
        intent.addFlags(
 | 
			
		||||
            Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
 | 
			
		||||
                Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
 | 
			
		||||
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
			
		||||
        )
 | 
			
		||||
        return intent
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -307,7 +313,9 @@ class HomeSettingsFragment : Fragment() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() =
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.root
 | 
			
		||||
        ) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
 | 
			
		||||
            val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,14 @@ import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import androidx.documentfile.provider.DocumentFile
 | 
			
		||||
import androidx.fragment.app.DialogFragment
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import java.io.BufferedOutputStream
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.FileOutputStream
 | 
			
		||||
import java.io.FilenameFilter
 | 
			
		||||
import java.time.LocalDateTime
 | 
			
		||||
import java.time.format.DateTimeFormatter
 | 
			
		||||
import java.util.zip.ZipEntry
 | 
			
		||||
import java.util.zip.ZipOutputStream
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
@@ -24,14 +32,6 @@ import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.features.DocumentProvider
 | 
			
		||||
import org.yuzu.yuzu_emu.getPublicFilesDir
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil
 | 
			
		||||
import java.io.BufferedOutputStream
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.FileOutputStream
 | 
			
		||||
import java.io.FilenameFilter
 | 
			
		||||
import java.time.LocalDateTime
 | 
			
		||||
import java.time.format.DateTimeFormatter
 | 
			
		||||
import java.util.zip.ZipEntry
 | 
			
		||||
import java.util.zip.ZipOutputStream
 | 
			
		||||
 | 
			
		||||
class ImportExportSavesFragment : DialogFragment() {
 | 
			
		||||
    private val context = YuzuApplication.appContext
 | 
			
		||||
@@ -98,7 +98,7 @@ class ImportExportSavesFragment : DialogFragment() {
 | 
			
		||||
            val outputZipFile = File(
 | 
			
		||||
                tempFolder,
 | 
			
		||||
                "yuzu saves - ${
 | 
			
		||||
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
 | 
			
		||||
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
 | 
			
		||||
                }.zip"
 | 
			
		||||
            )
 | 
			
		||||
            outputZipFile.createNewFile()
 | 
			
		||||
@@ -106,12 +106,14 @@ class ImportExportSavesFragment : DialogFragment() {
 | 
			
		||||
                saveFolder.walkTopDown().forEach { file ->
 | 
			
		||||
                    val zipFileName =
 | 
			
		||||
                        file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/")
 | 
			
		||||
                    if (zipFileName == "")
 | 
			
		||||
                    if (zipFileName == "") {
 | 
			
		||||
                        return@forEach
 | 
			
		||||
                    }
 | 
			
		||||
                    val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
 | 
			
		||||
                    zos.putNextEntry(entry)
 | 
			
		||||
                    if (file.isFile)
 | 
			
		||||
                    if (file.isFile) {
 | 
			
		||||
                        file.inputStream().use { fis -> fis.copyTo(zos) }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            lastZipCreated = outputZipFile
 | 
			
		||||
@@ -137,7 +139,8 @@ class ImportExportSavesFragment : DialogFragment() {
 | 
			
		||||
 | 
			
		||||
            withContext(Dispatchers.Main) {
 | 
			
		||||
                val file = DocumentFile.fromSingleUri(
 | 
			
		||||
                    context, DocumentsContract.buildDocumentUri(
 | 
			
		||||
                    context,
 | 
			
		||||
                    DocumentsContract.buildDocumentUri(
 | 
			
		||||
                        DocumentProvider.AUTHORITY,
 | 
			
		||||
                        "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
 | 
			
		||||
                    )
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.model.TaskViewModel
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IndeterminateProgressDialogFragment : DialogFragment() {
 | 
			
		||||
    private val taskViewModel: TaskViewModel by activityViewModels()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -113,7 +113,9 @@ class LicensesFragment : Fragment() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() =
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.root
 | 
			
		||||
        ) { _: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import androidx.fragment.app.activityViewModels
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import info.debatty.java.stringsimilarity.Jaccard
 | 
			
		||||
import info.debatty.java.stringsimilarity.JaroWinkler
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
 | 
			
		||||
@@ -29,8 +30,6 @@ import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
import org.yuzu.yuzu_emu.model.GamesViewModel
 | 
			
		||||
import org.yuzu.yuzu_emu.model.HomeViewModel
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.Log
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
 | 
			
		||||
class SearchFragment : Fragment() {
 | 
			
		||||
    private var _binding: FragmentSearchBinding? = null
 | 
			
		||||
@@ -130,15 +129,15 @@ class SearchFragment : Fragment() {
 | 
			
		||||
            R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
 | 
			
		||||
 | 
			
		||||
            R.id.chip_retail -> baseList.filter {
 | 
			
		||||
                FileUtil.hasExtension(it.path, "xci")
 | 
			
		||||
                        || FileUtil.hasExtension(it.path, "nsp")
 | 
			
		||||
                FileUtil.hasExtension(it.path, "xci") ||
 | 
			
		||||
                    FileUtil.hasExtension(it.path, "nsp")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            else -> baseList
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (binding.searchText.text.toString().isEmpty()
 | 
			
		||||
            && binding.chipGroup.checkedChipId != View.NO_ID
 | 
			
		||||
        if (binding.searchText.text.toString().isEmpty() &&
 | 
			
		||||
            binding.chipGroup.checkedChipId != View.NO_ID
 | 
			
		||||
        ) {
 | 
			
		||||
            gamesViewModel.setSearchedGames(filteredList)
 | 
			
		||||
            return
 | 
			
		||||
@@ -173,14 +172,16 @@ class SearchFragment : Fragment() {
 | 
			
		||||
    private fun focusSearch() {
 | 
			
		||||
        if (_binding != null) {
 | 
			
		||||
            binding.searchText.requestFocus()
 | 
			
		||||
            val imm =
 | 
			
		||||
                requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
 | 
			
		||||
            val imm = requireActivity()
 | 
			
		||||
                .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
 | 
			
		||||
            imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() =
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.root
 | 
			
		||||
        ) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
 | 
			
		||||
            val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med)
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import androidx.navigation.findNavController
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
 | 
			
		||||
import com.google.android.material.transition.MaterialFadeThrough
 | 
			
		||||
import java.io.File
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.adapters.SetupAdapter
 | 
			
		||||
@@ -35,7 +36,6 @@ import org.yuzu.yuzu_emu.model.SetupPage
 | 
			
		||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.GameHelper
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
class SetupFragment : Fragment() {
 | 
			
		||||
    private var _binding: FragmentSetupBinding? = null
 | 
			
		||||
@@ -82,7 +82,8 @@ class SetupFragment : Fragment() {
 | 
			
		||||
                        requireActivity().finish()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        requireActivity().window.navigationBarColor =
 | 
			
		||||
            ContextCompat.getColor(requireContext(), android.R.color.transparent)
 | 
			
		||||
@@ -148,14 +149,20 @@ class SetupFragment : Fragment() {
 | 
			
		||||
                    R.drawable.ic_add,
 | 
			
		||||
                    true,
 | 
			
		||||
                    R.string.add_games,
 | 
			
		||||
                    { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) },
 | 
			
		||||
                    {
 | 
			
		||||
                        mainActivity.getGamesDirectory.launch(
 | 
			
		||||
                            Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
 | 
			
		||||
                        )
 | 
			
		||||
                    },
 | 
			
		||||
                    true,
 | 
			
		||||
                    R.string.add_games_warning,
 | 
			
		||||
                    R.string.add_games_warning_description,
 | 
			
		||||
                    R.string.add_games_warning_help,
 | 
			
		||||
                    {
 | 
			
		||||
                        val preferences =
 | 
			
		||||
                            PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
 | 
			
		||||
                            PreferenceManager.getDefaultSharedPreferences(
 | 
			
		||||
                                YuzuApplication.appContext
 | 
			
		||||
                            )
 | 
			
		||||
                        preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
@@ -260,7 +267,9 @@ class SetupFragment : Fragment() {
 | 
			
		||||
    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 | 
			
		||||
    private val permissionLauncher =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.RequestPermission()) {
 | 
			
		||||
            if (!it && !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
 | 
			
		||||
            if (!it &&
 | 
			
		||||
                !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
 | 
			
		||||
            ) {
 | 
			
		||||
                PermissionDeniedDialogFragment().show(
 | 
			
		||||
                    childFragmentManager,
 | 
			
		||||
                    PermissionDeniedDialogFragment.TAG
 | 
			
		||||
@@ -315,7 +324,9 @@ class SetupFragment : Fragment() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() =
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.root
 | 
			
		||||
        ) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
 | 
			
		||||
            view.setPadding(
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,9 @@ class AutofitGridLayoutManager(
 | 
			
		||||
    override fun onLayoutChildren(recycler: Recycler, state: RecyclerView.State) {
 | 
			
		||||
        val width = width
 | 
			
		||||
        val height = height
 | 
			
		||||
        if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
 | 
			
		||||
        if (columnWidth > 0 && width > 0 && height > 0 &&
 | 
			
		||||
            (isColumnWidthChanged || lastWidth != width || lastHeight != height)
 | 
			
		||||
        ) {
 | 
			
		||||
            val totalSpace: Int = if (orientation == VERTICAL) {
 | 
			
		||||
                width - paddingRight - paddingLeft
 | 
			
		||||
            } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,9 @@
 | 
			
		||||
package org.yuzu.yuzu_emu.model
 | 
			
		||||
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import java.util.HashSet
 | 
			
		||||
import kotlinx.parcelize.Parcelize
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
import java.util.HashSet
 | 
			
		||||
 | 
			
		||||
@Parcelize
 | 
			
		||||
@Serializable
 | 
			
		||||
@@ -23,8 +23,9 @@ class Game(
 | 
			
		||||
    val keyLastPlayedTime get() = "${gameId}_LastPlayed"
 | 
			
		||||
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
        if (other !is Game)
 | 
			
		||||
        if (other !is Game) {
 | 
			
		||||
            return false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return hashCode() == other.hashCode()
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import androidx.lifecycle.MutableLiveData
 | 
			
		||||
import androidx.lifecycle.ViewModel
 | 
			
		||||
import androidx.lifecycle.viewModelScope
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.coroutines.withContext
 | 
			
		||||
@@ -20,7 +21,6 @@ import kotlinx.serialization.json.Json
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.GameHelper
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
 | 
			
		||||
@OptIn(ExperimentalSerializationApi::class)
 | 
			
		||||
class GamesViewModel : ViewModel() {
 | 
			
		||||
@@ -99,8 +99,9 @@ class GamesViewModel : ViewModel() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun reloadGames(directoryChanged: Boolean) {
 | 
			
		||||
        if (isReloading.value == true)
 | 
			
		||||
        if (isReloading.value == true) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        _isReloading.postValue(true)
 | 
			
		||||
 | 
			
		||||
        viewModelScope.launch {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.overlay
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.SharedPreferences
 | 
			
		||||
import android.content.res.Configuration
 | 
			
		||||
import android.graphics.Bitmap
 | 
			
		||||
import android.graphics.Canvas
 | 
			
		||||
import android.graphics.Point
 | 
			
		||||
@@ -24,6 +23,8 @@ import android.view.WindowInsets
 | 
			
		||||
import androidx.core.content.ContextCompat
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import androidx.window.layout.WindowMetricsCalculator
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
import kotlin.math.min
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary.StickType
 | 
			
		||||
@@ -31,14 +32,13 @@ import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
import kotlin.math.min
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Draws the interactive input overlay on top of the
 | 
			
		||||
 * [SurfaceView] that is rendering emulation.
 | 
			
		||||
 */
 | 
			
		||||
class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context, attrs),
 | 
			
		||||
class InputOverlay(context: Context, attrs: AttributeSet?) :
 | 
			
		||||
    SurfaceView(context, attrs),
 | 
			
		||||
    OnTouchListener {
 | 
			
		||||
    private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet()
 | 
			
		||||
    private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
 | 
			
		||||
@@ -95,7 +95,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
 | 
			
		||||
 | 
			
		||||
        var shouldUpdateView = false
 | 
			
		||||
        val playerIndex =
 | 
			
		||||
            if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
 | 
			
		||||
            if (NativeLibrary.isHandheldOnly()) {
 | 
			
		||||
                NativeLibrary.ConsoleDevice
 | 
			
		||||
            } else {
 | 
			
		||||
                NativeLibrary.Player1Device
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        for (button in overlayButtons) {
 | 
			
		||||
            if (!button.updateStatus(event)) {
 | 
			
		||||
@@ -158,8 +162,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
 | 
			
		||||
            shouldUpdateView = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (shouldUpdateView)
 | 
			
		||||
        if (shouldUpdateView) {
 | 
			
		||||
            invalidate()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) {
 | 
			
		||||
            return true
 | 
			
		||||
@@ -243,9 +248,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
 | 
			
		||||
                    // If no button is being moved now, remember the currently touched button to move.
 | 
			
		||||
                    if (buttonBeingConfigured == null &&
 | 
			
		||||
                        button.bounds.contains(
 | 
			
		||||
                            fingerPositionX,
 | 
			
		||||
                            fingerPositionY
 | 
			
		||||
                        )
 | 
			
		||||
                                fingerPositionX,
 | 
			
		||||
                                fingerPositionY
 | 
			
		||||
                            )
 | 
			
		||||
                    ) {
 | 
			
		||||
                        buttonBeingConfigured = button
 | 
			
		||||
                        buttonBeingConfigured!!.onConfigureTouch(event)
 | 
			
		||||
@@ -309,9 +314,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
 | 
			
		||||
                MotionEvent.ACTION_DOWN,
 | 
			
		||||
                MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null &&
 | 
			
		||||
                    joystick.bounds.contains(
 | 
			
		||||
                        fingerPositionX,
 | 
			
		||||
                        fingerPositionY
 | 
			
		||||
                    )
 | 
			
		||||
                            fingerPositionX,
 | 
			
		||||
                            fingerPositionY
 | 
			
		||||
                        )
 | 
			
		||||
                ) {
 | 
			
		||||
                    joystickBeingConfigured = joystick
 | 
			
		||||
                    joystickBeingConfigured!!.onConfigureTouch(event)
 | 
			
		||||
@@ -668,7 +673,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
 | 
			
		||||
        R.integer.SWITCH_STICK_L_Y_FOLDABLE
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private fun getResourceValue(orientation: String, position: Int) : Float {
 | 
			
		||||
    private fun getResourceValue(orientation: String, position: Int): Float {
 | 
			
		||||
        return when (orientation) {
 | 
			
		||||
            PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
 | 
			
		||||
            FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
 | 
			
		||||
@@ -820,7 +825,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
 | 
			
		||||
         * @param context       Context for getting the vector drawable
 | 
			
		||||
         * @param drawableId    The ID of the drawable to scale.
 | 
			
		||||
         * @param scale         The scale factor for the bitmap.
 | 
			
		||||
         * @return              The scaled [Bitmap]
 | 
			
		||||
         * @return The scaled [Bitmap]
 | 
			
		||||
         */
 | 
			
		||||
        private fun getBitmap(context: Context, drawableId: Int, scale: Float): Bitmap {
 | 
			
		||||
            val vectorDrawable = ContextCompat.getDrawable(context, drawableId) as VectorDrawable
 | 
			
		||||
@@ -854,7 +859,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
 | 
			
		||||
         * Gets the safe screen size for drawing the overlay
 | 
			
		||||
         *
 | 
			
		||||
         * @param context   Context for getting the window metrics
 | 
			
		||||
         * @return          A pair of points, the first being the top left corner of the safe area,
 | 
			
		||||
         * @return A pair of points, the first being the top left corner of the safe area,
 | 
			
		||||
         *                  the second being the bottom right corner of the safe area
 | 
			
		||||
         */
 | 
			
		||||
        private fun getSafeScreenSize(context: Context): Pair<Point, Point> {
 | 
			
		||||
@@ -872,10 +877,16 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
 | 
			
		||||
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
 | 
			
		||||
                val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
 | 
			
		||||
                if (insets != null) {
 | 
			
		||||
                    if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
 | 
			
		||||
                    if (insets.boundingRectTop.bottom != 0 &&
 | 
			
		||||
                        insets.boundingRectTop.bottom > maxY / 2
 | 
			
		||||
                    ) {
 | 
			
		||||
                        maxY = insets.boundingRectTop.bottom.toFloat()
 | 
			
		||||
                    if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
 | 
			
		||||
                    }
 | 
			
		||||
                    if (insets.boundingRectRight.left != 0 &&
 | 
			
		||||
                        insets.boundingRectRight.left > maxX / 2
 | 
			
		||||
                    ) {
 | 
			
		||||
                        maxX = insets.boundingRectRight.left.toFloat()
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
 | 
			
		||||
                    minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
 | 
			
		||||
 
 | 
			
		||||
@@ -133,7 +133,10 @@ class InputOverlayDrawableDpad(
 | 
			
		||||
            downButtonState = axisY > VIRT_AXIS_DEADZONE
 | 
			
		||||
            leftButtonState = axisX < -VIRT_AXIS_DEADZONE
 | 
			
		||||
            rightButtonState = axisX > VIRT_AXIS_DEADZONE
 | 
			
		||||
            return oldUpState != upButtonState || oldDownState != downButtonState || oldLeftState != leftButtonState || oldRightState != rightButtonState
 | 
			
		||||
            return oldUpState != upButtonState ||
 | 
			
		||||
                oldDownState != downButtonState ||
 | 
			
		||||
                oldLeftState != leftButtonState ||
 | 
			
		||||
                oldRightState != rightButtonState
 | 
			
		||||
        }
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,12 +9,12 @@ import android.graphics.Canvas
 | 
			
		||||
import android.graphics.Rect
 | 
			
		||||
import android.graphics.drawable.BitmapDrawable
 | 
			
		||||
import android.view.MotionEvent
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
 | 
			
		||||
import kotlin.math.atan2
 | 
			
		||||
import kotlin.math.cos
 | 
			
		||||
import kotlin.math.sin
 | 
			
		||||
import kotlin.math.sqrt
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Custom [BitmapDrawable] that is capable
 | 
			
		||||
@@ -241,14 +241,22 @@ class InputOverlayDrawableJoystick(
 | 
			
		||||
    private fun setInnerBounds() {
 | 
			
		||||
        var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt()
 | 
			
		||||
        var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt()
 | 
			
		||||
        if (x > virtBounds.centerX() + virtBounds.width() / 2) x =
 | 
			
		||||
            virtBounds.centerX() + virtBounds.width() / 2
 | 
			
		||||
        if (x < virtBounds.centerX() - virtBounds.width() / 2) x =
 | 
			
		||||
            virtBounds.centerX() - virtBounds.width() / 2
 | 
			
		||||
        if (y > virtBounds.centerY() + virtBounds.height() / 2) y =
 | 
			
		||||
            virtBounds.centerY() + virtBounds.height() / 2
 | 
			
		||||
        if (y < virtBounds.centerY() - virtBounds.height() / 2) y =
 | 
			
		||||
            virtBounds.centerY() - virtBounds.height() / 2
 | 
			
		||||
        if (x > virtBounds.centerX() + virtBounds.width() / 2) {
 | 
			
		||||
            x =
 | 
			
		||||
                virtBounds.centerX() + virtBounds.width() / 2
 | 
			
		||||
        }
 | 
			
		||||
        if (x < virtBounds.centerX() - virtBounds.width() / 2) {
 | 
			
		||||
            x =
 | 
			
		||||
                virtBounds.centerX() - virtBounds.width() / 2
 | 
			
		||||
        }
 | 
			
		||||
        if (y > virtBounds.centerY() + virtBounds.height() / 2) {
 | 
			
		||||
            y =
 | 
			
		||||
                virtBounds.centerY() + virtBounds.height() / 2
 | 
			
		||||
        }
 | 
			
		||||
        if (y < virtBounds.centerY() - virtBounds.height() / 2) {
 | 
			
		||||
            y =
 | 
			
		||||
                virtBounds.centerY() - virtBounds.height() / 2
 | 
			
		||||
        }
 | 
			
		||||
        val width = pressedStateInnerBitmap.bounds.width() / 2
 | 
			
		||||
        val height = pressedStateInnerBitmap.bounds.height() / 2
 | 
			
		||||
        defaultStateInnerBitmap.setBounds(
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,9 @@ class GamesFragment : Fragment() {
 | 
			
		||||
            }
 | 
			
		||||
            shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData ->
 | 
			
		||||
                if (shouldSwapData) {
 | 
			
		||||
                    (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value!!)
 | 
			
		||||
                    (binding.gridGames.adapter as GameAdapter).submitList(
 | 
			
		||||
                        gamesViewModel.games.value!!
 | 
			
		||||
                    )
 | 
			
		||||
                    gamesViewModel.setShouldSwapData(false)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -128,7 +130,9 @@ class GamesFragment : Fragment() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() =
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.root
 | 
			
		||||
        ) { view: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
 | 
			
		||||
            val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_large)
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,9 @@ import androidx.preference.PreferenceManager
 | 
			
		||||
import com.google.android.material.color.MaterialColors
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import com.google.android.material.navigation.NavigationBarView
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.FilenameFilter
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.coroutines.withContext
 | 
			
		||||
@@ -43,9 +46,6 @@ import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
 | 
			
		||||
import org.yuzu.yuzu_emu.model.GamesViewModel
 | 
			
		||||
import org.yuzu.yuzu_emu.model.HomeViewModel
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.*
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.FilenameFilter
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
 | 
			
		||||
class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
    private lateinit var binding: ActivityMainBinding
 | 
			
		||||
@@ -86,7 +86,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
                ThemeHelper.SYSTEM_BAR_ALPHA
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
 | 
			
		||||
        if (InsetsHelper.getSystemGestureType(applicationContext) !=
 | 
			
		||||
            InsetsHelper.GESTURE_NAVIGATION
 | 
			
		||||
        ) {
 | 
			
		||||
            binding.navigationBarShade.setBackgroundColor(
 | 
			
		||||
                ThemeHelper.getColorWithOpacity(
 | 
			
		||||
                    MaterialColors.getColor(
 | 
			
		||||
@@ -172,7 +174,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
                        binding.navigationView.height.toFloat() * 2
 | 
			
		||||
                    translationY(0f)
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) {
 | 
			
		||||
                    if (ViewCompat.getLayoutDirection(binding.navigationView) ==
 | 
			
		||||
                        ViewCompat.LAYOUT_DIRECTION_LTR
 | 
			
		||||
                    ) {
 | 
			
		||||
                        binding.navigationView.translationX =
 | 
			
		||||
                            binding.navigationView.width.toFloat() * -2
 | 
			
		||||
                        translationX(0f)
 | 
			
		||||
@@ -189,7 +193,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
                if (smallLayout) {
 | 
			
		||||
                    translationY(binding.navigationView.height.toFloat() * 2)
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) {
 | 
			
		||||
                    if (ViewCompat.getLayoutDirection(binding.navigationView) ==
 | 
			
		||||
                        ViewCompat.LAYOUT_DIRECTION_LTR
 | 
			
		||||
                    ) {
 | 
			
		||||
                        translationX(binding.navigationView.width.toFloat() * -2)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        translationX(binding.navigationView.width.toFloat() * 2)
 | 
			
		||||
@@ -234,7 +240,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setInsets() =
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
        ViewCompat.setOnApplyWindowInsetsListener(
 | 
			
		||||
            binding.root
 | 
			
		||||
        ) { _: View, windowInsets: WindowInsetsCompat ->
 | 
			
		||||
            val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
			
		||||
            val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams
 | 
			
		||||
            mlpStatusShade.height = insets.top
 | 
			
		||||
@@ -256,8 +264,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
 | 
			
		||||
    val getGamesDirectory =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
 | 
			
		||||
            if (result == null)
 | 
			
		||||
            if (result == null) {
 | 
			
		||||
                return@registerForActivityResult
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            contentResolver.takePersistableUriPermission(
 | 
			
		||||
                result,
 | 
			
		||||
@@ -281,8 +290,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
 | 
			
		||||
    val getProdKey =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
 | 
			
		||||
            if (result == null)
 | 
			
		||||
            if (result == null) {
 | 
			
		||||
                return@registerForActivityResult
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!FileUtil.hasExtension(result, "keys")) {
 | 
			
		||||
                MessageDialogFragment.newInstance(
 | 
			
		||||
@@ -324,8 +334,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
 | 
			
		||||
    val getFirmware =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
 | 
			
		||||
            if (result == null)
 | 
			
		||||
            if (result == null) {
 | 
			
		||||
                return@registerForActivityResult
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val inputZip = contentResolver.openInputStream(result)
 | 
			
		||||
            if (inputZip == null) {
 | 
			
		||||
@@ -376,8 +387,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
 | 
			
		||||
    val getAmiiboKey =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
 | 
			
		||||
            if (result == null)
 | 
			
		||||
            if (result == null) {
 | 
			
		||||
                return@registerForActivityResult
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!FileUtil.hasExtension(result, "bin")) {
 | 
			
		||||
                MessageDialogFragment.newInstance(
 | 
			
		||||
@@ -418,8 +430,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
 | 
			
		||||
    val getDriver =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
 | 
			
		||||
            if (result == null)
 | 
			
		||||
            if (result == null) {
 | 
			
		||||
                return@registerForActivityResult
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val takeFlags =
 | 
			
		||||
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
 | 
			
		||||
@@ -470,8 +483,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		||||
 | 
			
		||||
    val installGameUpdate =
 | 
			
		||||
        registerForActivityResult(ActivityResultContracts.OpenDocument()) {
 | 
			
		||||
            if (it == null)
 | 
			
		||||
            if (it == null) {
 | 
			
		||||
                return@registerForActivityResult
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            IndeterminateProgressDialogFragment.newInstance(
 | 
			
		||||
                this@MainActivity,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,9 @@ class ControllerMappingHelper {
 | 
			
		||||
            // The two analog triggers generate analog motion events as well as a keycode.
 | 
			
		||||
            // We always prefer to use the analog values, so throw away the button press
 | 
			
		||||
            keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2
 | 
			
		||||
        } else false
 | 
			
		||||
        } else {
 | 
			
		||||
            false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,8 @@
 | 
			
		||||
package org.yuzu.yuzu_emu.utils
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
 | 
			
		||||
object DirectoryInitialization {
 | 
			
		||||
    private var userPath: String? = null
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,10 @@ package org.yuzu.yuzu_emu.utils
 | 
			
		||||
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import androidx.documentfile.provider.DocumentFile
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.util.*
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
 | 
			
		||||
 | 
			
		||||
class DocumentsTree {
 | 
			
		||||
    private var root: DocumentsNode? = null
 | 
			
		||||
@@ -29,7 +29,9 @@ class DocumentsTree {
 | 
			
		||||
        val node = resolvePath(filepath)
 | 
			
		||||
        return if (node == null || node.isDirectory) {
 | 
			
		||||
            0
 | 
			
		||||
        } else FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
 | 
			
		||||
        } else {
 | 
			
		||||
            FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun exists(filepath: String): Boolean {
 | 
			
		||||
@@ -111,7 +113,9 @@ class DocumentsTree {
 | 
			
		||||
        fun isNativePath(path: String): Boolean {
 | 
			
		||||
            return if (path.isNotEmpty()) {
 | 
			
		||||
                path[0] == '/'
 | 
			
		||||
            } else false
 | 
			
		||||
            } else {
 | 
			
		||||
                false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,6 @@ import android.net.Uri
 | 
			
		||||
import android.provider.DocumentsContract
 | 
			
		||||
import android.provider.OpenableColumns
 | 
			
		||||
import androidx.documentfile.provider.DocumentFile
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
 | 
			
		||||
import java.io.BufferedInputStream
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.FileOutputStream
 | 
			
		||||
@@ -19,6 +17,8 @@ import java.io.InputStream
 | 
			
		||||
import java.net.URLDecoder
 | 
			
		||||
import java.util.zip.ZipEntry
 | 
			
		||||
import java.util.zip.ZipInputStream
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
 | 
			
		||||
 | 
			
		||||
object FileUtil {
 | 
			
		||||
    const val PATH_TREE = "tree"
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ class ForegroundService : Service() {
 | 
			
		||||
 | 
			
		||||
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
 | 
			
		||||
        if (intent == null) {
 | 
			
		||||
            return START_NOT_STICKY;
 | 
			
		||||
            return START_NOT_STICKY
 | 
			
		||||
        }
 | 
			
		||||
        if (intent.action == ACTION_STOP) {
 | 
			
		||||
            NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,12 @@ package org.yuzu.yuzu_emu.utils
 | 
			
		||||
import android.content.SharedPreferences
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import java.util.*
 | 
			
		||||
import kotlinx.serialization.encodeToString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
object GameHelper {
 | 
			
		||||
    const val KEY_GAME_PATH = "game_path"
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,14 @@ package org.yuzu.yuzu_emu.utils
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
 | 
			
		||||
import java.io.BufferedInputStream
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.FileInputStream
 | 
			
		||||
import java.io.FileOutputStream
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import java.util.zip.ZipInputStream
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
 | 
			
		||||
 | 
			
		||||
object GpuDriverHelper {
 | 
			
		||||
    private const val META_JSON_FILENAME = "meta.json"
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,12 @@
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.utils
 | 
			
		||||
 | 
			
		||||
import org.json.JSONException
 | 
			
		||||
import org.json.JSONObject
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import java.nio.charset.StandardCharsets
 | 
			
		||||
import java.nio.file.Files
 | 
			
		||||
import java.nio.file.Paths
 | 
			
		||||
import org.json.JSONException
 | 
			
		||||
import org.json.JSONObject
 | 
			
		||||
 | 
			
		||||
class GpuDriverMetadata(metadataFilePath: String) {
 | 
			
		||||
    var name: String? = null
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,8 @@ package org.yuzu.yuzu_emu.utils
 | 
			
		||||
 | 
			
		||||
import android.view.KeyEvent
 | 
			
		||||
import android.view.MotionEvent
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import kotlin.math.sqrt
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
 | 
			
		||||
class InputHandler {
 | 
			
		||||
    fun initialize() {
 | 
			
		||||
@@ -68,7 +68,11 @@ class InputHandler {
 | 
			
		||||
            6 -> NativeLibrary.Player6Device
 | 
			
		||||
            7 -> NativeLibrary.Player7Device
 | 
			
		||||
            8 -> NativeLibrary.Player8Device
 | 
			
		||||
            else -> if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
 | 
			
		||||
            else -> if (NativeLibrary.isHandheldOnly()) {
 | 
			
		||||
                NativeLibrary.ConsoleDevice
 | 
			
		||||
            } else {
 | 
			
		||||
                NativeLibrary.Player1Device
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -107,7 +111,11 @@ class InputHandler {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getAxisToButton(axis: Float): Int {
 | 
			
		||||
        return if (axis > 0.5f) NativeLibrary.ButtonState.PRESSED else NativeLibrary.ButtonState.RELEASED
 | 
			
		||||
        return if (axis > 0.5f) {
 | 
			
		||||
            NativeLibrary.ButtonState.PRESSED
 | 
			
		||||
        } else {
 | 
			
		||||
            NativeLibrary.ButtonState.RELEASED
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) {
 | 
			
		||||
@@ -287,7 +295,6 @@ class InputHandler {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
 | 
			
		||||
        // Joycon support is half dead. Right joystick doesn't work
 | 
			
		||||
        val playerNumber = getPlayerNumber(event.device.controllerNumber)
 | 
			
		||||
@@ -355,6 +362,4 @@ class InputHandler {
 | 
			
		||||
                )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,7 @@
 | 
			
		||||
package org.yuzu.yuzu_emu.utils
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.graphics.Rect
 | 
			
		||||
 | 
			
		||||
object InsetsHelper {
 | 
			
		||||
    const val THREE_BUTTON_NAVIGATION = 0
 | 
			
		||||
@@ -20,12 +18,8 @@ object InsetsHelper {
 | 
			
		||||
            resources.getIdentifier("config_navBarInteractionMode", "integer", "android")
 | 
			
		||||
        return if (resourceId != 0) {
 | 
			
		||||
            resources.getInteger(resourceId)
 | 
			
		||||
        } else 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getBottomPaddingRequired(activity: Activity): Int {
 | 
			
		||||
        val visibleFrame = Rect()
 | 
			
		||||
        activity.window.decorView.getWindowVisibleDisplayFrame(visibleFrame)
 | 
			
		||||
        return visibleFrame.bottom - visibleFrame.top - activity.resources.displayMetrics.heightPixels
 | 
			
		||||
        } else {
 | 
			
		||||
            0
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,8 @@ import android.nfc.tech.NfcA
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Handler
 | 
			
		||||
import android.os.Looper
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
 | 
			
		||||
class NfcReader(private val activity: Activity) {
 | 
			
		||||
    private var nfcAdapter: NfcAdapter? = null
 | 
			
		||||
@@ -25,10 +25,13 @@ class NfcReader(private val activity: Activity) {
 | 
			
		||||
 | 
			
		||||
        pendingIntent = PendingIntent.getActivity(
 | 
			
		||||
            activity,
 | 
			
		||||
            0, Intent(activity, activity.javaClass),
 | 
			
		||||
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
 | 
			
		||||
            0,
 | 
			
		||||
            Intent(activity, activity.javaClass),
 | 
			
		||||
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
 | 
			
		||||
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
 | 
			
		||||
            else PendingIntent.FLAG_UPDATE_CURRENT
 | 
			
		||||
            } else {
 | 
			
		||||
                PendingIntent.FLAG_UPDATE_CURRENT
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
 | 
			
		||||
@@ -45,9 +48,9 @@ class NfcReader(private val activity: Activity) {
 | 
			
		||||
 | 
			
		||||
    fun onNewIntent(intent: Intent) {
 | 
			
		||||
        val action = intent.action
 | 
			
		||||
        if (NfcAdapter.ACTION_TAG_DISCOVERED != action
 | 
			
		||||
            && NfcAdapter.ACTION_TECH_DISCOVERED != action
 | 
			
		||||
            && NfcAdapter.ACTION_NDEF_DISCOVERED != action
 | 
			
		||||
        if (NfcAdapter.ACTION_TAG_DISCOVERED != action &&
 | 
			
		||||
            NfcAdapter.ACTION_TECH_DISCOVERED != action &&
 | 
			
		||||
            NfcAdapter.ACTION_NDEF_DISCOVERED != action
 | 
			
		||||
        ) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
@@ -84,7 +87,7 @@ class NfcReader(private val activity: Activity) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun ntag215ReadAll(amiibo: NfcA): ByteArray? {
 | 
			
		||||
        val bufferSize = amiibo.maxTransceiveLength;
 | 
			
		||||
        val bufferSize = amiibo.maxTransceiveLength
 | 
			
		||||
        val tagSize = 0x21C
 | 
			
		||||
        val pageSize = 4
 | 
			
		||||
        val lastPage = tagSize / pageSize - 1
 | 
			
		||||
@@ -103,7 +106,7 @@ class NfcReader(private val activity: Activity) {
 | 
			
		||||
                val data = ntag215FastRead(amiibo, dataStart, dataEnd - 1)
 | 
			
		||||
                System.arraycopy(data, 0, tagData, i, (dataEnd - dataStart) * pageSize)
 | 
			
		||||
            } catch (e: IOException) {
 | 
			
		||||
                return null;
 | 
			
		||||
                return null
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return tagData
 | 
			
		||||
 
 | 
			
		||||
@@ -11,30 +11,34 @@ import java.io.Serializable
 | 
			
		||||
 | 
			
		||||
object SerializableHelper {
 | 
			
		||||
    inline fun <reified T : Serializable> Bundle.serializable(key: String): T? {
 | 
			
		||||
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
 | 
			
		||||
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
 | 
			
		||||
            getSerializable(key, T::class.java)
 | 
			
		||||
        else
 | 
			
		||||
        } else {
 | 
			
		||||
            getSerializable(key) as? T
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline fun <reified T : Serializable> Intent.serializable(key: String): T? {
 | 
			
		||||
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
 | 
			
		||||
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
 | 
			
		||||
            getSerializableExtra(key, T::class.java)
 | 
			
		||||
        else
 | 
			
		||||
        } else {
 | 
			
		||||
            getSerializableExtra(key) as? T
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? {
 | 
			
		||||
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
 | 
			
		||||
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
 | 
			
		||||
            getParcelable(key, T::class.java)
 | 
			
		||||
        else
 | 
			
		||||
        } else {
 | 
			
		||||
            getParcelable(key) as? T
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? {
 | 
			
		||||
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
 | 
			
		||||
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
 | 
			
		||||
            getParcelableExtra(key, T::class.java)
 | 
			
		||||
        else
 | 
			
		||||
        } else {
 | 
			
		||||
            getParcelableExtra(key) as? T
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,21 +3,19 @@
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.utils
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.res.Configuration
 | 
			
		||||
import android.graphics.Color
 | 
			
		||||
import androidx.annotation.ColorInt
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import androidx.appcompat.app.AppCompatDelegate
 | 
			
		||||
import androidx.core.content.ContextCompat
 | 
			
		||||
import androidx.core.view.WindowCompat
 | 
			
		||||
import androidx.core.view.WindowInsetsControllerCompat
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import kotlin.math.roundToInt
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
 | 
			
		||||
import org.yuzu.yuzu_emu.ui.main.ThemeProvider
 | 
			
		||||
import kotlin.math.roundToInt
 | 
			
		||||
 | 
			
		||||
object ThemeHelper {
 | 
			
		||||
    const val SYSTEM_BAR_ALPHA = 0.9f
 | 
			
		||||
@@ -36,8 +34,8 @@ object ThemeHelper {
 | 
			
		||||
        // Using a specific night mode check because this could apply incorrectly when using the
 | 
			
		||||
        // light app mode, dark system mode, and black backgrounds. Launching the settings activity
 | 
			
		||||
        // will then show light mode colors/navigation bars but with black backgrounds.
 | 
			
		||||
        if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
 | 
			
		||||
            && isNightMode(activity)
 | 
			
		||||
        if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) &&
 | 
			
		||||
            isNightMode(activity)
 | 
			
		||||
        ) {
 | 
			
		||||
            activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
 | 
			
		||||
        }
 | 
			
		||||
@@ -46,8 +44,10 @@ object ThemeHelper {
 | 
			
		||||
    @ColorInt
 | 
			
		||||
    fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int {
 | 
			
		||||
        return Color.argb(
 | 
			
		||||
            (alphaFactor * Color.alpha(color)).roundToInt(), Color.red(color),
 | 
			
		||||
            Color.green(color), Color.blue(color)
 | 
			
		||||
            (alphaFactor * Color.alpha(color)).roundToInt(),
 | 
			
		||||
            Color.red(color),
 | 
			
		||||
            Color.green(color),
 | 
			
		||||
            Color.blue(color)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -38,8 +38,8 @@ class FixedRatioSurfaceView @JvmOverloads constructor(
 | 
			
		||||
                newWidth = width
 | 
			
		||||
                newHeight = (width / aspectRatio).roundToInt()
 | 
			
		||||
            }
 | 
			
		||||
            val left = (width - newWidth) / 2;
 | 
			
		||||
            val top = (height - newHeight) / 2;
 | 
			
		||||
            val left = (width - newWidth) / 2
 | 
			
		||||
            val top = (height - newHeight) / 2
 | 
			
		||||
            setLeftTopRightBottom(left, top, left + newWidth, top + newHeight)
 | 
			
		||||
        } else {
 | 
			
		||||
            setLeftTopRightBottom(0, 0, width, height)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user