Support sharing via QR code

Another popular format is barcode but unfortunately I cannot seem to find documentations of that anywhere. Feel free to send me.
This commit is contained in:
Mygod
2019-04-04 19:17:00 +08:00
parent a1dd7107e1
commit 6225367e42
9 changed files with 89 additions and 19 deletions

View File

@@ -20,6 +20,7 @@ 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.QRCodeDialog
import be.mygod.vpnhotspot.util.toByteArray
import be.mygod.vpnhotspot.util.toParcelable
import kotlinx.android.parcel.Parcelize
@@ -128,14 +129,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
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)
}
if (!arg.p2pMode) dialogView.security.setSelection(configuration.apKeyManagement)
dialogView.password.setText(configuration.preSharedKey)
if (Build.VERSION.SDK_INT >= 23) {
dialogView.band.setSelection(if (configuration.apChannel in 1..165) {
@@ -171,18 +165,25 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { }
override fun afterTextChanged(editable: Editable) = validate()
override fun onMenuItemClick(item: MenuItem?) = when (item?.itemId) {
android.R.id.copy -> {
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())
override fun onMenuItemClick(item: MenuItem?): Boolean {
return when (item?.itemId) {
android.R.id.copy -> {
app.clipboard.primaryClip =
ClipData.newPlainText(null, Base64.encodeToString(ret.configuration.toByteArray(), BASE64_FLAGS))
true
}
true
android.R.id.paste -> {
app.clipboard.primaryClip?.getItemAt(0)?.text?.let {
populateFromConfiguration(Base64.decode(it.toString(), BASE64_FLAGS).toParcelable())
}
true
}
R.id.share_qr -> {
QRCodeDialog().withArg(ret.configuration.toQRString())
.show(fragmentManager ?: return false, "QRCodeDialog")
true
}
else -> false
}
else -> false
}
}

View File

@@ -65,6 +65,35 @@ fun channelToFrequency(channel: Int) = when (channel) {
else -> throw IllegalArgumentException("Invalid channel $channel")
}
val WifiConfiguration.apKeyManagement get() = allowedKeyManagement.nextSetBit(0).also { selected ->
check(selected >= 0) { "No key management selected" }
check(allowedKeyManagement.nextSetBit(selected + 1) < 0) { "More than 1 key managements supplied" }
}
private val qrSanitizer = Regex("([\\\\\":;,])")
/**
* Documentation: https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11
*/
fun WifiConfiguration.toQRString() = StringBuilder("WIFI:").apply {
fun String.sanitize() = qrSanitizer.replace(this) { "\\${it.groupValues[1]}" }
var password = true
when (apKeyManagement) {
WifiConfiguration.KeyMgmt.NONE -> password = false
WifiConfiguration.KeyMgmt.WPA_PSK, WifiConfiguration.KeyMgmt.WPA_EAP, WPA2_PSK -> append("T:WPA;")
else -> throw IllegalArgumentException("Unsupported authentication type")
}
append("S:")
append(SSID.sanitize())
append(';')
if (password) {
append("P:")
append(preSharedKey.sanitize())
append(';')
}
if (hiddenSSID) append("H:true;")
append(';')
}.toString()
/**
* Based on:
* https://android.googlesource.com/platform/packages/apps/Settings/+/android-5.0.0_r1/src/com/android/settings/wifi/WifiApDialog.java#88

View File

@@ -0,0 +1,26 @@
package be.mygod.vpnhotspot.util
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment
import be.mygod.vpnhotspot.R
import net.glxn.qrgen.android.QRCode
class QRCodeDialog : DialogFragment() {
companion object {
private const val KEY_ARG = "arg"
}
fun withArg(arg: String) = apply { arguments = bundleOf(KEY_ARG to arg) }
private val arg get() = arguments?.getString(KEY_ARG)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) =
ImageView(context).apply {
val size = resources.getDimensionPixelSize(R.dimen.qr_code_size)
layoutParams = ViewGroup.LayoutParams(size, size)
setImageBitmap((QRCode.from(arg).withSize(size, size) as QRCode).bitmap())
}
}