android: Add user directory shortcut

This commit is contained in:
Charles Lombardo 2023-04-30 22:14:57 -04:00 committed by bunnei
parent 265b9139e0
commit 912bf6a0c6
6 changed files with 140 additions and 25 deletions

View file

@ -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"

View file

@ -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 {

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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.&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>
<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>