[android, gameProperties] Add support for sharing per-game config file #478
5 changed files with 130 additions and 43 deletions
|
@ -83,6 +83,17 @@ class GamePropertiesAdapter(
|
|||
} else {
|
||||
binding.details.setVisible(false)
|
||||
}
|
||||
|
||||
if (submenuProperty.secondaryAction != null) {
|
||||
binding.buttonSecondaryAction.setVisible(submenuProperty.secondaryAction.isShown)
|
||||
binding.buttonSecondaryAction.setIconResource(submenuProperty.secondaryAction.iconId)
|
||||
binding.buttonSecondaryAction.contentDescription = binding.buttonSecondaryAction.context.getString(submenuProperty.secondaryAction.descriptionId)
|
||||
binding.buttonSecondaryAction.setOnClickListener {
|
||||
submenuProperty.secondaryAction.action.invoke()
|
||||
}
|
||||
} else {
|
||||
binding.buttonSecondaryAction.setVisible(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.os.Bundle
|
||||
import android.provider.DocumentsContract
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -14,6 +16,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -29,12 +32,14 @@ import org.yuzu.yuzu_emu.R
|
|||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.adapters.GamePropertiesAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentGamePropertiesBinding
|
||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.GameProperty
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.InstallableProperty
|
||||
import org.yuzu.yuzu_emu.model.SubMenuProperSecondaryAction
|
||||
import org.yuzu.yuzu_emu.model.SubmenuProperty
|
||||
import org.yuzu.yuzu_emu.model.TaskState
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
|
@ -137,25 +142,44 @@ class GamePropertiesFragment : Fragment() {
|
|||
SubmenuProperty(
|
||||
R.string.info,
|
||||
R.string.info_description,
|
||||
R.drawable.ic_info_outline
|
||||
) {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToGameInfoFragment(args.game)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
R.drawable.ic_info_outline,
|
||||
action = {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToGameInfoFragment(args.game)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
SubmenuProperty(
|
||||
R.string.preferences_settings,
|
||||
R.string.per_game_settings_description,
|
||||
R.drawable.ic_settings
|
||||
) {
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
||||
args.game,
|
||||
Settings.MenuTag.SECTION_ROOT
|
||||
R.drawable.ic_settings,
|
||||
action = {
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
||||
args.game,
|
||||
Settings.MenuTag.SECTION_ROOT
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
},
|
||||
secondaryAction = SubMenuProperSecondaryAction(
|
||||
isShown = File(
|
||||
DirectoryInitialization.userDirectory +
|
||||
"/config/custom/" + args.game.settingsName + ".ini"
|
||||
).exists(),
|
||||
descriptionId = R.string.share_game_settings,
|
||||
iconId = R.drawable.ic_share,
|
||||
action = {
|
||||
val configFile = File(
|
||||
DirectoryInitialization.userDirectory +
|
||||
"/config/custom/" + args.game.settingsName + ".ini"
|
||||
)
|
||||
if (configFile.exists()) {
|
||||
shareConfigFile(configFile)
|
||||
}
|
||||
}
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if (GpuDriverHelper.supportsCustomDriverLoading()) {
|
||||
|
@ -164,12 +188,13 @@ class GamePropertiesFragment : Fragment() {
|
|||
R.string.gpu_driver_manager,
|
||||
R.string.install_gpu_driver_description,
|
||||
R.drawable.ic_build,
|
||||
detailsFlow = driverViewModel.selectedDriverTitle
|
||||
) {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToDriverManagerFragment(args.game)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
detailsFlow = driverViewModel.selectedDriverTitle,
|
||||
action = {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToDriverManagerFragment(args.game)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -178,12 +203,13 @@ class GamePropertiesFragment : Fragment() {
|
|||
SubmenuProperty(
|
||||
R.string.add_ons,
|
||||
R.string.add_ons_description,
|
||||
R.drawable.ic_edit
|
||||
) {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToAddonsFragment(args.game)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
R.drawable.ic_edit,
|
||||
action = {
|
||||
val action = GamePropertiesFragmentDirections
|
||||
.actionPerGamePropertiesFragmentToAddonsFragment(args.game)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
InstallableProperty(
|
||||
|
@ -245,7 +271,7 @@ class GamePropertiesFragment : Fragment() {
|
|||
R.string.clear_shader_cache,
|
||||
R.string.clear_shader_cache_description,
|
||||
R.drawable.ic_delete,
|
||||
{
|
||||
details = {
|
||||
if (shaderCacheDir.exists()) {
|
||||
val bytes = shaderCacheDir.walkTopDown().filter { it.isFile }
|
||||
.map { it.length() }.sum()
|
||||
|
@ -253,23 +279,24 @@ class GamePropertiesFragment : Fragment() {
|
|||
} else {
|
||||
MemoryUtil.bytesToSizeUnit(0f)
|
||||
}
|
||||
},
|
||||
action = {
|
||||
MessageDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
titleId = R.string.clear_shader_cache,
|
||||
descriptionId = R.string.clear_shader_cache_warning_description,
|
||||
positiveAction = {
|
||||
shaderCacheDir.deleteRecursively()
|
||||
Toast.makeText(
|
||||
YuzuApplication.appContext,
|
||||
R.string.cleared_shaders_successfully,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
homeViewModel.reloadPropertiesList(true)
|
||||
}
|
||||
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
) {
|
||||
MessageDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
titleId = R.string.clear_shader_cache,
|
||||
descriptionId = R.string.clear_shader_cache_warning_description,
|
||||
positiveAction = {
|
||||
shaderCacheDir.deleteRecursively()
|
||||
Toast.makeText(
|
||||
YuzuApplication.appContext,
|
||||
R.string.cleared_shaders_successfully,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
homeViewModel.reloadPropertiesList(true)
|
||||
}
|
||||
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -284,6 +311,7 @@ class GamePropertiesFragment : Fragment() {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
driverViewModel.updateDriverNameForGame(args.game)
|
||||
reloadList()
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
|
@ -420,4 +448,30 @@ class GamePropertiesFragment : Fragment() {
|
|||
}
|
||||
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
private fun shareConfigFile(configFile: File) {
|
||||
val file = DocumentFile.fromSingleUri(
|
||||
requireContext(),
|
||||
DocumentsContract.buildDocumentUri(
|
||||
DocumentProvider.AUTHORITY,
|
||||
"${DocumentProvider.ROOT_ID}/${configFile}"
|
||||
)
|
||||
)!!
|
||||
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
.setDataAndType(file.uri, FileUtil.TEXT_PLAIN)
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
if (file.exists()) {
|
||||
intent.putExtra(Intent.EXTRA_STREAM, file.uri)
|
||||
startActivity(Intent.createChooser(intent, getText(R.string.share_game_settings)))
|
||||
} else {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
getText(R.string.share_config_failed),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,9 +24,17 @@ data class SubmenuProperty(
|
|||
override val iconId: Int,
|
||||
val details: (() -> String)? = null,
|
||||
val detailsFlow: StateFlow<String>? = null,
|
||||
val action: () -> Unit
|
||||
val action: () -> Unit,
|
||||
val secondaryAction: SubMenuProperSecondaryAction? = null
|
||||
) : GameProperty
|
||||
|
||||
data class SubMenuProperSecondaryAction(
|
||||
val isShown : Boolean,
|
||||
val descriptionId: Int,
|
||||
val iconId: Int,
|
||||
val action: () -> Unit
|
||||
)
|
||||
|
||||
data class InstallableProperty(
|
||||
override val titleId: Int,
|
||||
override val descriptionId: Int,
|
||||
|
|
|
@ -67,6 +67,17 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/buttonSecondaryAction"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
app:iconSize="20dp"
|
||||
tools:visibility="visible"
|
||||
tools:icon="@drawable/ic_info_outline" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
|
|
@ -752,6 +752,9 @@
|
|||
<string name="updates_and_dlc">Updates and DLC</string>
|
||||
<string name="mods_and_cheats">Mods and cheats</string>
|
||||
<string name="addon_notice">Important addon notice</string>
|
||||
<string name="share_game_settings">Share Game Settings</string>
|
||||
<string name="share_config_failed">Failed to share configuration file</string>
|
||||
|
||||
<!-- "cheats/" "romfs/" and "exefs/ should not be translated -->
|
||||
<string name="addon_notice_description">In order to install mods and cheats, you must select a folder that contains a cheats/, romfs/, or exefs/ directory. We can\'t verify if these will be compatible with your game so be careful!</string>
|
||||
<string name="invalid_directory">Invalid directory</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue