# bluememe-utils Cordova plugin for OutSystems 11 mobile apps. 功能: - 保存或连接指定 WiFi 热点 - Firebase Cloud Messaging 推送通知 - Touch ID / Face ID / Android biometric 解锁,并把解锁结果通知给应用 插件 ID:`bluememe-utils` JS 全局对象: - `BluememeUtils` - `cordova.plugins.bluememeUtils` ## Install 在 OutSystems 11 的 extensibility configuration 中添加插件仓库或本地包后,可使用下面的参数覆盖 Firebase 配置。 ```json { "plugin": { "url": "bluememe-utils", "variables": { "ANDROID_FIREBASE_APP_ID": "1:000000000000:android:xxxxxxxxxxxxxxxx", "ANDROID_FIREBASE_API_KEY": "AIza...", "ANDROID_FIREBASE_PROJECT_ID": "your-firebase-project", "ANDROID_FIREBASE_SENDER_ID": "000000000000", "ANDROID_FIREBASE_STORAGE_BUCKET": "your-firebase-project.appspot.com", "IOS_FIREBASE_GOOGLE_APP_ID": "1:000000000000:ios:xxxxxxxxxxxxxxxx", "IOS_FIREBASE_API_KEY": "AIza...", "IOS_FIREBASE_PROJECT_ID": "your-firebase-project", "IOS_FIREBASE_GCM_SENDER_ID": "000000000000", "IOS_FIREBASE_STORAGE_BUCKET": "your-firebase-project.appspot.com", "IOS_FACE_ID_USAGE_DESCRIPTION": "Use Face ID to unlock this application." } } } ``` 也可以让宿主 App 提供并处理标准 Firebase 配置文件: - Android:`google-services.json` - iOS:`GoogleService-Info.plist` 如果宿主工程已经由 OutSystems、MABS 配置或其他插件初始化 Firebase,本插件会复用已有的 default Firebase app。否则建议使用上面的 plugin variables,让本插件在运行时手动初始化 Firebase。 ## JavaScript API 所有方法都使用 Cordova callback 风格,适合在 OutSystems client action 中封装。 ```js BluememeUtils.getDeviceInfo(function (info) { console.log(info.deviceKey); }, error); ``` `getDeviceInfo` 返回: ```json { "deviceKey": "generated-and-persisted-uuid", "platform": "Android", "appIdentifier": "com.company.app", "appVersion": "1.0.0", "buildNumber": "1", "osVersion": "15", "sdkVersion": 35, "deviceModel": "Google Pixel 8", "manufacturer": "Google", "brand": "google", "model": "Pixel 8", "locale": "ja-JP", "timeZone": "Asia/Tokyo" } ``` `deviceKey` 首次调用时生成并保存在本地,用于标识当前 App 安装实例。App 卸载或清除数据后会重新生成。iOS 不返回 `sdkVersion`、`manufacturer`、`brand`,但会返回 `systemName`。 ```js BluememeUtils.requestWifiPermission(success, error); BluememeUtils.saveWifi({ ssid: "MyHotspot", password: "password123", security: "wpa2" }, success, error); BluememeUtils.connectWifi({ ssid: "MyHotspot", password: "password123", security: "wpa2" }, success, error); BluememeUtils.removeWifi({ ssid: "MyHotspot", password: "password123", security: "wpa2" }, success, error); BluememeUtils.disconnectWifi(success, error); ``` WiFi options: - `ssid`:必填 - `password`:开放网络可省略 - `security`:`open`、`wep`、`wpa2`、`wpa3`,默认按是否存在 password 推断为 `open` 或 `wpa2` - `hiddenSsid`:可选;Android 支持隐藏 SSID;iOS 15+ 会设置 `NEHotspotConfiguration.hidden` - `bssid`:可选,仅 Android 10+ 临时连接使用 - `joinOnce`:可选,仅 iOS;`connectWifi` 默认 `true`,`saveWifi` 默认 `false` - `iosReferenceFallback`:可选,仅 iOS,默认 `true`;当主实现返回 `NEHotspotConfigurationErrorDomain` code `8` 时,会按 `JGeraldoLima/cordova-wifi-plugin` 的最小 iOS 写法再尝试一次,用于区分本插件参数/分支问题和系统/API 问题 ## Push Notifications ```js BluememeUtils.requestPushPermission(success, error); BluememeUtils.getPushPermissionStatus(success, error); BluememeUtils.getFirebaseToken(function (result) { console.log(result.token); }, error); BluememeUtils.subscribeToTopic("news", success, error); BluememeUtils.unsubscribeFromTopic("news", success, error); BluememeUtils.startNotificationListener(function (event) { if (event.type === "token") { console.log(event.token); } if (event.type === "notification") { console.log(event.notification); } if (event.type === "notificationOpened") { console.log(event.notification); } }, error); BluememeUtils.getInitialNotification(success, error); BluememeUtils.stopNotificationListener(success, error); ``` `getPushPermissionStatus` 只读取当前系统通知状态,不弹授权框。可用于填充后端 `NotificationsEnabled`。iOS 返回 `authorizationStatus: "notDetermined"` 时,说明还没有触发系统通知授权弹窗,需要先调用 `requestPushPermission`,用户授权后再调用 `getFirebaseToken`。 iOS 获取 FCM token 的推荐顺序: ```js BluememeUtils.requestPushPermission(function (permission) { if (permission.enabled !== true) { return; } BluememeUtils.getFirebaseToken(function (result) { console.log(result.token); }, error); }, error); ``` iOS 上 FCM token 依赖 APNs device token。插件会在 `getFirebaseToken` 内等待 Cordova 收到 APNs token 后再向 Firebase 请求 FCM token;如果 APNs token 超时或注册失败,会返回 `APNS_TOKEN_NOT_AVAILABLE` 或 `APNS_TOKEN_FAILED`。 返回示例: ```json { "type": "notification", "platform": "Android", "enabled": true, "granted": true, "runtimePermissionGranted": true, "appNotificationsEnabled": true, "authorizationStatus": "authorized", "missing": [] } ``` 推荐注册 token 前调用: ```js BluememeUtils.getPushPermissionStatus(function (status) { var notificationsEnabled = status.enabled === true; }, error); ``` 事件也会派发到 `window`: - `bluememeutils.token` - `bluememeutils.notification` - `bluememeutils.notificationOpened` - `bluememeutils.unlock` 也可以用插件内置监听器: ```js BluememeUtils.on("unlock", function (event) { console.log(event.unlocked); }); ``` ## Biometric Unlock ```js BluememeUtils.isBiometricAvailable(success, error); BluememeUtils.unlockWithBiometrics({ reason: "Unlock application", title: "Unlock", subtitle: "", description: "", cancelText: "Cancel", fallbackTitle: "Use Passcode", allowDeviceCredential: true }, function (result) { console.log(result.unlocked, result.biometryType); }, error); ``` `biometryType` 可能值: - iOS:`face`、`touch`、`none`、`biometric` - Android:`face`、`fingerprint`、`iris`、`biometric` ## Platform Notes Android WiFi: - Android 10+ 不允许第三方应用静默保存并强制切换系统 WiFi。 - `saveWifi` 在 Android 10+ 使用 `WifiNetworkSuggestion`,系统可能要求用户批准。 - `connectWifi` 在 Android 10+ 使用 `WifiNetworkSpecifier`,这是临时连接,通常只绑定当前应用进程网络。 - Android 9 及以下使用旧 `WifiConfiguration` API,可保存并启用网络。 - 调用 WiFi API 前先调用 `requestWifiPermission`,并确保系统位置服务开启。 iOS WiFi: - 使用 `NEHotspotConfiguration`,当前插件 WiFi 功能要求 iOS 15+。 - 插件将 iOS deployment target 设置为 `15.0`;如果宿主 App 或构建平台覆盖该值,需要在宿主配置中同步设置 iOS 最低版本为 15.0 或更高。 - 插件会向 Cordova iOS entitlements 写入 `com.apple.developer.networking.HotspotConfiguration`,但仍必须在 Apple Developer App ID 中开启 `Hotspot Configuration` capability,并使用包含该 capability 的 provisioning profile 重新构建。 - 如果 iOS 返回 `NEHotspotConfigurationErrorDomain` code `8` / `internal error`,优先检查 App ID、entitlements、provisioning profile 是否都包含 `Hotspot Configuration`,并确认是在真机前台运行,不是在模拟器中测试。若签名和参数都正确,常见原因是真实热点使用 WPA2/WPA3 transition mode、WPA3 Personal、iPhone Personal Hotspot 默认安全模式,或其他 `NEHotspotConfiguration` 无法表达的 WiFi 能力。 - iOS 错误结果会包含 `applicationState`、`ssidUtf8Length`、`passwordUtf8Length`、`passwordAsciiPrintable` 等诊断字段;WPA/WPA2 密码建议使用 8-63 位可打印 ASCII 字符。 - iOS 错误结果会包含 `pluginVersion`,用于确认 IPA 中实际运行的原生插件版本。 - iOS code `8` 会自动启用 `iosReferenceFallback`。该 fallback 改编自 MIT 许可的 `JGeraldoLima/cordova-wifi-plugin`,只使用 `NEHotspotConfiguration(ssid:passphrase:isWEP:false)`、`joinOnce=false`,不设置 `hidden`,并会返回 `referenceNativeCode` / `referenceNativeError` 供对照。 - iOS 的 `NEHotspotConfiguration` password initializer 公开支持 WEP/WPA/WPA2 Personal,不提供 WPA3 Personal 参数。iOS 传入 `security` 包含 `wpa3` 时会返回 `UNSUPPORTED_WIFI_SECURITY`。WPA2/WPA3 mixed 通常只能按 `security: "wpa2"` 尝试;如果仍失败,需要用户手动加入,或改用配置描述文件/MDM 流程。 - iOS 不提供第三方 App 断开当前 WiFi 的公开 API,因此 `disconnectWifi` 在 iOS 返回错误。 Firebase: - Android 13+ 需要通知权限,调用 `requestPushPermission`。 - iOS 需要 Apple Developer App ID 开启 Push Notifications,并使用重新生成的 provisioning profile 构建。 - 插件会向 Cordova iOS entitlements 写入 `aps-environment`:Debug 为 `development`,Release 为 `production`。仍建议在构建后的 IPA 中检查签名 entitlements 是否包含该 key。 - Firebase Console 需要在 Project Settings > Cloud Messaging > Apple app configuration 中上传 APNs Auth Key `.p8` 或 APNs certificate。推荐使用 APNs Auth Key。 - Background Modes 中的 Remote notifications 只影响后台静默推送/后台唤醒,不是获取 APNs token 和 FCM token 的前置条件。 - 如果项目中还有其他推送插件,也可能同时修改 `UNUserNotificationCenterDelegate` 或 Firebase service,需要在真机上确认通知打开事件。 Biometric: - iOS Face ID 需要 `NSFaceIDUsageDescription`,可通过 `IOS_FACE_ID_USAGE_DESCRIPTION` 设置。 - Android 本插件使用平台 `BiometricPrompt`,`unlockWithBiometrics` 支持 Android 9+。 ## Error Shape 错误 callback 返回对象: ```json { "code": "ERROR_CODE", "message": "Human readable message" } ``` 常见错误: - `PERMISSION_REQUIRED` - `INVALID_WIFI_OPTIONS` - `WIFI_CONNECT_FAILED` - `FIREBASE_NOT_CONFIGURED` - `APNS_TOKEN_NOT_AVAILABLE` - `APNS_TOKEN_FAILED` - `FCM_TOKEN_FAILED` - `BIOMETRIC_UNAVAILABLE` - `AUTHENTICATION_ERROR`