From 7de86266fd4e34501091dca934c6ad5719b2bdcb Mon Sep 17 00:00:00 2001
From: Charles Lombardo <clombardo169@gmail.com>
Date: Sun, 28 May 2023 22:52:36 -0400
Subject: [PATCH] android: Show notification permission page during setup

---
 .../PermissionDeniedDialogFragment.kt         |  38 +++++
 .../yuzu/yuzu_emu/fragments/SetupFragment.kt  | 156 +++++++++++-------
 .../src/main/res/drawable/ic_notification.xml |   9 +
 .../app/src/main/res/values/strings.xml       |   7 +
 4 files changed, 151 insertions(+), 59 deletions(-)
 create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/PermissionDeniedDialogFragment.kt
 create mode 100644 src/android/app/src/main/res/drawable/ic_notification.xml

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 000000000..3478b9250
--- /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 153622072..258773380 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<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
@@ -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 000000000..b413f7585
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_notification.xml
@@ -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/colorOnSurface"
+        android:pathData="M7.58,4.08L6.15,2.65C3.75,4.48 2.17,7.3 2.03,10.5h2c0.15,-2.65 1.51,-4.97 3.55,-6.42zM19.97,10.5h2c-0.15,-3.2 -1.73,-6.02 -4.12,-7.85l-1.42,1.43c2.02,1.45 3.39,3.77 3.54,6.42zM18,11c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2v-5zM12,22c0.14,0 0.27,-0.01 0.4,-0.04 0.65,-0.14 1.18,-0.58 1.44,-1.18 0.1,-0.24 0.15,-0.5 0.15,-0.78h-4c0.01,1.1 0.9,2 2.01,2z" />
+</vector>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 09b95848f..dd1137b69 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 @@
     <string name="install_prod_keys_warning">Skip adding keys?</string>
     <string name="install_prod_keys_warning_description">Valid keys are required to emulate retail games. Only homebrew apps will function if you continue.</string>
     <string name="install_prod_keys_warning_help">https://yuzu-emu.org/help/quickstart/#guide-introduction</string>
+    <string name="notifications">Notifications</string>
+    <string name="notifications_description">Grant the notification permission with the button below.</string>
+    <string name="give_permission">Grant permission</string>
+    <string name="notification_warning">Skip granting the notification permission?</string>
+    <string name="notification_warning_description">yuzu won\'t be able to notify you of important information.</string>
+    <string name="permission_denied">Permission denied</string>
+    <string name="permission_denied_description">You denied this permission too many times and now you have to manually grant it in system settings.</string>
     <string name="about">About</string>
     <string name="about_description">Build version, credits, and more</string>
     <string name="warning_help">Help</string>