1275 lines
51 KiB
Java
1275 lines
51 KiB
Java
package com.bluememe.utils;
|
|
|
|
import android.Manifest;
|
|
import android.annotation.TargetApi;
|
|
import android.app.Activity;
|
|
import android.app.KeyguardManager;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
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;
|
|
import android.hardware.fingerprint.FingerprintManager;
|
|
import android.net.ConnectivityManager;
|
|
import android.net.Network;
|
|
import android.net.NetworkCapabilities;
|
|
import android.net.NetworkRequest;
|
|
import android.net.wifi.WifiConfiguration;
|
|
import android.net.wifi.WifiManager;
|
|
import android.net.wifi.WifiNetworkSpecifier;
|
|
import android.net.wifi.WifiNetworkSuggestion;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.CancellationSignal;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
|
|
import com.google.android.gms.tasks.OnCompleteListener;
|
|
import com.google.android.gms.tasks.Task;
|
|
import com.google.firebase.FirebaseApp;
|
|
import com.google.firebase.FirebaseOptions;
|
|
import com.google.firebase.messaging.FirebaseMessaging;
|
|
|
|
import org.apache.cordova.CallbackContext;
|
|
import org.apache.cordova.CordovaPlugin;
|
|
import org.apache.cordova.PermissionHelper;
|
|
import org.apache.cordova.PluginResult;
|
|
import org.json.JSONArray;
|
|
import org.json.JSONException;
|
|
import org.json.JSONObject;
|
|
|
|
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";
|
|
private static final String EXTRA_EVENT = "event";
|
|
private static final String EXTRA_PAYLOAD = "bluememe_payload";
|
|
private static final int REQ_WIFI_PERMISSION = 8101;
|
|
private static final int REQ_NOTIFICATION_PERMISSION = 8102;
|
|
|
|
private CallbackContext wifiPermissionCallback;
|
|
private CallbackContext notificationPermissionCallback;
|
|
private CallbackContext notificationCallback;
|
|
private ConnectivityManager.NetworkCallback networkCallback;
|
|
private BroadcastReceiver eventReceiver;
|
|
|
|
@Override
|
|
protected void pluginInitialize() {
|
|
ensureFirebaseInitialized(false);
|
|
registerEventReceiver();
|
|
captureLaunchIntent(cordova.getActivity().getIntent());
|
|
}
|
|
|
|
@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;
|
|
}
|
|
if ("connectWifi".equals(action)) {
|
|
connectWifi(args.optJSONObject(0), callbackContext);
|
|
return true;
|
|
}
|
|
if ("disconnectWifi".equals(action)) {
|
|
disconnectWifi(callbackContext);
|
|
return true;
|
|
}
|
|
if ("removeWifi".equals(action)) {
|
|
removeWifi(args.optJSONObject(0), callbackContext);
|
|
return true;
|
|
}
|
|
if ("requestWifiPermission".equals(action)) {
|
|
requestWifiPermission(callbackContext);
|
|
return true;
|
|
}
|
|
if ("isBiometricAvailable".equals(action)) {
|
|
isBiometricAvailable(callbackContext);
|
|
return true;
|
|
}
|
|
if ("unlockWithBiometrics".equals(action)) {
|
|
unlockWithBiometrics(args.optJSONObject(0), callbackContext);
|
|
return true;
|
|
}
|
|
if ("requestPushPermission".equals(action)) {
|
|
requestPushPermission(callbackContext);
|
|
return true;
|
|
}
|
|
if ("getFirebaseToken".equals(action)) {
|
|
getFirebaseToken(callbackContext);
|
|
return true;
|
|
}
|
|
if ("subscribeToTopic".equals(action)) {
|
|
subscribeToTopic(args.optString(0, ""), callbackContext);
|
|
return true;
|
|
}
|
|
if ("unsubscribeFromTopic".equals(action)) {
|
|
unsubscribeFromTopic(args.optString(0, ""), callbackContext);
|
|
return true;
|
|
}
|
|
if ("getInitialNotification".equals(action)) {
|
|
getInitialNotification(callbackContext);
|
|
return true;
|
|
}
|
|
if ("startNotificationListener".equals(action)) {
|
|
startNotificationListener(callbackContext);
|
|
return true;
|
|
}
|
|
if ("stopNotificationListener".equals(action)) {
|
|
notificationCallback = null;
|
|
callbackContext.success();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void onNewIntent(Intent intent) {
|
|
captureLaunchIntent(intent);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
unregisterEventReceiver();
|
|
stopNetworkRequest();
|
|
super.onDestroy();
|
|
}
|
|
|
|
@Override
|
|
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException {
|
|
if (requestCode == REQ_WIFI_PERMISSION) {
|
|
CallbackContext callback = wifiPermissionCallback;
|
|
wifiPermissionCallback = null;
|
|
if (callback != null) {
|
|
callback.success(permissionResult("wifi", allGranted(grantResults), missingWifiPermissions()));
|
|
}
|
|
return;
|
|
}
|
|
if (requestCode == REQ_NOTIFICATION_PERMISSION) {
|
|
CallbackContext callback = notificationPermissionCallback;
|
|
notificationPermissionCallback = null;
|
|
if (callback != null) {
|
|
callback.success(permissionResult("notification", allGranted(grantResults), new JSONArray()));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void saveWifi(JSONObject options, CallbackContext callbackContext) {
|
|
JSONObject normalized = normalizeWifiOptions(options);
|
|
String validationError = validateWifiOptions(normalized);
|
|
if (validationError != null) {
|
|
sendError(callbackContext, "INVALID_WIFI_OPTIONS", validationError);
|
|
return;
|
|
}
|
|
if (!hasWifiPermissions()) {
|
|
sendError(callbackContext, "PERMISSION_REQUIRED", "Call requestWifiPermission before saving a WiFi network.");
|
|
return;
|
|
}
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
saveWifiSuggestion(normalized, callbackContext);
|
|
} else {
|
|
saveLegacyWifi(normalized, callbackContext);
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (validationError != null) {
|
|
sendError(callbackContext, "INVALID_WIFI_OPTIONS", validationError);
|
|
return;
|
|
}
|
|
if (!hasWifiPermissions()) {
|
|
sendError(callbackContext, "PERMISSION_REQUIRED", "Call requestWifiPermission before connecting to a WiFi network.");
|
|
return;
|
|
}
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
connectWithSpecifier(normalized, callbackContext);
|
|
} else {
|
|
connectLegacyWifi(normalized, callbackContext);
|
|
}
|
|
}
|
|
|
|
private void removeWifi(JSONObject options, CallbackContext callbackContext) {
|
|
JSONObject normalized = normalizeWifiOptions(options);
|
|
String ssid = normalized.optString("ssid", "");
|
|
if (TextUtils.isEmpty(ssid)) {
|
|
sendError(callbackContext, "INVALID_WIFI_OPTIONS", "ssid is required.");
|
|
return;
|
|
}
|
|
if (!hasWifiPermissions()) {
|
|
sendError(callbackContext, "PERMISSION_REQUIRED", "Call requestWifiPermission before removing a WiFi network.");
|
|
return;
|
|
}
|
|
|
|
WifiManager wifiManager = wifiManager();
|
|
if (wifiManager == null) {
|
|
sendError(callbackContext, "WIFI_UNAVAILABLE", "WiFi manager is not available.");
|
|
return;
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
try {
|
|
int status = wifiManager.removeNetworkSuggestions(Collections.singletonList(buildSuggestion(normalized)));
|
|
JSONObject result = new JSONObject();
|
|
result.put("ssid", ssid);
|
|
result.put("platformMode", "suggestion");
|
|
result.put("status", status);
|
|
result.put("removed", status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
|
|
callbackContext.success(result);
|
|
} catch (Exception ex) {
|
|
sendError(callbackContext, "WIFI_REMOVE_FAILED", ex.getMessage());
|
|
}
|
|
return;
|
|
}
|
|
|
|
int networkId = findLegacyNetworkId(wifiManager, ssid);
|
|
if (networkId < 0) {
|
|
try {
|
|
JSONObject result = new JSONObject();
|
|
result.put("ssid", ssid);
|
|
result.put("removed", false);
|
|
result.put("reason", "not_found");
|
|
callbackContext.success(result);
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
return;
|
|
}
|
|
|
|
boolean removed = wifiManager.removeNetwork(networkId);
|
|
wifiManager.saveConfiguration();
|
|
try {
|
|
JSONObject result = new JSONObject();
|
|
result.put("ssid", ssid);
|
|
result.put("networkId", networkId);
|
|
result.put("removed", removed);
|
|
callbackContext.success(result);
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
private void disconnectWifi(CallbackContext callbackContext) {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
stopNetworkRequest();
|
|
try {
|
|
JSONObject result = new JSONObject();
|
|
result.put("disconnected", true);
|
|
result.put("platformMode", "specifier");
|
|
callbackContext.success(result);
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
return;
|
|
}
|
|
|
|
WifiManager wifiManager = wifiManager();
|
|
if (wifiManager == null) {
|
|
sendError(callbackContext, "WIFI_UNAVAILABLE", "WiFi manager is not available.");
|
|
return;
|
|
}
|
|
boolean disconnected = wifiManager.disconnect();
|
|
try {
|
|
JSONObject result = new JSONObject();
|
|
result.put("disconnected", disconnected);
|
|
result.put("platformMode", "legacy");
|
|
callbackContext.success(result);
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
private void requestWifiPermission(CallbackContext callbackContext) {
|
|
JSONArray missing = missingWifiPermissions();
|
|
if (missing.length() == 0) {
|
|
try {
|
|
callbackContext.success(permissionResult("wifi", true, missing));
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
return;
|
|
}
|
|
wifiPermissionCallback = callbackContext;
|
|
PermissionHelper.requestPermissions(this, REQ_WIFI_PERMISSION, jsonArrayToStringArray(missing));
|
|
}
|
|
|
|
private void isBiometricAvailable(CallbackContext callbackContext) {
|
|
try {
|
|
callbackContext.success(biometricAvailability());
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
private void unlockWithBiometrics(JSONObject options, CallbackContext callbackContext) {
|
|
try {
|
|
JSONObject availability = biometricAvailability();
|
|
if (!availability.optBoolean("available")) {
|
|
sendError(callbackContext, availability.optString("code", "BIOMETRIC_UNAVAILABLE"), availability.optString("message", "Biometric authentication is unavailable."));
|
|
return;
|
|
}
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
return;
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
|
sendError(callbackContext, "UNSUPPORTED_ANDROID_VERSION", "Biometric unlock requires Android 9.0 or newer in this plugin.");
|
|
return;
|
|
}
|
|
|
|
final JSONObject safeOptions = options == null ? new JSONObject() : options;
|
|
cordova.getActivity().runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
showBiometricPrompt(safeOptions, callbackContext);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void requestPushPermission(CallbackContext callbackContext) {
|
|
if (Build.VERSION.SDK_INT < 33 || PermissionHelper.hasPermission(this, Manifest.permission.POST_NOTIFICATIONS)) {
|
|
try {
|
|
callbackContext.success(permissionResult("notification", true, new JSONArray()));
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
return;
|
|
}
|
|
notificationPermissionCallback = callbackContext;
|
|
PermissionHelper.requestPermission(this, REQ_NOTIFICATION_PERMISSION, Manifest.permission.POST_NOTIFICATIONS);
|
|
}
|
|
|
|
private void getFirebaseToken(final CallbackContext callbackContext) {
|
|
if (!ensureFirebaseInitialized(true)) {
|
|
sendError(callbackContext, "FIREBASE_NOT_CONFIGURED", "Firebase is not configured. Add Google services files or pass Firebase plugin preferences.");
|
|
return;
|
|
}
|
|
|
|
FirebaseMessaging.getInstance().getToken().addOnCompleteListener(new OnCompleteListener<String>() {
|
|
@Override
|
|
public void onComplete(Task<String> task) {
|
|
if (!task.isSuccessful()) {
|
|
Exception exception = task.getException();
|
|
sendError(callbackContext, "FCM_TOKEN_FAILED", exception == null ? "Failed to get Firebase token." : exception.getMessage());
|
|
return;
|
|
}
|
|
String token = task.getResult();
|
|
saveToken(cordova.getActivity(), token);
|
|
try {
|
|
JSONObject result = new JSONObject();
|
|
result.put("token", token);
|
|
callbackContext.success(result);
|
|
|
|
JSONObject event = new JSONObject();
|
|
event.put("type", "token");
|
|
event.put("token", token);
|
|
sendEventToJs(event);
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void subscribeToTopic(String topic, final CallbackContext callbackContext) {
|
|
if (TextUtils.isEmpty(topic)) {
|
|
sendError(callbackContext, "INVALID_TOPIC", "topic is required.");
|
|
return;
|
|
}
|
|
if (!ensureFirebaseInitialized(true)) {
|
|
sendError(callbackContext, "FIREBASE_NOT_CONFIGURED", "Firebase is not configured.");
|
|
return;
|
|
}
|
|
FirebaseMessaging.getInstance().subscribeToTopic(topic).addOnCompleteListener(new OnCompleteListener<Void>() {
|
|
@Override
|
|
public void onComplete(Task<Void> task) {
|
|
completeFirebaseTask(task, callbackContext);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void unsubscribeFromTopic(String topic, final CallbackContext callbackContext) {
|
|
if (TextUtils.isEmpty(topic)) {
|
|
sendError(callbackContext, "INVALID_TOPIC", "topic is required.");
|
|
return;
|
|
}
|
|
if (!ensureFirebaseInitialized(true)) {
|
|
sendError(callbackContext, "FIREBASE_NOT_CONFIGURED", "Firebase is not configured.");
|
|
return;
|
|
}
|
|
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic).addOnCompleteListener(new OnCompleteListener<Void>() {
|
|
@Override
|
|
public void onComplete(Task<Void> task) {
|
|
completeFirebaseTask(task, callbackContext);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void getInitialNotification(CallbackContext callbackContext) {
|
|
SharedPreferences preferences = prefs(cordova.getActivity());
|
|
String value = preferences.getString(KEY_INITIAL_NOTIFICATION, null);
|
|
preferences.edit().remove(KEY_INITIAL_NOTIFICATION).apply();
|
|
if (TextUtils.isEmpty(value)) {
|
|
callbackContext.success((JSONObject) null);
|
|
return;
|
|
}
|
|
try {
|
|
callbackContext.success(new JSONObject(value));
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
private void startNotificationListener(CallbackContext callbackContext) {
|
|
notificationCallback = callbackContext;
|
|
PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
|
|
result.setKeepCallback(true);
|
|
callbackContext.sendPluginResult(result);
|
|
|
|
String token = prefs(cordova.getActivity()).getString(KEY_FCM_TOKEN, null);
|
|
if (!TextUtils.isEmpty(token)) {
|
|
try {
|
|
JSONObject event = new JSONObject();
|
|
event.put("type", "token");
|
|
event.put("token", token);
|
|
sendEventToJs(event);
|
|
} catch (JSONException ignored) {
|
|
Log.w(TAG, "Failed to dispatch cached FCM token.");
|
|
}
|
|
}
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.Q)
|
|
private void saveWifiSuggestion(JSONObject options, CallbackContext callbackContext) {
|
|
WifiManager wifiManager = wifiManager();
|
|
if (wifiManager == null) {
|
|
sendError(callbackContext, "WIFI_UNAVAILABLE", "WiFi manager is not available.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
WifiNetworkSuggestion suggestion = buildSuggestion(options);
|
|
int status = wifiManager.addNetworkSuggestions(Collections.singletonList(suggestion));
|
|
if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
|
|
&& status != WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE) {
|
|
sendError(callbackContext, "WIFI_SAVE_FAILED", "addNetworkSuggestions returned status " + status + ".");
|
|
return;
|
|
}
|
|
|
|
JSONObject result = new JSONObject();
|
|
result.put("ssid", options.optString("ssid"));
|
|
result.put("platformMode", "suggestion");
|
|
result.put("saved", true);
|
|
result.put("status", status);
|
|
result.put("requiresUserApproval", true);
|
|
callbackContext.success(result);
|
|
} catch (Exception ex) {
|
|
sendError(callbackContext, "WIFI_SAVE_FAILED", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
private void saveLegacyWifi(JSONObject options, CallbackContext callbackContext) {
|
|
WifiManager wifiManager = wifiManager();
|
|
if (wifiManager == null) {
|
|
sendError(callbackContext, "WIFI_UNAVAILABLE", "WiFi manager is not available.");
|
|
return;
|
|
}
|
|
|
|
int networkId = addOrUpdateLegacyNetwork(wifiManager, options);
|
|
if (networkId < 0) {
|
|
sendError(callbackContext, "WIFI_SAVE_FAILED", "Unable to add WiFi network configuration.");
|
|
return;
|
|
}
|
|
wifiManager.saveConfiguration();
|
|
|
|
try {
|
|
JSONObject result = new JSONObject();
|
|
result.put("ssid", options.optString("ssid"));
|
|
result.put("platformMode", "legacy");
|
|
result.put("networkId", networkId);
|
|
result.put("saved", true);
|
|
callbackContext.success(result);
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.Q)
|
|
private void connectWithSpecifier(final JSONObject options, final CallbackContext callbackContext) {
|
|
final ConnectivityManager connectivityManager = connectivityManager();
|
|
if (connectivityManager == null) {
|
|
sendError(callbackContext, "NETWORK_UNAVAILABLE", "Connectivity manager is not available.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
stopNetworkRequest();
|
|
WifiNetworkSpecifier specifier = buildSpecifier(options);
|
|
NetworkRequest request = new NetworkRequest.Builder()
|
|
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
|
.setNetworkSpecifier(specifier)
|
|
.build();
|
|
|
|
networkCallback = new ConnectivityManager.NetworkCallback() {
|
|
@Override
|
|
public void onAvailable(Network network) {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
connectivityManager.bindProcessToNetwork(network);
|
|
} else {
|
|
ConnectivityManager.setProcessDefaultNetwork(network);
|
|
}
|
|
try {
|
|
JSONObject result = new JSONObject();
|
|
result.put("ssid", options.optString("ssid"));
|
|
result.put("platformMode", "specifier");
|
|
result.put("connected", true);
|
|
result.put("temporaryConnection", true);
|
|
callbackContext.success(result);
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onUnavailable() {
|
|
sendError(callbackContext, "WIFI_CONNECT_FAILED", "Requested WiFi network is unavailable or the user rejected the connection.");
|
|
}
|
|
|
|
@Override
|
|
public void onLost(Network network) {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
connectivityManager.bindProcessToNetwork(null);
|
|
} else {
|
|
ConnectivityManager.setProcessDefaultNetwork(null);
|
|
}
|
|
}
|
|
};
|
|
|
|
connectivityManager.requestNetwork(request, networkCallback);
|
|
} catch (Exception ex) {
|
|
sendError(callbackContext, "WIFI_CONNECT_FAILED", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
private void connectLegacyWifi(JSONObject options, CallbackContext callbackContext) {
|
|
WifiManager wifiManager = wifiManager();
|
|
if (wifiManager == null) {
|
|
sendError(callbackContext, "WIFI_UNAVAILABLE", "WiFi manager is not available.");
|
|
return;
|
|
}
|
|
|
|
int networkId = addOrUpdateLegacyNetwork(wifiManager, options);
|
|
if (networkId < 0) {
|
|
sendError(callbackContext, "WIFI_CONNECT_FAILED", "Unable to add WiFi network configuration.");
|
|
return;
|
|
}
|
|
|
|
boolean enabled = wifiManager.enableNetwork(networkId, true);
|
|
boolean reconnecting = wifiManager.reconnect();
|
|
|
|
try {
|
|
JSONObject result = new JSONObject();
|
|
result.put("ssid", options.optString("ssid"));
|
|
result.put("platformMode", "legacy");
|
|
result.put("networkId", networkId);
|
|
result.put("enabled", enabled);
|
|
result.put("reconnecting", reconnecting);
|
|
callbackContext.success(result);
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
private void stopNetworkRequest() {
|
|
ConnectivityManager connectivityManager = connectivityManager();
|
|
if (connectivityManager != null && networkCallback != null) {
|
|
try {
|
|
connectivityManager.unregisterNetworkCallback(networkCallback);
|
|
} catch (RuntimeException ignored) {
|
|
Log.d(TAG, "Network callback was already unregistered.");
|
|
}
|
|
}
|
|
if (connectivityManager != null) {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
connectivityManager.bindProcessToNetwork(null);
|
|
} else {
|
|
ConnectivityManager.setProcessDefaultNetwork(null);
|
|
}
|
|
}
|
|
networkCallback = null;
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.Q)
|
|
private WifiNetworkSuggestion buildSuggestion(JSONObject options) {
|
|
WifiNetworkSuggestion.Builder builder = new WifiNetworkSuggestion.Builder()
|
|
.setSsid(options.optString("ssid"));
|
|
if (options.optBoolean("hiddenSsid", false)) {
|
|
builder.setIsHiddenSsid(true);
|
|
}
|
|
applySuggestionSecurity(builder, options);
|
|
return builder.build();
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.Q)
|
|
private WifiNetworkSpecifier buildSpecifier(JSONObject options) {
|
|
WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder()
|
|
.setSsid(options.optString("ssid"));
|
|
if (options.optBoolean("hiddenSsid", false)) {
|
|
builder.setIsHiddenSsid(true);
|
|
}
|
|
String bssid = options.optString("bssid", "");
|
|
if (!TextUtils.isEmpty(bssid)) {
|
|
builder.setBssid(android.net.MacAddress.fromString(bssid));
|
|
}
|
|
applySpecifierSecurity(builder, options);
|
|
return builder.build();
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.Q)
|
|
private void applySuggestionSecurity(WifiNetworkSuggestion.Builder builder, JSONObject options) {
|
|
String security = options.optString("security", "").toLowerCase(Locale.US);
|
|
String password = options.optString("password", "");
|
|
if (TextUtils.isEmpty(password) || "open".equals(security)) {
|
|
return;
|
|
}
|
|
if ("wpa3".equals(security) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
builder.setWpa3Passphrase(password);
|
|
return;
|
|
}
|
|
builder.setWpa2Passphrase(password);
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.Q)
|
|
private void applySpecifierSecurity(WifiNetworkSpecifier.Builder builder, JSONObject options) {
|
|
String security = options.optString("security", "").toLowerCase(Locale.US);
|
|
String password = options.optString("password", "");
|
|
if (TextUtils.isEmpty(password) || "open".equals(security)) {
|
|
return;
|
|
}
|
|
if ("wpa3".equals(security) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
builder.setWpa3Passphrase(password);
|
|
return;
|
|
}
|
|
builder.setWpa2Passphrase(password);
|
|
}
|
|
|
|
private int addOrUpdateLegacyNetwork(WifiManager wifiManager, JSONObject options) {
|
|
String ssid = options.optString("ssid");
|
|
int existingNetworkId = findLegacyNetworkId(wifiManager, ssid);
|
|
WifiConfiguration configuration = buildLegacyConfiguration(options);
|
|
if (existingNetworkId >= 0) {
|
|
configuration.networkId = existingNetworkId;
|
|
int updated = wifiManager.updateNetwork(configuration);
|
|
return updated >= 0 ? updated : existingNetworkId;
|
|
}
|
|
return wifiManager.addNetwork(configuration);
|
|
}
|
|
|
|
private WifiConfiguration buildLegacyConfiguration(JSONObject options) {
|
|
String security = options.optString("security", "").toLowerCase(Locale.US);
|
|
String password = options.optString("password", "");
|
|
|
|
WifiConfiguration configuration = new WifiConfiguration();
|
|
configuration.SSID = quote(options.optString("ssid"));
|
|
configuration.hiddenSSID = options.optBoolean("hiddenSsid", false);
|
|
configuration.status = WifiConfiguration.Status.ENABLED;
|
|
|
|
if (TextUtils.isEmpty(password) || "open".equals(security)) {
|
|
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
|
|
return configuration;
|
|
}
|
|
|
|
if ("wep".equals(security)) {
|
|
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
|
|
configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
|
|
configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
|
|
configuration.wepKeys[0] = quote(password);
|
|
configuration.wepTxKeyIndex = 0;
|
|
return configuration;
|
|
}
|
|
|
|
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
|
|
configuration.preSharedKey = quote(password);
|
|
return configuration;
|
|
}
|
|
|
|
private int findLegacyNetworkId(WifiManager wifiManager, String ssid) {
|
|
try {
|
|
List<WifiConfiguration> networks = wifiManager.getConfiguredNetworks();
|
|
if (networks == null) {
|
|
return -1;
|
|
}
|
|
String quotedSsid = quote(ssid);
|
|
for (WifiConfiguration network : networks) {
|
|
if (quotedSsid.equals(network.SSID)) {
|
|
return network.networkId;
|
|
}
|
|
}
|
|
} catch (SecurityException ex) {
|
|
Log.w(TAG, "Missing permission to read configured networks.", ex);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private JSONObject normalizeWifiOptions(JSONObject options) {
|
|
JSONObject normalized = options == null ? new JSONObject() : options;
|
|
try {
|
|
if (!normalized.has("security")) {
|
|
normalized.put("security", TextUtils.isEmpty(normalized.optString("password", "")) ? "open" : "wpa2");
|
|
}
|
|
} catch (JSONException ignored) {
|
|
Log.w(TAG, "Unable to normalize WiFi options.");
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
private String validateWifiOptions(JSONObject options) {
|
|
if (TextUtils.isEmpty(options.optString("ssid", ""))) {
|
|
return "ssid is required.";
|
|
}
|
|
String security = options.optString("security", "open").toLowerCase(Locale.US);
|
|
if (!"open".equals(security) && TextUtils.isEmpty(options.optString("password", ""))) {
|
|
return "password is required for secured WiFi networks.";
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private boolean hasWifiPermissions() {
|
|
return missingWifiPermissions().length() == 0;
|
|
}
|
|
|
|
private JSONArray missingWifiPermissions() {
|
|
JSONArray permissions = new JSONArray();
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
|
|
&& !PermissionHelper.hasPermission(this, Manifest.permission.NEARBY_WIFI_DEVICES)) {
|
|
permissions.put(Manifest.permission.NEARBY_WIFI_DEVICES);
|
|
}
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
|
&& !PermissionHelper.hasPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
|
|
permissions.put(Manifest.permission.ACCESS_FINE_LOCATION);
|
|
}
|
|
return permissions;
|
|
}
|
|
|
|
private JSONObject biometricAvailability() throws JSONException {
|
|
JSONObject result = new JSONObject();
|
|
result.put("available", false);
|
|
result.put("biometryType", "none");
|
|
|
|
Context context = cordova.getActivity();
|
|
KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
|
|
if (keyguardManager == null || !keyguardManager.isKeyguardSecure()) {
|
|
result.put("code", "NOT_ENROLLED");
|
|
result.put("message", "No secure lock screen or biometric credential is enrolled.");
|
|
return result;
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
BiometricManager biometricManager = (BiometricManager) context.getSystemService(BiometricManager.class);
|
|
int code = biometricManager == null
|
|
? BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
|
|
: biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK);
|
|
result.put("platformCode", code);
|
|
if (code == BiometricManager.BIOMETRIC_SUCCESS) {
|
|
result.put("available", true);
|
|
result.put("biometryType", detectAndroidBiometryType(context));
|
|
result.put("code", "OK");
|
|
result.put("message", "Biometric authentication is available.");
|
|
} else {
|
|
result.put("code", "BIOMETRIC_UNAVAILABLE");
|
|
result.put("message", "Biometric authentication is unavailable. Android code: " + code);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
BiometricManager biometricManager = (BiometricManager) context.getSystemService(BiometricManager.class);
|
|
int code = biometricManager == null
|
|
? BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
|
|
: biometricManager.canAuthenticate();
|
|
result.put("platformCode", code);
|
|
if (code == BiometricManager.BIOMETRIC_SUCCESS) {
|
|
result.put("available", true);
|
|
result.put("biometryType", detectAndroidBiometryType(context));
|
|
result.put("code", "OK");
|
|
result.put("message", "Biometric authentication is available.");
|
|
} else {
|
|
result.put("code", "BIOMETRIC_UNAVAILABLE");
|
|
result.put("message", "Biometric authentication is unavailable. Android code: " + code);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
if (!hasBiometricFeature(context)) {
|
|
result.put("code", "BIOMETRIC_UNAVAILABLE");
|
|
result.put("message", "No biometric hardware is available.");
|
|
return result;
|
|
}
|
|
result.put("available", true);
|
|
result.put("biometryType", detectAndroidBiometryType(context));
|
|
result.put("code", "OK");
|
|
result.put("message", "Biometric authentication is available.");
|
|
return result;
|
|
}
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
|
|
if (fingerprintManager != null && fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) {
|
|
result.put("available", true);
|
|
result.put("biometryType", "fingerprint");
|
|
result.put("code", "OK");
|
|
result.put("message", "Fingerprint authentication is available.");
|
|
} else {
|
|
result.put("code", "BIOMETRIC_UNAVAILABLE");
|
|
result.put("message", "Fingerprint authentication is unavailable.");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
result.put("code", "UNSUPPORTED_ANDROID_VERSION");
|
|
result.put("message", "Biometric authentication is not supported on this Android version.");
|
|
return result;
|
|
}
|
|
|
|
private boolean hasBiometricFeature(Context context) {
|
|
PackageManager packageManager = context.getPackageManager();
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
|
&& (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)
|
|
|| packageManager.hasSystemFeature(PackageManager.FEATURE_IRIS))) {
|
|
return true;
|
|
}
|
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
|
&& packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
|
|
}
|
|
|
|
@TargetApi(Build.VERSION_CODES.P)
|
|
private void showBiometricPrompt(JSONObject options, final CallbackContext callbackContext) {
|
|
Activity activity = cordova.getActivity();
|
|
Executor executor = activity.getMainExecutor();
|
|
String title = options.optString("title", "Unlock");
|
|
String subtitle = options.optString("subtitle", "");
|
|
String description = options.optString("description", "");
|
|
String negativeButton = options.optString("cancelText", "Cancel");
|
|
boolean allowDeviceCredential = options.optBoolean("allowDeviceCredential", true);
|
|
|
|
BiometricPrompt.Builder builder = new BiometricPrompt.Builder(activity).setTitle(title);
|
|
if (!TextUtils.isEmpty(subtitle)) {
|
|
builder.setSubtitle(subtitle);
|
|
}
|
|
if (!TextUtils.isEmpty(description)) {
|
|
builder.setDescription(description);
|
|
}
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && allowDeviceCredential) {
|
|
builder.setDeviceCredentialAllowed(true);
|
|
} else {
|
|
builder.setNegativeButton(negativeButton, executor, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
sendError(callbackContext, "AUTHENTICATION_CANCELLED", "User cancelled biometric authentication.");
|
|
}
|
|
});
|
|
}
|
|
|
|
BiometricPrompt prompt = builder.build();
|
|
CancellationSignal cancellationSignal = new CancellationSignal();
|
|
prompt.authenticate(cancellationSignal, executor, new BiometricPrompt.AuthenticationCallback() {
|
|
@Override
|
|
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult authenticationResult) {
|
|
try {
|
|
JSONObject result = new JSONObject();
|
|
result.put("unlocked", true);
|
|
result.put("biometryType", detectAndroidBiometryType(cordova.getActivity()));
|
|
callbackContext.success(result);
|
|
|
|
JSONObject event = new JSONObject(result.toString());
|
|
event.put("type", "unlock");
|
|
sendEventToJs(event);
|
|
} catch (JSONException ex) {
|
|
sendError(callbackContext, "JSON_ERROR", ex.getMessage());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
|
sendError(callbackContext, "AUTHENTICATION_ERROR", String.valueOf(errString));
|
|
}
|
|
|
|
@Override
|
|
public void onAuthenticationFailed() {
|
|
Log.d(TAG, "Biometric authentication attempt failed.");
|
|
}
|
|
});
|
|
}
|
|
|
|
private String detectAndroidBiometryType(Context context) {
|
|
PackageManager packageManager = context.getPackageManager();
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
|
|
return "face";
|
|
}
|
|
if (packageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)) {
|
|
return "iris";
|
|
}
|
|
}
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
|
&& packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
|
|
return "fingerprint";
|
|
}
|
|
return "biometric";
|
|
}
|
|
|
|
private void completeFirebaseTask(Task<?> task, CallbackContext callbackContext) {
|
|
if (!task.isSuccessful()) {
|
|
Exception exception = task.getException();
|
|
sendError(callbackContext, "FIREBASE_TASK_FAILED", exception == null ? "Firebase task failed." : exception.getMessage());
|
|
return;
|
|
}
|
|
callbackContext.success();
|
|
}
|
|
|
|
private boolean ensureFirebaseInitialized(boolean logMissingConfig) {
|
|
Context context = cordova.getActivity().getApplicationContext();
|
|
if (!FirebaseApp.getApps(context).isEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
|
|
Bundle metaData = appInfo.metaData;
|
|
String appId = metadataString(metaData, "bluememe.firebase.app_id");
|
|
String apiKey = metadataString(metaData, "bluememe.firebase.api_key");
|
|
String projectId = metadataString(metaData, "bluememe.firebase.project_id");
|
|
String senderId = metadataString(metaData, "bluememe.firebase.sender_id");
|
|
String storageBucket = metadataString(metaData, "bluememe.firebase.storage_bucket");
|
|
|
|
if (TextUtils.isEmpty(appId) || TextUtils.isEmpty(apiKey) || TextUtils.isEmpty(senderId)) {
|
|
if (logMissingConfig) {
|
|
Log.w(TAG, "Firebase options are missing.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FirebaseOptions.Builder builder = new FirebaseOptions.Builder()
|
|
.setApplicationId(appId)
|
|
.setApiKey(apiKey)
|
|
.setGcmSenderId(senderId);
|
|
if (!TextUtils.isEmpty(projectId)) {
|
|
builder.setProjectId(projectId);
|
|
}
|
|
if (!TextUtils.isEmpty(storageBucket)) {
|
|
builder.setStorageBucket(storageBucket);
|
|
}
|
|
FirebaseApp.initializeApp(context, builder.build());
|
|
return true;
|
|
} catch (Exception ex) {
|
|
Log.w(TAG, "Unable to initialize Firebase.", ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void registerEventReceiver() {
|
|
if (eventReceiver != null) {
|
|
return;
|
|
}
|
|
final Context context = cordova.getActivity().getApplicationContext();
|
|
eventReceiver = new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context receiverContext, Intent intent) {
|
|
String eventJson = intent.getStringExtra(EXTRA_EVENT);
|
|
if (TextUtils.isEmpty(eventJson)) {
|
|
return;
|
|
}
|
|
try {
|
|
sendEventToJs(new JSONObject(eventJson));
|
|
} catch (JSONException ex) {
|
|
Log.w(TAG, "Invalid event payload.", ex);
|
|
}
|
|
}
|
|
};
|
|
IntentFilter filter = new IntentFilter(eventAction(context));
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
context.registerReceiver(eventReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
|
|
} else {
|
|
context.registerReceiver(eventReceiver, filter);
|
|
}
|
|
}
|
|
|
|
private void unregisterEventReceiver() {
|
|
if (eventReceiver == null) {
|
|
return;
|
|
}
|
|
try {
|
|
cordova.getActivity().getApplicationContext().unregisterReceiver(eventReceiver);
|
|
} catch (RuntimeException ignored) {
|
|
Log.d(TAG, "Event receiver was already unregistered.");
|
|
}
|
|
eventReceiver = null;
|
|
}
|
|
|
|
private void captureLaunchIntent(Intent intent) {
|
|
JSONObject notification = notificationFromIntent(intent);
|
|
if (notification == null) {
|
|
return;
|
|
}
|
|
saveInitialNotification(cordova.getActivity(), notification);
|
|
try {
|
|
JSONObject event = new JSONObject();
|
|
event.put("type", "notificationOpened");
|
|
event.put("notification", notification);
|
|
sendEventToJs(event);
|
|
} catch (JSONException ex) {
|
|
Log.w(TAG, "Unable to dispatch launch notification.", ex);
|
|
}
|
|
}
|
|
|
|
private JSONObject notificationFromIntent(Intent intent) {
|
|
if (intent == null || intent.getExtras() == null) {
|
|
return null;
|
|
}
|
|
Bundle extras = intent.getExtras();
|
|
String payload = extras.getString(EXTRA_PAYLOAD);
|
|
if (!TextUtils.isEmpty(payload)) {
|
|
try {
|
|
return new JSONObject(payload);
|
|
} catch (JSONException ex) {
|
|
Log.w(TAG, "Invalid notification payload extra.", ex);
|
|
}
|
|
}
|
|
|
|
boolean looksLikeFcm = extras.containsKey("google.message_id") || extras.containsKey("gcm.message_id");
|
|
if (!looksLikeFcm) {
|
|
return null;
|
|
}
|
|
return bundleToJson(extras);
|
|
}
|
|
|
|
private void sendEventToJs(JSONObject event) {
|
|
if (notificationCallback == null) {
|
|
return;
|
|
}
|
|
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, event);
|
|
pluginResult.setKeepCallback(true);
|
|
notificationCallback.sendPluginResult(pluginResult);
|
|
}
|
|
|
|
private WifiManager wifiManager() {
|
|
return (WifiManager) cordova.getActivity().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
|
}
|
|
|
|
private ConnectivityManager connectivityManager() {
|
|
return (ConnectivityManager) cordova.getActivity().getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
}
|
|
|
|
private static JSONObject permissionResult(String type, boolean granted, JSONArray missing) throws JSONException {
|
|
JSONObject result = new JSONObject();
|
|
result.put("type", type);
|
|
result.put("granted", granted);
|
|
result.put("missing", missing);
|
|
return result;
|
|
}
|
|
|
|
private boolean allGranted(int[] grantResults) {
|
|
if (grantResults == null || grantResults.length == 0) {
|
|
return false;
|
|
}
|
|
for (int grantResult : grantResults) {
|
|
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static String[] jsonArrayToStringArray(JSONArray array) {
|
|
List<String> values = new ArrayList<>();
|
|
for (int i = 0; i < array.length(); i++) {
|
|
String value = array.optString(i, null);
|
|
if (!TextUtils.isEmpty(value)) {
|
|
values.add(value);
|
|
}
|
|
}
|
|
return values.toArray(new String[0]);
|
|
}
|
|
|
|
private static String quote(String value) {
|
|
if (value == null) {
|
|
return "\"\"";
|
|
}
|
|
if (value.startsWith("\"") && value.endsWith("\"")) {
|
|
return value;
|
|
}
|
|
return "\"" + value + "\"";
|
|
}
|
|
|
|
private static String metadataString(Bundle bundle, String key) {
|
|
if (bundle == null || !bundle.containsKey(key)) {
|
|
return "";
|
|
}
|
|
Object value = bundle.get(key);
|
|
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;
|
|
}
|
|
|
|
static void publishEvent(Context context, JSONObject event) {
|
|
Intent intent = new Intent(eventAction(context));
|
|
intent.setPackage(context.getPackageName());
|
|
intent.putExtra(EXTRA_EVENT, event.toString());
|
|
context.sendBroadcast(intent);
|
|
}
|
|
|
|
static void saveToken(Context context, String token) {
|
|
if (TextUtils.isEmpty(token)) {
|
|
return;
|
|
}
|
|
prefs(context).edit().putString(KEY_FCM_TOKEN, token).apply();
|
|
}
|
|
|
|
static void saveInitialNotification(Context context, JSONObject notification) {
|
|
if (notification == null) {
|
|
return;
|
|
}
|
|
prefs(context).edit().putString(KEY_INITIAL_NOTIFICATION, notification.toString()).apply();
|
|
}
|
|
|
|
static SharedPreferences prefs(Context context) {
|
|
return context.getApplicationContext().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
|
}
|
|
|
|
static JSONObject bundleToJson(Bundle bundle) {
|
|
JSONObject json = new JSONObject();
|
|
if (bundle == null) {
|
|
return json;
|
|
}
|
|
for (String key : bundle.keySet()) {
|
|
try {
|
|
Object value = bundle.get(key);
|
|
if (value instanceof Bundle) {
|
|
json.put(key, bundleToJson((Bundle) value));
|
|
} else {
|
|
json.put(key, value == null ? JSONObject.NULL : String.valueOf(value));
|
|
}
|
|
} catch (JSONException ex) {
|
|
Log.w(TAG, "Unable to add bundle value for key " + key + ".", ex);
|
|
}
|
|
}
|
|
return json;
|
|
}
|
|
|
|
private void sendError(CallbackContext callbackContext, String code, String message) {
|
|
try {
|
|
JSONObject error = new JSONObject();
|
|
error.put("code", code);
|
|
error.put("message", message == null ? "" : message);
|
|
callbackContext.error(error);
|
|
} catch (JSONException ex) {
|
|
callbackContext.error(code + ": " + message);
|
|
}
|
|
}
|
|
}
|