summaryrefslogtreecommitdiff
path: root/src/android
diff options
context:
space:
mode:
authorGravatar Charles Lombardo2023-04-30 22:14:57 -0400
committerGravatar bunnei2023-06-03 00:05:57 -0700
commit912bf6a0c65036ba1d0da03716db77ce26823b82 (patch)
treefbe358fe583a24d2117ecda7c2912a9f8ebdce81 /src/android
parentandroid: Fix inline keyboard input (diff)
downloadyuzu-912bf6a0c65036ba1d0da03716db77ce26823b82.tar.gz
yuzu-912bf6a0c65036ba1d0da03716db77ce26823b82.tar.xz
yuzu-912bf6a0c65036ba1d0da03716db77ce26823b82.zip
android: Add user directory shortcut
Diffstat (limited to 'src/android')
-rw-r--r--src/android/app/src/main/AndroidManifest.xml1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt33
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt89
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt4
-rw-r--r--src/android/app/src/main/res/values/strings.xml27
6 files changed, 140 insertions, 25 deletions
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 4791e3417..ae473ae3a 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -15,6 +15,7 @@
15 <uses-permission android:name="android.permission.INTERNET" /> 15 <uses-permission android:name="android.permission.INTERNET" />
16 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> 16 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
17 <uses-permission android:name="android.permission.NFC" /> 17 <uses-permission android:name="android.permission.NFC" />
18 <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
18 19
19 <application 20 <application
20 android:name="org.yuzu.yuzu_emu.YuzuApplication" 21 android:name="org.yuzu.yuzu_emu.YuzuApplication"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
index f987c6b7b..5848c87c1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
@@ -15,23 +15,29 @@ import java.io.File
15fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir 15fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
16 16
17class YuzuApplication : Application() { 17class YuzuApplication : Application() {
18 private fun createNotificationChannel() { 18 private fun createNotificationChannels() {
19 // Create the NotificationChannel, but only on API 26+ because 19 val emulationChannel = NotificationChannel(
20 // the NotificationChannel class is new and not in the support library 20 getString(R.string.emulation_notification_channel_id),
21 val name: CharSequence = getString(R.string.app_notification_channel_name) 21 getString(R.string.emulation_notification_channel_name),
22 val description = getString(R.string.app_notification_channel_description)
23 val channel = NotificationChannel(
24 getString(R.string.app_notification_channel_id),
25 name,
26 NotificationManager.IMPORTANCE_LOW 22 NotificationManager.IMPORTANCE_LOW
27 ) 23 )
28 channel.description = description 24 emulationChannel.description = getString(R.string.emulation_notification_channel_description)
29 channel.setSound(null, null) 25 emulationChannel.setSound(null, null)
30 channel.vibrationPattern = null 26 emulationChannel.vibrationPattern = null
27
28 val noticeChannel = NotificationChannel(
29 getString(R.string.notice_notification_channel_id),
30 getString(R.string.notice_notification_channel_name),
31 NotificationManager.IMPORTANCE_HIGH
32 )
33 noticeChannel.description = getString(R.string.notice_notification_channel_description)
34 noticeChannel.setSound(null, null)
35
31 // Register the channel with the system; you can't change the importance 36 // Register the channel with the system; you can't change the importance
32 // or other notification behaviors after this 37 // or other notification behaviors after this
33 val notificationManager = getSystemService(NotificationManager::class.java) 38 val notificationManager = getSystemService(NotificationManager::class.java)
34 notificationManager.createNotificationChannel(channel) 39 notificationManager.createNotificationChannel(emulationChannel)
40 notificationManager.createNotificationChannel(noticeChannel)
35 } 41 }
36 42
37 override fun onCreate() { 43 override fun onCreate() {
@@ -42,8 +48,7 @@ class YuzuApplication : Application() {
42 GpuDriverHelper.initializeDriverParameters(applicationContext) 48 GpuDriverHelper.initializeDriverParameters(applicationContext)
43 NativeLibrary.logDeviceInfo() 49 NativeLibrary.logDeviceInfo()
44 50
45 // TODO(bunnei): Disable notifications until we support app suspension. 51 createNotificationChannels();
46 //createNotificationChannel();
47 } 52 }
48 53
49 companion object { 54 companion object {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 23d2da4d0..0e7c181ea 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -3,14 +3,21 @@
3 3
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.Manifest
7import android.content.ActivityNotFoundException
6import android.content.DialogInterface 8import android.content.DialogInterface
7import android.content.Intent 9import android.content.Intent
10import android.content.pm.PackageManager
8import android.os.Bundle 11import android.os.Bundle
12import android.provider.DocumentsContract
9import android.view.LayoutInflater 13import android.view.LayoutInflater
10import android.view.View 14import android.view.View
11import android.view.ViewGroup 15import android.view.ViewGroup
12import android.widget.Toast 16import android.widget.Toast
13import androidx.appcompat.app.AppCompatActivity 17import androidx.appcompat.app.AppCompatActivity
18import androidx.core.app.ActivityCompat
19import androidx.core.app.NotificationCompat
20import androidx.core.app.NotificationManagerCompat
14import androidx.core.view.ViewCompat 21import androidx.core.view.ViewCompat
15import androidx.core.view.WindowInsetsCompat 22import androidx.core.view.WindowInsetsCompat
16import androidx.fragment.app.Fragment 23import androidx.fragment.app.Fragment
@@ -19,6 +26,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
19import org.yuzu.yuzu_emu.R 26import org.yuzu.yuzu_emu.R
20import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter 27import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
21import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding 28import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
29import org.yuzu.yuzu_emu.features.DocumentProvider
22import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity 30import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
23import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 31import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
24import org.yuzu.yuzu_emu.model.HomeSetting 32import org.yuzu.yuzu_emu.model.HomeSetting
@@ -50,6 +58,11 @@ class HomeSettingsFragment : Fragment() {
50 R.drawable.ic_settings 58 R.drawable.ic_settings
51 ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }, 59 ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") },
52 HomeSetting( 60 HomeSetting(
61 R.string.open_user_folder,
62 R.string.open_user_folder_description,
63 R.drawable.ic_folder
64 ) { openFileManager() },
65 HomeSetting(
53 R.string.install_gpu_driver, 66 R.string.install_gpu_driver,
54 R.string.install_gpu_driver_description, 67 R.string.install_gpu_driver_description,
55 R.drawable.ic_input 68 R.drawable.ic_input
@@ -84,6 +97,82 @@ class HomeSettingsFragment : Fragment() {
84 _binding = null 97 _binding = null
85 } 98 }
86 99
100 private fun openFileManager() {
101 // First, try to open the user data folder directly
102 try {
103 startActivity(getFileManagerIntentOnDocumentProvider(Intent.ACTION_VIEW))
104 return
105 } catch (_: ActivityNotFoundException) {}
106
107 try {
108 startActivity(getFileManagerIntentOnDocumentProvider("android.provider.action.BROWSE"))
109 return
110 } catch (_: ActivityNotFoundException) {}
111
112 // Just try to open the file manager, try the package name used on "normal" phones
113 try {
114 startActivity(getFileManagerIntent("com.google.android.documentsui"))
115 showNoLinkNotification()
116 return
117 } catch (_: ActivityNotFoundException) {}
118
119 try {
120 // Next, try the AOSP package name
121 startActivity(getFileManagerIntent("com.android.documentsui"))
122 showNoLinkNotification()
123 return
124 } catch (_: ActivityNotFoundException) {}
125
126 Toast.makeText(
127 requireContext(),
128 resources.getString(R.string.no_file_manager),
129 Toast.LENGTH_LONG
130 ).show()
131 }
132
133 private fun getFileManagerIntent(packageName: String): Intent {
134 // Fragile, but some phones don't expose the system file manager in any better way
135 val intent = Intent(Intent.ACTION_MAIN)
136 intent.setClassName(packageName, "com.android.documentsui.files.FilesActivity")
137 intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
138 return intent
139 }
140
141 private fun getFileManagerIntentOnDocumentProvider(action: String): Intent {
142 val authority = "${requireContext().packageName}.user"
143 val intent = Intent(action)
144 intent.addCategory(Intent.CATEGORY_DEFAULT)
145 intent.data = DocumentsContract.buildRootUri(authority, DocumentProvider.ROOT_ID)
146 intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
147 return intent
148 }
149
150 private fun showNoLinkNotification() {
151 val builder = NotificationCompat.Builder(requireContext(), getString(R.string.notice_notification_channel_id))
152 .setSmallIcon(R.drawable.ic_stat_notification_logo)
153 .setContentTitle(getString(R.string.notification_no_directory_link))
154 .setContentText(getString(R.string.notification_no_directory_link_description))
155 .setPriority(NotificationCompat.PRIORITY_HIGH)
156 .setAutoCancel(true)
157 // TODO: Make the click action for this notification lead to a help article
158
159 with(NotificationManagerCompat.from(requireContext())) {
160 if (ActivityCompat.checkSelfPermission(
161 requireContext(),
162 Manifest.permission.POST_NOTIFICATIONS
163 ) != PackageManager.PERMISSION_GRANTED
164 ) {
165 Toast.makeText(
166 requireContext(),
167 resources.getString(R.string.notification_permission_not_granted),
168 Toast.LENGTH_LONG
169 ).show()
170 return
171 }
172 notify(0, builder.build())
173 }
174 }
175
87 private fun driverInstaller() { 176 private fun driverInstaller() {
88 // Get the driver name for the dialog message. 177 // Get the driver name for the dialog message.
89 var driverName = GpuDriverHelper.customDriverName 178 var driverName = GpuDriverHelper.customDriverName
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 2af78b02c..3d2f8719c 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
@@ -4,11 +4,13 @@
4package org.yuzu.yuzu_emu.fragments 4package org.yuzu.yuzu_emu.fragments
5 5
6import android.content.Intent 6import android.content.Intent
7import android.os.Build
7import android.os.Bundle 8import android.os.Bundle
8import android.view.LayoutInflater 9import android.view.LayoutInflater
9import android.view.View 10import android.view.View
10import android.view.ViewGroup 11import android.view.ViewGroup
11import androidx.activity.OnBackPressedCallback 12import androidx.activity.OnBackPressedCallback
13import androidx.activity.result.contract.ActivityResultContracts
12import androidx.appcompat.app.AppCompatActivity 14import androidx.appcompat.app.AppCompatActivity
13import androidx.core.content.ContextCompat 15import androidx.core.content.ContextCompat
14import androidx.core.view.ViewCompat 16import androidx.core.view.ViewCompat
@@ -63,6 +65,10 @@ class SetupFragment : Fragment() {
63 } 65 }
64 66
65 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 67 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
68 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
69 pushNotificationPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
70 }
71
66 mainActivity = requireActivity() as MainActivity 72 mainActivity = requireActivity() as MainActivity
67 73
68 homeViewModel.setNavigationVisibility(false) 74 homeViewModel.setNavigationVisibility(false)
@@ -219,6 +225,11 @@ class SetupFragment : Fragment() {
219 _binding = null 225 _binding = null
220 } 226 }
221 227
228 private val pushNotificationPermissionLauncher =
229 registerForActivityResult(ActivityResultContracts.RequestPermission()) {
230 // TODO: Show proper notification request reason and confirmation
231 }
232
222 private fun finishSetup() { 233 private fun finishSetup() {
223 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() 234 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
224 .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false) 235 .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
index 557fe0738..7e33ff044 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ForegroundService.kt
@@ -29,10 +29,10 @@ class ForegroundService : Service() {
29 PendingIntent.FLAG_IMMUTABLE 29 PendingIntent.FLAG_IMMUTABLE
30 ) 30 )
31 val builder = 31 val builder =
32 NotificationCompat.Builder(this, getString(R.string.app_notification_channel_id)) 32 NotificationCompat.Builder(this, getString(R.string.emulation_notification_channel_id))
33 .setSmallIcon(R.drawable.ic_stat_notification_logo) 33 .setSmallIcon(R.drawable.ic_stat_notification_logo)
34 .setContentTitle(getString(R.string.app_name)) 34 .setContentTitle(getString(R.string.app_name))
35 .setContentText(getString(R.string.app_notification_running)) 35 .setContentText(getString(R.string.emulation_notification_running))
36 .setPriority(NotificationCompat.PRIORITY_LOW) 36 .setPriority(NotificationCompat.PRIORITY_LOW)
37 .setOngoing(true) 37 .setOngoing(true)
38 .setVibrate(null) 38 .setVibrate(null)
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 8220cd412..58ae37790 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -4,10 +4,14 @@
4 <!-- General application strings --> 4 <!-- General application strings -->
5 <string name="app_name" translatable="false">yuzu</string> 5 <string name="app_name" translatable="false">yuzu</string>
6 <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string> 6 <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string>
7 <string name="app_notification_channel_name" translatable="false">yuzu</string> 7 <string name="emulation_notification_channel_name">Emulation is Active</string>
8 <string name="app_notification_channel_id" translatable="false">yuzu</string> 8 <string name="emulation_notification_channel_id" translatable="false">emulationIsActive</string>
9 <string name="app_notification_channel_description">yuzu Switch emulator notifications</string> 9 <string name="emulation_notification_channel_description">Shows a persistent notification when emulation is running.</string>
10 <string name="app_notification_running">yuzu is running</string> 10 <string name="emulation_notification_running">yuzu is running</string>
11 <string name="notice_notification_channel_name">Notices and errors</string>
12 <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
13 <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
14 <string name="notification_permission_not_granted">Notification permission not granted!</string>
11 15
12 <!-- Setup strings --> 16 <!-- Setup strings -->
13 <string name="welcome">Welcome!</string> 17 <string name="welcome">Welcome!</string>
@@ -29,14 +33,14 @@
29 <!-- Home strings --> 33 <!-- Home strings -->
30 <string name="home_games">Games</string> 34 <string name="home_games">Games</string>
31 <string name="home_settings">Settings</string> 35 <string name="home_settings">Settings</string>
32 <string name="select_games_folder">Select Games Folder</string> 36 <string name="select_games_folder">Select games folder</string>
33 <string name="select_games_folder_description">Allows yuzu to populate the games list</string> 37 <string name="select_games_folder_description">Allows yuzu to populate the games list</string>
34 <string name="add_games_warning">Skip selecting games folder?</string> 38 <string name="add_games_warning">Skip selecting games folder?</string>
35 <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> 39 <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string>
36 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 40 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
37 <string name="home_search_games">Search Games</string> 41 <string name="home_search_games">Search Games</string>
38 <string name="games_dir_selected">Games directory selected</string> 42 <string name="games_dir_selected">Games directory selected</string>
39 <string name="install_prod_keys">Install Prod.keys</string> 43 <string name="install_prod_keys">Install prod.keys</string>
40 <string name="install_prod_keys_description">Required to decrypt retail games</string> 44 <string name="install_prod_keys_description">Required to decrypt retail games</string>
41 <string name="install_prod_keys_warning">Skip adding keys?</string> 45 <string name="install_prod_keys_warning">Skip adding keys?</string>
42 <string name="install_prod_keys_warning_description">Valid keys are required to emulate retail games. Only homebrew apps will function if you continue.</string> 46 <string name="install_prod_keys_warning_description">Valid keys are required to emulate retail games. Only homebrew apps will function if you continue.</string>
@@ -44,16 +48,21 @@
44 <string name="warning_help">Help</string> 48 <string name="warning_help">Help</string>
45 <string name="warning_skip">Skip</string> 49 <string name="warning_skip">Skip</string>
46 <string name="warning_cancel">Cancel</string> 50 <string name="warning_cancel">Cancel</string>
47 <string name="install_amiibo_keys">Install Amiibo Keys</string> 51 <string name="install_amiibo_keys">Install Amiibo keys</string>
48 <string name="install_amiibo_keys_description">Required to use Amiibo in game</string> 52 <string name="install_amiibo_keys_description">Required to use Amiibo in game</string>
49 <string name="invalid_keys_file">Invalid keys file selected</string> 53 <string name="invalid_keys_file">Invalid keys file selected</string>
50 <string name="install_keys_success">Keys successfully installed</string> 54 <string name="install_keys_success">Keys successfully installed</string>
51 <string name="install_keys_failure">Keys file (prod.keys) is invalid</string> 55 <string name="install_keys_failure">Keys file (prod.keys) is invalid</string>
52 <string name="install_amiibo_keys_failure">Keys file (key_retail.bin) is invalid</string> 56 <string name="install_amiibo_keys_failure">Keys file (key_retail.bin) is invalid</string>
53 <string name="install_gpu_driver">Install GPU Driver</string> 57 <string name="install_gpu_driver">Install GPU driver</string>
54 <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> 58 <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
55 <string name="advanced_settings">Advanced Settings</string> 59 <string name="advanced_settings">Advanced settings</string>
56 <string name="settings_description">Configure emulator settings</string> 60 <string name="settings_description">Configure emulator settings</string>
61 <string name="open_user_folder">Open yuzu folder</string>
62 <string name="open_user_folder_description">Manage yuzu\'s internal files</string>
63 <string name="no_file_manager">No file manager found</string>
64 <string name="notification_no_directory_link">Couldn\'t open yuzu directory</string>
65 <string name="notification_no_directory_link_description">Please locate the user folder with the file manager\'s side panel manually.</string>
57 66
58 <!-- General settings strings --> 67 <!-- General settings strings -->
59 <string name="frame_limit_enable">Enable limit speed</string> 68 <string name="frame_limit_enable">Enable limit speed</string>