Draft implementation of new SACC elements
This commit is contained in:
@@ -229,30 +229,8 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
|||||||
if (Build.VERSION.SDK_INT >= 31) {
|
if (Build.VERSION.SDK_INT >= 31) {
|
||||||
val list = SoftApConfigurationCompat.BAND_TYPES.map { band ->
|
val list = SoftApConfigurationCompat.BAND_TYPES.map { band ->
|
||||||
val channels = capability.getSupportedChannelList(band)
|
val channels = capability.getSupportedChannelList(band)
|
||||||
if (channels.isNotEmpty()) StringBuilder().apply {
|
if (channels.isNotEmpty()) {
|
||||||
append(SoftApConfigurationCompat.bandLookup(band, true))
|
"${SoftApConfigurationCompat.bandLookup(band, true)} (${RangeInput.toString(channels)})"
|
||||||
append(" (")
|
|
||||||
channels.sort()
|
|
||||||
var pending: Int? = null
|
|
||||||
var last = channels[0]
|
|
||||||
append(last)
|
|
||||||
for (channel in channels.asSequence().drop(1)) {
|
|
||||||
if (channel == last + 1) pending = channel else {
|
|
||||||
pending?.let {
|
|
||||||
append('-')
|
|
||||||
append(it)
|
|
||||||
pending = null
|
|
||||||
}
|
|
||||||
append(',')
|
|
||||||
append(channel)
|
|
||||||
}
|
|
||||||
last = channel
|
|
||||||
}
|
|
||||||
pending?.let {
|
|
||||||
append('-')
|
|
||||||
append(it)
|
|
||||||
}
|
|
||||||
append(')')
|
|
||||||
} else null
|
} else null
|
||||||
}.filterNotNull()
|
}.filterNotNull()
|
||||||
if (list.isNotEmpty()) result.append(parent.getText(R.string.tethering_manage_wifi_supported_channels)
|
if (list.isNotEmpty()) result.append(parent.getText(R.string.tethering_manage_wifi_supported_channels)
|
||||||
|
|||||||
@@ -455,6 +455,13 @@ data class SoftApConfigurationCompat(
|
|||||||
fun testPlatformValidity(channels: SparseIntArray) = setChannelsCompat(staticBuilder, channels)
|
fun testPlatformValidity(channels: SparseIntArray) = setChannelsCompat(staticBuilder, channels)
|
||||||
@RequiresApi(30)
|
@RequiresApi(30)
|
||||||
fun testPlatformValidity(bssid: MacAddress) = setBssid(staticBuilder, bssid)
|
fun testPlatformValidity(bssid: MacAddress) = setBssid(staticBuilder, bssid)
|
||||||
|
@RequiresApi(33)
|
||||||
|
fun testPlatformValidity(vendorElements: List<ScanResult.InformationElement>) =
|
||||||
|
setVendorElements(staticBuilder, vendorElements)
|
||||||
|
@RequiresApi(33)
|
||||||
|
fun testPlatformValidity(band: Int, channels: IntArray) = setAllowedAcsChannels(staticBuilder, band, channels)
|
||||||
|
@RequiresApi(33)
|
||||||
|
fun testPlatformValidity(bandwidth: Int) = setMaxChannelBandwidth(staticBuilder, bandwidth)
|
||||||
@RequiresApi(30)
|
@RequiresApi(30)
|
||||||
fun testPlatformTimeoutValidity(timeout: Long) = setShutdownTimeoutMillis(staticBuilder, timeout)
|
fun testPlatformTimeoutValidity(timeout: Long) = setShutdownTimeoutMillis(staticBuilder, timeout)
|
||||||
@RequiresApi(33)
|
@RequiresApi(33)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.annotation.TargetApi
|
|||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipDescription
|
import android.content.ClipDescription
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.net.wifi.ScanResult
|
||||||
import android.net.wifi.SoftApConfiguration
|
import android.net.wifi.SoftApConfiguration
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
@@ -19,6 +20,7 @@ import android.widget.AdapterView
|
|||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.os.persistableBundleOf
|
import androidx.core.os.persistableBundleOf
|
||||||
@@ -33,6 +35,7 @@ import be.mygod.vpnhotspot.databinding.DialogWifiApBinding
|
|||||||
import be.mygod.vpnhotspot.net.MacAddressCompat
|
import be.mygod.vpnhotspot.net.MacAddressCompat
|
||||||
import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor
|
import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor
|
||||||
import be.mygod.vpnhotspot.util.QRCodeDialog
|
import be.mygod.vpnhotspot.util.QRCodeDialog
|
||||||
|
import be.mygod.vpnhotspot.util.RangeInput
|
||||||
import be.mygod.vpnhotspot.util.readableMessage
|
import be.mygod.vpnhotspot.util.readableMessage
|
||||||
import be.mygod.vpnhotspot.util.showAllowingStateLoss
|
import be.mygod.vpnhotspot.util.showAllowingStateLoss
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
@@ -78,6 +81,13 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
ChannelOption(SoftApConfigurationCompat.BAND_5GHZ)) + channels5G
|
ChannelOption(SoftApConfigurationCompat.BAND_5GHZ)) + channels5G
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@get:RequiresApi(30)
|
||||||
|
private val bandWidthOptions by lazy {
|
||||||
|
SoftApInfo.channelWidthLookup.lookup.let { lookup ->
|
||||||
|
Array(lookup.size()) { BandWidth(lookup.keyAt(it), lookup.valueAt(it).substring(14)) }.apply { sort() }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -104,6 +114,11 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
} else "${SoftApConfigurationCompat.channelToFrequency(band, channel)} MHz ($channel)"
|
} else "${SoftApConfigurationCompat.channelToFrequency(band, channel)} MHz ($channel)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class BandWidth(val width: Int, val name: String = "") : Comparable<BandWidth> {
|
||||||
|
override fun compareTo(other: BandWidth) = width - other.width
|
||||||
|
override fun toString() = name
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var dialogView: DialogWifiApBinding
|
private lateinit var dialogView: DialogWifiApBinding
|
||||||
private lateinit var base: SoftApConfigurationCompat
|
private lateinit var base: SoftApConfigurationCompat
|
||||||
private var pasted = false
|
private var pasted = false
|
||||||
@@ -113,6 +128,13 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
RepeaterService.safeMode -> p2pSafeOptions
|
RepeaterService.safeMode -> p2pSafeOptions
|
||||||
else -> p2pUnsafeOptions
|
else -> p2pUnsafeOptions
|
||||||
}
|
}
|
||||||
|
private val acsList by lazy {
|
||||||
|
listOf(
|
||||||
|
Triple(SoftApConfigurationCompat.BAND_2GHZ, dialogView.acs2g, dialogView.acs2gWrapper),
|
||||||
|
Triple(SoftApConfigurationCompat.BAND_5GHZ, dialogView.acs5g, dialogView.acs5gWrapper),
|
||||||
|
Triple(SoftApConfigurationCompat.BAND_6GHZ, dialogView.acs6g, dialogView.acs6gWrapper),
|
||||||
|
)
|
||||||
|
}
|
||||||
override val ret get() = Arg(generateConfig())
|
override val ret get() = Arg(generateConfig())
|
||||||
|
|
||||||
private fun generateChannels() = SparseIntArray(2).apply {
|
private fun generateChannels() = SparseIntArray(2).apply {
|
||||||
@@ -121,6 +143,13 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
}
|
}
|
||||||
(dialogView.bandPrimary.selectedItem as ChannelOption).apply { put(band, channel) }
|
(dialogView.bandPrimary.selectedItem as ChannelOption).apply { put(band, channel) }
|
||||||
}
|
}
|
||||||
|
private fun generateVendorElements() = (dialogView.vendorElements.text ?: "").split("\n").map { line ->
|
||||||
|
if (line.isBlank()) return@map null
|
||||||
|
require(line.length % 2 == 0) { "Input should be hex: $line" }
|
||||||
|
(0 until line.length / 2).map {
|
||||||
|
Integer.parseInt(line.substring(it * 2, it * 2 + 2), 16).toByte()
|
||||||
|
}.toByteArray()
|
||||||
|
}.filterNotNull().map { ScanResult.InformationElement(221, 0, it) }
|
||||||
private fun generateConfig(full: Boolean = true) = base.copy(
|
private fun generateConfig(full: Boolean = true) = base.copy(
|
||||||
ssid = dialogView.ssid.text.toString(),
|
ssid = dialogView.ssid.text.toString(),
|
||||||
passphrase = if (dialogView.password.length() != 0) dialogView.password.text.toString() else null).apply {
|
passphrase = if (dialogView.password.length() != 0) dialogView.password.text.toString() else null).apply {
|
||||||
@@ -153,6 +182,12 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
bridgedModeOpportunisticShutdownTimeoutMillis = dialogView.bridgedTimeout.text.let { text ->
|
bridgedModeOpportunisticShutdownTimeoutMillis = dialogView.bridgedTimeout.text.let { text ->
|
||||||
if (text.isNullOrEmpty()) -1L else text.toString().toLong()
|
if (text.isNullOrEmpty()) -1L else text.toString().toLong()
|
||||||
}
|
}
|
||||||
|
vendorElements = generateVendorElements()
|
||||||
|
persistentRandomizedMacAddress = if (dialogView.persistentRandomizedMac.length() != 0) {
|
||||||
|
MacAddressCompat.fromString(dialogView.persistentRandomizedMac.text.toString()).toPlatform()
|
||||||
|
} else null
|
||||||
|
allowedAcsChannels = acsList.associate { (band, text, _) -> band to RangeInput.fromString(text.text) }
|
||||||
|
maxChannelBandwidth = dialogView.maxChannelBandwidth.selectedItemId.toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,10 +268,28 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
dialogView.bridgedTimeoutWrapper.helperText = getString(R.string.wifi_hotspot_timeout_default,
|
dialogView.bridgedTimeoutWrapper.helperText = getString(R.string.wifi_hotspot_timeout_default,
|
||||||
TetherTimeoutMonitor.defaultTimeoutBridged)
|
TetherTimeoutMonitor.defaultTimeoutBridged)
|
||||||
}
|
}
|
||||||
|
if (Build.VERSION.SDK_INT < 33) dialogView.vendorElementsWrapper.isGone = true
|
||||||
|
else if (!arg.readOnly) dialogView.vendorElements.addTextChangedListener(this@WifiApDialogFragment)
|
||||||
if (arg.p2pMode || Build.VERSION.SDK_INT < 33) {
|
if (arg.p2pMode || Build.VERSION.SDK_INT < 33) {
|
||||||
dialogView.ieee80211be.isGone = true
|
dialogView.ieee80211be.isGone = true
|
||||||
dialogView.bridgedTimeout.isEnabled = false
|
dialogView.bridgedTimeout.isEnabled = false
|
||||||
} else dialogView.bridgedTimeout.addTextChangedListener(this@WifiApDialogFragment)
|
dialogView.persistentRandomizedMacWrapper.isGone = true
|
||||||
|
for ((_, _, wrapper) in acsList) wrapper.isGone = true
|
||||||
|
dialogView.maxChannelBandwidth.isGone = true
|
||||||
|
} else {
|
||||||
|
dialogView.maxChannelBandwidth.adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0,
|
||||||
|
bandWidthOptions).apply {
|
||||||
|
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
|
}
|
||||||
|
if (!arg.readOnly) {
|
||||||
|
dialogView.bridgedTimeout.addTextChangedListener(this@WifiApDialogFragment)
|
||||||
|
dialogView.persistentRandomizedMac.addTextChangedListener(this@WifiApDialogFragment)
|
||||||
|
for ((_, text, _) in acsList) text.addTextChangedListener(this@WifiApDialogFragment)
|
||||||
|
dialogView.acs5g.addTextChangedListener(this@WifiApDialogFragment)
|
||||||
|
dialogView.acs6g.addTextChangedListener(this@WifiApDialogFragment)
|
||||||
|
dialogView.maxChannelBandwidth.onItemSelectedListener = this@WifiApDialogFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
base = arg.configuration
|
base = arg.configuration
|
||||||
populateFromConfiguration()
|
populateFromConfiguration()
|
||||||
}
|
}
|
||||||
@@ -279,6 +332,23 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
dialogView.bridgedTimeout.setText(base.bridgedModeOpportunisticShutdownTimeoutMillis.let {
|
dialogView.bridgedTimeout.setText(base.bridgedModeOpportunisticShutdownTimeoutMillis.let {
|
||||||
if (it == -1L) "" else it.toString()
|
if (it == -1L) "" else it.toString()
|
||||||
})
|
})
|
||||||
|
dialogView.vendorElements.setText(base.vendorElements.joinToString("\n") { element ->
|
||||||
|
element.bytes.let { buffer ->
|
||||||
|
StringBuilder().apply {
|
||||||
|
while (buffer.hasRemaining()) append("%02x".format(buffer.get()))
|
||||||
|
}.toString()
|
||||||
|
}.also {
|
||||||
|
if (element.id != 221 || element.idExt != 0 || it.isEmpty()) Timber.w(Exception(
|
||||||
|
"Unexpected InformationElement ${element.id}, ${element.idExt}, $it"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dialogView.persistentRandomizedMac.setText(base.persistentRandomizedMacAddress?.toString())
|
||||||
|
for ((band, text, _) in acsList) text.setText(RangeInput.toString(base.allowedAcsChannels[band]))
|
||||||
|
if (Build.VERSION.SDK_INT >= 33) bandWidthOptions.binarySearch(BandWidth(base.maxChannelBandwidth)).let {
|
||||||
|
if (it < 0) {
|
||||||
|
Timber.w(Exception("Cannot locate bandwidth ${base.maxChannelBandwidth}"))
|
||||||
|
} else dialogView.maxChannelBandwidth.setSelection(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
@@ -376,8 +446,49 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialogView.bridgedTimeoutWrapper.error = bridgedTimeoutError
|
dialogView.bridgedTimeoutWrapper.error = bridgedTimeoutError
|
||||||
|
val vendorElementsError = if (Build.VERSION.SDK_INT >= 33) {
|
||||||
|
try {
|
||||||
|
generateVendorElements().also {
|
||||||
|
if (!arg.p2pMode) SoftApConfigurationCompat.testPlatformValidity(it)
|
||||||
|
}
|
||||||
|
null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.readableMessage
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
dialogView.vendorElementsWrapper.error = vendorElementsError
|
||||||
|
dialogView.persistentRandomizedMacWrapper.error = null
|
||||||
|
val persistentRandomizedMacValid = dialogView.persistentRandomizedMac.length() == 0 || try {
|
||||||
|
MacAddressCompat.fromString(dialogView.persistentRandomizedMac.text.toString())
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
dialogView.persistentRandomizedMacWrapper.error = e.readableMessage
|
||||||
|
false
|
||||||
|
}
|
||||||
|
val acsNoError = if (!arg.p2pMode && Build.VERSION.SDK_INT >= 33) acsList.all { (band, text, wrapper) ->
|
||||||
|
try {
|
||||||
|
wrapper.error = null
|
||||||
|
SoftApConfigurationCompat.testPlatformValidity(band, RangeInput.fromString(text.text).toIntArray())
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
wrapper.error = e.readableMessage
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else true
|
||||||
|
val bandwidthError = if (!arg.p2pMode && Build.VERSION.SDK_INT >= 33) {
|
||||||
|
try {
|
||||||
|
SoftApConfigurationCompat.testPlatformValidity(
|
||||||
|
(dialogView.maxChannelBandwidth.selectedItem as BandWidth).width)
|
||||||
|
null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.readableMessage
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
dialogView.maxChannelBandwidthError.isGone = bandwidthError.isNullOrEmpty()
|
||||||
|
dialogView.maxChannelBandwidthError.text = bandwidthError
|
||||||
val canCopy = timeoutError == null && bssidValid && maxClientError == null && listsNoError &&
|
val canCopy = timeoutError == null && bssidValid && maxClientError == null && listsNoError &&
|
||||||
bridgedTimeoutError == null
|
bridgedTimeoutError == null && vendorElementsError == null && persistentRandomizedMacValid &&
|
||||||
|
acsNoError && bandwidthError == null
|
||||||
(dialog as? AlertDialog)?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled =
|
(dialog as? AlertDialog)?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled =
|
||||||
ssidLengthOk && passwordValid && bandError == null && canCopy
|
ssidLengthOk && passwordValid && bandError == null && canCopy
|
||||||
dialogView.toolbar.menu.findItem(android.R.id.copy).isEnabled = canCopy
|
dialogView.toolbar.menu.findItem(android.R.id.copy).isEnabled = canCopy
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import timber.log.Timber
|
|||||||
|
|
||||||
class ConstantLookup(private val prefix: String, private val lookup29: Array<out String?>,
|
class ConstantLookup(private val prefix: String, private val lookup29: Array<out String?>,
|
||||||
private val clazz: () -> Class<*>) {
|
private val clazz: () -> Class<*>) {
|
||||||
private val lookup by lazy {
|
val lookup by lazy {
|
||||||
SparseArrayCompat<String>().apply {
|
SparseArrayCompat<String>().apply {
|
||||||
for (field in clazz().declaredFields) try {
|
for (field in clazz().declaredFields) try {
|
||||||
if (field?.type == Int::class.java && field.name.startsWith(prefix)) put(field.getInt(null), field.name)
|
if (field?.type == Int::class.java && field.name.startsWith(prefix)) put(field.getInt(null), field.name)
|
||||||
|
|||||||
41
mobile/src/main/java/be/mygod/vpnhotspot/util/RangeInput.kt
Normal file
41
mobile/src/main/java/be/mygod/vpnhotspot/util/RangeInput.kt
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package be.mygod.vpnhotspot.util
|
||||||
|
|
||||||
|
object RangeInput {
|
||||||
|
fun toString(input: IntArray) = StringBuilder().apply {
|
||||||
|
if (input.isEmpty()) return@apply
|
||||||
|
input.sort()
|
||||||
|
var pending: Int? = null
|
||||||
|
var last = input[0]
|
||||||
|
append(last)
|
||||||
|
for (channel in input.asSequence().drop(1)) {
|
||||||
|
if (channel == last + 1) pending = channel else {
|
||||||
|
pending?.let {
|
||||||
|
append('-')
|
||||||
|
append(it)
|
||||||
|
pending = null
|
||||||
|
}
|
||||||
|
append(",\u200b") // zero-width space to save space
|
||||||
|
append(channel)
|
||||||
|
}
|
||||||
|
last = channel
|
||||||
|
}
|
||||||
|
pending?.let {
|
||||||
|
append('-')
|
||||||
|
append(it)
|
||||||
|
}
|
||||||
|
}.toString()
|
||||||
|
fun toString(input: Set<Int>?) = input?.run { toString(toIntArray()) }
|
||||||
|
|
||||||
|
fun fromString(input: CharSequence?, min: Int = 1, max: Int = 999) = mutableSetOf<Int>().apply {
|
||||||
|
if (input == null) return@apply
|
||||||
|
for (unit in input.split(',')) {
|
||||||
|
if (unit.isBlank()) continue
|
||||||
|
val blocks = unit.split('-', limit = 2).map { it.trim().toInt() }
|
||||||
|
require(blocks[0] in min..max) { "Out of range: ${blocks[0]}" }
|
||||||
|
if (blocks.size == 2) {
|
||||||
|
require(blocks[1] in min..max) { "Out of range: ${blocks[1]}" }
|
||||||
|
addAll(blocks[0]..blocks[1])
|
||||||
|
} else add(blocks[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,8 +54,7 @@
|
|||||||
android:id="@+id/ssid"
|
android:id="@+id/ssid"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/wifi_item_edit_content"
|
style="@style/wifi_item_edit_content" />
|
||||||
android:inputType="textMultiLine|textNoSuggestions" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/security_wrapper"
|
android:id="@+id/security_wrapper"
|
||||||
@@ -123,6 +122,32 @@
|
|||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:maxLength="19" />
|
android:maxLength="19" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/bridged_mode_opportunistic_shutdown"
|
||||||
|
style="@style/wifi_item_label"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dip"
|
||||||
|
android:minHeight="@dimen/touch_target_min"
|
||||||
|
android:text="@string/wifi_bridged_mode_opportunistic_shutdown" />
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/bridged_timeout_wrapper"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dip"
|
||||||
|
android:hint="@string/wifi_hotspot_timeout_bridged"
|
||||||
|
app:counterEnabled="true"
|
||||||
|
app:counterMaxLength="19"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
app:suffixText="ms">
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/bridged_timeout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/wifi_item_edit_content"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLength="19" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/band_group"
|
android:id="@+id/band_group"
|
||||||
@@ -130,10 +155,14 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dip"
|
android:layout_marginTop="8dip"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
<com.google.android.material.divider.MaterialDivider
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/wifi_item_divider" />
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/wifi_item_label"
|
style="@style/wifi_item_subhead"
|
||||||
android:text="@string/wifi_hotspot_ap_band_title" />
|
android:text="@string/wifi_hotspot_ap_band_title" />
|
||||||
<Spinner
|
<Spinner
|
||||||
android:id="@+id/band_primary"
|
android:id="@+id/band_primary"
|
||||||
@@ -159,6 +188,73 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:text="error text placeholder"
|
tools:text="error text placeholder"
|
||||||
tools:visibility="visible"/>
|
tools:visibility="visible"/>
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/acs_2g_wrapper"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dip"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
android:hint="@string/wifi_hotspot_acs_channel_2g">
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/acs_2g"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/wifi_item_edit_content"
|
||||||
|
android:imeOptions="flagForceAscii"
|
||||||
|
android:inputType="textNoSuggestions" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/acs_5g_wrapper"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dip"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
android:hint="@string/wifi_hotspot_acs_channel_5g">
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/acs_5g"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/wifi_item_edit_content"
|
||||||
|
android:imeOptions="flagForceAscii"
|
||||||
|
android:inputType="textNoSuggestions" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/acs_6g_wrapper"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dip"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
android:hint="@string/wifi_hotspot_acs_channel_6g">
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/acs_6g"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/wifi_item_edit_content"
|
||||||
|
android:imeOptions="flagForceAscii"
|
||||||
|
android:inputType="textNoSuggestions" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/wifi_item_label"
|
||||||
|
android:text="@string/wifi_hotspot_max_channel_bandwidth" />
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/max_channel_bandwidth"
|
||||||
|
style="@style/wifi_item_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="@dimen/touch_target_min"
|
||||||
|
android:prompt="@string/wifi_hotspot_max_channel_bandwidth" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_channel_bandwidth_error"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dip"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.Design.Error"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="error text placeholder"
|
||||||
|
tools:visibility="visible"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -212,6 +308,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/wifi_item_edit_content"
|
style="@style/wifi_item_edit_content"
|
||||||
|
android:imeOptions="flagForceAscii"
|
||||||
android:inputType="textMultiLine|textNoSuggestions" />
|
android:inputType="textMultiLine|textNoSuggestions" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
@@ -226,6 +323,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/wifi_item_edit_content"
|
style="@style/wifi_item_edit_content"
|
||||||
|
android:imeOptions="flagForceAscii"
|
||||||
android:inputType="textMultiLine|textNoSuggestions" />
|
android:inputType="textMultiLine|textNoSuggestions" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -258,6 +356,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/wifi_item_edit_content"
|
style="@style/wifi_item_edit_content"
|
||||||
|
android:imeOptions="flagForceAscii"
|
||||||
android:inputType="textNoSuggestions"
|
android:inputType="textNoSuggestions"
|
||||||
android:maxLength="17" />
|
android:maxLength="17" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
@@ -274,6 +373,24 @@
|
|||||||
android:minHeight="@dimen/touch_target_min"
|
android:minHeight="@dimen/touch_target_min"
|
||||||
android:entries="@array/wifi_mac_randomization"
|
android:entries="@array/wifi_mac_randomization"
|
||||||
android:prompt="@string/wifi_mac_randomization" />
|
android:prompt="@string/wifi_mac_randomization" />
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/persistent_randomized_mac_wrapper"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dip"
|
||||||
|
app:counterEnabled="true"
|
||||||
|
app:counterMaxLength="17"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
android:hint="@string/wifi_advanced_mac_address_persistent_randomized">
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/persistent_randomized_mac"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/wifi_item_edit_content"
|
||||||
|
android:imeOptions="flagForceAscii"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
android:maxLength="17" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
android:id="@+id/hidden_ssid"
|
android:id="@+id/hidden_ssid"
|
||||||
style="@style/wifi_item_label"
|
style="@style/wifi_item_label"
|
||||||
@@ -298,32 +415,6 @@
|
|||||||
android:layout_marginTop="8dip"
|
android:layout_marginTop="8dip"
|
||||||
android:minHeight="@dimen/touch_target_min"
|
android:minHeight="@dimen/touch_target_min"
|
||||||
android:text="@string/wifi_ieee_80211be" />
|
android:text="@string/wifi_ieee_80211be" />
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
|
||||||
android:id="@+id/bridged_mode_opportunistic_shutdown"
|
|
||||||
style="@style/wifi_item_label"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dip"
|
|
||||||
android:minHeight="@dimen/touch_target_min"
|
|
||||||
android:text="@string/wifi_bridged_mode_opportunistic_shutdown" />
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/bridged_timeout_wrapper"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dip"
|
|
||||||
android:hint="@string/wifi_hotspot_timeout_bridged"
|
|
||||||
app:counterEnabled="true"
|
|
||||||
app:counterMaxLength="19"
|
|
||||||
app:errorEnabled="true"
|
|
||||||
app:suffixText="ms">
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/bridged_timeout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/wifi_item_edit_content"
|
|
||||||
android:inputType="number"
|
|
||||||
android:maxLength="19" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
android:id="@+id/user_config"
|
android:id="@+id/user_config"
|
||||||
style="@style/wifi_item_label"
|
style="@style/wifi_item_label"
|
||||||
@@ -332,6 +423,21 @@
|
|||||||
android:layout_marginTop="8dip"
|
android:layout_marginTop="8dip"
|
||||||
android:minHeight="@dimen/touch_target_min"
|
android:minHeight="@dimen/touch_target_min"
|
||||||
android:text="@string/wifi_user_config" />
|
android:text="@string/wifi_user_config" />
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/vendor_elements_wrapper"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dip"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
android:hint="@string/wifi_vendor_elements">
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/vendor_elements"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/wifi_item_edit_content"
|
||||||
|
android:imeOptions="flagForceAscii"
|
||||||
|
android:inputType="textMultiLine|textNoSuggestions" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -183,19 +183,28 @@
|
|||||||
<string name="wifi_hotspot_ap_band_title" msgid="1165801173359290681">"AP 频段"</string>
|
<string name="wifi_hotspot_ap_band_title" msgid="1165801173359290681">"AP 频段"</string>
|
||||||
<string name="wifi_ap_choose_disabled">Disabled</string>
|
<string name="wifi_ap_choose_disabled">Disabled</string>
|
||||||
<string name="wifi_ap_choose_G" msgid="8724267386885036210">"%s GHz 频段"</string>
|
<string name="wifi_ap_choose_G" msgid="8724267386885036210">"%s GHz 频段"</string>
|
||||||
|
<string name="wifi_hotspot_acs_channel_2g">2.4 GHz ACS 可选频段</string>
|
||||||
|
<string name="wifi_hotspot_acs_channel_5g">5 GHz ACS 可选频段</string>
|
||||||
|
<string name="wifi_hotspot_acs_channel_6g">6 GHz ACS 可选频段</string>
|
||||||
|
<string name="wifi_hotspot_max_channel_bandwidth">最大频宽</string>
|
||||||
<string name="wifi_hotspot_access_control_title">访问控制</string>
|
<string name="wifi_hotspot_access_control_title">访问控制</string>
|
||||||
<string name="wifi_hotspot_ap_advanced_title">高级接入点设置</string>
|
<string name="wifi_hotspot_ap_advanced_title">高级接入点设置</string>
|
||||||
<string name="wifi_advanced_mac_address_title" msgid="6571335466330978393">"MAC 地址"</string>
|
<string name="wifi_advanced_mac_address_title" msgid="6571335466330978393">"MAC 地址"</string>
|
||||||
|
<string name="wifi_advanced_mac_address_persistent_randomized">持久性随机 MAC 地址</string>
|
||||||
<string name="wifi_hidden_network" msgid="973162091800925000">"隐藏的网络"</string>
|
<string name="wifi_hidden_network" msgid="973162091800925000">"隐藏的网络"</string>
|
||||||
<string name="wifi_max_clients">允许连接设备数上限</string>
|
<string name="wifi_max_clients">允许连接设备数上限</string>
|
||||||
<string name="wifi_client_user_control">过滤可以连接的设备</string>
|
<string name="wifi_client_user_control">过滤可以连接的设备</string>
|
||||||
<string name="wifi_blocked_list">设备黑名单</string>
|
<string name="wifi_blocked_list">设备黑名单</string>
|
||||||
<string name="wifi_allowed_list">设备白名单</string>
|
<string name="wifi_allowed_list">设备白名单</string>
|
||||||
<string name="wifi_mac_randomization">随机生成 MAC 地址</string>
|
<string name="wifi_mac_randomization">随机生成 MAC 地址</string>
|
||||||
|
<string name="wifi_mac_randomization_none">无</string>
|
||||||
|
<string name="wifi_mac_randomization_persistent">持久化</string>
|
||||||
|
<string name="wifi_mac_randomization_non_persistent">不持久化</string>
|
||||||
<string name="wifi_bridged_mode_opportunistic_shutdown">启用桥接模式伺机关闭</string>
|
<string name="wifi_bridged_mode_opportunistic_shutdown">启用桥接模式伺机关闭</string>
|
||||||
<string name="wifi_ieee_80211ax">启用 Wi\u2011Fi 6</string>
|
<string name="wifi_ieee_80211ax">启用 Wi\u2011Fi 6</string>
|
||||||
<string name="wifi_ieee_80211be">启用 Wi\u2011Fi 7</string>
|
<string name="wifi_ieee_80211be">启用 Wi\u2011Fi 7</string>
|
||||||
<string name="wifi_user_config">用户提供配置</string>
|
<string name="wifi_user_config">用户提供配置</string>
|
||||||
|
<string name="wifi_vendor_elements">供应商特定元素</string>
|
||||||
<string name="wifi_save" msgid="3331121567988522826">"保存"</string>
|
<string name="wifi_save" msgid="3331121567988522826">"保存"</string>
|
||||||
|
|
||||||
<!-- Based on: https://github.com/PrivacyApps/donations/blob/747d36a18433c7e9329691054122a8ad337a62d2/Donations/src/main/res/values-zh/donations__strings.xml -->
|
<!-- Based on: https://github.com/PrivacyApps/donations/blob/747d36a18433c7e9329691054122a8ad337a62d2/Donations/src/main/res/values-zh/donations__strings.xml -->
|
||||||
|
|||||||
@@ -207,9 +207,14 @@
|
|||||||
<string name="wifi_hotspot_ap_band_title">AP Band</string>
|
<string name="wifi_hotspot_ap_band_title">AP Band</string>
|
||||||
<string name="wifi_ap_choose_disabled">Disabled</string>
|
<string name="wifi_ap_choose_disabled">Disabled</string>
|
||||||
<string name="wifi_ap_choose_G">%s GHz Band</string>
|
<string name="wifi_ap_choose_G">%s GHz Band</string>
|
||||||
|
<string name="wifi_hotspot_acs_channel_2g">Allowed 2.4 GHz ACS channels</string>
|
||||||
|
<string name="wifi_hotspot_acs_channel_5g">Allowed 5 GHz ACS channels</string>
|
||||||
|
<string name="wifi_hotspot_acs_channel_6g">Allowed 6 GHz ACS channels</string>
|
||||||
|
<string name="wifi_hotspot_max_channel_bandwidth">Maximum channel bandwidth</string>
|
||||||
<string name="wifi_hotspot_access_control_title">Access Control</string>
|
<string name="wifi_hotspot_access_control_title">Access Control</string>
|
||||||
<string name="wifi_hotspot_ap_advanced_title">Advanced AP Options</string>
|
<string name="wifi_hotspot_ap_advanced_title">Advanced AP Options</string>
|
||||||
<string name="wifi_advanced_mac_address_title">MAC address</string>
|
<string name="wifi_advanced_mac_address_title">MAC address</string>
|
||||||
|
<string name="wifi_advanced_mac_address_persistent_randomized">Persistent Randomized MAC address</string>
|
||||||
<string name="wifi_hidden_network">Hidden network</string>
|
<string name="wifi_hidden_network">Hidden network</string>
|
||||||
<string name="wifi_max_clients">Maximum number of clients</string>
|
<string name="wifi_max_clients">Maximum number of clients</string>
|
||||||
<string name="wifi_client_user_control">Control which client can use hotspot</string>
|
<string name="wifi_client_user_control">Control which client can use hotspot</string>
|
||||||
@@ -223,7 +228,8 @@
|
|||||||
<string name="wifi_hotspot_timeout_bridged">Inactive timeout for a bridged instance</string>
|
<string name="wifi_hotspot_timeout_bridged">Inactive timeout for a bridged instance</string>
|
||||||
<string name="wifi_ieee_80211ax">Enable Wi\u2011Fi 6</string>
|
<string name="wifi_ieee_80211ax">Enable Wi\u2011Fi 6</string>
|
||||||
<string name="wifi_ieee_80211be">Enable Wi\u2011Fi 7</string>
|
<string name="wifi_ieee_80211be">Enable Wi\u2011Fi 7</string>
|
||||||
<string name="wifi_user_config">User Supplied Configuration</string>
|
<string name="wifi_user_config">User supplied configuration</string>
|
||||||
|
<string name="wifi_vendor_elements">Vendor elements</string>
|
||||||
<string name="wifi_save">Save</string>
|
<string name="wifi_save">Save</string>
|
||||||
|
|
||||||
<!-- Based on: https://github.com/PrivacyApps/donations/blob/747d36a18433c7e9329691054122a8ad337a62d2/Donations/src/main/res/values/donations__strings.xml -->
|
<!-- Based on: https://github.com/PrivacyApps/donations/blob/747d36a18433c7e9329691054122a8ad337a62d2/Donations/src/main/res/values/donations__strings.xml -->
|
||||||
|
|||||||
Reference in New Issue
Block a user