mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -04:00
feat: ask for all files access and offer storage and Bluetooth permissions in Setup
This commit is contained in:
1 parent
abfcb338c4
commit
eb84def168
9 files changed
+112
-2
No files matched your search
@@ -7,6 +7,7 @@
|
||||
<!-- Session logs go to Downloads so a failed run can be handed over as one folder. -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<!-- Store download progress and "installed" / "failed" notices (Android 13+). -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
@@ -182,6 +182,7 @@ class MainActivity : ComponentActivity() {
|
||||
private val wifiLocationReceiver = object : android.content.BroadcastReceiver() {
|
||||
override fun onReceive(context: android.content.Context?, intent: Intent?) {
|
||||
refreshWifiDiscovery()
|
||||
refreshPermissionRows()
|
||||
}
|
||||
}
|
||||
private val mediaRefresh = Runnable {
|
||||
@@ -370,6 +371,8 @@ class MainActivity : ComponentActivity() {
|
||||
private var runningLabel by mutableStateOf<String?>(null)
|
||||
private var logsEnabled by mutableStateOf(true)
|
||||
private var agentCommands by mutableStateOf(false)
|
||||
private var bluetoothAllowed by mutableStateOf<Boolean?>(null)
|
||||
private var allFilesAllowed by mutableStateOf<Boolean?>(null)
|
||||
private var showRoms by mutableStateOf(false)
|
||||
private var homeAppSelected by mutableStateOf(false)
|
||||
private var homeScreenEnabled by mutableStateOf(false)
|
||||
@@ -569,6 +572,7 @@ class MainActivity : ComponentActivity() {
|
||||
desktopInstalled = desktopInstalled, desktopPresent = desktopPresent,
|
||||
offlineAccount = offlineAccount, offline = offline,
|
||||
frameGenLabel = frameGenLabel, romsDir = romsDir, logsEnabled = logsEnabled, agentCommands = agentCommands,
|
||||
bluetoothAllowed = bluetoothAllowed, allFilesAllowed = allFilesAllowed,
|
||||
steamGames = steamGames, emulators = emulatorList, running = runningLabel,
|
||||
frameGen = FrameGen.mode(this),
|
||||
lossless = lossless,
|
||||
@@ -716,6 +720,8 @@ class MainActivity : ComponentActivity() {
|
||||
com.droiddeck.launcher.agent.AgentAccess.setCommandsEnabled(this, !com.droiddeck.launcher.agent.AgentAccess.commandsEnabled(this))
|
||||
agentCommands = com.droiddeck.launcher.agent.AgentAccess.commandsEnabled(this)
|
||||
},
|
||||
onBluetoothAllowed = { askBluetooth() },
|
||||
onAllFilesAllowed = { com.droiddeck.launcher.core.AllFilesAccess.openSettings(this) },
|
||||
onShareLogs = {
|
||||
SessionLogShare.prepare(this, { SessionLogShare.latest(this) }) { zip ->
|
||||
if (zip == null) android.widget.Toast.makeText(this, R.string.main_no_logs_yet, android.widget.Toast.LENGTH_LONG).show()
|
||||
@@ -913,6 +919,14 @@ class MainActivity : ComponentActivity() {
|
||||
SessionPrefs.setMicAsked(this)
|
||||
}
|
||||
if (wanted.isNotEmpty()) requestPermissions(wanted.toTypedArray(), 1)
|
||||
else if (!com.droiddeck.launcher.core.AllFilesAccess.granted() && !SessionPrefs.allFilesAsked(this)) {
|
||||
Thread({
|
||||
if (com.droiddeck.launcher.core.AllFilesAccess.usesSharedStorage(this)) runOnUiThread {
|
||||
SessionPrefs.setAllFilesAsked(this)
|
||||
com.droiddeck.launcher.core.AllFilesAccess.ask(this)
|
||||
}
|
||||
}, "all-files-access").start()
|
||||
}
|
||||
// A store sign-in an earlier build left in plain text is sealed with the Keystore key.
|
||||
Thread({ com.droiddeck.launcher.stores.StoreAccounts.encryptAll(this) }, "store-credentials").start()
|
||||
// A session folder left without its ending - the process was killed - gets it now.
|
||||
@@ -993,6 +1007,26 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshPermissionRows() {
|
||||
bluetoothAllowed = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED else null
|
||||
allFilesAllowed = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) com.droiddeck.launcher.core.AllFilesAccess.granted() else null
|
||||
}
|
||||
|
||||
private fun askBluetooth() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return
|
||||
val permission = Manifest.permission.BLUETOOTH_CONNECT
|
||||
val granted = checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
|
||||
if (granted || (SessionPrefs.bluetoothAsked(this) && !shouldShowRequestPermissionRationale(permission))) {
|
||||
runCatching {
|
||||
startActivity(Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, android.net.Uri.parse("package:$packageName")))
|
||||
}
|
||||
} else {
|
||||
SessionPrefs.setBluetoothAsked(this)
|
||||
requestPermissions(arrayOf(permission), 3)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
orientation = SessionPrefs.orientation(this)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.droiddeck.launcher.core
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.Settings
|
||||
import com.droiddeck.launcher.R
|
||||
import com.droiddeck.launcher.frontend.AddedExes
|
||||
import com.droiddeck.launcher.stores.StoreInstallRoot
|
||||
|
||||
object AllFilesAccess {
|
||||
fun granted(): Boolean = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || Environment.isExternalStorageManager()
|
||||
|
||||
fun usesSharedStorage(context: Context): Boolean {
|
||||
val own = (listOf(context.filesDir.parentFile) + context.getExternalFilesDirs(null).map { it?.parentFile })
|
||||
.filterNotNull().map { it.absolutePath + "/" }
|
||||
fun shared(path: String) = own.none { (path + "/").startsWith(it) }
|
||||
return AddedExes.list(context).any { shared(it.folder) } ||
|
||||
StoreInstallRoot.roots(context).any { shared(it.absolutePath) }
|
||||
}
|
||||
|
||||
fun ask(activity: Activity) {
|
||||
if (granted() || activity.isFinishing) return
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.all_files_title)
|
||||
.setMessage(R.string.all_files_message)
|
||||
.setPositiveButton(R.string.all_files_allow) { _, _ -> openSettings(activity) }
|
||||
.setNegativeButton(R.string.all_files_later, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
fun openSettings(activity: Activity) {
|
||||
val forApp = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, Uri.parse("package:" + activity.packageName))
|
||||
try {
|
||||
activity.startActivity(forApp)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
runCatching { activity.startActivity(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.droiddeck.launcher.files
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
@@ -23,7 +24,9 @@ class FileManagerActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
requestedOrientation = com.droiddeck.launcher.session.SessionPrefs.orientation(this)
|
||||
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
com.droiddeck.launcher.core.AllFilesAccess.ask(this)
|
||||
} else if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 1)
|
||||
}
|
||||
val initialDir = intent.getStringExtra(EXTRA_START_DIR)?.let(::File)
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
@@ -36,7 +37,9 @@ class FilePickerActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
requestedOrientation = com.droiddeck.launcher.session.SessionPrefs.orientation(this)
|
||||
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
com.droiddeck.launcher.core.AllFilesAccess.ask(this)
|
||||
} else if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 1)
|
||||
}
|
||||
|
||||
|
||||
@@ -259,6 +259,12 @@ object SessionPrefs {
|
||||
prefs(context).edit().putBoolean("micAsked", true).apply()
|
||||
}
|
||||
|
||||
fun allFilesAsked(context: Context): Boolean = prefs(context).getBoolean("allFilesAsked", false)
|
||||
|
||||
fun setAllFilesAsked(context: Context) {
|
||||
prefs(context).edit().putBoolean("allFilesAsked", true).apply()
|
||||
}
|
||||
|
||||
fun bluetoothAsked(context: Context): Boolean = prefs(context).getBoolean("bluetoothAsked", false)
|
||||
|
||||
fun setBluetoothAsked(context: Context) {
|
||||
|
||||
@@ -97,6 +97,8 @@ class FrontEndState(
|
||||
val logsEnabled: Boolean,
|
||||
/** Setup's Debugging tools toggle (AgentAccess): off unless the user turns it on. */
|
||||
val agentCommands: Boolean = false,
|
||||
val bluetoothAllowed: Boolean? = null,
|
||||
val allFilesAllowed: Boolean? = null,
|
||||
val steamGames: List<Library.SteamGame>,
|
||||
val emulators: List<Library.Emulator>,
|
||||
val running: String?,
|
||||
@@ -198,6 +200,8 @@ class FrontEndActions(
|
||||
val onBrowseFiles: (File) -> Unit = {},
|
||||
val onLogs: () -> Unit,
|
||||
val onAgentCommands: () -> Unit = {},
|
||||
val onBluetoothAllowed: () -> Unit = {},
|
||||
val onAllFilesAllowed: () -> Unit = {},
|
||||
val onShareLogs: () -> Unit = {},
|
||||
val onClearLogs: () -> Unit = {},
|
||||
val onOffline: () -> Unit,
|
||||
|
||||
@@ -311,6 +311,12 @@ internal fun SetupPanel(
|
||||
}
|
||||
ToggleRow(host, "logs", stringResource(R.string.setup_logs), stringResource(R.string.setup_logs_hint), s.logsEnabled) { a.onLogs() }
|
||||
ToggleRow(host, "agent-commands", stringResource(R.string.setup_agent_commands), null, s.agentCommands) { a.onAgentCommands() }
|
||||
s.allFilesAllowed?.let { on ->
|
||||
ToggleRow(host, "all-files", stringResource(R.string.setup_all_files), stringResource(R.string.setup_all_files_hint), on) { a.onAllFilesAllowed() }
|
||||
}
|
||||
s.bluetoothAllowed?.let { on ->
|
||||
ToggleRow(host, "bluetooth", stringResource(R.string.setup_bluetooth), stringResource(R.string.setup_bluetooth_hint), on) { a.onBluetoothAllowed() }
|
||||
}
|
||||
ActionRow(stringResource(R.string.setup_latest_logs), stringResource(R.string.drawer_logs_hint), stringResource(R.string.drawer_share_logs), a.onShareLogs,
|
||||
progress = com.droiddeck.launcher.session.SessionLogShare.progress)
|
||||
ActionRow(stringResource(R.string.setup_saved_logs), stringResource(R.string.setup_saved_logs_hint, com.droiddeck.launcher.session.SessionPaths.KEEP_SESSIONS), stringResource(R.string.setup_clear_logs), a.onClearLogs)
|
||||
|
||||
@@ -1727,6 +1727,14 @@
|
||||
<!-- Label of the notification's reply field -->
|
||||
<string name="adbpair_code_label">6-digit pairing code</string>
|
||||
<string name="adbpair_code_hint">Tap Enter code and type the 6 digits shown in Settings.</string>
|
||||
<string name="setup_all_files">All files access</string>
|
||||
<string name="setup_all_files_hint">Lets DroidDeck see games and programs on your storage and SD card</string>
|
||||
<string name="setup_bluetooth">Bluetooth Steam Controller</string>
|
||||
<string name="setup_bluetooth_hint">Lets DroidDeck find a paired Steam Controller (Nearby devices)</string>
|
||||
<string name="all_files_title">Allow access to your game folders</string>
|
||||
<string name="all_files_message">DroidDeck runs games and programs from your phone\'s storage and SD card. Android only lets it see those files with All files access, which you can turn on for DroidDeck on the next screen.</string>
|
||||
<string name="all_files_allow">Open settings</string>
|
||||
<string name="all_files_later">Not now</string>
|
||||
<string name="adbpair_title_code">Enter the Wi-Fi pairing code</string>
|
||||
<string name="adbpair_title_retry">Try the code again</string>
|
||||
<!-- Notification action button -->
|
||||
|
||||
Reference in new issue
Block a user