diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/PermissionDeniedDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/PermissionDeniedDialogFragment.kt new file mode 100644 index 0000000000..3478b92500 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/PermissionDeniedDialogFragment.kt @@ -0,0 +1,38 @@ +// 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.content.DialogInterface +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.provider.Settings +import androidx.fragment.app.DialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.yuzu.yuzu_emu.R + +class PermissionDeniedDialogFragment : DialogFragment() { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return MaterialAlertDialogBuilder(requireContext()) + .setPositiveButton(R.string.home_settings) { _: DialogInterface?, _: Int -> + openSettings() + } + .setNegativeButton(android.R.string.cancel, null) + .setTitle(R.string.permission_denied) + .setMessage(R.string.permission_denied_description) + .show() + } + + private fun openSettings() { + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + val uri = Uri.fromParts("package", requireActivity().packageName, null) + intent.data = uri + startActivity(intent) + } + + companion object { + const val TAG = "PermissionDeniedDialogFragment" + } +} 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 index 1536220724..2587733800 100644 --- 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 @@ -3,6 +3,7 @@ package org.yuzu.yuzu_emu.fragments +import android.Manifest import android.content.Intent import android.os.Build import android.os.Bundle @@ -11,7 +12,9 @@ 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 @@ -65,10 +68,6 @@ class SetupFragment : Fragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - pushNotificationPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS) - } - mainActivity = requireActivity() as MainActivity homeViewModel.setNavigationVisibility(visible = false, animated = false) @@ -88,60 +87,93 @@ class SetupFragment : Fragment() { requireActivity().window.navigationBarColor = ContextCompat.getColor(requireContext(), android.R.color.transparent) - val pages = listOf( - SetupPage( - R.drawable.ic_yuzu_title, - R.string.welcome, - R.string.welcome_description, - 0, - true, - R.string.get_started, - { pageForward() }, - false - ), - 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() } - ), - 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() - } - ), - SetupPage( - R.drawable.ic_check, - R.string.done, - R.string.done_description, - R.drawable.ic_arrow_forward, - false, - R.string.text_continue, - { finishSetup() }, - false + val pages = mutableListOf() + 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 @@ -225,9 +257,15 @@ class SetupFragment : Fragment() { _binding = null } - private val pushNotificationPermissionLauncher = + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private val permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { - // TODO: Show proper notification request reason and confirmation + if (!it && !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { + PermissionDeniedDialogFragment().show( + childFragmentManager, + PermissionDeniedDialogFragment.TAG + ) + } } private fun finishSetup() { diff --git a/src/android/app/src/main/res/drawable/ic_notification.xml b/src/android/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000000..b413f75853 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 09b95848fc..dd1137b69e 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -48,6 +48,13 @@ Skip adding keys? Valid keys are required to emulate retail games. Only homebrew apps will function if you continue. https://yuzu-emu.org/help/quickstart/#guide-introduction + Notifications + Grant the notification permission with the button below. + Grant permission + Skip granting the notification permission? + yuzu won\'t be able to notify you of important information. + Permission denied + You denied this permission too many times and now you have to manually grant it in system settings. About Build version, credits, and more Help