mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-19 10:43:33 +00:00
Merge branch 'list-view-and-applets-settings' into 'master'
feat: Add list view toggle and separate applets settings category See merge request citron/rewrite!50
This commit is contained in:
@@ -23,6 +23,7 @@ import org.citron.citron_emu.HomeNavigationDirections
|
|||||||
import org.citron.citron_emu.R
|
import org.citron.citron_emu.R
|
||||||
import org.citron.citron_emu.CitronApplication
|
import org.citron.citron_emu.CitronApplication
|
||||||
import org.citron.citron_emu.databinding.CardGameBinding
|
import org.citron.citron_emu.databinding.CardGameBinding
|
||||||
|
import org.citron.citron_emu.databinding.CardGameListBinding
|
||||||
import org.citron.citron_emu.model.Game
|
import org.citron.citron_emu.model.Game
|
||||||
import org.citron.citron_emu.model.GamesViewModel
|
import org.citron.citron_emu.model.GamesViewModel
|
||||||
import org.citron.citron_emu.utils.GameIconUtils
|
import org.citron.citron_emu.utils.GameIconUtils
|
||||||
@@ -30,13 +31,41 @@ import org.citron.citron_emu.utils.ViewUtils.marquee
|
|||||||
import org.citron.citron_emu.viewholder.AbstractViewHolder
|
import org.citron.citron_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class GameAdapter(private val activity: AppCompatActivity) :
|
class GameAdapter(private val activity: AppCompatActivity) :
|
||||||
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>(exact = false) {
|
AbstractDiffAdapter<Game, AbstractViewHolder<Game>>(exact = false) {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
|
|
||||||
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
companion object {
|
||||||
.also { return GameViewHolder(it) }
|
const val VIEW_TYPE_GRID = 0
|
||||||
|
const val VIEW_TYPE_LIST = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class GameViewHolder(val binding: CardGameBinding) :
|
private var isListView = false
|
||||||
|
|
||||||
|
fun setListView(listView: Boolean) {
|
||||||
|
if (isListView != listView) {
|
||||||
|
isListView = listView
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return if (isListView) VIEW_TYPE_LIST else VIEW_TYPE_GRID
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractViewHolder<Game> {
|
||||||
|
return when (viewType) {
|
||||||
|
VIEW_TYPE_GRID -> {
|
||||||
|
val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
GameGridViewHolder(binding)
|
||||||
|
}
|
||||||
|
VIEW_TYPE_LIST -> {
|
||||||
|
val binding = CardGameListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
GameListViewHolder(binding)
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("Invalid view type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class GameGridViewHolder(val binding: CardGameBinding) :
|
||||||
AbstractViewHolder<Game>(binding) {
|
AbstractViewHolder<Game>(binding) {
|
||||||
override fun bind(model: Game) {
|
override fun bind(model: Game) {
|
||||||
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
|
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
|
||||||
@@ -50,50 +79,79 @@ class GameAdapter(private val activity: AppCompatActivity) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onClick(game: Game) {
|
fun onClick(game: Game) {
|
||||||
val gameExists = DocumentFile.fromSingleUri(
|
handleGameClick(game)
|
||||||
CitronApplication.appContext,
|
|
||||||
Uri.parse(game.path)
|
|
||||||
)?.exists() == true
|
|
||||||
if (!gameExists) {
|
|
||||||
Toast.makeText(
|
|
||||||
CitronApplication.appContext,
|
|
||||||
R.string.loader_error_file_not_found,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
|
|
||||||
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val preferences =
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(CitronApplication.appContext)
|
|
||||||
preferences.edit()
|
|
||||||
.putLong(
|
|
||||||
game.keyLastPlayedTime,
|
|
||||||
System.currentTimeMillis()
|
|
||||||
)
|
|
||||||
.apply()
|
|
||||||
|
|
||||||
activity.lifecycleScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val shortcut =
|
|
||||||
ShortcutInfoCompat.Builder(CitronApplication.appContext, game.path)
|
|
||||||
.setShortLabel(game.title)
|
|
||||||
.setIcon(GameIconUtils.getShortcutIcon(activity, game))
|
|
||||||
.setIntent(game.launchIntent)
|
|
||||||
.build()
|
|
||||||
ShortcutManagerCompat.pushDynamicShortcut(CitronApplication.appContext, shortcut)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true)
|
|
||||||
binding.root.findNavController().navigate(action)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onLongClick(game: Game): Boolean {
|
fun onLongClick(game: Game): Boolean {
|
||||||
val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game)
|
return handleGameLongClick(game)
|
||||||
binding.root.findNavController().navigate(action)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner class GameListViewHolder(val binding: CardGameListBinding) :
|
||||||
|
AbstractViewHolder<Game>(binding) {
|
||||||
|
override fun bind(model: Game) {
|
||||||
|
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
|
||||||
|
GameIconUtils.loadGameIcon(model, binding.imageGameScreen)
|
||||||
|
|
||||||
|
binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
|
||||||
|
|
||||||
|
binding.cardGame.setOnClickListener { onClick(model) }
|
||||||
|
binding.cardGame.setOnLongClickListener { onLongClick(model) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onClick(game: Game) {
|
||||||
|
handleGameClick(game)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onLongClick(game: Game): Boolean {
|
||||||
|
return handleGameLongClick(game)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleGameClick(game: Game) {
|
||||||
|
val gameExists = DocumentFile.fromSingleUri(
|
||||||
|
CitronApplication.appContext,
|
||||||
|
Uri.parse(game.path)
|
||||||
|
)?.exists() == true
|
||||||
|
if (!gameExists) {
|
||||||
|
Toast.makeText(
|
||||||
|
CitronApplication.appContext,
|
||||||
|
R.string.loader_error_file_not_found,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
|
||||||
|
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val preferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(CitronApplication.appContext)
|
||||||
|
preferences.edit()
|
||||||
|
.putLong(
|
||||||
|
game.keyLastPlayedTime,
|
||||||
|
System.currentTimeMillis()
|
||||||
|
)
|
||||||
|
.apply()
|
||||||
|
|
||||||
|
activity.lifecycleScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val shortcut =
|
||||||
|
ShortcutInfoCompat.Builder(CitronApplication.appContext, game.path)
|
||||||
|
.setShortLabel(game.title)
|
||||||
|
.setIcon(GameIconUtils.getShortcutIcon(activity, game))
|
||||||
|
.setIntent(game.launchIntent)
|
||||||
|
.build()
|
||||||
|
ShortcutManagerCompat.pushDynamicShortcut(CitronApplication.appContext, shortcut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true)
|
||||||
|
activity.findNavController(R.id.fragment_container).navigate(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleGameLongClick(game: Game): Boolean {
|
||||||
|
val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game)
|
||||||
|
activity.findNavController(R.id.fragment_container).navigate(action)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ object Settings {
|
|||||||
SECTION_INPUT_PLAYER_EIGHT,
|
SECTION_INPUT_PLAYER_EIGHT,
|
||||||
SECTION_THEME(R.string.preferences_theme),
|
SECTION_THEME(R.string.preferences_theme),
|
||||||
SECTION_DEBUG(R.string.preferences_debug),
|
SECTION_DEBUG(R.string.preferences_debug),
|
||||||
SECTION_ZEP_ZONE(R.string.preferences_zep_zone);
|
SECTION_ZEP_ZONE(R.string.preferences_zep_zone),
|
||||||
|
SECTION_APPLETS_ANDROID(R.string.preferences_applets_android);
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPlayerString(player: Int): String =
|
fun getPlayerString(player: Int): String =
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import org.citron.citron_emu.utils.NativeConfig
|
|||||||
|
|
||||||
enum class StringSetting(override val key: String) : AbstractStringSetting {
|
enum class StringSetting(override val key: String) : AbstractStringSetting {
|
||||||
DRIVER_PATH("driver_path"),
|
DRIVER_PATH("driver_path"),
|
||||||
DEVICE_NAME("device_name");
|
DEVICE_NAME("device_name"),
|
||||||
|
LOG_FILTER("log_filter");
|
||||||
|
|
||||||
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
|
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ class SettingsFragmentPresenter(
|
|||||||
MenuTag.SECTION_THEME -> addThemeSettings(sl)
|
MenuTag.SECTION_THEME -> addThemeSettings(sl)
|
||||||
MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
|
MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
|
||||||
MenuTag.SECTION_ZEP_ZONE -> addZepZoneSettings(sl)
|
MenuTag.SECTION_ZEP_ZONE -> addZepZoneSettings(sl)
|
||||||
|
MenuTag.SECTION_APPLETS_ANDROID -> addAppletsAndroidSettings(sl)
|
||||||
}
|
}
|
||||||
settingsList = sl
|
settingsList = sl
|
||||||
adapter.submitList(settingsList) {
|
adapter.submitList(settingsList) {
|
||||||
@@ -151,6 +152,14 @@ class SettingsFragmentPresenter(
|
|||||||
menuKey = MenuTag.SECTION_ZEP_ZONE
|
menuKey = MenuTag.SECTION_ZEP_ZONE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
add(
|
||||||
|
SubmenuSetting(
|
||||||
|
titleId = R.string.preferences_applets_android,
|
||||||
|
descriptionId = R.string.preferences_applets_android_description,
|
||||||
|
iconId = R.drawable.ic_applet,
|
||||||
|
menuKey = MenuTag.SECTION_APPLETS_ANDROID
|
||||||
|
)
|
||||||
|
)
|
||||||
add(
|
add(
|
||||||
RunnableSetting(
|
RunnableSetting(
|
||||||
titleId = R.string.reset_to_default,
|
titleId = R.string.reset_to_default,
|
||||||
@@ -981,6 +990,15 @@ class SettingsFragmentPresenter(
|
|||||||
add(IntSetting.CPU_ACCURACY.key)
|
add(IntSetting.CPU_ACCURACY.key)
|
||||||
add(BooleanSetting.CPU_DEBUG_MODE.key)
|
add(BooleanSetting.CPU_DEBUG_MODE.key)
|
||||||
add(SettingsItem.FASTMEM_COMBINED)
|
add(SettingsItem.FASTMEM_COMBINED)
|
||||||
|
|
||||||
|
add(HeaderSetting(R.string.logging))
|
||||||
|
add(
|
||||||
|
StringInputSetting(
|
||||||
|
StringSetting.LOG_FILTER,
|
||||||
|
titleId = R.string.log_filter,
|
||||||
|
descriptionId = R.string.log_filter_description
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1018,11 @@ class SettingsFragmentPresenter(
|
|||||||
add(HeaderSetting(R.string.frame_skipping_header))
|
add(HeaderSetting(R.string.frame_skipping_header))
|
||||||
add(IntSetting.FRAME_SKIPPING.key)
|
add(IntSetting.FRAME_SKIPPING.key)
|
||||||
add(IntSetting.FRAME_SKIPPING_MODE.key)
|
add(IntSetting.FRAME_SKIPPING_MODE.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addAppletsAndroidSettings(sl: ArrayList<SettingsItem>) {
|
||||||
|
sl.apply {
|
||||||
add(HeaderSetting(R.string.applet_settings_header))
|
add(HeaderSetting(R.string.applet_settings_header))
|
||||||
add(IntSetting.CABINET_APPLET_MODE.key)
|
add(IntSetting.CABINET_APPLET_MODE.key)
|
||||||
add(IntSetting.CONTROLLER_APPLET_MODE.key)
|
add(IntSetting.CONTROLLER_APPLET_MODE.key)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import org.citron.citron_emu.R
|
import org.citron.citron_emu.R
|
||||||
import org.citron.citron_emu.adapters.GameAdapter
|
import org.citron.citron_emu.adapters.GameAdapter
|
||||||
@@ -31,6 +32,9 @@ class GamesFragment : Fragment() {
|
|||||||
private val gamesViewModel: GamesViewModel by activityViewModels()
|
private val gamesViewModel: GamesViewModel by activityViewModels()
|
||||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
|
|
||||||
|
private lateinit var gameAdapter: GameAdapter
|
||||||
|
private var isListView = false
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
@@ -45,12 +49,19 @@ class GamesFragment : Fragment() {
|
|||||||
homeViewModel.setNavigationVisibility(visible = true, animated = true)
|
homeViewModel.setNavigationVisibility(visible = true, animated = true)
|
||||||
homeViewModel.setStatusBarShadeVisibility(true)
|
homeViewModel.setStatusBarShadeVisibility(true)
|
||||||
|
|
||||||
|
gameAdapter = GameAdapter(requireActivity() as AppCompatActivity)
|
||||||
|
|
||||||
binding.gridGames.apply {
|
binding.gridGames.apply {
|
||||||
layoutManager = AutofitGridLayoutManager(
|
layoutManager = AutofitGridLayoutManager(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
requireContext().resources.getDimensionPixelSize(R.dimen.card_width)
|
requireContext().resources.getDimensionPixelSize(R.dimen.card_width)
|
||||||
)
|
)
|
||||||
adapter = GameAdapter(requireActivity() as AppCompatActivity)
|
adapter = gameAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up button for view switching
|
||||||
|
binding.btnViewToggle.setOnClickListener {
|
||||||
|
toggleViewMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.swipeRefresh.apply {
|
binding.swipeRefresh.apply {
|
||||||
@@ -90,14 +101,14 @@ class GamesFragment : Fragment() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
gamesViewModel.games.collect(viewLifecycleOwner) {
|
gamesViewModel.games.collect(viewLifecycleOwner) {
|
||||||
(binding.gridGames.adapter as GameAdapter).submitList(it)
|
gameAdapter.submitList(it)
|
||||||
}
|
}
|
||||||
gamesViewModel.shouldSwapData.collect(
|
gamesViewModel.shouldSwapData.collect(
|
||||||
viewLifecycleOwner,
|
viewLifecycleOwner,
|
||||||
resetState = { gamesViewModel.setShouldSwapData(false) }
|
resetState = { gamesViewModel.setShouldSwapData(false) }
|
||||||
) {
|
) {
|
||||||
if (it) {
|
if (it) {
|
||||||
(binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value)
|
gameAdapter.submitList(gamesViewModel.games.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gamesViewModel.shouldScrollToTop.collect(
|
gamesViewModel.shouldScrollToTop.collect(
|
||||||
@@ -108,6 +119,27 @@ class GamesFragment : Fragment() {
|
|||||||
setInsets()
|
setInsets()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun toggleViewMode() {
|
||||||
|
isListView = !isListView
|
||||||
|
|
||||||
|
if (isListView) {
|
||||||
|
// Switch to list view
|
||||||
|
binding.gridGames.layoutManager = LinearLayoutManager(requireContext())
|
||||||
|
binding.btnViewToggle.setIconResource(R.drawable.ic_view_grid)
|
||||||
|
binding.btnViewToggle.contentDescription = getString(R.string.switch_to_grid_view)
|
||||||
|
} else {
|
||||||
|
// Switch to grid view
|
||||||
|
binding.gridGames.layoutManager = AutofitGridLayoutManager(
|
||||||
|
requireContext(),
|
||||||
|
requireContext().resources.getDimensionPixelSize(R.dimen.card_width)
|
||||||
|
)
|
||||||
|
binding.btnViewToggle.setIconResource(R.drawable.ic_view_list)
|
||||||
|
binding.btnViewToggle.contentDescription = getString(R.string.switch_to_list_view)
|
||||||
|
}
|
||||||
|
|
||||||
|
gameAdapter.setListView(isListView)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
_binding = null
|
_binding = null
|
||||||
@@ -155,6 +187,14 @@ class GamesFragment : Fragment() {
|
|||||||
|
|
||||||
binding.noticeText.updatePadding(bottom = spacingNavigation)
|
binding.noticeText.updatePadding(bottom = spacingNavigation)
|
||||||
|
|
||||||
|
// Update button margins
|
||||||
|
val buttonSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
|
||||||
|
binding.btnViewToggle.updateMargins(
|
||||||
|
left = leftInsets + buttonSpacing,
|
||||||
|
right = rightInsets + buttonSpacing,
|
||||||
|
bottom = barInsets.bottom + spacingNavigation + buttonSpacing
|
||||||
|
)
|
||||||
|
|
||||||
windowInsets
|
windowInsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/android/app/src/main/res/drawable/ic_view_grid.xml
Normal file
10
src/android/app/src/main/res/drawable/ic_view_grid.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorOnSurface">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,3v8h8L11,3L3,3zM9,9L5,9L5,5h4v4zM3,13v8h8v-8L3,13zM9,19L5,19v-4h4v4zM13,3v8h8L21,3h-8zM19,9h-4L15,5h4v4zM13,13v8h8v-8h-8zM19,19h-4v-4h4v4z"/>
|
||||||
|
</vector>
|
||||||
10
src/android/app/src/main/res/drawable/ic_view_list.xml
Normal file
10
src/android/app/src/main/res/drawable/ic_view_list.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorOnSurface">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,13h2v-2L3,11v2zM3,17h2v-2L3,15v2zM3,9h2L5,7L3,7v2zM7,13h14v-2L7,11v2zM7,17h14v-2L7,15v2zM7,7v2h14L21,7L7,7z"/>
|
||||||
|
</vector>
|
||||||
54
src/android/app/src/main/res/layout/card_game_list.xml
Normal file
54
src/android/app/src/main/res/layout/card_game_list.xml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/card_game"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
android:layout_marginVertical="4dp"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:focusable="true"
|
||||||
|
android:transitionName="card_game"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
app:cardElevation="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/image_game_screen"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.Small"
|
||||||
|
tools:src="@drawable/default_icon" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/text_game_title"
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="The Legend of Zelda: Skyward Sword" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/swipe_refresh"
|
android:id="@+id/swipe_refresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@@ -30,6 +31,18 @@
|
|||||||
android:defaultFocusHighlightEnabled="false"
|
android:defaultFocusHighlightEnabled="false"
|
||||||
tools:listitem="@layout/card_game" />
|
tools:listitem="@layout/card_game" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_view_toggle"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:contentDescription="@string/switch_to_list_view"
|
||||||
|
app:icon="@drawable/ic_view_list"
|
||||||
|
app:iconSize="24dp" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
<string name="home_settings">Settings</string>
|
<string name="home_settings">Settings</string>
|
||||||
<string name="empty_gamelist">No files were found or no game directory has been selected yet.</string>
|
<string name="empty_gamelist">No files were found or no game directory has been selected yet.</string>
|
||||||
<string name="search_and_filter_games">Search and filter games</string>
|
<string name="search_and_filter_games">Search and filter games</string>
|
||||||
|
<string name="switch_to_list_view">Switch to list view</string>
|
||||||
|
<string name="switch_to_grid_view">Switch to grid view</string>
|
||||||
<string name="select_games_folder">Select games folder</string>
|
<string name="select_games_folder">Select games folder</string>
|
||||||
<string name="manage_game_folders">Manage game folders</string>
|
<string name="manage_game_folders">Manage game folders</string>
|
||||||
<string name="select_games_folder_description">Allows citron to populate the games list</string>
|
<string name="select_games_folder_description">Allows citron to populate the games list</string>
|
||||||
@@ -256,6 +258,9 @@
|
|||||||
<string name="renderer_debug">Graphics debugging</string>
|
<string name="renderer_debug">Graphics debugging</string>
|
||||||
<string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string>
|
<string name="renderer_debug_description">Sets the graphics API to a slow debugging mode.</string>
|
||||||
<string name="fastmem">Fastmem</string>
|
<string name="fastmem">Fastmem</string>
|
||||||
|
<string name="logging">Logging</string>
|
||||||
|
<string name="log_filter">Log Filter</string>
|
||||||
|
<string name="log_filter_description">Configure which log messages to display. Format: <class>:<level>. Example: *:Info Service:Debug</string>
|
||||||
|
|
||||||
<!-- Audio settings strings -->
|
<!-- Audio settings strings -->
|
||||||
<string name="audio_output_engine">Output engine</string>
|
<string name="audio_output_engine">Output engine</string>
|
||||||
@@ -418,6 +423,8 @@
|
|||||||
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
|
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
|
||||||
<string name="preferences_zep_zone">Zep Zone</string>
|
<string name="preferences_zep_zone">Zep Zone</string>
|
||||||
<string name="preferences_zep_zone_description">Advanced emulation settings</string>
|
<string name="preferences_zep_zone_description">Advanced emulation settings</string>
|
||||||
|
<string name="preferences_applets_android">Applets on Android</string>
|
||||||
|
<string name="preferences_applets_android_description">System applet configuration settings</string>
|
||||||
|
|
||||||
<!-- Zep Zone Headers -->
|
<!-- Zep Zone Headers -->
|
||||||
<string name="memory_layout_header">Memory Layout</string>
|
<string name="memory_layout_header">Memory Layout</string>
|
||||||
|
|||||||
@@ -634,7 +634,7 @@ struct Values {
|
|||||||
Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
|
Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous};
|
Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Debugging};
|
||||||
Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
|
Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
|
||||||
|
|
||||||
// Network
|
// Network
|
||||||
|
|||||||
Reference in New Issue
Block a user