diff options
Diffstat (limited to 'src/android/app/src/main/java/org')
28 files changed, 339 insertions, 331 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 07f1b4842..f2ba2504c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -230,8 +230,6 @@ object NativeLibrary { */ external fun onTouchReleased(finger_id: Int) - external fun reloadSettings() - external fun initGameIni(gameID: String?) external fun setAppDirectory(directory: String) @@ -252,7 +250,7 @@ object NativeLibrary { external fun reloadKeys(): Boolean - external fun initializeSystem() + external fun initializeSystem(reload: Boolean) external fun defaultCPUCore(): Int @@ -462,12 +460,12 @@ object NativeLibrary { } fun setEmulationActivity(emulationActivity: EmulationActivity?) { - Log.verbose("[NativeLibrary] Registering EmulationActivity.") + Log.debug("[NativeLibrary] Registering EmulationActivity.") sEmulationActivity = WeakReference(emulationActivity) } fun clearEmulationActivity() { - Log.verbose("[NativeLibrary] Unregistering EmulationActivity.") + Log.debug("[NativeLibrary] Unregistering EmulationActivity.") sEmulationActivity.clear() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 8c053670c..d114bd53d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt @@ -11,6 +11,7 @@ import java.io.File import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.Log fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir @@ -49,6 +50,7 @@ class YuzuApplication : Application() { DirectoryInitialization.start() GpuDriverHelper.initializeDriverParameters() NativeLibrary.logDeviceInfo() + Log.logDeviceInfo() createNotificationChannels() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index f37875ffe..f41d7bdbf 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -47,6 +47,7 @@ import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.ForegroundService import org.yuzu.yuzu_emu.utils.InputHandler +import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.NfcReader import org.yuzu.yuzu_emu.utils.ThemeHelper @@ -80,6 +81,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { } override fun onCreate(savedInstanceState: Bundle?) { + Log.gameLaunched = true ThemeHelper.setTheme(this) super.onCreate(savedInstanceState) @@ -105,7 +107,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) { - if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) { + if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) { Toast.makeText( this, getString( @@ -371,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val isEmulationActive = emulationViewModel.emulationStarted.value && + !emulationViewModel.isEmulationStopping.value pictureInPictureParamsBuilder.setAutoEnterEnabled( - BooleanSetting.PICTURE_IN_PICTURE.boolean + BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive ) } setPictureInPictureParams(pictureInPictureParamsBuilder.build()) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index 0c82cdba8..2ef638559 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toDrawable 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 kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication @@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) : data = Uri.parse(holder.game.path) } - val layerDrawable = ResourcesCompat.getDrawable( - YuzuApplication.appContext.resources, - R.drawable.shortcut, - null - ) as LayerDrawable - layerDrawable.setDrawableByLayerId( - R.id.shortcut_foreground, - GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources) - ) - val inset = YuzuApplication.appContext.resources - .getDimensionPixelSize(R.dimen.icon_inset) - layerDrawable.setLayerInset(1, inset, inset, inset, inset) - val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) - .setShortLabel(holder.game.title) - .setIcon( - IconCompat.createWithAdaptiveBitmap( - layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) + activity.lifecycleScope.launch { + withContext(Dispatchers.IO) { + val layerDrawable = ResourcesCompat.getDrawable( + YuzuApplication.appContext.resources, + R.drawable.shortcut, + null + ) as LayerDrawable + layerDrawable.setDrawableByLayerId( + R.id.shortcut_foreground, + GameIconUtils.getGameIcon(activity, holder.game) + .toDrawable(YuzuApplication.appContext.resources) ) - ) - .setIntent(openIntent) - .build() - ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + val inset = YuzuApplication.appContext.resources + .getDimensionPixelSize(R.dimen.icon_inset) + layerDrawable.setLayerInset(1, inset, inset, inset, inset) + val shortcut = + ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path) + .setShortLabel(holder.game.title) + .setIcon( + IconCompat.createWithAdaptiveBitmap( + layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) + ) + ) + .setIntent(openIntent) + .build() + ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) + } + } val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) view.findNavController().navigate(action) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index 08e2a973d..d005c656e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt @@ -7,7 +7,7 @@ import android.text.TextUtils import android.widget.Toast import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication -import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile +import org.yuzu.yuzu_emu.utils.NativeConfig object Settings { private val context get() = YuzuApplication.appContext @@ -19,7 +19,7 @@ object Settings { context.getString(R.string.ini_saved), Toast.LENGTH_SHORT ).show() - SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG) + NativeConfig.saveSettings() } else { // TODO: Save custom game settings Toast.makeText( @@ -82,7 +82,6 @@ object Settings { enum class MenuTag(val titleId: Int) { SECTION_ROOT(R.string.advanced_settings), - SECTION_GENERAL(R.string.preferences_general), SECTION_SYSTEM(R.string.preferences_system), SECTION_RENDERER(R.string.preferences_graphics), SECTION_AUDIO(R.string.preferences_audio), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt index 522cc49df..425160024 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt @@ -3,10 +3,13 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.DrawableRes + class RunnableSetting( titleId: Int, descriptionId: Int, val isRuntimeRunnable: Boolean, + @DrawableRes val iconId: Int = 0, val runnable: () -> Unit ) : SettingsItem(emptySetting, titleId, descriptionId) { override val type = TYPE_RUNNABLE diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index b3b3fc209..6aba69dbe 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -73,7 +73,7 @@ abstract class SettingsItem( R.string.frame_limit_slider, R.string.frame_limit_slider_description, 1, - 200, + 400, "%" ) ) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt index b343e527e..94953b18a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt @@ -3,11 +3,14 @@ package org.yuzu.yuzu_emu.features.settings.model.view +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import org.yuzu.yuzu_emu.features.settings.model.Settings class SubmenuSetting( - titleId: Int, - descriptionId: Int, + @StringRes titleId: Int, + @StringRes descriptionId: Int, + @DrawableRes val iconId: Int, val menuKey: Settings.MenuTag ) : SettingsItem(emptySetting, titleId, descriptionId) { override val type = TYPE_SUBMENU diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index c73edd50e..48bdbdd75 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt @@ -21,7 +21,6 @@ import androidx.navigation.navArgs import com.google.android.material.color.MaterialColors import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import org.yuzu.yuzu_emu.NativeLibrary import java.io.IOException import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding @@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() { settingsViewModel.shouldSave = false // Delete settings file because the user may have changed values that do not exist in the UI + NativeConfig.unloadConfig() val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) if (!settingsFile.delete()) { throw IOException("Failed to delete $settingsFile") } - NativeLibrary.reloadSettings() + NativeConfig.initializeConfig() Toast.makeText( applicationContext, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt index 70d8ec14b..769baf744 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt @@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.divider.MaterialDividerItemDecoration import com.google.android.material.transition.MaterialSharedAxis import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -68,15 +67,9 @@ class SettingsFragment : Fragment() { ) binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) - val dividerDecoration = MaterialDividerItemDecoration( - requireContext(), - LinearLayoutManager.VERTICAL - ) - dividerDecoration.isLastItemDecorated = false binding.listSettings.apply { adapter = settingsAdapter layoutManager = LinearLayoutManager(requireContext()) - addItemDecoration(dividerDecoration) } binding.toolbarSettings.setNavigationOnClickListener { @@ -94,17 +87,6 @@ class SettingsFragment : Fragment() { } } } - launch { - settingsViewModel.isUsingSearch.collectLatest { - if (it) { - reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) - exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) - } else { - reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) - exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) - } - } - } } if (args.menuTag == Settings.MenuTag.SECTION_ROOT) { @@ -112,8 +94,6 @@ class SettingsFragment : Fragment() { binding.toolbarSettings.setOnMenuItemClickListener { when (it.itemId) { R.id.action_search -> { - reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) - exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) view.findNavController() .navigate(R.id.action_settingsFragment_to_settingsSearchFragment) true @@ -129,11 +109,6 @@ class SettingsFragment : Fragment() { setInsets() } - override fun onResume() { - super.onResume() - settingsViewModel.setIsUsingSearch(false) - } - private fun setInsets() { ViewCompat.setOnApplyWindowInsetsListener( binding.root @@ -144,10 +119,9 @@ class SettingsFragment : Fragment() { val leftInsets = barInsets.left + cutoutInsets.left val rightInsets = barInsets.right + cutoutInsets.right - val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge) val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams - mlpSettingsList.leftMargin = sideMargin + leftInsets - mlpSettingsList.rightMargin = sideMargin + rightInsets + mlpSettingsList.leftMargin = leftInsets + mlpSettingsList.rightMargin = rightInsets binding.listSettings.layoutParams = mlpSettingsList binding.listSettings.updatePadding( bottom = barInsets.bottom diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 766414a6c..8b71e32f3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -3,7 +3,6 @@ package org.yuzu.yuzu_emu.features.settings.ui -import android.content.Context import android.content.SharedPreferences import android.os.Build import android.widget.Toast @@ -32,8 +31,6 @@ class SettingsFragmentPresenter( private val preferences: SharedPreferences get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - private val context: Context get() = YuzuApplication.appContext - // Extension for populating settings list based on paired settings fun ArrayList<SettingsItem>.add(key: String) { val item = SettingsItem.settingsItems[key]!! @@ -53,7 +50,6 @@ class SettingsFragmentPresenter( val sl = ArrayList<SettingsItem>() when (menuTag) { Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) - Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl) Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) @@ -75,30 +71,53 @@ class SettingsFragmentPresenter( private fun addConfigSettings(sl: ArrayList<SettingsItem>) { sl.apply { - add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL)) - add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM)) - add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER)) - add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO)) - add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG)) add( - RunnableSetting(R.string.reset_to_default, 0, false) { - settingsViewModel.setShouldShowResetSettingsDialog(true) - } + SubmenuSetting( + R.string.preferences_system, + R.string.preferences_system_description, + R.drawable.ic_system_settings, + Settings.MenuTag.SECTION_SYSTEM + ) + ) + add( + SubmenuSetting( + R.string.preferences_graphics, + R.string.preferences_graphics_description, + R.drawable.ic_graphics, + Settings.MenuTag.SECTION_RENDERER + ) + ) + add( + SubmenuSetting( + R.string.preferences_audio, + R.string.preferences_audio_description, + R.drawable.ic_audio, + Settings.MenuTag.SECTION_AUDIO + ) + ) + add( + SubmenuSetting( + R.string.preferences_debug, + R.string.preferences_debug_description, + R.drawable.ic_code, + Settings.MenuTag.SECTION_DEBUG + ) + ) + add( + RunnableSetting( + R.string.reset_to_default, + R.string.reset_to_default_description, + false, + R.drawable.ic_restore + ) { settingsViewModel.setShouldShowResetSettingsDialog(true) } ) } } - private fun addGeneralSettings(sl: ArrayList<SettingsItem>) { + private fun addSystemSettings(sl: ArrayList<SettingsItem>) { sl.apply { add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) add(ShortSetting.RENDERER_SPEED_LIMIT.key) - add(IntSetting.CPU_ACCURACY.key) - add(BooleanSetting.PICTURE_IN_PICTURE.key) - } - } - - private fun addSystemSettings(sl: ArrayList<SettingsItem>) { - sl.apply { add(BooleanSetting.USE_DOCKED_MODE.key) add(IntSetting.REGION_INDEX.key) add(IntSetting.LANGUAGE_INDEX.key) @@ -116,6 +135,7 @@ class SettingsFragmentPresenter( add(IntSetting.RENDERER_ANTI_ALIASING.key) add(IntSetting.RENDERER_SCREEN_LAYOUT.key) add(IntSetting.RENDERER_ASPECT_RATIO.key) + add(BooleanSetting.PICTURE_IN_PICTURE.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) @@ -249,6 +269,7 @@ class SettingsFragmentPresenter( add(BooleanSetting.RENDERER_DEBUG.key) add(HeaderSetting(R.string.cpu)) + add(IntSetting.CPU_ACCURACY.key) add(BooleanSetting.CPU_DEBUG_MODE.key) add(SettingsItem.FASTMEM_COMBINED) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt index 83a2e94f1..036195624 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt @@ -4,6 +4,7 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View +import androidx.core.content.res.ResourcesCompat import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting @@ -16,6 +17,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA override fun bind(item: SettingsItem) { setting = item as RunnableSetting + if (item.iconId != 0) { + binding.icon.visibility = View.VISIBLE + binding.icon.setImageDrawable( + ResourcesCompat.getDrawable( + binding.icon.resources, + item.iconId, + binding.icon.context.theme + ) + ) + } else { + binding.icon.visibility = View.GONE + } + binding.textSettingName.setText(item.nameId) if (item.descriptionId != 0) { binding.textSettingDescription.setText(item.descriptionId) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt index 1cf581a9d..8100c65dd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt @@ -4,6 +4,7 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder import android.view.View +import androidx.core.content.res.ResourcesCompat import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting @@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd override fun bind(item: SettingsItem) { this.item = item as SubmenuSetting + if (item.iconId != 0) { + binding.icon.visibility = View.VISIBLE + binding.icon.setImageDrawable( + ResourcesCompat.getDrawable( + binding.icon.resources, + item.iconId, + binding.icon.context.theme + ) + ) + } else { + binding.icon.visibility = View.GONE + } + binding.textSettingName.setText(item.nameId) if (item.descriptionId != 0) { binding.textSettingDescription.setText(item.descriptionId) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt index 2b04d666a..3ae5b4653 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt @@ -3,15 +3,8 @@ package org.yuzu.yuzu_emu.features.settings.utils -import android.widget.Toast import java.io.* -import org.ini4j.Wini -import org.yuzu.yuzu_emu.R -import org.yuzu.yuzu_emu.YuzuApplication -import org.yuzu.yuzu_emu.features.settings.model.* import org.yuzu.yuzu_emu.utils.DirectoryInitialization -import org.yuzu.yuzu_emu.utils.Log -import org.yuzu.yuzu_emu.utils.NativeConfig /** * Contains static methods for interacting with .ini files in which settings are stored. @@ -19,41 +12,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig object SettingsFile { const val FILE_NAME_CONFIG = "config" - /** - * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error - * telling why it failed. - * - * @param fileName The target filename without a path or extension. - */ - fun saveFile(fileName: String) { - val ini = getSettingsFile(fileName) - try { - val wini = Wini(ini) - for (specificCategory in Settings.Category.values()) { - val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal) - for (setting in Settings.settingsList) { - if (setting.key!!.isEmpty()) continue - - val settingCategoryHeader = - NativeConfig.getConfigHeader(setting.category.ordinal) - val iniSetting: String? = wini.get(categoryHeader, setting.key) - if (iniSetting != null || settingCategoryHeader == categoryHeader) { - wini.put(settingCategoryHeader, setting.key, setting.valueAsString) - } - } - } - wini.store() - } catch (e: IOException) { - Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message) - val context = YuzuApplication.appContext - Toast.makeText( - context, - context.getString(R.string.error_saving, fileName, e.message), - Toast.LENGTH_SHORT - ).show() - } - } - fun getSettingsFile(fileName: String): File = File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini") } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt index 2ff827c6b..a1620fbb7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt @@ -114,10 +114,10 @@ class AboutFragment : Fragment() { val leftInsets = barInsets.left + cutoutInsets.left val rightInsets = barInsets.right + cutoutInsets.right - val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams - mlpAppBar.leftMargin = leftInsets - mlpAppBar.rightMargin = rightInsets - binding.appbarAbout.layoutParams = mlpAppBar + val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams + mlpToolbar.leftMargin = leftInsets + mlpToolbar.rightMargin = rightInsets + binding.toolbarAbout.layoutParams = mlpToolbar val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams mlpScrollAbout.leftMargin = leftInsets diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 07bd78bf7..c32fa0d7e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -10,7 +10,6 @@ import android.content.DialogInterface import android.content.SharedPreferences import android.content.pm.ActivityInfo import android.content.res.Configuration -import android.graphics.Color import android.net.Uri import android.os.Bundle import android.os.Handler @@ -155,7 +154,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } binding.surfaceEmulation.holder.addCallback(this) - binding.showFpsText.setTextColor(Color.YELLOW) binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } binding.drawerLayout.addDrawerListener(object : DrawerListener { @@ -312,6 +310,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { ViewUtils.showView(binding.surfaceInputOverlay) ViewUtils.hideView(binding.loadingIndicator) + emulationState.updateSurface() + // Setup overlay updateShowFpsOverlay() } @@ -412,12 +412,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { val FRAMETIME = 2 val SPEED = 3 perfStatsUpdater = { - if (emulationViewModel.emulationStarted.value == true) { + if (emulationViewModel.emulationStarted.value) { val perfStats = NativeLibrary.getPerfStats() - if (perfStats[FPS] > 0 && _binding != null) { + if (_binding != null) { binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) } - perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) + perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) } } perfStatsUpdateHandler.post(perfStatsUpdater!!) @@ -462,7 +462,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) { // Restrict emulation and overlays to the top of the screen binding.emulationContainer.layoutParams.height = it.bounds.top - binding.overlayContainer.layoutParams.height = it.bounds.top // Restrict input and menu drawer to the bottom of the screen binding.inputContainer.layoutParams.height = it.bounds.bottom binding.inGameMenu.layoutParams.height = it.bounds.bottom @@ -476,7 +475,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { if (!isFolding) { binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT - binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT isInFoldableLayout = false updateOrientation() @@ -484,7 +482,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } binding.emulationContainer.requestLayout() binding.inputContainer.requestLayout() - binding.overlayContainer.requestLayout() binding.inGameMenu.requestLayout() } @@ -710,24 +707,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } v.setPadding(left, cutInsets.top, right, 0) - - // Ensure FPS text doesn't get cut off by rounded display corners - val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_xtralarge) - if (cutInsets.left == 0) { - binding.showFpsText.setPadding( - sidePadding, - cutInsets.top, - cutInsets.right, - cutInsets.bottom - ) - } else { - binding.showFpsText.setPadding( - cutInsets.left, - cutInsets.top, - cutInsets.right, - cutInsets.bottom - ) - } windowInsets } } @@ -805,6 +784,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } @Synchronized + fun updateSurface() { + if (surface != null) { + NativeLibrary.surfaceChanged(surface) + } + } + + @Synchronized fun clearSurface() { if (surface == null) { Log.warning("[EmulationFragment] clearSurface called, but surface already null.") diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index 6e19fc6c0..4720daec4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -42,6 +42,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.GpuDriverHelper +import org.yuzu.yuzu_emu.utils.Log class HomeSettingsFragment : Fragment() { private var _binding: FragmentHomeSettingsBinding? = null @@ -86,28 +87,6 @@ class HomeSettingsFragment : Fragment() { ) add( HomeSetting( - R.string.open_user_folder, - R.string.open_user_folder_description, - R.drawable.ic_folder_open, - { openFileManager() } - ) - ) - add( - HomeSetting( - R.string.preferences_theme, - R.string.theme_and_color_description, - R.drawable.ic_palette, - { - val action = HomeNavigationDirections.actionGlobalSettingsActivity( - null, - Settings.MenuTag.SECTION_THEME - ) - binding.root.findNavController().navigate(action) - } - ) - ) - add( - HomeSetting( R.string.gpu_driver_manager, R.string.install_gpu_driver_description, R.drawable.ic_build, @@ -123,17 +102,6 @@ class HomeSettingsFragment : Fragment() { ) add( HomeSetting( - R.string.manage_yuzu_data, - R.string.manage_yuzu_data_description, - R.drawable.ic_install, - { - binding.root.findNavController() - .navigate(R.id.action_homeSettingsFragment_to_installableFragment) - } - ) - ) - add( - HomeSetting( R.string.applets, R.string.applets_description, R.drawable.ic_applet, @@ -148,6 +116,17 @@ class HomeSettingsFragment : Fragment() { ) add( HomeSetting( + R.string.manage_yuzu_data, + R.string.manage_yuzu_data_description, + R.drawable.ic_install, + { + binding.root.findNavController() + .navigate(R.id.action_homeSettingsFragment_to_installableFragment) + } + ) + ) + add( + HomeSetting( R.string.select_games_folder, R.string.select_games_folder_description, R.drawable.ic_add, @@ -172,6 +151,28 @@ class HomeSettingsFragment : Fragment() { ) add( HomeSetting( + R.string.open_user_folder, + R.string.open_user_folder_description, + R.drawable.ic_folder_open, + { openFileManager() } + ) + ) + add( + HomeSetting( + R.string.preferences_theme, + R.string.theme_and_color_description, + R.drawable.ic_palette, + { + val action = HomeNavigationDirections.actionGlobalSettingsActivity( + null, + Settings.MenuTag.SECTION_THEME + ) + binding.root.findNavController().navigate(action) + } + ) + ) + add( + HomeSetting( R.string.about, R.string.about_description, R.drawable.ic_info_outline, @@ -312,19 +313,32 @@ class HomeSettingsFragment : Fragment() { } } + // Share the current log if we just returned from a game but share the old log + // if we just started the app and the old log exists. private fun shareLog() { - val file = DocumentFile.fromSingleUri( + val currentLog = DocumentFile.fromSingleUri( mainActivity, DocumentsContract.buildDocumentUri( DocumentProvider.AUTHORITY, "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt" ) )!! - if (file.exists()) { - val intent = Intent(Intent.ACTION_SEND) - .setDataAndType(file.uri, FileUtil.TEXT_PLAIN) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .putExtra(Intent.EXTRA_STREAM, file.uri) + val oldLog = DocumentFile.fromSingleUri( + mainActivity, + DocumentsContract.buildDocumentUri( + DocumentProvider.AUTHORITY, + "${DocumentProvider.ROOT_ID}/log/yuzu_log.txt.old.txt" + ) + )!! + + val intent = Intent(Intent.ACTION_SEND) + .setDataAndType(currentLog.uri, FileUtil.TEXT_PLAIN) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + if (!Log.gameLaunched && oldLog.exists()) { + intent.putExtra(Intent.EXTRA_STREAM, oldLog.uri) + startActivity(Intent.createChooser(intent, getText(R.string.share_log))) + } else if (currentLog.exists()) { + intent.putExtra(Intent.EXTRA_STREAM, currentLog.uri) startActivity(Intent.createChooser(intent, getText(R.string.share_log))) } else { Toast.makeText( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt index ec116ab62..6940fc757 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt @@ -21,6 +21,8 @@ import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.Installable import org.yuzu.yuzu_emu.ui.main.MainActivity +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter class InstallableFragment : Fragment() { private var _binding: FragmentInstallablesBinding? = null @@ -78,7 +80,15 @@ class InstallableFragment : Fragment() { R.string.manage_save_data, R.string.import_export_saves_description, install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }, - export = { mainActivity.exportSave() } + export = { + mainActivity.exportSaves.launch( + "yuzu saves - ${ + LocalDateTime.now().format( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") + ) + }.zip" + ) + } ) } else { Installable( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt index 9d0594c6e..f95d545bf 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt @@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) - returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) + enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) } override fun onCreateView( @@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - settingsViewModel.setIsUsingSearch(true) if (savedInstanceState != null) { binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index de84b2adb..2fa3ab31b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -18,8 +18,8 @@ class Game( val version: String = "", val isHomebrew: Boolean = false ) : Parcelable { - val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime" - val keyLastPlayedTime get() = "${programId}_LastPlayed" + val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime" + val keyLastPlayedTime get() = "${path}_LastPlayed" override fun equals(other: Any?): Boolean { if (other !is Game) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt index 53fa7a8de..6f947674e 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt @@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() { val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList private val _shouldReloadSettingsList = MutableStateFlow(false) - val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch - private val _isUsingSearch = MutableStateFlow(false) - val sliderProgress: StateFlow<Int> get() = _sliderProgress private val _sliderProgress = MutableStateFlow(-1) @@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() { _shouldReloadSettingsList.value = value } - fun setIsUsingSearch(value: Boolean) { - _isUsingSearch.value = value - } - fun setSliderTextValue(value: Float, units: String) { _sliderProgress.value = value.toInt() _sliderTextValue.value = String.format( diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index ba1177426..bd2f4cd25 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.ui.main import android.content.Intent import android.net.Uri import android.os.Bundle -import android.provider.DocumentsContract import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.view.WindowManager @@ -20,7 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat -import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -41,7 +39,6 @@ import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.databinding.ActivityMainBinding -import org.yuzu.yuzu_emu.features.DocumentProvider import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment @@ -53,9 +50,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel import org.yuzu.yuzu_emu.utils.* import java.io.BufferedInputStream import java.io.BufferedOutputStream -import java.io.FileOutputStream -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter import java.util.zip.ZipEntry import java.util.zip.ZipInputStream @@ -73,7 +67,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { // Get first subfolder in saves folder (should be the user folder) val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" - private var lastZipCreated: File? = null override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() @@ -403,7 +396,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } else { firmwarePath.deleteRecursively() cacheFirmwareDir.copyRecursively(firmwarePath, true) - NativeLibrary.initializeSystem() + NativeLibrary.initializeSystem(true) getString(R.string.save_file_imported_success) } } catch (e: Exception) { @@ -632,6 +625,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } // Clear existing user data + NativeConfig.unloadConfig() File(DirectoryInitialization.userDirectory!!).deleteRecursively() // Copy archive to internal storage @@ -649,7 +643,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } // Reinitialize relevant data - NativeLibrary.initializeSystem() + NativeLibrary.initializeSystem(true) + NativeConfig.initializeConfig() gamesViewModel.reloadGames(false) return@newInstance getString(R.string.user_data_import_success) @@ -657,74 +652,30 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } /** - * Zips the save files located in the given folder path and creates a new zip file with the current date and time. - * @return true if the zip file is successfully created, false otherwise. - */ - private fun zipSave(): Boolean { - try { - val tempFolder = File(getPublicFilesDir().canonicalPath, "temp") - tempFolder.mkdirs() - val saveFolder = File(savesFolderRoot) - val outputZipFile = File( - tempFolder, - "yuzu saves - ${ - LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) - }.zip" - ) - outputZipFile.createNewFile() - val result = FileUtil.zipFromInternalStorage( - saveFolder, - savesFolderRoot, - BufferedOutputStream(FileOutputStream(outputZipFile)) - ) - if (result == TaskState.Failed) { - return false - } - lastZipCreated = outputZipFile - } catch (e: Exception) { - return false - } - return true - } - - /** * Exports the save file located in the given folder path by creating a zip file and sharing it via intent. */ - fun exportSave() { - CoroutineScope(Dispatchers.IO).launch { - val wasZipCreated = zipSave() - val lastZipFile = lastZipCreated - if (!wasZipCreated || lastZipFile == null) { - withContext(Dispatchers.Main) { - Toast.makeText( - this@MainActivity, - getString(R.string.export_save_failed), - Toast.LENGTH_LONG - ).show() - } - return@launch - } + val exportSaves = registerForActivityResult( + ActivityResultContracts.CreateDocument("application/zip") + ) { result -> + if (result == null) { + return@registerForActivityResult + } - withContext(Dispatchers.Main) { - val file = DocumentFile.fromSingleUri( - this@MainActivity, - DocumentsContract.buildDocumentUri( - DocumentProvider.AUTHORITY, - "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" - ) - )!! - val intent = Intent(Intent.ACTION_SEND) - .setDataAndType(file.uri, "application/zip") - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .putExtra(Intent.EXTRA_STREAM, file.uri) - startForResultExportSave.launch( - Intent.createChooser( - intent, - getString(R.string.share_save_file) - ) - ) + IndeterminateProgressDialogFragment.newInstance( + this, + R.string.save_files_exporting, + false + ) { + val zipResult = FileUtil.zipFromInternalStorage( + File(savesFolderRoot), + savesFolderRoot, + BufferedOutputStream(contentResolver.openOutputStream(result)) + ) + return@newInstance when (zipResult) { + TaskState.Completed -> getString(R.string.export_success) + TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed) } - } + }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) } private val startForResultExportSave = diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt index 79a07f7ef..21270fc84 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt @@ -15,7 +15,8 @@ object DirectoryInitialization { fun start() { if (!areDirectoriesReady) { initializeInternalStorage() - NativeLibrary.initializeSystem() + NativeLibrary.initializeSystem(false) + NativeConfig.initializeConfig() areDirectoriesReady = true } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt index 654d62f52..2e9b0beb8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt @@ -8,9 +8,9 @@ import android.graphics.BitmapFactory import android.widget.ImageView import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toDrawable +import androidx.lifecycle.LifecycleOwner import coil.ImageLoader import coil.decode.DataSource -import coil.executeBlocking import coil.fetch.DrawableResult import coil.fetch.FetchResult import coil.fetch.Fetcher @@ -76,12 +76,13 @@ object GameIconUtils { imageLoader.enqueue(request) } - fun getGameIcon(game: Game): Bitmap { + suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap { val request = ImageRequest.Builder(YuzuApplication.appContext) .data(game) + .lifecycle(lifecycleOwner) .error(R.drawable.default_icon) .build() - return imageLoader.executeBlocking(request) + return imageLoader.execute(request) .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt index fc6a8b5cb..e63382e1d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/InputHandler.kt @@ -27,6 +27,8 @@ object InputHandler { 0x054C -> getInputDS5ButtonKey(event.keyCode) 0x057E -> getInputJoyconButtonKey(event.keyCode) 0x1532 -> getInputRazerButtonKey(event.keyCode) + 0x3537 -> getInputRedmagicButtonKey(event.keyCode) + 0x358A -> getInputBackboneLabsButtonKey(event.keyCode) else -> getInputGenericButtonKey(event.keyCode) } @@ -68,7 +70,7 @@ object InputHandler { private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int { var deviceIndex = index if (deviceId != -1) { - deviceIndex = controllerIds[deviceId]!! + deviceIndex = controllerIds[deviceId] ?: 0 } // TODO: Joycons are handled as different controllers. Find a way to merge them. @@ -227,6 +229,42 @@ object InputHandler { } } + private fun getInputRedmagicButtonKey(key: Int): Int { + return when (key) { + KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B + KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A + KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y + KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X + KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L + KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R + KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL + KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR + KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L + KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R + KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS + KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS + else -> -1 + } + } + + private fun getInputBackboneLabsButtonKey(key: Int): Int { + return when (key) { + KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_B + KeyEvent.KEYCODE_BUTTON_B -> NativeLibrary.ButtonType.BUTTON_A + KeyEvent.KEYCODE_BUTTON_X -> NativeLibrary.ButtonType.BUTTON_Y + KeyEvent.KEYCODE_BUTTON_Y -> NativeLibrary.ButtonType.BUTTON_X + KeyEvent.KEYCODE_BUTTON_L1 -> NativeLibrary.ButtonType.TRIGGER_L + KeyEvent.KEYCODE_BUTTON_R1 -> NativeLibrary.ButtonType.TRIGGER_R + KeyEvent.KEYCODE_BUTTON_L2 -> NativeLibrary.ButtonType.TRIGGER_ZL + KeyEvent.KEYCODE_BUTTON_R2 -> NativeLibrary.ButtonType.TRIGGER_ZR + KeyEvent.KEYCODE_BUTTON_THUMBL -> NativeLibrary.ButtonType.STICK_L + KeyEvent.KEYCODE_BUTTON_THUMBR -> NativeLibrary.ButtonType.STICK_R + KeyEvent.KEYCODE_BUTTON_START -> NativeLibrary.ButtonType.BUTTON_PLUS + KeyEvent.KEYCODE_BUTTON_SELECT -> NativeLibrary.ButtonType.BUTTON_MINUS + else -> -1 + } + } + private fun getInputGenericButtonKey(key: Int): Int { return when (key) { KeyEvent.KEYCODE_BUTTON_A -> NativeLibrary.ButtonType.BUTTON_A diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt index a193e82a4..aebe84b0f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/Log.kt @@ -3,38 +3,29 @@ package org.yuzu.yuzu_emu.utils -import android.util.Log -import org.yuzu.yuzu_emu.BuildConfig - -/** - * Contains methods that call through to [android.util.Log], but - * with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log - * levels in release builds. - */ +import android.os.Build + object Log { - private const val TAG = "Yuzu Frontend" + // Tracks whether we should share the old log or the current log + var gameLaunched = false - fun verbose(message: String) { - if (BuildConfig.DEBUG) { - Log.v(TAG, message) - } - } + external fun debug(message: String) - fun debug(message: String) { - if (BuildConfig.DEBUG) { - Log.d(TAG, message) - } - } + external fun warning(message: String) - fun info(message: String) { - Log.i(TAG, message) - } + external fun info(message: String) - fun warning(message: String) { - Log.w(TAG, message) - } + external fun error(message: String) - fun error(message: String) { - Log.e(TAG, message) + external fun critical(message: String) + + fun logDeviceInfo() { + info("Device Manufacturer - ${Build.MANUFACTURER}") + info("Device Model - ${Build.MODEL}") + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { + info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}") + info("SoC Model - ${Build.SOC_MODEL}") + } + info("Total System Memory - ${MemoryUtil.getDeviceRAM()}") } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt index aa4a5539a..9076a86c4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt @@ -27,7 +27,7 @@ object MemoryUtil { const val Pb = Tb * 1024 const val Eb = Pb * 1024 - private fun bytesToSizeUnit(size: Float): String = + private fun bytesToSizeUnit(size: Float, roundUp: Boolean = false): String = when { size < Kb -> { context.getString( @@ -39,63 +39,59 @@ object MemoryUtil { size < Mb -> { context.getString( R.string.memory_formatted, - (size / Kb).hundredths, + if (roundUp) ceil(size / Kb) else (size / Kb).hundredths, context.getString(R.string.memory_kilobyte) ) } size < Gb -> { context.getString( R.string.memory_formatted, - (size / Mb).hundredths, + if (roundUp) ceil(size / Mb) else (size / Mb).hundredths, context.getString(R.string.memory_megabyte) ) } size < Tb -> { context.getString( R.string.memory_formatted, - (size / Gb).hundredths, + if (roundUp) ceil(size / Gb) else (size / Gb).hundredths, context.getString(R.string.memory_gigabyte) ) } size < Pb -> { context.getString( R.string.memory_formatted, - (size / Tb).hundredths, + if (roundUp) ceil(size / Tb) else (size / Tb).hundredths, context.getString(R.string.memory_terabyte) ) } size < Eb -> { context.getString( R.string.memory_formatted, - (size / Pb).hundredths, + if (roundUp) ceil(size / Pb) else (size / Pb).hundredths, context.getString(R.string.memory_petabyte) ) } else -> { context.getString( R.string.memory_formatted, - (size / Eb).hundredths, + if (roundUp) ceil(size / Eb) else (size / Eb).hundredths, context.getString(R.string.memory_exabyte) ) } } - // 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 - private val totalMemory: Float + val totalMemory: Float get() { val memInfo = ActivityManager.MemoryInfo() with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) { getMemoryInfo(memInfo) } - return ceil( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - memInfo.advertisedMem.toFloat() - } else { - memInfo.totalMem.toFloat() - } - ) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + memInfo.advertisedMem.toFloat() + } else { + memInfo.totalMem.toFloat() + } } fun isLessThan(minimum: Int, size: Float): Boolean = @@ -109,5 +105,7 @@ object MemoryUtil { else -> totalMemory < Kb && totalMemory < minimum } - fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory) + // 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 + fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory, true) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt index 9425f8b99..87e579fa7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt @@ -4,6 +4,30 @@ package org.yuzu.yuzu_emu.utils object NativeConfig { + /** + * Creates a Config object and opens the emulation config. + */ + @Synchronized + external fun initializeConfig() + + /** + * Destroys the stored config object. This automatically saves the existing config. + */ + @Synchronized + external fun unloadConfig() + + /** + * Reads values saved to the config file and saves them. + */ + @Synchronized + external fun reloadSettings() + + /** + * Saves settings values in memory to disk. + */ + @Synchronized + external fun saveSettings() + external fun getBoolean(key: String, getDefault: Boolean): Boolean external fun setBoolean(key: String, value: Boolean) |