Files
aria 8fcb440d8a •插件初始化时会给宿主 AppDelegate 安装 APNs 注册回调桥接。
•捕获:
◦application:didRegisterForRemoteNotificationsWithDeviceToken:
◦application:didFailToRegisterForRemoteNotificationsWithError:
•即使宿主没有转发 Cordova 的 CDVRemoteNotification,插件也能自己收到 APNs token。
•getFirebaseToken 等待 APNs token 时,会同时轮询 Firebase Messaging 已缓存的 APNSToken。
•返回结果增加诊断字段:
◦apnsTokenSource: plugin / firebase / none
◦cachedAPNSTokenAvailable
◦firebaseAPNSTokenAvailable
2026-05-25 11:26:31 +09:00

11 KiB
Raw Permalink Blame History

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 配置。

{
  "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 中封装。

BluememeUtils.getDeviceInfo(function (info) {
  console.log(info.deviceKey);
}, error);

getDeviceInfo 返回:

{
  "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。

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

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 的推荐顺序:

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。必须等待 requestPushPermission 的 success callback 返回后再调用 getFirebaseToken,不要把 requestPushPermission() 当成同步调用。

插件会在 getFirebaseToken 内等待 APNs token 后再向 Firebase 请求 FCM token。iOS 侧会同时使用三种来源降低宿主 AppDelegate 未转发回调造成的问题:

  • 直接桥接 application:didRegisterForRemoteNotificationsWithDeviceToken:
  • 监听 Cordova 的 CDVRemoteNotification
  • 轮询 Firebase Messaging 已缓存的 APNSToken

如果 APNs token 超时或注册失败,会返回 APNS_TOKEN_NOT_AVAILABLE 或 APNS_TOKEN_FAILED。返回结果会包含 apnsTokenSource、cachedAPNSTokenAvailable、firebaseAPNSTokenAvailable 等诊断字段。

返回示例:

{
  "type": "notification",
  "platform": "Android",
  "enabled": true,
  "granted": true,
  "runtimePermissionGranted": true,
  "appNotificationsEnabled": true,
  "authorizationStatus": "authorized",
  "missing": []
}

推荐注册 token 前调用:

BluememeUtils.getPushPermissionStatus(function (status) {
  var notificationsEnabled = status.enabled === true;
}, error);

事件也会派发到 window:

  • bluememeutils.token
  • bluememeutils.notification
  • bluememeutils.notificationOpened
  • bluememeutils.unlock

也可以用插件内置监听器:

BluememeUtils.on("unlock", function (event) {
  console.log(event.unlocked);
});

Biometric Unlock

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 返回对象:

{
  "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