summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
new file mode 100644
index 000000000..258773380
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -0,0 +1,329 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.fragments
+
+import android.Manifest
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.RequiresApi
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.content.ContextCompat
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.navigation.findNavController
+import androidx.preference.PreferenceManager
+import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
+import com.google.android.material.transition.MaterialFadeThrough
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.adapters.SetupAdapter
+import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding
+import org.yuzu.yuzu_emu.features.settings.model.Settings
+import org.yuzu.yuzu_emu.model.HomeViewModel
+import org.yuzu.yuzu_emu.model.SetupPage
+import org.yuzu.yuzu_emu.ui.main.MainActivity
+import org.yuzu.yuzu_emu.utils.DirectoryInitialization
+import org.yuzu.yuzu_emu.utils.GameHelper
+import java.io.File
+
+class SetupFragment : Fragment() {
+ private var _binding: FragmentSetupBinding? = null
+ private val binding get() = _binding!!
+
+ private val homeViewModel: HomeViewModel by activityViewModels()
+
+ private lateinit var mainActivity: MainActivity
+
+ private lateinit var hasBeenWarned: BooleanArray
+
+ companion object {
+ const val KEY_NEXT_VISIBILITY = "NextButtonVisibility"
+ const val KEY_BACK_VISIBILITY = "BackButtonVisibility"
+ const val KEY_HAS_BEEN_WARNED = "HasBeenWarned"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ exitTransition = MaterialFadeThrough()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentSetupBinding.inflate(layoutInflater)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ mainActivity = requireActivity() as MainActivity
+
+ homeViewModel.setNavigationVisibility(visible = false, animated = false)
+
+ requireActivity().onBackPressedDispatcher.addCallback(
+ viewLifecycleOwner,
+ object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ if (binding.viewPager2.currentItem > 0) {
+ pageBackward()
+ } else {
+ requireActivity().finish()
+ }
+ }
+ })
+
+ requireActivity().window.navigationBarColor =
+ ContextCompat.getColor(requireContext(), android.R.color.transparent)
+
+ val pages = mutableListOf<SetupPage>()
+ pages.apply {
+ add(
+ SetupPage(
+ R.drawable.ic_yuzu_title,
+ R.string.welcome,
+ R.string.welcome_description,
+ 0,
+ true,
+ R.string.get_started,
+ { pageForward() },
+ false
+ )
+ )
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ add(
+ SetupPage(
+ R.drawable.ic_notification,
+ R.string.notifications,
+ R.string.notifications_description,
+ 0,
+ false,
+ R.string.give_permission,
+ { permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) },
+ true,
+ R.string.notification_warning,
+ R.string.notification_warning_description,
+ 0,
+ {
+ NotificationManagerCompat.from(requireContext())
+ .areNotificationsEnabled()
+ }
+ )
+ )
+ }
+
+ add(
+ SetupPage(
+ R.drawable.ic_key,
+ R.string.keys,
+ R.string.keys_description,
+ R.drawable.ic_add,
+ true,
+ R.string.select_keys,
+ { mainActivity.getProdKey.launch(arrayOf("*/*")) },
+ true,
+ R.string.install_prod_keys_warning,
+ R.string.install_prod_keys_warning_description,
+ R.string.install_prod_keys_warning_help,
+ { File(DirectoryInitialization.userDirectory + "/keys/prod.keys").exists() }
+ )
+ )
+ add(
+ SetupPage(
+ R.drawable.ic_controller,
+ R.string.games,
+ R.string.games_description,
+ R.drawable.ic_add,
+ true,
+ R.string.add_games,
+ { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) },
+ true,
+ R.string.add_games_warning,
+ R.string.add_games_warning_description,
+ R.string.add_games_warning_help,
+ {
+ val preferences =
+ PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
+ preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()
+ }
+ )
+ )
+ add(
+ SetupPage(
+ R.drawable.ic_check,
+ R.string.done,
+ R.string.done_description,
+ R.drawable.ic_arrow_forward,
+ false,
+ R.string.text_continue,
+ { finishSetup() },
+ false
+ )
+ )
+ }
+
+ binding.viewPager2.apply {
+ adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
+ offscreenPageLimit = 2
+ isUserInputEnabled = false
+ }
+
+ binding.viewPager2.registerOnPageChangeCallback(object : OnPageChangeCallback() {
+ var previousPosition: Int = 0
+
+ override fun onPageSelected(position: Int) {
+ super.onPageSelected(position)
+
+ if (position == 1 && previousPosition == 0) {
+ showView(binding.buttonNext)
+ showView(binding.buttonBack)
+ } else if (position == 0 && previousPosition == 1) {
+ hideView(binding.buttonBack)
+ hideView(binding.buttonNext)
+ } else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
+ hideView(binding.buttonNext)
+ } else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
+ showView(binding.buttonNext)
+ }
+
+ previousPosition = position
+ }
+ })
+
+ binding.buttonNext.setOnClickListener {
+ val index = binding.viewPager2.currentItem
+ val currentPage = pages[index]
+
+ // Checks if the user has completed the task on the current page
+ if (currentPage.hasWarning) {
+ if (currentPage.taskCompleted.invoke()) {
+ pageForward()
+ return@setOnClickListener
+ }
+
+ if (!hasBeenWarned[index]) {
+ SetupWarningDialogFragment.newInstance(
+ currentPage.warningTitleId,
+ currentPage.warningDescriptionId,
+ currentPage.warningHelpLinkId,
+ index
+ ).show(childFragmentManager, SetupWarningDialogFragment.TAG)
+ return@setOnClickListener
+ }
+ }
+ pageForward()
+ }
+ binding.buttonBack.setOnClickListener { pageBackward() }
+
+ if (savedInstanceState != null) {
+ val nextIsVisible = savedInstanceState.getBoolean(KEY_NEXT_VISIBILITY)
+ val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
+ hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
+
+ if (nextIsVisible) {
+ binding.buttonNext.visibility = View.VISIBLE
+ }
+ if (backIsVisible) {
+ binding.buttonBack.visibility = View.VISIBLE
+ }
+ } else {
+ hasBeenWarned = BooleanArray(pages.size)
+ }
+
+ setInsets()
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible)
+ outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
+ outState.putBooleanArray(KEY_HAS_BEEN_WARNED, hasBeenWarned)
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private val permissionLauncher =
+ registerForActivityResult(ActivityResultContracts.RequestPermission()) {
+ if (!it && !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
+ PermissionDeniedDialogFragment().show(
+ childFragmentManager,
+ PermissionDeniedDialogFragment.TAG
+ )
+ }
+ }
+
+ private fun finishSetup() {
+ PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
+ .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
+ .apply()
+ mainActivity.finishSetup(binding.root.findNavController())
+ }
+
+ private fun showView(view: View) {
+ view.apply {
+ alpha = 0f
+ visibility = View.VISIBLE
+ isClickable = true
+ }.animate().apply {
+ duration = 300
+ alpha(1f)
+ }.start()
+ }
+
+ private fun hideView(view: View) {
+ if (view.visibility == View.INVISIBLE) {
+ return
+ }
+
+ view.apply {
+ alpha = 1f
+ isClickable = false
+ }.animate().apply {
+ duration = 300
+ alpha(0f)
+ }.withEndAction {
+ view.visibility = View.INVISIBLE
+ }
+ }
+
+ fun pageForward() {
+ binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
+ }
+
+ fun pageBackward() {
+ binding.viewPager2.currentItem = binding.viewPager2.currentItem - 1
+ }
+
+ fun setPageWarned(page: Int) {
+ hasBeenWarned[page] = true
+ }
+
+ private fun setInsets() =
+ ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+ val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
+ view.setPadding(
+ barInsets.left + cutoutInsets.left,
+ barInsets.top + cutoutInsets.top,
+ barInsets.right + cutoutInsets.right,
+ barInsets.bottom + cutoutInsets.bottom
+ )
+ windowInsets
+ }
+}