rnww-plugin-wifi 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +841 -0
- package/lib/bridge/background-bridge.d.ts +22 -0
- package/lib/bridge/background-bridge.d.ts.map +1 -0
- package/lib/bridge/background-bridge.js +493 -0
- package/lib/bridge/index.d.ts +6 -0
- package/lib/bridge/index.d.ts.map +1 -0
- package/lib/bridge/index.js +23 -0
- package/lib/bridge/wifi-bridge.d.ts +16 -0
- package/lib/bridge/wifi-bridge.d.ts.map +1 -0
- package/lib/bridge/wifi-bridge.js +202 -0
- package/lib/modules/index.d.ts +41 -0
- package/lib/modules/index.d.ts.map +1 -0
- package/lib/modules/index.js +150 -0
- package/lib/types/background-module.d.ts +291 -0
- package/lib/types/background-module.d.ts.map +1 -0
- package/lib/types/background-module.js +5 -0
- package/lib/types/bridge.d.ts +22 -0
- package/lib/types/bridge.d.ts.map +1 -0
- package/lib/types/bridge.js +5 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +5 -0
- package/lib/types/platform.d.ts +10 -0
- package/lib/types/platform.d.ts.map +1 -0
- package/lib/types/platform.js +2 -0
- package/lib/types/wifi-module.d.ts +146 -0
- package/lib/types/wifi-module.d.ts.map +1 -0
- package/lib/types/wifi-module.js +6 -0
- package/package.json +42 -0
- package/src/modules/android/build.gradle +64 -0
- package/src/modules/android/src/main/AndroidManifest.xml +28 -0
- package/src/modules/android/src/main/java/expo/modules/customwifi/WifiModule.kt +517 -0
- package/src/modules/android/src/main/java/expo/modules/customwifi/WifiStateReceiver.kt +46 -0
- package/src/modules/expo-module.config.json +9 -0
- package/src/modules/index.ts +166 -0
- package/src/modules/ios/WifiModule.swift +399 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WiFi Module (Cross-Platform)
|
|
3
|
+
* WiFi 정보 조회 및 연결 관리 기능 제공
|
|
4
|
+
* Supports: Android, iOS
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
8
|
+
import { Platform } from 'react-native';
|
|
9
|
+
import type {
|
|
10
|
+
WifiInfoResult,
|
|
11
|
+
WifiListResult,
|
|
12
|
+
WifiStateResult,
|
|
13
|
+
ConnectOptions,
|
|
14
|
+
ConnectResult,
|
|
15
|
+
WifiResult,
|
|
16
|
+
PermissionStatus,
|
|
17
|
+
WifiStateChangeEvent,
|
|
18
|
+
} from '../types/wifi-module';
|
|
19
|
+
|
|
20
|
+
// Lazy 모듈 로드 (크래시 방지)
|
|
21
|
+
let WifiModule: any = null;
|
|
22
|
+
|
|
23
|
+
function getWifiModule() {
|
|
24
|
+
if (Platform.OS !== 'android' && Platform.OS !== 'ios') {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (WifiModule === null) {
|
|
29
|
+
try {
|
|
30
|
+
WifiModule = requireNativeModule('CustomWifi');
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error('[CustomWifi] Failed to load native module:', error);
|
|
33
|
+
WifiModule = undefined;
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return WifiModule === undefined ? null : WifiModule;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 현재 연결된 WiFi 정보 조회
|
|
43
|
+
*/
|
|
44
|
+
export async function getCurrentWifiInfo(): Promise<WifiInfoResult> {
|
|
45
|
+
const module = getWifiModule();
|
|
46
|
+
if (!module) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: 'Module not available',
|
|
50
|
+
errorCode: 'MODULE_NOT_AVAILABLE',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return await module.getCurrentWifiInfo();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 사용 가능한 WiFi 목록 스캔 (Android only)
|
|
58
|
+
*/
|
|
59
|
+
export async function getWifiList(): Promise<WifiListResult> {
|
|
60
|
+
const module = getWifiModule();
|
|
61
|
+
if (!module) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: 'Module not available',
|
|
65
|
+
errorCode: 'MODULE_NOT_AVAILABLE',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (Platform.OS === 'ios') {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: 'WiFi scanning is not supported on iOS',
|
|
73
|
+
errorCode: 'NOT_SUPPORTED',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return await module.getWifiList();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* WiFi 활성화 상태 확인
|
|
82
|
+
*/
|
|
83
|
+
export async function isWifiEnabled(): Promise<WifiStateResult> {
|
|
84
|
+
const module = getWifiModule();
|
|
85
|
+
if (!module) {
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
error: 'Module not available',
|
|
89
|
+
errorCode: 'MODULE_NOT_AVAILABLE',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return await module.isWifiEnabled();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* WiFi 네트워크에 연결
|
|
97
|
+
*/
|
|
98
|
+
export async function connectToWifi(options: ConnectOptions): Promise<ConnectResult> {
|
|
99
|
+
const module = getWifiModule();
|
|
100
|
+
if (!module) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
error: 'Module not available',
|
|
104
|
+
errorCode: 'MODULE_NOT_AVAILABLE',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return await module.connectToWifi(options);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* WiFi 연결 해제
|
|
112
|
+
*/
|
|
113
|
+
export async function disconnect(): Promise<WifiResult> {
|
|
114
|
+
const module = getWifiModule();
|
|
115
|
+
if (!module) {
|
|
116
|
+
return {
|
|
117
|
+
success: false,
|
|
118
|
+
error: 'Module not available',
|
|
119
|
+
errorCode: 'MODULE_NOT_AVAILABLE',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return await module.disconnect();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* WiFi 권한 확인
|
|
127
|
+
*/
|
|
128
|
+
export async function checkPermission(): Promise<PermissionStatus> {
|
|
129
|
+
const module = getWifiModule();
|
|
130
|
+
if (!module) {
|
|
131
|
+
return {
|
|
132
|
+
locationGranted: false,
|
|
133
|
+
canAccessWifiInfo: false,
|
|
134
|
+
requiredPermissions: [],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return await module.checkPermission();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* WiFi 권한 요청
|
|
142
|
+
*/
|
|
143
|
+
export async function requestPermission(): Promise<PermissionStatus> {
|
|
144
|
+
const module = getWifiModule();
|
|
145
|
+
if (!module) {
|
|
146
|
+
return {
|
|
147
|
+
locationGranted: false,
|
|
148
|
+
canAccessWifiInfo: false,
|
|
149
|
+
requiredPermissions: [],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return await module.requestPermission();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* WiFi 상태 변경 이벤트 리스너 등록
|
|
157
|
+
*/
|
|
158
|
+
export function addWifiStateListener(
|
|
159
|
+
listener: (event: WifiStateChangeEvent) => void
|
|
160
|
+
): { remove: () => void } {
|
|
161
|
+
const module = getWifiModule();
|
|
162
|
+
if (!module || !module.addListener) {
|
|
163
|
+
return { remove: () => {} };
|
|
164
|
+
}
|
|
165
|
+
return module.addListener('onWifiStateChange', listener);
|
|
166
|
+
}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import UIKit
|
|
3
|
+
import NetworkExtension
|
|
4
|
+
import SystemConfiguration.CaptiveNetwork
|
|
5
|
+
import CoreLocation
|
|
6
|
+
|
|
7
|
+
public class WifiModule: Module {
|
|
8
|
+
private var locationManager: CLLocationManager?
|
|
9
|
+
private var permissionPromise: Promise?
|
|
10
|
+
private var connectPromise: Promise?
|
|
11
|
+
private var foregroundObserver: NSObjectProtocol?
|
|
12
|
+
private var reachabilityObserver: NSObjectProtocol?
|
|
13
|
+
|
|
14
|
+
public func definition() -> ModuleDefinition {
|
|
15
|
+
Name("CustomWifi")
|
|
16
|
+
|
|
17
|
+
Events("onWifiStateChange")
|
|
18
|
+
|
|
19
|
+
OnCreate {
|
|
20
|
+
self.setupLocationManager()
|
|
21
|
+
self.setupReachabilityObserver()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
OnDestroy {
|
|
25
|
+
self.cleanup()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 현재 WiFi 정보 조회
|
|
29
|
+
AsyncFunction("getCurrentWifiInfo") { (promise: Promise) in
|
|
30
|
+
self.getCurrentWifiInfo(promise: promise)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// WiFi 목록 스캔 (iOS에서는 지원되지 않음)
|
|
34
|
+
AsyncFunction("getWifiList") { (promise: Promise) in
|
|
35
|
+
promise.resolve([
|
|
36
|
+
"success": false,
|
|
37
|
+
"error": "WiFi scanning is not supported on iOS",
|
|
38
|
+
"errorCode": "NOT_SUPPORTED"
|
|
39
|
+
])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// WiFi 활성화 상태 확인
|
|
43
|
+
AsyncFunction("isWifiEnabled") { (promise: Promise) in
|
|
44
|
+
let isEnabled = self.isWifiEnabled()
|
|
45
|
+
promise.resolve([
|
|
46
|
+
"success": true,
|
|
47
|
+
"isEnabled": isEnabled,
|
|
48
|
+
"wifiState": isEnabled ? "ENABLED" : "UNKNOWN"
|
|
49
|
+
])
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// WiFi 연결 (iOS 11+)
|
|
53
|
+
AsyncFunction("connectToWifi") { (options: [String: Any], promise: Promise) in
|
|
54
|
+
self.connectToWifi(options: options, promise: promise)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// WiFi 연결 해제
|
|
58
|
+
AsyncFunction("disconnect") { (promise: Promise) in
|
|
59
|
+
self.disconnect(promise: promise)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 권한 확인
|
|
63
|
+
AsyncFunction("checkPermission") { (promise: Promise) in
|
|
64
|
+
self.checkPermission(promise: promise)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 권한 요청
|
|
68
|
+
AsyncFunction("requestPermission") { (promise: Promise) in
|
|
69
|
+
self.requestPermission(promise: promise)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// MARK: - Private Methods
|
|
74
|
+
|
|
75
|
+
private func setupLocationManager() {
|
|
76
|
+
locationManager = CLLocationManager()
|
|
77
|
+
locationManager?.delegate = LocationDelegate(module: self)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private func setupReachabilityObserver() {
|
|
81
|
+
// 네트워크 상태 변경 감지
|
|
82
|
+
reachabilityObserver = NotificationCenter.default.addObserver(
|
|
83
|
+
forName: NSNotification.Name(rawValue: "kNetworkReachabilityChangedNotification"),
|
|
84
|
+
object: nil,
|
|
85
|
+
queue: .main
|
|
86
|
+
) { [weak self] _ in
|
|
87
|
+
self?.notifyWifiStateChange()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private func cleanup() {
|
|
92
|
+
if let observer = foregroundObserver {
|
|
93
|
+
NotificationCenter.default.removeObserver(observer)
|
|
94
|
+
foregroundObserver = nil
|
|
95
|
+
}
|
|
96
|
+
if let observer = reachabilityObserver {
|
|
97
|
+
NotificationCenter.default.removeObserver(observer)
|
|
98
|
+
reachabilityObserver = nil
|
|
99
|
+
}
|
|
100
|
+
locationManager = nil
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private func getCurrentWifiInfo(promise: Promise) {
|
|
104
|
+
// 위치 권한 확인
|
|
105
|
+
let status = CLLocationManager.authorizationStatus()
|
|
106
|
+
guard status == .authorizedWhenInUse || status == .authorizedAlways else {
|
|
107
|
+
promise.resolve([
|
|
108
|
+
"success": false,
|
|
109
|
+
"error": "Location permission required for WiFi info",
|
|
110
|
+
"errorCode": "PERMISSION_DENIED"
|
|
111
|
+
])
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let wifiInfo = getWifiInfo()
|
|
116
|
+
promise.resolve([
|
|
117
|
+
"success": true,
|
|
118
|
+
"data": wifiInfo
|
|
119
|
+
])
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private func getWifiInfo() -> [String: Any?] {
|
|
123
|
+
var ssid: String? = nil
|
|
124
|
+
var bssid: String? = nil
|
|
125
|
+
|
|
126
|
+
// CNCopyCurrentNetworkInfo를 사용하여 SSID 및 BSSID 가져오기
|
|
127
|
+
if let interfaces = CNCopySupportedInterfaces() as? [String] {
|
|
128
|
+
for interface in interfaces {
|
|
129
|
+
if let networkInfo = CNCopyCurrentNetworkInfo(interface as CFString) as? [String: Any] {
|
|
130
|
+
ssid = networkInfo[kCNNetworkInfoKeySSID as String] as? String
|
|
131
|
+
bssid = networkInfo[kCNNetworkInfoKeyBSSID as String] as? String
|
|
132
|
+
break
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let isConnected = ssid != nil
|
|
138
|
+
|
|
139
|
+
return [
|
|
140
|
+
"ssid": ssid,
|
|
141
|
+
"bssid": bssid,
|
|
142
|
+
"rssi": nil, // iOS에서는 직접 가져올 수 없음
|
|
143
|
+
"signalLevel": nil,
|
|
144
|
+
"frequency": nil,
|
|
145
|
+
"linkSpeed": nil,
|
|
146
|
+
"ipAddress": getWiFiIPAddress(),
|
|
147
|
+
"isConnected": isConnected
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private func getWiFiIPAddress() -> String? {
|
|
152
|
+
var address: String?
|
|
153
|
+
var ifaddr: UnsafeMutablePointer<ifaddrs>?
|
|
154
|
+
|
|
155
|
+
guard getifaddrs(&ifaddr) == 0 else { return nil }
|
|
156
|
+
defer { freeifaddrs(ifaddr) }
|
|
157
|
+
|
|
158
|
+
var ptr = ifaddr
|
|
159
|
+
while ptr != nil {
|
|
160
|
+
defer { ptr = ptr?.pointee.ifa_next }
|
|
161
|
+
|
|
162
|
+
let interface = ptr?.pointee
|
|
163
|
+
let addrFamily = interface?.ifa_addr.pointee.sa_family
|
|
164
|
+
|
|
165
|
+
if addrFamily == UInt8(AF_INET) {
|
|
166
|
+
let name = String(cString: (interface?.ifa_name)!)
|
|
167
|
+
if name == "en0" { // WiFi interface
|
|
168
|
+
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
169
|
+
getnameinfo(
|
|
170
|
+
interface?.ifa_addr,
|
|
171
|
+
socklen_t((interface?.ifa_addr.pointee.sa_len)!),
|
|
172
|
+
&hostname,
|
|
173
|
+
socklen_t(hostname.count),
|
|
174
|
+
nil,
|
|
175
|
+
socklen_t(0),
|
|
176
|
+
NI_NUMERICHOST
|
|
177
|
+
)
|
|
178
|
+
address = String(cString: hostname)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return address
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private func isWifiEnabled() -> Bool {
|
|
186
|
+
// iOS에서는 WiFi 상태를 직접 확인할 수 없으므로 연결 여부로 대체
|
|
187
|
+
let wifiInfo = getWifiInfo()
|
|
188
|
+
return wifiInfo["ssid"] != nil
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private func connectToWifi(options: [String: Any], promise: Promise) {
|
|
192
|
+
guard let ssid = options["ssid"] as? String, !ssid.isEmpty else {
|
|
193
|
+
promise.resolve([
|
|
194
|
+
"success": false,
|
|
195
|
+
"error": "SSID is required",
|
|
196
|
+
"errorCode": "INVALID_SSID"
|
|
197
|
+
])
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let password = options["password"] as? String
|
|
202
|
+
let isHidden = options["isHidden"] as? Bool ?? false
|
|
203
|
+
|
|
204
|
+
if #available(iOS 11.0, *) {
|
|
205
|
+
var hotspotConfig: NEHotspotConfiguration
|
|
206
|
+
|
|
207
|
+
if let pwd = password, !pwd.isEmpty {
|
|
208
|
+
hotspotConfig = NEHotspotConfiguration(ssid: ssid, passphrase: pwd, isWEP: false)
|
|
209
|
+
} else {
|
|
210
|
+
hotspotConfig = NEHotspotConfiguration(ssid: ssid)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
hotspotConfig.joinOnce = false
|
|
214
|
+
|
|
215
|
+
if isHidden {
|
|
216
|
+
hotspotConfig.hidden = true
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
NEHotspotConfigurationManager.shared.apply(hotspotConfig) { [weak self] error in
|
|
220
|
+
if let error = error as NSError? {
|
|
221
|
+
if error.domain == NEHotspotConfigurationErrorDomain {
|
|
222
|
+
switch error.code {
|
|
223
|
+
case NEHotspotConfigurationError.alreadyAssociated.rawValue:
|
|
224
|
+
// 이미 연결된 경우는 성공으로 처리
|
|
225
|
+
let wifiInfo = self?.getWifiInfo()
|
|
226
|
+
promise.resolve([
|
|
227
|
+
"success": true,
|
|
228
|
+
"wifiInfo": wifiInfo as Any
|
|
229
|
+
])
|
|
230
|
+
return
|
|
231
|
+
case NEHotspotConfigurationError.userDenied.rawValue:
|
|
232
|
+
promise.resolve([
|
|
233
|
+
"success": false,
|
|
234
|
+
"error": "User denied connection",
|
|
235
|
+
"errorCode": "CONNECTION_FAILED"
|
|
236
|
+
])
|
|
237
|
+
return
|
|
238
|
+
case NEHotspotConfigurationError.invalidSSID.rawValue:
|
|
239
|
+
promise.resolve([
|
|
240
|
+
"success": false,
|
|
241
|
+
"error": "Invalid SSID",
|
|
242
|
+
"errorCode": "INVALID_SSID"
|
|
243
|
+
])
|
|
244
|
+
return
|
|
245
|
+
case NEHotspotConfigurationError.invalidWPAPassphrase.rawValue:
|
|
246
|
+
promise.resolve([
|
|
247
|
+
"success": false,
|
|
248
|
+
"error": "Invalid password",
|
|
249
|
+
"errorCode": "INVALID_PASSWORD"
|
|
250
|
+
])
|
|
251
|
+
return
|
|
252
|
+
default:
|
|
253
|
+
break
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
promise.resolve([
|
|
257
|
+
"success": false,
|
|
258
|
+
"error": error.localizedDescription,
|
|
259
|
+
"errorCode": "CONNECTION_FAILED"
|
|
260
|
+
])
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 연결 성공
|
|
265
|
+
let wifiInfo = self?.getWifiInfo()
|
|
266
|
+
promise.resolve([
|
|
267
|
+
"success": true,
|
|
268
|
+
"wifiInfo": wifiInfo as Any
|
|
269
|
+
])
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
promise.resolve([
|
|
273
|
+
"success": false,
|
|
274
|
+
"error": "WiFi connection requires iOS 11 or later",
|
|
275
|
+
"errorCode": "NOT_SUPPORTED"
|
|
276
|
+
])
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private func disconnect(promise: Promise) {
|
|
281
|
+
if #available(iOS 11.0, *) {
|
|
282
|
+
// 현재 연결된 SSID 가져오기
|
|
283
|
+
let wifiInfo = getWifiInfo()
|
|
284
|
+
if let ssid = wifiInfo["ssid"] as? String {
|
|
285
|
+
NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid)
|
|
286
|
+
}
|
|
287
|
+
promise.resolve(["success": true])
|
|
288
|
+
} else {
|
|
289
|
+
promise.resolve([
|
|
290
|
+
"success": false,
|
|
291
|
+
"error": "WiFi management requires iOS 11 or later",
|
|
292
|
+
"errorCode": "NOT_SUPPORTED"
|
|
293
|
+
])
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private func checkPermission(promise: Promise) {
|
|
298
|
+
let status = CLLocationManager.authorizationStatus()
|
|
299
|
+
let locationGranted = status == .authorizedWhenInUse || status == .authorizedAlways
|
|
300
|
+
|
|
301
|
+
var requiredPermissions: [String] = []
|
|
302
|
+
if !locationGranted {
|
|
303
|
+
requiredPermissions.append("Location (When In Use)")
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
promise.resolve([
|
|
307
|
+
"locationGranted": locationGranted,
|
|
308
|
+
"canAccessWifiInfo": locationGranted,
|
|
309
|
+
"requiredPermissions": requiredPermissions,
|
|
310
|
+
"details": [
|
|
311
|
+
"fineLocation": locationGranted,
|
|
312
|
+
"coarseLocation": status == .authorizedAlways
|
|
313
|
+
]
|
|
314
|
+
])
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private func requestPermission(promise: Promise) {
|
|
318
|
+
let status = CLLocationManager.authorizationStatus()
|
|
319
|
+
|
|
320
|
+
if status == .authorizedWhenInUse || status == .authorizedAlways {
|
|
321
|
+
self.checkPermission(promise: promise)
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if status == .notDetermined {
|
|
326
|
+
// 권한 요청
|
|
327
|
+
permissionPromise = promise
|
|
328
|
+
locationManager?.requestWhenInUseAuthorization()
|
|
329
|
+
} else if status == .denied || status == .restricted {
|
|
330
|
+
// 설정으로 안내
|
|
331
|
+
if let observer = foregroundObserver {
|
|
332
|
+
NotificationCenter.default.removeObserver(observer)
|
|
333
|
+
foregroundObserver = nil
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
guard let url = URL(string: UIApplication.openSettingsURLString) else {
|
|
337
|
+
self.checkPermission(promise: promise)
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 앱이 포그라운드로 돌아올 때 상태 확인
|
|
342
|
+
foregroundObserver = NotificationCenter.default.addObserver(
|
|
343
|
+
forName: UIApplication.willEnterForegroundNotification,
|
|
344
|
+
object: nil,
|
|
345
|
+
queue: .main
|
|
346
|
+
) { [weak self] _ in
|
|
347
|
+
if let observer = self?.foregroundObserver {
|
|
348
|
+
NotificationCenter.default.removeObserver(observer)
|
|
349
|
+
self?.foregroundObserver = nil
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
353
|
+
self?.checkPermission(promise: promise)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
DispatchQueue.main.async {
|
|
358
|
+
UIApplication.shared.open(url)
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
func handlePermissionResult() {
|
|
364
|
+
if let promise = permissionPromise {
|
|
365
|
+
checkPermission(promise: promise)
|
|
366
|
+
permissionPromise = nil
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private func notifyWifiStateChange() {
|
|
371
|
+
let wifiInfo = getWifiInfo()
|
|
372
|
+
let connectionState = (wifiInfo["isConnected"] as? Bool ?? false) ? "CONNECTED" : "DISCONNECTED"
|
|
373
|
+
|
|
374
|
+
sendEvent("onWifiStateChange", [
|
|
375
|
+
"type": "CONNECTION_STATE_CHANGED",
|
|
376
|
+
"connectionState": connectionState,
|
|
377
|
+
"wifiInfo": wifiInfo,
|
|
378
|
+
"timestamp": Int64(Date().timeIntervalSince1970 * 1000)
|
|
379
|
+
])
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// MARK: - Location Delegate
|
|
384
|
+
private class LocationDelegate: NSObject, CLLocationManagerDelegate {
|
|
385
|
+
weak var module: WifiModule?
|
|
386
|
+
|
|
387
|
+
init(module: WifiModule) {
|
|
388
|
+
self.module = module
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
|
|
392
|
+
module?.handlePermissionResult()
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
@available(iOS 14.0, *)
|
|
396
|
+
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
|
397
|
+
module?.handlePermissionResult()
|
|
398
|
+
}
|
|
399
|
+
}
|