ui: add view to debug MDM settings

Adds a view to see the currently set MDM settings, we're going to need this to debug actual MDM integrations more effectively.

Signed-off-by: Andrea Gottardo <andrea@gottardo.me>
pull/189/head
Andrea Gottardo 2 months ago
parent 01034ab5be
commit 8ebec955e4

@ -22,6 +22,7 @@ import com.tailscale.ipn.ui.theme.AppTheme
import com.tailscale.ipn.ui.view.AboutView
import com.tailscale.ipn.ui.view.BugReportView
import com.tailscale.ipn.ui.view.ExitNodePicker
import com.tailscale.ipn.ui.view.MDMSettingsDebugView
import com.tailscale.ipn.ui.view.MainView
import com.tailscale.ipn.ui.view.MainViewNavigation
import com.tailscale.ipn.ui.view.PeerDetails
@ -58,7 +59,8 @@ class MainActivity : ComponentActivity() {
val settingsNav = SettingsNav(
onNavigateToBugReport = { navController.navigate("bugReport") },
onNavigateToAbout = { navController.navigate("about") }
onNavigateToAbout = { navController.navigate("about") },
onNavigateToMDMSettings = { navController.navigate("mdmSettings") }
)
composable("main") {
@ -80,6 +82,9 @@ class MainActivity : ComponentActivity() {
composable("about") {
AboutView()
}
composable("mdmSettings") {
MDMSettingsDebugView(manager.mdmSettings)
}
}
}
}

@ -4,31 +4,63 @@ import android.content.RestrictionsManager
import com.tailscale.ipn.App
class MDMSettings(private val restrictionsManager: RestrictionsManager? = null) {
// TODO(angott): implement a typed enum string array type
val hiddenNetworkDevices: List<NetworkDevices> = emptyList()
fun get(setting: BooleanSetting): Boolean {
restrictionsManager?.let { restrictionsManager ->
restrictionsManager.applicationRestrictions.containsKey(setting.key)
return restrictionsManager.applicationRestrictions.getBoolean(setting.key)
} ?: run {
return App.getApplication().encryptedPrefs.getBoolean(setting.key, false)
restrictionsManager?.let {
if (it.applicationRestrictions.containsKey(setting.key)) {
return it.applicationRestrictions.getBoolean(setting.key)
}
}
return App.getApplication().encryptedPrefs.getBoolean(setting.key, false)
}
fun get(setting: StringSetting): String? {
return App.getApplication().encryptedPrefs.getString(setting.key, null)
return restrictionsManager?.applicationRestrictions?.getString(setting.key)
?: App.getApplication().encryptedPrefs.getString(setting.key, null)
}
fun get(setting: AlwaysNeverUserDecidesSetting): AlwaysNeverUserDecidesValue {
val storedString = App.getApplication().encryptedPrefs.getString(setting.key, "user-decides")
val storedString: String =
restrictionsManager?.applicationRestrictions?.getString(setting.key)
?: App.getApplication().encryptedPrefs.getString(setting.key, null)
?: "user-decides"
return AlwaysNeverUserDecidesValue.valueOf(storedString)
return when (storedString) {
"always" -> {
AlwaysNeverUserDecidesValue.Always
}
"never" -> {
AlwaysNeverUserDecidesValue.Never
}
else -> {
AlwaysNeverUserDecidesValue.UserDecides
}
}
}
fun get(setting: ShowHideSetting): ShowHideValue {
val storedString = App.getApplication().encryptedPrefs.getString(setting.key, "show")
val storedString: String =
restrictionsManager?.applicationRestrictions?.getString(setting.key)
?: App.getApplication().encryptedPrefs.getString(setting.key, null)
?: "show"
return ShowHideValue.valueOf(storedString)
return when (storedString) {
"hide" -> {
ShowHideValue.Hide
}
else -> {
ShowHideValue.Show
}
}
}
fun get(setting: StringArraySetting): Array<String>? {
restrictionsManager?.let {
if (it.applicationRestrictions.containsKey(setting.key)) {
return it.applicationRestrictions.getStringArray(setting.key)
}
}
return App.getApplication().encryptedPrefs.getStringSet(setting.key, HashSet<String>())
?.toTypedArray()?.sortedArray()
}
}

@ -14,6 +14,10 @@ enum class StringSetting(val key: String, val localizedTitle: String) {
Tailnet("Tailnet", "Recommended/Required Tailnet Name"),
}
enum class StringArraySetting(val key: String, val localizedTitle: String) {
HiddenNetworkDevices("HiddenNetworkDevices", "Hidden Network Device Categories")
}
// A setting representing a String value which is set to either `always`, `never` or `user-decides`.
enum class AlwaysNeverUserDecidesSetting(val key: String, val localizedTitle: String) {
AllowIncomingConnections("AllowIncomingConnections", "Allow Incoming Connections"),

@ -0,0 +1,119 @@
package com.tailscale.ipn.ui.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.tailscale.ipn.R
import com.tailscale.ipn.mdm.AlwaysNeverUserDecidesSetting
import com.tailscale.ipn.mdm.BooleanSetting
import com.tailscale.ipn.mdm.MDMSettings
import com.tailscale.ipn.mdm.ShowHideSetting
import com.tailscale.ipn.mdm.StringArraySetting
import com.tailscale.ipn.mdm.StringSetting
import com.tailscale.ipn.ui.util.defaultPaddingModifier
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MDMSettingsDebugView(mdmSettings: MDMSettings) {
Scaffold(
topBar = {
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surfaceContainer,
titleContentColor = MaterialTheme.colorScheme.primary,
),
title = {
Text(stringResource(R.string.current_mdm_settings))
}
)
},
) { innerPadding ->
LazyColumn(modifier = Modifier.padding(innerPadding)) {
items(enumValues<BooleanSetting>()) { booleanSetting ->
MDMSettingView(
title = booleanSetting.localizedTitle,
caption = booleanSetting.key,
valueDescription = mdmSettings.get(booleanSetting).toString()
)
}
items(enumValues<StringSetting>()) { stringSetting ->
MDMSettingView(
title = stringSetting.localizedTitle,
caption = stringSetting.key,
valueDescription = mdmSettings.get(stringSetting).toString()
)
}
items(enumValues<ShowHideSetting>()) { showHideSetting ->
MDMSettingView(
title = showHideSetting.localizedTitle,
caption = showHideSetting.key,
valueDescription = mdmSettings.get(showHideSetting).toString()
)
}
items(enumValues<AlwaysNeverUserDecidesSetting>()) { anuSetting ->
MDMSettingView(
title = anuSetting.localizedTitle,
caption = anuSetting.key,
valueDescription = mdmSettings.get(anuSetting).toString()
)
}
items(enumValues<StringArraySetting>()) { stringArraySetting ->
MDMSettingView(
title = stringArraySetting.localizedTitle,
caption = stringArraySetting.key,
valueDescription = mdmSettings.get(stringArraySetting).toString()
)
}
}
}
}
@Composable
fun MDMSettingView(title: String, caption: String, valueDescription: String) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = defaultPaddingModifier()
.fillMaxWidth()
) {
Column {
Text(title, maxLines = 3)
Text(
caption,
fontSize = MaterialTheme.typography.labelSmall.fontSize,
color = MaterialTheme.colorScheme.tertiary,
fontFamily = FontFamily.Monospace
)
}
Text(
valueDescription,
color = MaterialTheme.colorScheme.secondary,
fontFamily = FontFamily.Monospace,
maxLines = 1,
fontWeight = FontWeight.SemiBold
)
}
}

@ -32,6 +32,7 @@ import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import com.tailscale.ipn.mdm.MDMSettings
import com.tailscale.ipn.R
import com.tailscale.ipn.ui.Links
import com.tailscale.ipn.ui.model.IpnLocal
@ -44,7 +45,8 @@ import com.tailscale.ipn.ui.viewModel.SettingsViewModel
data class SettingsNav(
val onNavigateToBugReport: () -> Unit,
val onNavigateToAbout: () -> Unit
val onNavigateToAbout: () -> Unit,
val onNavigateToMDMSettings: () -> Unit
)
@Composable

@ -73,7 +73,8 @@ class SettingsViewModel(val model: IpnModel, val ipnActions: IpnActions, val nav
// General settings, always enabled
SettingBundle(settings = listOf(
Setting(R.string.about, SettingType.NAV, onClick = { navigation.onNavigateToAbout()}, enabled = MutableStateFlow(true)),
Setting(R.string.bug_report, SettingType.NAV, onClick = { navigation.onNavigateToBugReport()}, enabled = MutableStateFlow(true))
Setting(R.string.bug_report, SettingType.NAV, onClick = { navigation.onNavigateToBugReport()}, enabled = MutableStateFlow(true)),
Setting(R.string.mdm_settings, SettingType.NAV, onClick = { navigation.onNavigateToMDMSettings()}, enabled = MutableStateFlow(true))
))
)

@ -45,5 +45,9 @@
<string name="os">OS</string>
<string name="key_expiry">Key Expiry</string>
<!-- Strings for MDM settings -->
<string name="current_mdm_settings">Current MDM Settings</string>
<string name="mdm_settings">MDM Settings</string>
</resources>

Loading…
Cancel
Save