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.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="org.yuzu.yuzu_emu.YuzuApplication"
|
android:name="org.yuzu.yuzu_emu.YuzuApplication"
|
||||||
|
|
|
@ -15,23 +15,29 @@ import java.io.File
|
||||||
fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
|
fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
|
||||||
|
|
||||||
class YuzuApplication : Application() {
|
class YuzuApplication : Application() {
|
||||||
private fun createNotificationChannel() {
|
private fun createNotificationChannels() {
|
||||||
// Create the NotificationChannel, but only on API 26+ because
|
val emulationChannel = NotificationChannel(
|
||||||
// the NotificationChannel class is new and not in the support library
|
getString(R.string.emulation_notification_channel_id),
|
||||||
val name: CharSequence = getString(R.string.app_notification_channel_name)
|
getString(R.string.emulation_notification_channel_name),
|
||||||
val description = getString(R.string.app_notification_channel_description)
|
|
||||||
val channel = NotificationChannel(
|
|
||||||
getString(R.string.app_notification_channel_id),
|
|
||||||
name,
|
|
||||||
NotificationManager.IMPORTANCE_LOW
|
NotificationManager.IMPORTANCE_LOW
|
||||||
)
|
)
|
||||||
channel.description = description
|
emulationChannel.description = getString(R.string.emulation_notification_channel_description)
|
||||||
channel.setSound(null, null)
|
emulationChannel.setSound(null, null)
|
||||||
channel.vibrationPattern = 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
|
// Register the channel with the system; you can't change the importance
|
||||||
// or other notification behaviors after this
|
// or other notification behaviors after this
|
||||||
val notificationManager = getSystemService(NotificationManager::class.java)
|
val notificationManager = getSystemService(NotificationManager::class.java)
|
||||||
notificationManager.createNotificationChannel(channel)
|
notificationManager.createNotificationChannel(emulationChannel)
|
||||||
|
notificationManager.createNotificationChannel(noticeChannel)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
@ -42,8 +48,7 @@ class YuzuApplication : Application() {
|
||||||
GpuDriverHelper.initializeDriverParameters(applicationContext)
|
GpuDriverHelper.initializeDriverParameters(applicationContext)
|
||||||
NativeLibrary.logDeviceInfo()
|
NativeLibrary.logDeviceInfo()
|
||||||
|
|
||||||
// TODO(bunnei): Disable notifications until we support app suspension.
|
createNotificationChannels();
|
||||||
//createNotificationChannel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -3,14 +3,21 @@
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.DocumentsContract
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
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.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.fragment.app.Fragment
|
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.R
|
||||||
import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
|
import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
|
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.ui.SettingsActivity
|
||||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
||||||
import org.yuzu.yuzu_emu.model.HomeSetting
|
import org.yuzu.yuzu_emu.model.HomeSetting
|
||||||
|
@ -49,6 +57,11 @@ class HomeSettingsFragment : Fragment() {
|
||||||
R.string.settings_description,
|
R.string.settings_description,
|
||||||
R.drawable.ic_settings
|
R.drawable.ic_settings
|
||||||
) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") },
|
) { 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(
|
HomeSetting(
|
||||||
R.string.install_gpu_driver,
|
R.string.install_gpu_driver,
|
||||||
R.string.install_gpu_driver_description,
|
R.string.install_gpu_driver_description,
|
||||||
|
@ -84,6 +97,82 @@ class HomeSettingsFragment : Fragment() {
|
||||||
_binding = null
|
_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() {
|
private fun driverInstaller() {
|
||||||
// Get the driver name for the dialog message.
|
// Get the driver name for the dialog message.
|
||||||
var driverName = GpuDriverHelper.customDriverName
|
var driverName = GpuDriverHelper.customDriverName
|
||||||
|
|
|
@ -4,11 +4,13 @@
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
@ -63,6 +65,10 @@ class SetupFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
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
|
mainActivity = requireActivity() as MainActivity
|
||||||
|
|
||||||
homeViewModel.setNavigationVisibility(false)
|
homeViewModel.setNavigationVisibility(false)
|
||||||
|
@ -219,6 +225,11 @@ class SetupFragment : Fragment() {
|
||||||
_binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val pushNotificationPermissionLauncher =
|
||||||
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||||
|
// TODO: Show proper notification request reason and confirmation
|
||||||
|
}
|
||||||
|
|
||||||
private fun finishSetup() {
|
private fun finishSetup() {
|
||||||
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
|
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
|
||||||
.putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
|
.putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
|
||||||
|
|
|
@ -29,10 +29,10 @@ class ForegroundService : Service() {
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
val builder =
|
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)
|
.setSmallIcon(R.drawable.ic_stat_notification_logo)
|
||||||
.setContentTitle(getString(R.string.app_name))
|
.setContentTitle(getString(R.string.app_name))
|
||||||
.setContentText(getString(R.string.app_notification_running))
|
.setContentText(getString(R.string.emulation_notification_running))
|
||||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setVibrate(null)
|
.setVibrate(null)
|
||||||
|
|
|
@ -4,10 +4,14 @@
|
||||||
<!-- General application strings -->
|
<!-- General application strings -->
|
||||||
<string name="app_name" translatable="false">yuzu</string>
|
<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_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="emulation_notification_channel_name">Emulation is Active</string>
|
||||||
<string name="app_notification_channel_id" translatable="false">yuzu</string>
|
<string name="emulation_notification_channel_id" translatable="false">emulationIsActive</string>
|
||||||
<string name="app_notification_channel_description">yuzu Switch emulator notifications</string>
|
<string name="emulation_notification_channel_description">Shows a persistent notification when emulation is running.</string>
|
||||||
<string name="app_notification_running">yuzu 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 -->
|
<!-- Setup strings -->
|
||||||
<string name="welcome">Welcome!</string>
|
<string name="welcome">Welcome!</string>
|
||||||
|
@ -29,14 +33,14 @@
|
||||||
<!-- Home strings -->
|
<!-- Home strings -->
|
||||||
<string name="home_games">Games</string>
|
<string name="home_games">Games</string>
|
||||||
<string name="home_settings">Settings</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="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">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_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="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
|
||||||
<string name="home_search_games">Search Games</string>
|
<string name="home_search_games">Search Games</string>
|
||||||
<string name="games_dir_selected">Games directory selected</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_description">Required to decrypt retail games</string>
|
||||||
<string name="install_prod_keys_warning">Skip adding keys?</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>
|
<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_help">Help</string>
|
||||||
<string name="warning_skip">Skip</string>
|
<string name="warning_skip">Skip</string>
|
||||||
<string name="warning_cancel">Cancel</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="install_amiibo_keys_description">Required to use Amiibo in game</string>
|
||||||
<string name="invalid_keys_file">Invalid keys file selected</string>
|
<string name="invalid_keys_file">Invalid keys file selected</string>
|
||||||
<string name="install_keys_success">Keys successfully installed</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_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_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="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="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 -->
|
<!-- General settings strings -->
|
||||||
<string name="frame_limit_enable">Enable limit speed</string>
|
<string name="frame_limit_enable">Enable limit speed</string>
|
||||||
|
|
Reference in New Issue