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:
@@ -87,6 +87,7 @@ dependencies {
|
|||||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
implementation 'com.linkedin.dexmaker:dexmaker:2.25.0'
|
implementation 'com.linkedin.dexmaker:dexmaker:2.25.0'
|
||||||
implementation 'com.takisoft.preferencex:preferencex-simplemenu:1.0.0'
|
implementation 'com.takisoft.preferencex:preferencex-simplemenu:1.0.0'
|
||||||
|
implementation 'net.glxn.qrgen:android:2.0'
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
|
||||||
for (dep in aux) {
|
for (dep in aux) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import androidx.core.view.isGone
|
|||||||
import be.mygod.vpnhotspot.AlertDialogFragment
|
import be.mygod.vpnhotspot.AlertDialogFragment
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.R
|
import be.mygod.vpnhotspot.R
|
||||||
|
import be.mygod.vpnhotspot.util.QRCodeDialog
|
||||||
import be.mygod.vpnhotspot.util.toByteArray
|
import be.mygod.vpnhotspot.util.toByteArray
|
||||||
import be.mygod.vpnhotspot.util.toParcelable
|
import be.mygod.vpnhotspot.util.toParcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
@@ -128,14 +129,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
|
|
||||||
private fun populateFromConfiguration(configuration: WifiConfiguration) {
|
private fun populateFromConfiguration(configuration: WifiConfiguration) {
|
||||||
dialogView.ssid.setText(configuration.SSID)
|
dialogView.ssid.setText(configuration.SSID)
|
||||||
if (!arg.p2pMode) {
|
if (!arg.p2pMode) dialogView.security.setSelection(configuration.apKeyManagement)
|
||||||
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)
|
dialogView.password.setText(configuration.preSharedKey)
|
||||||
if (Build.VERSION.SDK_INT >= 23) {
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
dialogView.band.setSelection(if (configuration.apChannel in 1..165) {
|
dialogView.band.setSelection(if (configuration.apChannel in 1..165) {
|
||||||
@@ -171,7 +165,8 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { }
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { }
|
||||||
override fun afterTextChanged(editable: Editable) = validate()
|
override fun afterTextChanged(editable: Editable) = validate()
|
||||||
|
|
||||||
override fun onMenuItemClick(item: MenuItem?) = when (item?.itemId) {
|
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||||
|
return when (item?.itemId) {
|
||||||
android.R.id.copy -> {
|
android.R.id.copy -> {
|
||||||
app.clipboard.primaryClip =
|
app.clipboard.primaryClip =
|
||||||
ClipData.newPlainText(null, Base64.encodeToString(ret.configuration.toByteArray(), BASE64_FLAGS))
|
ClipData.newPlainText(null, Base64.encodeToString(ret.configuration.toByteArray(), BASE64_FLAGS))
|
||||||
@@ -183,6 +178,12 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.share_qr -> {
|
||||||
|
QRCodeDialog().withArg(ret.configuration.toQRString())
|
||||||
|
.show(fragmentManager ?: return false, "QRCodeDialog")
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,6 +65,35 @@ fun channelToFrequency(channel: Int) = when (channel) {
|
|||||||
else -> throw IllegalArgumentException("Invalid channel $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:
|
* Based on:
|
||||||
* https://android.googlesource.com/platform/packages/apps/Settings/+/android-5.0.0_r1/src/com/android/settings/wifi/WifiApDialog.java#88
|
* https://android.googlesource.com/platform/packages/apps/Settings/+/android-5.0.0_r1/src/com/android/settings/wifi/WifiApDialog.java#88
|
||||||
|
|||||||
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
6
mobile/src/main/res/drawable/ic_social_share.xml
Normal file
6
mobile/src/main/res/drawable/ic_social_share.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<vector android:autoMirrored="true" android:height="24dp"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||||
|
</vector>
|
||||||
@@ -11,4 +11,8 @@
|
|||||||
android:icon="?attr/actionModePasteDrawable"
|
android:icon="?attr/actionModePasteDrawable"
|
||||||
android:title="@android:string/paste"
|
android:title="@android:string/paste"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
|
<item android:id="@+id/share_qr"
|
||||||
|
android:icon="@drawable/ic_social_share"
|
||||||
|
android:title="@string/configuration_share"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@@ -126,6 +126,7 @@
|
|||||||
<string name="noisy_su_failure">发生异常,详情请查看调试信息。</string>
|
<string name="noisy_su_failure">发生异常,详情请查看调试信息。</string>
|
||||||
|
|
||||||
<string name="configuration_view">设置 WLAN 中继</string>
|
<string name="configuration_view">设置 WLAN 中继</string>
|
||||||
|
<string name="configuration_share">使用 QR 码分享</string>
|
||||||
<string name="configuration_rejected">Android 系统拒绝使用此配置。(详情参见日志)</string>
|
<string name="configuration_rejected">Android 系统拒绝使用此配置。(详情参见日志)</string>
|
||||||
<string name="wifi_ssid" msgid="5519636102673067319">"网络名称"</string>
|
<string name="wifi_ssid" msgid="5519636102673067319">"网络名称"</string>
|
||||||
<string name="wifi_security" msgid="6603611185592956936">"安全性"</string>
|
<string name="wifi_security" msgid="6603611185592956936">"安全性"</string>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<dimen name="listitem_manage_tether_padding_start">56dp</dimen>
|
<dimen name="listitem_manage_tether_padding_start">56dp</dimen>
|
||||||
|
<dimen name="qr_code_size">250dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -134,6 +134,7 @@
|
|||||||
<string name="noisy_su_failure">Something went wrong, please check the debug information.</string>
|
<string name="noisy_su_failure">Something went wrong, please check the debug information.</string>
|
||||||
|
|
||||||
<string name="configuration_view">Wi\u2011Fi configuration</string>
|
<string name="configuration_view">Wi\u2011Fi configuration</string>
|
||||||
|
<string name="configuration_share">Share via QR code</string>
|
||||||
<string name="configuration_rejected">Android system refuses such configuration. (see logcat)</string>
|
<string name="configuration_rejected">Android system refuses such configuration. (see logcat)</string>
|
||||||
<string name="wifi_ssid">Network name</string>
|
<string name="wifi_ssid">Network name</string>
|
||||||
<string name="wifi_security">Security</string>
|
<string name="wifi_security">Security</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user