mirror of
https://github.com/bitwarden/android.git
synced 2024-12-26 19:08:32 +03:00
[SG-702] Tapping Push Notification does not open the account the request is for (#2112)
* [SG-702] Tap notification now switches accounts if it is a passwordless notification. * [SG-702] Fix compilation errors * [SG-702] Fixed iOS notification tap fix * [SG-702] Notification data model * [SG-702] Change method signature with object containing properties. PR fixes.
This commit is contained in:
parent
1e5eab0574
commit
abada481b7
11 changed files with 128 additions and 12 deletions
|
@ -20,6 +20,8 @@ using Bit.Core.Enums;
|
|||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Receivers;
|
||||
using Bit.Droid.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Xamarin.Essentials;
|
||||
using ZXing.Net.Mobile.Android;
|
||||
using FileProvider = AndroidX.Core.Content.FileProvider;
|
||||
|
@ -39,6 +41,7 @@ namespace Bit.Droid
|
|||
private IStateService _stateService;
|
||||
private IAppIdService _appIdService;
|
||||
private IEventService _eventService;
|
||||
private IPushNotificationListenerService _pushNotificationListenerService;
|
||||
private ILogger _logger;
|
||||
private PendingIntent _eventUploadPendingIntent;
|
||||
private AppOptions _appOptions;
|
||||
|
@ -61,6 +64,7 @@ namespace Bit.Droid
|
|||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||
_pushNotificationListenerService = ServiceContainer.Resolve<IPushNotificationListenerService>();
|
||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
|
||||
TabLayoutResource = Resource.Layout.Tabbar;
|
||||
|
@ -145,6 +149,15 @@ namespace Bit.Droid
|
|||
AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
|
||||
if (Intent?.GetStringExtra(Constants.NotificationData) is string notificationDataJson)
|
||||
{
|
||||
var notificationType = JToken.Parse(notificationDataJson).SelectToken(Constants.NotificationDataType);
|
||||
if (notificationType.ToString() == PasswordlessNotificationData.TYPE)
|
||||
{
|
||||
_pushNotificationListenerService.OnNotificationTapped(JsonConvert.DeserializeObject<PasswordlessNotificationData>(notificationDataJson)).FireAndForget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNewIntent(Intent intent)
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
#if !FDROID
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using AndroidX.Core.App;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Droid.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
|
@ -67,28 +70,34 @@ namespace Bit.Droid.Services
|
|||
}
|
||||
}
|
||||
|
||||
public void SendLocalNotification(string title, string message, string notificationId)
|
||||
public void SendLocalNotification(string title, string message, BaseNotificationData data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(notificationId))
|
||||
if (string.IsNullOrEmpty(data.Id))
|
||||
{
|
||||
throw new ArgumentNullException("notificationId cannot be null or empty.");
|
||||
}
|
||||
|
||||
var context = Android.App.Application.Context;
|
||||
var intent = new Intent(context, typeof(MainActivity));
|
||||
intent.PutExtra(Constants.NotificationData, JsonConvert.SerializeObject(data));
|
||||
|
||||
var pendingIntentFlags = AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.UpdateCurrent, true);
|
||||
var pendingIntent = PendingIntent.GetActivity(context, 20220801, intent, pendingIntentFlags);
|
||||
var builder = new NotificationCompat.Builder(context, Constants.AndroidNotificationChannelId)
|
||||
.SetContentIntent(pendingIntent)
|
||||
.SetContentTitle(title)
|
||||
.SetContentText(message)
|
||||
.SetTimeoutAfter(Constants.PasswordlessNotificationTimeoutInMinutes * 60000)
|
||||
.SetSmallIcon(Resource.Drawable.ic_notification)
|
||||
.SetColor((int)Android.Graphics.Color.White)
|
||||
.SetAutoCancel(true);
|
||||
|
||||
if (data is PasswordlessNotificationData passwordlessNotificationData && passwordlessNotificationData.TimeoutInMinutes > 0)
|
||||
{
|
||||
builder.SetTimeoutAfter(passwordlessNotificationData.TimeoutInMinutes * 60000);
|
||||
}
|
||||
|
||||
var notificationManager = NotificationManagerCompat.From(context);
|
||||
notificationManager.Notify(int.Parse(notificationId), builder.Build());
|
||||
notificationManager.Notify(int.Parse(data.Id), builder.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
|
@ -9,6 +10,7 @@ namespace Bit.App.Abstractions
|
|||
Task OnRegisteredAsync(string token, string device);
|
||||
void OnUnregistered(string device);
|
||||
void OnError(string message, string device);
|
||||
Task OnNotificationTapped(BaseNotificationData data);
|
||||
bool ShouldShowNotification();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
{
|
||||
|
@ -10,7 +11,7 @@ namespace Bit.App.Abstractions
|
|||
Task<string> GetTokenAsync();
|
||||
Task RegisterAsync();
|
||||
Task UnregisterAsync();
|
||||
void SendLocalNotification(string title, string message, string notificationId);
|
||||
void SendLocalNotification(string title, string message, BaseNotificationData data);
|
||||
void DismissLocalNotification(string notificationId);
|
||||
}
|
||||
}
|
||||
|
|
23
src/App/Models/NotificationData.cs
Normal file
23
src/App/Models/NotificationData.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
namespace Bit.App.Models
|
||||
{
|
||||
public abstract class BaseNotificationData
|
||||
{
|
||||
public abstract string Type { get; }
|
||||
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
public class PasswordlessNotificationData : BaseNotificationData
|
||||
{
|
||||
public const string TYPE = "passwordlessNotificationData";
|
||||
|
||||
public override string Type => TYPE;
|
||||
|
||||
public int TimeoutInMinutes { get; set; }
|
||||
|
||||
public string UserEmail { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Bit.App.Services
|
||||
|
@ -28,5 +29,10 @@ namespace Bit.App.Services
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task OnNotificationTapped(BaseNotificationData data)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
|
||||
namespace Bit.App.Services
|
||||
{
|
||||
|
@ -30,6 +31,6 @@ namespace Bit.App.Services
|
|||
|
||||
public void DismissLocalNotification(string notificationId) { }
|
||||
|
||||
public void SendLocalNotification(string title, string message, string notificationId) { }
|
||||
public void SendLocalNotification(string title, string message, BaseNotificationData data) { }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#if !FDROID
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Pages;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core;
|
||||
|
@ -11,6 +13,7 @@ using Bit.Core.Abstractions;
|
|||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Response;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
@ -30,6 +33,7 @@ namespace Bit.App.Services
|
|||
private IApiService _apiService;
|
||||
private IMessagingService _messagingService;
|
||||
private IPushNotificationService _pushNotificationService;
|
||||
private ILogger _logger;
|
||||
|
||||
public async Task OnMessageAsync(JObject value, string deviceType)
|
||||
{
|
||||
|
@ -147,7 +151,14 @@ namespace Bit.App.Services
|
|||
await _stateService.SetPasswordlessLoginNotificationAsync(passwordlessLoginMessage, passwordlessLoginMessage?.UserId);
|
||||
var userEmail = await _stateService.GetEmailAsync(passwordlessLoginMessage?.UserId);
|
||||
|
||||
_pushNotificationService.SendLocalNotification(AppResources.LogInRequested, String.Format(AppResources.ConfimLogInAttempForX, userEmail), Constants.PasswordlessNotificationId);
|
||||
var notificationData = new PasswordlessNotificationData()
|
||||
{
|
||||
Id = Constants.PasswordlessNotificationId,
|
||||
TimeoutInMinutes = Constants.PasswordlessNotificationTimeoutInMinutes,
|
||||
UserEmail = userEmail,
|
||||
};
|
||||
|
||||
_pushNotificationService.SendLocalNotification(AppResources.LogInRequested, String.Format(AppResources.ConfimLogInAttempForX, userEmail), notificationData);
|
||||
_messagingService.Send("passwordlessLoginRequest", passwordlessLoginMessage);
|
||||
break;
|
||||
default:
|
||||
|
@ -213,6 +224,27 @@ namespace Bit.App.Services
|
|||
Debug.WriteLine($"{TAG} error - {message}");
|
||||
}
|
||||
|
||||
public async Task OnNotificationTapped(BaseNotificationData data)
|
||||
{
|
||||
Resolve();
|
||||
try
|
||||
{
|
||||
if (data is PasswordlessNotificationData passwordlessNotificationData)
|
||||
{
|
||||
var notificationUserId = await _stateService.GetUserIdAsync(passwordlessNotificationData.UserEmail);
|
||||
if (notificationUserId != null)
|
||||
{
|
||||
await _stateService.SetActiveUserAsync(notificationUserId);
|
||||
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldShowNotification()
|
||||
{
|
||||
return _showNotification;
|
||||
|
@ -230,6 +262,7 @@ namespace Bit.App.Services
|
|||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
|
||||
_logger = ServiceContainer.Resolve<ILogger>();
|
||||
_resolved = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
public static string RememberedOrgIdentifierKey = "rememberedOrgIdentifier";
|
||||
public const string PasswordlessNotificationId = "26072022";
|
||||
public const string AndroidNotificationChannelId = "general_notification_channel";
|
||||
public const string NotificationData = "notificationData";
|
||||
public const string NotificationDataType = "NotificationType";
|
||||
public const int SelectFileRequestCode = 42;
|
||||
public const int SelectFilePermissionRequestCode = 43;
|
||||
public const int SaveFileRequestCode = 44;
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using CoreData;
|
||||
using Foundation;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UserNotifications;
|
||||
using Xamarin.Forms;
|
||||
|
@ -92,9 +97,20 @@ namespace Bit.iOS.Services
|
|||
{
|
||||
Debug.WriteLine($"{TAG} DidReceiveNotificationResponse {response?.Notification?.Request?.Content?.UserInfo}");
|
||||
|
||||
if (response.IsDefaultAction)
|
||||
if (response.IsDefaultAction && response?.Notification?.Request?.Content?.UserInfo != null)
|
||||
{
|
||||
OnMessageReceived(response?.Notification?.Request?.Content?.UserInfo);
|
||||
var userInfo = response?.Notification?.Request?.Content?.UserInfo;
|
||||
OnMessageReceived(userInfo);
|
||||
|
||||
if (userInfo.TryGetValue(NSString.FromObject(Constants.NotificationData), out NSObject nsObject))
|
||||
{
|
||||
var token = JToken.Parse(NSString.FromObject(nsObject).ToString());
|
||||
var typeToken = token.SelectToken(Constants.NotificationDataType);
|
||||
if (typeToken.ToString() == PasswordlessNotificationData.TYPE)
|
||||
{
|
||||
_pushNotificationListenerService.OnNotificationTapped(token.ToObject<PasswordlessNotificationData>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inform caller it has been handled
|
||||
|
|
|
@ -4,8 +4,13 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Services;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Services;
|
||||
using Foundation;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UIKit;
|
||||
using UserNotifications;
|
||||
|
||||
|
@ -69,9 +74,9 @@ namespace Bit.iOS.Services
|
|||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void SendLocalNotification(string title, string message, string notificationId)
|
||||
public void SendLocalNotification(string title, string message, BaseNotificationData data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(notificationId))
|
||||
if (string.IsNullOrEmpty(data.Id))
|
||||
{
|
||||
throw new ArgumentNullException("notificationId cannot be null or empty.");
|
||||
}
|
||||
|
@ -82,7 +87,12 @@ namespace Bit.iOS.Services
|
|||
Body = message
|
||||
};
|
||||
|
||||
var request = UNNotificationRequest.FromIdentifier(notificationId, content, null);
|
||||
if (data != null)
|
||||
{
|
||||
content.UserInfo = NSDictionary.FromObjectAndKey(NSData.FromString(JsonConvert.SerializeObject(data), NSStringEncoding.UTF8), new NSString(Constants.NotificationData));
|
||||
}
|
||||
|
||||
var request = UNNotificationRequest.FromIdentifier(data.Id, content, null);
|
||||
UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
|
||||
{
|
||||
if (err != null)
|
||||
|
|
Loading…
Reference in a new issue