diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt index ba743039..4cf08393 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/App.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/App.kt @@ -3,6 +3,7 @@ package be.mygod.vpnhotspot import android.annotation.SuppressLint import android.app.Application import android.app.UiModeManager +import android.content.ClipboardManager import android.content.res.Configuration import android.net.ConnectivityManager import android.net.wifi.WifiManager @@ -69,6 +70,7 @@ class App : Application() { } val pref by lazy { PreferenceManager.getDefaultSharedPreferences(deviceStorage) } val connectivity by lazy { getSystemService()!! } + val clipboard by lazy { getSystemService()!! } val uiMode by lazy { getSystemService()!! } val wifi by lazy { getSystemService()!! } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt index b77eee0c..1ef06bef 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/configuration/WifiApDialogFragment.kt @@ -1,6 +1,7 @@ package be.mygod.vpnhotspot.net.wifi.configuration import android.annotation.TargetApi +import android.content.ClipData import android.content.DialogInterface import android.net.wifi.WifiConfiguration import android.net.wifi.WifiConfiguration.AuthAlgorithm @@ -8,14 +9,19 @@ import android.os.Build import android.os.Parcelable import android.text.Editable import android.text.TextWatcher +import android.util.Base64 +import android.view.MenuItem import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter import androidx.appcompat.app.AlertDialog +import androidx.appcompat.widget.Toolbar import androidx.core.view.isGone import be.mygod.vpnhotspot.AlertDialogFragment import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.R +import be.mygod.vpnhotspot.util.toByteArray +import be.mygod.vpnhotspot.util.toParcelable import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.dialog_wifi_ap.view.* import java.lang.IllegalStateException @@ -27,7 +33,13 @@ import java.nio.charset.Charset * This dialog has been deprecated in API 28, but we are still using it since it works better for our purposes. * Related: https://android.googlesource.com/platform/packages/apps/Settings/+/defb1183ecb00d6231bac7d934d07f58f90261ea */ -class WifiApDialogFragment : AlertDialogFragment(), TextWatcher { +class WifiApDialogFragment : AlertDialogFragment(), TextWatcher, + Toolbar.OnMenuItemClickListener { + companion object { + private const val BASE64_FLAGS = Base64.NO_PADDING or Base64.NO_WRAP + private val channels by lazy { (1..165).map { BandOption.Channel(it) } } + } + @Parcelize data class Arg(val configuration: WifiConfiguration, val readOnly: Boolean = false, @@ -59,29 +71,29 @@ class WifiApDialogFragment : AlertDialogFragment= 23) { - val bandOption = dialogView.band.selectedItem as BandOption - apBand = bandOption.apBand - apChannel = bandOption.apChannel - } - }) - } + private lateinit var bandOptions: MutableList + private var started = false + override val ret get() = Arg(WifiConfiguration().apply { + SSID = dialogView.ssid.text.toString() + allowedKeyManagement.set( + if (arg.p2pMode) WifiConfiguration.KeyMgmt.WPA_PSK else dialogView.security.selectedItemPosition) + allowedAuthAlgorithms.set(AuthAlgorithm.OPEN) + if (dialogView.password.length() != 0) preSharedKey = dialogView.password.text.toString() + if (Build.VERSION.SDK_INT >= 23) { + val bandOption = dialogView.band.selectedItem as BandOption + apBand = bandOption.apBand + apChannel = bandOption.apChannel + } + }) override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) { val activity = requireActivity() dialogView = activity.layoutInflater.inflate(R.layout.dialog_wifi_ap, null) setView(dialogView) - setTitle(R.string.configuration_view) if (!arg.readOnly) setPositiveButton(R.string.wifi_save, listener) setNegativeButton(R.string.donations__button_close, null) - dialogView.ssid.setText(arg.configuration.SSID) + dialogView.toolbar.inflateMenu(R.menu.toolbar_configuration) + dialogView.toolbar.setOnMenuItemClickListener(this@WifiApDialogFragment) if (!arg.readOnly) dialogView.ssid.addTextChangedListener(this@WifiApDialogFragment) if (arg.p2pMode) dialogView.security_wrapper.isGone = true else dialogView.security.apply { adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0, @@ -95,35 +107,46 @@ class WifiApDialogFragment : AlertDialogFragment= 0) { "No key management selected" } - check(arg.configuration.allowedKeyManagement.nextSetBit(selected + 1) < 0) { - "More than 1 key managements supplied" - } - setSelection(selected) } - dialogView.password.setText(arg.configuration.preSharedKey) if (!arg.readOnly) dialogView.password.addTextChangedListener(this@WifiApDialogFragment) - if (Build.VERSION.SDK_INT >= 23) dialogView.band.apply { - val options = mutableListOf().apply { + if (Build.VERSION.SDK_INT >= 23) { + bandOptions = mutableListOf().apply { if (arg.p2pMode) add(BandOption.BandAny) else { if (Build.VERSION.SDK_INT >= 28) add(BandOption.BandAny) add(BandOption.Band2GHz) add(BandOption.Band5GHz) } - addAll((1..165).map { BandOption.Channel(it) }) + addAll(channels) } - adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0, options).apply { + dialogView.band.adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0, + bandOptions).apply { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) } - setSelection(if (arg.configuration.apChannel in 1..165) { - options.indexOfFirst { it.apChannel == arg.configuration.apChannel } - } else options.indexOfFirst { it.apBand == arg.configuration.apBand }) } else dialogView.band_wrapper.isGone = true + populateFromConfiguration(arg.configuration) } - override fun onResume() { - super.onResume() + private fun populateFromConfiguration(configuration: WifiConfiguration) { + dialogView.ssid.setText(configuration.SSID) + if (!arg.p2pMode) { + val selected = configuration.allowedKeyManagement.nextSetBit(0) + check(selected >= 0) { "No key management selected" } + check(configuration.allowedKeyManagement.nextSetBit(selected + 1) < 0) { + "More than 1 key managements supplied" + } + dialogView.security.setSelection(selected) + } + dialogView.password.setText(configuration.preSharedKey) + if (Build.VERSION.SDK_INT >= 23) { + dialogView.band.setSelection(if (configuration.apChannel in 1..165) { + bandOptions.indexOfFirst { it.apChannel == configuration.apChannel } + } else bandOptions.indexOfFirst { it.apBand == configuration.apBand }) + } + } + + override fun onStart() { + super.onStart() + started = true if (!arg.readOnly) validate() } @@ -131,6 +154,7 @@ class WifiApDialogFragment : AlertDialogFragment { + app.clipboard.primaryClip = + ClipData.newPlainText(null, Base64.encodeToString(ret.configuration.toByteArray(), BASE64_FLAGS)) + true + } + android.R.id.paste -> { + app.clipboard.primaryClip?.getItemAt(0)?.text?.let { + populateFromConfiguration(Base64.decode(it.toString(), BASE64_FLAGS).toParcelable()) + } + true + } + else -> false + } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index 54b63d2a..1063706c 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.* import android.os.Build import android.os.Parcel +import android.os.Parcelable import android.text.Spannable import android.text.SpannableString import android.text.SpannableStringBuilder @@ -42,6 +43,16 @@ fun useParcel(block: (Parcel) -> T) = Parcel.obtain().run { } } +fun Parcelable.toByteArray(parcelableFlags: Int = 0) = useParcel { p -> + p.writeParcelable(this, parcelableFlags) + p.marshall() +} +fun ByteArray.toParcelable() = useParcel { p -> + p.unmarshall(this, 0, size) + p.setDataPosition(0) + p.readParcelable(javaClass.classLoader) +} + fun broadcastReceiver(receiver: (Context, Intent) -> Unit) = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) = receiver(context, intent) } diff --git a/mobile/src/main/res/layout/dialog_wifi_ap.xml b/mobile/src/main/res/layout/dialog_wifi_ap.xml index 1290d53e..f89c79d4 100644 --- a/mobile/src/main/res/layout/dialog_wifi_ap.xml +++ b/mobile/src/main/res/layout/dialog_wifi_ap.xml @@ -1,6 +1,11 @@ - - + - - + + - - - + android:paddingLeft="20dp" + android:paddingRight="20dp" + app:title="@string/configuration_view"/> + - + + + + - + + + + + android:layout_marginTop="8dip" + app:passwordToggleEnabled="true" + app:errorEnabled="true"> + + + + + + - - - - - - - - - + + diff --git a/mobile/src/main/res/menu/toolbar_configuration.xml b/mobile/src/main/res/menu/toolbar_configuration.xml new file mode 100644 index 00000000..faad44a1 --- /dev/null +++ b/mobile/src/main/res/menu/toolbar_configuration.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/mobile/src/main/res/values/styles.xml b/mobile/src/main/res/values/styles.xml index ede8762e..3a9273fc 100644 --- a/mobile/src/main/res/values/styles.xml +++ b/mobile/src/main/res/values/styles.xml @@ -13,7 +13,6 @@