Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92735af955 | ||
|
|
41739ef881 |
No files matched your search
@@ -19,33 +19,37 @@
|
||||
```json
|
||||
{
|
||||
"plugin": {
|
||||
"url": "https://<your-git-host>/cordova-plugin-bluememe-wifi-connector.git#v1.0.0"
|
||||
"url": "git+https://<your-git-host>/cordova-plugin-bluememe-wifi-connector.git#v1.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果你直接使用当前仓库作为插件仓库,把 `<your-git-host>` 换成你的 Git 地址即可。
|
||||
这里要使用 `git+https://` 前缀,让 Cordova 按 Git 仓库而不是普通下载地址处理这个 URL。
|
||||
|
||||
如果你直接使用当前仓库作为插件仓库,把 `<your-git-host>` 换成你的 Git 地址即可;开发阶段也可以临时写成 `#main`,正式发布建议固定到 tag。
|
||||
|
||||
## JavaScript 调用
|
||||
|
||||
插件加载后可通过 `cordova.plugins.BluememeWifiConnector` 调用。
|
||||
|
||||
```javascript
|
||||
document.addEventListener("deviceready", function () {
|
||||
cordova.plugins.BluememeWifiConnector.connect({
|
||||
ssid: "Factory_AP_01",
|
||||
passphrase: "12345678",
|
||||
document.addEventListener("deviceready", function () {
|
||||
cordova.plugins.BluememeWifiConnector.connect({
|
||||
ssid: "Factory_AP_01",
|
||||
passphrase: "12345678",
|
||||
security: "WPA2",
|
||||
mode: "auto"
|
||||
}).then(function (result) {
|
||||
console.log("Wi-Fi success", result);
|
||||
}).catch(function (error) {
|
||||
console.error("Wi-Fi error", error);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 推荐模式
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
iOS 成功时会额外返回 `connectionSucceeded`、`connectionStatus` 和 `details`。其中 `details` 会包含 `configuredSSIDs`、`requestedSSIDConfiguredByApp`、`passphraseLength` 等诊断字段;失败时同样会把输入摘要和 native 错误信息放进 `error.details`,便于定位是参数问题还是系统拒绝。
|
||||
|
||||
## 推荐模式
|
||||
|
||||
### 1. 设备级保存网络
|
||||
|
||||
|
||||
+175
-24
@@ -23,7 +23,14 @@
|
||||
}
|
||||
|
||||
NSString *message = [NSString stringWithFormat:@"Mode '%@' is not supported on iOS. Use 'save' or 'localOnly'.", resolvedMode];
|
||||
[self sendErrorCode:@"MODE_NOT_SUPPORTED" message:message details:nil command:command];
|
||||
[self sendErrorCode:@"MODE_NOT_SUPPORTED"
|
||||
message:message
|
||||
details:@{
|
||||
@"platform": @"ios",
|
||||
@"requestedMode": requestedMode ?: @"",
|
||||
@"resolvedMode": resolvedMode ?: @""
|
||||
}
|
||||
command:command];
|
||||
}
|
||||
|
||||
- (void)connectLocalOnly:(CDVInvokedUrlCommand *)command
|
||||
@@ -101,15 +108,22 @@
|
||||
{
|
||||
if (@available(iOS 11.0, *)) {
|
||||
NSString *ssid = [self stringForKey:@"ssid" inOptions:options];
|
||||
NSString *security = [self normalizeSecurity:[self stringForKey:@"security" inOptions:options]];
|
||||
NSMutableDictionary *requestDetails = [self requestDetailsForOptions:options
|
||||
ssid:ssid
|
||||
security:security
|
||||
mode:mode
|
||||
joinOnce:joinOnce];
|
||||
|
||||
if (ssid.length == 0) {
|
||||
requestDetails[@"invalidOption"] = @"ssid";
|
||||
[self sendErrorCode:@"INVALID_OPTIONS"
|
||||
message:@"Missing required option: ssid"
|
||||
details:nil
|
||||
details:requestDetails
|
||||
command:command];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *security = [self normalizeSecurity:[self stringForKey:@"security" inOptions:options]];
|
||||
NEHotspotConfiguration *configuration = nil;
|
||||
|
||||
if ([security isEqualToString:@"OPEN"]) {
|
||||
@@ -117,23 +131,36 @@
|
||||
} else if ([security isEqualToString:@"WPA2"]) {
|
||||
NSString *passphrase = [self stringForKey:@"passphrase" inOptions:options];
|
||||
if (passphrase.length == 0) {
|
||||
requestDetails[@"invalidOption"] = @"passphrase";
|
||||
[self sendErrorCode:@"INVALID_OPTIONS"
|
||||
message:@"Missing required option: passphrase"
|
||||
details:nil
|
||||
details:requestDetails
|
||||
command:command];
|
||||
return;
|
||||
}
|
||||
|
||||
if (passphrase.length < 8 || passphrase.length > 63) {
|
||||
requestDetails[@"invalidOption"] = @"passphrase";
|
||||
requestDetails[@"validation"] = @"WPA2 passphrase must be between 8 and 63 characters.";
|
||||
[self sendErrorCode:@"INVALID_OPTIONS"
|
||||
message:@"Invalid passphrase length for WPA2 Wi-Fi."
|
||||
details:requestDetails
|
||||
command:command];
|
||||
return;
|
||||
}
|
||||
|
||||
configuration = [[NEHotspotConfiguration alloc] initWithSSID:ssid passphrase:passphrase isWEP:NO];
|
||||
} else if ([security isEqualToString:@"WPA3"]) {
|
||||
requestDetails[@"invalidOption"] = @"security";
|
||||
[self sendErrorCode:@"MODE_NOT_SUPPORTED"
|
||||
message:@"iOS hotspot configuration supports OPEN, WEP, WPA, and WPA2. WPA3 is not exposed by this plugin on iOS."
|
||||
details:nil
|
||||
details:requestDetails
|
||||
command:command];
|
||||
return;
|
||||
} else {
|
||||
NSString *message = [NSString stringWithFormat:@"Unsupported security type on iOS: %@", security];
|
||||
[self sendErrorCode:@"INVALID_SECURITY" message:message details:nil command:command];
|
||||
requestDetails[@"invalidOption"] = @"security";
|
||||
[self sendErrorCode:@"INVALID_SECURITY" message:message details:requestDetails command:command];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,26 +169,24 @@
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NEHotspotConfigurationManager sharedManager] applyConfiguration:configuration completionHandler:^(NSError * _Nullable error) {
|
||||
if (error != nil) {
|
||||
[self sendNativeError:error code:@"IOS_WIFI_CONFIGURATION_FAILED" command:command];
|
||||
if (error != nil && ![self isAlreadyAssociatedError:error]) {
|
||||
[self sendNativeError:error
|
||||
code:@"IOS_WIFI_CONFIGURATION_FAILED"
|
||||
requestDetails:requestDetails
|
||||
command:command];
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableDictionary *result = [@{
|
||||
@"mode": mode,
|
||||
@"ssid": ssid,
|
||||
@"apiName": @"NEHotspotConfigurationManager",
|
||||
@"platform": @"ios",
|
||||
@"joinOnce": @(joinOnce),
|
||||
@"hidden": @(configuration.hidden),
|
||||
@"saved": @(!joinOnce)
|
||||
} mutableCopy];
|
||||
|
||||
if ([mode isEqualToString:@"localOnly"]) {
|
||||
result[@"boundToProcess"] = @NO;
|
||||
}
|
||||
|
||||
[self sendSuccess:result command:command];
|
||||
NSString *connectionStatus = error == nil ? @"APPLIED" : @"ALREADY_ASSOCIATED";
|
||||
[self sendApplyConfigurationSuccessForSSID:ssid
|
||||
security:security
|
||||
mode:mode
|
||||
joinOnce:joinOnce
|
||||
configuration:configuration
|
||||
requestDetails:requestDetails
|
||||
connectionStatus:connectionStatus
|
||||
nativeError:error
|
||||
command:command];
|
||||
}];
|
||||
});
|
||||
return;
|
||||
@@ -173,6 +198,99 @@
|
||||
command:command];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)requestDetailsForOptions:(NSDictionary *)options
|
||||
ssid:(NSString *)ssid
|
||||
security:(NSString *)security
|
||||
mode:(NSString *)mode
|
||||
joinOnce:(BOOL)joinOnce
|
||||
{
|
||||
NSString *passphrase = [self stringForKey:@"passphrase" inOptions:options];
|
||||
BOOL hidden = [self boolForKey:@"hidden" inOptions:options defaultValue:NO];
|
||||
NSMutableDictionary *details = [@{
|
||||
@"platform": @"ios",
|
||||
@"apiName": @"NEHotspotConfigurationManager",
|
||||
@"resolvedMode": mode ?: @"",
|
||||
@"security": security ?: @"",
|
||||
@"joinOnce": @(joinOnce),
|
||||
@"hidden": @(hidden),
|
||||
@"ssidLength": @(ssid.length),
|
||||
@"passphraseProvided": @(passphrase.length > 0),
|
||||
@"systemVersion": [[NSProcessInfo processInfo] operatingSystemVersionString] ?: @""
|
||||
} mutableCopy];
|
||||
|
||||
if (passphrase.length > 0) {
|
||||
details[@"passphraseLength"] = @(passphrase.length);
|
||||
}
|
||||
|
||||
if (options[@"localOnly"] != nil) {
|
||||
details[@"localOnly"] = @([self boolForKey:@"localOnly" inOptions:options defaultValue:NO]);
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)baseConnectionResultForSSID:(NSString *)ssid
|
||||
security:(NSString *)security
|
||||
mode:(NSString *)mode
|
||||
joinOnce:(BOOL)joinOnce
|
||||
configuration:(NEHotspotConfiguration *)configuration
|
||||
requestDetails:(NSDictionary *)requestDetails
|
||||
connectionStatus:(NSString *)connectionStatus
|
||||
{
|
||||
NSMutableDictionary *result = [@{
|
||||
@"mode": mode ?: @"",
|
||||
@"ssid": ssid ?: @"",
|
||||
@"security": security ?: @"",
|
||||
@"apiName": @"NEHotspotConfigurationManager",
|
||||
@"platform": @"ios",
|
||||
@"joinOnce": @(joinOnce),
|
||||
@"hidden": @(configuration.hidden),
|
||||
@"saved": @(!joinOnce),
|
||||
@"connectionSucceeded": @YES,
|
||||
@"connectionStatus": connectionStatus ?: @"UNKNOWN",
|
||||
@"details": [requestDetails mutableCopy]
|
||||
} mutableCopy];
|
||||
|
||||
if ([mode isEqualToString:@"localOnly"]) {
|
||||
result[@"boundToProcess"] = @NO;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)sendApplyConfigurationSuccessForSSID:(NSString *)ssid
|
||||
security:(NSString *)security
|
||||
mode:(NSString *)mode
|
||||
joinOnce:(BOOL)joinOnce
|
||||
configuration:(NEHotspotConfiguration *)configuration
|
||||
requestDetails:(NSDictionary *)requestDetails
|
||||
connectionStatus:(NSString *)connectionStatus
|
||||
nativeError:(NSError * _Nullable)nativeError
|
||||
command:(CDVInvokedUrlCommand *)command API_AVAILABLE(ios(11.0))
|
||||
{
|
||||
NSMutableDictionary *result = [self baseConnectionResultForSSID:ssid
|
||||
security:security
|
||||
mode:mode
|
||||
joinOnce:joinOnce
|
||||
configuration:configuration
|
||||
requestDetails:requestDetails
|
||||
connectionStatus:connectionStatus];
|
||||
NSMutableDictionary *details = [result[@"details"] mutableCopy];
|
||||
details[@"configurationApplied"] = @YES;
|
||||
|
||||
if (nativeError != nil) {
|
||||
[details addEntriesFromDictionary:[self nativeErrorDetails:nativeError]];
|
||||
}
|
||||
|
||||
[[NEHotspotConfigurationManager sharedManager] getConfiguredSSIDsWithCompletionHandler:^(NSArray<NSString *> * _Nonnull ssids) {
|
||||
NSArray<NSString *> *configuredSsids = ssids ?: @[];
|
||||
details[@"configuredSSIDs"] = configuredSsids;
|
||||
details[@"requestedSSIDConfiguredByApp"] = @([configuredSsids containsObject:ssid]);
|
||||
result[@"details"] = details;
|
||||
[self sendSuccess:result command:command];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDictionary *)optionsFromCommand:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
if ([command.arguments count] == 0) {
|
||||
@@ -279,19 +397,52 @@
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)sendNativeError:(NSError *)error code:(NSString *)code command:(CDVInvokedUrlCommand *)command
|
||||
- (NSDictionary *)nativeErrorDetails:(NSError *)error
|
||||
{
|
||||
NSMutableDictionary *details = [NSMutableDictionary dictionary];
|
||||
if (error.domain.length > 0) {
|
||||
details[@"domain"] = error.domain;
|
||||
}
|
||||
details[@"nativeCode"] = @(error.code);
|
||||
if (error.localizedDescription.length > 0) {
|
||||
details[@"nativeMessage"] = error.localizedDescription;
|
||||
}
|
||||
|
||||
id failureReason = error.userInfo[NSLocalizedFailureReasonErrorKey];
|
||||
if ([failureReason isKindOfClass:[NSString class]] && [failureReason length] > 0) {
|
||||
details[@"failureReason"] = failureReason;
|
||||
}
|
||||
|
||||
id recoverySuggestion = error.userInfo[NSLocalizedRecoverySuggestionErrorKey];
|
||||
if ([recoverySuggestion isKindOfClass:[NSString class]] && [recoverySuggestion length] > 0) {
|
||||
details[@"recoverySuggestion"] = recoverySuggestion;
|
||||
}
|
||||
|
||||
id debugDescription = error.userInfo[NSDebugDescriptionErrorKey];
|
||||
if ([debugDescription isKindOfClass:[NSString class]] && [debugDescription length] > 0) {
|
||||
details[@"debugDescription"] = debugDescription;
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
- (BOOL)isAlreadyAssociatedError:(NSError *)error
|
||||
{
|
||||
return error != nil &&
|
||||
[error.domain isEqualToString:NEHotspotConfigurationErrorDomain] &&
|
||||
error.code == NEHotspotConfigurationErrorAlreadyAssociated;
|
||||
}
|
||||
|
||||
- (void)sendNativeError:(NSError *)error
|
||||
code:(NSString *)code
|
||||
requestDetails:(NSDictionary *)requestDetails
|
||||
command:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
NSMutableDictionary *details = [requestDetails mutableCopy] ?: [NSMutableDictionary dictionary];
|
||||
[details addEntriesFromDictionary:[self nativeErrorDetails:error]];
|
||||
details[@"connectionSucceeded"] = @NO;
|
||||
details[@"connectionStatus"] = @"FAILED";
|
||||
|
||||
[self sendErrorCode:code
|
||||
message:error.localizedDescription
|
||||
details:details
|
||||
|
||||
Reference in new issue
Block a user