mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-10-31 14:56:40 +08:00 
			
		
		
		
	android: Add user directory shortcut
This commit is contained in:
		
							parent
							
								
									265b9139e0
								
							
						
					
					
						commit
						912bf6a0c6
					
				| @ -15,6 +15,7 @@ | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | ||||
|     <uses-permission android:name="android.permission.NFC" /> | ||||
|     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> | ||||
| 
 | ||||
|     <application | ||||
|         android:name="org.yuzu.yuzu_emu.YuzuApplication" | ||||
|  | ||||
| @ -15,23 +15,29 @@ import java.io.File | ||||
| fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir | ||||
| 
 | ||||
| class YuzuApplication : Application() { | ||||
|     private fun createNotificationChannel() { | ||||
|         // Create the NotificationChannel, but only on API 26+ because | ||||
|         // the NotificationChannel class is new and not in the support library | ||||
|         val name: CharSequence = getString(R.string.app_notification_channel_name) | ||||
|         val description = getString(R.string.app_notification_channel_description) | ||||
|         val channel = NotificationChannel( | ||||
|             getString(R.string.app_notification_channel_id), | ||||
|             name, | ||||
|     private fun createNotificationChannels() { | ||||
|         val emulationChannel = NotificationChannel( | ||||
|             getString(R.string.emulation_notification_channel_id), | ||||
|             getString(R.string.emulation_notification_channel_name), | ||||
|             NotificationManager.IMPORTANCE_LOW | ||||
|         ) | ||||
|         channel.description = description | ||||
|         channel.setSound(null, null) | ||||
|         channel.vibrationPattern = null | ||||
|         emulationChannel.description = getString(R.string.emulation_notification_channel_description) | ||||
|         emulationChannel.setSound(null, null) | ||||
|         emulationChannel.vibrationPattern = null | ||||
| 
 | ||||
|         val noticeChannel = NotificationChannel( | ||||
|             getString(R.string.notice_notification_channel_id), | ||||
|             getString(R.string.notice_notification_channel_name), | ||||
|             NotificationManager.IMPORTANCE_HIGH | ||||
|         ) | ||||
|         noticeChannel.description = getString(R.string.notice_notification_channel_description) | ||||
|         noticeChannel.setSound(null, null) | ||||
| 
 | ||||
|         // Register the channel with the system; you can't change the importance | ||||
|         // or other notification behaviors after this | ||||
|         val notificationManager = getSystemService(NotificationManager::class.java) | ||||
|         notificationManager.createNotificationChannel(channel) | ||||
|         notificationManager.createNotificationChannel(emulationChannel) | ||||
|         notificationManager.createNotificationChannel(noticeChannel) | ||||
|     } | ||||
| 
 | ||||
|     override fun onCreate() { | ||||
| @ -42,8 +48,7 @@ class YuzuApplication : Application() { | ||||
|         GpuDriverHelper.initializeDriverParameters(applicationContext) | ||||
|         NativeLibrary.logDeviceInfo() | ||||
| 
 | ||||
|         // TODO(bunnei): Disable notifications until we support app suspension. | ||||
|         //createNotificationChannel(); | ||||
|         createNotificationChannels(); | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|  | ||||
| @ -3,14 +3,21 @@ | ||||
| 
 | ||||
| package org.yuzu.yuzu_emu.fragments | ||||
| 
 | ||||
| import android.Manifest | ||||
| import android.content.ActivityNotFoundException | ||||
| import android.content.DialogInterface | ||||
| import android.content.Intent | ||||
| import android.content.pm.PackageManager | ||||
| import android.os.Bundle | ||||
| import android.provider.DocumentsContract | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.widget.Toast | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.core.app.ActivityCompat | ||||
| import androidx.core.app.NotificationCompat | ||||
| import androidx.core.app.NotificationManagerCompat | ||||
| import androidx.core.view.ViewCompat | ||||
| import androidx.core.view.WindowInsetsCompat | ||||
| import androidx.fragment.app.Fragment | ||||
| @ -19,6 +26,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||||
| import org.yuzu.yuzu_emu.R | ||||
| import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter | ||||
| import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding | ||||
| import org.yuzu.yuzu_emu.features.DocumentProvider | ||||
| import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity | ||||
| import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile | ||||
| import org.yuzu.yuzu_emu.model.HomeSetting | ||||
| @ -49,6 +57,11 @@ class HomeSettingsFragment : Fragment() { | ||||
|                 R.string.settings_description, | ||||
|                 R.drawable.ic_settings | ||||
|             ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }, | ||||
|             HomeSetting( | ||||
|                 R.string.open_user_folder, | ||||
|                 R.string.open_user_folder_description, | ||||
|                 R.drawable.ic_folder | ||||
|             ) { openFileManager() }, | ||||
|             HomeSetting( | ||||
|                 R.string.install_gpu_driver, | ||||
|                 R.string.install_gpu_driver_description, | ||||
| @ -84,6 +97,82 @@ class HomeSettingsFragment : Fragment() { | ||||
|         _binding = null | ||||
|     } | ||||
| 
 | ||||
|     private fun openFileManager() { | ||||
|         // First, try to open the user data folder directly | ||||
|         try { | ||||
|             startActivity(getFileManagerIntentOnDocumentProvider(Intent.ACTION_VIEW)) | ||||
|             return | ||||
|         } catch (_: ActivityNotFoundException) {} | ||||
| 
 | ||||
|         try { | ||||
|             startActivity(getFileManagerIntentOnDocumentProvider("android.provider.action.BROWSE")) | ||||
|             return | ||||
|         } catch (_: ActivityNotFoundException) {} | ||||
| 
 | ||||
|         // Just try to open the file manager, try the package name used on "normal" phones | ||||
|         try { | ||||
|             startActivity(getFileManagerIntent("com.google.android.documentsui")) | ||||
|             showNoLinkNotification() | ||||
|             return | ||||
|         } catch (_: ActivityNotFoundException) {} | ||||
| 
 | ||||
|         try { | ||||
|             // Next, try the AOSP package name | ||||
|             startActivity(getFileManagerIntent("com.android.documentsui")) | ||||
|             showNoLinkNotification() | ||||
|             return | ||||
|         } catch (_: ActivityNotFoundException) {} | ||||
| 
 | ||||
|         Toast.makeText( | ||||
|             requireContext(), | ||||
|             resources.getString(R.string.no_file_manager), | ||||
|             Toast.LENGTH_LONG | ||||
|         ).show() | ||||
|     } | ||||
| 
 | ||||
|     private fun getFileManagerIntent(packageName: String): Intent { | ||||
|         // Fragile, but some phones don't expose the system file manager in any better way | ||||
|         val intent = Intent(Intent.ACTION_MAIN) | ||||
|         intent.setClassName(packageName, "com.android.documentsui.files.FilesActivity") | ||||
|         intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK | ||||
|         return intent | ||||
|     } | ||||
| 
 | ||||
|     private fun getFileManagerIntentOnDocumentProvider(action: String): Intent { | ||||
|         val authority = "${requireContext().packageName}.user" | ||||
|         val intent = Intent(action) | ||||
|         intent.addCategory(Intent.CATEGORY_DEFAULT) | ||||
|         intent.data = DocumentsContract.buildRootUri(authority, DocumentProvider.ROOT_ID) | ||||
|         intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) | ||||
|         return intent | ||||
|     } | ||||
| 
 | ||||
|     private fun showNoLinkNotification() { | ||||
|         val builder = NotificationCompat.Builder(requireContext(), getString(R.string.notice_notification_channel_id)) | ||||
|             .setSmallIcon(R.drawable.ic_stat_notification_logo) | ||||
|             .setContentTitle(getString(R.string.notification_no_directory_link)) | ||||
|             .setContentText(getString(R.string.notification_no_directory_link_description)) | ||||
|             .setPriority(NotificationCompat.PRIORITY_HIGH) | ||||
|             .setAutoCancel(true) | ||||
|         // TODO: Make the click action for this notification lead to a help article | ||||
| 
 | ||||
|         with(NotificationManagerCompat.from(requireContext())) { | ||||
|             if (ActivityCompat.checkSelfPermission( | ||||
|                     requireContext(), | ||||
|                     Manifest.permission.POST_NOTIFICATIONS | ||||
|                 ) != PackageManager.PERMISSION_GRANTED | ||||
|             ) { | ||||
|                 Toast.makeText( | ||||
|                     requireContext(), | ||||
|                     resources.getString(R.string.notification_permission_not_granted), | ||||
|                     Toast.LENGTH_LONG | ||||
|                 ).show() | ||||
|                 return | ||||
|             } | ||||
|             notify(0, builder.build()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun driverInstaller() { | ||||
|         // Get the driver name for the dialog message. | ||||
|         var driverName = GpuDriverHelper.customDriverName | ||||
|  | ||||
| @ -4,11 +4,13 @@ | ||||
| package org.yuzu.yuzu_emu.fragments | ||||
| 
 | ||||
| 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.appcompat.app.AppCompatActivity | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.core.view.ViewCompat | ||||
| @ -63,6 +65,10 @@ 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(false) | ||||
| @ -219,6 +225,11 @@ class SetupFragment : Fragment() { | ||||
|         _binding = null | ||||
|     } | ||||
| 
 | ||||
|     private val pushNotificationPermissionLauncher = | ||||
|         registerForActivityResult(ActivityResultContracts.RequestPermission()) { | ||||
|             // TODO: Show proper notification request reason and confirmation | ||||
|         } | ||||
| 
 | ||||
|     private fun finishSetup() { | ||||
|         PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() | ||||
|             .putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false) | ||||
|  | ||||
| @ -29,10 +29,10 @@ class ForegroundService : Service() { | ||||
|             PendingIntent.FLAG_IMMUTABLE | ||||
|         ) | ||||
|         val builder = | ||||
|             NotificationCompat.Builder(this, getString(R.string.app_notification_channel_id)) | ||||
|             NotificationCompat.Builder(this, getString(R.string.emulation_notification_channel_id)) | ||||
|                 .setSmallIcon(R.drawable.ic_stat_notification_logo) | ||||
|                 .setContentTitle(getString(R.string.app_name)) | ||||
|                 .setContentText(getString(R.string.app_notification_running)) | ||||
|                 .setContentText(getString(R.string.emulation_notification_running)) | ||||
|                 .setPriority(NotificationCompat.PRIORITY_LOW) | ||||
|                 .setOngoing(true) | ||||
|                 .setVibrate(null) | ||||
|  | ||||
| @ -4,10 +4,14 @@ | ||||
|     <!-- General application strings --> | ||||
|     <string name="app_name" translatable="false">yuzu</string> | ||||
|     <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href="https://yuzu-emu.org/help/quickstart">Learn more</a>]]></string> | ||||
|     <string name="app_notification_channel_name" translatable="false">yuzu</string> | ||||
|     <string name="app_notification_channel_id" translatable="false">yuzu</string> | ||||
|     <string name="app_notification_channel_description">yuzu Switch emulator notifications</string> | ||||
|     <string name="app_notification_running">yuzu is running</string> | ||||
|     <string name="emulation_notification_channel_name">Emulation is Active</string> | ||||
|     <string name="emulation_notification_channel_id" translatable="false">emulationIsActive</string> | ||||
|     <string name="emulation_notification_channel_description">Shows a persistent notification when emulation is running.</string> | ||||
|     <string name="emulation_notification_running">yuzu is running</string> | ||||
|     <string name="notice_notification_channel_name">Notices and errors</string> | ||||
|     <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string> | ||||
|     <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string> | ||||
|     <string name="notification_permission_not_granted">Notification permission not granted!</string> | ||||
| 
 | ||||
|     <!-- Setup strings --> | ||||
|     <string name="welcome">Welcome!</string> | ||||
| @ -29,14 +33,14 @@ | ||||
|     <!-- Home strings --> | ||||
|     <string name="home_games">Games</string> | ||||
|     <string name="home_settings">Settings</string> | ||||
|     <string name="select_games_folder">Select Games Folder</string> | ||||
|     <string name="select_games_folder">Select games folder</string> | ||||
|     <string name="select_games_folder_description">Allows yuzu to populate the games list</string> | ||||
|     <string name="add_games_warning">Skip selecting games folder?</string> | ||||
|     <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> | ||||
|     <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> | ||||
|     <string name="home_search_games">Search Games</string> | ||||
|     <string name="games_dir_selected">Games directory selected</string> | ||||
|     <string name="install_prod_keys">Install Prod.keys</string> | ||||
|     <string name="install_prod_keys">Install prod.keys</string> | ||||
|     <string name="install_prod_keys_description">Required to decrypt retail games</string> | ||||
|     <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> | ||||
| @ -44,16 +48,21 @@ | ||||
|     <string name="warning_help">Help</string> | ||||
|     <string name="warning_skip">Skip</string> | ||||
|     <string name="warning_cancel">Cancel</string> | ||||
|     <string name="install_amiibo_keys">Install Amiibo Keys</string> | ||||
|     <string name="install_amiibo_keys">Install Amiibo keys</string> | ||||
|     <string name="install_amiibo_keys_description">Required to use Amiibo in game</string> | ||||
|     <string name="invalid_keys_file">Invalid keys file selected</string> | ||||
|     <string name="install_keys_success">Keys successfully installed</string> | ||||
|     <string name="install_keys_failure">Keys file (prod.keys) is invalid</string> | ||||
|     <string name="install_amiibo_keys_failure">Keys file (key_retail.bin) is invalid</string> | ||||
|     <string name="install_gpu_driver">Install GPU Driver</string> | ||||
|     <string name="install_gpu_driver">Install GPU driver</string> | ||||
|     <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> | ||||
|     <string name="advanced_settings">Advanced Settings</string> | ||||
|     <string name="advanced_settings">Advanced settings</string> | ||||
|     <string name="settings_description">Configure emulator settings</string> | ||||
|     <string name="open_user_folder">Open yuzu folder</string> | ||||
|     <string name="open_user_folder_description">Manage yuzu\'s internal files</string> | ||||
|     <string name="no_file_manager">No file manager found</string> | ||||
|     <string name="notification_no_directory_link">Couldn\'t open yuzu directory</string> | ||||
|     <string name="notification_no_directory_link_description">Please locate the user folder with the file manager\'s side panel manually.</string> | ||||
| 
 | ||||
|     <!-- General settings strings --> | ||||
|     <string name="frame_limit_enable">Enable limit speed</string> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user