@ -53,7 +53,7 @@ import com.tailscale.ipn.mdm.ShowHide
import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.ui.theme.AppTheme
import com.tailscale.ipn.ui.util.AndroidTVUtil
import com.tailscale.ipn.ui.util.AndroidTVUtil .isAndroidTV
import com.tailscale.ipn.ui.util.set
import com.tailscale.ipn.ui.util.universalFit
import com.tailscale.ipn.ui.view.AboutView
@ -177,7 +177,9 @@ class MainActivity : ComponentActivity() {
}
viewModel . setVpnPermissionLauncher ( vpnPermissionLauncher )
val directoryPickerLauncher =
var directoryPickerLauncher : ActivityResultLauncher < Uri ? > ? = null
if ( canOpenDocumentTree ( ) ) {
directoryPickerLauncher =
registerForActivityResult ( ActivityResultContracts . OpenDocumentTree ( ) ) { uri : Uri ? ->
if ( uri != null ) {
try {
@ -213,13 +215,15 @@ class MainActivity : ComponentActivity() {
}
} else {
TSLog . d (
" MainActivity " , " Taildrop directory not saved. Will fall back to internal storage. " )
" MainActivity " ,
" Taildrop directory not saved. Will fall back to internal storage. " )
// Fall back to internal storage.
}
}
viewModel . setDirectoryPickerLauncher ( directoryPickerLauncher )
}
setContent {
navController = rememberNavController ( )
@ -354,9 +358,11 @@ class MainActivity : ComponentActivity() {
{ navController . navigate ( " taildropDir " ) } ,
{ navController . navigate ( " notifications " ) } )
}
directoryPickerLauncher ?. let {
val launcher = it
composable ( " taildropDir " ) {
TaildropDirView (
backTo ( " permissions " ) , directoryPickerLauncher , permissionsViewModel )
TaildropDirView ( backTo ( " permissions " ) , launcher , permissionsViewModel )
}
}
composable ( " notifications " ) {
NotificationsView ( backTo ( " permissions " ) , :: openApplicationSettings )
@ -406,6 +412,16 @@ class MainActivity : ComponentActivity() {
lifecycleScope . launch { Notifier . loginFinished . collect { _ -> loginQRCode . set ( null ) } }
}
// Most AndroidTV's don't support this and the UX is completely broken regardless. We have
// reports of some old devices throwing ActivityNotFound exceptions on TV as well, so we
// carefully guard against the attempt.
private fun Context . canOpenDocumentTree ( ) : Boolean {
return !is AndroidTV ( ) &&
Intent ( Intent . ACTION _OPEN _DOCUMENT _TREE )
. addCategory ( Intent . CATEGORY _DEFAULT )
. resolveActivity ( packageManager ) != null
}
private fun showOtherVPNConflictDialog ( ) {
AlertDialog . Builder ( this )
. setTitle ( R . string . vpn _permission _denied )
@ -437,7 +453,7 @@ class MainActivity : ComponentActivity() {
// Returns true if we should render a QR code instead of launching a browser
// for login requests
private fun useQRCodeLogin ( ) : Boolean {
return AndroidTVUtil . isAndroidTV ( )
return isAndroidTV ( )
}
override fun onNewIntent ( intent : Intent ) {