Add WiZ provisioning wizard
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
package ru.akokos.ignis_app
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.net.wifi.WifiInfo
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.Build
|
||||
import android.location.LocationManager
|
||||
import android.provider.Settings
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
@@ -14,9 +22,13 @@ import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
private var pendingNotificationPermissionResult: MethodChannel.Result? = null
|
||||
private var pendingProvisioningPermissionResult: MethodChannel.Result? = null
|
||||
private val notificationPrefs by lazy {
|
||||
getSharedPreferences(notificationPrefsName, MODE_PRIVATE)
|
||||
}
|
||||
private val provisioningPrefs by lazy {
|
||||
getSharedPreferences(provisioningPrefsName, MODE_PRIVATE)
|
||||
}
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
@@ -94,6 +106,34 @@ class MainActivity : FlutterActivity() {
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
MethodChannel(
|
||||
flutterEngine.dartExecutor.binaryMessenger,
|
||||
wizProvisioningChannelName,
|
||||
).setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"getProvisioningEnvironment" -> {
|
||||
result.success(buildProvisioningEnvironment())
|
||||
}
|
||||
"requestProvisioningPermissions" -> {
|
||||
requestProvisioningPermissions(result)
|
||||
}
|
||||
"openWifiSettings" -> {
|
||||
startActivity(Intent(Settings.ACTION_WIFI_SETTINGS))
|
||||
result.success(null)
|
||||
}
|
||||
"openAppSettings" -> {
|
||||
val intent =
|
||||
Intent(
|
||||
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||
Uri.fromParts("package", packageName, null),
|
||||
)
|
||||
startActivity(intent)
|
||||
result.success(null)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
@@ -104,6 +144,10 @@ class MainActivity : FlutterActivity() {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
|
||||
if (requestCode != notificationPermissionRequestCode) {
|
||||
if (requestCode == provisioningPermissionRequestCode) {
|
||||
pendingProvisioningPermissionResult?.success(buildProvisioningEnvironment())
|
||||
pendingProvisioningPermissionResult = null
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -111,6 +155,30 @@ class MainActivity : FlutterActivity() {
|
||||
pendingNotificationPermissionResult = null
|
||||
}
|
||||
|
||||
private fun requestProvisioningPermissions(result: MethodChannel.Result) {
|
||||
if (pendingProvisioningPermissionResult != null) {
|
||||
result.error(
|
||||
"request_in_progress",
|
||||
"Provisioning permission request is already in progress",
|
||||
null,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (hasProvisioningPermissions()) {
|
||||
result.success(buildProvisioningEnvironment())
|
||||
return
|
||||
}
|
||||
|
||||
pendingProvisioningPermissionResult = result
|
||||
provisioningPrefs.edit().putBoolean(provisioningPermissionRequestedKey, true).apply()
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
requiredProvisioningPermissions(),
|
||||
provisioningPermissionRequestCode,
|
||||
)
|
||||
}
|
||||
|
||||
private fun requestNotificationPermission(result: MethodChannel.Result) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
result.success(getNotificationPermissionStatusValue())
|
||||
@@ -182,10 +250,137 @@ class MainActivity : FlutterActivity() {
|
||||
NotificationManagerCompat.from(this).areNotificationsEnabled()
|
||||
}
|
||||
|
||||
private fun buildProvisioningEnvironment(): Map<String, Any?> {
|
||||
val wifiSnapshot = currentWifiSnapshot()
|
||||
return mapOf(
|
||||
"platform" to "android",
|
||||
"androidApiLevel" to Build.VERSION.SDK_INT,
|
||||
"smartPairingSupported" to true,
|
||||
"wifiSettingsSupported" to true,
|
||||
"appSettingsSupported" to true,
|
||||
"permissionStatus" to provisioningPermissionStatusValue(),
|
||||
"locationServicesEnabled" to isLocationServicesEnabled(),
|
||||
"connectedToWifi" to wifiSnapshot.connectedToWifi,
|
||||
"ssid" to wifiSnapshot.ssid,
|
||||
"bssid" to wifiSnapshot.bssid,
|
||||
"frequencyMhz" to wifiSnapshot.frequencyMhz,
|
||||
)
|
||||
}
|
||||
|
||||
private fun provisioningPermissionStatusValue(): String {
|
||||
if (hasProvisioningPermissions()) {
|
||||
return "granted"
|
||||
}
|
||||
|
||||
val wasRequestedBefore =
|
||||
provisioningPrefs.getBoolean(provisioningPermissionRequestedKey, false)
|
||||
val canShowPromptAgain =
|
||||
requiredProvisioningPermissions().any {
|
||||
ActivityCompat.shouldShowRequestPermissionRationale(this, it)
|
||||
}
|
||||
|
||||
return if (!wasRequestedBefore || canShowPromptAgain) {
|
||||
"requestable"
|
||||
} else {
|
||||
"settings_required"
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasProvisioningPermissions(): Boolean {
|
||||
return requiredProvisioningPermissions().all { permission ->
|
||||
ContextCompat.checkSelfPermission(this, permission) ==
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
}
|
||||
|
||||
private fun requiredProvisioningPermissions(): Array<String> {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
arrayOf(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
Manifest.permission.NEARBY_WIFI_DEVICES,
|
||||
)
|
||||
} else {
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isLocationServicesEnabled(): Boolean {
|
||||
val manager = getSystemService(Context.LOCATION_SERVICE) as? LocationManager
|
||||
return manager?.let(LocationManagerCompat::isLocationEnabled) ?: false
|
||||
}
|
||||
|
||||
private fun currentWifiSnapshot(): WifiSnapshot {
|
||||
if (!hasProvisioningPermissions()) {
|
||||
return WifiSnapshot()
|
||||
}
|
||||
|
||||
val connectivityManager =
|
||||
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val activeNetwork = connectivityManager.activeNetwork
|
||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
|
||||
val isWifiTransport =
|
||||
networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true
|
||||
val wifiInfo =
|
||||
when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
|
||||
networkCapabilities?.transportInfo is WifiInfo -> {
|
||||
networkCapabilities.transportInfo as WifiInfo
|
||||
}
|
||||
else -> {
|
||||
@Suppress("DEPRECATION")
|
||||
val wifiManager =
|
||||
applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||
@Suppress("DEPRECATION")
|
||||
wifiManager.connectionInfo
|
||||
}
|
||||
}
|
||||
|
||||
val sanitizedSsid = sanitizeSsid(wifiInfo?.ssid)
|
||||
val sanitizedBssid = sanitizeBssid(wifiInfo?.bssid)
|
||||
val frequency = wifiInfo?.frequency?.takeIf { it > 0 }
|
||||
|
||||
return WifiSnapshot(
|
||||
connectedToWifi = isWifiTransport && sanitizedSsid != null,
|
||||
ssid = sanitizedSsid,
|
||||
bssid = sanitizedBssid,
|
||||
frequencyMhz = frequency,
|
||||
)
|
||||
}
|
||||
|
||||
private fun sanitizeSsid(raw: String?): String? {
|
||||
val trimmed = raw?.trim()?.removePrefix("\"")?.removeSuffix("\"")
|
||||
return when {
|
||||
trimmed.isNullOrEmpty() -> null
|
||||
trimmed.equals(WifiManager.UNKNOWN_SSID, ignoreCase = true) -> null
|
||||
else -> trimmed
|
||||
}
|
||||
}
|
||||
|
||||
private fun sanitizeBssid(raw: String?): String? {
|
||||
val trimmed = raw?.trim()
|
||||
return when {
|
||||
trimmed.isNullOrEmpty() -> null
|
||||
trimmed == "02:00:00:00:00:00" -> null
|
||||
else -> trimmed.lowercase()
|
||||
}
|
||||
}
|
||||
|
||||
data class WifiSnapshot(
|
||||
val connectedToWifi: Boolean = false,
|
||||
val ssid: String? = null,
|
||||
val bssid: String? = null,
|
||||
val frequencyMhz: Int? = null,
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val notificationPermissionRequestCode = 4102
|
||||
private const val notificationPrefsName = "ignis_notification_permissions"
|
||||
private const val notificationPermissionRequestedKey =
|
||||
"post_notifications_requested"
|
||||
private const val provisioningPermissionRequestCode = 4103
|
||||
private const val provisioningPrefsName = "ignis_wiz_provisioning"
|
||||
private const val provisioningPermissionRequestedKey =
|
||||
"wiz_provisioning_permissions_requested"
|
||||
private const val wizProvisioningChannelName = "ignis/wiz_provisioning"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user