first commit
This commit is contained in:
commit
4140922530
10 files changed
+2362
No files matched your search
@@ -0,0 +1,196 @@
|
||||
package com.bluememe.utils;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BluememeMessagingService extends FirebaseMessagingService {
|
||||
private static final String TAG = "BluememeMessaging";
|
||||
private static final String CHANNEL_ID = "bluememe_utils_notifications";
|
||||
private static final String EXTRA_PAYLOAD = "bluememe_payload";
|
||||
|
||||
@Override
|
||||
public void onNewToken(String token) {
|
||||
super.onNewToken(token);
|
||||
BluememeUtils.saveToken(this, token);
|
||||
try {
|
||||
JSONObject event = new JSONObject();
|
||||
event.put("type", "token");
|
||||
event.put("token", token);
|
||||
BluememeUtils.publishEvent(this, event);
|
||||
} catch (JSONException ex) {
|
||||
Log.w(TAG, "Unable to publish token event.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(RemoteMessage remoteMessage) {
|
||||
super.onMessageReceived(remoteMessage);
|
||||
JSONObject notification = remoteMessageToJson(remoteMessage);
|
||||
try {
|
||||
JSONObject event = new JSONObject();
|
||||
event.put("type", "notification");
|
||||
event.put("notification", notification);
|
||||
BluememeUtils.publishEvent(this, event);
|
||||
} catch (JSONException ex) {
|
||||
Log.w(TAG, "Unable to publish notification event.", ex);
|
||||
}
|
||||
|
||||
if (!isAppInForeground() && shouldShowNotification(notification)) {
|
||||
showNotification(notification);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject remoteMessageToJson(RemoteMessage message) {
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
json.put("messageId", message.getMessageId());
|
||||
json.put("from", message.getFrom());
|
||||
json.put("collapseKey", message.getCollapseKey());
|
||||
json.put("sentTime", message.getSentTime());
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
for (Map.Entry<String, String> entry : message.getData().entrySet()) {
|
||||
data.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
json.put("data", data);
|
||||
|
||||
RemoteMessage.Notification remoteNotification = message.getNotification();
|
||||
if (remoteNotification != null) {
|
||||
JSONObject notification = new JSONObject();
|
||||
notification.put("title", remoteNotification.getTitle());
|
||||
notification.put("body", remoteNotification.getBody());
|
||||
notification.put("tag", remoteNotification.getTag());
|
||||
notification.put("clickAction", remoteNotification.getClickAction());
|
||||
notification.put("channelId", remoteNotification.getChannelId());
|
||||
if (remoteNotification.getImageUrl() != null) {
|
||||
notification.put("imageUrl", remoteNotification.getImageUrl().toString());
|
||||
}
|
||||
json.put("notification", notification);
|
||||
}
|
||||
} catch (JSONException ex) {
|
||||
Log.w(TAG, "Unable to convert RemoteMessage to JSON.", ex);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
private boolean shouldShowNotification(JSONObject payload) {
|
||||
JSONObject notification = payload.optJSONObject("notification");
|
||||
if (notification != null) {
|
||||
return !TextUtils.isEmpty(notification.optString("title", ""))
|
||||
|| !TextUtils.isEmpty(notification.optString("body", ""));
|
||||
}
|
||||
JSONObject data = payload.optJSONObject("data");
|
||||
return data != null
|
||||
&& (!TextUtils.isEmpty(data.optString("title", ""))
|
||||
|| !TextUtils.isEmpty(data.optString("body", "")));
|
||||
}
|
||||
|
||||
private void showNotification(JSONObject payload) {
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notificationManager == null) {
|
||||
return;
|
||||
}
|
||||
createNotificationChannel(notificationManager);
|
||||
|
||||
String title = valueFromPayload(payload, "title", appLabel());
|
||||
String body = valueFromPayload(payload, "body", "");
|
||||
Intent launchIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
|
||||
if (launchIntent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
launchIntent.putExtra(EXTRA_PAYLOAD, payload.toString());
|
||||
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
flags |= PendingIntent.FLAG_IMMUTABLE;
|
||||
}
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, payload.toString().hashCode(), launchIntent, flags);
|
||||
|
||||
int icon = getApplicationInfo().icon == 0 ? android.R.drawable.ic_dialog_info : getApplicationInfo().icon;
|
||||
Notification.Builder builder = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
? new Notification.Builder(this, CHANNEL_ID)
|
||||
: new Notification.Builder(this);
|
||||
|
||||
builder.setSmallIcon(icon)
|
||||
.setContentTitle(title)
|
||||
.setContentText(body)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setShowWhen(true);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
builder.setStyle(new Notification.BigTextStyle().bigText(body));
|
||||
}
|
||||
|
||||
notificationManager.notify(payload.toString().hashCode(), builder.build());
|
||||
}
|
||||
|
||||
private String valueFromPayload(JSONObject payload, String key, String defaultValue) {
|
||||
JSONObject notification = payload.optJSONObject("notification");
|
||||
if (notification != null && !TextUtils.isEmpty(notification.optString(key, ""))) {
|
||||
return notification.optString(key);
|
||||
}
|
||||
JSONObject data = payload.optJSONObject("data");
|
||||
if (data != null && !TextUtils.isEmpty(data.optString(key, ""))) {
|
||||
return data.optString(key);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private String appLabel() {
|
||||
ApplicationInfo applicationInfo = getApplicationInfo();
|
||||
CharSequence label = getPackageManager().getApplicationLabel(applicationInfo);
|
||||
return label == null ? getPackageName() : label.toString();
|
||||
}
|
||||
|
||||
private void createNotificationChannel(NotificationManager notificationManager) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return;
|
||||
}
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Application notifications",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
private boolean isAppInForeground() {
|
||||
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (activityManager == null) {
|
||||
return false;
|
||||
}
|
||||
List<ActivityManager.RunningAppProcessInfo> processes = activityManager.getRunningAppProcesses();
|
||||
if (processes == null) {
|
||||
return false;
|
||||
}
|
||||
String packageName = getPackageName();
|
||||
for (ActivityManager.RunningAppProcessInfo process : processes) {
|
||||
if (process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
||||
&& packageName.equals(process.processName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,6 @@
|
||||
#import <Cordova/CDV.h>
|
||||
#import <FirebaseMessaging/FirebaseMessaging.h>
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
|
||||
@interface CDVBluememeUtils : CDVPlugin <FIRMessagingDelegate, UNUserNotificationCenterDelegate>
|
||||
@end
|
||||
@@ -0,0 +1,490 @@
|
||||
#import "CDVBluememeUtils.h"
|
||||
|
||||
#import <FirebaseCore/FirebaseCore.h>
|
||||
#import <LocalAuthentication/LocalAuthentication.h>
|
||||
#import <NetworkExtension/NetworkExtension.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface CDVBluememeUtils ()
|
||||
@property (nonatomic, copy) NSString *notificationCallbackId;
|
||||
@property (nonatomic, strong) NSDictionary *initialNotification;
|
||||
@property (nonatomic, weak) id<UNUserNotificationCenterDelegate> previousNotificationDelegate;
|
||||
@end
|
||||
|
||||
@implementation CDVBluememeUtils
|
||||
|
||||
- (void)pluginInitialize
|
||||
{
|
||||
if ([self ensureFirebaseConfigured:nil]) {
|
||||
[FIRMessaging messaging].delegate = self;
|
||||
}
|
||||
|
||||
if (@available(iOS 10.0, *)) {
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
self.previousNotificationDelegate = center.delegate;
|
||||
center.delegate = self;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)saveWifi:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
[self applyWifiCommand:command defaultJoinOnce:NO];
|
||||
}
|
||||
|
||||
- (void)connectWifi:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
[self applyWifiCommand:command defaultJoinOnce:YES];
|
||||
}
|
||||
|
||||
- (void)removeWifi:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
NSDictionary *options = [self firstDictionaryArgument:command];
|
||||
NSString *ssid = [self stringFromDictionary:options key:@"ssid"];
|
||||
if (ssid.length == 0) {
|
||||
[self sendErrorForCommand:command code:@"INVALID_WIFI_OPTIONS" message:@"ssid is required."];
|
||||
return;
|
||||
}
|
||||
|
||||
if (@available(iOS 11.0, *)) {
|
||||
[[NEHotspotConfigurationManager sharedManager] removeConfigurationForSSID:ssid];
|
||||
[self sendSuccessForCommand:command dictionary:@{@"ssid": ssid, @"removed": @YES}];
|
||||
} else {
|
||||
[self sendErrorForCommand:command code:@"UNSUPPORTED_IOS_VERSION" message:@"WiFi configuration requires iOS 11 or newer."];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)disconnectWifi:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
[self sendErrorForCommand:command code:@"UNSUPPORTED_PLATFORM_ACTION" message:@"iOS does not expose a public API for disconnecting WiFi from third-party apps."];
|
||||
}
|
||||
|
||||
- (void)requestWifiPermission:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
[self sendSuccessForCommand:command dictionary:@{@"type": @"wifi", @"granted": @YES, @"missing": @[]}];
|
||||
}
|
||||
|
||||
- (void)isBiometricAvailable:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
[self sendSuccessForCommand:command dictionary:[self biometricAvailability]];
|
||||
}
|
||||
|
||||
- (void)unlockWithBiometrics:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
NSDictionary *options = [self firstDictionaryArgument:command];
|
||||
LAContext *context = [[LAContext alloc] init];
|
||||
NSString *fallbackTitle = [self stringFromDictionary:options key:@"fallbackTitle"];
|
||||
NSString *cancelTitle = [self stringFromDictionary:options key:@"cancelTitle"];
|
||||
if (fallbackTitle.length > 0) {
|
||||
context.localizedFallbackTitle = fallbackTitle;
|
||||
}
|
||||
if (cancelTitle.length > 0) {
|
||||
context.localizedCancelTitle = cancelTitle;
|
||||
}
|
||||
|
||||
BOOL allowPasscode = [self boolFromDictionary:options key:@"allowDeviceCredential" defaultValue:YES];
|
||||
LAPolicy policy = allowPasscode ? LAPolicyDeviceOwnerAuthentication : LAPolicyDeviceOwnerAuthenticationWithBiometrics;
|
||||
NSError *availabilityError = nil;
|
||||
if (![context canEvaluatePolicy:policy error:&availabilityError]) {
|
||||
[self sendErrorForCommand:command code:@"BIOMETRIC_UNAVAILABLE" message:(availabilityError.localizedDescription ? availabilityError.localizedDescription : @"Biometric authentication is unavailable.")];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *reason = [self stringFromDictionary:options key:@"reason"];
|
||||
if (reason.length == 0) {
|
||||
reason = @"Unlock";
|
||||
}
|
||||
|
||||
[context evaluatePolicy:policy localizedReason:reason reply:^(BOOL success, NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (success) {
|
||||
NSDictionary *result = @{@"unlocked": @YES, @"biometryType": [self biometryTypeForContext:context]};
|
||||
[self sendSuccessForCommand:command dictionary:result];
|
||||
NSMutableDictionary *event = [result mutableCopy];
|
||||
event[@"type"] = @"unlock";
|
||||
[self sendEvent:event];
|
||||
} else {
|
||||
[self sendErrorForCommand:command code:@"AUTHENTICATION_ERROR" message:(error.localizedDescription ? error.localizedDescription : @"Authentication failed.")];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)requestPushPermission:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
NSError *firebaseError = nil;
|
||||
if (![self ensureFirebaseConfigured:&firebaseError]) {
|
||||
[self sendErrorForCommand:command code:@"FIREBASE_NOT_CONFIGURED" message:(firebaseError.localizedDescription ? firebaseError.localizedDescription : @"Firebase is not configured.")];
|
||||
return;
|
||||
}
|
||||
[FIRMessaging messaging].delegate = self;
|
||||
|
||||
if (@available(iOS 10.0, *)) {
|
||||
UNAuthorizationOptions options = UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound;
|
||||
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error != nil) {
|
||||
[self sendErrorForCommand:command code:@"PUSH_PERMISSION_FAILED" message:error.localizedDescription];
|
||||
return;
|
||||
}
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
[self sendSuccessForCommand:command dictionary:@{@"type": @"notification", @"granted": @(granted), @"missing": granted ? @[] : @[@"notification"]}];
|
||||
});
|
||||
}];
|
||||
} else {
|
||||
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound) categories:nil];
|
||||
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
[self sendSuccessForCommand:command dictionary:@{@"type": @"notification", @"granted": @YES, @"missing": @[]}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getFirebaseToken:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
NSError *firebaseError = nil;
|
||||
if (![self ensureFirebaseConfigured:&firebaseError]) {
|
||||
[self sendErrorForCommand:command code:@"FIREBASE_NOT_CONFIGURED" message:(firebaseError.localizedDescription ? firebaseError.localizedDescription : @"Firebase is not configured.")];
|
||||
return;
|
||||
}
|
||||
[FIRMessaging messaging].delegate = self;
|
||||
|
||||
[[FIRMessaging messaging] tokenWithCompletion:^(NSString *token, NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error != nil || token.length == 0) {
|
||||
[self sendErrorForCommand:command code:@"FCM_TOKEN_FAILED" message:(error.localizedDescription ? error.localizedDescription : @"Failed to get Firebase token.")];
|
||||
return;
|
||||
}
|
||||
[self sendSuccessForCommand:command dictionary:@{@"token": token}];
|
||||
[self sendEvent:@{@"type": @"token", @"token": token}];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)subscribeToTopic:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
NSString *topic = [command.arguments firstObject];
|
||||
if (![topic isKindOfClass:[NSString class]] || topic.length == 0) {
|
||||
[self sendErrorForCommand:command code:@"INVALID_TOPIC" message:@"topic is required."];
|
||||
return;
|
||||
}
|
||||
NSError *firebaseError = nil;
|
||||
if (![self ensureFirebaseConfigured:&firebaseError]) {
|
||||
[self sendErrorForCommand:command code:@"FIREBASE_NOT_CONFIGURED" message:(firebaseError.localizedDescription ? firebaseError.localizedDescription : @"Firebase is not configured.")];
|
||||
return;
|
||||
}
|
||||
[[FIRMessaging messaging] subscribeToTopic:topic completion:^(NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error != nil) {
|
||||
[self sendErrorForCommand:command code:@"FIREBASE_TASK_FAILED" message:error.localizedDescription];
|
||||
} else {
|
||||
[self sendSuccessForCommand:command dictionary:nil];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)unsubscribeFromTopic:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
NSString *topic = [command.arguments firstObject];
|
||||
if (![topic isKindOfClass:[NSString class]] || topic.length == 0) {
|
||||
[self sendErrorForCommand:command code:@"INVALID_TOPIC" message:@"topic is required."];
|
||||
return;
|
||||
}
|
||||
NSError *firebaseError = nil;
|
||||
if (![self ensureFirebaseConfigured:&firebaseError]) {
|
||||
[self sendErrorForCommand:command code:@"FIREBASE_NOT_CONFIGURED" message:(firebaseError.localizedDescription ? firebaseError.localizedDescription : @"Firebase is not configured.")];
|
||||
return;
|
||||
}
|
||||
[[FIRMessaging messaging] unsubscribeFromTopic:topic completion:^(NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error != nil) {
|
||||
[self sendErrorForCommand:command code:@"FIREBASE_TASK_FAILED" message:error.localizedDescription];
|
||||
} else {
|
||||
[self sendSuccessForCommand:command dictionary:nil];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)getInitialNotification:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
[self sendSuccessForCommand:command dictionary:self.initialNotification];
|
||||
self.initialNotification = nil;
|
||||
}
|
||||
|
||||
- (void)startNotificationListener:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
self.notificationCallbackId = command.callbackId;
|
||||
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT];
|
||||
[result setKeepCallbackAsBool:YES];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)stopNotificationListener:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
self.notificationCallbackId = nil;
|
||||
[self sendSuccessForCommand:command dictionary:nil];
|
||||
}
|
||||
|
||||
- (void)applyWifiCommand:(CDVInvokedUrlCommand *)command defaultJoinOnce:(BOOL)defaultJoinOnce
|
||||
{
|
||||
NSDictionary *options = [self firstDictionaryArgument:command];
|
||||
NSString *ssid = [self stringFromDictionary:options key:@"ssid"];
|
||||
NSString *password = [self stringFromDictionary:options key:@"password"];
|
||||
NSString *security = [[self stringFromDictionary:options key:@"security"] lowercaseString];
|
||||
BOOL isWep = [security isEqualToString:@"wep"] || [self boolFromDictionary:options key:@"isWep" defaultValue:NO];
|
||||
BOOL joinOnce = [self boolFromDictionary:options key:@"joinOnce" defaultValue:defaultJoinOnce];
|
||||
|
||||
if (ssid.length == 0) {
|
||||
[self sendErrorForCommand:command code:@"INVALID_WIFI_OPTIONS" message:@"ssid is required."];
|
||||
return;
|
||||
}
|
||||
|
||||
if (@available(iOS 11.0, *)) {
|
||||
NEHotspotConfiguration *configuration = nil;
|
||||
if (password.length == 0 || [security isEqualToString:@"open"]) {
|
||||
configuration = [[NEHotspotConfiguration alloc] initWithSSID:ssid];
|
||||
} else {
|
||||
configuration = [[NEHotspotConfiguration alloc] initWithSSID:ssid passphrase:password isWEP:isWep];
|
||||
}
|
||||
configuration.joinOnce = joinOnce;
|
||||
|
||||
[[NEHotspotConfigurationManager sharedManager] applyConfiguration:configuration completionHandler:^(NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error != nil && error.code != NEHotspotConfigurationErrorAlreadyAssociated) {
|
||||
[self sendErrorForCommand:command code:@"WIFI_CONNECT_FAILED" message:(error.localizedDescription ? error.localizedDescription : @"Unable to apply WiFi configuration.")];
|
||||
return;
|
||||
}
|
||||
NSDictionary *result = @{
|
||||
@"ssid": ssid,
|
||||
@"platformMode": @"hotspotConfiguration",
|
||||
@"connected": @YES,
|
||||
@"saved": @(!joinOnce),
|
||||
@"alreadyAssociated": @(error != nil && error.code == NEHotspotConfigurationErrorAlreadyAssociated)
|
||||
};
|
||||
[self sendSuccessForCommand:command dictionary:result];
|
||||
});
|
||||
}];
|
||||
} else {
|
||||
[self sendErrorForCommand:command code:@"UNSUPPORTED_IOS_VERSION" message:@"WiFi configuration requires iOS 11 or newer."];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)biometricAvailability
|
||||
{
|
||||
LAContext *context = [[LAContext alloc] init];
|
||||
NSError *error = nil;
|
||||
BOOL available = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
|
||||
NSString *type = [self biometryTypeForContext:context];
|
||||
if (available) {
|
||||
return @{@"available": @YES, @"biometryType": type, @"code": @"OK", @"message": @"Biometric authentication is available."};
|
||||
}
|
||||
|
||||
BOOL passcodeAvailable = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:nil];
|
||||
return @{
|
||||
@"available": @NO,
|
||||
@"biometryType": type,
|
||||
@"passcodeAvailable": @(passcodeAvailable),
|
||||
@"code": @"BIOMETRIC_UNAVAILABLE",
|
||||
@"message": error.localizedDescription ? error.localizedDescription : @"Biometric authentication is unavailable."
|
||||
};
|
||||
}
|
||||
|
||||
- (NSString *)biometryTypeForContext:(LAContext *)context
|
||||
{
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if (context.biometryType == LABiometryTypeFaceID) {
|
||||
return @"face";
|
||||
}
|
||||
if (context.biometryType == LABiometryTypeTouchID) {
|
||||
return @"touch";
|
||||
}
|
||||
if (@available(iOS 11.2, *)) {
|
||||
if (context.biometryType == LABiometryTypeNone) {
|
||||
return @"none";
|
||||
}
|
||||
}
|
||||
return @"biometric";
|
||||
}
|
||||
return @"touch";
|
||||
}
|
||||
|
||||
- (BOOL)ensureFirebaseConfigured:(NSError **)error
|
||||
{
|
||||
if ([FIRApp defaultApp] != nil) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
|
||||
if (plistPath.length > 0) {
|
||||
[FIRApp configure];
|
||||
return [FIRApp defaultApp] != nil;
|
||||
}
|
||||
|
||||
NSString *googleAppId = [self infoStringForKey:@"BLUEMEME_FIREBASE_GOOGLE_APP_ID"];
|
||||
NSString *gcmSenderId = [self infoStringForKey:@"BLUEMEME_FIREBASE_GCM_SENDER_ID"];
|
||||
NSString *apiKey = [self infoStringForKey:@"BLUEMEME_FIREBASE_API_KEY"];
|
||||
NSString *projectId = [self infoStringForKey:@"BLUEMEME_FIREBASE_PROJECT_ID"];
|
||||
NSString *storageBucket = [self infoStringForKey:@"BLUEMEME_FIREBASE_STORAGE_BUCKET"];
|
||||
|
||||
if (googleAppId.length == 0 || gcmSenderId.length == 0) {
|
||||
if (error != nil) {
|
||||
*error = [NSError errorWithDomain:@"BluememeUtils" code:1001 userInfo:@{NSLocalizedDescriptionKey: @"Firebase options are missing."}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:googleAppId GCMSenderID:gcmSenderId];
|
||||
if (apiKey.length > 0) {
|
||||
options.APIKey = apiKey;
|
||||
}
|
||||
if (projectId.length > 0) {
|
||||
options.projectID = projectId;
|
||||
}
|
||||
if (storageBucket.length > 0) {
|
||||
options.storageBucket = storageBucket;
|
||||
}
|
||||
options.bundleID = [[NSBundle mainBundle] bundleIdentifier];
|
||||
[FIRApp configureWithOptions:options];
|
||||
return [FIRApp defaultApp] != nil;
|
||||
}
|
||||
|
||||
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken
|
||||
{
|
||||
if (fcmToken.length == 0) {
|
||||
return;
|
||||
}
|
||||
[self sendEvent:@{@"type": @"token", @"token": fcmToken}];
|
||||
}
|
||||
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
willPresentNotification:(UNNotification *)notification
|
||||
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler API_AVAILABLE(ios(10.0))
|
||||
{
|
||||
[self sendEvent:@{@"type": @"notification", @"notification": [self payloadFromNotification:notification]}];
|
||||
|
||||
if (self.previousNotificationDelegate != nil
|
||||
&& self.previousNotificationDelegate != self
|
||||
&& [self.previousNotificationDelegate respondsToSelector:@selector(userNotificationCenter:willPresentNotification:withCompletionHandler:)]) {
|
||||
[self.previousNotificationDelegate userNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler];
|
||||
return;
|
||||
}
|
||||
|
||||
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionBadge);
|
||||
}
|
||||
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0))
|
||||
{
|
||||
NSDictionary *payload = [self payloadFromNotification:response.notification];
|
||||
self.initialNotification = payload;
|
||||
[self sendEvent:@{@"type": @"notificationOpened", @"notification": payload}];
|
||||
|
||||
if (self.previousNotificationDelegate != nil
|
||||
&& self.previousNotificationDelegate != self
|
||||
&& [self.previousNotificationDelegate respondsToSelector:@selector(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:)]) {
|
||||
[self.previousNotificationDelegate userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
|
||||
return;
|
||||
}
|
||||
|
||||
completionHandler();
|
||||
}
|
||||
|
||||
- (NSDictionary *)payloadFromNotification:(UNNotification *)notification API_AVAILABLE(ios(10.0))
|
||||
{
|
||||
NSMutableDictionary *payload = [NSMutableDictionary dictionary];
|
||||
NSDictionary *userInfo = notification.request.content.userInfo ? notification.request.content.userInfo : @{};
|
||||
payload[@"data"] = userInfo;
|
||||
if (notification.request.identifier.length > 0) {
|
||||
payload[@"identifier"] = notification.request.identifier;
|
||||
}
|
||||
|
||||
NSMutableDictionary *apsNotification = [NSMutableDictionary dictionary];
|
||||
if (notification.request.content.title.length > 0) {
|
||||
apsNotification[@"title"] = notification.request.content.title;
|
||||
}
|
||||
if (notification.request.content.body.length > 0) {
|
||||
apsNotification[@"body"] = notification.request.content.body;
|
||||
}
|
||||
if (notification.request.content.categoryIdentifier.length > 0) {
|
||||
apsNotification[@"category"] = notification.request.content.categoryIdentifier;
|
||||
}
|
||||
if (apsNotification.count > 0) {
|
||||
payload[@"notification"] = apsNotification;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
- (void)sendEvent:(NSDictionary *)event
|
||||
{
|
||||
if (self.notificationCallbackId.length == 0 || event == nil) {
|
||||
return;
|
||||
}
|
||||
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:event];
|
||||
[result setKeepCallbackAsBool:YES];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:self.notificationCallbackId];
|
||||
}
|
||||
|
||||
- (NSDictionary *)firstDictionaryArgument:(CDVInvokedUrlCommand *)command
|
||||
{
|
||||
id first = [command.arguments firstObject];
|
||||
if ([first isKindOfClass:[NSDictionary class]]) {
|
||||
return first;
|
||||
}
|
||||
return @{};
|
||||
}
|
||||
|
||||
- (NSString *)stringFromDictionary:(NSDictionary *)dictionary key:(NSString *)key
|
||||
{
|
||||
id value = dictionary[key];
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
return value;
|
||||
}
|
||||
if ([value respondsToSelector:@selector(stringValue)]) {
|
||||
return [value stringValue];
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (BOOL)boolFromDictionary:(NSDictionary *)dictionary key:(NSString *)key defaultValue:(BOOL)defaultValue
|
||||
{
|
||||
id value = dictionary[key];
|
||||
if ([value isKindOfClass:[NSNumber class]]) {
|
||||
return [value boolValue];
|
||||
}
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
NSString *lowercase = [value lowercaseString];
|
||||
if ([lowercase isEqualToString:@"true"] || [lowercase isEqualToString:@"1"] || [lowercase isEqualToString:@"yes"]) {
|
||||
return YES;
|
||||
}
|
||||
if ([lowercase isEqualToString:@"false"] || [lowercase isEqualToString:@"0"] || [lowercase isEqualToString:@"no"]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
- (NSString *)infoStringForKey:(NSString *)key
|
||||
{
|
||||
id value = [[NSBundle mainBundle] objectForInfoDictionaryKey:key];
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
return value;
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (void)sendSuccessForCommand:(CDVInvokedUrlCommand *)command dictionary:(NSDictionary *)dictionary
|
||||
{
|
||||
CDVPluginResult *result = dictionary == nil
|
||||
? [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]
|
||||
: [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)sendErrorForCommand:(CDVInvokedUrlCommand *)command code:(NSString *)code message:(NSString *)message
|
||||
{
|
||||
NSDictionary *error = @{@"code": code ? code : @"ERROR", @"message": message ? message : @""};
|
||||
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:error];
|
||||
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||
}
|
||||
@end
|
||||
Reference in new issue
Block a user