add device info collector
This commit is contained in:
1 parent
4140922530
commit
1c3886e10c
4 files changed
+177
No files matched your search
@@ -51,6 +51,34 @@ JS 全局对象:
|
||||
|
||||
所有方法都使用 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);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
import android.hardware.biometrics.BiometricPrompt;
|
||||
@@ -47,11 +48,14 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class BluememeUtils extends CordovaPlugin {
|
||||
private static final String TAG = "BluememeUtils";
|
||||
private static final String PREFS = "bluememe_utils";
|
||||
private static final String KEY_DEVICE_KEY = "device_key";
|
||||
private static final String KEY_FCM_TOKEN = "fcm_token";
|
||||
private static final String KEY_INITIAL_NOTIFICATION = "initial_notification";
|
||||
private static final String ACTION_SUFFIX = ".BLUEMEME_UTILS_EVENT";
|
||||
@@ -75,6 +79,10 @@ public class BluememeUtils extends CordovaPlugin {
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if ("getDeviceInfo".equals(action)) {
|
||||
getDeviceInfo(callbackContext);
|
||||
return true;
|
||||
}
|
||||
if ("saveWifi".equals(action)) {
|
||||
saveWifi(args.optJSONObject(0), callbackContext);
|
||||
return true;
|
||||
@@ -184,6 +192,32 @@ public class BluememeUtils extends CordovaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private void getDeviceInfo(CallbackContext callbackContext) {
|
||||
Context context = cordova.getActivity().getApplicationContext();
|
||||
try {
|
||||
JSONObject result = new JSONObject();
|
||||
PackageInfo packageInfo = packageInfo(context);
|
||||
|
||||
result.put("deviceKey", getOrCreateDeviceKey(context));
|
||||
result.put("platform", "Android");
|
||||
result.put("appIdentifier", context.getPackageName());
|
||||
result.put("appVersion", packageInfo == null || packageInfo.versionName == null ? "" : packageInfo.versionName);
|
||||
result.put("buildNumber", packageInfo == null ? "" : String.valueOf(packageVersionCode(packageInfo)));
|
||||
result.put("osVersion", Build.VERSION.RELEASE == null ? "" : Build.VERSION.RELEASE);
|
||||
result.put("sdkVersion", Build.VERSION.SDK_INT);
|
||||
result.put("deviceModel", deviceModel());
|
||||
result.put("manufacturer", Build.MANUFACTURER == null ? "" : Build.MANUFACTURER);
|
||||
result.put("brand", Build.BRAND == null ? "" : Build.BRAND);
|
||||
result.put("model", Build.MODEL == null ? "" : Build.MODEL);
|
||||
result.put("locale", currentLocaleTag());
|
||||
result.put("timeZone", TimeZone.getDefault().getID());
|
||||
|
||||
callbackContext.success(result);
|
||||
} catch (Exception ex) {
|
||||
sendError(callbackContext, "DEVICE_INFO_FAILED", ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void connectWifi(JSONObject options, CallbackContext callbackContext) {
|
||||
JSONObject normalized = normalizeWifiOptions(options);
|
||||
String validationError = validateWifiOptions(normalized);
|
||||
@@ -1124,6 +1158,60 @@ public class BluememeUtils extends CordovaPlugin {
|
||||
return value == null ? "" : String.valueOf(value);
|
||||
}
|
||||
|
||||
private static PackageInfo packageInfo(Context context) {
|
||||
try {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
return packageManager.getPackageInfo(context.getPackageName(), PackageManager.PackageInfoFlags.of(0));
|
||||
}
|
||||
return packageManager.getPackageInfo(context.getPackageName(), 0);
|
||||
} catch (PackageManager.NameNotFoundException ex) {
|
||||
Log.w(TAG, "Unable to read package info.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static long packageVersionCode(PackageInfo packageInfo) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
return packageInfo.getLongVersionCode();
|
||||
}
|
||||
return packageInfo.versionCode;
|
||||
}
|
||||
|
||||
private static String getOrCreateDeviceKey(Context context) {
|
||||
SharedPreferences preferences = prefs(context);
|
||||
String existing = preferences.getString(KEY_DEVICE_KEY, "");
|
||||
if (!TextUtils.isEmpty(existing)) {
|
||||
return existing;
|
||||
}
|
||||
String generated = UUID.randomUUID().toString();
|
||||
preferences.edit().putString(KEY_DEVICE_KEY, generated).apply();
|
||||
return generated;
|
||||
}
|
||||
|
||||
private static String deviceModel() {
|
||||
String manufacturer = Build.MANUFACTURER == null ? "" : Build.MANUFACTURER;
|
||||
String model = Build.MODEL == null ? "" : Build.MODEL;
|
||||
if (TextUtils.isEmpty(manufacturer)) {
|
||||
return model;
|
||||
}
|
||||
if (TextUtils.isEmpty(model)) {
|
||||
return manufacturer;
|
||||
}
|
||||
if (model.toLowerCase(Locale.US).startsWith(manufacturer.toLowerCase(Locale.US))) {
|
||||
return model;
|
||||
}
|
||||
return manufacturer + " " + model;
|
||||
}
|
||||
|
||||
private static String currentLocaleTag() {
|
||||
Locale locale = Locale.getDefault();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return locale.toLanguageTag();
|
||||
}
|
||||
return locale.toString();
|
||||
}
|
||||
|
||||
static String eventAction(Context context) {
|
||||
return context.getPackageName() + ACTION_SUFFIX;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#import <NetworkExtension/NetworkExtension.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
static NSString * const BluememeDeviceKeyDefaultsKey = @"bluememe_utils_device_key";
|
||||
|
||||
@interface CDVBluememeUtils ()
|
||||
@property (nonatomic, copy) NSString *notificationCallbackId;
|
||||
@property (nonatomic, strong) NSDictionary *initialNotification;
|
||||
@@ -36,6 +38,33 @@
|
||||
[self applyWifiCommand:command defaultJoinOnce:YES];
|
||||
}
|
||||
|
||||
- (void)getDeviceInfo:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString *bundleIdentifier = [self safeString:[[NSBundle mainBundle] bundleIdentifier]];
|
||||
NSString *appVersion = [self safeString:[infoDictionary objectForKey:@"CFBundleShortVersionString"]];
|
||||
NSString *buildNumber = [self safeString:[infoDictionary objectForKey:@"CFBundleVersion"]];
|
||||
UIDevice *device = [UIDevice currentDevice];
|
||||
NSLocale *locale = [NSLocale currentLocale];
|
||||
NSString *localeIdentifier = [self safeString:[locale localeIdentifier]];
|
||||
NSString *timeZone = [self safeString:[[NSTimeZone localTimeZone] name]];
|
||||
|
||||
NSDictionary *result = @{
|
||||
@"deviceKey": [self getOrCreateDeviceKey],
|
||||
@"platform": @"iOS",
|
||||
@"appIdentifier": bundleIdentifier,
|
||||
@"appVersion": appVersion,
|
||||
@"buildNumber": buildNumber,
|
||||
@"osVersion": [self safeString:device.systemVersion],
|
||||
@"systemName": [self safeString:device.systemName],
|
||||
@"deviceModel": [self safeString:device.model],
|
||||
@"model": [self safeString:device.model],
|
||||
@"locale": localeIdentifier,
|
||||
@"timeZone": timeZone
|
||||
};
|
||||
[self sendSuccessForCommand:command dictionary:result];
|
||||
}
|
||||
|
||||
- (void)removeWifi:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
NSDictionary *options = [self firstDictionaryArgument:command];
|
||||
@@ -446,6 +475,20 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (NSString *)safeString:(id)value
|
||||
{
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
return value;
|
||||
}
|
||||
if ([value respondsToSelector:@selector(stringValue)]) {
|
||||
return [value stringValue];
|
||||
}
|
||||
if ([value respondsToSelector:@selector(description)]) {
|
||||
return [value description];
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (BOOL)boolFromDictionary:(NSDictionary *)dictionary key:(NSString *)key defaultValue:(BOOL)defaultValue
|
||||
{
|
||||
id value = dictionary[key];
|
||||
@@ -473,6 +516,20 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (NSString *)getOrCreateDeviceKey
|
||||
{
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
NSString *existing = [defaults stringForKey:BluememeDeviceKeyDefaultsKey];
|
||||
if (existing.length > 0) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
NSString *generated = [[NSUUID UUID] UUIDString];
|
||||
[defaults setObject:generated forKey:BluememeDeviceKeyDefaultsKey];
|
||||
[defaults synchronize];
|
||||
return generated;
|
||||
}
|
||||
|
||||
- (void)sendSuccessForCommand:(CDVInvokedUrlCommand *)command dictionary:(NSDictionary *)dictionary
|
||||
{
|
||||
CDVPluginResult *result = dictionary == nil
|
||||
|
||||
@@ -45,6 +45,10 @@ function dispatchLocalEvent(event) {
|
||||
}
|
||||
|
||||
var api = {
|
||||
getDeviceInfo: function (success, error) {
|
||||
call('getDeviceInfo', [], success, error);
|
||||
},
|
||||
|
||||
saveWifi: function (options, success, error) {
|
||||
call('saveWifi', [asWifiOptions(options)], success, error);
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user