mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-17 08:29:27 -04:00
android: Apply automatic kotlin formatting fixes
This commit is contained in:
parent
a2a20fcc65
commit
2940069fe2
94 changed files with 1901 additions and 1223 deletions
|
|
@ -80,7 +80,7 @@ android {
|
|||
"-DENABLE_SDL2=0", // Don't use SDL
|
||||
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
|
||||
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", // Support Android 15 16KiB page sizes
|
||||
"-DENABLE_GDBSTUB=OFF", // Disable GDB stub
|
||||
"-DENABLE_GDBSTUB=OFF" // Disable GDB stub
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -147,7 +147,7 @@ android {
|
|||
}
|
||||
lint {
|
||||
checkReleaseBuilds = false // Ditto
|
||||
// The name of this property is misleading, this doesn't actually disable linting for the `release` build.
|
||||
// ^- The name of this property is misleading, this doesn't actually disable linting for the `release` build.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +215,9 @@ dependencies {
|
|||
|
||||
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
|
||||
val downloadVulkanValidationLayers = tasks.register<Download>("downloadVulkanValidationLayers") {
|
||||
src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.313.0/android-binaries-1.4.313.0.zip")
|
||||
src(
|
||||
"https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.313.0/android-binaries-1.4.313.0.zip"
|
||||
)
|
||||
dest(file("${layout.buildDirectory.get().asFile.path}/tmp/Vulkan-ValidationLayers.zip"))
|
||||
onlyIfModified(true)
|
||||
}
|
||||
|
|
@ -266,7 +268,7 @@ fun getGitHash(): String =
|
|||
fun getBranch(): String =
|
||||
runGitCommand(ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")) ?: "dummy-branch"
|
||||
|
||||
fun runGitCommand(command: ProcessBuilder) : String? {
|
||||
fun runGitCommand(command: ProcessBuilder): String? {
|
||||
try {
|
||||
command.directory(project.rootDir)
|
||||
val process = command.start()
|
||||
|
|
@ -292,7 +294,7 @@ android.applicationVariants.configureEach {
|
|||
val variant = this
|
||||
val capitalizedName = variant.name.capitalizeUS()
|
||||
|
||||
val copyTask = tasks.register("copyBundle${capitalizedName}") {
|
||||
val copyTask = tasks.register("copyBundle$capitalizedName") {
|
||||
doLast {
|
||||
project.copy {
|
||||
from(variant.outputs.first().outputFile.parentFile)
|
||||
|
|
@ -306,5 +308,5 @@ android.applicationVariants.configureEach {
|
|||
}
|
||||
}
|
||||
}
|
||||
tasks.named("bundle${capitalizedName}").configure { finalizedBy(copyTask) }
|
||||
tasks.named("bundle$capitalizedName").configure { finalizedBy(copyTask) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import android.os.Build
|
|||
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||
import org.citra.citra_emu.utils.DocumentsTree
|
||||
import org.citra.citra_emu.utils.GpuDriverHelper
|
||||
import org.citra.citra_emu.utils.PermissionsHandler
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import org.citra.citra_emu.utils.MemoryUtil
|
||||
import org.citra.citra_emu.utils.PermissionsHandler
|
||||
|
||||
class CitraApplication : Application() {
|
||||
private fun createNotificationChannel() {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import androidx.core.net.toUri
|
|||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.Date
|
||||
import org.citra.citra_emu.activities.EmulationActivity
|
||||
import org.citra.citra_emu.model.Game
|
||||
import org.citra.citra_emu.utils.BuildUtil
|
||||
|
|
@ -32,8 +34,6 @@ import org.citra.citra_emu.utils.FileUtil
|
|||
import org.citra.citra_emu.utils.Log
|
||||
import org.citra.citra_emu.utils.RemovableStorageHelper
|
||||
import org.citra.citra_emu.viewmodel.CompressProgressDialogViewModel
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Class which contains methods that interact
|
||||
|
|
@ -135,10 +135,7 @@ object NativeLibrary {
|
|||
*/
|
||||
external fun setUserDirectory(directory: String)
|
||||
|
||||
data class InstalledGame(
|
||||
val path: String,
|
||||
val mediaType: Game.MediaType
|
||||
)
|
||||
data class InstalledGame(val path: String, val mediaType: Game.MediaType)
|
||||
fun getInstalledGamePaths(): Array<InstalledGame> {
|
||||
val games = getInstalledGamePathsImpl()
|
||||
|
||||
|
|
@ -253,9 +250,8 @@ object NativeLibrary {
|
|||
external fun playTimeManagerGetCurrentTitleId(): Long
|
||||
|
||||
private external fun uninstallTitle(titleId: Long, mediaType: Int): Boolean
|
||||
fun uninstallTitle(titleId: Long, mediaType: Game.MediaType): Boolean {
|
||||
return uninstallTitle(titleId, mediaType.value)
|
||||
}
|
||||
fun uninstallTitle(titleId: Long, mediaType: Game.MediaType): Boolean =
|
||||
uninstallTitle(titleId, mediaType.value)
|
||||
|
||||
external fun nativeFileExists(path: String): Boolean
|
||||
|
||||
|
|
@ -352,7 +348,7 @@ object NativeLibrary {
|
|||
@get:JvmStatic
|
||||
val isPortraitMode: Boolean
|
||||
get() = CitraApplication.appContext.resources.configuration.orientation ==
|
||||
Configuration.ORIENTATION_PORTRAIT
|
||||
Configuration.ORIENTATION_PORTRAIT
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
|
|
@ -501,16 +497,20 @@ object NativeLibrary {
|
|||
|
||||
else -> {
|
||||
title = getString(R.string.loader_error_generic_title)
|
||||
message = getString(R.string.loader_error_generic,
|
||||
getString(coreError.stringRes), coreError.value)
|
||||
message = getString(
|
||||
R.string.loader_error_generic,
|
||||
getString(coreError.stringRes),
|
||||
coreError.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val alert = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(title)
|
||||
.setMessage(
|
||||
Html.fromHtml(message,
|
||||
Html.FROM_HTML_MODE_LEGACY
|
||||
Html.fromHtml(
|
||||
message,
|
||||
Html.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||
|
|
@ -641,7 +641,7 @@ object NativeLibrary {
|
|||
fun loadStateIfAvailable(slot: Int): Boolean {
|
||||
var available = false
|
||||
getSavestateInfo()?.forEach {
|
||||
if (it.slot == slot){
|
||||
if (it.slot == slot) {
|
||||
available = true
|
||||
return@forEach
|
||||
}
|
||||
|
|
@ -679,19 +679,17 @@ object NativeLibrary {
|
|||
// Compression / Decompression
|
||||
private external fun compressFileNative(inputPath: String?, outputPath: String): Int
|
||||
|
||||
fun compressFile(inputPath: String?, outputPath: String): CompressStatus {
|
||||
return CompressStatus.fromValue(
|
||||
fun compressFile(inputPath: String?, outputPath: String): CompressStatus =
|
||||
CompressStatus.fromValue(
|
||||
compressFileNative(inputPath, outputPath)
|
||||
)
|
||||
}
|
||||
|
||||
private external fun decompressFileNative(inputPath: String?, outputPath: String): Int
|
||||
|
||||
fun decompressFile(inputPath: String?, outputPath: String): CompressStatus {
|
||||
return CompressStatus.fromValue(
|
||||
fun decompressFile(inputPath: String?, outputPath: String): CompressStatus =
|
||||
CompressStatus.fromValue(
|
||||
decompressFileNative(inputPath, outputPath)
|
||||
)
|
||||
}
|
||||
|
||||
external fun getRecommendedExtension(inputPath: String?, shouldCompress: Boolean): String
|
||||
|
||||
|
|
@ -725,21 +723,19 @@ object NativeLibrary {
|
|||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun openContentUri(path: String, openMode: String): Int =
|
||||
if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.openContentUri(path, openMode)
|
||||
} else {
|
||||
FileUtil.openContentUri(path, openMode)
|
||||
}
|
||||
fun openContentUri(path: String, openMode: String): Int = if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.openContentUri(path, openMode)
|
||||
} else {
|
||||
FileUtil.openContentUri(path, openMode)
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun getFilesName(path: String): Array<String?> =
|
||||
if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.getFilesName(path)
|
||||
} else {
|
||||
FileUtil.getFilesName(path)
|
||||
}
|
||||
fun getFilesName(path: String): Array<String?> = if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.getFilesName(path)
|
||||
} else {
|
||||
FileUtil.getFilesName(path)
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
|
|
@ -765,10 +761,14 @@ object NativeLibrary {
|
|||
return primaryStoragePath + dirSep + virtualPath
|
||||
} else { // User directory probably located on a removable storage device
|
||||
val storageIdString = pathSegment.substringBefore(":")
|
||||
val removablePath = RemovableStorageHelper.getRemovableStoragePath(CitraApplication.appContext, storageIdString)
|
||||
val removablePath = RemovableStorageHelper.getRemovableStoragePath(
|
||||
CitraApplication.appContext,
|
||||
storageIdString
|
||||
)
|
||||
|
||||
if (removablePath == null) {
|
||||
android.util.Log.e("NativeLibrary",
|
||||
android.util.Log.e(
|
||||
"NativeLibrary",
|
||||
"Unknown mount location for storage device '$storageIdString' (URI: $uri)"
|
||||
)
|
||||
return ""
|
||||
|
|
@ -788,12 +788,11 @@ object NativeLibrary {
|
|||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun getSize(path: String): Long =
|
||||
if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.getFileSize(path)
|
||||
} else {
|
||||
FileUtil.getFileSize(path)
|
||||
}
|
||||
fun getSize(path: String): Long = if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.getFileSize(path)
|
||||
} else {
|
||||
FileUtil.getFileSize(path)
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
|
|
@ -801,21 +800,19 @@ object NativeLibrary {
|
|||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun fileExists(path: String): Boolean =
|
||||
if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.exists(path)
|
||||
} else {
|
||||
FileUtil.exists(path)
|
||||
}
|
||||
fun fileExists(path: String): Boolean = if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.exists(path)
|
||||
} else {
|
||||
FileUtil.exists(path)
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun isDirectory(path: String): Boolean =
|
||||
if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.isDirectory(path)
|
||||
} else {
|
||||
FileUtil.isDirectory(path)
|
||||
}
|
||||
fun isDirectory(path: String): Boolean = if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.isDirectory(path)
|
||||
} else {
|
||||
FileUtil.isDirectory(path)
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
|
|
@ -823,19 +820,18 @@ object NativeLibrary {
|
|||
sourcePath: String,
|
||||
destinationParentPath: String,
|
||||
destinationFilename: String
|
||||
): Boolean =
|
||||
if (FileUtil.isNativePath(sourcePath) &&
|
||||
FileUtil.isNativePath(destinationParentPath)
|
||||
) {
|
||||
CitraApplication.documentsTree
|
||||
.copyFile(sourcePath, destinationParentPath, destinationFilename)
|
||||
} else {
|
||||
FileUtil.copyFile(
|
||||
Uri.parse(sourcePath),
|
||||
Uri.parse(destinationParentPath),
|
||||
destinationFilename
|
||||
)
|
||||
}
|
||||
): Boolean = if (FileUtil.isNativePath(sourcePath) &&
|
||||
FileUtil.isNativePath(destinationParentPath)
|
||||
) {
|
||||
CitraApplication.documentsTree
|
||||
.copyFile(sourcePath, destinationParentPath, destinationFilename)
|
||||
} else {
|
||||
FileUtil.copyFile(
|
||||
Uri.parse(sourcePath),
|
||||
Uri.parse(destinationParentPath),
|
||||
destinationFilename
|
||||
)
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
|
|
@ -870,12 +866,11 @@ object NativeLibrary {
|
|||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun deleteDocument(path: String): Boolean =
|
||||
if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.deleteDocument(path)
|
||||
} else {
|
||||
FileUtil.deleteDocument(path)
|
||||
}
|
||||
fun deleteDocument(path: String): Boolean = if (FileUtil.isNativePath(path)) {
|
||||
CitraApplication.documentsTree.deleteDocument(path)
|
||||
} else {
|
||||
FileUtil.deleteDocument(path)
|
||||
}
|
||||
|
||||
enum class CoreError(val value: Int, @StringRes val stringRes: Int) {
|
||||
Success(0, R.string.core_error_success),
|
||||
|
|
@ -898,9 +893,7 @@ object NativeLibrary {
|
|||
ErrorUnknown(17, R.string.core_error_unknown);
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int): CoreError {
|
||||
return entries.find { it.value == value } ?: ErrorUnknown
|
||||
}
|
||||
fun fromInt(value: Int): CoreError = entries.find { it.value == value } ?: ErrorUnknown
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -952,7 +945,11 @@ object NativeLibrary {
|
|||
const val MESSAGE = "message"
|
||||
const val CAN_CONTINUE = "canContinue"
|
||||
|
||||
fun newInstance(title: String, message: String, canContinue: Boolean): CoreErrorDialogFragment {
|
||||
fun newInstance(
|
||||
title: String,
|
||||
message: String,
|
||||
canContinue: Boolean
|
||||
): CoreErrorDialogFragment {
|
||||
val frag = CoreErrorDialogFragment()
|
||||
val args = Bundle()
|
||||
args.putString(TITLE, title)
|
||||
|
|
|
|||
|
|
@ -46,9 +46,9 @@ import org.citra.citra_emu.fragments.MessageDialogFragment
|
|||
import org.citra.citra_emu.model.Game
|
||||
import org.citra.citra_emu.utils.BuildUtil
|
||||
import org.citra.citra_emu.utils.ControllerMappingHelper
|
||||
import org.citra.citra_emu.utils.FileBrowserHelper
|
||||
import org.citra.citra_emu.utils.EmulationLifecycleUtil
|
||||
import org.citra.citra_emu.utils.EmulationMenuSettings
|
||||
import org.citra.citra_emu.utils.FileBrowserHelper
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import org.citra.citra_emu.utils.RefreshRateUtil
|
||||
import org.citra.citra_emu.utils.ThemeUtil
|
||||
|
|
@ -334,11 +334,13 @@ class EmulationActivity : AppCompatActivity() {
|
|||
}
|
||||
return hotkeyUtility.handleKeyPress(event)
|
||||
}
|
||||
|
||||
KeyEvent.ACTION_UP -> {
|
||||
return hotkeyUtility.handleKeyRelease(event)
|
||||
}
|
||||
|
||||
else -> {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -358,7 +360,8 @@ class EmulationActivity : AppCompatActivity() {
|
|||
// TODO: Move this check into native code - prevents crash if input pressed before starting emulation
|
||||
if (!NativeLibrary.isRunning() ||
|
||||
(event.source and InputDevice.SOURCE_CLASS_JOYSTICK == 0) ||
|
||||
emulationFragment.isDrawerOpen()) {
|
||||
emulationFragment.isDrawerOpen()
|
||||
) {
|
||||
return super.dispatchGenericMotionEvent(event)
|
||||
}
|
||||
|
||||
|
|
@ -387,7 +390,10 @@ class EmulationActivity : AppCompatActivity() {
|
|||
preferences.getInt(InputBindingSetting.getInputAxisButtonKey(axis), -1)
|
||||
val guestOrientation =
|
||||
preferences.getInt(InputBindingSetting.getInputAxisOrientationKey(axis), -1)
|
||||
val inverted = preferences.getBoolean(InputBindingSetting.getInputAxisInvertedKey(axis),false);
|
||||
val inverted = preferences.getBoolean(
|
||||
InputBindingSetting.getInputAxisInvertedKey(axis),
|
||||
false
|
||||
)
|
||||
if (nextMapping == -1 || guestOrientation == -1) {
|
||||
// Axis is unmapped
|
||||
continue
|
||||
|
|
@ -396,7 +402,7 @@ class EmulationActivity : AppCompatActivity() {
|
|||
// Skip joystick wobble
|
||||
value = 0f
|
||||
}
|
||||
if (inverted) value = -value;
|
||||
if (inverted) value = -value
|
||||
|
||||
when (nextMapping) {
|
||||
NativeLibrary.ButtonType.STICK_LEFT -> {
|
||||
|
|
@ -573,7 +579,9 @@ class EmulationActivity : AppCompatActivity() {
|
|||
registerForActivityResult(OpenFileResultContract()) { result: Intent? ->
|
||||
if (result == null) return@registerForActivityResult
|
||||
val selectedFiles = FileBrowserHelper.getSelectedFiles(
|
||||
result, applicationContext, listOf<String>("bin")
|
||||
result,
|
||||
applicationContext,
|
||||
listOf<String>("bin")
|
||||
) ?: return@registerForActivityResult
|
||||
if (BuildUtil.isGooglePlayBuild) {
|
||||
onAmiiboSelected(selectedFiles[0])
|
||||
|
|
@ -596,8 +604,6 @@ class EmulationActivity : AppCompatActivity() {
|
|||
companion object {
|
||||
private var instance: EmulationActivity? = null
|
||||
|
||||
fun isRunning(): Boolean {
|
||||
return instance?.isEmulationRunning ?: false
|
||||
}
|
||||
fun isRunning(): Boolean = instance?.isEmulationRunning ?: false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import androidx.recyclerview.widget.ListAdapter
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.databinding.CardDriverOptionBinding
|
||||
import org.citra.citra_emu.utils.GpuDriverHelper
|
||||
import org.citra.citra_emu.utils.GpuDriverMetadata
|
||||
import org.citra.citra_emu.viewmodel.DriverViewModel
|
||||
import org.citra.citra_emu.utils.GpuDriverHelper
|
||||
|
||||
class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
||||
ListAdapter<Pair<Uri, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
|
||||
|
|
@ -105,15 +105,11 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
|||
override fun areItemsTheSame(
|
||||
oldItem: Pair<Uri, GpuDriverMetadata>,
|
||||
newItem: Pair<Uri, GpuDriverMetadata>
|
||||
): Boolean {
|
||||
return oldItem.first == newItem.first
|
||||
}
|
||||
): Boolean = oldItem.first == newItem.first
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: Pair<Uri, GpuDriverMetadata>,
|
||||
newItem: Pair<Uri, GpuDriverMetadata>
|
||||
): Boolean {
|
||||
return oldItem.second == newItem.second
|
||||
}
|
||||
): Boolean = oldItem.second == newItem.second
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,24 +4,25 @@
|
|||
|
||||
package org.citra.citra_emu.adapters
|
||||
|
||||
import android.graphics.drawable.Icon
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.net.Uri
|
||||
import android.os.SystemClock
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.widget.TextView
|
||||
import android.widget.ImageView
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.Bitmap
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.edit
|
||||
|
|
@ -29,24 +30,23 @@ import androidx.core.graphics.scale
|
|||
import androidx.core.net.toUri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.AsyncDifferConfig
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.widget.PopupMenu
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.citra.citra_emu.HomeNavigationDirections
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.HomeNavigationDirections
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.adapters.GameAdapter.GameViewHolder
|
||||
|
|
@ -65,10 +65,12 @@ class GameAdapter(
|
|||
private val activity: AppCompatActivity,
|
||||
private val inflater: LayoutInflater,
|
||||
private val openImageLauncher: ActivityResultLauncher<String>?,
|
||||
private val onRequestCompressOrDecompress: ((inputPath: String, suggestedName: String, shouldCompress: Boolean) -> Unit)? = null
|
||||
) :
|
||||
ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
|
||||
View.OnClickListener, View.OnLongClickListener {
|
||||
private val onRequestCompressOrDecompress: (
|
||||
(inputPath: String, suggestedName: String, shouldCompress: Boolean) -> Unit
|
||||
)? = null
|
||||
) : ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
|
||||
View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
private var lastClickTime = 0L
|
||||
private var imagePath: String? = null
|
||||
private var dialogShortcutBinding: DialogShortcutBinding? = null
|
||||
|
|
@ -212,15 +214,18 @@ class GameAdapter(
|
|||
binding.textGameTitle.text = game.title
|
||||
binding.textCompany.text = game.company
|
||||
binding.textGameRegion.text = game.regions
|
||||
binding.imageCartridge.visibility = if (preferences.getString("insertedCartridge", "") != game.path) {
|
||||
View.GONE
|
||||
} else {
|
||||
View.VISIBLE
|
||||
}
|
||||
binding.imageCartridge.visibility =
|
||||
if (preferences.getString("insertedCartridge", "") != game.path) {
|
||||
View.GONE
|
||||
} else {
|
||||
View.VISIBLE
|
||||
}
|
||||
|
||||
val backgroundColorId =
|
||||
if (
|
||||
isValidGame(game.filename.substring(game.filename.lastIndexOf(".") + 1).lowercase())
|
||||
isValidGame(
|
||||
game.filename.substring(game.filename.lastIndexOf(".") + 1).lowercase()
|
||||
)
|
||||
) {
|
||||
R.attr.colorSurface
|
||||
} else {
|
||||
|
|
@ -263,13 +268,41 @@ class GameAdapter(
|
|||
val basePath = "sdmc/Nintendo 3DS/00000000000000000000000000000000/00000000000000000000000000000000"
|
||||
return GameDirectories(
|
||||
gameDir = game.path.substringBeforeLast("/"),
|
||||
saveDir = basePath + "/title/${String.format("%016x", game.titleId).lowercase().substring(0, 8)}/${String.format("%016x", game.titleId).lowercase().substring(8)}/data/00000001",
|
||||
saveDir =
|
||||
basePath +
|
||||
"/title/${String.format(
|
||||
"%016x",
|
||||
game.titleId
|
||||
).lowercase().substring(
|
||||
0,
|
||||
8
|
||||
)}/${String.format(
|
||||
"%016x",
|
||||
game.titleId
|
||||
).lowercase().substring(8)}/data/00000001",
|
||||
modsDir = "load/mods/${String.format("%016X", game.titleId)}",
|
||||
texturesDir = "load/textures/${String.format("%016X", game.titleId)}",
|
||||
appDir = game.path.substringBeforeLast("/").split("/").filter { it.isNotEmpty() }.joinToString("/"),
|
||||
dlcDir = basePath + "/title/0004008c/${String.format("%016x", game.titleId).lowercase().substring(8)}/content",
|
||||
updatesDir = basePath + "/title/0004000e/${String.format("%016x", game.titleId).lowercase().substring(8)}/content",
|
||||
extraDir = basePath + "/extdata/00000000/${String.format("%016X", game.titleId).substring(8, 14).padStart(8, '0')}"
|
||||
appDir = game.path.substringBeforeLast("/").split("/").filter {
|
||||
it.isNotEmpty()
|
||||
}.joinToString("/"),
|
||||
dlcDir =
|
||||
basePath +
|
||||
"/title/0004008c/${String.format(
|
||||
"%016x",
|
||||
game.titleId
|
||||
).lowercase().substring(8)}/content",
|
||||
updatesDir =
|
||||
basePath +
|
||||
"/title/0004000e/${String.format(
|
||||
"%016x",
|
||||
game.titleId
|
||||
).lowercase().substring(8)}/content",
|
||||
extraDir =
|
||||
basePath +
|
||||
"/extdata/00000000/${String.format(
|
||||
"%016X",
|
||||
game.titleId
|
||||
).substring(8, 14).padStart(8, '0')}"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -299,13 +332,36 @@ class GameAdapter(
|
|||
.setType("*/*")
|
||||
|
||||
val uri = when (menuItem.itemId) {
|
||||
R.id.game_context_open_app -> CitraApplication.documentsTree.folderUriHelper(dirs.appDir)
|
||||
R.id.game_context_open_save_dir -> CitraApplication.documentsTree.folderUriHelper(dirs.saveDir)
|
||||
R.id.game_context_open_updates -> CitraApplication.documentsTree.folderUriHelper(dirs.updatesDir)
|
||||
R.id.game_context_open_dlc -> CitraApplication.documentsTree.folderUriHelper(dirs.dlcDir)
|
||||
R.id.game_context_open_extra -> CitraApplication.documentsTree.folderUriHelper(dirs.extraDir)
|
||||
R.id.game_context_open_textures -> CitraApplication.documentsTree.folderUriHelper(dirs.texturesDir, true)
|
||||
R.id.game_context_open_mods -> CitraApplication.documentsTree.folderUriHelper(dirs.modsDir, true)
|
||||
R.id.game_context_open_app -> CitraApplication.documentsTree.folderUriHelper(
|
||||
dirs.appDir
|
||||
)
|
||||
|
||||
R.id.game_context_open_save_dir -> CitraApplication.documentsTree.folderUriHelper(
|
||||
dirs.saveDir
|
||||
)
|
||||
|
||||
R.id.game_context_open_updates -> CitraApplication.documentsTree.folderUriHelper(
|
||||
dirs.updatesDir
|
||||
)
|
||||
|
||||
R.id.game_context_open_dlc -> CitraApplication.documentsTree.folderUriHelper(
|
||||
dirs.dlcDir
|
||||
)
|
||||
|
||||
R.id.game_context_open_extra -> CitraApplication.documentsTree.folderUriHelper(
|
||||
dirs.extraDir
|
||||
)
|
||||
|
||||
R.id.game_context_open_textures -> CitraApplication.documentsTree.folderUriHelper(
|
||||
dirs.texturesDir,
|
||||
true
|
||||
)
|
||||
|
||||
R.id.game_context_open_mods -> CitraApplication.documentsTree.folderUriHelper(
|
||||
dirs.modsDir,
|
||||
true
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
|
@ -319,7 +375,11 @@ class GameAdapter(
|
|||
popup.show()
|
||||
}
|
||||
|
||||
private fun showUninstallContextMenu(view: View, game: Game, bottomSheetDialog: BottomSheetDialog) {
|
||||
private fun showUninstallContextMenu(
|
||||
view: View,
|
||||
game: Game,
|
||||
bottomSheetDialog: BottomSheetDialog
|
||||
) {
|
||||
val dirs = getGameDirectories(game)
|
||||
val popup = PopupMenu(view.context, view).apply {
|
||||
menuInflater.inflate(R.menu.game_context_menu_uninstall, menu)
|
||||
|
|
@ -342,16 +402,38 @@ class GameAdapter(
|
|||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
val uninstallAction: () -> Unit = {
|
||||
when (menuItem.itemId) {
|
||||
R.id.game_context_uninstall -> NativeLibrary.uninstallTitle(titleId, game.mediaType)
|
||||
R.id.game_context_uninstall_dlc -> NativeLibrary.uninstallTitle(dlcTitleId, Game.MediaType.SDMC)
|
||||
R.id.game_context_uninstall_updates -> NativeLibrary.uninstallTitle(updateTitleId, Game.MediaType.SDMC)
|
||||
R.id.game_context_uninstall -> NativeLibrary.uninstallTitle(
|
||||
titleId,
|
||||
game.mediaType
|
||||
)
|
||||
|
||||
R.id.game_context_uninstall_dlc -> NativeLibrary.uninstallTitle(
|
||||
dlcTitleId,
|
||||
Game.MediaType.SDMC
|
||||
)
|
||||
|
||||
R.id.game_context_uninstall_updates -> NativeLibrary.uninstallTitle(
|
||||
updateTitleId,
|
||||
Game.MediaType.SDMC
|
||||
)
|
||||
}
|
||||
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
|
||||
bottomSheetDialog.dismiss()
|
||||
}
|
||||
|
||||
if (menuItem.itemId in listOf(R.id.game_context_uninstall, R.id.game_context_uninstall_dlc, R.id.game_context_uninstall_updates)) {
|
||||
IndeterminateProgressDialogFragment.newInstance(activity, R.string.uninstalling, false, uninstallAction)
|
||||
if (menuItem.itemId in
|
||||
listOf(
|
||||
R.id.game_context_uninstall,
|
||||
R.id.game_context_uninstall_dlc,
|
||||
R.id.game_context_uninstall_updates
|
||||
)
|
||||
) {
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
activity,
|
||||
R.string.uninstalling,
|
||||
false,
|
||||
uninstallAction
|
||||
)
|
||||
.show(activity.supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
true
|
||||
} else {
|
||||
|
|
@ -362,7 +444,12 @@ class GameAdapter(
|
|||
popup.show()
|
||||
}
|
||||
|
||||
private fun showAboutGameDialog(context: Context, game: Game, holder: GameViewHolder, view: View) {
|
||||
private fun showAboutGameDialog(
|
||||
context: Context,
|
||||
game: Game,
|
||||
holder: GameViewHolder,
|
||||
view: View
|
||||
) {
|
||||
val bottomSheetView = inflater.inflate(R.layout.dialog_about_game, null)
|
||||
|
||||
val bottomSheetDialog = BottomSheetDialog(context)
|
||||
|
|
@ -374,12 +461,22 @@ class GameAdapter(
|
|||
bottomSheetView.findViewById<TextView>(R.id.about_game_title).text = game.title
|
||||
bottomSheetView.findViewById<TextView>(R.id.about_game_company).text = game.company
|
||||
bottomSheetView.findViewById<TextView>(R.id.about_game_region).text = game.regions
|
||||
bottomSheetView.findViewById<TextView>(R.id.about_game_id).text = context.getString(R.string.game_context_id) + " " + String.format("%016X", game.titleId)
|
||||
bottomSheetView.findViewById<TextView>(R.id.about_game_filename).text = context.getString(R.string.game_context_file) + " " + game.filename
|
||||
bottomSheetView.findViewById<TextView>(R.id.about_game_filetype).text = context.getString(R.string.game_context_type) + " " + game.fileType
|
||||
bottomSheetView.findViewById<TextView>(R.id.about_game_id).text =
|
||||
context.getString(R.string.game_context_id) + " " + String.format("%016X", game.titleId)
|
||||
bottomSheetView.findViewById<TextView>(R.id.about_game_filename).text =
|
||||
context.getString(R.string.game_context_file) + " " + game.filename
|
||||
bottomSheetView.findViewById<TextView>(R.id.about_game_filetype).text =
|
||||
context.getString(R.string.game_context_type) + " " + game.fileType
|
||||
|
||||
val insertButton = bottomSheetView.findViewById<MaterialButton>(R.id.insert_cartridge_button)
|
||||
insertButton.text = if (inserted) { context.getString(R.string.game_context_eject) } else { context.getString(R.string.game_context_insert) }
|
||||
val insertButton = bottomSheetView.findViewById<MaterialButton>(
|
||||
R.id.insert_cartridge_button
|
||||
)
|
||||
insertButton.text =
|
||||
if (inserted) {
|
||||
context.getString(R.string.game_context_eject)
|
||||
} else {
|
||||
context.getString(R.string.game_context_insert)
|
||||
}
|
||||
insertButton.visibility = if (insertable) View.VISIBLE else View.GONE
|
||||
insertButton.setOnClickListener {
|
||||
if (inserted) {
|
||||
|
|
@ -420,7 +517,7 @@ class GameAdapter(
|
|||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
// Default to false for zoomed in shortcut icons
|
||||
preferences.edit() {
|
||||
preferences.edit {
|
||||
putBoolean(
|
||||
"shouldStretchIcon",
|
||||
false
|
||||
|
|
@ -452,7 +549,11 @@ class GameAdapter(
|
|||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val shortcutName = dialogShortcutBinding!!.shortcutNameInput.text.toString()
|
||||
if (shortcutName.isEmpty()) {
|
||||
Toast.makeText(context, R.string.shortcut_name_empty, Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(
|
||||
context,
|
||||
R.string.shortcut_name_empty,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
return@setPositiveButton
|
||||
}
|
||||
val iconBitmap = (dialogShortcutBinding!!.shortcutIcon.drawable as BitmapDrawable).bitmap
|
||||
|
|
@ -463,9 +564,11 @@ class GameAdapter(
|
|||
val shortcut = ShortcutInfo.Builder(context, shortcutName)
|
||||
.setShortLabel(shortcutName)
|
||||
.setIcon(icon)
|
||||
.setIntent(game.launchIntent.apply {
|
||||
putExtra("launchedFromShortcut", true)
|
||||
})
|
||||
.setIntent(
|
||||
game.launchIntent.apply {
|
||||
putExtra("launchedFromShortcut", true)
|
||||
}
|
||||
)
|
||||
.build()
|
||||
|
||||
shortcutManager?.requestPinShortcut(shortcut, null)
|
||||
|
|
@ -486,7 +589,9 @@ class GameAdapter(
|
|||
bottomSheetDialog.dismiss()
|
||||
}
|
||||
|
||||
val compressDecompressButton = bottomSheetView.findViewById<MaterialButton>(R.id.compress_decompress)
|
||||
val compressDecompressButton = bottomSheetView.findViewById<MaterialButton>(
|
||||
R.id.compress_decompress
|
||||
)
|
||||
if (game.isInstalled) {
|
||||
compressDecompressButton.setOnClickListener {
|
||||
Toast.makeText(
|
||||
|
|
@ -499,31 +604,42 @@ class GameAdapter(
|
|||
} else {
|
||||
compressDecompressButton.setOnClickListener {
|
||||
val shouldCompress = !game.isCompressed
|
||||
val recommendedExt = NativeLibrary.getRecommendedExtension(holder.game.path, shouldCompress)
|
||||
val recommendedExt = NativeLibrary.getRecommendedExtension(
|
||||
holder.game.path,
|
||||
shouldCompress
|
||||
)
|
||||
val baseName = holder.game.filename.substringBeforeLast('.')
|
||||
onRequestCompressOrDecompress?.invoke(holder.game.path, "$baseName.$recommendedExt", shouldCompress)
|
||||
onRequestCompressOrDecompress?.invoke(
|
||||
holder.game.path,
|
||||
"$baseName.$recommendedExt",
|
||||
shouldCompress
|
||||
)
|
||||
bottomSheetDialog.dismiss()
|
||||
}
|
||||
}
|
||||
compressDecompressButton.text = context.getString(if (!game.isCompressed) R.string.compress else R.string.decompress)
|
||||
compressDecompressButton.text =
|
||||
context.getString(if (!game.isCompressed) R.string.compress else R.string.decompress)
|
||||
|
||||
bottomSheetView.findViewById<MaterialButton>(R.id.menu_button_open).setOnClickListener {
|
||||
showOpenContextMenu(it, game)
|
||||
}
|
||||
|
||||
bottomSheetView.findViewById<MaterialButton>(R.id.menu_button_uninstall).setOnClickListener {
|
||||
bottomSheetView.findViewById<MaterialButton>(
|
||||
R.id.menu_button_uninstall
|
||||
).setOnClickListener {
|
||||
showUninstallContextMenu(it, game, bottomSheetDialog)
|
||||
}
|
||||
|
||||
bottomSheetView.findViewById<MaterialButton>(R.id.delete_cache).setOnClickListener {
|
||||
val options = arrayOf(context.getString(R.string.vulkan), context.getString(R.string.opengles))
|
||||
val options =
|
||||
arrayOf(context.getString(R.string.vulkan), context.getString(R.string.opengles))
|
||||
var selectedIndex = -1
|
||||
val dialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.delete_cache_select_backend)
|
||||
.setSingleChoiceItems(options, -1) { dialog, which ->
|
||||
selectedIndex = which
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) {_, _ ->
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val progToast = Toast.makeText(
|
||||
CitraApplication.appContext,
|
||||
R.string.deleting_shader_cache,
|
||||
|
|
@ -532,11 +648,11 @@ class GameAdapter(
|
|||
progToast.show()
|
||||
|
||||
activity.lifecycleScope.launch(Dispatchers.IO) {
|
||||
|
||||
when (selectedIndex) {
|
||||
0 -> {
|
||||
NativeLibrary.deleteVulkanShaderCache(game.titleId)
|
||||
}
|
||||
|
||||
1 -> {
|
||||
NativeLibrary.deleteOpenGLShaderCache(game.titleId)
|
||||
}
|
||||
|
|
@ -620,10 +736,8 @@ class GameAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun isValidGame(extension: String): Boolean {
|
||||
return Game.badExtensions.stream()
|
||||
.noneMatch { extension == it.lowercase() }
|
||||
}
|
||||
private fun isValidGame(extension: String): Boolean = Game.badExtensions.stream()
|
||||
.noneMatch { extension == it.lowercase() }
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
|
||||
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||
|
|
@ -632,8 +746,6 @@ class GameAdapter(
|
|||
return oldItem.titleId == newItem.titleId && oldItem.title == newItem.title
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean = oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ class HomeSettingAdapter(
|
|||
private val activity: AppCompatActivity,
|
||||
private val viewLifecycle: LifecycleOwner,
|
||||
var options: List<HomeSetting>
|
||||
) : RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(), View.OnClickListener {
|
||||
) : RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(),
|
||||
View.OnClickListener {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder {
|
||||
val binding =
|
||||
CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
|
|
@ -37,9 +38,7 @@ class HomeSettingAdapter(
|
|||
return HomeOptionViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return options.size
|
||||
}
|
||||
override fun getItemCount(): Int = options.size
|
||||
|
||||
override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) {
|
||||
holder.bind(options[position])
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.databinding.PageSetupBinding
|
||||
import org.citra.citra_emu.model.ButtonState
|
||||
import org.citra.citra_emu.model.PageState
|
||||
import org.citra.citra_emu.model.SetupCallback
|
||||
import org.citra.citra_emu.model.SetupPage
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.utils.ViewUtils
|
||||
|
||||
class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
|
||||
|
|
@ -35,7 +35,8 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
|||
holder.bind(pages[position])
|
||||
|
||||
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
|
||||
RecyclerView.ViewHolder(binding.root), SetupCallback {
|
||||
RecyclerView.ViewHolder(binding.root),
|
||||
SetupCallback {
|
||||
lateinit var page: SetupPage
|
||||
|
||||
init {
|
||||
|
|
@ -49,7 +50,9 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
|||
onStepCompleted(0, pageFullyCompleted = true)
|
||||
}
|
||||
|
||||
if (page.pageButtons != null && page.pageSteps.invoke() != PageState.PAGE_STEPS_COMPLETE) {
|
||||
if (page.pageButtons != null &&
|
||||
page.pageSteps.invoke() != PageState.PAGE_STEPS_COMPLETE
|
||||
) {
|
||||
for (pageButton in page.pageButtons) {
|
||||
val pageButtonView = LayoutInflater.from(activity)
|
||||
.inflate(
|
||||
|
|
@ -108,9 +111,17 @@ class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>)
|
|||
.alpha(0.38f)
|
||||
.setDuration(200)
|
||||
.start()
|
||||
button.setTextColor(button.context.getColor(com.google.android.material.R.color.material_on_surface_disabled))
|
||||
button.setTextColor(
|
||||
button.context.getColor(
|
||||
com.google.android.material.R.color.material_on_surface_disabled
|
||||
)
|
||||
)
|
||||
button.iconTint =
|
||||
ColorStateList.valueOf(button.context.getColor(com.google.android.material.R.color.material_on_surface_disabled))
|
||||
ColorStateList.valueOf(
|
||||
button.context.getColor(
|
||||
com.google.android.material.R.color.material_on_surface_disabled
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
package org.citra.citra_emu.applets
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import java.io.Serializable
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.fragments.MiiSelectorDialogFragment
|
||||
import java.io.Serializable
|
||||
|
||||
@Keep
|
||||
object MiiSelector {
|
||||
|
|
@ -43,5 +43,5 @@ object MiiSelector {
|
|||
lateinit var miiNames: Array<String>
|
||||
}
|
||||
|
||||
class MiiSelectorData (var returnCode: Long, var index: Int)
|
||||
class MiiSelectorData(var returnCode: Long, var index: Int)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ package org.citra.citra_emu.applets
|
|||
import android.text.InputFilter
|
||||
import android.text.Spanned
|
||||
import androidx.annotation.Keep
|
||||
import java.io.Serializable
|
||||
import org.citra.citra_emu.CitraApplication.Companion.appContext
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.fragments.KeyboardDialogFragment
|
||||
import org.citra.citra_emu.fragments.MessageDialogFragment
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import java.io.Serializable
|
||||
|
||||
@Keep
|
||||
object SoftwareKeyboard {
|
||||
|
|
@ -81,17 +81,17 @@ object SoftwareKeyboard {
|
|||
private external fun ValidateFilters(text: String): ValidationError
|
||||
external fun ValidateInput(text: String): ValidationError
|
||||
|
||||
/// Corresponds to Frontend::ButtonConfig
|
||||
// / Corresponds to Frontend::ButtonConfig
|
||||
interface ButtonConfig {
|
||||
companion object {
|
||||
const val Single = 0 /// Ok button
|
||||
const val Dual = 1 /// Cancel | Ok buttons
|
||||
const val Triple = 2 /// Cancel | I Forgot | Ok buttons
|
||||
const val None = 3 /// No button (returned by swkbdInputText in special cases)
|
||||
const val Single = 0 // / Ok button
|
||||
const val Dual = 1 // / Cancel | Ok buttons
|
||||
const val Triple = 2 // / Cancel | I Forgot | Ok buttons
|
||||
const val None = 3 // / No button (returned by swkbdInputText in special cases)
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to Frontend::ValidationError
|
||||
// / Corresponds to Frontend::ValidationError
|
||||
enum class ValidationError {
|
||||
None,
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ object SoftwareKeyboard {
|
|||
lateinit var buttonText: Array<String>
|
||||
}
|
||||
|
||||
/// Corresponds to Frontend::KeyboardData
|
||||
// / Corresponds to Frontend::KeyboardData
|
||||
class KeyboardData(var button: Int, var text: String)
|
||||
class Filter : InputFilter {
|
||||
override fun filter(
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ import android.content.Intent
|
|||
import androidx.activity.result.contract.ActivityResultContract
|
||||
|
||||
class OpenFileResultContract : ActivityResultContract<Boolean?, Intent?>() {
|
||||
override fun createIntent(context: Context, input: Boolean?): Intent {
|
||||
return Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
override fun createIntent(context: Context, input: Boolean?): Intent =
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
.setType("application/octet-stream")
|
||||
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, input)
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): Intent? = intent
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@
|
|||
|
||||
package org.citra.citra_emu.display
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.app.Activity
|
||||
import android.view.WindowManager
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||
import org.citra.citra_emu.features.settings.model.IntListSetting
|
||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
||||
import org.citra.citra_emu.utils.EmulationMenuSettings
|
||||
|
|
@ -20,7 +20,7 @@ import org.citra.citra_emu.utils.EmulationMenuSettings
|
|||
class ScreenAdjustmentUtil(
|
||||
private val context: Context,
|
||||
private val windowManager: WindowManager,
|
||||
private val settings: Settings,
|
||||
private val settings: Settings
|
||||
) {
|
||||
fun swapScreen() {
|
||||
val isEnabled = !EmulationMenuSettings.swapScreens
|
||||
|
|
@ -34,14 +34,15 @@ class ScreenAdjustmentUtil(
|
|||
}
|
||||
|
||||
fun cycleLayouts() {
|
||||
|
||||
val landscapeLayoutsToCycle = IntListSetting.LAYOUTS_TO_CYCLE.list;
|
||||
val landscapeLayoutsToCycle = IntListSetting.LAYOUTS_TO_CYCLE.list
|
||||
val landscapeValues =
|
||||
if (landscapeLayoutsToCycle.isNotEmpty())
|
||||
if (landscapeLayoutsToCycle.isNotEmpty()) {
|
||||
landscapeLayoutsToCycle.toIntArray()
|
||||
else context.resources.getIntArray(
|
||||
R.array.landscapeValues
|
||||
)
|
||||
} else {
|
||||
context.resources.getIntArray(
|
||||
R.array.landscapeValues
|
||||
)
|
||||
}
|
||||
val portraitValues = context.resources.getIntArray(R.array.portraitValues)
|
||||
|
||||
if (NativeLibrary.isPortraitMode) {
|
||||
|
|
@ -73,7 +74,7 @@ class ScreenAdjustmentUtil(
|
|||
|
||||
fun changeSecondaryOrientation(layoutOption: Int) {
|
||||
IntSetting.SECONDARY_DISPLAY_LAYOUT.int = layoutOption
|
||||
settings.saveSetting(IntSetting.SECONDARY_DISPLAY_LAYOUT,SettingsFile.FILE_NAME_CONFIG)
|
||||
settings.saveSetting(IntSetting.SECONDARY_DISPLAY_LAYOUT, SettingsFile.FILE_NAME_CONFIG)
|
||||
NativeLibrary.reloadSettings()
|
||||
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
|
||||
}
|
||||
|
|
@ -102,6 +103,5 @@ class ScreenAdjustmentUtil(
|
|||
settings.saveSetting(BooleanSetting.UPRIGHT_SCREEN, SettingsFile.FILE_NAME_CONFIG)
|
||||
NativeLibrary.reloadSettings()
|
||||
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,8 @@ enum class ScreenLayout(val int: Int) {
|
|||
HYBRID_SCREEN(4),
|
||||
CUSTOM_LAYOUT(5);
|
||||
|
||||
|
||||
companion object {
|
||||
fun from(int: Int): ScreenLayout {
|
||||
return entries.firstOrNull { it.int == int } ?: LARGE_SCREEN
|
||||
}
|
||||
fun from(int: Int): ScreenLayout = entries.firstOrNull { it.int == int } ?: LARGE_SCREEN
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -32,9 +29,7 @@ enum class SmallScreenPosition(val int: Int) {
|
|||
BELOW(7);
|
||||
|
||||
companion object {
|
||||
fun from(int: Int): SmallScreenPosition {
|
||||
return entries.firstOrNull { it.int == int } ?: TOP_RIGHT
|
||||
}
|
||||
fun from(int: Int): SmallScreenPosition = entries.firstOrNull { it.int == int } ?: TOP_RIGHT
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,9 +40,8 @@ enum class PortraitScreenLayout(val int: Int) {
|
|||
ORIGINAL(2);
|
||||
|
||||
companion object {
|
||||
fun from(int: Int): PortraitScreenLayout {
|
||||
return entries.firstOrNull { it.int == int } ?: TOP_FULL_WIDTH
|
||||
}
|
||||
fun from(int: Int): PortraitScreenLayout =
|
||||
entries.firstOrNull { it.int == int } ?: TOP_FULL_WIDTH
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -66,9 +60,7 @@ enum class SecondaryDisplayLayout(val int: Int) {
|
|||
;
|
||||
|
||||
companion object {
|
||||
fun from(int: Int): SecondaryDisplayLayout {
|
||||
return entries.firstOrNull { it.int == int } ?: NONE
|
||||
}
|
||||
fun from(int: Int): SecondaryDisplayLayout = entries.firstOrNull { it.int == int } ?: NONE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,26 +73,22 @@ enum class StereoWhichDisplay(val int: Int) {
|
|||
SECONDARY_ONLY(3);
|
||||
|
||||
companion object {
|
||||
fun from(int: Int): StereoWhichDisplay {
|
||||
return entries.firstOrNull { it.int == int } ?: NONE
|
||||
}
|
||||
fun from(int: Int): StereoWhichDisplay = entries.firstOrNull { it.int == int } ?: NONE
|
||||
}
|
||||
}
|
||||
|
||||
enum class StereoMode(val int: Int) {
|
||||
// These must match what is defined in src/common/settings.h
|
||||
// These must match what is defined in src/common/settings.h
|
||||
|
||||
OFF(0),
|
||||
SIDE_BY_SIDE(1),
|
||||
SIDE_BY_SIDE_FULL(2),
|
||||
ANAGLYPH(3),
|
||||
INTERLACED(4),
|
||||
REVERSE_INTERLACED (5),
|
||||
CARDBOARD_VR (6);
|
||||
REVERSE_INTERLACED(5),
|
||||
CARDBOARD_VR(6);
|
||||
|
||||
companion object {
|
||||
fun from(int: Int): StereoMode {
|
||||
return entries.firstOrNull { it.int == int } ?: OFF
|
||||
}
|
||||
fun from(int: Int): StereoMode = entries.firstOrNull { it.int == int } ?: OFF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import android.view.MotionEvent
|
|||
import android.view.SurfaceHolder
|
||||
import android.view.SurfaceView
|
||||
import android.view.WindowManager
|
||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||
import org.citra.citra_emu.utils.Log
|
||||
|
||||
class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
||||
|
|
@ -55,26 +55,27 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
|||
NativeLibrary.secondarySurfaceDestroyed()
|
||||
}
|
||||
|
||||
private fun getSecondaryDisplays(): List<Display> {
|
||||
val dm = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||
val currentDisplayId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
context.display.displayId
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager)
|
||||
.defaultDisplay.displayId
|
||||
}
|
||||
private fun getSecondaryDisplays(): List<Display> {
|
||||
val dm = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||
val currentDisplayId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
context.display.displayId
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager)
|
||||
.defaultDisplay.displayId
|
||||
}
|
||||
val displays = dm.displays
|
||||
val presDisplays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
|
||||
val presDisplays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
|
||||
return displays.filter {
|
||||
val isPresentable = presDisplays.any { pd -> pd.displayId == it.displayId }
|
||||
val isNotDefaultOrPresentable = (it != null && it.displayId != Display.DEFAULT_DISPLAY) || isPresentable
|
||||
val isNotDefaultOrPresentable =
|
||||
(it != null && it.displayId != Display.DEFAULT_DISPLAY) || isPresentable
|
||||
|
||||
isNotDefaultOrPresentable &&
|
||||
it.displayId != currentDisplayId &&
|
||||
it.name != "HiddenDisplay" &&
|
||||
it.state != Display.STATE_OFF &&
|
||||
it.isValid
|
||||
it.displayId != currentDisplayId &&
|
||||
it.name != "HiddenDisplay" &&
|
||||
it.state != Display.STATE_OFF &&
|
||||
it.isValid
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,23 +89,26 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
|||
// Theoretically, the NONE option is no longer selectable, but
|
||||
// I am leaving this in for backwards compatibility
|
||||
IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int ||
|
||||
! BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean
|
||||
!BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean
|
||||
) {
|
||||
currentDisplayId = -1
|
||||
vd.display
|
||||
} else if (preferredDisplayId >=0 && availableDisplays.any { it.displayId == preferredDisplayId }) {
|
||||
} else if (preferredDisplayId >= 0 &&
|
||||
availableDisplays.any { it.displayId == preferredDisplayId }
|
||||
) {
|
||||
currentDisplayId = preferredDisplayId
|
||||
availableDisplays.first { it.displayId == preferredDisplayId }
|
||||
} else {
|
||||
val dm = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||
val default = dm.displays.first {it.displayId == Display.DEFAULT_DISPLAY}
|
||||
val default = dm.displays.first { it.displayId == Display.DEFAULT_DISPLAY }
|
||||
// prioritize displays that have a different name from the default display, as
|
||||
// some devices such as the Odin 2 create a permanent virtual display with the same
|
||||
// name as the default display that should be skipped in most cases
|
||||
currentDisplayId = availableDisplays.firstOrNull{
|
||||
it.name != default.name && !it.name.contains("Built",true)}?.displayId ?:
|
||||
availableDisplays[0].displayId
|
||||
availableDisplays.first{ it.displayId == currentDisplayId }
|
||||
currentDisplayId = availableDisplays.firstOrNull {
|
||||
it.name != default.name && !it.name.contains("Built", true)
|
||||
}?.displayId
|
||||
?: availableDisplays[0].displayId
|
||||
availableDisplays.first { it.displayId == currentDisplayId }
|
||||
}
|
||||
|
||||
// if our presentation is already on the right display, ignore
|
||||
|
|
@ -151,7 +155,9 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
|
|||
}
|
||||
}
|
||||
class SecondaryDisplayPresentation(
|
||||
context: Context, display: Display, val parent: SecondaryDisplay
|
||||
context: Context,
|
||||
display: Display,
|
||||
val parent: SecondaryDisplay
|
||||
) : Presentation(context, display) {
|
||||
private lateinit var surfaceView: SurfaceView
|
||||
private var touchscreenPointerId = -1
|
||||
|
|
@ -173,9 +179,12 @@ class SecondaryDisplayPresentation(
|
|||
}
|
||||
|
||||
override fun surfaceChanged(
|
||||
holder: SurfaceHolder, format: Int, width: Int, height: Int
|
||||
holder: SurfaceHolder,
|
||||
format: Int,
|
||||
width: Int,
|
||||
height: Int
|
||||
) {
|
||||
Log.debug("SecondaryDisplay Surface changed: ${width}x${height}")
|
||||
Log.debug("SecondaryDisplay Surface changed: ${width}x$height")
|
||||
parent.updateSurface()
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +234,5 @@ class SecondaryDisplayPresentation(
|
|||
}
|
||||
|
||||
// Publicly accessible method to get the SurfaceHolder
|
||||
fun getSurfaceHolder(): SurfaceHolder {
|
||||
return surfaceView.holder
|
||||
}
|
||||
fun getSurfaceHolder(): SurfaceHolder = surfaceView.holder
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class CheatsViewModel : ViewModel() {
|
|||
private var selectedCheatPosition = -1
|
||||
|
||||
fun initialize(titleId_: Long) {
|
||||
titleId = titleId_;
|
||||
titleId = titleId_
|
||||
load()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,24 +170,23 @@ class CheatDetailsFragment : Fragment() {
|
|||
binding.buttonOk.visibility = if (isEditing) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View?, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View?, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
val mlpAppBar = binding.toolbarCheatDetails.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarCheatDetails.layoutParams = mlpAppBar
|
||||
val mlpAppBar = binding.toolbarCheatDetails.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarCheatDetails.layoutParams = mlpAppBar
|
||||
|
||||
binding.scrollView.updatePadding(left = leftInsets, right = rightInsets)
|
||||
binding.buttonContainer.updatePadding(left = leftInsets, right = rightInsets)
|
||||
binding.scrollView.updatePadding(left = leftInsets, right = rightInsets)
|
||||
binding.buttonContainer.updatePadding(left = leftInsets, right = rightInsets)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ class CheatListFragment : Fragment() {
|
|||
left = leftInsets,
|
||||
right = rightInsets,
|
||||
bottom = barInsets.bottom +
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_fab_list)
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_fab_list)
|
||||
)
|
||||
|
||||
val mlpFab = binding.fab.layoutParams as MarginLayoutParams
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ class CheatsAdapter(
|
|||
}
|
||||
|
||||
inner class CheatViewHolder(private val binding: ListItemCheatBinding) :
|
||||
RecyclerView.ViewHolder(binding.root), View.OnClickListener,
|
||||
RecyclerView.ViewHolder(binding.root),
|
||||
View.OnClickListener,
|
||||
CompoundButton.OnCheckedChangeListener {
|
||||
private lateinit var viewModel: CheatsViewModel
|
||||
private lateinit var cheat: Cheat
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ import org.citra.citra_emu.ui.TwoPaneOnBackPressedCallback
|
|||
import org.citra.citra_emu.ui.main.MainActivity
|
||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||
|
||||
class CheatsFragment : Fragment(), SlidingPaneLayout.PanelSlideListener {
|
||||
class CheatsFragment :
|
||||
Fragment(),
|
||||
SlidingPaneLayout.PanelSlideListener {
|
||||
private var cheatListLastFocus: View? = null
|
||||
private var cheatDetailsLastFocus: View? = null
|
||||
|
||||
|
|
@ -238,7 +240,8 @@ class CheatsFragment : Fragment(), SlidingPaneLayout.PanelSlideListener {
|
|||
binding.cheatDetailsContainer.layoutParams = mlpDetails
|
||||
return insets
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@ enum class Hotkey(val button: Int) {
|
|||
QUICKSAVE(10005),
|
||||
QUICKLOAD(10006),
|
||||
TURBO_LIMIT(10007),
|
||||
ENABLE(10008);
|
||||
ENABLE(10008)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ import androidx.preference.PreferenceManager
|
|||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.features.settings.model.view.InputBindingSetting
|
||||
import org.citra.citra_emu.utils.EmulationLifecycleUtil
|
||||
import org.citra.citra_emu.utils.TurboHelper
|
||||
import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
||||
import org.citra.citra_emu.features.settings.model.view.InputBindingSetting
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
|
||||
class HotkeyUtility(
|
||||
private val screenAdjustmentUtil: ScreenAdjustmentUtil,
|
||||
|
|
@ -41,7 +41,7 @@ class HotkeyUtility(
|
|||
// Now process all internal buttons associated with this keypress
|
||||
for (button in buttonSet) {
|
||||
currentlyPressedButtons.add(button)
|
||||
//option 1 - this is the enable command, which was already handled
|
||||
// option 1 - this is the enable command, which was already handled
|
||||
if (button == Hotkey.ENABLE.button) {
|
||||
handled = true
|
||||
}
|
||||
|
|
@ -74,7 +74,8 @@ class HotkeyUtility(
|
|||
val thisKeyIsHotkey =
|
||||
!thisKeyIsEnableButton && Hotkey.entries.any { buttonSet.contains(it.button) }
|
||||
if (thisKeyIsEnableButton) {
|
||||
handled = true; hotkeyIsEnabled = false
|
||||
handled = true
|
||||
hotkeyIsEnabled = false
|
||||
}
|
||||
|
||||
for (button in buttonSet) {
|
||||
|
|
@ -109,10 +110,15 @@ class HotkeyUtility(
|
|||
fun handleHotkey(bindedButton: Int): Boolean {
|
||||
when (bindedButton) {
|
||||
Hotkey.SWAP_SCREEN.button -> screenAdjustmentUtil.swapScreen()
|
||||
|
||||
Hotkey.CYCLE_LAYOUT.button -> screenAdjustmentUtil.cycleLayouts()
|
||||
|
||||
Hotkey.CLOSE_GAME.button -> EmulationLifecycleUtil.closeGame()
|
||||
|
||||
Hotkey.PAUSE_OR_RESUME.button -> EmulationLifecycleUtil.pauseOrResume()
|
||||
|
||||
Hotkey.TURBO_LIMIT.button -> TurboHelper.toggleTurbo(true)
|
||||
|
||||
Hotkey.QUICKSAVE.button -> {
|
||||
NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT)
|
||||
Toast.makeText(
|
||||
|
|
|
|||
|
|
@ -142,4 +142,4 @@ object SettingKeys {
|
|||
external fun screen_orientation(): String
|
||||
external fun performance_overlay_position(): String
|
||||
external fun enable_secondary_display(): String
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,19 +20,63 @@ enum class BooleanSetting(
|
|||
SWAP_SCREEN(SettingKeys.swap_screen(), Settings.SECTION_LAYOUT, false),
|
||||
INSTANT_DEBUG_LOG(SettingKeys.instant_debug_log(), Settings.SECTION_DEBUG, false),
|
||||
ENABLE_RPC_SERVER(SettingKeys.enable_rpc_server(), Settings.SECTION_DEBUG, false),
|
||||
TOGGLE_UNIQUE_DATA_CONSOLE_TYPE(SettingKeys.toggle_unique_data_console_type(), Settings.SECTION_DEBUG, false),
|
||||
SWAP_EYES_3D(SettingKeys.swap_eyes_3d(),Settings.SECTION_RENDERER, false),
|
||||
TOGGLE_UNIQUE_DATA_CONSOLE_TYPE(
|
||||
SettingKeys.toggle_unique_data_console_type(),
|
||||
Settings.SECTION_DEBUG,
|
||||
false
|
||||
),
|
||||
SWAP_EYES_3D(SettingKeys.swap_eyes_3d(), Settings.SECTION_RENDERER, false),
|
||||
PERF_OVERLAY_ENABLE(SettingKeys.performance_overlay_enable(), Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_SHOW_FPS(SettingKeys.performance_overlay_show_fps(), Settings.SECTION_LAYOUT, true),
|
||||
PERF_OVERLAY_SHOW_FRAMETIME(SettingKeys.performance_overlay_show_frame_time(), Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_SHOW_SPEED(SettingKeys.performance_overlay_show_speed(), Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_SHOW_APP_RAM_USAGE(SettingKeys.performance_overlay_show_app_ram_usage(), Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_SHOW_AVAILABLE_RAM(SettingKeys.performance_overlay_show_available_ram(), Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_SHOW_BATTERY_TEMP(SettingKeys.performance_overlay_show_battery_temp(), Settings.SECTION_LAYOUT, false),
|
||||
PERF_OVERLAY_BACKGROUND(SettingKeys.performance_overlay_background(), Settings.SECTION_LAYOUT, false),
|
||||
DELAY_START_LLE_MODULES(SettingKeys.delay_start_for_lle_modules(), Settings.SECTION_DEBUG, true),
|
||||
DETERMINISTIC_ASYNC_OPERATIONS(SettingKeys.deterministic_async_operations(), Settings.SECTION_DEBUG, false),
|
||||
REQUIRED_ONLINE_LLE_MODULES(SettingKeys.enable_required_online_lle_modules(), Settings.SECTION_SYSTEM, false),
|
||||
PERF_OVERLAY_SHOW_FPS(
|
||||
SettingKeys.performance_overlay_show_fps(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
true
|
||||
),
|
||||
PERF_OVERLAY_SHOW_FRAMETIME(
|
||||
SettingKeys.performance_overlay_show_frame_time(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
false
|
||||
),
|
||||
PERF_OVERLAY_SHOW_SPEED(
|
||||
SettingKeys.performance_overlay_show_speed(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
false
|
||||
),
|
||||
PERF_OVERLAY_SHOW_APP_RAM_USAGE(
|
||||
SettingKeys.performance_overlay_show_app_ram_usage(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
false
|
||||
),
|
||||
PERF_OVERLAY_SHOW_AVAILABLE_RAM(
|
||||
SettingKeys.performance_overlay_show_available_ram(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
false
|
||||
),
|
||||
PERF_OVERLAY_SHOW_BATTERY_TEMP(
|
||||
SettingKeys.performance_overlay_show_battery_temp(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
false
|
||||
),
|
||||
PERF_OVERLAY_BACKGROUND(
|
||||
SettingKeys.performance_overlay_background(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
false
|
||||
),
|
||||
DELAY_START_LLE_MODULES(
|
||||
SettingKeys.delay_start_for_lle_modules(),
|
||||
Settings.SECTION_DEBUG,
|
||||
true
|
||||
),
|
||||
DETERMINISTIC_ASYNC_OPERATIONS(
|
||||
SettingKeys.deterministic_async_operations(),
|
||||
Settings.SECTION_DEBUG,
|
||||
false
|
||||
),
|
||||
REQUIRED_ONLINE_LLE_MODULES(
|
||||
SettingKeys.enable_required_online_lle_modules(),
|
||||
Settings.SECTION_SYSTEM,
|
||||
false
|
||||
),
|
||||
LLE_APPLETS(SettingKeys.lle_applets(), Settings.SECTION_SYSTEM, false),
|
||||
NEW_3DS(SettingKeys.is_new_3ds(), Settings.SECTION_SYSTEM, true),
|
||||
LINEAR_FILTERING(SettingKeys.filter_mode(), Settings.SECTION_RENDERER, true),
|
||||
|
|
@ -44,23 +88,43 @@ enum class BooleanSetting(
|
|||
PRELOAD_TEXTURES(SettingKeys.preload_textures(), Settings.SECTION_UTILITY, false),
|
||||
ENABLE_AUDIO_STRETCHING(SettingKeys.enable_audio_stretching(), Settings.SECTION_AUDIO, true),
|
||||
ENABLE_REALTIME_AUDIO(SettingKeys.enable_realtime_audio(), Settings.SECTION_AUDIO, false),
|
||||
SIMULATE_HEADPHONES_PLUGGED(SettingKeys.simulate_headphones_plugged(), Settings.SECTION_AUDIO, false),
|
||||
SIMULATE_HEADPHONES_PLUGGED(
|
||||
SettingKeys.simulate_headphones_plugged(),
|
||||
Settings.SECTION_AUDIO,
|
||||
false
|
||||
),
|
||||
CPU_JIT(SettingKeys.use_cpu_jit(), Settings.SECTION_CORE, true),
|
||||
HW_SHADER(SettingKeys.use_hw_shader(), Settings.SECTION_RENDERER, true),
|
||||
SHADER_JIT(SettingKeys.use_shader_jit(), Settings.SECTION_RENDERER, true),
|
||||
VSYNC(SettingKeys.use_vsync(), Settings.SECTION_RENDERER, false),
|
||||
USE_FRAME_LIMIT(SettingKeys.use_frame_limit(), Settings.SECTION_RENDERER, true),
|
||||
DEBUG_RENDERER(SettingKeys.renderer_debug(), Settings.SECTION_DEBUG, false),
|
||||
DISABLE_RIGHT_EYE_RENDER(SettingKeys.disable_right_eye_render(), Settings.SECTION_RENDERER, false),
|
||||
USE_ARTIC_BASE_CONTROLLER(SettingKeys.use_artic_base_controller(), Settings.SECTION_CONTROLS, false),
|
||||
DISABLE_RIGHT_EYE_RENDER(
|
||||
SettingKeys.disable_right_eye_render(),
|
||||
Settings.SECTION_RENDERER,
|
||||
false
|
||||
),
|
||||
USE_ARTIC_BASE_CONTROLLER(
|
||||
SettingKeys.use_artic_base_controller(),
|
||||
Settings.SECTION_CONTROLS,
|
||||
false
|
||||
),
|
||||
UPRIGHT_SCREEN(SettingKeys.upright_screen(), Settings.SECTION_LAYOUT, false),
|
||||
COMPRESS_INSTALLED_CIA_CONTENT(SettingKeys.compress_cia_installs(), Settings.SECTION_STORAGE, false),
|
||||
COMPRESS_INSTALLED_CIA_CONTENT(
|
||||
SettingKeys.compress_cia_installs(),
|
||||
Settings.SECTION_STORAGE,
|
||||
false
|
||||
),
|
||||
ASYNC_FS_OPERATIONS(SettingKeys.async_fs_operations(), Settings.SECTION_STORAGE, true),
|
||||
ANDROID_HIDE_IMAGES(SettingKeys.android_hide_images(), Settings.SECTION_MISC, false),
|
||||
APPLY_REGION_FREE_PATCH(SettingKeys.apply_region_free_patch(), Settings.SECTION_SYSTEM, true),
|
||||
USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false),
|
||||
ENABLE_SECONDARY_DISPLAY(SettingKeys.enable_secondary_display(), Settings.SECTION_LAYOUT, true),
|
||||
SIMULATE_3DS_GPU_TIMINGS(SettingKeys.simulate_3ds_gpu_timings(), Settings.SECTION_RENDERER, true);
|
||||
SIMULATE_3DS_GPU_TIMINGS(
|
||||
SettingKeys.simulate_3ds_gpu_timings(),
|
||||
Settings.SECTION_RENDERER,
|
||||
true
|
||||
);
|
||||
|
||||
override var boolean: Boolean = defaultValue
|
||||
|
||||
|
|
@ -80,7 +144,7 @@ enum class BooleanSetting(
|
|||
companion object {
|
||||
private val NOT_RUNTIME_EDITABLE = listOf(
|
||||
PLUGIN_LOADER,
|
||||
ALLOW_PLUGIN_LOADER,
|
||||
ALLOW_PLUGIN_LOADER,
|
||||
ASYNC_SHADERS,
|
||||
DELAY_START_LLE_MODULES,
|
||||
DETERMINISTIC_ASYNC_OPERATIONS,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,12 @@ enum class FloatSetting(
|
|||
override val section: String,
|
||||
override val defaultValue: Float
|
||||
) : AbstractFloatSetting {
|
||||
LARGE_SCREEN_PROPORTION(SettingKeys.large_screen_proportion(),Settings.SECTION_LAYOUT,2.25f),
|
||||
SECOND_SCREEN_OPACITY(SettingKeys.custom_second_layer_opacity(), Settings.SECTION_RENDERER, 100f),
|
||||
LARGE_SCREEN_PROPORTION(SettingKeys.large_screen_proportion(), Settings.SECTION_LAYOUT, 2.25f),
|
||||
SECOND_SCREEN_OPACITY(
|
||||
SettingKeys.custom_second_layer_opacity(),
|
||||
Settings.SECTION_RENDERER,
|
||||
100f
|
||||
),
|
||||
BACKGROUND_RED(SettingKeys.bg_red(), Settings.SECTION_RENDERER, 0f),
|
||||
BACKGROUND_BLUE(SettingKeys.bg_blue(), Settings.SECTION_RENDERER, 0f),
|
||||
BACKGROUND_GREEN(SettingKeys.bg_green(), Settings.SECTION_RENDERER, 0f);
|
||||
|
|
|
|||
|
|
@ -13,10 +13,15 @@ enum class IntListSetting(
|
|||
val canBeEmpty: Boolean = true
|
||||
) : AbstractListSetting<Int> {
|
||||
|
||||
LAYOUTS_TO_CYCLE(SettingKeys.layouts_to_cycle(), Settings.SECTION_LAYOUT, listOf(0, 1, 2, 3, 4, 5), canBeEmpty = false);
|
||||
LAYOUTS_TO_CYCLE(
|
||||
SettingKeys.layouts_to_cycle(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
listOf(0, 1, 2, 3, 4, 5),
|
||||
canBeEmpty = false
|
||||
);
|
||||
|
||||
private var backingList: List<Int> = defaultValue
|
||||
private var lastValidList : List<Int> = defaultValue
|
||||
private var lastValidList: List<Int> = defaultValue
|
||||
|
||||
override var list: List<Int>
|
||||
get() = backingList
|
||||
|
|
@ -32,7 +37,6 @@ enum class IntListSetting(
|
|||
override val valueAsString: String
|
||||
get() = list.joinToString()
|
||||
|
||||
|
||||
override val isRuntimeEditable: Boolean
|
||||
get() {
|
||||
for (setting in NOT_RUNTIME_EDITABLE) {
|
||||
|
|
@ -46,8 +50,7 @@ enum class IntListSetting(
|
|||
companion object {
|
||||
private val NOT_RUNTIME_EDITABLE: List<IntListSetting> = emptyList()
|
||||
|
||||
fun from(key: String): IntListSetting? =
|
||||
values().firstOrNull { it.key == key }
|
||||
fun from(key: String): IntListSetting? = values().firstOrNull { it.key == key }
|
||||
|
||||
fun clear() = values().forEach { it.list = it.defaultValue }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,26 +26,30 @@ enum class IntSetting(
|
|||
CARDBOARD_X_SHIFT(SettingKeys.cardboard_x_shift(), Settings.SECTION_LAYOUT, 0),
|
||||
CARDBOARD_Y_SHIFT(SettingKeys.cardboard_y_shift(), Settings.SECTION_LAYOUT, 0),
|
||||
SCREEN_LAYOUT(SettingKeys.layout_option(), Settings.SECTION_LAYOUT, 0),
|
||||
SMALL_SCREEN_POSITION(SettingKeys.small_screen_position(),Settings.SECTION_LAYOUT,0),
|
||||
LANDSCAPE_TOP_X(SettingKeys.custom_top_x(),Settings.SECTION_LAYOUT,0),
|
||||
LANDSCAPE_TOP_Y(SettingKeys.custom_top_y(),Settings.SECTION_LAYOUT,0),
|
||||
LANDSCAPE_TOP_WIDTH(SettingKeys.custom_top_width(),Settings.SECTION_LAYOUT,800),
|
||||
LANDSCAPE_TOP_HEIGHT(SettingKeys.custom_top_height(),Settings.SECTION_LAYOUT,480),
|
||||
LANDSCAPE_BOTTOM_X(SettingKeys.custom_bottom_x(),Settings.SECTION_LAYOUT,80),
|
||||
LANDSCAPE_BOTTOM_Y(SettingKeys.custom_bottom_y(),Settings.SECTION_LAYOUT,480),
|
||||
LANDSCAPE_BOTTOM_WIDTH(SettingKeys.custom_bottom_width(),Settings.SECTION_LAYOUT,640),
|
||||
LANDSCAPE_BOTTOM_HEIGHT(SettingKeys.custom_bottom_height(),Settings.SECTION_LAYOUT,480),
|
||||
SCREEN_GAP(SettingKeys.screen_gap(),Settings.SECTION_LAYOUT,0),
|
||||
PORTRAIT_SCREEN_LAYOUT(SettingKeys.portrait_layout_option(),Settings.SECTION_LAYOUT,0),
|
||||
SECONDARY_DISPLAY_LAYOUT(SettingKeys.secondary_display_layout(),Settings.SECTION_LAYOUT,4),
|
||||
PORTRAIT_TOP_X(SettingKeys.custom_portrait_top_x(),Settings.SECTION_LAYOUT,0),
|
||||
PORTRAIT_TOP_Y(SettingKeys.custom_portrait_top_y(),Settings.SECTION_LAYOUT,0),
|
||||
PORTRAIT_TOP_WIDTH(SettingKeys.custom_portrait_top_width(),Settings.SECTION_LAYOUT,800),
|
||||
PORTRAIT_TOP_HEIGHT(SettingKeys.custom_portrait_top_height(),Settings.SECTION_LAYOUT,480),
|
||||
PORTRAIT_BOTTOM_X(SettingKeys.custom_portrait_bottom_x(),Settings.SECTION_LAYOUT,80),
|
||||
PORTRAIT_BOTTOM_Y(SettingKeys.custom_portrait_bottom_y(),Settings.SECTION_LAYOUT,480),
|
||||
PORTRAIT_BOTTOM_WIDTH(SettingKeys.custom_portrait_bottom_width(),Settings.SECTION_LAYOUT,640),
|
||||
PORTRAIT_BOTTOM_HEIGHT(SettingKeys.custom_portrait_bottom_height(),Settings.SECTION_LAYOUT,480),
|
||||
SMALL_SCREEN_POSITION(SettingKeys.small_screen_position(), Settings.SECTION_LAYOUT, 0),
|
||||
LANDSCAPE_TOP_X(SettingKeys.custom_top_x(), Settings.SECTION_LAYOUT, 0),
|
||||
LANDSCAPE_TOP_Y(SettingKeys.custom_top_y(), Settings.SECTION_LAYOUT, 0),
|
||||
LANDSCAPE_TOP_WIDTH(SettingKeys.custom_top_width(), Settings.SECTION_LAYOUT, 800),
|
||||
LANDSCAPE_TOP_HEIGHT(SettingKeys.custom_top_height(), Settings.SECTION_LAYOUT, 480),
|
||||
LANDSCAPE_BOTTOM_X(SettingKeys.custom_bottom_x(), Settings.SECTION_LAYOUT, 80),
|
||||
LANDSCAPE_BOTTOM_Y(SettingKeys.custom_bottom_y(), Settings.SECTION_LAYOUT, 480),
|
||||
LANDSCAPE_BOTTOM_WIDTH(SettingKeys.custom_bottom_width(), Settings.SECTION_LAYOUT, 640),
|
||||
LANDSCAPE_BOTTOM_HEIGHT(SettingKeys.custom_bottom_height(), Settings.SECTION_LAYOUT, 480),
|
||||
SCREEN_GAP(SettingKeys.screen_gap(), Settings.SECTION_LAYOUT, 0),
|
||||
PORTRAIT_SCREEN_LAYOUT(SettingKeys.portrait_layout_option(), Settings.SECTION_LAYOUT, 0),
|
||||
SECONDARY_DISPLAY_LAYOUT(SettingKeys.secondary_display_layout(), Settings.SECTION_LAYOUT, 4),
|
||||
PORTRAIT_TOP_X(SettingKeys.custom_portrait_top_x(), Settings.SECTION_LAYOUT, 0),
|
||||
PORTRAIT_TOP_Y(SettingKeys.custom_portrait_top_y(), Settings.SECTION_LAYOUT, 0),
|
||||
PORTRAIT_TOP_WIDTH(SettingKeys.custom_portrait_top_width(), Settings.SECTION_LAYOUT, 800),
|
||||
PORTRAIT_TOP_HEIGHT(SettingKeys.custom_portrait_top_height(), Settings.SECTION_LAYOUT, 480),
|
||||
PORTRAIT_BOTTOM_X(SettingKeys.custom_portrait_bottom_x(), Settings.SECTION_LAYOUT, 80),
|
||||
PORTRAIT_BOTTOM_Y(SettingKeys.custom_portrait_bottom_y(), Settings.SECTION_LAYOUT, 480),
|
||||
PORTRAIT_BOTTOM_WIDTH(SettingKeys.custom_portrait_bottom_width(), Settings.SECTION_LAYOUT, 640),
|
||||
PORTRAIT_BOTTOM_HEIGHT(
|
||||
SettingKeys.custom_portrait_bottom_height(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
480
|
||||
),
|
||||
AUDIO_INPUT_TYPE(SettingKeys.input_type(), Settings.SECTION_AUDIO, 0),
|
||||
CPU_CLOCK_SPEED(SettingKeys.cpu_clock_percentage(), Settings.SECTION_CORE, 100),
|
||||
TEXTURE_FILTER(SettingKeys.texture_filter(), Settings.SECTION_RENDERER, 0),
|
||||
|
|
@ -54,8 +58,12 @@ enum class IntSetting(
|
|||
DELAY_RENDER_THREAD_US(SettingKeys.delay_game_render_thread_us(), Settings.SECTION_RENDERER, 0),
|
||||
ORIENTATION_OPTION(SettingKeys.screen_orientation(), Settings.SECTION_LAYOUT, 2),
|
||||
TURBO_LIMIT(SettingKeys.turbo_limit(), Settings.SECTION_CORE, 200),
|
||||
PERFORMANCE_OVERLAY_POSITION(SettingKeys.performance_overlay_position(), Settings.SECTION_LAYOUT, 0),
|
||||
RENDER_3D_WHICH_DISPLAY(SettingKeys.render_3d_which_display(),Settings.SECTION_RENDERER,0),
|
||||
PERFORMANCE_OVERLAY_POSITION(
|
||||
SettingKeys.performance_overlay_position(),
|
||||
Settings.SECTION_LAYOUT,
|
||||
0
|
||||
),
|
||||
RENDER_3D_WHICH_DISPLAY(SettingKeys.render_3d_which_display(), Settings.SECTION_RENDERER, 0),
|
||||
ASPECT_RATIO(SettingKeys.aspect_ratio(), Settings.SECTION_LAYOUT, 0);
|
||||
|
||||
override var int: Int = defaultValue
|
||||
|
|
@ -78,7 +86,7 @@ enum class IntSetting(
|
|||
EMULATED_REGION,
|
||||
INIT_CLOCK,
|
||||
GRAPHICS_API,
|
||||
AUDIO_INPUT_TYPE,
|
||||
AUDIO_INPUT_TYPE
|
||||
)
|
||||
|
||||
fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ class SettingSection(val name: String) {
|
|||
* @param key Used to retrieve the Setting.
|
||||
* @return A Setting object (you should probably cast this before using)
|
||||
*/
|
||||
fun getSetting(key: String): AbstractSetting? {
|
||||
return settings[key]
|
||||
}
|
||||
fun getSetting(key: String): AbstractSetting? = settings[key]
|
||||
|
||||
fun mergeSection(settingSection: SettingSection) {
|
||||
for (setting in settingSection.settings.values) {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
package org.citra.citra_emu.features.settings.model
|
||||
|
||||
import android.text.TextUtils
|
||||
import java.util.TreeMap
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.features.settings.ui.SettingsActivityView
|
||||
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
||||
import java.util.TreeMap
|
||||
|
||||
class Settings {
|
||||
private var gameId: String? = null
|
||||
|
|
@ -33,9 +33,7 @@ class Settings {
|
|||
|
||||
var sections: HashMap<String, SettingSection?> = SettingsSectionMap()
|
||||
|
||||
fun getSection(sectionName: String): SettingSection? {
|
||||
return sections[sectionName]
|
||||
}
|
||||
fun getSection(sectionName: String): SettingSection? = sections[sectionName]
|
||||
|
||||
val isEmpty: Boolean
|
||||
get() = sections.isEmpty()
|
||||
|
|
@ -182,7 +180,7 @@ class Settings {
|
|||
KEY_BUTTON_RIGHT
|
||||
)
|
||||
val axisTitles = listOf(
|
||||
R.string.controller_axis_vertical,
|
||||
R.string.controller_axis_vertical,
|
||||
R.string.controller_axis_horizontal
|
||||
)
|
||||
val dPadTitles = listOf(
|
||||
|
|
@ -250,4 +248,4 @@ class Settings {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,17 @@ enum class StringSetting(
|
|||
CAMERA_INNER_NAME(SettingKeys.camera_inner_name(), Settings.SECTION_CAMERA, "ndk"),
|
||||
CAMERA_INNER_CONFIG(SettingKeys.camera_inner_config(), Settings.SECTION_CAMERA, "_front"),
|
||||
CAMERA_OUTER_LEFT_NAME(SettingKeys.camera_outer_left_name(), Settings.SECTION_CAMERA, "ndk"),
|
||||
CAMERA_OUTER_LEFT_CONFIG(SettingKeys.camera_outer_left_config(), Settings.SECTION_CAMERA, "_back"),
|
||||
CAMERA_OUTER_LEFT_CONFIG(
|
||||
SettingKeys.camera_outer_left_config(),
|
||||
Settings.SECTION_CAMERA,
|
||||
"_back"
|
||||
),
|
||||
CAMERA_OUTER_RIGHT_NAME(SettingKeys.camera_outer_right_name(), Settings.SECTION_CAMERA, "ndk"),
|
||||
CAMERA_OUTER_RIGHT_CONFIG(SettingKeys.camera_outer_right_config(), Settings.SECTION_CAMERA, "_back");
|
||||
CAMERA_OUTER_RIGHT_CONFIG(
|
||||
SettingKeys.camera_outer_right_config(),
|
||||
Settings.SECTION_CAMERA,
|
||||
"_back"
|
||||
);
|
||||
|
||||
override var string: String = defaultValue
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
package org.citra.citra_emu.features.settings.model.view
|
||||
|
||||
class HeaderSetting(titleId: Int,descId: Int = 0) : SettingsItem(null, titleId, descId) {
|
||||
class HeaderSetting(titleId: Int, descId: Int = 0) : SettingsItem(null, titleId, descId) {
|
||||
override val type = TYPE_HEADER
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,8 @@ import org.citra.citra_emu.features.settings.model.AbstractSetting
|
|||
import org.citra.citra_emu.features.settings.model.AbstractStringSetting
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
|
||||
class InputBindingSetting(
|
||||
val abstractSetting: AbstractSetting,
|
||||
titleId: Int
|
||||
) : SettingsItem(abstractSetting, titleId, 0) {
|
||||
class InputBindingSetting(val abstractSetting: AbstractSetting, titleId: Int) :
|
||||
SettingsItem(abstractSetting, titleId, 0) {
|
||||
private val context: Context get() = CitraApplication.appContext
|
||||
private val preferences: SharedPreferences
|
||||
get() = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
|
@ -39,74 +37,66 @@ class InputBindingSetting(
|
|||
/**
|
||||
* Returns true if this key is for the 3DS Circle Pad
|
||||
*/
|
||||
fun isCirclePad(): Boolean =
|
||||
when (abstractSetting.key) {
|
||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
||||
Settings.KEY_CIRCLEPAD_AXIS_VERTICAL -> true
|
||||
fun isCirclePad(): Boolean = when (abstractSetting.key) {
|
||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
||||
Settings.KEY_CIRCLEPAD_AXIS_VERTICAL -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this key is for a horizontal axis for a 3DS analog stick or D-pad
|
||||
*/
|
||||
fun isHorizontalOrientation(): Boolean =
|
||||
when (abstractSetting.key) {
|
||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
||||
Settings.KEY_DPAD_AXIS_HORIZONTAL -> true
|
||||
fun isHorizontalOrientation(): Boolean = when (abstractSetting.key) {
|
||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
||||
Settings.KEY_DPAD_AXIS_HORIZONTAL -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this key is for the 3DS C-Stick
|
||||
*/
|
||||
fun isCStick(): Boolean =
|
||||
when (abstractSetting.key) {
|
||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
||||
Settings.KEY_CSTICK_AXIS_VERTICAL -> true
|
||||
fun isCStick(): Boolean = when (abstractSetting.key) {
|
||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
||||
Settings.KEY_CSTICK_AXIS_VERTICAL -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this key is for the 3DS D-Pad
|
||||
*/
|
||||
fun isDPad(): Boolean =
|
||||
when (abstractSetting.key) {
|
||||
Settings.KEY_DPAD_AXIS_HORIZONTAL,
|
||||
Settings.KEY_DPAD_AXIS_VERTICAL -> true
|
||||
fun isDPad(): Boolean = when (abstractSetting.key) {
|
||||
Settings.KEY_DPAD_AXIS_HORIZONTAL,
|
||||
Settings.KEY_DPAD_AXIS_VERTICAL -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
/**
|
||||
* Returns true if this key is for the 3DS L/R or ZL/ZR buttons. Note, these are not real
|
||||
* triggers on the 3DS, but we support them as such on a physical gamepad.
|
||||
*/
|
||||
fun isTrigger(): Boolean =
|
||||
when (abstractSetting.key) {
|
||||
Settings.KEY_BUTTON_L,
|
||||
Settings.KEY_BUTTON_R,
|
||||
Settings.KEY_BUTTON_ZL,
|
||||
Settings.KEY_BUTTON_ZR -> true
|
||||
fun isTrigger(): Boolean = when (abstractSetting.key) {
|
||||
Settings.KEY_BUTTON_L,
|
||||
Settings.KEY_BUTTON_R,
|
||||
Settings.KEY_BUTTON_ZL,
|
||||
Settings.KEY_BUTTON_ZR -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a gamepad axis can be used to map this key.
|
||||
*/
|
||||
fun isAxisMappingSupported(): Boolean {
|
||||
return isCirclePad() || isCStick() || isDPad() || isTrigger()
|
||||
}
|
||||
fun isAxisMappingSupported(): Boolean = isCirclePad() || isCStick() || isDPad() || isTrigger()
|
||||
|
||||
/**
|
||||
* Returns true if a gamepad button can be used to map this key.
|
||||
*/
|
||||
fun isButtonMappingSupported(): Boolean {
|
||||
return !isAxisMappingSupported() || isTrigger()
|
||||
}
|
||||
fun isButtonMappingSupported(): Boolean = !isAxisMappingSupported() || isTrigger()
|
||||
|
||||
/**
|
||||
* Returns the Citra button code for the settings key.
|
||||
|
|
@ -177,10 +167,10 @@ class InputBindingSetting(
|
|||
} catch (e: ClassCastException) {
|
||||
// if this is an int pref, either old button or an axis, so just remove it
|
||||
preferences.edit().remove(oldKey).apply()
|
||||
return;
|
||||
return
|
||||
}
|
||||
buttonCodes.remove(buttonCode.toString());
|
||||
preferences.edit().putStringSet(oldKey,buttonCodes).apply()
|
||||
buttonCodes.remove(buttonCode.toString())
|
||||
preferences.edit().putStringSet(oldKey, buttonCodes).apply()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +187,7 @@ class InputBindingSetting(
|
|||
// Cleanup old mapping for this setting
|
||||
removeOldMapping()
|
||||
|
||||
editor.putStringSet(key, buttonCodes.mapTo(mutableSetOf()) {it.toString()})
|
||||
editor.putStringSet(key, buttonCodes.mapTo(mutableSetOf()) { it.toString() })
|
||||
|
||||
// Write next reverse mapping for future cleanup
|
||||
editor.putString(reverseKey, key)
|
||||
|
|
@ -217,7 +207,7 @@ class InputBindingSetting(
|
|||
preferences.edit()
|
||||
.putInt(getInputAxisOrientationKey(axis), if (isHorizontalOrientation()) 0 else 1)
|
||||
.putInt(getInputAxisButtonKey(axis), value)
|
||||
.putBoolean(getInputAxisInvertedKey(axis),inverted)
|
||||
.putBoolean(getInputAxisInvertedKey(axis), inverted)
|
||||
// Write next reverse mapping for future cleanup
|
||||
.putString(reverseKey, getInputAxisKey(axis))
|
||||
.apply()
|
||||
|
|
@ -271,9 +261,8 @@ class InputBindingSetting(
|
|||
companion object {
|
||||
private const val INPUT_MAPPING_PREFIX = "InputMapping"
|
||||
|
||||
private fun toTitleCase(raw: String): String =
|
||||
raw.replace("_", " ").lowercase()
|
||||
.split(" ").joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
||||
private fun toTitleCase(raw: String): String = raw.replace("_", " ").lowercase()
|
||||
.split(" ").joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
||||
|
||||
private const val BUTTON_NAME_L3 = "Button L3"
|
||||
private const val BUTTON_NAME_R3 = "Button R3"
|
||||
|
|
@ -287,15 +276,15 @@ class InputBindingSetting(
|
|||
LINUX_BTN_DPAD_RIGHT to "Dpad Right"
|
||||
)
|
||||
|
||||
fun getButtonName(keyCode: Int): String =
|
||||
buttonNameOverrides[keyCode]
|
||||
?: toTitleCase(KeyEvent.keyCodeToString(keyCode).removePrefix("KEYCODE_"))
|
||||
fun getButtonName(keyCode: Int): String = buttonNameOverrides[keyCode]
|
||||
?: toTitleCase(KeyEvent.keyCodeToString(keyCode).removePrefix("KEYCODE_"))
|
||||
|
||||
private data class DefaultButtonMapping(
|
||||
val settingKey: String,
|
||||
val hostKeyCode: Int,
|
||||
val guestButtonCode: Int
|
||||
)
|
||||
|
||||
// Auto-map always sets inverted = false. Users needing inverted axes should remap manually.
|
||||
private data class DefaultAxisMapping(
|
||||
val settingKey: String,
|
||||
|
|
@ -306,45 +295,153 @@ class InputBindingSetting(
|
|||
)
|
||||
|
||||
private val xboxFaceButtonMappings = listOf(
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_A, KeyEvent.KEYCODE_BUTTON_B, NativeLibrary.ButtonType.BUTTON_A),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_B, KeyEvent.KEYCODE_BUTTON_A, NativeLibrary.ButtonType.BUTTON_B),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_X, KeyEvent.KEYCODE_BUTTON_Y, NativeLibrary.ButtonType.BUTTON_X),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_Y, KeyEvent.KEYCODE_BUTTON_X, NativeLibrary.ButtonType.BUTTON_Y)
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_A,
|
||||
KeyEvent.KEYCODE_BUTTON_B,
|
||||
NativeLibrary.ButtonType.BUTTON_A
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_B,
|
||||
KeyEvent.KEYCODE_BUTTON_A,
|
||||
NativeLibrary.ButtonType.BUTTON_B
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_X,
|
||||
KeyEvent.KEYCODE_BUTTON_Y,
|
||||
NativeLibrary.ButtonType.BUTTON_X
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_Y,
|
||||
KeyEvent.KEYCODE_BUTTON_X,
|
||||
NativeLibrary.ButtonType.BUTTON_Y
|
||||
)
|
||||
)
|
||||
|
||||
private val nintendoFaceButtonMappings = listOf(
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_A, KeyEvent.KEYCODE_BUTTON_A, NativeLibrary.ButtonType.BUTTON_A),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_B, KeyEvent.KEYCODE_BUTTON_B, NativeLibrary.ButtonType.BUTTON_B),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_X, KeyEvent.KEYCODE_BUTTON_X, NativeLibrary.ButtonType.BUTTON_X),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_Y, KeyEvent.KEYCODE_BUTTON_Y, NativeLibrary.ButtonType.BUTTON_Y)
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_A,
|
||||
KeyEvent.KEYCODE_BUTTON_A,
|
||||
NativeLibrary.ButtonType.BUTTON_A
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_B,
|
||||
KeyEvent.KEYCODE_BUTTON_B,
|
||||
NativeLibrary.ButtonType.BUTTON_B
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_X,
|
||||
KeyEvent.KEYCODE_BUTTON_X,
|
||||
NativeLibrary.ButtonType.BUTTON_X
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_Y,
|
||||
KeyEvent.KEYCODE_BUTTON_Y,
|
||||
NativeLibrary.ButtonType.BUTTON_Y
|
||||
)
|
||||
)
|
||||
|
||||
private val commonButtonMappings = listOf(
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_L, KeyEvent.KEYCODE_BUTTON_L1, NativeLibrary.ButtonType.TRIGGER_L),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_R, KeyEvent.KEYCODE_BUTTON_R1, NativeLibrary.ButtonType.TRIGGER_R),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_ZL, KeyEvent.KEYCODE_BUTTON_L2, NativeLibrary.ButtonType.BUTTON_ZL),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_ZR, KeyEvent.KEYCODE_BUTTON_R2, NativeLibrary.ButtonType.BUTTON_ZR),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_SELECT, KeyEvent.KEYCODE_BUTTON_SELECT, NativeLibrary.ButtonType.BUTTON_SELECT),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_START, KeyEvent.KEYCODE_BUTTON_START, NativeLibrary.ButtonType.BUTTON_START)
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_L,
|
||||
KeyEvent.KEYCODE_BUTTON_L1,
|
||||
NativeLibrary.ButtonType.TRIGGER_L
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_R,
|
||||
KeyEvent.KEYCODE_BUTTON_R1,
|
||||
NativeLibrary.ButtonType.TRIGGER_R
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_ZL,
|
||||
KeyEvent.KEYCODE_BUTTON_L2,
|
||||
NativeLibrary.ButtonType.BUTTON_ZL
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_ZR,
|
||||
KeyEvent.KEYCODE_BUTTON_R2,
|
||||
NativeLibrary.ButtonType.BUTTON_ZR
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_SELECT,
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||
NativeLibrary.ButtonType.BUTTON_SELECT
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_START,
|
||||
KeyEvent.KEYCODE_BUTTON_START,
|
||||
NativeLibrary.ButtonType.BUTTON_START
|
||||
)
|
||||
)
|
||||
|
||||
private val dpadButtonMappings = listOf(
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_UP, KeyEvent.KEYCODE_DPAD_UP, NativeLibrary.ButtonType.DPAD_UP),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_DOWN, KeyEvent.KEYCODE_DPAD_DOWN, NativeLibrary.ButtonType.DPAD_DOWN),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_LEFT, KeyEvent.KEYCODE_DPAD_LEFT, NativeLibrary.ButtonType.DPAD_LEFT),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_RIGHT, KeyEvent.KEYCODE_DPAD_RIGHT, NativeLibrary.ButtonType.DPAD_RIGHT)
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_UP,
|
||||
KeyEvent.KEYCODE_DPAD_UP,
|
||||
NativeLibrary.ButtonType.DPAD_UP
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
NativeLibrary.ButtonType.DPAD_DOWN
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_LEFT,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||
NativeLibrary.ButtonType.DPAD_LEFT
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_RIGHT,
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
NativeLibrary.ButtonType.DPAD_RIGHT
|
||||
)
|
||||
)
|
||||
|
||||
private val stickAxisMappings = listOf(
|
||||
DefaultAxisMapping(Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL, MotionEvent.AXIS_X, NativeLibrary.ButtonType.STICK_LEFT, 0, false),
|
||||
DefaultAxisMapping(Settings.KEY_CIRCLEPAD_AXIS_VERTICAL, MotionEvent.AXIS_Y, NativeLibrary.ButtonType.STICK_LEFT, 1, false),
|
||||
DefaultAxisMapping(Settings.KEY_CSTICK_AXIS_HORIZONTAL, MotionEvent.AXIS_Z, NativeLibrary.ButtonType.STICK_C, 0, false),
|
||||
DefaultAxisMapping(Settings.KEY_CSTICK_AXIS_VERTICAL, MotionEvent.AXIS_RZ, NativeLibrary.ButtonType.STICK_C, 1, false)
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
||||
MotionEvent.AXIS_X,
|
||||
NativeLibrary.ButtonType.STICK_LEFT,
|
||||
0,
|
||||
false
|
||||
),
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_CIRCLEPAD_AXIS_VERTICAL,
|
||||
MotionEvent.AXIS_Y,
|
||||
NativeLibrary.ButtonType.STICK_LEFT,
|
||||
1,
|
||||
false
|
||||
),
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
||||
MotionEvent.AXIS_Z,
|
||||
NativeLibrary.ButtonType.STICK_C,
|
||||
0,
|
||||
false
|
||||
),
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_CSTICK_AXIS_VERTICAL,
|
||||
MotionEvent.AXIS_RZ,
|
||||
NativeLibrary.ButtonType.STICK_C,
|
||||
1,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
private val dpadAxisMappings = listOf(
|
||||
DefaultAxisMapping(Settings.KEY_DPAD_AXIS_HORIZONTAL, MotionEvent.AXIS_HAT_X, NativeLibrary.ButtonType.DPAD, 0, false),
|
||||
DefaultAxisMapping(Settings.KEY_DPAD_AXIS_VERTICAL, MotionEvent.AXIS_HAT_Y, NativeLibrary.ButtonType.DPAD, 1, false)
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_DPAD_AXIS_HORIZONTAL,
|
||||
MotionEvent.AXIS_HAT_X,
|
||||
NativeLibrary.ButtonType.DPAD,
|
||||
0,
|
||||
false
|
||||
),
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_DPAD_AXIS_VERTICAL,
|
||||
MotionEvent.AXIS_HAT_Y,
|
||||
NativeLibrary.ButtonType.DPAD,
|
||||
1,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
// Nintendo Switch Joy-Con specific mappings.
|
||||
|
|
@ -360,37 +457,93 @@ class InputBindingSetting(
|
|||
// KEYCODE_UNKNOWN with these scan codes because Android's input layer doesn't
|
||||
// translate them to KEYCODE_DPAD_*. translateEventToKeyId() falls back to
|
||||
// the scan code in that case.
|
||||
private const val LINUX_BTN_DPAD_UP = 0x220 // 544
|
||||
private const val LINUX_BTN_DPAD_DOWN = 0x221 // 545
|
||||
private const val LINUX_BTN_DPAD_LEFT = 0x222 // 546
|
||||
private const val LINUX_BTN_DPAD_UP = 0x220 // 544
|
||||
private const val LINUX_BTN_DPAD_DOWN = 0x221 // 545
|
||||
private const val LINUX_BTN_DPAD_LEFT = 0x222 // 546
|
||||
private const val LINUX_BTN_DPAD_RIGHT = 0x223 // 547
|
||||
|
||||
// Joy-Con face buttons: A/B are swapped by Android's evdev layer, but X/Y are not.
|
||||
// This is different from both the standard Xbox table (full swap) and the
|
||||
// Nintendo table (no swap).
|
||||
private val joyconFaceButtonMappings = listOf(
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_A, KeyEvent.KEYCODE_BUTTON_B, NativeLibrary.ButtonType.BUTTON_A),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_B, KeyEvent.KEYCODE_BUTTON_A, NativeLibrary.ButtonType.BUTTON_B),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_X, KeyEvent.KEYCODE_BUTTON_X, NativeLibrary.ButtonType.BUTTON_X),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_Y, KeyEvent.KEYCODE_BUTTON_Y, NativeLibrary.ButtonType.BUTTON_Y)
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_A,
|
||||
KeyEvent.KEYCODE_BUTTON_B,
|
||||
NativeLibrary.ButtonType.BUTTON_A
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_B,
|
||||
KeyEvent.KEYCODE_BUTTON_A,
|
||||
NativeLibrary.ButtonType.BUTTON_B
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_X,
|
||||
KeyEvent.KEYCODE_BUTTON_X,
|
||||
NativeLibrary.ButtonType.BUTTON_X
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_Y,
|
||||
KeyEvent.KEYCODE_BUTTON_Y,
|
||||
NativeLibrary.ButtonType.BUTTON_Y
|
||||
)
|
||||
)
|
||||
|
||||
// Joy-Con D-pad: uses Linux scan codes because Android reports BTN_DPAD_* as KEYCODE_UNKNOWN
|
||||
private val joyconDpadButtonMappings = listOf(
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_UP, LINUX_BTN_DPAD_UP, NativeLibrary.ButtonType.DPAD_UP),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_DOWN, LINUX_BTN_DPAD_DOWN, NativeLibrary.ButtonType.DPAD_DOWN),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_LEFT, LINUX_BTN_DPAD_LEFT, NativeLibrary.ButtonType.DPAD_LEFT),
|
||||
DefaultButtonMapping(Settings.KEY_BUTTON_RIGHT, LINUX_BTN_DPAD_RIGHT, NativeLibrary.ButtonType.DPAD_RIGHT)
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_UP,
|
||||
LINUX_BTN_DPAD_UP,
|
||||
NativeLibrary.ButtonType.DPAD_UP
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_DOWN,
|
||||
LINUX_BTN_DPAD_DOWN,
|
||||
NativeLibrary.ButtonType.DPAD_DOWN
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_LEFT,
|
||||
LINUX_BTN_DPAD_LEFT,
|
||||
NativeLibrary.ButtonType.DPAD_LEFT
|
||||
),
|
||||
DefaultButtonMapping(
|
||||
Settings.KEY_BUTTON_RIGHT,
|
||||
LINUX_BTN_DPAD_RIGHT,
|
||||
NativeLibrary.ButtonType.DPAD_RIGHT
|
||||
)
|
||||
)
|
||||
|
||||
// Joy-Con sticks: left stick is AXIS_X/Y (standard), right stick is AXIS_RX/RY
|
||||
// (not Z/RZ like most controllers). The horizontal axis is inverted relative to
|
||||
// the standard orientation - verified empirically on paired Joy-Cons via Bluetooth.
|
||||
private val joyconStickAxisMappings = listOf(
|
||||
DefaultAxisMapping(Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL, MotionEvent.AXIS_X, NativeLibrary.ButtonType.STICK_LEFT, 0, false),
|
||||
DefaultAxisMapping(Settings.KEY_CIRCLEPAD_AXIS_VERTICAL, MotionEvent.AXIS_Y, NativeLibrary.ButtonType.STICK_LEFT, 1, false),
|
||||
DefaultAxisMapping(Settings.KEY_CSTICK_AXIS_HORIZONTAL, MotionEvent.AXIS_RX, NativeLibrary.ButtonType.STICK_C, 0, true),
|
||||
DefaultAxisMapping(Settings.KEY_CSTICK_AXIS_VERTICAL, MotionEvent.AXIS_RY, NativeLibrary.ButtonType.STICK_C, 1, false)
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_CIRCLEPAD_AXIS_HORIZONTAL,
|
||||
MotionEvent.AXIS_X,
|
||||
NativeLibrary.ButtonType.STICK_LEFT,
|
||||
0,
|
||||
false
|
||||
),
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_CIRCLEPAD_AXIS_VERTICAL,
|
||||
MotionEvent.AXIS_Y,
|
||||
NativeLibrary.ButtonType.STICK_LEFT,
|
||||
1,
|
||||
false
|
||||
),
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_CSTICK_AXIS_HORIZONTAL,
|
||||
MotionEvent.AXIS_RX,
|
||||
NativeLibrary.ButtonType.STICK_C,
|
||||
0,
|
||||
true
|
||||
),
|
||||
DefaultAxisMapping(
|
||||
Settings.KEY_CSTICK_AXIS_VERTICAL,
|
||||
MotionEvent.AXIS_RY,
|
||||
NativeLibrary.ButtonType.STICK_C,
|
||||
1,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
@ -417,9 +570,11 @@ class InputBindingSetting(
|
|||
}
|
||||
|
||||
private val allBindingKeys: Set<String> by lazy {
|
||||
(Settings.buttonKeys + Settings.triggerKeys +
|
||||
Settings.circlePadKeys + Settings.cStickKeys + Settings.dPadAxisKeys +
|
||||
Settings.dPadButtonKeys).toSet()
|
||||
(
|
||||
Settings.buttonKeys + Settings.triggerKeys +
|
||||
Settings.circlePadKeys + Settings.cStickKeys + Settings.dPadAxisKeys +
|
||||
Settings.dPadButtonKeys
|
||||
).toSet()
|
||||
}
|
||||
|
||||
fun clearAllBindings() {
|
||||
|
|
@ -509,35 +664,37 @@ class InputBindingSetting(
|
|||
/**
|
||||
* Returns the settings key for the specified Citra button code.
|
||||
*/
|
||||
private fun getButtonKey(buttonCode: Int): String =
|
||||
when (buttonCode) {
|
||||
NativeLibrary.ButtonType.BUTTON_A -> Settings.KEY_BUTTON_A
|
||||
NativeLibrary.ButtonType.BUTTON_B -> Settings.KEY_BUTTON_B
|
||||
NativeLibrary.ButtonType.BUTTON_X -> Settings.KEY_BUTTON_X
|
||||
NativeLibrary.ButtonType.BUTTON_Y -> Settings.KEY_BUTTON_Y
|
||||
NativeLibrary.ButtonType.TRIGGER_L -> Settings.KEY_BUTTON_L
|
||||
NativeLibrary.ButtonType.TRIGGER_R -> Settings.KEY_BUTTON_R
|
||||
NativeLibrary.ButtonType.BUTTON_ZL -> Settings.KEY_BUTTON_ZL
|
||||
NativeLibrary.ButtonType.BUTTON_ZR -> Settings.KEY_BUTTON_ZR
|
||||
NativeLibrary.ButtonType.BUTTON_SELECT -> Settings.KEY_BUTTON_SELECT
|
||||
NativeLibrary.ButtonType.BUTTON_START -> Settings.KEY_BUTTON_START
|
||||
NativeLibrary.ButtonType.BUTTON_HOME -> Settings.KEY_BUTTON_HOME
|
||||
NativeLibrary.ButtonType.DPAD_UP -> Settings.KEY_BUTTON_UP
|
||||
NativeLibrary.ButtonType.DPAD_DOWN -> Settings.KEY_BUTTON_DOWN
|
||||
NativeLibrary.ButtonType.DPAD_LEFT -> Settings.KEY_BUTTON_LEFT
|
||||
NativeLibrary.ButtonType.DPAD_RIGHT -> Settings.KEY_BUTTON_RIGHT
|
||||
else -> ""
|
||||
}
|
||||
private fun getButtonKey(buttonCode: Int): String = when (buttonCode) {
|
||||
NativeLibrary.ButtonType.BUTTON_A -> Settings.KEY_BUTTON_A
|
||||
NativeLibrary.ButtonType.BUTTON_B -> Settings.KEY_BUTTON_B
|
||||
NativeLibrary.ButtonType.BUTTON_X -> Settings.KEY_BUTTON_X
|
||||
NativeLibrary.ButtonType.BUTTON_Y -> Settings.KEY_BUTTON_Y
|
||||
NativeLibrary.ButtonType.TRIGGER_L -> Settings.KEY_BUTTON_L
|
||||
NativeLibrary.ButtonType.TRIGGER_R -> Settings.KEY_BUTTON_R
|
||||
NativeLibrary.ButtonType.BUTTON_ZL -> Settings.KEY_BUTTON_ZL
|
||||
NativeLibrary.ButtonType.BUTTON_ZR -> Settings.KEY_BUTTON_ZR
|
||||
NativeLibrary.ButtonType.BUTTON_SELECT -> Settings.KEY_BUTTON_SELECT
|
||||
NativeLibrary.ButtonType.BUTTON_START -> Settings.KEY_BUTTON_START
|
||||
NativeLibrary.ButtonType.BUTTON_HOME -> Settings.KEY_BUTTON_HOME
|
||||
NativeLibrary.ButtonType.DPAD_UP -> Settings.KEY_BUTTON_UP
|
||||
NativeLibrary.ButtonType.DPAD_DOWN -> Settings.KEY_BUTTON_DOWN
|
||||
NativeLibrary.ButtonType.DPAD_LEFT -> Settings.KEY_BUTTON_LEFT
|
||||
NativeLibrary.ButtonType.DPAD_RIGHT -> Settings.KEY_BUTTON_RIGHT
|
||||
else -> ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mutable set of int button values this key should map to given an event
|
||||
*/
|
||||
fun getButtonSet(keyCode: KeyEvent):MutableSet<Int> {
|
||||
fun getButtonSet(keyCode: KeyEvent): MutableSet<Int> {
|
||||
val key = getInputButtonKey(keyCode)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(
|
||||
CitraApplication.appContext
|
||||
)
|
||||
var buttonCodes = try {
|
||||
preferences.getStringSet(key, mutableSetOf<String>())
|
||||
} catch (e: ClassCastException) {
|
||||
val prefInt = preferences.getInt(key, -1);
|
||||
val prefInt = preferences.getInt(key, -1)
|
||||
val migratedSet = if (prefInt != -1) {
|
||||
mutableSetOf(prefInt.toString())
|
||||
} else {
|
||||
|
|
@ -549,15 +706,17 @@ class InputBindingSetting(
|
|||
return buttonCodes.mapNotNull { it.toIntOrNull() }.toMutableSet()
|
||||
}
|
||||
|
||||
private fun getInputButtonKey(keyId: Int): String = "${INPUT_MAPPING_PREFIX}_HostAxis_${keyId}"
|
||||
private fun getInputButtonKey(keyId: Int): String =
|
||||
"${INPUT_MAPPING_PREFIX}_HostAxis_$keyId"
|
||||
|
||||
/** Falls back to the scan code when keyCode is KEYCODE_UNKNOWN. */
|
||||
fun getInputButtonKey(event: KeyEvent): String = getInputButtonKey(translateEventToKeyId(event))
|
||||
fun getInputButtonKey(event: KeyEvent): String =
|
||||
getInputButtonKey(translateEventToKeyId(event))
|
||||
|
||||
/**
|
||||
* Helper function to get the settings key for an gamepad axis.
|
||||
*/
|
||||
fun getInputAxisKey(axis: Int): String = "${INPUT_MAPPING_PREFIX}_HostAxis_${axis}"
|
||||
fun getInputAxisKey(axis: Int): String = "${INPUT_MAPPING_PREFIX}_HostAxis_$axis"
|
||||
|
||||
/**
|
||||
* Helper function to get the settings key for an gamepad axis button (stick or trigger).
|
||||
|
|
@ -575,7 +734,6 @@ class InputBindingSetting(
|
|||
fun getInputAxisOrientationKey(axis: Int): String =
|
||||
"${getInputAxisKey(axis)}_GuestOrientation"
|
||||
|
||||
|
||||
/**
|
||||
* This function translates a keyEvent into an "keyid"
|
||||
* This key id is either the keyCode from the event, or
|
||||
|
|
@ -585,12 +743,10 @@ class InputBindingSetting(
|
|||
* This handles keys like the media-keys on google statia-controllers
|
||||
* that don't have a conventional "mapping" and report as "unknown"
|
||||
*/
|
||||
fun translateEventToKeyId(event: KeyEvent): Int {
|
||||
return if (event.keyCode == 0) {
|
||||
event.scanCode
|
||||
} else {
|
||||
event.keyCode
|
||||
}
|
||||
fun translateEventToKeyId(event: KeyEvent): Int = if (event.keyCode == 0) {
|
||||
event.scanCode
|
||||
} else {
|
||||
event.keyCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class MultiChoiceSetting(
|
|||
try {
|
||||
val setting = setting as IntListSetting
|
||||
return setting.list
|
||||
}catch (_: ClassCastException) {
|
||||
} catch (_: ClassCastException) {
|
||||
}
|
||||
return defaultValue!!
|
||||
}
|
||||
|
|
@ -42,5 +42,4 @@ class MultiChoiceSetting(
|
|||
intSetting.list = selection
|
||||
return intSetting
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,11 @@ class SliderSetting(
|
|||
|
||||
val ret = when (setting) {
|
||||
is AbstractIntSetting -> setting.int.toFloat()
|
||||
|
||||
is FloatSetting -> setting.float
|
||||
|
||||
is ScaledFloatSetting -> setting.float
|
||||
|
||||
else -> {
|
||||
Log.error("[SliderSetting] Error casting setting type.")
|
||||
-1f
|
||||
|
|
@ -38,6 +41,7 @@ class SliderSetting(
|
|||
}
|
||||
return ret.coerceIn(min.toFloat(), max.toFloat())
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a value to the backing int. If that int was previously null,
|
||||
* initializes a new one and returns it, so it can be added to the Hashmap.
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.core.view.updatePadding
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import java.io.IOException
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.databinding.ActivitySettingsBinding
|
||||
import java.io.IOException
|
||||
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
||||
import org.citra.citra_emu.features.settings.model.FloatSetting
|
||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||
|
|
@ -35,13 +35,15 @@ import org.citra.citra_emu.features.settings.model.Settings
|
|||
import org.citra.citra_emu.features.settings.model.SettingsViewModel
|
||||
import org.citra.citra_emu.features.settings.model.StringSetting
|
||||
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
||||
import org.citra.citra_emu.utils.SystemSaveGame
|
||||
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||
import org.citra.citra_emu.utils.InsetsHelper
|
||||
import org.citra.citra_emu.utils.RefreshRateUtil
|
||||
import org.citra.citra_emu.utils.SystemSaveGame
|
||||
import org.citra.citra_emu.utils.ThemeUtil
|
||||
|
||||
class SettingsActivity : AppCompatActivity(), SettingsActivityView {
|
||||
class SettingsActivity :
|
||||
AppCompatActivity(),
|
||||
SettingsActivityView {
|
||||
private val presenter = SettingsActivityPresenter(this)
|
||||
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
|
|
@ -205,7 +207,7 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
|
|||
presenter.onSettingsReset()
|
||||
|
||||
val controllerKeys = Settings.buttonKeys + Settings.circlePadKeys + Settings.cStickKeys +
|
||||
Settings.dPadAxisKeys + Settings.dPadButtonKeys + Settings.triggerKeys
|
||||
Settings.dPadAxisKeys + Settings.dPadButtonKeys + Settings.triggerKeys
|
||||
val editor =
|
||||
PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext).edit()
|
||||
controllerKeys.forEach { editor.remove(it) }
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import org.citra.citra_emu.CitraApplication
|
|||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.utils.SystemSaveGame
|
||||
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||
import org.citra.citra_emu.utils.FileUtil
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import org.citra.citra_emu.utils.PermissionsHandler
|
||||
import org.citra.citra_emu.utils.SystemSaveGame
|
||||
import org.citra.citra_emu.utils.TurboHelper
|
||||
|
||||
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
|
||||
|
|
@ -72,11 +72,15 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
|
|||
val nomediaFileExists: Boolean
|
||||
try {
|
||||
dataDirTreeUri = PermissionsHandler.citraDirectory
|
||||
dataDirDocument = DocumentFile.fromTreeUri(CitraApplication.appContext, dataDirTreeUri)!!
|
||||
dataDirDocument =
|
||||
DocumentFile.fromTreeUri(CitraApplication.appContext, dataDirTreeUri)!!
|
||||
nomediaFileDocument = dataDirDocument.findFile(".nomedia")
|
||||
nomediaFileExists = (nomediaFileDocument != null)
|
||||
} catch (e: Exception) {
|
||||
Log.error("[SettingsActivity]: Error occurred while trying to find .nomedia, error: " + e.message)
|
||||
Log.error(
|
||||
"[SettingsActivity]: Error occurred while trying to find .nomedia, error: " +
|
||||
e.message
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +99,7 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
|
|||
if (finishing && shouldSave) {
|
||||
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
|
||||
settings.saveSettings(activityView)
|
||||
//added to ensure that layout changes take effect as soon as settings window closes
|
||||
// added to ensure that layout changes take effect as soon as settings window closes
|
||||
NativeLibrary.reloadSettings()
|
||||
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
|
||||
updateAndroidImageVisibility()
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ import com.google.android.material.textfield.TextInputEditText
|
|||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.google.android.material.timepicker.MaterialTimePicker
|
||||
import com.google.android.material.timepicker.TimeFormat
|
||||
import java.lang.NumberFormatException
|
||||
import java.text.SimpleDateFormat
|
||||
import kotlin.math.roundToInt
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.databinding.DialogSliderBinding
|
||||
import org.citra.citra_emu.databinding.DialogSoftwareKeyboardBinding
|
||||
|
|
@ -39,16 +42,16 @@ import org.citra.citra_emu.features.settings.model.AbstractBooleanSetting
|
|||
import org.citra.citra_emu.features.settings.model.AbstractFloatSetting
|
||||
import org.citra.citra_emu.features.settings.model.AbstractIntSetting
|
||||
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
||||
import org.citra.citra_emu.features.settings.model.AbstractShortSetting
|
||||
import org.citra.citra_emu.features.settings.model.AbstractStringSetting
|
||||
import org.citra.citra_emu.features.settings.model.FloatSetting
|
||||
import org.citra.citra_emu.features.settings.model.IntListSetting
|
||||
import org.citra.citra_emu.features.settings.model.ScaledFloatSetting
|
||||
import org.citra.citra_emu.features.settings.model.AbstractShortSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.DateTimeSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.InputBindingSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.MultiChoiceSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.SettingsItem
|
||||
import org.citra.citra_emu.features.settings.model.view.SingleChoiceSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.MultiChoiceSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.SliderSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.StringInputSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.StringSingleChoiceSetting
|
||||
|
|
@ -69,14 +72,10 @@ import org.citra.citra_emu.fragments.AutoMapDialogFragment
|
|||
import org.citra.citra_emu.fragments.MessageDialogFragment
|
||||
import org.citra.citra_emu.fragments.MotionBottomSheetDialogFragment
|
||||
import org.citra.citra_emu.utils.SystemSaveGame
|
||||
import java.lang.NumberFormatException
|
||||
import java.text.SimpleDateFormat
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class SettingsAdapter(
|
||||
private val fragmentView: SettingsFragmentView,
|
||||
public val context: Context
|
||||
) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener,
|
||||
class SettingsAdapter(private val fragmentView: SettingsFragmentView, public val context: Context) :
|
||||
RecyclerView.Adapter<SettingViewHolder?>(),
|
||||
DialogInterface.OnClickListener,
|
||||
DialogInterface.OnMultiChoiceClickListener {
|
||||
private var settings: ArrayList<SettingsItem>? = null
|
||||
private var clickedItem: SettingsItem? = null
|
||||
|
|
@ -148,17 +147,11 @@ class SettingsAdapter(
|
|||
getItem(position)?.let { holder.bind(it) }
|
||||
}
|
||||
|
||||
private fun getItem(position: Int): SettingsItem? {
|
||||
return settings?.get(position)
|
||||
}
|
||||
private fun getItem(position: Int): SettingsItem? = settings?.get(position)
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return settings?.size ?: 0
|
||||
}
|
||||
override fun getItemCount(): Int = settings?.size ?: 0
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return getItem(position)?.type ?: -1
|
||||
}
|
||||
override fun getItemViewType(position: Int): Int = getItem(position)?.type ?: -1
|
||||
|
||||
fun setSettingsList(newSettings: ArrayList<SettingsItem>?) {
|
||||
if (settings == null) {
|
||||
|
|
@ -196,22 +189,28 @@ class SettingsAdapter(
|
|||
}
|
||||
|
||||
SettingsItem.TYPE_SINGLE_CHOICE -> {
|
||||
(oldItem as SingleChoiceSetting).isEnabled == (newItem as SingleChoiceSetting).isEnabled
|
||||
(oldItem as SingleChoiceSetting).isEnabled ==
|
||||
(newItem as SingleChoiceSetting).isEnabled
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_MULTI_CHOICE -> {
|
||||
(oldItem as MultiChoiceSetting).isEnabled == (newItem as MultiChoiceSetting).isEnabled
|
||||
(oldItem as MultiChoiceSetting).isEnabled ==
|
||||
(newItem as MultiChoiceSetting).isEnabled
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_DATETIME_SETTING -> {
|
||||
(oldItem as DateTimeSetting).isEnabled == (newItem as DateTimeSetting).isEnabled
|
||||
(oldItem as DateTimeSetting).isEnabled ==
|
||||
(newItem as DateTimeSetting).isEnabled
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
|
||||
(oldItem as StringSingleChoiceSetting).isEnabled == (newItem as StringSingleChoiceSetting).isEnabled
|
||||
(oldItem as StringSingleChoiceSetting).isEnabled ==
|
||||
(newItem as StringSingleChoiceSetting).isEnabled
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_STRING_INPUT -> {
|
||||
(oldItem as StringInputSetting).isEnabled == (newItem as StringInputSetting).isEnabled
|
||||
(oldItem as StringInputSetting).isEnabled ==
|
||||
(newItem as StringInputSetting).isEnabled
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
|
@ -231,9 +230,10 @@ class SettingsAdapter(
|
|||
fragmentView.onSettingChanged()
|
||||
|
||||
// If statement is required otherwise the app will crash on activity recreate ex. theme settings
|
||||
if (fragmentView.activityView != null)
|
||||
// Reload the settings list to update the UI
|
||||
if (fragmentView.activityView != null) {
|
||||
// Reload the settings list to update the UI
|
||||
fragmentView.loadSettingsList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSingleChoiceClick(item: SingleChoiceSetting) {
|
||||
|
|
@ -253,7 +253,7 @@ class SettingsAdapter(
|
|||
private fun onMultiChoiceClick(item: MultiChoiceSetting) {
|
||||
clickedItem = item
|
||||
|
||||
val value: BooleanArray = getSelectionForMultiChoiceValue(item);
|
||||
val value: BooleanArray = getSelectionForMultiChoiceValue(item)
|
||||
dialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(item.nameId)
|
||||
.setMultiChoiceItems(item.choicesId, value, this)
|
||||
|
|
@ -298,7 +298,7 @@ class SettingsAdapter(
|
|||
val time = item.value.substringAfter(" ")
|
||||
|
||||
val formatter = SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssZZZZ")
|
||||
val gmt = formatter.parse("${date}T${time}+0000")
|
||||
val gmt = formatter.parse("${date}T$time+0000")
|
||||
gmt!!.time
|
||||
}
|
||||
|
||||
|
|
@ -354,7 +354,6 @@ class SettingsAdapter(
|
|||
clickedPosition = position
|
||||
sliderProgress = (item.selectedFloat * 100f).roundToInt() / 100f
|
||||
|
||||
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val sliderBinding = DialogSliderBinding.inflate(inflater)
|
||||
textInputLayout = sliderBinding.textInput
|
||||
|
|
@ -376,9 +375,9 @@ class SettingsAdapter(
|
|||
value = sliderProgress
|
||||
textSliderValue?.addTextChangedListener(object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
var textValue = s.toString().toFloatOrNull();
|
||||
var textValue = s.toString().toFloatOrNull()
|
||||
if (item.setting !is FloatSetting) {
|
||||
textValue = textValue?.roundToInt()?.toFloat();
|
||||
textValue = textValue?.roundToInt()?.toFloat()
|
||||
}
|
||||
if (textValue == null || textValue < valueFrom || textValue > valueTo) {
|
||||
textInputLayout?.error = "Inappropriate value"
|
||||
|
|
@ -425,6 +424,7 @@ class SettingsAdapter(
|
|||
}
|
||||
|
||||
is FloatSetting -> (item.setting as FloatSetting).defaultValue
|
||||
|
||||
else -> item.defaultValue ?: 0f
|
||||
}
|
||||
onClick(dialog, which)
|
||||
|
|
@ -495,7 +495,9 @@ class SettingsAdapter(
|
|||
it.setSelectedValue(value)
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("Unrecognized type used for SingleChoiceSetting!")
|
||||
else -> throw IllegalStateException(
|
||||
"Unrecognized type used for SingleChoiceSetting!"
|
||||
)
|
||||
}
|
||||
fragmentView?.putSetting(setting)
|
||||
fragmentView.loadSettingsList()
|
||||
|
|
@ -518,7 +520,9 @@ class SettingsAdapter(
|
|||
it.setSelectedValue(it.getValueAt(which)?.toShort() ?: 1)
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("Unrecognized type used for StringSingleChoiceSetting!")
|
||||
else -> throw IllegalStateException(
|
||||
"Unrecognized type used for StringSingleChoiceSetting!"
|
||||
)
|
||||
}
|
||||
|
||||
fragmentView?.putSetting(setting)
|
||||
|
|
@ -569,13 +573,22 @@ class SettingsAdapter(
|
|||
textInputValue = ""
|
||||
}
|
||||
|
||||
//onclick for multichoice
|
||||
// onclick for multichoice
|
||||
override fun onClick(dialog: DialogInterface?, which: Int, isChecked: Boolean) {
|
||||
val mcsetting = clickedItem as? MultiChoiceSetting
|
||||
mcsetting?.let {
|
||||
val value = getValueForMultiChoiceSelection(it, which)
|
||||
if (it.selectedValues.contains(value) != isChecked) {
|
||||
val setting = it.setSelectedValue((if (isChecked) it.selectedValues + value else it.selectedValues - value).sorted())
|
||||
val setting = it.setSelectedValue(
|
||||
(
|
||||
if (isChecked) {
|
||||
it.selectedValues + value
|
||||
} else {
|
||||
it.selectedValues -
|
||||
value
|
||||
}
|
||||
).sorted()
|
||||
)
|
||||
fragmentView?.putSetting(setting)
|
||||
fragmentView?.onSettingChanged()
|
||||
}
|
||||
|
|
@ -583,13 +596,13 @@ class SettingsAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fun onLongClick(setting: AbstractSetting, position: Int): Boolean {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setMessage(R.string.reset_setting_confirmation)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
|
||||
when (setting) {
|
||||
is AbstractBooleanSetting -> setting.boolean = setting.defaultValue as Boolean
|
||||
|
||||
is AbstractFloatSetting -> {
|
||||
if (setting is ScaledFloatSetting) {
|
||||
setting.float = setting.defaultValue * setting.scale
|
||||
|
|
@ -599,7 +612,9 @@ class SettingsAdapter(
|
|||
}
|
||||
|
||||
is AbstractIntSetting -> setting.int = setting.defaultValue as Int
|
||||
|
||||
is AbstractStringSetting -> setting.string = setting.defaultValue as String
|
||||
|
||||
is AbstractShortSetting -> setting.short = setting.defaultValue as Short
|
||||
}
|
||||
notifyItemChanged(position)
|
||||
|
|
@ -628,14 +643,16 @@ class SettingsAdapter(
|
|||
}
|
||||
|
||||
fun onClickDisabledSetting(isRuntimeDisabled: Boolean) {
|
||||
val titleId = if (isRuntimeDisabled)
|
||||
val titleId = if (isRuntimeDisabled) {
|
||||
R.string.setting_not_editable
|
||||
else
|
||||
} else {
|
||||
R.string.setting_disabled
|
||||
val messageId = if (isRuntimeDisabled)
|
||||
}
|
||||
val messageId = if (isRuntimeDisabled) {
|
||||
R.string.setting_not_editable_description
|
||||
else
|
||||
} else {
|
||||
R.string.setting_disabled_description
|
||||
}
|
||||
|
||||
MessageDialogFragment.newInstance(
|
||||
titleId,
|
||||
|
|
@ -652,7 +669,10 @@ class SettingsAdapter(
|
|||
}
|
||||
|
||||
fun onLongClickAutoMap(): Boolean {
|
||||
showConfirmationDialog(R.string.controller_clear_all, R.string.controller_clear_all_confirm) {
|
||||
showConfirmationDialog(
|
||||
R.string.controller_clear_all,
|
||||
R.string.controller_clear_all_confirm
|
||||
) {
|
||||
InputBindingSetting.clearAllBindings()
|
||||
fragmentView.loadSettingsList()
|
||||
fragmentView.onSettingChanged()
|
||||
|
|
@ -661,14 +681,20 @@ class SettingsAdapter(
|
|||
}
|
||||
|
||||
fun onClickRegenerateConsoleId() {
|
||||
showConfirmationDialog(R.string.regenerate_console_id, R.string.regenerate_console_id_description) {
|
||||
showConfirmationDialog(
|
||||
R.string.regenerate_console_id,
|
||||
R.string.regenerate_console_id_description
|
||||
) {
|
||||
SystemSaveGame.regenerateConsoleId()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun onClickRegenerateMAC() {
|
||||
showConfirmationDialog(R.string.regenerate_mac_address, R.string.regenerate_mac_address_description) {
|
||||
showConfirmationDialog(
|
||||
R.string.regenerate_mac_address,
|
||||
R.string.regenerate_mac_address_description
|
||||
) {
|
||||
SystemSaveGame.regenerateMac()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
|
@ -732,18 +758,18 @@ class SettingsAdapter(
|
|||
}
|
||||
|
||||
private fun getSelectionForMultiChoiceValue(item: MultiChoiceSetting): BooleanArray {
|
||||
val value = item.selectedValues;
|
||||
val valuesId = item.valuesId;
|
||||
val value = item.selectedValues
|
||||
val valuesId = item.valuesId
|
||||
if (valuesId > 0) {
|
||||
val valuesArray = context.resources.getIntArray(valuesId);
|
||||
val res = BooleanArray(valuesArray.size){false}
|
||||
val valuesArray = context.resources.getIntArray(valuesId)
|
||||
val res = BooleanArray(valuesArray.size) { false }
|
||||
for (index in valuesArray.indices) {
|
||||
if (value.contains(valuesArray[index])) {
|
||||
res[index] = true;
|
||||
res[index] = true
|
||||
}
|
||||
}
|
||||
return res;
|
||||
return res
|
||||
}
|
||||
return BooleanArray(1){false};
|
||||
return BooleanArray(1) { false }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ import org.citra.citra_emu.databinding.FragmentSettingsBinding
|
|||
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.SettingsItem
|
||||
|
||||
class SettingsFragment : Fragment(), SettingsFragmentView {
|
||||
class SettingsFragment :
|
||||
Fragment(),
|
||||
SettingsFragmentView {
|
||||
override var activityView: SettingsActivityView? = null
|
||||
|
||||
private val fragmentPresenter = SettingsFragmentPresenter(this)
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import org.citra.citra_emu.features.settings.model.AbstractShortSetting
|
|||
import org.citra.citra_emu.features.settings.model.AbstractStringSetting
|
||||
import org.citra.citra_emu.features.settings.model.BooleanSetting
|
||||
import org.citra.citra_emu.features.settings.model.FloatSetting
|
||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||
import org.citra.citra_emu.features.settings.model.IntListSetting
|
||||
import org.citra.citra_emu.features.settings.model.IntSetting
|
||||
import org.citra.citra_emu.features.settings.model.ScaledFloatSetting
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.features.settings.model.StringSetting
|
||||
|
|
@ -94,18 +94,31 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
}
|
||||
when (menuTag) {
|
||||
SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl)
|
||||
|
||||
Settings.SECTION_CORE -> addGeneralSettings(sl)
|
||||
|
||||
Settings.SECTION_SYSTEM -> addSystemSettings(sl)
|
||||
|
||||
Settings.SECTION_CAMERA -> addCameraSettings(sl)
|
||||
|
||||
Settings.SECTION_CONTROLS -> addControlsSettings(sl)
|
||||
|
||||
Settings.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||
|
||||
Settings.SECTION_LAYOUT -> addLayoutSettings(sl)
|
||||
|
||||
Settings.SECTION_AUDIO -> addAudioSettings(sl)
|
||||
|
||||
Settings.SECTION_DEBUG -> addDebugSettings(sl)
|
||||
|
||||
Settings.SECTION_THEME -> addThemeSettings(sl)
|
||||
|
||||
Settings.SECTION_CUSTOM_LANDSCAPE -> addCustomLandscapeSettings(sl)
|
||||
|
||||
Settings.SECTION_CUSTOM_PORTRAIT -> addCustomPortraitSettings(sl)
|
||||
|
||||
Settings.SECTION_PERFORMANCE_OVERLAY -> addPerformanceOverlaySettings(sl)
|
||||
|
||||
else -> {
|
||||
fragmentView.showToastMessage("Unimplemented menu", false)
|
||||
return
|
||||
|
|
@ -128,13 +141,9 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
}
|
||||
}
|
||||
|
||||
private fun getSmallerDimension(): Int {
|
||||
return getDimensions().min()
|
||||
}
|
||||
private fun getSmallerDimension(): Int = getDimensions().min()
|
||||
|
||||
private fun getLargerDimension(): Int {
|
||||
return getDimensions().max()
|
||||
}
|
||||
private fun getLargerDimension(): Int = getDimensions().max()
|
||||
|
||||
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_settings))
|
||||
|
|
@ -360,7 +369,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
R.string.emulated_region,
|
||||
0,
|
||||
R.array.regionNames,
|
||||
R.array.regionValues,
|
||||
R.array.regionValues
|
||||
)
|
||||
)
|
||||
add(
|
||||
|
|
@ -377,7 +386,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
get() {
|
||||
val ret = SystemSaveGame.getCountryCode()
|
||||
checkCountryCompatibility()
|
||||
return ret;
|
||||
return ret
|
||||
}
|
||||
set(value) {
|
||||
SystemSaveGame.setCountryCode(value)
|
||||
|
|
@ -626,20 +635,23 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
if (characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
|
||||
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
|
||||
) {
|
||||
continue // Legacy cameras cannot be used with the NDK
|
||||
continue // Legacy cameras cannot be used with the NDK
|
||||
}
|
||||
supportedCameraIdList.add(id)
|
||||
val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
|
||||
var stringId: Int = R.string.camera_facing_external
|
||||
when (facing) {
|
||||
CameraCharacteristics.LENS_FACING_FRONT -> stringId =
|
||||
R.string.camera_facing_front
|
||||
CameraCharacteristics.LENS_FACING_FRONT ->
|
||||
stringId =
|
||||
R.string.camera_facing_front
|
||||
|
||||
CameraCharacteristics.LENS_FACING_BACK -> stringId =
|
||||
R.string.camera_facing_back
|
||||
CameraCharacteristics.LENS_FACING_BACK ->
|
||||
stringId =
|
||||
R.string.camera_facing_back
|
||||
|
||||
CameraCharacteristics.LENS_FACING_EXTERNAL -> stringId =
|
||||
R.string.camera_facing_external
|
||||
CameraCharacteristics.LENS_FACING_EXTERNAL ->
|
||||
stringId =
|
||||
R.string.camera_facing_external
|
||||
}
|
||||
supportedCameraNameList.add(
|
||||
String.format("%1\$s (%2\$s)", id, settingsActivity.getString(stringId))
|
||||
|
|
@ -816,12 +828,22 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
add(InputBindingSetting(button, Settings.axisTitles[i]))
|
||||
}
|
||||
|
||||
add(HeaderSetting(R.string.controller_dpad_axis,R.string.controller_dpad_axis_description))
|
||||
add(
|
||||
HeaderSetting(
|
||||
R.string.controller_dpad_axis,
|
||||
R.string.controller_dpad_axis_description
|
||||
)
|
||||
)
|
||||
Settings.dPadAxisKeys.forEachIndexed { i: Int, key: String ->
|
||||
val button = getInputObject(key)
|
||||
add(InputBindingSetting(button, Settings.axisTitles[i]))
|
||||
}
|
||||
add(HeaderSetting(R.string.controller_dpad_button,R.string.controller_dpad_button_description))
|
||||
add(
|
||||
HeaderSetting(
|
||||
R.string.controller_dpad_button,
|
||||
R.string.controller_dpad_button_description
|
||||
)
|
||||
)
|
||||
Settings.dPadButtonKeys.forEachIndexed { i: Int, key: String ->
|
||||
val button = getInputObject(key)
|
||||
add(InputBindingSetting(button, Settings.dPadTitles[i]))
|
||||
|
|
@ -833,7 +855,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
add(InputBindingSetting(button, Settings.triggerTitles[i]))
|
||||
}
|
||||
|
||||
add(HeaderSetting(R.string.controller_hotkeys,R.string.controller_hotkeys_description))
|
||||
add(HeaderSetting(R.string.controller_hotkeys, R.string.controller_hotkeys_description))
|
||||
Settings.hotKeys.forEachIndexed { i: Int, key: String ->
|
||||
val button = getInputObject(key)
|
||||
add(InputBindingSetting(button, Settings.hotkeyTitles[i]))
|
||||
|
|
@ -851,8 +873,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
}
|
||||
}
|
||||
|
||||
private fun getInputObject(key: String): AbstractStringSetting {
|
||||
return object : AbstractStringSetting {
|
||||
private fun getInputObject(key: String): AbstractStringSetting =
|
||||
object : AbstractStringSetting {
|
||||
override var string: String
|
||||
get() = preferences.getString(key, "")!!
|
||||
set(value) {
|
||||
|
|
@ -866,7 +888,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
override val valueAsString = preferences.getString(key, "")!!
|
||||
override val defaultValue = ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics))
|
||||
|
|
@ -889,7 +910,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
R.string.spirv_shader_gen,
|
||||
R.string.spirv_shader_gen_description,
|
||||
BooleanSetting.SPIRV_SHADER_GEN.key,
|
||||
BooleanSetting.SPIRV_SHADER_GEN.defaultValue,
|
||||
BooleanSetting.SPIRV_SHADER_GEN.defaultValue
|
||||
)
|
||||
)
|
||||
add(
|
||||
|
|
@ -898,7 +919,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
R.string.disable_spirv_optimizer,
|
||||
R.string.disable_spirv_optimizer_description,
|
||||
BooleanSetting.DISABLE_SPIRV_OPTIMIZER.key,
|
||||
BooleanSetting.DISABLE_SPIRV_OPTIMIZER.defaultValue,
|
||||
BooleanSetting.DISABLE_SPIRV_OPTIMIZER.defaultValue
|
||||
)
|
||||
)
|
||||
add(
|
||||
|
|
@ -921,7 +942,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
IntSetting.RESOLUTION_FACTOR.defaultValue
|
||||
)
|
||||
)
|
||||
add(
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_INTEGER_SCALING,
|
||||
R.string.use_integer_scaling,
|
||||
|
|
@ -1002,7 +1023,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
R.array.render3dValues,
|
||||
IntSetting.STEREOSCOPIC_3D_MODE.key,
|
||||
IntSetting.STEREOSCOPIC_3D_MODE.defaultValue,
|
||||
isEnabled = IntSetting.RENDER_3D_WHICH_DISPLAY.int != StereoWhichDisplay.NONE.int
|
||||
isEnabled =
|
||||
IntSetting.RENDER_3D_WHICH_DISPLAY.int != StereoWhichDisplay.NONE.int
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -1035,7 +1057,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
R.string.swap_eyes_3d_description,
|
||||
BooleanSetting.SWAP_EYES_3D.key,
|
||||
BooleanSetting.SWAP_EYES_3D.defaultValue,
|
||||
isEnabled = IntSetting.RENDER_3D_WHICH_DISPLAY.int != StereoWhichDisplay.NONE.int
|
||||
isEnabled =
|
||||
IntSetting.RENDER_3D_WHICH_DISPLAY.int != StereoWhichDisplay.NONE.int
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -1201,7 +1224,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
IntSetting.PORTRAIT_SCREEN_LAYOUT.defaultValue
|
||||
)
|
||||
)
|
||||
add (
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_SECONDARY_DISPLAY,
|
||||
R.string.emulation_secondary_display_enable,
|
||||
|
|
@ -1231,7 +1254,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
R.array.aspectRatioValues,
|
||||
IntSetting.ASPECT_RATIO.key,
|
||||
IntSetting.ASPECT_RATIO.defaultValue,
|
||||
isEnabled = IntSetting.SCREEN_LAYOUT.int == ScreenLayout.SINGLE_SCREEN.int,
|
||||
isEnabled = IntSetting.SCREEN_LAYOUT.int == ScreenLayout.SINGLE_SCREEN.int
|
||||
)
|
||||
)
|
||||
add(
|
||||
|
|
@ -1288,7 +1311,10 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
get() = (FloatSetting.BACKGROUND_RED.float * 255).toInt()
|
||||
set(value) {
|
||||
FloatSetting.BACKGROUND_RED.float = value.toFloat() / 255
|
||||
settings.saveSetting(FloatSetting.BACKGROUND_RED, SettingsFile.FILE_NAME_CONFIG)
|
||||
settings.saveSetting(
|
||||
FloatSetting.BACKGROUND_RED,
|
||||
SettingsFile.FILE_NAME_CONFIG
|
||||
)
|
||||
}
|
||||
override val key = null
|
||||
override val section = null
|
||||
|
|
@ -1311,7 +1337,10 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
get() = (FloatSetting.BACKGROUND_GREEN.float * 255).toInt()
|
||||
set(value) {
|
||||
FloatSetting.BACKGROUND_GREEN.float = value.toFloat() / 255
|
||||
settings.saveSetting(FloatSetting.BACKGROUND_GREEN, SettingsFile.FILE_NAME_CONFIG)
|
||||
settings.saveSetting(
|
||||
FloatSetting.BACKGROUND_GREEN,
|
||||
SettingsFile.FILE_NAME_CONFIG
|
||||
)
|
||||
}
|
||||
override val key = null
|
||||
override val section = null
|
||||
|
|
@ -1334,7 +1363,10 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
get() = (FloatSetting.BACKGROUND_BLUE.float * 255).toInt()
|
||||
set(value) {
|
||||
FloatSetting.BACKGROUND_BLUE.float = value.toFloat() / 255
|
||||
settings.saveSetting(FloatSetting.BACKGROUND_BLUE, SettingsFile.FILE_NAME_CONFIG)
|
||||
settings.saveSetting(
|
||||
FloatSetting.BACKGROUND_BLUE,
|
||||
SettingsFile.FILE_NAME_CONFIG
|
||||
)
|
||||
}
|
||||
override val key = null
|
||||
override val section = null
|
||||
|
|
@ -1380,9 +1412,10 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
}
|
||||
|
||||
private fun addPerformanceOverlaySettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.performance_overlay_options))
|
||||
settingsActivity.setToolbarTitle(
|
||||
settingsActivity.getString(R.string.performance_overlay_options)
|
||||
)
|
||||
sl.apply {
|
||||
|
||||
add(HeaderSetting(R.string.visibility))
|
||||
|
||||
add(
|
||||
|
|
@ -1411,11 +1444,10 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
R.string.performance_overlay_position,
|
||||
R.string.performance_overlay_position_description,
|
||||
R.array.statsPosition,
|
||||
R.array.statsPositionValues,
|
||||
R.array.statsPositionValues
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
add(HeaderSetting(R.string.information))
|
||||
|
||||
add(
|
||||
|
|
@ -1481,7 +1513,9 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
}
|
||||
|
||||
private fun addCustomLandscapeSettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.emulation_landscape_custom_layout))
|
||||
settingsActivity.setToolbarTitle(
|
||||
settingsActivity.getString(R.string.emulation_landscape_custom_layout)
|
||||
)
|
||||
sl.apply {
|
||||
add(HeaderSetting(R.string.emulation_top_screen))
|
||||
add(
|
||||
|
|
@ -1582,11 +1616,12 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun addCustomPortraitSettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.emulation_portrait_custom_layout))
|
||||
settingsActivity.setToolbarTitle(
|
||||
settingsActivity.getString(R.string.emulation_portrait_custom_layout)
|
||||
)
|
||||
sl.apply {
|
||||
add(HeaderSetting(R.string.emulation_top_screen))
|
||||
add(
|
||||
|
|
@ -1687,7 +1722,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
|
||||
|
|
@ -1881,7 +1915,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
BooleanSetting.DETERMINISTIC_ASYNC_OPERATIONS.defaultValue
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ package org.citra.citra_emu.features.settings.ui.viewholder
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import org.citra.citra_emu.databinding.ListItemSettingBinding
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import org.citra.citra_emu.databinding.ListItemSettingBinding
|
||||
import org.citra.citra_emu.features.settings.model.view.DateTimeSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.SettingsItem
|
||||
import org.citra.citra_emu.features.settings.ui.SettingsAdapter
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||
SettingViewHolder(binding.root, adapter) {
|
||||
|
|
@ -39,7 +39,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
|||
val time = setting.value.substringAfter(" ")
|
||||
|
||||
val formatter = SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssZZZZ")
|
||||
val gmt = formatter.parse("${date}T${time}+0000")
|
||||
val gmt = formatter.parse("${date}T$time+0000")
|
||||
gmt!!.time / 1000
|
||||
}
|
||||
val instant = Instant.ofEpochMilli(epochTime * 1000)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class HeaderViewHolder(val binding: ListItemSettingsHeaderBinding, adapter: Sett
|
|||
if (item.descriptionId != 0) {
|
||||
binding.textHeaderDescription.visibility = View.VISIBLE
|
||||
binding.textHeaderDescription.setText(item.descriptionId)
|
||||
}else {
|
||||
} else {
|
||||
binding.textHeaderDescription.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ package org.citra.citra_emu.features.settings.ui.viewholder
|
|||
|
||||
import android.view.View
|
||||
import org.citra.citra_emu.databinding.ListItemSettingBinding
|
||||
import org.citra.citra_emu.features.settings.model.view.SettingsItem
|
||||
import org.citra.citra_emu.features.settings.model.view.MultiChoiceSetting
|
||||
import org.citra.citra_emu.features.settings.model.view.SettingsItem
|
||||
import org.citra.citra_emu.features.settings.ui.SettingsAdapter
|
||||
|
||||
class MultiChoiceViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||
|
|
@ -42,13 +42,13 @@ class MultiChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Settin
|
|||
is MultiChoiceSetting -> {
|
||||
val resMgr = binding.textSettingDescription.context.resources
|
||||
val values = resMgr.getIntArray(item.valuesId)
|
||||
var resList:List<String> = emptyList();
|
||||
var resList: List<String> = emptyList()
|
||||
values.forEachIndexed { i: Int, value: Int ->
|
||||
if ((setting as MultiChoiceSetting).selectedValues.contains(value)) {
|
||||
resList = resList + resMgr.getStringArray(item.choicesId)[i];
|
||||
resList = resList + resMgr.getStringArray(item.choicesId)[i]
|
||||
}
|
||||
}
|
||||
return resList.joinToString();
|
||||
return resList.joinToString()
|
||||
}
|
||||
|
||||
else -> return ""
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import org.citra.citra_emu.features.settings.model.view.SettingsItem
|
|||
import org.citra.citra_emu.features.settings.ui.SettingsAdapter
|
||||
|
||||
abstract class SettingViewHolder(itemView: View, protected val adapter: SettingsAdapter) :
|
||||
RecyclerView.ViewHolder(itemView), View.OnClickListener, View.OnLongClickListener {
|
||||
RecyclerView.ViewHolder(itemView),
|
||||
View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener(this)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,9 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
|
|||
binding.textSettingValue.text = when (setting.setting) {
|
||||
is ScaledFloatSetting ->
|
||||
"${(setting.setting as ScaledFloatSetting).float.toInt()}${setting.units}"
|
||||
|
||||
is FloatSetting -> "${(setting.setting as AbstractFloatSetting).float}${setting.units}"
|
||||
|
||||
else -> "${(setting.setting as AbstractIntSetting).int}${setting.units}"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ package org.citra.citra_emu.features.settings.utils
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.util.TreeMap
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.features.settings.model.AbstractSetting
|
||||
|
|
@ -23,12 +28,6 @@ import org.citra.citra_emu.utils.BiMap
|
|||
import org.citra.citra_emu.utils.DirectoryInitialization.userDirectory
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import org.ini4j.Wini
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.util.TreeMap
|
||||
|
||||
|
||||
/**
|
||||
* Contains static methods for interacting with .ini files in which settings are stored.
|
||||
|
|
@ -90,9 +89,8 @@ object SettingsFile {
|
|||
return sections
|
||||
}
|
||||
|
||||
fun readFile(fileName: String, view: SettingsActivityView?): HashMap<String, SettingSection?> {
|
||||
return readFile(getSettingsFile(fileName), false, view)
|
||||
}
|
||||
fun readFile(fileName: String, view: SettingsActivityView?): HashMap<String, SettingSection?> =
|
||||
readFile(getSettingsFile(fileName), false, view)
|
||||
|
||||
fun readFile(fileName: String): HashMap<String, SettingSection?> = readFile(fileName, null)
|
||||
|
||||
|
|
@ -107,9 +105,7 @@ object SettingsFile {
|
|||
fun readCustomGameSettings(
|
||||
gameId: String,
|
||||
view: SettingsActivityView?
|
||||
): HashMap<String, SettingSection?> {
|
||||
return readFile(getCustomGameSettingsFile(gameId), true, view)
|
||||
}
|
||||
): HashMap<String, SettingSection?> = readFile(getCustomGameSettingsFile(gameId), true, view)
|
||||
|
||||
/**
|
||||
* Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
|
||||
|
|
@ -143,15 +139,13 @@ object SettingsFile {
|
|||
Log.error("[SettingsFile] File not found: $fileName.ini: ${e.message}")
|
||||
view.showToastMessage(
|
||||
CitraApplication.appContext
|
||||
.getString(R.string.error_saving, fileName, e.message), false
|
||||
.getString(R.string.error_saving, fileName, e.message),
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun saveFile(
|
||||
fileName: String,
|
||||
setting: AbstractSetting
|
||||
) {
|
||||
fun saveFile(fileName: String, setting: AbstractSetting) {
|
||||
val ini = getSettingsFile(fileName)
|
||||
try {
|
||||
val context: Context = CitraApplication.appContext
|
||||
|
|
@ -168,21 +162,19 @@ object SettingsFile {
|
|||
}
|
||||
}
|
||||
|
||||
private fun mapSectionNameFromIni(generalSectionName: String): String? {
|
||||
return if (sectionsMap.getForward(generalSectionName) != null) {
|
||||
private fun mapSectionNameFromIni(generalSectionName: String): String? =
|
||||
if (sectionsMap.getForward(generalSectionName) != null) {
|
||||
sectionsMap.getForward(generalSectionName)
|
||||
} else {
|
||||
generalSectionName
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapSectionNameToIni(generalSectionName: String): String {
|
||||
return if (sectionsMap.getBackward(generalSectionName) != null) {
|
||||
private fun mapSectionNameToIni(generalSectionName: String): String =
|
||||
if (sectionsMap.getBackward(generalSectionName) != null) {
|
||||
sectionsMap.getBackward(generalSectionName).toString()
|
||||
} else {
|
||||
generalSectionName
|
||||
}
|
||||
}
|
||||
|
||||
fun getSettingsFile(fileName: String): DocumentFile {
|
||||
val root = DocumentFile.fromTreeUri(CitraApplication.appContext, Uri.parse(userDirectory))
|
||||
|
|
|
|||
|
|
@ -96,28 +96,27 @@ class AboutFragment : Fragment() {
|
|||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
val mlpAppBar = binding.toolbarAbout.layoutParams as MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarAbout.layoutParams = mlpAppBar
|
||||
val mlpAppBar = binding.toolbarAbout.layoutParams as MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarAbout.layoutParams = mlpAppBar
|
||||
|
||||
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
|
||||
mlpScrollAbout.leftMargin = leftInsets
|
||||
mlpScrollAbout.rightMargin = rightInsets
|
||||
binding.scrollAbout.layoutParams = mlpScrollAbout
|
||||
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
|
||||
mlpScrollAbout.leftMargin = leftInsets
|
||||
mlpScrollAbout.rightMargin = rightInsets
|
||||
binding.scrollAbout.layoutParams = mlpScrollAbout
|
||||
|
||||
binding.contentAbout.updatePadding(bottom = barInsets.bottom)
|
||||
binding.contentAbout.updatePadding(bottom = barInsets.bottom)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,9 @@ class AutoMapDialogFragment : BottomSheetDialogFragment() {
|
|||
// Nintendo layout: east position sends KEYCODE_BUTTON_A (96)
|
||||
val isNintendoLayout = when (keyCode) {
|
||||
KeyEvent.KEYCODE_BUTTON_A -> true
|
||||
|
||||
KeyEvent.KEYCODE_BUTTON_B -> false
|
||||
|
||||
else -> {
|
||||
// Unrecognized button - ignore and wait for a valid press
|
||||
Log.warning("[AutoMap] Ignoring unrecognized keycode $keyCode, waiting for A or B")
|
||||
|
|
@ -117,9 +119,7 @@ class AutoMapDialogFragment : BottomSheetDialogFragment() {
|
|||
companion object {
|
||||
const val TAG = "AutoMapDialogFragment"
|
||||
|
||||
fun newInstance(
|
||||
onComplete: () -> Unit
|
||||
): AutoMapDialogFragment {
|
||||
fun newInstance(onComplete: () -> Unit): AutoMapDialogFragment {
|
||||
val dialog = AutoMapDialogFragment()
|
||||
dialog.onComplete = onComplete
|
||||
return dialog
|
||||
|
|
|
|||
|
|
@ -60,7 +60,9 @@ class CitraDirectoryDialogFragment : DialogFragment() {
|
|||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _: DialogInterface?, _: Int ->
|
||||
if (!PermissionsHandler.hasWriteAccess(requireContext())) {
|
||||
PermissionsHandler.compatibleSelectDirectory((requireActivity() as MainActivity).openCitraDirectory)
|
||||
PermissionsHandler.compatibleSelectDirectory(
|
||||
(requireActivity() as MainActivity).openCitraDirectory
|
||||
)
|
||||
}
|
||||
}
|
||||
.show()
|
||||
|
|
|
|||
|
|
@ -9,16 +9,16 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.viewmodel.CompressProgressDialogViewModel
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
|
||||
class CompressProgressDialogFragment : DialogFragment() {
|
||||
private lateinit var progressBar: ProgressBar
|
||||
|
|
@ -37,14 +37,27 @@ class CompressProgressDialogFragment : DialogFragment() {
|
|||
val view = layoutInflater.inflate(R.layout.dialog_compress_progress, null)
|
||||
progressBar = view.findViewById(R.id.compress_progress)
|
||||
val label = view.findViewById<android.widget.TextView>(R.id.compress_label)
|
||||
label.text = if (isCompressing) getString(R.string.compressing) else getString(R.string.decompressing)
|
||||
label.text =
|
||||
if (isCompressing) {
|
||||
getString(
|
||||
R.string.compressing
|
||||
)
|
||||
} else {
|
||||
getString(R.string.decompressing)
|
||||
}
|
||||
|
||||
isCancelable = false
|
||||
progressBar.isIndeterminate = true
|
||||
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
combine(CompressProgressDialogViewModel.total, CompressProgressDialogViewModel.progress) { total, progress ->
|
||||
combine(
|
||||
CompressProgressDialogViewModel.total,
|
||||
CompressProgressDialogViewModel.progress
|
||||
) {
|
||||
total,
|
||||
progress
|
||||
->
|
||||
total to progress
|
||||
}.collectLatest { (total, progress) ->
|
||||
if (total <= 0) {
|
||||
|
|
@ -63,7 +76,10 @@ class CompressProgressDialogFragment : DialogFragment() {
|
|||
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(view)
|
||||
.setCancelable(false)
|
||||
.setNegativeButton(android.R.string.cancel) { _: android.content.DialogInterface, _: Int ->
|
||||
.setNegativeButton(android.R.string.cancel) {
|
||||
_: android.content.DialogInterface,
|
||||
_: Int
|
||||
->
|
||||
outputPath?.let { path ->
|
||||
NativeLibrary.deleteDocument(path)
|
||||
}
|
||||
|
|
@ -77,7 +93,10 @@ class CompressProgressDialogFragment : DialogFragment() {
|
|||
private const val ARG_IS_COMPRESSING = "isCompressing"
|
||||
private const val ARG_OUTPUT_PATH = "outputPath"
|
||||
|
||||
fun newInstance(isCompressing: Boolean, outputPath: String?): CompressProgressDialogFragment {
|
||||
fun newInstance(
|
||||
isCompressing: Boolean,
|
||||
outputPath: String?
|
||||
): CompressProgressDialogFragment {
|
||||
val frag = CompressProgressDialogFragment()
|
||||
val args = Bundle()
|
||||
args.putBoolean(ARG_IS_COMPRESSING, isCompressing)
|
||||
|
|
|
|||
|
|
@ -52,9 +52,7 @@ class CopyDirProgressDialog : DialogFragment() {
|
|||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return binding.root
|
||||
}
|
||||
): View = binding.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
|
@ -144,7 +142,8 @@ class CopyDirProgressDialog : DialogFragment() {
|
|||
callback?.onStepCompleted(0, false)
|
||||
viewModel.setCopyComplete(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
return CopyDirProgressDialog()
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.citra.citra_emu.R
|
||||
|
|
@ -27,9 +28,8 @@ import org.citra.citra_emu.databinding.FragmentDriverManagerBinding
|
|||
import org.citra.citra_emu.utils.FileUtil.asDocumentFile
|
||||
import org.citra.citra_emu.utils.FileUtil.inputStream
|
||||
import org.citra.citra_emu.utils.GpuDriverHelper
|
||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||
import org.citra.citra_emu.viewmodel.DriverViewModel
|
||||
import java.io.IOException
|
||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||
|
||||
class DriverManagerFragment : Fragment() {
|
||||
private var _binding: FragmentDriverManagerBinding? = null
|
||||
|
|
@ -110,41 +110,40 @@ class DriverManagerFragment : Fragment() {
|
|||
driverViewModel.onCloseDriverManager()
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarDrivers.layoutParams = mlpAppBar
|
||||
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarDrivers.layoutParams = mlpAppBar
|
||||
|
||||
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlplistDrivers.leftMargin = leftInsets
|
||||
mlplistDrivers.rightMargin = rightInsets
|
||||
binding.listDrivers.layoutParams = mlplistDrivers
|
||||
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlplistDrivers.leftMargin = leftInsets
|
||||
mlplistDrivers.rightMargin = rightInsets
|
||||
binding.listDrivers.layoutParams = mlplistDrivers
|
||||
|
||||
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
|
||||
val mlpFab =
|
||||
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpFab.leftMargin = leftInsets + fabSpacing
|
||||
mlpFab.rightMargin = rightInsets + fabSpacing
|
||||
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
|
||||
binding.buttonInstall.layoutParams = mlpFab
|
||||
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
|
||||
val mlpFab =
|
||||
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpFab.leftMargin = leftInsets + fabSpacing
|
||||
mlpFab.rightMargin = rightInsets + fabSpacing
|
||||
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
|
||||
binding.buttonInstall.layoutParams = mlpFab
|
||||
|
||||
binding.listDrivers.updatePadding(
|
||||
bottom = barInsets.bottom +
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
|
||||
)
|
||||
binding.listDrivers.updatePadding(
|
||||
bottom = barInsets.bottom +
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
|
||||
)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
windowInsets
|
||||
}
|
||||
|
||||
private val getDriver =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ import androidx.activity.OnBackPressedCallback
|
|||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.get
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.get
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
|
||||
import androidx.fragment.app.Fragment
|
||||
|
|
@ -78,15 +78,18 @@ import org.citra.citra_emu.model.Game
|
|||
import org.citra.citra_emu.utils.BuildUtil
|
||||
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||
import org.citra.citra_emu.utils.DirectoryInitialization.DirectoryInitializationState
|
||||
import org.citra.citra_emu.utils.EmulationLifecycleUtil
|
||||
import org.citra.citra_emu.utils.EmulationMenuSettings
|
||||
import org.citra.citra_emu.utils.GameHelper
|
||||
import org.citra.citra_emu.utils.GameIconUtils
|
||||
import org.citra.citra_emu.utils.EmulationLifecycleUtil
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import org.citra.citra_emu.utils.ViewUtils
|
||||
import org.citra.citra_emu.viewmodel.EmulationViewModel
|
||||
|
||||
class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.FrameCallback {
|
||||
class EmulationFragment :
|
||||
Fragment(),
|
||||
SurfaceHolder.Callback,
|
||||
Choreographer.FrameCallback {
|
||||
private val preferences: SharedPreferences
|
||||
get() = PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
|
||||
|
||||
|
|
@ -117,7 +120,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
if (context is EmulationActivity) {
|
||||
NativeLibrary.setEmulationActivity(context)
|
||||
NativeLibrary.setEmulationActivity(context)
|
||||
} else {
|
||||
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
|
||||
}
|
||||
|
|
@ -145,10 +148,16 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
if (!BuildUtil.isGooglePlayBuild) {
|
||||
val intentUriString = intentUri.toString()
|
||||
// We need to build a special path as the incoming URI may be SAF exclusive
|
||||
Log.warning("[EmulationFragment] Cannot determine native path of URI \"" +
|
||||
intentUriString + "\", using file descriptor instead.")
|
||||
Log.warning(
|
||||
"[EmulationFragment] Cannot determine native path of URI \"" +
|
||||
intentUriString + "\", using file descriptor instead."
|
||||
)
|
||||
if (!intentUriString.startsWith("!")) {
|
||||
gameFd = requireContext().contentResolver.openFileDescriptor(intentUri, "r")?.detachFd()
|
||||
gameFd =
|
||||
requireContext().contentResolver.openFileDescriptor(
|
||||
intentUri,
|
||||
"r"
|
||||
)?.detachFd()
|
||||
intentUri = if (gameFd != null) {
|
||||
Uri.parse("fd://" + gameFd.toString())
|
||||
} else {
|
||||
|
|
@ -159,7 +168,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
intentGame =
|
||||
intentUri?.let {
|
||||
// isInstalled, addedToLibrary and mediaType do not matter here
|
||||
GameHelper.getGame(it, isInstalled = false, addedToLibrary = false, mediaType = Game.MediaType.GAME_CARD)
|
||||
GameHelper.getGame(
|
||||
it,
|
||||
isInstalled = false,
|
||||
addedToLibrary = false,
|
||||
mediaType = Game.MediaType.GAME_CARD
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,10 +213,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
emulationActivity.secondaryDisplayManager.availableDisplays.isNotEmpty()
|
||||
binding.inGameMenu.menu.findItem(R.id.menu_landscape_screen_layout).isVisible =
|
||||
CitraApplication.appContext.resources.configuration.orientation !=
|
||||
Configuration.ORIENTATION_PORTRAIT
|
||||
Configuration.ORIENTATION_PORTRAIT
|
||||
binding.inGameMenu.menu.findItem(R.id.menu_portrait_screen_layout).isVisible =
|
||||
CitraApplication.appContext.resources.configuration.orientation ==
|
||||
Configuration.ORIENTATION_PORTRAIT
|
||||
Configuration.ORIENTATION_PORTRAIT
|
||||
return binding.root
|
||||
}
|
||||
|
||||
|
|
@ -486,7 +500,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
ViewUtils.showView(binding.surfaceInputOverlay)
|
||||
binding.inGameMenu.menu.findItem(R.id.menu_emulation_savestates)
|
||||
.setVisible(NativeLibrary.getSavestateInfo() != null)
|
||||
binding.drawerLayout.setDrawerLockMode(EmulationMenuSettings.drawerLockMode)
|
||||
binding.drawerLayout.setDrawerLockMode(
|
||||
EmulationMenuSettings.drawerLockMode
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -496,9 +512,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
setInsets()
|
||||
}
|
||||
|
||||
fun isDrawerOpen(): Boolean {
|
||||
return binding.drawerLayout.isOpen
|
||||
}
|
||||
fun isDrawerOpen(): Boolean = binding.drawerLayout.isOpen
|
||||
|
||||
private fun togglePause() {
|
||||
if (emulationState.isPaused) {
|
||||
|
|
@ -612,7 +626,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
}
|
||||
|
||||
private fun showStateSubmenu(isSaving: Boolean) {
|
||||
|
||||
val savestates = NativeLibrary.getSavestateInfo()
|
||||
|
||||
val popupMenu = PopupMenu(
|
||||
|
|
@ -1023,7 +1036,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
|
||||
else ->
|
||||
R.id.menu_portrait_layout_top_full
|
||||
|
||||
}
|
||||
|
||||
popupMenu.menu.findItem(layoutOptionMenuItem).setChecked(true)
|
||||
|
|
@ -1031,12 +1043,16 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
popupMenu.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_portrait_layout_top_full -> {
|
||||
screenAdjustmentUtil.changePortraitOrientation(PortraitScreenLayout.TOP_FULL_WIDTH.int)
|
||||
screenAdjustmentUtil.changePortraitOrientation(
|
||||
PortraitScreenLayout.TOP_FULL_WIDTH.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_portrait_layout_original -> {
|
||||
screenAdjustmentUtil.changePortraitOrientation(PortraitScreenLayout.ORIGINAL.int)
|
||||
screenAdjustmentUtil.changePortraitOrientation(
|
||||
PortraitScreenLayout.ORIGINAL.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -1046,7 +1062,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
R.string.emulation_adjust_custom_layout,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
screenAdjustmentUtil.changePortraitOrientation(PortraitScreenLayout.CUSTOM_PORTRAIT_LAYOUT.int)
|
||||
screenAdjustmentUtil.changePortraitOrientation(
|
||||
PortraitScreenLayout.CUSTOM_PORTRAIT_LAYOUT.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -1071,12 +1089,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
val displays =
|
||||
emulationActivity.secondaryDisplayManager.availableDisplays
|
||||
|
||||
if (selectedLayout == SecondaryDisplayLayout.NONE.int || !BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean) {
|
||||
if (selectedLayout == SecondaryDisplayLayout.NONE.int ||
|
||||
!BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean
|
||||
) {
|
||||
BooleanSetting.ENABLE_SECONDARY_DISPLAY.boolean = false
|
||||
enableSecondaryCheckbox.isChecked = false
|
||||
chooserMenu.isVisible = false
|
||||
popupMenu.menu.setGroupEnabled(R.id.menu_secondary_layout_group, false)
|
||||
|
||||
} else {
|
||||
popupMenu.menu.setGroupEnabled(R.id.menu_secondary_layout_group, true)
|
||||
chooserMenu.isVisible = (displays.size > 1)
|
||||
|
|
@ -1144,37 +1163,51 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
}
|
||||
|
||||
R.id.menu_secondary_layout_opposite -> {
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(SecondaryDisplayLayout.REVERSE_PRIMARY.int)
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(
|
||||
SecondaryDisplayLayout.REVERSE_PRIMARY.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_secondary_layout_top -> {
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(SecondaryDisplayLayout.TOP_SCREEN.int)
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(
|
||||
SecondaryDisplayLayout.TOP_SCREEN.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_secondary_layout_bottom -> {
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(SecondaryDisplayLayout.BOTTOM_SCREEN.int)
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(
|
||||
SecondaryDisplayLayout.BOTTOM_SCREEN.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_secondary_layout_side_by_side -> {
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(SecondaryDisplayLayout.SIDE_BY_SIDE.int)
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(
|
||||
SecondaryDisplayLayout.SIDE_BY_SIDE.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_secondary_layout_hybrid -> {
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(SecondaryDisplayLayout.HYBRID.int)
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(
|
||||
SecondaryDisplayLayout.HYBRID.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_secondary_layout_original -> {
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(SecondaryDisplayLayout.ORIGINAL.int)
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(
|
||||
SecondaryDisplayLayout.ORIGINAL.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_secondary_layout_largescreen -> {
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(SecondaryDisplayLayout.LARGE_SCREEN.int)
|
||||
screenAdjustmentUtil.changeSecondaryOrientation(
|
||||
SecondaryDisplayLayout.LARGE_SCREEN.int
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -1222,7 +1255,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.emulation_toggle_controls)
|
||||
.setMultiChoiceItems(
|
||||
R.array.n3dsButtons, enabledButtons
|
||||
R.array.n3dsButtons,
|
||||
enabledButtons
|
||||
) { _: DialogInterface?, indexSelected: Int, isChecked: Boolean ->
|
||||
editor.putBoolean("buttonToggle$indexSelected", isChecked)
|
||||
}
|
||||
|
|
@ -1267,14 +1301,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||
})
|
||||
slider.addOnChangeListener(
|
||||
Slider.OnChangeListener { slider: Slider, progress: Float, _: Boolean ->
|
||||
Slider.OnChangeListener {
|
||||
slider: Slider,
|
||||
progress: Float,
|
||||
_: Boolean
|
||||
->
|
||||
if (textValue.text.toString() != (slider.value + 50).toInt().toString()) {
|
||||
textValue.setText((slider.value + 50).toInt().toString())
|
||||
textValue.setSelection(textValue.length())
|
||||
setControlScale(slider.value.toInt(), target)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
)
|
||||
textInput.suffixText = "%"
|
||||
}
|
||||
val previousProgress = sliderBinding.slider.value.toInt()
|
||||
|
|
@ -1318,7 +1356,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||
})
|
||||
|
||||
|
||||
slider.addOnChangeListener { _: Slider, value: Float, _: Boolean ->
|
||||
|
||||
if (textValue.text.toString() != slider.value.toInt().toString()) {
|
||||
|
|
@ -1451,7 +1488,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
(perfStats[TIME_SWAP] * 1000.0f).toFloat(),
|
||||
(perfStats[TIME_IPC] * 1000.0f).toFloat(),
|
||||
(perfStats[TIME_SVC] * 1000.0f).toFloat(),
|
||||
(perfStats[TIME_REM] * 1000.0f).toFloat(),
|
||||
(perfStats[TIME_REM] * 1000.0f).toFloat()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -1469,7 +1506,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
if (BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.boolean) {
|
||||
if (sb.isNotEmpty()) sb.append(dividerString)
|
||||
val appRamUsage =
|
||||
File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000
|
||||
File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 /
|
||||
1000000
|
||||
sb.append("Process\u00A0RAM:\u00A0$appRamUsage\u00A0MB")
|
||||
}
|
||||
|
||||
|
|
@ -1494,7 +1532,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
}
|
||||
|
||||
if (BooleanSetting.PERF_OVERLAY_BACKGROUND.boolean) {
|
||||
binding.performanceOverlayShowText.setBackgroundResource(R.color.citra_transparent_black)
|
||||
binding.performanceOverlayShowText.setBackgroundResource(
|
||||
R.color.citra_transparent_black
|
||||
)
|
||||
} else {
|
||||
binding.performanceOverlayShowText.setBackgroundResource(0)
|
||||
}
|
||||
|
|
@ -1558,10 +1598,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
|||
}
|
||||
}
|
||||
|
||||
private fun celsiusToFahrenheit(celsius: Float): Float {
|
||||
return (celsius * 9 / 5) + 32
|
||||
}
|
||||
|
||||
private fun celsiusToFahrenheit(celsius: Float): Float = (celsius * 9 / 5) + 32
|
||||
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
// We purposely don't do anything here.
|
||||
|
|
|
|||
|
|
@ -59,7 +59,13 @@ class GamesFragment : Fragment() {
|
|||
private var pendingCompressInvocation: String? = null
|
||||
|
||||
companion object {
|
||||
fun doCompression(fragment: Fragment, gamesViewModel: GamesViewModel, inputPath: String?, outputUri: Uri?, shouldCompress: Boolean) {
|
||||
fun doCompression(
|
||||
fragment: Fragment,
|
||||
gamesViewModel: GamesViewModel,
|
||||
inputPath: String?,
|
||||
outputUri: Uri?,
|
||||
shouldCompress: Boolean
|
||||
) {
|
||||
if (outputUri != null) {
|
||||
val outputPath: String =
|
||||
if (!BuildUtil.isGooglePlayBuild) {
|
||||
|
|
@ -247,41 +253,40 @@ class GamesFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
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)
|
||||
val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
|
||||
val spacingNavigationRail =
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_navigation_rail)
|
||||
private fun setInsets() = 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)
|
||||
val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
|
||||
val spacingNavigationRail =
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_navigation_rail)
|
||||
|
||||
binding.gridGames.updatePadding(
|
||||
top = barInsets.top + extraListSpacing,
|
||||
bottom = barInsets.bottom + spacingNavigation + extraListSpacing
|
||||
)
|
||||
binding.gridGames.updatePadding(
|
||||
top = barInsets.top + extraListSpacing,
|
||||
bottom = barInsets.bottom + spacingNavigation + extraListSpacing
|
||||
)
|
||||
|
||||
binding.swipeRefresh.setProgressViewEndTarget(
|
||||
false,
|
||||
barInsets.top + resources.getDimensionPixelSize(R.dimen.spacing_refresh_end)
|
||||
)
|
||||
binding.swipeRefresh.setProgressViewEndTarget(
|
||||
false,
|
||||
barInsets.top + resources.getDimensionPixelSize(R.dimen.spacing_refresh_end)
|
||||
)
|
||||
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
val mlpSwipe = binding.swipeRefresh.layoutParams as MarginLayoutParams
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
mlpSwipe.leftMargin = leftInsets + spacingNavigationRail
|
||||
mlpSwipe.rightMargin = rightInsets
|
||||
} else {
|
||||
mlpSwipe.leftMargin = leftInsets
|
||||
mlpSwipe.rightMargin = rightInsets + spacingNavigationRail
|
||||
}
|
||||
binding.swipeRefresh.layoutParams = mlpSwipe
|
||||
|
||||
binding.noticeText.updatePadding(bottom = spacingNavigation)
|
||||
|
||||
windowInsets
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
val mlpSwipe = binding.swipeRefresh.layoutParams as MarginLayoutParams
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
mlpSwipe.leftMargin = leftInsets + spacingNavigationRail
|
||||
mlpSwipe.rightMargin = rightInsets
|
||||
} else {
|
||||
mlpSwipe.leftMargin = leftInsets
|
||||
mlpSwipe.rightMargin = rightInsets + spacingNavigationRail
|
||||
}
|
||||
binding.swipeRefresh.layoutParams = mlpSwipe
|
||||
|
||||
binding.noticeText.updatePadding(bottom = spacingNavigation)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@ class GrantMissingFilesystemPermissionFragment : DialogFragment() {
|
|||
{ permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.filesystem_permission_warning)
|
||||
.setMessage(R.string.filesystem_permission_lost)
|
||||
|
|
|
|||
|
|
@ -32,19 +32,19 @@ import org.citra.citra_emu.R
|
|||
import org.citra.citra_emu.adapters.HomeSettingAdapter
|
||||
import org.citra.citra_emu.databinding.DialogSoftwareKeyboardBinding
|
||||
import org.citra.citra_emu.databinding.FragmentHomeSettingsBinding
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.features.settings.SettingKeys
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.features.settings.ui.SettingsActivity
|
||||
import org.citra.citra_emu.features.settings.utils.SettingsFile
|
||||
import org.citra.citra_emu.model.Game
|
||||
import org.citra.citra_emu.model.HomeSetting
|
||||
import org.citra.citra_emu.ui.main.MainActivity
|
||||
import org.citra.citra_emu.utils.GameHelper
|
||||
import org.citra.citra_emu.utils.PermissionsHandler
|
||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||
import org.citra.citra_emu.utils.GpuDriverHelper
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import org.citra.citra_emu.utils.PermissionsHandler
|
||||
import org.citra.citra_emu.viewmodel.DriverViewModel
|
||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||
|
||||
class HomeSettingsFragment : Fragment() {
|
||||
private var _binding: FragmentHomeSettingsBinding? = null
|
||||
|
|
@ -89,7 +89,10 @@ class HomeSettingsFragment : Fragment() {
|
|||
{
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val inputBinding = DialogSoftwareKeyboardBinding.inflate(inflater)
|
||||
var textInputValue: String = preferences.getString(SettingKeys.last_artic_base_addr(), "")!!
|
||||
var textInputValue: String = preferences.getString(
|
||||
SettingKeys.last_artic_base_addr(),
|
||||
""
|
||||
)!!
|
||||
|
||||
inputBinding.editTextInput.setText(textInputValue)
|
||||
inputBinding.editTextInput.doOnTextChanged { text, _, _, _ ->
|
||||
|
|
@ -103,7 +106,10 @@ class HomeSettingsFragment : Fragment() {
|
|||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
if (textInputValue.isNotEmpty()) {
|
||||
preferences.edit()
|
||||
.putString(SettingKeys.last_artic_base_addr(), textInputValue)
|
||||
.putString(
|
||||
SettingKeys.last_artic_base_addr(),
|
||||
textInputValue
|
||||
)
|
||||
.apply()
|
||||
val menu = Game(
|
||||
title = getString(R.string.artic_base),
|
||||
|
|
@ -115,7 +121,7 @@ class HomeSettingsFragment : Fragment() {
|
|||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) {_, _ -> }
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
|
@ -267,37 +273,36 @@ class HomeSettingsFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
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)
|
||||
val spacingNavigationRail =
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_navigation_rail)
|
||||
private fun setInsets() = 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)
|
||||
val spacingNavigationRail =
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_navigation_rail)
|
||||
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
binding.scrollViewSettings.updatePadding(
|
||||
top = barInsets.top,
|
||||
bottom = barInsets.bottom
|
||||
)
|
||||
binding.scrollViewSettings.updatePadding(
|
||||
top = barInsets.top,
|
||||
bottom = barInsets.bottom
|
||||
)
|
||||
|
||||
val mlpScrollSettings = binding.scrollViewSettings.layoutParams as MarginLayoutParams
|
||||
mlpScrollSettings.leftMargin = leftInsets
|
||||
mlpScrollSettings.rightMargin = rightInsets
|
||||
binding.scrollViewSettings.layoutParams = mlpScrollSettings
|
||||
val mlpScrollSettings = binding.scrollViewSettings.layoutParams as MarginLayoutParams
|
||||
mlpScrollSettings.leftMargin = leftInsets
|
||||
mlpScrollSettings.rightMargin = rightInsets
|
||||
binding.scrollViewSettings.layoutParams = mlpScrollSettings
|
||||
|
||||
binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation)
|
||||
binding.linearLayoutSettings.updatePadding(bottom = spacingNavigation)
|
||||
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
binding.linearLayoutSettings.updatePadding(left = spacingNavigationRail)
|
||||
} else {
|
||||
binding.linearLayoutSettings.updatePadding(right = spacingNavigationRail)
|
||||
}
|
||||
|
||||
windowInsets
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
binding.linearLayoutSettings.updatePadding(left = spacingNavigationRail)
|
||||
} else {
|
||||
binding.linearLayoutSettings.updatePadding(right = spacingNavigationRail)
|
||||
}
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,9 +56,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
|||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return binding.root
|
||||
}
|
||||
): View = binding.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
|
|
|||
|
|
@ -78,21 +78,27 @@ class KeyboardDialogFragment : DialogFragment() {
|
|||
return@setOnClickListener
|
||||
}
|
||||
dismiss()
|
||||
synchronized(SoftwareKeyboard.finishLock) { SoftwareKeyboard.finishLock.notifyAll() }
|
||||
synchronized(SoftwareKeyboard.finishLock) {
|
||||
SoftwareKeyboard.finishLock.notifyAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL) != null) {
|
||||
alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener {
|
||||
SoftwareKeyboard.data.button = 1
|
||||
dismiss()
|
||||
synchronized(SoftwareKeyboard.finishLock) { SoftwareKeyboard.finishLock.notifyAll() }
|
||||
synchronized(SoftwareKeyboard.finishLock) {
|
||||
SoftwareKeyboard.finishLock.notifyAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE) != null) {
|
||||
alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener {
|
||||
SoftwareKeyboard.data.button = 0
|
||||
dismiss()
|
||||
synchronized(SoftwareKeyboard.finishLock) { SoftwareKeyboard.finishLock.notifyAll() }
|
||||
synchronized(SoftwareKeyboard.finishLock) {
|
||||
SoftwareKeyboard.finishLock.notifyAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,9 +57,7 @@ class LicenseBottomSheetDialogFragment : BottomSheetDialogFragment() {
|
|||
|
||||
const val LICENSE = "License"
|
||||
|
||||
fun newInstance(
|
||||
license: License
|
||||
): LicenseBottomSheetDialogFragment {
|
||||
fun newInstance(license: License): LicenseBottomSheetDialogFragment {
|
||||
val dialog = LicenseBottomSheetDialogFragment()
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(LICENSE, license)
|
||||
|
|
|
|||
|
|
@ -174,28 +174,27 @@ class LicensesFragment : Fragment() {
|
|||
setInsets()
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
val mlpAppBar = binding.toolbarLicenses.layoutParams as MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarLicenses.layoutParams = mlpAppBar
|
||||
val mlpAppBar = binding.toolbarLicenses.layoutParams as MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarLicenses.layoutParams = mlpAppBar
|
||||
|
||||
val mlpScrollAbout = binding.listLicenses.layoutParams as MarginLayoutParams
|
||||
mlpScrollAbout.leftMargin = leftInsets
|
||||
mlpScrollAbout.rightMargin = rightInsets
|
||||
binding.listLicenses.layoutParams = mlpScrollAbout
|
||||
val mlpScrollAbout = binding.listLicenses.layoutParams as MarginLayoutParams
|
||||
mlpScrollAbout.leftMargin = leftInsets
|
||||
mlpScrollAbout.rightMargin = rightInsets
|
||||
binding.listLicenses.layoutParams = mlpScrollAbout
|
||||
|
||||
binding.listLicenses.updatePadding(bottom = barInsets.bottom)
|
||||
binding.listLicenses.updatePadding(bottom = barInsets.bottom)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,22 @@ class MiiSelectorDialogFragment : DialogFragment() {
|
|||
list.add(getString(R.string.standard_mii))
|
||||
list.addAll(config.miiNames)
|
||||
val initialIndex =
|
||||
if (config.initiallySelectedMiiIndex < list.size) config.initiallySelectedMiiIndex.toInt() else 0
|
||||
if (config.initiallySelectedMiiIndex <
|
||||
list.size
|
||||
) {
|
||||
config.initiallySelectedMiiIndex.toInt()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
MiiSelector.data.index = initialIndex
|
||||
val builder = MaterialAlertDialogBuilder(requireActivity())
|
||||
.setTitle(if (config.title!!.isEmpty()) getString(R.string.mii_selector) else config.title)
|
||||
.setSingleChoiceItems(list.toTypedArray(), initialIndex) { _: DialogInterface?, which: Int ->
|
||||
.setTitle(
|
||||
if (config.title!!.isEmpty()) getString(R.string.mii_selector) else config.title
|
||||
)
|
||||
.setSingleChoiceItems(list.toTypedArray(), initialIndex) {
|
||||
_: DialogInterface?,
|
||||
which: Int
|
||||
->
|
||||
MiiSelector.data.index = which
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import kotlin.math.abs
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.databinding.DialogInputBinding
|
||||
import org.citra.citra_emu.features.settings.model.view.InputBindingSetting
|
||||
import org.citra.citra_emu.utils.Log
|
||||
import kotlin.math.abs
|
||||
|
||||
class MotionBottomSheetDialogFragment : BottomSheetDialogFragment() {
|
||||
private var _binding: DialogInputBinding? = null
|
||||
|
|
|
|||
|
|
@ -28,19 +28,19 @@ import androidx.preference.PreferenceManager
|
|||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import info.debatty.java.stringsimilarity.Jaccard
|
||||
import info.debatty.java.stringsimilarity.JaroWinkler
|
||||
import java.time.temporal.ChronoField
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.adapters.GameAdapter
|
||||
import org.citra.citra_emu.databinding.FragmentSearchBinding
|
||||
import org.citra.citra_emu.model.Game
|
||||
import org.citra.citra_emu.viewmodel.CompressProgressDialogViewModel
|
||||
import org.citra.citra_emu.viewmodel.GamesViewModel
|
||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||
import java.time.temporal.ChronoField
|
||||
import java.util.Locale
|
||||
|
||||
class SearchFragment : Fragment() {
|
||||
private var _binding: FragmentSearchBinding? = null
|
||||
|
|
@ -61,7 +61,13 @@ class SearchFragment : Fragment() {
|
|||
private val onCompressDecompressLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.CreateDocument("application/octet-stream")
|
||||
) { uri: Uri? ->
|
||||
GamesFragment.doCompression(this, gamesViewModel, pendingCompressInvocation, uri, shouldCompress)
|
||||
GamesFragment.doCompression(
|
||||
this,
|
||||
gamesViewModel,
|
||||
pendingCompressInvocation,
|
||||
uri,
|
||||
shouldCompress
|
||||
)
|
||||
pendingCompressInvocation = null
|
||||
}
|
||||
|
||||
|
|
@ -184,14 +190,16 @@ class SearchFragment : Fragment() {
|
|||
R.id.chip_recently_played -> {
|
||||
baseList.filter {
|
||||
val lastPlayedTime = preferences.getLong(it.keyLastPlayedTime, 0L)
|
||||
lastPlayedTime > (System.currentTimeMillis() - ChronoField.MILLI_OF_DAY.range().maximum)
|
||||
lastPlayedTime >
|
||||
(System.currentTimeMillis() - ChronoField.MILLI_OF_DAY.range().maximum)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.chip_recently_added -> {
|
||||
baseList.filter {
|
||||
val addedTime = preferences.getLong(it.keyAddedToLibraryTime, 0L)
|
||||
addedTime > (System.currentTimeMillis() - ChronoField.MILLI_OF_DAY.range().maximum)
|
||||
addedTime >
|
||||
(System.currentTimeMillis() - ChronoField.MILLI_OF_DAY.range().maximum)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -242,54 +250,53 @@ class SearchFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
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)
|
||||
val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
|
||||
val spacingNavigationRail =
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_navigation_rail)
|
||||
val chipSpacing = resources.getDimensionPixelSize(R.dimen.spacing_chip)
|
||||
private fun setInsets() = 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)
|
||||
val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
|
||||
val spacingNavigationRail =
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_navigation_rail)
|
||||
val chipSpacing = resources.getDimensionPixelSize(R.dimen.spacing_chip)
|
||||
|
||||
binding.constraintSearch.updatePadding(
|
||||
left = barInsets.left + cutoutInsets.left,
|
||||
top = barInsets.top,
|
||||
right = barInsets.right + cutoutInsets.right
|
||||
binding.constraintSearch.updatePadding(
|
||||
left = barInsets.left + cutoutInsets.left,
|
||||
top = barInsets.top,
|
||||
right = barInsets.right + cutoutInsets.right
|
||||
)
|
||||
|
||||
binding.gridGamesSearch.updatePadding(
|
||||
top = extraListSpacing,
|
||||
bottom = barInsets.bottom + spacingNavigation + extraListSpacing
|
||||
)
|
||||
binding.noResultsView.updatePadding(bottom = spacingNavigation + barInsets.bottom)
|
||||
|
||||
val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
binding.frameSearch.updatePadding(left = spacingNavigationRail)
|
||||
binding.gridGamesSearch.updatePadding(left = spacingNavigationRail)
|
||||
binding.noResultsView.updatePadding(left = spacingNavigationRail)
|
||||
binding.chipGroup.updatePadding(
|
||||
left = chipSpacing + spacingNavigationRail,
|
||||
right = chipSpacing
|
||||
)
|
||||
|
||||
binding.gridGamesSearch.updatePadding(
|
||||
top = extraListSpacing,
|
||||
bottom = barInsets.bottom + spacingNavigation + extraListSpacing
|
||||
mlpDivider.leftMargin = chipSpacing + spacingNavigationRail
|
||||
mlpDivider.rightMargin = chipSpacing
|
||||
} else {
|
||||
binding.frameSearch.updatePadding(right = spacingNavigationRail)
|
||||
binding.gridGamesSearch.updatePadding(right = spacingNavigationRail)
|
||||
binding.noResultsView.updatePadding(right = spacingNavigationRail)
|
||||
binding.chipGroup.updatePadding(
|
||||
left = chipSpacing,
|
||||
right = chipSpacing + spacingNavigationRail
|
||||
)
|
||||
binding.noResultsView.updatePadding(bottom = spacingNavigation + barInsets.bottom)
|
||||
|
||||
val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
binding.frameSearch.updatePadding(left = spacingNavigationRail)
|
||||
binding.gridGamesSearch.updatePadding(left = spacingNavigationRail)
|
||||
binding.noResultsView.updatePadding(left = spacingNavigationRail)
|
||||
binding.chipGroup.updatePadding(
|
||||
left = chipSpacing + spacingNavigationRail,
|
||||
right = chipSpacing
|
||||
)
|
||||
mlpDivider.leftMargin = chipSpacing + spacingNavigationRail
|
||||
mlpDivider.rightMargin = chipSpacing
|
||||
} else {
|
||||
binding.frameSearch.updatePadding(right = spacingNavigationRail)
|
||||
binding.gridGamesSearch.updatePadding(right = spacingNavigationRail)
|
||||
binding.noResultsView.updatePadding(right = spacingNavigationRail)
|
||||
binding.chipGroup.updatePadding(
|
||||
left = chipSpacing,
|
||||
right = chipSpacing + spacingNavigationRail
|
||||
)
|
||||
mlpDivider.leftMargin = chipSpacing
|
||||
mlpDivider.rightMargin = chipSpacing + spacingNavigationRail
|
||||
}
|
||||
binding.divider.layoutParams = mlpDivider
|
||||
|
||||
windowInsets
|
||||
mlpDivider.leftMargin = chipSpacing
|
||||
mlpDivider.rightMargin = chipSpacing + spacingNavigationRail
|
||||
}
|
||||
binding.divider.layoutParams = mlpDivider
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,15 @@ import org.citra.citra_emu.ui.main.MainActivity
|
|||
import org.citra.citra_emu.utils.PermissionsHandler
|
||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||
|
||||
class SelectUserDirectoryDialogFragment(titleOverride: Int? = null, descriptionOverride: Int? = null) : DialogFragment() {
|
||||
class SelectUserDirectoryDialogFragment(
|
||||
titleOverride: Int? = null,
|
||||
descriptionOverride: Int? = null
|
||||
) : DialogFragment() {
|
||||
private lateinit var mainActivity: MainActivity
|
||||
|
||||
private val title = titleOverride ?: R.string.select_citra_user_folder
|
||||
private val description = descriptionOverride ?: R.string.selecting_user_directory_without_write_permissions
|
||||
private val description =
|
||||
descriptionOverride ?: R.string.selecting_user_directory_without_write_permissions
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
mainActivity = requireActivity() as MainActivity
|
||||
|
|
@ -31,7 +35,9 @@ class SelectUserDirectoryDialogFragment(titleOverride: Int? = null, descriptionO
|
|||
.setTitle(title)
|
||||
.setMessage(description)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
|
||||
PermissionsHandler.compatibleSelectDirectory(mainActivity.openCitraDirectoryLostPermission)
|
||||
PermissionsHandler.compatibleSelectDirectory(
|
||||
mainActivity.openCitraDirectoryLostPermission
|
||||
)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
|
@ -39,8 +45,11 @@ class SelectUserDirectoryDialogFragment(titleOverride: Int? = null, descriptionO
|
|||
companion object {
|
||||
const val TAG = "SelectUserDirectoryDialogFragment"
|
||||
|
||||
fun newInstance(activity: FragmentActivity, titleOverride: Int? = null, descriptionOverride: Int? = null):
|
||||
SelectUserDirectoryDialogFragment {
|
||||
fun newInstance(
|
||||
activity: FragmentActivity,
|
||||
titleOverride: Int? = null,
|
||||
descriptionOverride: Int? = null
|
||||
): SelectUserDirectoryDialogFragment {
|
||||
ViewModelProvider(activity)[HomeViewModel::class.java].setPickingUserDir(true)
|
||||
return SelectUserDirectoryDialogFragment(titleOverride, descriptionOverride)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,9 @@ class SetupFragment : Fragment() {
|
|||
)
|
||||
)
|
||||
} else {
|
||||
permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
permissionLauncher.launch(
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
}
|
||||
},
|
||||
buttonState = {
|
||||
|
|
@ -187,7 +189,7 @@ class SetupFragment : Fragment() {
|
|||
isUnskippable = true,
|
||||
hasWarning = true,
|
||||
R.string.filesystem_permission_warning,
|
||||
R.string.filesystem_permission_warning_description,
|
||||
R.string.filesystem_permission_warning_description
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -199,7 +201,9 @@ class SetupFragment : Fragment() {
|
|||
R.string.notifications_description,
|
||||
buttonAction = {
|
||||
pageButtonCallback = it
|
||||
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
permissionLauncher.launch(
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
)
|
||||
},
|
||||
buttonState = {
|
||||
if (NotificationManagerCompat.from(requireContext())
|
||||
|
|
@ -213,7 +217,7 @@ class SetupFragment : Fragment() {
|
|||
isUnskippable = false,
|
||||
hasWarning = true,
|
||||
R.string.notification_warning,
|
||||
R.string.notification_warning_description,
|
||||
R.string.notification_warning_description
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -236,7 +240,7 @@ class SetupFragment : Fragment() {
|
|||
} else {
|
||||
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
|
|
@ -258,10 +262,10 @@ class SetupFragment : Fragment() {
|
|||
} else {
|
||||
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
}
|
||||
) {
|
||||
var permissionsComplete =
|
||||
// Microphone
|
||||
|
|
@ -269,14 +273,14 @@ class SetupFragment : Fragment() {
|
|||
requireContext(),
|
||||
Manifest.permission.RECORD_AUDIO
|
||||
) == PackageManager.PERMISSION_GRANTED &&
|
||||
// Camera
|
||||
ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.CAMERA
|
||||
) == PackageManager.PERMISSION_GRANTED &&
|
||||
// Notifications
|
||||
NotificationManagerCompat.from(requireContext())
|
||||
.areNotificationsEnabled()
|
||||
// Camera
|
||||
ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.CAMERA
|
||||
) == PackageManager.PERMISSION_GRANTED &&
|
||||
// Notifications
|
||||
NotificationManagerCompat.from(requireContext())
|
||||
.areNotificationsEnabled()
|
||||
// External Storage
|
||||
if (!BuildUtil.isGooglePlayBuild) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
|
|
@ -284,10 +288,12 @@ class SetupFragment : Fragment() {
|
|||
(permissionsComplete && Environment.isExternalStorageManager())
|
||||
} else {
|
||||
permissionsComplete =
|
||||
(permissionsComplete && ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED)
|
||||
(
|
||||
permissionsComplete && ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -315,7 +321,9 @@ class SetupFragment : Fragment() {
|
|||
R.string.select_citra_user_folder_description,
|
||||
buttonAction = {
|
||||
pageButtonCallback = it
|
||||
PermissionsHandler.compatibleSelectDirectory(mainActivity.setupOpenCitraDirectory)
|
||||
PermissionsHandler.compatibleSelectDirectory(
|
||||
mainActivity.setupOpenCitraDirectory
|
||||
)
|
||||
},
|
||||
buttonState = {
|
||||
if (PermissionsHandler.hasWriteAccess(requireContext())) {
|
||||
|
|
@ -344,7 +352,11 @@ class SetupFragment : Fragment() {
|
|||
)
|
||||
},
|
||||
buttonState = {
|
||||
if (preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()) {
|
||||
if (preferences.getString(
|
||||
GameHelper.KEY_GAME_PATH,
|
||||
""
|
||||
)!!.isNotEmpty()
|
||||
) {
|
||||
ButtonState.BUTTON_ACTION_COMPLETE
|
||||
} else {
|
||||
ButtonState.BUTTON_ACTION_INCOMPLETE
|
||||
|
|
@ -353,17 +365,16 @@ class SetupFragment : Fragment() {
|
|||
isUnskippable = false,
|
||||
hasWarning = true,
|
||||
R.string.add_games_warning,
|
||||
R.string.add_games_warning_description,
|
||||
R.string.add_games_warning_description
|
||||
)
|
||||
)
|
||||
},
|
||||
}
|
||||
) {
|
||||
if (
|
||||
PermissionsHandler.hasWriteAccess(requireContext()) &&
|
||||
preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()
|
||||
) {
|
||||
PageState.PAGE_STEPS_COMPLETE
|
||||
|
||||
} else {
|
||||
PageState.PAGE_STEPS_INCOMPLETE
|
||||
}
|
||||
|
|
@ -483,7 +494,8 @@ class SetupFragment : Fragment() {
|
|||
if (savedInstanceState == null) {
|
||||
hasBeenWarned = BooleanArray(pages.size)
|
||||
} else {
|
||||
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED) ?: BooleanArray(pages.size)
|
||||
hasBeenWarned =
|
||||
savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED) ?: BooleanArray(pages.size)
|
||||
}
|
||||
|
||||
updateNavigationButtons(binding.viewPager2.currentItem)
|
||||
|
|
@ -590,8 +602,11 @@ class SetupFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
CitraDirectoryHelper(requireActivity(), true).showCitraDirectoryDialog(result,
|
||||
null, checkForButtonState)
|
||||
CitraDirectoryHelper(requireActivity(), true).showCitraDirectoryDialog(
|
||||
result,
|
||||
null,
|
||||
checkForButtonState
|
||||
)
|
||||
}
|
||||
|
||||
private fun onGetGamesDirectory(result: Uri) {
|
||||
|
|
@ -632,33 +647,32 @@ class SetupFragment : Fragment() {
|
|||
hasBeenWarned[page] = true
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
private fun setInsets() = ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
|
||||
val leftPadding = barInsets.left + cutoutInsets.left
|
||||
val topPadding = barInsets.top + cutoutInsets.top
|
||||
val rightPadding = barInsets.right + cutoutInsets.right
|
||||
val bottomPadding = barInsets.bottom + cutoutInsets.bottom
|
||||
val leftPadding = barInsets.left + cutoutInsets.left
|
||||
val topPadding = barInsets.top + cutoutInsets.top
|
||||
val rightPadding = barInsets.right + cutoutInsets.right
|
||||
val bottomPadding = barInsets.bottom + cutoutInsets.bottom
|
||||
|
||||
if (resources.getBoolean(R.bool.small_layout)) {
|
||||
binding.viewPager2
|
||||
.updatePadding(left = leftPadding, top = topPadding, right = rightPadding)
|
||||
binding.constraintButtons
|
||||
.updatePadding(left = leftPadding, right = rightPadding, bottom = bottomPadding)
|
||||
} else {
|
||||
binding.viewPager2.updatePadding(top = topPadding, bottom = bottomPadding)
|
||||
binding.constraintButtons
|
||||
.setPadding(
|
||||
leftPadding + rightPadding,
|
||||
topPadding,
|
||||
rightPadding + leftPadding,
|
||||
bottomPadding
|
||||
)
|
||||
}
|
||||
windowInsets
|
||||
if (resources.getBoolean(R.bool.small_layout)) {
|
||||
binding.viewPager2
|
||||
.updatePadding(left = leftPadding, top = topPadding, right = rightPadding)
|
||||
binding.constraintButtons
|
||||
.updatePadding(left = leftPadding, right = rightPadding, bottom = bottomPadding)
|
||||
} else {
|
||||
binding.viewPager2.updatePadding(top = topPadding, bottom = bottomPadding)
|
||||
binding.constraintButtons
|
||||
.setPadding(
|
||||
leftPadding + rightPadding,
|
||||
topPadding,
|
||||
rightPadding + leftPadding,
|
||||
bottomPadding
|
||||
)
|
||||
}
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ import org.citra.citra_emu.NativeLibrary
|
|||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.databinding.DialogSoftwareKeyboardBinding
|
||||
import org.citra.citra_emu.databinding.FragmentSystemFilesBinding
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.features.settings.SettingKeys
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.model.Game
|
||||
import org.citra.citra_emu.utils.SystemSaveGame
|
||||
import org.citra.citra_emu.viewmodel.GamesViewModel
|
||||
|
|
@ -163,10 +163,12 @@ class SystemFilesFragment : Fragment() {
|
|||
binding.buttonUnlinkConsoleData.setOnClickListener {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.delete_system_files)
|
||||
.setMessage(HtmlCompat.fromHtml(
|
||||
requireContext().getString(R.string.delete_system_files_description),
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
))
|
||||
.setMessage(
|
||||
HtmlCompat.fromHtml(
|
||||
requireContext().getString(R.string.delete_system_files_description),
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
|
||||
NativeLibrary.unlinkConsole()
|
||||
binding.buttonUnlinkConsoleData.isEnabled = NativeLibrary.isFullConsoleLinked()
|
||||
|
|
@ -178,7 +180,10 @@ class SystemFilesFragment : Fragment() {
|
|||
binding.buttonSetUpSystemFiles.setOnClickListener {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val inputBinding = DialogSoftwareKeyboardBinding.inflate(inflater)
|
||||
var textInputValue: String = preferences.getString(SettingKeys.last_artic_base_addr(), "")!!
|
||||
var textInputValue: String = preferences.getString(
|
||||
SettingKeys.last_artic_base_addr(),
|
||||
""
|
||||
)!!
|
||||
|
||||
val progressDialog = showProgressDialog(
|
||||
getText(R.string.setup_system_files),
|
||||
|
|
@ -273,9 +278,14 @@ class SystemFilesFragment : Fragment() {
|
|||
.setView(inputBinding.root)
|
||||
.setTitle(getString(R.string.setup_system_files_enter_address))
|
||||
.setPositiveButton(android.R.string.ok) { diag, _ ->
|
||||
if (textInputValue.isNotEmpty() && !(!buttonO3ds.isChecked && !buttonN3ds.isChecked)) {
|
||||
if (textInputValue.isNotEmpty() &&
|
||||
!(!buttonO3ds.isChecked && !buttonN3ds.isChecked)
|
||||
) {
|
||||
preferences.edit()
|
||||
.putString(SettingKeys.last_artic_base_addr(), textInputValue)
|
||||
.putString(
|
||||
SettingKeys.last_artic_base_addr(),
|
||||
textInputValue
|
||||
)
|
||||
.apply()
|
||||
val menu = Game(
|
||||
title = getString(R.string.artic_base),
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ class UpdateUserDirectoryDialogFragment : DialogFragment() {
|
|||
isCancelable = false
|
||||
val preferences: SharedPreferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
|
||||
val ld = preferences.getString("LIME3DS_DIRECTORY","")
|
||||
val cd = preferences.getString("CITRA_DIRECTORY","")
|
||||
val ld = preferences.getString("LIME3DS_DIRECTORY", "")
|
||||
val cd = preferences.getString("CITRA_DIRECTORY", "")
|
||||
val dialogView = LayoutInflater.from(requireContext())
|
||||
.inflate(R.layout.dialog_select_which_directory, null)
|
||||
|
||||
|
|
@ -97,7 +97,12 @@ class UpdateUserDirectoryDialogFragment : DialogFragment() {
|
|||
}
|
||||
|
||||
ViewModelProvider(mainActivity)[HomeViewModel::class.java].setPickingUserDir(false)
|
||||
ViewModelProvider(mainActivity)[HomeViewModel::class.java].setUserDir(this.requireActivity(),PermissionsHandler.citraDirectory.path!!)
|
||||
ViewModelProvider(
|
||||
mainActivity
|
||||
)[HomeViewModel::class.java].setUserDir(
|
||||
this.requireActivity(),
|
||||
PermissionsHandler.citraDirectory.path!!
|
||||
)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
package org.citra.citra_emu.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import androidx.core.net.toUri
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
|
@ -36,7 +36,7 @@ class Game(
|
|||
val icon: IntArray? = null,
|
||||
val fileType: String = "",
|
||||
val isCompressed: Boolean = false,
|
||||
val filename: String,
|
||||
val filename: String
|
||||
) : Parcelable {
|
||||
val keyAddedToLibraryTime get() = "${filename}_AddedToLibraryTime"
|
||||
val keyLastPlayedTime get() = "${filename}_LastPlayed"
|
||||
|
|
@ -51,7 +51,9 @@ class Game(
|
|||
val nativePath = NativeLibrary.getUserDirectory() + "/" + path
|
||||
val nativeFile = File(nativePath)
|
||||
if (!nativeFile.exists()) {
|
||||
throw IOException("Attempting to create shortcut for an executable that doesn't exist: $nativePath")
|
||||
throw IOException(
|
||||
"Attempting to create shortcut for an executable that doesn't exist: $nativePath"
|
||||
)
|
||||
}
|
||||
appUri = Uri.fromFile(nativeFile)
|
||||
}
|
||||
|
|
@ -89,9 +91,7 @@ class Game(
|
|||
GAME_CARD(2);
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int): MediaType? {
|
||||
return MediaType.entries.find { it.value == value }
|
||||
}
|
||||
fun fromInt(value: Int): MediaType? = MediaType.entries.find { it.value == value }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ data class SetupPage(
|
|||
val leftAlignedIcon: Boolean,
|
||||
val buttonTextId: Int,
|
||||
val pageButtons: List<PageButton>? = null,
|
||||
val pageSteps: () -> PageState = { PageState.PAGE_STEPS_UNDEFINED },
|
||||
val pageSteps: () -> PageState = { PageState.PAGE_STEPS_UNDEFINED }
|
||||
)
|
||||
|
||||
data class PageButton(
|
||||
|
|
@ -29,7 +29,7 @@ data class PageButton(
|
|||
)
|
||||
|
||||
interface SetupCallback {
|
||||
fun onStepCompleted(pageButtonId : Int, pageFullyCompleted: Boolean)
|
||||
fun onStepCompleted(pageButtonId: Int, pageFullyCompleted: Boolean)
|
||||
}
|
||||
|
||||
enum class PageState {
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ import android.view.View
|
|||
import android.view.View.OnTouchListener
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import java.lang.NullPointerException
|
||||
import kotlin.math.min
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.utils.EmulationMenuSettings
|
||||
import org.citra.citra_emu.utils.TurboHelper
|
||||
import java.lang.NullPointerException
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Draws the interactive input overlay on top of the
|
||||
|
|
@ -36,7 +36,8 @@ import kotlin.math.min
|
|||
* @param context The current [Context].
|
||||
* @param attrs [AttributeSet] for parsing XML attributes.
|
||||
*/
|
||||
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()
|
||||
|
|
@ -87,9 +88,10 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
)
|
||||
}
|
||||
|
||||
fun hapticFeedback(type:Int){
|
||||
if(EmulationMenuSettings.hapticFeedback)
|
||||
fun hapticFeedback(type: Int) {
|
||||
if (EmulationMenuSettings.hapticFeedback) {
|
||||
performHapticFeedback(type)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
|
|
@ -138,7 +140,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
}
|
||||
|
||||
var hasActiveJoystick = false
|
||||
if(!hasActiveButtons && !hasActiveDpad){
|
||||
if (!hasActiveButtons && !hasActiveDpad) {
|
||||
for (joystick in overlayJoysticks) {
|
||||
if (joystick.trackId == pointerId) {
|
||||
hasActiveJoystick = true
|
||||
|
|
@ -161,18 +163,26 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
|
||||
var anyOverlayStateChanged = false
|
||||
var shouldUpdateView = false
|
||||
if(!hasActiveDpad && !hasActiveJoystick) {
|
||||
if (!hasActiveDpad && !hasActiveJoystick) {
|
||||
for (button in overlayButtons) {
|
||||
val stateChanged = button.updateStatus(event, pointerIndex, hasActiveButtons, this)
|
||||
val stateChanged = button.updateStatus(
|
||||
event,
|
||||
pointerIndex,
|
||||
hasActiveButtons,
|
||||
this
|
||||
)
|
||||
if (!stateChanged) {
|
||||
continue
|
||||
}
|
||||
anyOverlayStateChanged = true
|
||||
|
||||
if (button.id == NativeLibrary.ButtonType.BUTTON_SWAP && button.status == NativeLibrary.ButtonState.PRESSED) {
|
||||
if (button.id == NativeLibrary.ButtonType.BUTTON_SWAP &&
|
||||
button.status == NativeLibrary.ButtonState.PRESSED
|
||||
) {
|
||||
swapScreen()
|
||||
}
|
||||
else if (button.id == NativeLibrary.ButtonType.BUTTON_TURBO && button.status == NativeLibrary.ButtonState.PRESSED) {
|
||||
} else if (button.id == NativeLibrary.ButtonType.BUTTON_TURBO &&
|
||||
button.status == NativeLibrary.ButtonState.PRESSED
|
||||
) {
|
||||
TurboHelper.toggleTurbo(true)
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +196,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
}
|
||||
}
|
||||
|
||||
if(!hasActiveButtons && !hasActiveJoystick) {
|
||||
if (!hasActiveButtons && !hasActiveJoystick) {
|
||||
for (dpad in overlayDpads) {
|
||||
val stateChanged = dpad.updateStatus(
|
||||
event,
|
||||
|
|
@ -225,9 +235,14 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
}
|
||||
}
|
||||
|
||||
if(!hasActiveDpad && !hasActiveButtons) {
|
||||
if (!hasActiveDpad && !hasActiveButtons) {
|
||||
for (joystick in overlayJoysticks) {
|
||||
val stateChanged = joystick.updateStatus(event, pointerIndex, hasActiveJoystick, this)
|
||||
val stateChanged = joystick.updateStatus(
|
||||
event,
|
||||
pointerIndex,
|
||||
hasActiveJoystick,
|
||||
this
|
||||
)
|
||||
if (!stateChanged) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -281,7 +296,6 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
if (!isActionMove) {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -291,7 +305,13 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
val fingerPositionX = event.getX(pointerIndex).toInt()
|
||||
val fingerPositionY = event.getY(pointerIndex).toInt()
|
||||
val orientation =
|
||||
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else ""
|
||||
if (resources.configuration.orientation ==
|
||||
Configuration.ORIENTATION_PORTRAIT
|
||||
) {
|
||||
"-Portrait"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
// Maybe combine Button and Joystick as subclasses of the same parent?
|
||||
// Or maybe create an interface like IMoveableHUDControl?
|
||||
|
|
@ -313,12 +333,15 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
return true
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured == it) {
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured ==
|
||||
it
|
||||
) {
|
||||
// Persist button position by saving new place.
|
||||
saveControlPosition(
|
||||
buttonBeingConfigured!!.id,
|
||||
buttonBeingConfigured!!.bounds.left,
|
||||
buttonBeingConfigured!!.bounds.top, orientation
|
||||
buttonBeingConfigured!!.bounds.top,
|
||||
orientation
|
||||
)
|
||||
buttonBeingConfigured = null
|
||||
}
|
||||
|
|
@ -347,7 +370,8 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
// Persist button position by saving new place.
|
||||
saveControlPosition(
|
||||
dpadBeingConfigured!!.upId,
|
||||
dpadBeingConfigured!!.bounds.left, dpadBeingConfigured!!.bounds.top,
|
||||
dpadBeingConfigured!!.bounds.left,
|
||||
dpadBeingConfigured!!.bounds.top,
|
||||
orientation
|
||||
)
|
||||
dpadBeingConfigured = null
|
||||
|
|
@ -374,7 +398,8 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
saveControlPosition(
|
||||
joystickBeingConfigured!!.joystickId,
|
||||
joystickBeingConfigured!!.bounds.left,
|
||||
joystickBeingConfigured!!.bounds.top, orientation
|
||||
joystickBeingConfigured!!.bounds.top,
|
||||
orientation
|
||||
)
|
||||
joystickBeingConfigured = null
|
||||
}
|
||||
|
|
@ -935,9 +960,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
.apply()
|
||||
}
|
||||
|
||||
override fun isInEditMode(): Boolean {
|
||||
return isInEditMode
|
||||
}
|
||||
override fun isInEditMode(): Boolean = isInEditMode
|
||||
|
||||
companion object {
|
||||
private val preferences
|
||||
|
|
@ -1044,6 +1067,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
NativeLibrary.ButtonType.BUTTON_START,
|
||||
NativeLibrary.ButtonType.BUTTON_SELECT,
|
||||
NativeLibrary.ButtonType.BUTTON_SWAP -> 0.08f
|
||||
|
||||
NativeLibrary.ButtonType.BUTTON_TURBO -> 0.10f
|
||||
|
||||
NativeLibrary.ButtonType.TRIGGER_L,
|
||||
|
|
@ -1056,17 +1080,22 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
scale *= (preferences.getInt("controlScale", 50) + 50).toFloat()
|
||||
scale /= 100f
|
||||
|
||||
|
||||
scale *= (preferences.getInt("controlScale-$buttonId", 50) + 50).toFloat()
|
||||
scale /= 100f
|
||||
|
||||
|
||||
val opacity: Int = preferences.getInt("controlOpacity", 50) * 255 / 100
|
||||
|
||||
// Initialize the InputOverlayDrawableButton.
|
||||
val defaultStateBitmap = getBitmap(context, defaultResId, scale)
|
||||
val pressedStateBitmap = getBitmap(context, pressedResId, scale)
|
||||
val overlayDrawable =
|
||||
InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId, opacity)
|
||||
InputOverlayDrawableButton(
|
||||
res,
|
||||
defaultStateBitmap,
|
||||
pressedStateBitmap,
|
||||
buttonId,
|
||||
opacity
|
||||
)
|
||||
|
||||
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
|
||||
// These were set in the input overlay configuration menu.
|
||||
|
|
@ -1118,19 +1147,22 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
|||
scale *= (preferences.getInt("controlScale", 50) + 50).toFloat()
|
||||
scale /= 100f
|
||||
|
||||
scale *= (preferences.getInt(
|
||||
"controlScale-" + NativeLibrary.ButtonType.DPAD,
|
||||
50
|
||||
) + 50).toFloat()
|
||||
|
||||
scale *= (
|
||||
preferences.getInt(
|
||||
"controlScale-" + NativeLibrary.ButtonType.DPAD,
|
||||
50
|
||||
) + 50
|
||||
).toFloat()
|
||||
|
||||
scale /= 100f
|
||||
|
||||
|
||||
val opacity: Int = preferences.getInt("controlOpacity", 50) * 255 / 100
|
||||
|
||||
// Initialize the InputOverlayDrawableDpad.
|
||||
val defaultStateBitmap = getBitmap(context, defaultResId, scale)
|
||||
val pressedOneDirectionStateBitmap = getBitmap(context, pressedOneDirectionResId, scale)
|
||||
val pressedTwoDirectionsStateBitmap = getBitmap(context, pressedTwoDirectionsResId, scale)
|
||||
val pressedTwoDirectionsStateBitmap =
|
||||
getBitmap(context, pressedTwoDirectionsResId, scale)
|
||||
val overlayDrawable = InputOverlayDrawableDpad(
|
||||
res,
|
||||
defaultStateBitmap,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,12 @@ class InputOverlayDrawableButton(
|
|||
*
|
||||
* @return true if value was changed
|
||||
*/
|
||||
fun updateStatus(event: MotionEvent, pointerIndex: Int, hasActiveButtons: Boolean, overlay: InputOverlay): Boolean {
|
||||
fun updateStatus(
|
||||
event: MotionEvent,
|
||||
pointerIndex: Int,
|
||||
hasActiveButtons: Boolean,
|
||||
overlay: InputOverlay
|
||||
): Boolean {
|
||||
val buttonSliding = EmulationMenuSettings.buttonSlide
|
||||
val xPosition = event.getX(pointerIndex).toInt()
|
||||
val yPosition = event.getY(pointerIndex).toInt()
|
||||
|
|
|
|||
|
|
@ -63,7 +63,13 @@ class InputOverlayDrawableDpad(
|
|||
trackId = -1
|
||||
}
|
||||
|
||||
fun updateStatus(event: MotionEvent, pointerIndex: Int, hasActiveButtons: Boolean, dpadSlide: Boolean, overlay: InputOverlay): Boolean {
|
||||
fun updateStatus(
|
||||
event: MotionEvent,
|
||||
pointerIndex: Int,
|
||||
hasActiveButtons: Boolean,
|
||||
dpadSlide: Boolean,
|
||||
overlay: InputOverlay
|
||||
): Boolean {
|
||||
var isDown = false
|
||||
val xPosition = event.getX(pointerIndex).toInt()
|
||||
val yPosition = event.getY(pointerIndex).toInt()
|
||||
|
|
@ -125,12 +131,16 @@ class InputOverlayDrawableDpad(
|
|||
leftButtonState = xAxis < -VIRT_AXIS_DEADZONE
|
||||
rightButtonState = xAxis > VIRT_AXIS_DEADZONE
|
||||
|
||||
val stateChanged = upState != upButtonState || downState != downButtonState || leftState != leftButtonState || rightState != rightButtonState
|
||||
val stateChanged =
|
||||
upState != upButtonState || downState != downButtonState ||
|
||||
leftState != leftButtonState ||
|
||||
rightState != rightButtonState
|
||||
|
||||
if(stateChanged)
|
||||
if (stateChanged) {
|
||||
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
|
||||
else if(isDown)
|
||||
} else if (isDown) {
|
||||
overlay.hapticFeedback(HapticFeedbackConstants.CLOCK_TICK)
|
||||
}
|
||||
|
||||
return stateChanged
|
||||
}
|
||||
|
|
@ -259,7 +269,9 @@ class InputOverlayDrawableDpad(
|
|||
controlPositionX += fingerPositionX - previousTouchX
|
||||
controlPositionY += fingerPositionY - previousTouchY
|
||||
setBounds(
|
||||
controlPositionX, controlPositionY, width + controlPositionX,
|
||||
controlPositionX,
|
||||
controlPositionY,
|
||||
width + controlPositionX,
|
||||
height + controlPositionY
|
||||
)
|
||||
previousTouchX = fingerPositionX
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ import android.graphics.Rect
|
|||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.MotionEvent
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.utils.EmulationMenuSettings
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.utils.EmulationMenuSettings
|
||||
|
||||
/**
|
||||
* Custom [BitmapDrawable] that is capable
|
||||
|
|
@ -93,7 +93,12 @@ class InputOverlayDrawableJoystick(
|
|||
currentStateBitmapDrawable.draw(canvas)
|
||||
}
|
||||
|
||||
fun updateStatus(event: MotionEvent, pointerIndex: Int, hasActiveButtons: Boolean, overlay: InputOverlay): Boolean {
|
||||
fun updateStatus(
|
||||
event: MotionEvent,
|
||||
pointerIndex: Int,
|
||||
hasActiveButtons: Boolean,
|
||||
overlay: InputOverlay
|
||||
): Boolean {
|
||||
val xPosition = event.getX(pointerIndex).toInt()
|
||||
val yPosition = event.getY(pointerIndex).toInt()
|
||||
val pointerId = event.getPointerId(pointerIndex)
|
||||
|
|
@ -171,8 +176,9 @@ class InputOverlayDrawableJoystick(
|
|||
this.yAxis = sin(angle.toDouble()).toFloat() * radius
|
||||
setInnerBounds()
|
||||
|
||||
if (kotlin.math.abs(oldRadius - radius) > .34f
|
||||
|| radius > .5f && kotlin.math.abs(oldAngle - angle) > kotlin.math.PI / 8) {
|
||||
if (kotlin.math.abs(oldRadius - radius) > .34f ||
|
||||
radius > .5f && kotlin.math.abs(oldAngle - angle) > kotlin.math.PI / 8
|
||||
) {
|
||||
this.radius = radius
|
||||
this.angle = angle
|
||||
|
||||
|
|
@ -237,14 +243,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(x - width, y - height, x + width, y + height)
|
||||
|
|
|
|||
|
|
@ -61,13 +61,15 @@ import org.citra.citra_emu.utils.CitraDirectoryUtils
|
|||
import org.citra.citra_emu.utils.DirectoryInitialization
|
||||
import org.citra.citra_emu.utils.FileBrowserHelper
|
||||
import org.citra.citra_emu.utils.InsetsHelper
|
||||
import org.citra.citra_emu.utils.RefreshRateUtil
|
||||
import org.citra.citra_emu.utils.PermissionsHandler
|
||||
import org.citra.citra_emu.utils.RefreshRateUtil
|
||||
import org.citra.citra_emu.utils.ThemeUtil
|
||||
import org.citra.citra_emu.viewmodel.GamesViewModel
|
||||
import org.citra.citra_emu.viewmodel.HomeViewModel
|
||||
|
||||
class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
class MainActivity :
|
||||
AppCompatActivity(),
|
||||
ThemeProvider {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
private val homeViewModel: HomeViewModel by viewModels()
|
||||
|
|
@ -87,14 +89,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
CitraDirectoryUtils.attemptAutomaticUpdateDirectory()
|
||||
splashScreen.setKeepOnScreenCondition {
|
||||
!DirectoryInitialization.areCitraDirectoriesReady() &&
|
||||
PermissionsHandler.hasWriteAccess(this) &&
|
||||
!CitraDirectoryUtils.needToUpdateManually()
|
||||
PermissionsHandler.hasWriteAccess(this) &&
|
||||
!CitraDirectoryUtils.needToUpdateManually()
|
||||
}
|
||||
|
||||
|
||||
if (PermissionsHandler.hasWriteAccess(applicationContext) &&
|
||||
DirectoryInitialization.areCitraDirectoriesReady() &&
|
||||
!CitraDirectoryUtils.needToUpdateManually()) {
|
||||
!CitraDirectoryUtils.needToUpdateManually()
|
||||
) {
|
||||
settingsViewModel.settings.loadSettings()
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +154,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
|
||||
gamesViewModel.setShouldScrollToTop(true)
|
||||
}
|
||||
|
||||
R.id.searchFragment -> gamesViewModel.setSearchFocused(true)
|
||||
|
||||
R.id.homeSettingsFragment -> SettingsActivity.launch(
|
||||
this,
|
||||
SettingsFile.FILE_NAME_CONFIG,
|
||||
|
|
@ -229,7 +233,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
GrantMissingFilesystemPermissionFragment.newInstance()
|
||||
.show(supportFragmentManager, GrantMissingFilesystemPermissionFragment.TAG)
|
||||
|
||||
if (supportFragmentManager.findFragmentByTag(GrantMissingFilesystemPermissionFragment.TAG) == null) {
|
||||
if (supportFragmentManager.findFragmentByTag(
|
||||
GrantMissingFilesystemPermissionFragment.TAG
|
||||
) ==
|
||||
null
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
requestMissingFilesystemPermission()
|
||||
|
|
@ -256,12 +264,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
return
|
||||
} else if (CitraDirectoryUtils.needToUpdateManually()) {
|
||||
UpdateUserDirectoryDialogFragment.newInstance(this)
|
||||
.show(supportFragmentManager,UpdateUserDirectoryDialogFragment.TAG)
|
||||
.show(supportFragmentManager, UpdateUserDirectoryDialogFragment.TAG)
|
||||
return
|
||||
}
|
||||
|
||||
if (!BuildUtil.isGooglePlayBuild) {
|
||||
if (supportFragmentManager.findFragmentByTag(SelectUserDirectoryDialogFragment.TAG) == null) {
|
||||
if (supportFragmentManager.findFragmentByTag(SelectUserDirectoryDialogFragment.TAG) ==
|
||||
null
|
||||
) {
|
||||
if (NativeLibrary.getUserDirectory() == "") {
|
||||
SelectUserDirectoryDialogFragment.newInstance(this)
|
||||
.show(supportFragmentManager, SelectUserDirectoryDialogFragment.TAG)
|
||||
|
|
@ -365,28 +375,29 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
}.start()
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
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
|
||||
binding.statusBarShade.layoutParams = mlpStatusShade
|
||||
private fun setInsets() = 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
|
||||
binding.statusBarShade.layoutParams = mlpStatusShade
|
||||
|
||||
// The only situation where we care to have a nav bar shade is when it's at the bottom
|
||||
// of the screen where scrolling list elements can go behind it.
|
||||
val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams
|
||||
mlpNavShade.height = insets.bottom
|
||||
binding.navigationBarShade.layoutParams = mlpNavShade
|
||||
// The only situation where we care to have a nav bar shade is when it's at the bottom
|
||||
// of the screen where scrolling list elements can go behind it.
|
||||
val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams
|
||||
mlpNavShade.height = insets.bottom
|
||||
binding.navigationBarShade.layoutParams = mlpNavShade
|
||||
|
||||
windowInsets
|
||||
}
|
||||
windowInsets
|
||||
}
|
||||
|
||||
private fun createOpenCitraDirectoryLauncher(
|
||||
permissionsLost: Boolean
|
||||
): ActivityResultLauncher<Uri?> {
|
||||
return registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result: Uri? ->
|
||||
return registerForActivityResult(
|
||||
ActivityResultContracts.OpenDocumentTree()
|
||||
) { result: Uri? ->
|
||||
if (result == null) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
|
@ -427,7 +438,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
|
||||
val workManager = WorkManager.getInstance(applicationContext)
|
||||
workManager.enqueueUniqueWork(
|
||||
"installCiaWork", ExistingWorkPolicy.APPEND_OR_REPLACE,
|
||||
"installCiaWork",
|
||||
ExistingWorkPolicy.APPEND_OR_REPLACE,
|
||||
OneTimeWorkRequest.Builder(CiaInstallWorker::class.java)
|
||||
.setInputData(
|
||||
Data.Builder().putStringArray("CIA_FILES", selectedFiles)
|
||||
|
|
@ -439,7 +451,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
}
|
||||
|
||||
val setupOpenCitraDirectory = registerForActivityResult(
|
||||
ActivityResultContracts.OpenDocumentTree(),
|
||||
ActivityResultContracts.OpenDocumentTree()
|
||||
) { result: Uri? ->
|
||||
homeViewModel.selectedCitraDirectory = result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
|
|
@ -16,12 +17,8 @@ import org.citra.citra_emu.NativeLibrary
|
|||
import org.citra.citra_emu.NativeLibrary.InstallStatus
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.utils.FileUtil.getFilename
|
||||
import androidx.core.net.toUri
|
||||
|
||||
class CiaInstallWorker(
|
||||
val context: Context,
|
||||
params: WorkerParameters
|
||||
) : Worker(context, params) {
|
||||
class CiaInstallWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
private val GROUP_KEY_CIA_INSTALL_STATUS = "org.citra.citra_emu.CIA_INSTALL_STATUS"
|
||||
private var lastNotifiedTime: Long = 0
|
||||
private val SUMMARY_NOTIFICATION_ID = 0xC1A0000
|
||||
|
|
@ -123,7 +120,8 @@ class CiaInstallWorker(
|
|||
val selectedFiles = inputData.getStringArray("CIA_FILES")!!
|
||||
val toastText: CharSequence = context.resources.getQuantityString(
|
||||
R.plurals.cia_install_toast,
|
||||
selectedFiles.size, selectedFiles.size
|
||||
selectedFiles.size,
|
||||
selectedFiles.size
|
||||
)
|
||||
context.mainExecutor.execute {
|
||||
Toast.makeText(context, toastText, Toast.LENGTH_LONG).show()
|
||||
|
|
|
|||
|
|
@ -16,8 +16,15 @@ import org.citra.citra_emu.viewmodel.HomeViewModel
|
|||
/**
|
||||
* Citra directory initialization ui flow controller.
|
||||
*/
|
||||
class CitraDirectoryHelper(private val fragmentActivity: FragmentActivity, private val lostPermission: Boolean) {
|
||||
fun showCitraDirectoryDialog(result: Uri, callback: SetupCallback? = null, buttonState: () -> Unit) {
|
||||
class CitraDirectoryHelper(
|
||||
private val fragmentActivity: FragmentActivity,
|
||||
private val lostPermission: Boolean
|
||||
) {
|
||||
fun showCitraDirectoryDialog(
|
||||
result: Uri,
|
||||
callback: SetupCallback? = null,
|
||||
buttonState: () -> Unit
|
||||
) {
|
||||
val citraDirectoryDialog = CitraDirectoryDialogFragment.newInstance(
|
||||
fragmentActivity,
|
||||
result.toString(),
|
||||
|
|
@ -29,7 +36,7 @@ class CitraDirectoryHelper(private val fragmentActivity: FragmentActivity, priva
|
|||
}
|
||||
|
||||
val takeFlags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
fragmentActivity.contentResolver.takePersistableUriPermission(
|
||||
path,
|
||||
takeFlags
|
||||
|
|
@ -46,7 +53,8 @@ class CitraDirectoryHelper(private val fragmentActivity: FragmentActivity, priva
|
|||
// If user check move data, show copy progress dialog.
|
||||
CopyDirProgressDialog.newInstance(fragmentActivity, previous, path, callback)
|
||||
?.show(fragmentActivity.supportFragmentManager, CopyDirProgressDialog.TAG)
|
||||
})
|
||||
}
|
||||
)
|
||||
citraDirectoryDialog.show(
|
||||
fragmentActivity.supportFragmentManager,
|
||||
CitraDirectoryDialogFragment.TAG
|
||||
|
|
|
|||
|
|
@ -16,24 +16,26 @@ object CitraDirectoryUtils {
|
|||
|
||||
fun needToUpdateManually(): Boolean {
|
||||
val directoryString = preferences.getString(CITRA_DIRECTORY, "")
|
||||
val limeDirectoryString = preferences.getString(LIME3DS_DIRECTORY,"")
|
||||
return (directoryString != "" && limeDirectoryString != "" && directoryString != limeDirectoryString)
|
||||
val limeDirectoryString = preferences.getString(LIME3DS_DIRECTORY, "")
|
||||
return (
|
||||
directoryString != "" && limeDirectoryString != "" &&
|
||||
directoryString != limeDirectoryString
|
||||
)
|
||||
}
|
||||
|
||||
fun attemptAutomaticUpdateDirectory() {
|
||||
val directoryString = preferences.getString(CITRA_DIRECTORY, "")
|
||||
val limeDirectoryString = preferences.getString(LIME3DS_DIRECTORY,"")
|
||||
val limeDirectoryString = preferences.getString(LIME3DS_DIRECTORY, "")
|
||||
if (needToUpdateManually()) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (directoryString == "" && limeDirectoryString != "") {
|
||||
if (directoryString == "" && limeDirectoryString != "") {
|
||||
// Upgrade from Lime3DS to Azahar
|
||||
PermissionsHandler.setCitraDirectory(limeDirectoryString)
|
||||
PermissionsHandler.setCitraDirectory(limeDirectoryString)
|
||||
removeLimeDirectoryPreference()
|
||||
DirectoryInitialization.resetCitraDirectoryState()
|
||||
DirectoryInitialization.start()
|
||||
|
||||
} else if (directoryString != "" && directoryString == limeDirectoryString) {
|
||||
} else if (directoryString != "" && directoryString == limeDirectoryString) {
|
||||
// Both the Lime3DS and Azahar directories are the same,
|
||||
// so delete the obsolete Lime3DS value.
|
||||
removeLimeDirectoryPreference()
|
||||
|
|
|
|||
|
|
@ -15,13 +15,14 @@ object ControllerMappingHelper {
|
|||
/**
|
||||
* Some controllers report extra button presses that can be ignored.
|
||||
*/
|
||||
fun shouldKeyBeIgnored(inputDevice: InputDevice, keyCode: Int): Boolean {
|
||||
return if (isDualShock4(inputDevice)) {
|
||||
fun shouldKeyBeIgnored(inputDevice: InputDevice, keyCode: Int): Boolean =
|
||||
if (isDualShock4(inputDevice)) {
|
||||
// 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale an axis to be zero-centered with a proper range.
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@ package org.citra.citra_emu.utils
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.citra.citra_emu.BuildConfig
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.utils.PermissionsHandler.hasWriteAccess
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import org.citra.citra_emu.BuildConfig
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.utils.PermissionsHandler.hasWriteAccess
|
||||
|
||||
/**
|
||||
* A service that spawns its own thread in order to copy several binary and shader files
|
||||
|
|
@ -70,9 +70,8 @@ object DirectoryInitialization {
|
|||
}
|
||||
|
||||
@JvmStatic
|
||||
fun areCitraDirectoriesReady(): Boolean {
|
||||
return directoryState == DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED
|
||||
}
|
||||
fun areCitraDirectoriesReady(): Boolean =
|
||||
directoryState == DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED
|
||||
|
||||
fun resetCitraDirectoryState() {
|
||||
directoryState = null
|
||||
|
|
@ -130,18 +129,22 @@ object DirectoryInitialization {
|
|||
createdFolder = true
|
||||
}
|
||||
copyAssetFolder(
|
||||
assetFolder + File.separator + file, File(outputFolder, file),
|
||||
overwrite, context
|
||||
assetFolder + File.separator + file,
|
||||
File(outputFolder, file),
|
||||
overwrite,
|
||||
context
|
||||
)
|
||||
copyAsset(
|
||||
assetFolder + File.separator + file, File(outputFolder, file), overwrite,
|
||||
assetFolder + File.separator + file,
|
||||
File(outputFolder, file),
|
||||
overwrite,
|
||||
context
|
||||
)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.error(
|
||||
"[DirectoryInitialization] Failed to copy asset folder: $assetFolder" +
|
||||
e.message
|
||||
e.message
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ object DiskShaderCacheProgress {
|
|||
emulationActivity.runOnUiThread {
|
||||
when (stage) {
|
||||
LoadCallbackStage.Prepare -> prepareViewModel()
|
||||
|
||||
LoadCallbackStage.Decompile -> emulationViewModel.updateProgress(
|
||||
emulationActivity.getString(R.string.preparing_shaders),
|
||||
progress,
|
||||
|
|
@ -40,7 +41,7 @@ object DiskShaderCacheProgress {
|
|||
)
|
||||
|
||||
LoadCallbackStage.Build -> emulationViewModel.updateProgress(
|
||||
emulationActivity.getString(R.string.building_shaders, obj ),
|
||||
emulationActivity.getString(R.string.building_shaders, obj),
|
||||
progress,
|
||||
max
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import android.net.Uri
|
|||
import android.provider.DocumentsContract
|
||||
import androidx.core.net.toUri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.model.CheapDocument
|
||||
import org.citra.citra_emu.utils.BuildUtil
|
||||
import java.io.IOException
|
||||
import java.net.URLDecoder
|
||||
import java.nio.file.Paths
|
||||
import java.util.StringTokenizer
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.model.CheapDocument
|
||||
import org.citra.citra_emu.utils.BuildUtil
|
||||
|
||||
/**
|
||||
* A cached document tree for Citra user directory.
|
||||
|
|
@ -127,7 +127,8 @@ class DocumentsTree {
|
|||
// Create directory if it doesn't exist and creation is enabled
|
||||
if (child == null && createIfNotExists) {
|
||||
try {
|
||||
val createdDir = FileUtil.createDir(current.uri.toString(), component) ?: return null
|
||||
val createdDir =
|
||||
FileUtil.createDir(current.uri.toString(), component) ?: return null
|
||||
child = DocumentsNode(createdDir, true).apply {
|
||||
parent = current
|
||||
}
|
||||
|
|
@ -152,9 +153,7 @@ class DocumentsTree {
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
fun exists(filepath: String): Boolean {
|
||||
return resolvePath(filepath) != null
|
||||
}
|
||||
fun exists(filepath: String): Boolean = resolvePath(filepath) != null
|
||||
|
||||
@Synchronized
|
||||
fun copyFile(
|
||||
|
|
@ -200,7 +199,11 @@ class DocumentsTree {
|
|||
val node = resolvePath(filepath) ?: return false
|
||||
try {
|
||||
val filename = URLDecoder.decode(destinationFilename, FileUtil.DECODE_METHOD)
|
||||
val newUri = DocumentsContract.renameDocument(context.contentResolver, node.uri!!, filename)
|
||||
val newUri = DocumentsContract.renameDocument(
|
||||
context.contentResolver,
|
||||
node.uri!!,
|
||||
filename
|
||||
)
|
||||
node.rename(filename, newUri)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
|
|
@ -214,7 +217,12 @@ class DocumentsTree {
|
|||
val sourceDirNode = resolvePath(sourceDirPath) ?: return false
|
||||
val destDirNode = resolvePath(destDirPath) ?: return false
|
||||
try {
|
||||
val newUri = DocumentsContract.moveDocument(context.contentResolver, sourceFileNode.uri!!, sourceDirNode.uri!!, destDirNode.uri!!)
|
||||
val newUri = DocumentsContract.moveDocument(
|
||||
context.contentResolver,
|
||||
sourceFileNode.uri!!,
|
||||
sourceDirNode.uri!!,
|
||||
destDirNode.uri!!
|
||||
)
|
||||
updateDocumentLocation("$sourceDirPath/$filename", "$destDirPath/$filename")
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
|
|
@ -359,8 +367,7 @@ class DocumentsTree {
|
|||
fun findChild(filename: String) = children[filename.lowercase()]
|
||||
|
||||
@Synchronized
|
||||
fun getChildNames(): Array<String?> =
|
||||
children.mapNotNull { it.value!!.name }.toTypedArray()
|
||||
fun getChildNames(): Array<String?> = children.mapNotNull { it.value!!.name }.toTypedArray()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
@ -368,7 +375,7 @@ class DocumentsTree {
|
|||
val kotlinDirectoryAccessWhitelist = arrayOf(
|
||||
"/config/",
|
||||
"/log/",
|
||||
"/gpu_drivers/",
|
||||
"/gpu_drivers/"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ object EmulationLifecycleUtil {
|
|||
private var shutdownHooks: MutableList<Runnable> = ArrayList()
|
||||
private var pauseResumeHooks: MutableList<Runnable> = ArrayList()
|
||||
|
||||
|
||||
fun closeGame() {
|
||||
shutdownHooks.forEach(Runnable::run)
|
||||
}
|
||||
|
|
@ -19,7 +18,9 @@ object EmulationLifecycleUtil {
|
|||
|
||||
fun addShutdownHook(hook: Runnable) {
|
||||
if (shutdownHooks.contains(hook)) {
|
||||
Log.warning("[EmulationLifecycleUtil] Tried to add shutdown hook for function that already existed. Skipping.")
|
||||
Log.warning(
|
||||
"[EmulationLifecycleUtil] Tried to add shutdown hook for function that already existed. Skipping."
|
||||
)
|
||||
} else {
|
||||
shutdownHooks.add(hook)
|
||||
}
|
||||
|
|
@ -27,7 +28,9 @@ object EmulationLifecycleUtil {
|
|||
|
||||
fun addPauseResumeHook(hook: Runnable) {
|
||||
if (pauseResumeHooks.contains(hook)) {
|
||||
Log.warning("[EmulationLifecycleUtil] Tried to add pause resume hook for function that already existed. Skipping.")
|
||||
Log.warning(
|
||||
"[EmulationLifecycleUtil] Tried to add pause resume hook for function that already existed. Skipping."
|
||||
)
|
||||
} else {
|
||||
pauseResumeHooks.add(hook)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,10 @@ object EmulationMenuSettings {
|
|||
.apply()
|
||||
}
|
||||
var buttonSlide: Int
|
||||
get() = preferences.getInt("EmulationMenuSettings_ButtonSlideMode", ButtonSlidingMode.Disabled.int)
|
||||
get() = preferences.getInt(
|
||||
"EmulationMenuSettings_ButtonSlideMode",
|
||||
ButtonSlidingMode.Disabled.int
|
||||
)
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putInt("EmulationMenuSettings_ButtonSlideMode", value)
|
||||
|
|
@ -39,8 +42,8 @@ object EmulationMenuSettings {
|
|||
get() = preferences.getBoolean("EmulationMenuSettings_HapticFeedback", true)
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putBoolean("EmulationMenuSettings_HapticFeedback", value)
|
||||
.apply()
|
||||
.putBoolean("EmulationMenuSettings_HapticFeedback", value)
|
||||
.apply()
|
||||
}
|
||||
var swapScreens: Boolean
|
||||
get() = preferences.getBoolean("EmulationMenuSettings_SwapScreens", false)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
package org.citra.citra_emu.utils
|
||||
|
||||
import okio.ByteString.Companion.readByteString
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
|
|
@ -13,8 +12,6 @@ import android.system.Os
|
|||
import android.util.Pair
|
||||
import androidx.core.net.toUri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.model.CheapDocument
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
|
@ -25,6 +22,9 @@ import java.net.URLDecoder
|
|||
import java.nio.charset.StandardCharsets
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import okio.ByteString.Companion.readByteString
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.model.CheapDocument
|
||||
|
||||
object FileUtil {
|
||||
const val PATH_TREE = "tree"
|
||||
|
|
@ -228,7 +228,7 @@ object FileUtil {
|
|||
|
||||
if (uri.scheme == "file") {
|
||||
BuildUtil.assertNotGooglePlay()
|
||||
val file = File(uri.path!!);
|
||||
val file = File(uri.path!!)
|
||||
return file.name
|
||||
}
|
||||
|
||||
|
|
@ -289,11 +289,7 @@ object FileUtil {
|
|||
}
|
||||
|
||||
@JvmStatic
|
||||
fun copyFile(
|
||||
sourceUri: Uri,
|
||||
destinationUri: Uri,
|
||||
destinationFilename: String
|
||||
): Boolean {
|
||||
fun copyFile(sourceUri: Uri, destinationUri: Uri, destinationFilename: String): Boolean {
|
||||
try {
|
||||
val destinationParent =
|
||||
DocumentFile.fromTreeUri(context, destinationUri) ?: return false
|
||||
|
|
@ -362,11 +358,7 @@ object FileUtil {
|
|||
return false
|
||||
}
|
||||
|
||||
fun copyDir(
|
||||
sourcePath: String,
|
||||
destinationPath: String,
|
||||
listener: CopyDirListener?
|
||||
) {
|
||||
fun copyDir(sourcePath: String, destinationPath: String, listener: CopyDirListener?) {
|
||||
try {
|
||||
val sourceUri = Uri.parse(sourcePath)
|
||||
val destinationUri = Uri.parse(destinationPath)
|
||||
|
|
@ -451,7 +443,12 @@ object FileUtil {
|
|||
val sourceFileUri = ("$sourceDirUriString%2F$filename").toUri()
|
||||
val sourceDirUri = sourceDirUriString.toUri()
|
||||
val destDirUri = destDirUriString.toUri()
|
||||
DocumentsContract.moveDocument(context.contentResolver, sourceFileUri, sourceDirUri, destDirUri)
|
||||
DocumentsContract.moveDocument(
|
||||
context.contentResolver,
|
||||
sourceFileUri,
|
||||
sourceDirUri,
|
||||
destDirUri
|
||||
)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Log.error("[FileUtil]: Cannot move file, error: " + e.message)
|
||||
|
|
@ -529,10 +526,7 @@ object FileUtil {
|
|||
}
|
||||
}
|
||||
|
||||
fun copyToExternalStorage(
|
||||
sourceFile: Uri,
|
||||
destinationDir: DocumentFile
|
||||
): DocumentFile? {
|
||||
fun copyToExternalStorage(sourceFile: Uri, destinationDir: DocumentFile): DocumentFile? {
|
||||
val filename = getFilename(sourceFile)
|
||||
val destinationFile = destinationDir.createFile("application/zip", filename)!!
|
||||
destinationFile.outputStream().use { os ->
|
||||
|
|
@ -555,21 +549,20 @@ object FileUtil {
|
|||
false
|
||||
}
|
||||
|
||||
fun getFreeSpace(context: Context, uri: Uri?): Double =
|
||||
try {
|
||||
val docTreeUri = DocumentsContract.buildDocumentUriUsingTree(
|
||||
uri,
|
||||
DocumentsContract.getTreeDocumentId(uri)
|
||||
)
|
||||
val pfd = context.contentResolver.openFileDescriptor(docTreeUri, "r")!!
|
||||
val stats = Os.fstatvfs(pfd.fileDescriptor)
|
||||
val spaceInGigaBytes = stats.f_bavail * stats.f_bsize / 1024.0 / 1024 / 1024
|
||||
pfd.close()
|
||||
spaceInGigaBytes
|
||||
} catch (e: Exception) {
|
||||
Log.error("[FileUtil] Cannot get storage size.")
|
||||
0.0
|
||||
}
|
||||
fun getFreeSpace(context: Context, uri: Uri?): Double = try {
|
||||
val docTreeUri = DocumentsContract.buildDocumentUriUsingTree(
|
||||
uri,
|
||||
DocumentsContract.getTreeDocumentId(uri)
|
||||
)
|
||||
val pfd = context.contentResolver.openFileDescriptor(docTreeUri, "r")!!
|
||||
val stats = Os.fstatvfs(pfd.fileDescriptor)
|
||||
val spaceInGigaBytes = stats.f_bavail * stats.f_bsize / 1024.0 / 1024 / 1024
|
||||
pfd.close()
|
||||
spaceInGigaBytes
|
||||
} catch (e: Exception) {
|
||||
Log.error("[FileUtil] Cannot get storage size.")
|
||||
0.0
|
||||
}
|
||||
|
||||
fun closeQuietly(closeable: AutoCloseable?) {
|
||||
if (closeable != null) {
|
||||
|
|
@ -589,8 +582,7 @@ object FileUtil {
|
|||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getStringFromFile(file: File): String =
|
||||
String(file.readBytes(), StandardCharsets.UTF_8)
|
||||
fun getStringFromFile(file: File): String = String(file.readBytes(), StandardCharsets.UTF_8)
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getStringFromInputStream(stream: InputStream, length: Long = 0L): String =
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package org.citra.citra_emu.utils
|
|||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import androidx.preference.PreferenceManager
|
||||
import java.io.IOException
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
|
|
@ -15,7 +16,6 @@ import org.citra.citra_emu.R
|
|||
import org.citra.citra_emu.model.CheapDocument
|
||||
import org.citra.citra_emu.model.Game
|
||||
import org.citra.citra_emu.model.GameInfo
|
||||
import java.io.IOException
|
||||
|
||||
object GameHelper {
|
||||
const val KEY_GAME_PATH = "game_path"
|
||||
|
|
@ -32,7 +32,9 @@ object GameHelper {
|
|||
|
||||
addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
|
||||
NativeLibrary.getInstalledGamePaths().forEach {
|
||||
games.add(getGame(Uri.parse(it.path), isInstalled = true, addedToLibrary = true, it.mediaType))
|
||||
games.add(
|
||||
getGame(Uri.parse(it.path), isInstalled = true, addedToLibrary = true, it.mediaType)
|
||||
)
|
||||
}
|
||||
|
||||
// Cache list of games found on disk
|
||||
|
|
@ -62,24 +64,38 @@ object GameHelper {
|
|||
addGamesRecursive(games, FileUtil.listFiles(it.uri), depth - 1)
|
||||
} else {
|
||||
if (Game.allExtensions.contains(FileUtil.getExtension(it.uri))) {
|
||||
games.add(getGame(it.uri, isInstalled = false, addedToLibrary = true, Game.MediaType.GAME_CARD))
|
||||
games.add(
|
||||
getGame(
|
||||
it.uri,
|
||||
isInstalled = false,
|
||||
addedToLibrary = true,
|
||||
Game.MediaType.GAME_CARD
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getGame(uri: Uri, isInstalled: Boolean, addedToLibrary: Boolean, mediaType: Game.MediaType): Game {
|
||||
fun getGame(
|
||||
uri: Uri,
|
||||
isInstalled: Boolean,
|
||||
addedToLibrary: Boolean,
|
||||
mediaType: Game.MediaType
|
||||
): Game {
|
||||
val filePath = uri.toString()
|
||||
var nativePath: String? = null
|
||||
var gameInfo: GameInfo?
|
||||
if (BuildUtil.isGooglePlayBuild || FileUtil.isNativePath(filePath) || filePath.startsWith("!")) {
|
||||
if (BuildUtil.isGooglePlayBuild || FileUtil.isNativePath(filePath) ||
|
||||
filePath.startsWith("!")
|
||||
) {
|
||||
gameInfo = GameInfo(filePath)
|
||||
} else {
|
||||
nativePath = if (uri.scheme == "fd") {
|
||||
uri.toString()
|
||||
} else {
|
||||
"!" + NativeLibrary.getNativePath(uri)
|
||||
};
|
||||
}
|
||||
gameInfo = GameInfo(nativePath)
|
||||
}
|
||||
|
||||
|
|
@ -92,10 +108,16 @@ object GameHelper {
|
|||
|
||||
val newGame = Game(
|
||||
valid,
|
||||
(gameInfo?.getTitle() ?: FileUtil.getFilename(uri)).replace("[\\t\\n\\r]+".toRegex(), " "),
|
||||
(
|
||||
gameInfo?.getTitle() ?: FileUtil.getFilename(
|
||||
uri
|
||||
)
|
||||
).replace("[\\t\\n\\r]+".toRegex(), " "),
|
||||
filePath.replace("\n", " "),
|
||||
// TODO: This next line can be deduplicated but I don't want to right now -OS
|
||||
if (BuildUtil.isGooglePlayBuild || FileUtil.isNativePath(filePath) || filePath.startsWith("!")) {
|
||||
if (BuildUtil.isGooglePlayBuild || FileUtil.isNativePath(filePath) ||
|
||||
filePath.startsWith("!")
|
||||
) {
|
||||
filePath
|
||||
} else {
|
||||
nativePath!!
|
||||
|
|
@ -103,7 +125,12 @@ object GameHelper {
|
|||
gameInfo?.getTitleID() ?: 0,
|
||||
mediaType,
|
||||
gameInfo?.getCompany() ?: "",
|
||||
if (isEncrypted) { CitraApplication.appContext.getString(R.string.unsupported_encrypted) } else { gameInfo?.getRegions() ?: "" },
|
||||
if (isEncrypted) {
|
||||
CitraApplication.appContext.getString(R.string.unsupported_encrypted)
|
||||
} else {
|
||||
gameInfo?.getRegions()
|
||||
?: ""
|
||||
},
|
||||
isInstalled,
|
||||
gameInfo?.isSystemTitle() ?: false,
|
||||
gameInfo?.getIsVisibleSystemTitle() ?: false,
|
||||
|
|
|
|||
|
|
@ -18,21 +18,16 @@ import coil.memory.MemoryCache
|
|||
import coil.request.ImageRequest
|
||||
import coil.request.Options
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import java.nio.IntBuffer
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.model.Game
|
||||
import java.nio.IntBuffer
|
||||
|
||||
class GameIconFetcher(
|
||||
private val game: Game,
|
||||
private val options: Options
|
||||
) : Fetcher {
|
||||
override suspend fun fetch(): FetchResult {
|
||||
return DrawableResult(
|
||||
drawable = getGameIcon(game.icon)!!.toDrawable(options.context.resources),
|
||||
isSampled = false,
|
||||
dataSource = DataSource.DISK
|
||||
)
|
||||
}
|
||||
class GameIconFetcher(private val game: Game, private val options: Options) : Fetcher {
|
||||
override suspend fun fetch(): FetchResult = DrawableResult(
|
||||
drawable = getGameIcon(game.icon)!!.toDrawable(options.context.resources),
|
||||
isSampled = false,
|
||||
dataSource = DataSource.DISK
|
||||
)
|
||||
|
||||
private fun getGameIcon(vector: IntArray?): Bitmap? {
|
||||
val bitmap = Bitmap.createBitmap(48, 48, Bitmap.Config.RGB_565)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@ package org.citra.citra_emu.utils
|
|||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.utils.FileUtil.asDocumentFile
|
||||
import org.citra.citra_emu.utils.FileUtil.inputStream
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
|
@ -19,6 +15,10 @@ import java.lang.IllegalStateException
|
|||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipException
|
||||
import java.util.zip.ZipInputStream
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
import org.citra.citra_emu.utils.FileUtil.asDocumentFile
|
||||
import org.citra.citra_emu.utils.FileUtil.inputStream
|
||||
|
||||
object GpuDriverHelper {
|
||||
private const val META_JSON_FILENAME = "meta.json"
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
package org.citra.citra_emu.utils
|
||||
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
class GpuDriverMetadata {
|
||||
/**
|
||||
|
|
@ -76,12 +76,12 @@ class GpuDriverMetadata {
|
|||
}
|
||||
|
||||
return other.name == name &&
|
||||
other.description == description &&
|
||||
other.author == author &&
|
||||
other.vendor == vendor &&
|
||||
other.version == version &&
|
||||
other.minApi == minApi &&
|
||||
other.libraryName == libraryName
|
||||
other.description == description &&
|
||||
other.author == author &&
|
||||
other.vendor == vendor &&
|
||||
other.version == version &&
|
||||
other.minApi == minApi &&
|
||||
other.libraryName == libraryName
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ package org.citra.citra_emu.utils
|
|||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.R
|
||||
import java.util.Locale
|
||||
import kotlin.math.ceil
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.R
|
||||
|
||||
object MemoryUtil {
|
||||
private val context get() = CitraApplication.appContext
|
||||
|
|
@ -24,59 +24,64 @@ object MemoryUtil {
|
|||
const val Pb = Tb * 1024
|
||||
const val Eb = Pb * 1024
|
||||
|
||||
fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String =
|
||||
when {
|
||||
size < Kb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
size.hundredths,
|
||||
context.getString(R.string.memory_byte_shorthand)
|
||||
)
|
||||
}
|
||||
size < Mb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Kb) else (size / Kb).hundredths,
|
||||
context.getString(R.string.memory_kilobyte)
|
||||
)
|
||||
}
|
||||
size < Gb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Mb) else (size / Mb).hundredths,
|
||||
context.getString(R.string.memory_megabyte)
|
||||
)
|
||||
}
|
||||
size < Tb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Gb) else (size / Gb).hundredths,
|
||||
context.getString(R.string.memory_gigabyte)
|
||||
)
|
||||
}
|
||||
size < Pb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Tb) else (size / Tb).hundredths,
|
||||
context.getString(R.string.memory_terabyte)
|
||||
)
|
||||
}
|
||||
size < Eb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Pb) else (size / Pb).hundredths,
|
||||
context.getString(R.string.memory_petabyte)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Eb) else (size / Eb).hundredths,
|
||||
context.getString(R.string.memory_exabyte)
|
||||
)
|
||||
}
|
||||
fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String = when {
|
||||
size < Kb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
size.hundredths,
|
||||
context.getString(R.string.memory_byte_shorthand)
|
||||
)
|
||||
}
|
||||
|
||||
size < Mb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Kb) else (size / Kb).hundredths,
|
||||
context.getString(R.string.memory_kilobyte)
|
||||
)
|
||||
}
|
||||
|
||||
size < Gb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Mb) else (size / Mb).hundredths,
|
||||
context.getString(R.string.memory_megabyte)
|
||||
)
|
||||
}
|
||||
|
||||
size < Tb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Gb) else (size / Gb).hundredths,
|
||||
context.getString(R.string.memory_gigabyte)
|
||||
)
|
||||
}
|
||||
|
||||
size < Pb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Tb) else (size / Tb).hundredths,
|
||||
context.getString(R.string.memory_terabyte)
|
||||
)
|
||||
}
|
||||
|
||||
size < Eb -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Pb) else (size / Pb).hundredths,
|
||||
context.getString(R.string.memory_petabyte)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
context.getString(
|
||||
R.string.memory_formatted,
|
||||
if (roundUp) ceil(size / Eb) else (size / Eb).hundredths,
|
||||
context.getString(R.string.memory_exabyte)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val totalMemory: Float
|
||||
get() {
|
||||
val memInfo = ActivityManager.MemoryInfo()
|
||||
|
|
@ -91,16 +96,15 @@ object MemoryUtil {
|
|||
}
|
||||
}
|
||||
|
||||
fun isLessThan(minimum: Int, size: Float): Boolean =
|
||||
when (size) {
|
||||
Kb -> totalMemory < Mb && totalMemory < minimum
|
||||
Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
|
||||
Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
|
||||
Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
|
||||
Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
|
||||
Eb -> totalMemory / Eb < minimum
|
||||
else -> totalMemory < Kb && totalMemory < minimum
|
||||
}
|
||||
fun isLessThan(minimum: Int, size: Float): Boolean = when (size) {
|
||||
Kb -> totalMemory < Mb && totalMemory < minimum
|
||||
Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
|
||||
Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
|
||||
Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
|
||||
Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
|
||||
Eb -> totalMemory / Eb < minimum
|
||||
else -> totalMemory < Kb && totalMemory < minimum
|
||||
}
|
||||
|
||||
// Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for
|
||||
// the potential error created by memInfo.totalMem
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import android.net.Uri
|
|||
import android.os.Build
|
||||
import android.provider.DocumentsContract
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
|
||||
object PermissionsHandler {
|
||||
|
|
@ -38,7 +38,10 @@ object PermissionsHandler {
|
|||
context.contentResolver.releasePersistableUriPermission(uri, takeFlags)
|
||||
} catch (e: Exception) {
|
||||
// Do not use native library logging, as the native library may not be loaded yet
|
||||
android.util.Log.e("PermissionsHandler", "Cannot check citra data directory permission, error: ${e.message}")
|
||||
android.util.Log.e(
|
||||
"PermissionsHandler",
|
||||
"Cannot check citra data directory permission, error: ${e.message}"
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -62,6 +65,5 @@ object PermissionsHandler {
|
|||
)
|
||||
activityLauncher.launch(initialUri)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,4 +50,4 @@ object RefreshRateUtil {
|
|||
window.attributes.preferredDisplayModeId = newModeId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.preference.PreferenceManager
|
||||
import kotlin.math.roundToInt
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.features.settings.model.Settings
|
||||
import org.citra.citra_emu.ui.main.ThemeProvider
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
object ThemeUtil {
|
||||
const val SYSTEM_BAR_ALPHA = 0.9f
|
||||
|
|
@ -75,18 +75,19 @@ object ThemeUtil {
|
|||
false -> setLightModeSystemBars(windowController)
|
||||
true -> setDarkModeSystemBars(windowController)
|
||||
}
|
||||
|
||||
AppCompatDelegate.MODE_NIGHT_NO -> setLightModeSystemBars(windowController)
|
||||
|
||||
AppCompatDelegate.MODE_NIGHT_YES -> setDarkModeSystemBars(windowController)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isNightMode(activity: AppCompatActivity): Boolean {
|
||||
return when (activity.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
||||
private fun isNightMode(activity: AppCompatActivity): Boolean =
|
||||
when (activity.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
||||
Configuration.UI_MODE_NIGHT_NO -> false
|
||||
Configuration.UI_MODE_NIGHT_YES -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLightModeSystemBars(windowController: WindowInsetsControllerCompat) {
|
||||
windowController.isAppearanceLightStatusBars = true
|
||||
|
|
@ -107,21 +108,24 @@ object ThemeUtil {
|
|||
}
|
||||
|
||||
@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)
|
||||
)
|
||||
}
|
||||
fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int = Color.argb(
|
||||
(alphaFactor * Color.alpha(color)).roundToInt(),
|
||||
Color.red(color),
|
||||
Color.green(color),
|
||||
Color.blue(color)
|
||||
)
|
||||
|
||||
// Listener that detects if the theme keys are being changed from the setting menu and recreates the activity
|
||||
private var listener: SharedPreferences.OnSharedPreferenceChangeListener? = null
|
||||
|
||||
fun ThemeChangeListener(activity: AppCompatActivity) {
|
||||
listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
||||
val relevantKeys = listOf(Settings.PREF_STATIC_THEME_COLOR, Settings.PREF_MATERIAL_YOU, Settings.PREF_BLACK_BACKGROUNDS)
|
||||
val relevantKeys =
|
||||
listOf(
|
||||
Settings.PREF_STATIC_THEME_COLOR,
|
||||
Settings.PREF_MATERIAL_YOU,
|
||||
Settings.PREF_BLACK_BACKGROUNDS
|
||||
)
|
||||
if (key in relevantKeys) {
|
||||
activity.recreate()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ import org.citra.citra_emu.features.settings.model.IntSetting
|
|||
object TurboHelper {
|
||||
private var turboSpeedEnabled = false
|
||||
|
||||
fun isTurboSpeedEnabled(): Boolean {
|
||||
return turboSpeedEnabled
|
||||
}
|
||||
fun isTurboSpeedEnabled(): Boolean = turboSpeedEnabled
|
||||
|
||||
fun reloadTurbo(showToast: Boolean) {
|
||||
val context = CitraApplication.appContext
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import androidx.lifecycle.ViewModel
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
object CompressProgressDialogViewModel: ViewModel() {
|
||||
object CompressProgressDialogViewModel : ViewModel() {
|
||||
private val _progress = MutableStateFlow(0)
|
||||
val progress = _progress.asStateFlow()
|
||||
|
||||
|
|
@ -30,4 +30,4 @@ object CompressProgressDialogViewModel: ViewModel() {
|
|||
_total.value = 0
|
||||
_message.value = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import kotlinx.coroutines.withContext
|
|||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.utils.FileUtil.asDocumentFile
|
||||
import org.citra.citra_emu.utils.GpuDriverMetadata
|
||||
import org.citra.citra_emu.utils.GpuDriverHelper
|
||||
import org.citra.citra_emu.utils.GpuDriverMetadata
|
||||
|
||||
class DriverViewModel : ViewModel() {
|
||||
val areDriversLoading get() = _areDriversLoading.asStateFlow()
|
||||
|
|
|
|||
|
|
@ -70,13 +70,17 @@ class HomeViewModel : ViewModel() {
|
|||
val selectedCitraDirectoryLiveData: LiveData<Uri?> = _selectedCitraDirectory
|
||||
var selectedCitraDirectory: Uri?
|
||||
get() = _selectedCitraDirectory.value
|
||||
set(value) { _selectedCitraDirectory.value = value }
|
||||
set(value) {
|
||||
_selectedCitraDirectory.value = value
|
||||
}
|
||||
|
||||
private val _selectedGamesDirectory = MutableLiveData<Uri?>()
|
||||
val selectedGamesDirectoryLiveData: LiveData<Uri?> = _selectedGamesDirectory
|
||||
var selectedGamesDirectory: Uri?
|
||||
get() = _selectedGamesDirectory.value
|
||||
set(value) { _selectedGamesDirectory.value = value }
|
||||
set(value) {
|
||||
_selectedGamesDirectory.value = value
|
||||
}
|
||||
|
||||
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
|
||||
if (_navigationVisible.value.first == visible) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue