1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-09-23 03:12:55 +02:00

Fixes for iOS push notifications (#1708)

* WIP Fixes for iOS push notifications

* WIP Fixes for iOS push notifications, fix missed implementation on android

* Fix some issues on the push notifications, changed to Debug Console.WriteLine, and added update on entitlements on the build.yml
This commit is contained in:
Federico Maccaroni 2022-01-18 11:52:08 -03:00 committed by GitHub
parent 42403210a0
commit 2791d4b8ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 31 deletions

View File

@ -397,6 +397,15 @@ jobs:
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
shell: bash shell: bash
- name: Update Entitlements
run: |
echo "########################################"
echo "##### Updating Entitlements"
echo "########################################"
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./src/iOS/Entitlements.plist
shell: bash
- name: Set up Keychain - name: Set up Keychain
env: env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }} KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}

View File

@ -1,6 +1,7 @@
#if !FDROID #if !FDROID
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using AndroidX.Core.App;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.Core; using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
@ -21,6 +22,8 @@ namespace Bit.Droid.Services
_pushNotificationListenerService = pushNotificationListenerService; _pushNotificationListenerService = pushNotificationListenerService;
} }
public bool IsRegisteredForPush => NotificationManagerCompat.From(Android.App.Application.Context)?.AreNotificationsEnabled() ?? false;
public async Task<string> GetTokenAsync() public async Task<string> GetTokenAsync()
{ {
return await _storageService.GetAsync<string>(Constants.PushCurrentTokenKey); return await _storageService.GetAsync<string>(Constants.PushCurrentTokenKey);

View File

@ -4,6 +4,7 @@ namespace Bit.App.Abstractions
{ {
public interface IPushNotificationService public interface IPushNotificationService
{ {
bool IsRegisteredForPush { get; }
Task<string> GetTokenAsync(); Task<string> GetTokenAsync();
Task RegisterAsync(); Task RegisterAsync();
Task UnregisterAsync(); Task UnregisterAsync();

View File

@ -1,15 +1,14 @@
using Bit.App.Abstractions; using System;
using Bit.App.Models; using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core; using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Utilities;
using Bit.Core.Models.Request; using Bit.Core.Models.Request;
using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@ -135,7 +134,7 @@ namespace Bit.App.Pages
// Users with key connector and without biometric or pin has no MP to unlock with // Users with key connector and without biometric or pin has no MP to unlock with
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector(); _usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
if ( _usingKeyConnector && !(BiometricLock || PinLock)) if (_usingKeyConnector && !(BiometricLock || PinLock))
{ {
await _vaultTimeoutService.LogOutAsync(); await _vaultTimeoutService.LogOutAsync();
} }

View File

@ -5,6 +5,8 @@ namespace Bit.App.Services
{ {
public class NoopPushNotificationService : IPushNotificationService public class NoopPushNotificationService : IPushNotificationService
{ {
public bool IsRegisteredForPush => false;
public Task<string> GetTokenAsync() public Task<string> GetTokenAsync()
{ {
return Task.FromResult(null as string); return Task.FromResult(null as string);

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
@ -25,5 +25,7 @@
<array> <array>
<string>webcredentials:bitwarden.com</string> <string>webcredentials:bitwarden.com</string>
</array> </array>
<key>aps-environment</key>
<string>development</string>
</dict> </dict>
</plist> </plist>

View File

@ -1,13 +1,15 @@
using Bit.App.Abstractions; using System;
using Foundation;
using Newtonsoft.Json.Linq;
using System;
using System.Diagnostics; using System.Diagnostics;
using Bit.App.Abstractions;
using Foundation;
using Microsoft.AppCenter.Crashes;
using Newtonsoft.Json.Linq;
using UserNotifications;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.iOS.Services namespace Bit.iOS.Services
{ {
public class iOSPushNotificationHandler public class iOSPushNotificationHandler : NSObject, IUNUserNotificationCenterDelegate
{ {
private const string TokenSetting = "token"; private const string TokenSetting = "token";
private const string DomainName = "iOSPushNotificationService"; private const string DomainName = "iOSPushNotificationService";
@ -22,20 +24,27 @@ namespace Bit.iOS.Services
public void OnMessageReceived(NSDictionary userInfo) public void OnMessageReceived(NSDictionary userInfo)
{ {
var json = DictionaryToJson(userInfo); try
var values = JObject.Parse(json);
var keyAps = new NSString("aps");
if (userInfo.ContainsKey(keyAps) && userInfo.ValueForKey(keyAps) is NSDictionary aps)
{ {
foreach (var apsKey in aps) var json = DictionaryToJson(userInfo);
var values = JObject.Parse(json);
var keyAps = new NSString("aps");
if (userInfo.ContainsKey(keyAps) && userInfo.ValueForKey(keyAps) is NSDictionary aps)
{ {
if (!values.TryGetValue(apsKey.Key.ToString(), out JToken temp)) foreach (var apsKey in aps)
{ {
values.Add(apsKey.Key.ToString(), apsKey.Value.ToString()); if (!values.TryGetValue(apsKey.Key.ToString(), out JToken temp))
{
values.Add(apsKey.Key.ToString(), apsKey.Value.ToString());
}
} }
} }
_pushNotificationListenerService.OnMessageAsync(values, Device.iOS);
}
catch (Exception ex)
{
Crashes.TrackError(ex);
} }
_pushNotificationListenerService.OnMessageAsync(values, Device.iOS);
} }
public void OnErrorReceived(NSError error) public void OnErrorReceived(NSError error)
@ -47,9 +56,15 @@ namespace Bit.iOS.Services
public void OnRegisteredSuccess(NSData token) public void OnRegisteredSuccess(NSData token)
{ {
Debug.WriteLine("{0} - Successfully Registered.", DomainName); Debug.WriteLine("{0} - Successfully Registered.", DomainName);
var hexDeviceToken = BitConverter.ToString(token.ToArray()) var hexDeviceToken = BitConverter.ToString(token.ToArray())
.Replace("-", string.Empty).ToLowerInvariant(); .Replace("-", string.Empty)
Console.WriteLine("{0} - Token: {1}", DomainName, hexDeviceToken); .ToLowerInvariant();
Debug.WriteLine("{0} - Token: {1}", DomainName, hexDeviceToken);
UNUserNotificationCenter.Current.Delegate = this;
_pushNotificationListenerService.OnRegisteredAsync(hexDeviceToken, Device.iOS); _pushNotificationListenerService.OnRegisteredAsync(hexDeviceToken, Device.iOS);
NSUserDefaults.StandardUserDefaults.SetString(hexDeviceToken, TokenSetting); NSUserDefaults.StandardUserDefaults.SetString(hexDeviceToken, TokenSetting);
NSUserDefaults.StandardUserDefaults.Synchronize(); NSUserDefaults.StandardUserDefaults.Synchronize();
@ -60,5 +75,29 @@ namespace Bit.iOS.Services
var json = NSJsonSerialization.Serialize(dictionary, NSJsonWritingOptions.PrettyPrinted, out NSError error); var json = NSJsonSerialization.Serialize(dictionary, NSJsonWritingOptions.PrettyPrinted, out NSError error);
return json.ToString(NSStringEncoding.UTF8); return json.ToString(NSStringEncoding.UTF8);
} }
// To receive notifications in foreground on iOS 10 devices.
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
Debug.WriteLine($"{DomainName} WillPresentNotification {notification?.Request?.Content?.UserInfo}");
OnMessageReceived(notification?.Request?.Content?.UserInfo);
completionHandler(UNNotificationPresentationOptions.Alert);
}
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
Debug.WriteLine($"{DomainName} DidReceiveNotificationResponse {response?.Notification?.Request?.Content?.UserInfo}");
if (response.IsDefaultAction)
{
OnMessageReceived(response?.Notification?.Request?.Content?.UserInfo);
}
// Inform caller it has been handled
completionHandler();
}
} }
} }

View File

@ -1,11 +1,14 @@
using System.Threading.Tasks; using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Foundation; using Foundation;
using UIKit; using UIKit;
using UserNotifications;
namespace Bit.iOS.Services namespace Bit.iOS.Services
{ {
public class iOSPushNotificationService : IPushNotificationService public class iOSPushNotificationService : NSObject, IPushNotificationService, IUNUserNotificationCenterDelegate
{ {
private const string TokenSetting = "token"; private const string TokenSetting = "token";
@ -14,13 +17,31 @@ namespace Bit.iOS.Services
return Task.FromResult(NSUserDefaults.StandardUserDefaults.StringForKey(TokenSetting)); return Task.FromResult(NSUserDefaults.StandardUserDefaults.StringForKey(TokenSetting));
} }
public Task RegisterAsync() public bool IsRegisteredForPush => UIApplication.SharedApplication.IsRegisteredForRemoteNotifications;
public async Task RegisterAsync()
{ {
var userNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | var tcs = new TaskCompletionSource<bool>();
UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(userNotificationTypes, null); var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings); UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
return Task.FromResult(0); {
if (error != null)
{
Debug.WriteLine($"Push Notifications {error}");
}
else
{
Debug.WriteLine($"Push Notifications {granted}");
}
tcs.SetResult(granted);
});
if (await tcs.Task)
{
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
} }
public Task UnregisterAsync() public Task UnregisterAsync()