mirror of
				https://github.com/yuzu-emu/yuzu-android.git
				synced 2025-11-04 16:04:58 +00:00 
			
		
		
		
	Merge pull request #11931 from t895/applet-launcher
android: Applet launcher UI
This commit is contained in:
		
						commit
						7e284809de
					
				| 
						 | 
				
			
			@ -252,7 +252,7 @@ object NativeLibrary {
 | 
			
		|||
 | 
			
		||||
    external fun reloadKeys(): Boolean
 | 
			
		||||
 | 
			
		||||
    external fun initializeEmulation()
 | 
			
		||||
    external fun initializeSystem()
 | 
			
		||||
 | 
			
		||||
    external fun defaultCPUCore(): Int
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -505,6 +505,36 @@ object NativeLibrary {
 | 
			
		|||
     */
 | 
			
		||||
    external fun initializeEmptyUserDirectory()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the launch path for a given applet. It is the caller's responsibility to also
 | 
			
		||||
     * set the system's current applet ID before trying to launch the nca given by this function.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id The applet entry ID
 | 
			
		||||
     * @return The applet's launch path
 | 
			
		||||
     */
 | 
			
		||||
    external fun getAppletLaunchPath(id: Long): String
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the system's current applet ID before launching.
 | 
			
		||||
     *
 | 
			
		||||
     * @param appletId One of the ids in the Service::AM::Applets::AppletId enum
 | 
			
		||||
     */
 | 
			
		||||
    external fun setCurrentAppletId(appletId: Int)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the cabinet mode for launching the cabinet applet.
 | 
			
		||||
     *
 | 
			
		||||
     * @param cabinetMode One of the modes that corresponds to the enum in Service::NFP::CabinetMode
 | 
			
		||||
     */
 | 
			
		||||
    external fun setCabinetMode(cabinetMode: Int)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks whether NAND contents are available and valid.
 | 
			
		||||
     *
 | 
			
		||||
     * @return 'true' if firmware is available
 | 
			
		||||
     */
 | 
			
		||||
    external fun isFirmwareAvailable(): Boolean
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Button type for use in onTouchEvent
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.adapters
 | 
			
		||||
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.core.content.res.ResourcesCompat
 | 
			
		||||
import androidx.fragment.app.FragmentActivity
 | 
			
		||||
import androidx.navigation.findNavController
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.CardAppletOptionBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Applet
 | 
			
		||||
import org.yuzu.yuzu_emu.model.AppletInfo
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
 | 
			
		||||
class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) :
 | 
			
		||||
    RecyclerView.Adapter<AppletAdapter.AppletViewHolder>(),
 | 
			
		||||
    View.OnClickListener {
 | 
			
		||||
 | 
			
		||||
    override fun onCreateViewHolder(
 | 
			
		||||
        parent: ViewGroup,
 | 
			
		||||
        viewType: Int
 | 
			
		||||
    ): AppletAdapter.AppletViewHolder {
 | 
			
		||||
        CardAppletOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
			
		||||
            .apply { root.setOnClickListener(this@AppletAdapter) }
 | 
			
		||||
            .also { return AppletViewHolder(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onBindViewHolder(holder: AppletViewHolder, position: Int) =
 | 
			
		||||
        holder.bind(applets[position])
 | 
			
		||||
 | 
			
		||||
    override fun getItemCount(): Int = applets.size
 | 
			
		||||
 | 
			
		||||
    override fun onClick(view: View) {
 | 
			
		||||
        val applet = (view.tag as AppletViewHolder).applet
 | 
			
		||||
        val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
 | 
			
		||||
        if (appletPath.isEmpty()) {
 | 
			
		||||
            Toast.makeText(
 | 
			
		||||
                YuzuApplication.appContext,
 | 
			
		||||
                R.string.applets_error_applet,
 | 
			
		||||
                Toast.LENGTH_SHORT
 | 
			
		||||
            ).show()
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (applet.appletInfo == AppletInfo.Cabinet) {
 | 
			
		||||
            view.findNavController()
 | 
			
		||||
                .navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
 | 
			
		||||
        val appletGame = Game(
 | 
			
		||||
            title = YuzuApplication.appContext.getString(applet.titleId),
 | 
			
		||||
            path = appletPath
 | 
			
		||||
        )
 | 
			
		||||
        val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
 | 
			
		||||
        view.findNavController().navigate(action)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inner class AppletViewHolder(val binding: CardAppletOptionBinding) :
 | 
			
		||||
        RecyclerView.ViewHolder(binding.root) {
 | 
			
		||||
        lateinit var applet: Applet
 | 
			
		||||
 | 
			
		||||
        init {
 | 
			
		||||
            itemView.tag = this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun bind(applet: Applet) {
 | 
			
		||||
            this.applet = applet
 | 
			
		||||
 | 
			
		||||
            binding.title.setText(applet.titleId)
 | 
			
		||||
            binding.description.setText(applet.descriptionId)
 | 
			
		||||
            binding.icon.setImageDrawable(
 | 
			
		||||
                ResourcesCompat.getDrawable(
 | 
			
		||||
                    binding.icon.context.resources,
 | 
			
		||||
                    applet.iconId,
 | 
			
		||||
                    binding.icon.context.theme
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.adapters
 | 
			
		||||
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import androidx.core.content.res.ResourcesCompat
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import androidx.navigation.fragment.findNavController
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.DialogListItemBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.model.CabinetMode
 | 
			
		||||
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder
 | 
			
		||||
import org.yuzu.yuzu_emu.model.AppletInfo
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Game
 | 
			
		||||
 | 
			
		||||
class CabinetLauncherDialogAdapter(val fragment: Fragment) :
 | 
			
		||||
    RecyclerView.Adapter<CabinetModeViewHolder>(),
 | 
			
		||||
    View.OnClickListener {
 | 
			
		||||
    private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size)
 | 
			
		||||
 | 
			
		||||
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder {
 | 
			
		||||
        DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
			
		||||
            .apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) }
 | 
			
		||||
            .also { return CabinetModeViewHolder(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getItemCount(): Int = cabinetModes.size
 | 
			
		||||
 | 
			
		||||
    override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) =
 | 
			
		||||
        holder.bind(cabinetModes[position])
 | 
			
		||||
 | 
			
		||||
    override fun onClick(view: View) {
 | 
			
		||||
        val mode = (view.tag as CabinetModeViewHolder).cabinetMode
 | 
			
		||||
        val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
 | 
			
		||||
        NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
 | 
			
		||||
        NativeLibrary.setCabinetMode(mode.id)
 | 
			
		||||
        val appletGame = Game(
 | 
			
		||||
            title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
 | 
			
		||||
            path = appletPath
 | 
			
		||||
        )
 | 
			
		||||
        val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
 | 
			
		||||
        fragment.findNavController().navigate(action)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inner class CabinetModeViewHolder(val binding: DialogListItemBinding) :
 | 
			
		||||
        RecyclerView.ViewHolder(binding.root) {
 | 
			
		||||
        lateinit var cabinetMode: CabinetMode
 | 
			
		||||
 | 
			
		||||
        init {
 | 
			
		||||
            itemView.tag = this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun bind(cabinetMode: CabinetMode) {
 | 
			
		||||
            this.cabinetMode = cabinetMode
 | 
			
		||||
            binding.icon.setImageDrawable(
 | 
			
		||||
                ResourcesCompat.getDrawable(
 | 
			
		||||
                    binding.icon.context.resources,
 | 
			
		||||
                    cabinetMode.iconId,
 | 
			
		||||
                    binding.icon.context.theme
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            binding.title.setText(cabinetMode.titleId)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.fragments
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import androidx.core.view.ViewCompat
 | 
			
		||||
import androidx.core.view.WindowInsetsCompat
 | 
			
		||||
import androidx.core.view.updatePadding
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import androidx.fragment.app.activityViewModels
 | 
			
		||||
import androidx.navigation.findNavController
 | 
			
		||||
import androidx.recyclerview.widget.GridLayoutManager
 | 
			
		||||
import com.google.android.material.transition.MaterialSharedAxis
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.adapters.AppletAdapter
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Applet
 | 
			
		||||
import org.yuzu.yuzu_emu.model.AppletInfo
 | 
			
		||||
import org.yuzu.yuzu_emu.model.HomeViewModel
 | 
			
		||||
 | 
			
		||||
class AppletLauncherFragment : Fragment() {
 | 
			
		||||
    private var _binding: FragmentAppletLauncherBinding? = null
 | 
			
		||||
    private val binding get() = _binding!!
 | 
			
		||||
 | 
			
		||||
    private val homeViewModel: HomeViewModel by activityViewModels()
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
 | 
			
		||||
        returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
 | 
			
		||||
        reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreateView(
 | 
			
		||||
        inflater: LayoutInflater,
 | 
			
		||||
        container: ViewGroup?,
 | 
			
		||||
        savedInstanceState: Bundle?
 | 
			
		||||
    ): View {
 | 
			
		||||
        _binding = FragmentAppletLauncherBinding.inflate(inflater)
 | 
			
		||||
        return binding.root
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState)
 | 
			
		||||
        homeViewModel.setNavigationVisibility(visible = false, animated = true)
 | 
			
		||||
        homeViewModel.setStatusBarShadeVisibility(visible = false)
 | 
			
		||||
 | 
			
		||||
        binding.toolbarApplets.setNavigationOnClickListener {
 | 
			
		||||
            binding.root.findNavController().popBackStack()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val applets = listOf(
 | 
			
		||||
            Applet(
 | 
			
		||||
                R.string.album_applet,
 | 
			
		||||
                R.string.album_applet_description,
 | 
			
		||||
                R.drawable.ic_album,
 | 
			
		||||
                AppletInfo.PhotoViewer
 | 
			
		||||
            ),
 | 
			
		||||
            Applet(
 | 
			
		||||
                R.string.cabinet_applet,
 | 
			
		||||
                R.string.cabinet_applet_description,
 | 
			
		||||
                R.drawable.ic_nfc,
 | 
			
		||||
                AppletInfo.Cabinet
 | 
			
		||||
            ),
 | 
			
		||||
            Applet(
 | 
			
		||||
                R.string.mii_edit_applet,
 | 
			
		||||
                R.string.mii_edit_applet_description,
 | 
			
		||||
                R.drawable.ic_mii,
 | 
			
		||||
                AppletInfo.MiiEdit
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        binding.listApplets.apply {
 | 
			
		||||
            layoutManager = GridLayoutManager(
 | 
			
		||||
                requireContext(),
 | 
			
		||||
                resources.getInteger(R.integer.grid_columns)
 | 
			
		||||
            )
 | 
			
		||||
            adapter = AppletAdapter(requireActivity(), applets)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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())
 | 
			
		||||
 | 
			
		||||
            val leftInsets = barInsets.left + cutoutInsets.left
 | 
			
		||||
            val rightInsets = barInsets.right + cutoutInsets.right
 | 
			
		||||
 | 
			
		||||
            val mlpAppBar = binding.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams
 | 
			
		||||
            mlpAppBar.leftMargin = leftInsets
 | 
			
		||||
            mlpAppBar.rightMargin = rightInsets
 | 
			
		||||
            binding.toolbarApplets.layoutParams = mlpAppBar
 | 
			
		||||
 | 
			
		||||
            val mlpListApplets =
 | 
			
		||||
                binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams
 | 
			
		||||
            mlpListApplets.leftMargin = leftInsets
 | 
			
		||||
            mlpListApplets.rightMargin = rightInsets
 | 
			
		||||
            binding.listApplets.layoutParams = mlpListApplets
 | 
			
		||||
 | 
			
		||||
            binding.listApplets.updatePadding(bottom = barInsets.bottom)
 | 
			
		||||
 | 
			
		||||
            windowInsets
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.fragments
 | 
			
		||||
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import androidx.fragment.app.DialogFragment
 | 
			
		||||
import androidx.recyclerview.widget.LinearLayoutManager
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.DialogListBinding
 | 
			
		||||
 | 
			
		||||
class CabinetLauncherDialogFragment : DialogFragment() {
 | 
			
		||||
    private lateinit var binding: DialogListBinding
 | 
			
		||||
 | 
			
		||||
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
 | 
			
		||||
        binding = DialogListBinding.inflate(layoutInflater)
 | 
			
		||||
        binding.dialogList.apply {
 | 
			
		||||
            layoutManager = LinearLayoutManager(requireContext())
 | 
			
		||||
            adapter = CabinetLauncherDialogAdapter(this@CabinetLauncherDialogFragment)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return MaterialAlertDialogBuilder(requireContext())
 | 
			
		||||
            .setTitle(R.string.cabinet_launcher)
 | 
			
		||||
            .setView(binding.root)
 | 
			
		||||
            .create()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreateView(
 | 
			
		||||
        inflater: LayoutInflater,
 | 
			
		||||
        container: ViewGroup?,
 | 
			
		||||
        savedInstanceState: Bundle?
 | 
			
		||||
    ): View {
 | 
			
		||||
        return binding.root
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ import androidx.recyclerview.widget.GridLayoutManager
 | 
			
		|||
import com.google.android.material.transition.MaterialSharedAxis
 | 
			
		||||
import org.yuzu.yuzu_emu.BuildConfig
 | 
			
		||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
 | 
			
		||||
import org.yuzu.yuzu_emu.NativeLibrary
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
 | 
			
		||||
| 
						 | 
				
			
			@ -131,6 +132,20 @@ class HomeSettingsFragment : Fragment() {
 | 
			
		|||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            add(
 | 
			
		||||
                HomeSetting(
 | 
			
		||||
                    R.string.applets,
 | 
			
		||||
                    R.string.applets_description,
 | 
			
		||||
                    R.drawable.ic_applet,
 | 
			
		||||
                    {
 | 
			
		||||
                        binding.root.findNavController()
 | 
			
		||||
                            .navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment)
 | 
			
		||||
                    },
 | 
			
		||||
                    { NativeLibrary.isFirmwareAvailable() },
 | 
			
		||||
                    R.string.applets_error_firmware,
 | 
			
		||||
                    R.string.applets_error_description
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            add(
 | 
			
		||||
                HomeSetting(
 | 
			
		||||
                    R.string.select_games_folder,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ import android.content.DialogInterface
 | 
			
		|||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.text.Html
 | 
			
		||||
import androidx.fragment.app.DialogFragment
 | 
			
		||||
import androidx.fragment.app.FragmentActivity
 | 
			
		||||
import androidx.fragment.app.activityViewModels
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +33,9 @@ class MessageDialogFragment : DialogFragment() {
 | 
			
		|||
        if (titleId != 0) dialog.setTitle(titleId)
 | 
			
		||||
        if (titleString.isNotEmpty()) dialog.setTitle(titleString)
 | 
			
		||||
 | 
			
		||||
        if (descriptionId != 0) dialog.setMessage(descriptionId)
 | 
			
		||||
        if (descriptionId != 0) {
 | 
			
		||||
            dialog.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY))
 | 
			
		||||
        }
 | 
			
		||||
        if (descriptionString.isNotEmpty()) dialog.setMessage(descriptionString)
 | 
			
		||||
 | 
			
		||||
        if (helpLinkId != 0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
package org.yuzu.yuzu_emu.model
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.DrawableRes
 | 
			
		||||
import androidx.annotation.StringRes
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
 | 
			
		||||
data class Applet(
 | 
			
		||||
    @StringRes val titleId: Int,
 | 
			
		||||
    @StringRes val descriptionId: Int,
 | 
			
		||||
    @DrawableRes val iconId: Int,
 | 
			
		||||
    val appletInfo: AppletInfo,
 | 
			
		||||
    val cabinetMode: CabinetMode = CabinetMode.None
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Combination of Common::AM::Applets::AppletId enum and the entry id
 | 
			
		||||
enum class AppletInfo(val appletId: Int, val entryId: Long = 0) {
 | 
			
		||||
    None(0x00),
 | 
			
		||||
    Application(0x01),
 | 
			
		||||
    OverlayDisplay(0x02),
 | 
			
		||||
    QLaunch(0x03),
 | 
			
		||||
    Starter(0x04),
 | 
			
		||||
    Auth(0x0A),
 | 
			
		||||
    Cabinet(0x0B, 0x0100000000001002),
 | 
			
		||||
    Controller(0x0C),
 | 
			
		||||
    DataErase(0x0D),
 | 
			
		||||
    Error(0x0E),
 | 
			
		||||
    NetConnect(0x0F),
 | 
			
		||||
    ProfileSelect(0x10),
 | 
			
		||||
    SoftwareKeyboard(0x11),
 | 
			
		||||
    MiiEdit(0x12, 0x0100000000001009),
 | 
			
		||||
    Web(0x13),
 | 
			
		||||
    Shop(0x14),
 | 
			
		||||
    PhotoViewer(0x015, 0x010000000000100D),
 | 
			
		||||
    Settings(0x16),
 | 
			
		||||
    OfflineWeb(0x17),
 | 
			
		||||
    LoginShare(0x18),
 | 
			
		||||
    WebAuth(0x19),
 | 
			
		||||
    MyPage(0x1A)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Matches enum in Service::NFP::CabinetMode with extra metadata
 | 
			
		||||
enum class CabinetMode(
 | 
			
		||||
    val id: Int,
 | 
			
		||||
    @StringRes val titleId: Int = 0,
 | 
			
		||||
    @DrawableRes val iconId: Int = 0
 | 
			
		||||
) {
 | 
			
		||||
    None(-1),
 | 
			
		||||
    StartNicknameAndOwnerSettings(0, R.string.cabinet_nickname_and_owner, R.drawable.ic_edit),
 | 
			
		||||
    StartGameDataEraser(1, R.string.cabinet_game_data_eraser, R.drawable.ic_refresh),
 | 
			
		||||
    StartRestorer(2, R.string.cabinet_restorer, R.drawable.ic_restore),
 | 
			
		||||
    StartFormatter(3, R.string.cabinet_formatter, R.drawable.ic_clear)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -11,12 +11,12 @@ import kotlinx.serialization.Serializable
 | 
			
		|||
@Parcelize
 | 
			
		||||
@Serializable
 | 
			
		||||
class Game(
 | 
			
		||||
    val title: String,
 | 
			
		||||
    val title: String = "",
 | 
			
		||||
    val path: String,
 | 
			
		||||
    val programId: String,
 | 
			
		||||
    val developer: String,
 | 
			
		||||
    val version: String,
 | 
			
		||||
    val isHomebrew: Boolean
 | 
			
		||||
    val programId: String = "",
 | 
			
		||||
    val developer: String = "",
 | 
			
		||||
    val version: String = "",
 | 
			
		||||
    val isHomebrew: Boolean = false
 | 
			
		||||
) : Parcelable {
 | 
			
		||||
    val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime"
 | 
			
		||||
    val keyLastPlayedTime get() = "${programId}_LastPlayed"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -403,6 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		|||
                    } else {
 | 
			
		||||
                        firmwarePath.deleteRecursively()
 | 
			
		||||
                        cacheFirmwareDir.copyRecursively(firmwarePath, true)
 | 
			
		||||
                        NativeLibrary.initializeSystem()
 | 
			
		||||
                        getString(R.string.save_file_imported_success)
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (e: Exception) {
 | 
			
		||||
| 
						 | 
				
			
			@ -648,7 +649,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
			
		|||
                }
 | 
			
		||||
 | 
			
		||||
                // Reinitialize relevant data
 | 
			
		||||
                NativeLibrary.initializeEmulation()
 | 
			
		||||
                NativeLibrary.initializeSystem()
 | 
			
		||||
                gamesViewModel.reloadGames(false)
 | 
			
		||||
 | 
			
		||||
                return@newInstance getString(R.string.user_data_import_success)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ object DirectoryInitialization {
 | 
			
		|||
    fun start() {
 | 
			
		||||
        if (!areDirectoriesReady) {
 | 
			
		||||
            initializeInternalStorage()
 | 
			
		||||
            NativeLibrary.initializeEmulation()
 | 
			
		||||
            NativeLibrary.initializeSystem()
 | 
			
		||||
            areDirectoriesReady = true
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -247,6 +247,17 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmulationSession::InitializeSystem() {
 | 
			
		||||
    // Initialize filesystem.
 | 
			
		||||
    m_system.SetFilesystem(m_vfs);
 | 
			
		||||
    m_system.GetUserChannel().clear();
 | 
			
		||||
    m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
 | 
			
		||||
    m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
 | 
			
		||||
    m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
 | 
			
		||||
                                     m_manual_provider.get());
 | 
			
		||||
    m_system.GetFileSystemController().CreateFactories(*m_vfs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) {
 | 
			
		||||
    std::scoped_lock lock(m_mutex);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -254,9 +265,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
 | 
			
		|||
    m_window =
 | 
			
		||||
        std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
 | 
			
		||||
 | 
			
		||||
    m_system.SetFilesystem(m_vfs);
 | 
			
		||||
    m_system.GetUserChannel().clear();
 | 
			
		||||
 | 
			
		||||
    // Initialize system.
 | 
			
		||||
    jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
 | 
			
		||||
    m_software_keyboard = android_keyboard.get();
 | 
			
		||||
| 
						 | 
				
			
			@ -277,11 +285,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
 | 
			
		|||
    });
 | 
			
		||||
 | 
			
		||||
    // Initialize filesystem.
 | 
			
		||||
    m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
 | 
			
		||||
    m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
 | 
			
		||||
    m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
 | 
			
		||||
                                     m_manual_provider.get());
 | 
			
		||||
    m_system.GetFileSystemController().CreateFactories(*m_vfs);
 | 
			
		||||
    ConfigureFilesystemProvider(filepath);
 | 
			
		||||
 | 
			
		||||
    // Initialize account manager
 | 
			
		||||
| 
						 | 
				
			
			@ -663,11 +666,12 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
 | 
			
		||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz) {
 | 
			
		||||
    // Create the default config.ini.
 | 
			
		||||
    Config{};
 | 
			
		||||
    // Initialize the emulated system.
 | 
			
		||||
    EmulationSession::GetInstance().System().Initialize();
 | 
			
		||||
    EmulationSession::GetInstance().InitializeSystem();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
 | 
			
		||||
| 
						 | 
				
			
			@ -755,4 +759,49 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, jclass clazz,
 | 
			
		||||
                                                                  jlong jid) {
 | 
			
		||||
    auto bis_system =
 | 
			
		||||
        EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
 | 
			
		||||
    if (!bis_system) {
 | 
			
		||||
        return ToJString(env, "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto applet_nca =
 | 
			
		||||
        bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program);
 | 
			
		||||
    if (!applet_nca) {
 | 
			
		||||
        return ToJString(env, "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ToJString(env, applet_nca->GetFullPath());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz,
 | 
			
		||||
                                                              jint jappletId) {
 | 
			
		||||
    EmulationSession::GetInstance().System().GetAppletManager().SetCurrentAppletId(
 | 
			
		||||
        static_cast<Service::AM::Applets::AppletId>(jappletId));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass clazz,
 | 
			
		||||
                                                          jint jcabinetMode) {
 | 
			
		||||
    EmulationSession::GetInstance().System().GetAppletManager().SetCabinetMode(
 | 
			
		||||
        static_cast<Service::NFP::CabinetMode>(jcabinetMode));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env, jclass clazz) {
 | 
			
		||||
    auto bis_system =
 | 
			
		||||
        EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
 | 
			
		||||
    if (!bis_system) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Query an applet to see if it's available
 | 
			
		||||
    auto applet_nca =
 | 
			
		||||
        bis_system->GetEntry(0x010000000000100Dull, FileSys::ContentRecordType::Program);
 | 
			
		||||
    if (!applet_nca) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // extern "C"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ public:
 | 
			
		|||
 | 
			
		||||
    const Core::PerfStatsResults& PerfStats() const;
 | 
			
		||||
    void ConfigureFilesystemProvider(const std::string& filepath);
 | 
			
		||||
    void InitializeSystem();
 | 
			
		||||
    Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
 | 
			
		||||
 | 
			
		||||
    bool IsHandheldOnly();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_album.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_album.xml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="?attr/colorControlNormal"
 | 
			
		||||
        android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_applet.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_applet.xml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="?attr/colorControlNormal"
 | 
			
		||||
        android:pathData="M17,16l-4,-4V8.82C14.16,8.4 15,7.3 15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6c0,1.3 0.84,2.4 2,2.82V12l-4,4H3v5h5v-3.05l4,-4.2 4,4.2V21h5v-5h-4z" />
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_edit.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_edit.xml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="?attr/colorControlNormal"
 | 
			
		||||
        android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										18
									
								
								src/android/app/src/main/res/drawable/ic_mii.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/android/app/src/main/res/drawable/ic_mii.xml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="?attr/colorControlNormal"
 | 
			
		||||
        android:pathData="M9,13m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="?attr/colorControlNormal"
 | 
			
		||||
        android:pathData="M20.77,8.58l-0.92,2.01c0.09,0.46 0.15,0.93 0.15,1.41 0,4.41 -3.59,8 -8,8s-8,-3.59 -8,-8c0,-0.05 0.01,-0.1 0,-0.14 2.6,-0.98 4.69,-2.99 5.74,-5.55C11.58,8.56 14.37,10 17.5,10c0.45,0 0.89,-0.04 1.33,-0.1l-0.6,-1.32 -0.88,-1.93 -1.93,-0.88 -2.79,-1.27 2.79,-1.27 0.71,-0.32C14.87,2.33 13.47,2 12,2 6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10c0,-1.47 -0.33,-2.87 -0.9,-4.13l-0.33,0.71z" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="?attr/colorControlNormal"
 | 
			
		||||
        android:pathData="M15,13m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0" />
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="?attr/colorControlNormal"
 | 
			
		||||
        android:pathData="M20.6,5.6L19.5,8l-1.1,-2.4L16,4.5l2.4,-1.1L19.5,1l1.1,2.4L23,4.5z" />
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_refresh.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_refresh.xml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="?attr/colorControlNormal"
 | 
			
		||||
        android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z" />
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_restore.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/android/app/src/main/res/drawable/ic_restore.xml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="?attr/colorControlNormal"
 | 
			
		||||
        android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z" />
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										57
									
								
								src/android/app/src/main/res/layout/card_applet_option.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/android/app/src/main/res/layout/card_applet_option.xml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<com.google.android.material.card.MaterialCardView 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"
 | 
			
		||||
    style="?attr/materialCardViewOutlinedStyle"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="wrap_content"
 | 
			
		||||
    android:layout_marginHorizontal="16dp"
 | 
			
		||||
    android:layout_marginVertical="12dp"
 | 
			
		||||
    android:background="?attr/selectableItemBackground"
 | 
			
		||||
    android:clickable="true"
 | 
			
		||||
    android:focusable="true">
 | 
			
		||||
 | 
			
		||||
    <LinearLayout
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:orientation="horizontal"
 | 
			
		||||
        android:layout_gravity="center"
 | 
			
		||||
        android:padding="24dp">
 | 
			
		||||
 | 
			
		||||
        <ImageView
 | 
			
		||||
            android:id="@+id/icon"
 | 
			
		||||
            android:layout_width="24dp"
 | 
			
		||||
            android:layout_height="24dp"
 | 
			
		||||
            android:layout_marginEnd="20dp"
 | 
			
		||||
            android:layout_gravity="center_vertical"
 | 
			
		||||
            app:tint="?attr/colorOnSurface" />
 | 
			
		||||
 | 
			
		||||
        <LinearLayout
 | 
			
		||||
            android:layout_width="0dp"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:layout_weight="1"
 | 
			
		||||
            android:orientation="vertical"
 | 
			
		||||
            android:layout_gravity="center_vertical">
 | 
			
		||||
 | 
			
		||||
            <com.google.android.material.textview.MaterialTextView
 | 
			
		||||
                android:id="@+id/title"
 | 
			
		||||
                style="@style/TextAppearance.Material3.TitleMedium"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:textAlignment="viewStart"
 | 
			
		||||
                tools:text="@string/applets" />
 | 
			
		||||
 | 
			
		||||
            <com.google.android.material.textview.MaterialTextView
 | 
			
		||||
                android:id="@+id/description"
 | 
			
		||||
                style="@style/TextAppearance.Material3.BodyMedium"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:layout_marginTop="6dp"
 | 
			
		||||
                android:textAlignment="viewStart"
 | 
			
		||||
                tools:text="@string/applets_description" />
 | 
			
		||||
 | 
			
		||||
        </LinearLayout>
 | 
			
		||||
 | 
			
		||||
    </LinearLayout>
 | 
			
		||||
 | 
			
		||||
</com.google.android.material.card.MaterialCardView>
 | 
			
		||||
							
								
								
									
										15
									
								
								src/android/app/src/main/res/layout/dialog_list.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/android/app/src/main/res/layout/dialog_list.xml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="wrap_content">
 | 
			
		||||
 | 
			
		||||
    <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
        android:id="@+id/dialog_list"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:clipToPadding="false"
 | 
			
		||||
        android:fadeScrollbars="false"
 | 
			
		||||
        android:paddingVertical="12dp"
 | 
			
		||||
        android:scrollbars="vertical" />
 | 
			
		||||
 | 
			
		||||
</androidx.appcompat.widget.LinearLayoutCompat>
 | 
			
		||||
							
								
								
									
										30
									
								
								src/android/app/src/main/res/layout/dialog_list_item.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/android/app/src/main/res/layout/dialog_list_item.xml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="wrap_content"
 | 
			
		||||
    android:background="?attr/selectableItemBackground"
 | 
			
		||||
    android:clickable="true"
 | 
			
		||||
    android:focusable="true"
 | 
			
		||||
    android:orientation="horizontal"
 | 
			
		||||
    android:paddingHorizontal="24dp"
 | 
			
		||||
    android:paddingVertical="16dp">
 | 
			
		||||
 | 
			
		||||
    <ImageView
 | 
			
		||||
        android:id="@+id/icon"
 | 
			
		||||
        android:layout_width="20dp"
 | 
			
		||||
        android:layout_height="20dp"
 | 
			
		||||
        android:layout_gravity="center"
 | 
			
		||||
        tools:src="@drawable/ic_nfc" />
 | 
			
		||||
 | 
			
		||||
    <com.google.android.material.textview.MaterialTextView
 | 
			
		||||
        android:id="@+id/title"
 | 
			
		||||
        style="@style/TextAppearance.Material3.BodyMedium"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_marginStart="16dp"
 | 
			
		||||
        android:layout_gravity="center_vertical|start"
 | 
			
		||||
        android:textAlignment="viewStart"
 | 
			
		||||
        tools:text="List option" />
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
    android:id="@+id/coordinator_applets"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="match_parent"
 | 
			
		||||
    android:background="?attr/colorSurface">
 | 
			
		||||
 | 
			
		||||
    <com.google.android.material.appbar.AppBarLayout
 | 
			
		||||
        android:id="@+id/appbar_applets"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:fitsSystemWindows="true">
 | 
			
		||||
 | 
			
		||||
        <com.google.android.material.appbar.MaterialToolbar
 | 
			
		||||
            android:id="@+id/toolbar_applets"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="?attr/actionBarSize"
 | 
			
		||||
            app:navigationIcon="@drawable/ic_back"
 | 
			
		||||
            app:title="@string/applets" />
 | 
			
		||||
 | 
			
		||||
    </com.google.android.material.appbar.AppBarLayout>
 | 
			
		||||
 | 
			
		||||
    <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
        android:id="@+id/list_applets"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="match_parent"
 | 
			
		||||
        android:clipToPadding="false"
 | 
			
		||||
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 | 
			
		||||
 | 
			
		||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +25,9 @@
 | 
			
		|||
        <action
 | 
			
		||||
            android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
 | 
			
		||||
            app:destination="@id/driverManagerFragment" />
 | 
			
		||||
        <action
 | 
			
		||||
            android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment"
 | 
			
		||||
            app:destination="@id/appletLauncherFragment" />
 | 
			
		||||
    </fragment>
 | 
			
		||||
 | 
			
		||||
    <fragment
 | 
			
		||||
| 
						 | 
				
			
			@ -102,5 +105,17 @@
 | 
			
		|||
        android:id="@+id/driverManagerFragment"
 | 
			
		||||
        android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
 | 
			
		||||
        android:label="DriverManagerFragment" />
 | 
			
		||||
    <fragment
 | 
			
		||||
        android:id="@+id/appletLauncherFragment"
 | 
			
		||||
        android:name="org.yuzu.yuzu_emu.fragments.AppletLauncherFragment"
 | 
			
		||||
        android:label="AppletLauncherFragment" >
 | 
			
		||||
        <action
 | 
			
		||||
            android:id="@+id/action_appletLauncherFragment_to_cabinetLauncherDialogFragment"
 | 
			
		||||
            app:destination="@id/cabinetLauncherDialogFragment" />
 | 
			
		||||
    </fragment>
 | 
			
		||||
    <dialog
 | 
			
		||||
        android:id="@+id/cabinetLauncherDialogFragment"
 | 
			
		||||
        android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment"
 | 
			
		||||
        android:label="CabinetLauncherDialogFragment" />
 | 
			
		||||
 | 
			
		||||
</navigation>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,6 +124,24 @@
 | 
			
		|||
    <string name="share_save_file">Share save file</string>
 | 
			
		||||
    <string name="export_save_failed">Failed to export save</string>
 | 
			
		||||
 | 
			
		||||
    <!-- Applet launcher strings -->
 | 
			
		||||
    <string name="applets">Applet launcher</string>
 | 
			
		||||
    <string name="applets_description">Launch system applets using installed firmware</string>
 | 
			
		||||
    <string name="applets_error_firmware">Firmware not installed</string>
 | 
			
		||||
    <string name="applets_error_applet">Applet not available</string>
 | 
			
		||||
    <string name="applets_error_description"><![CDATA[Please ensure your <a href="https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys">prod.keys</a> file and <a href="https://yuzu-emu.org/help/quickstart/#dumping-system-firmware">firmware</a> are installed and try again.]]></string>
 | 
			
		||||
    <string name="album_applet">Album</string>
 | 
			
		||||
    <string name="album_applet_description">See images stored in the user screenshots folder with the system photo viewer</string>
 | 
			
		||||
    <string name="mii_edit_applet">Mii edit</string>
 | 
			
		||||
    <string name="mii_edit_applet_description">View and edit Miis with the system editor</string>
 | 
			
		||||
    <string name="cabinet_applet">Cabinet</string>
 | 
			
		||||
    <string name="cabinet_applet_description">Edit and delete data stored on amiibo</string>
 | 
			
		||||
    <string name="cabinet_launcher">Cabinet launcher</string>
 | 
			
		||||
    <string name="cabinet_nickname_and_owner">Nickname and owner settings</string>
 | 
			
		||||
    <string name="cabinet_game_data_eraser">Game data eraser</string>
 | 
			
		||||
    <string name="cabinet_restorer">Restorer</string>
 | 
			
		||||
    <string name="cabinet_formatter">Formatter</string>
 | 
			
		||||
 | 
			
		||||
    <!-- About screen strings -->
 | 
			
		||||
    <string name="gaia_is_not_real">Gaia isn\'t real</string>
 | 
			
		||||
    <string name="copied_to_clipboard">Copied to clipboard</string>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue