switch from GCM to FCM

This commit is contained in:
Kyle Spearrin 2017-12-21 22:14:56 -05:00
parent 2b4ffaa357
commit 582e6ee322
12 changed files with 4875 additions and 4406 deletions

View file

@ -81,6 +81,8 @@
<ItemGroup> <ItemGroup>
<Compile Include="AutofillActivity.cs" /> <Compile Include="AutofillActivity.cs" />
<Compile Include="AutofillCredentials.cs" /> <Compile Include="AutofillCredentials.cs" />
<Compile Include="FirebaseInstanceIdService.cs" />
<Compile Include="FirebaseMessagingService.cs" />
<Compile Include="Autofill\Field.cs" /> <Compile Include="Autofill\Field.cs" />
<Compile Include="Autofill\FieldCollection.cs" /> <Compile Include="Autofill\FieldCollection.cs" />
<Compile Include="Autofill\AutofillService.cs" /> <Compile Include="Autofill\AutofillService.cs" />
@ -103,13 +105,14 @@
<Compile Include="Controls\ExtendedPickerRenderer.cs" /> <Compile Include="Controls\ExtendedPickerRenderer.cs" />
<Compile Include="Controls\ExtendedEntryRenderer.cs" /> <Compile Include="Controls\ExtendedEntryRenderer.cs" />
<Compile Include="MyVaultTileService.cs" /> <Compile Include="MyVaultTileService.cs" />
<Compile Include="Services\AndroidPushNotificationService.cs" />
<Compile Include="Services\GoogleAnalyticsService.cs" />
<Compile Include="Services\HttpService.cs" /> <Compile Include="Services\HttpService.cs" />
<Compile Include="Services\AndroidKeyStoreStorageService.cs" /> <Compile Include="Services\AndroidKeyStoreStorageService.cs" />
<Compile Include="Services\LocalizeService.cs" /> <Compile Include="Services\LocalizeService.cs" />
<Compile Include="MainApplication.cs" /> <Compile Include="MainApplication.cs" />
<Compile Include="Resources\Resource.Designer.cs" /> <Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Services\DeviceInfoService.cs" /> <Compile Include="Services\DeviceInfoService.cs" />
<Compile Include="Services\GoogleAnalyticsService.cs" />
<Compile Include="Services\AppInfoService.cs" /> <Compile Include="Services\AppInfoService.cs" />
<Compile Include="Services\DeviceActionService.cs" /> <Compile Include="Services\DeviceActionService.cs" />
<Compile Include="Services\BouncyCastleKeyDerivationService.cs" /> <Compile Include="Services\BouncyCastleKeyDerivationService.cs" />
@ -117,7 +120,6 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\LogService.cs" /> <Compile Include="Services\LogService.cs" />
<Compile Include="Services\MemoryService.cs" /> <Compile Include="Services\MemoryService.cs" />
<Compile Include="Services\AndroidPushNotificationService.cs" />
<Compile Include="Services\ReflectionService.cs" /> <Compile Include="Services\ReflectionService.cs" />
<Compile Include="Services\SqlService.cs" /> <Compile Include="Services\SqlService.cs" />
<Compile Include="SplashActivity.cs" /> <Compile Include="SplashActivity.cs" />
@ -127,6 +129,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="8bit.keystore.enc" /> <None Include="8bit.keystore.enc" />
<GoogleServicesJson Include="google-services.json" />
<None Include="google-services.json.enc" />
<None Include="increment-version.ps1" /> <None Include="increment-version.ps1" />
<None Include="Resources\AboutResources.txt" /> <None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" /> <None Include="Assets\AboutAssets.txt" />
@ -891,11 +895,11 @@
<PackageReference Include="SimpleInjector"> <PackageReference Include="SimpleInjector">
<Version>4.0.12</Version> <Version>4.0.12</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Xamarin.GooglePlayServices.Analytics"> <PackageReference Include="Xamarin.Firebase.Messaging">
<Version>29.0.0.2</Version> <Version>42.1021.1</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Xamarin.GooglePlayServices.Gcm"> <PackageReference Include="Xamarin.GooglePlayServices.Analytics">
<Version>29.0.0.2</Version> <Version>42.1021.1</Version>
</PackageReference> </PackageReference>
<PackageReference Include="XLabs.IoC.SimpleInjector" Version="2.0.5782" /> <PackageReference Include="XLabs.IoC.SimpleInjector" Version="2.0.5782" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.1.3" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.1.3" />

View file

@ -0,0 +1,23 @@
using System;
using Android.App;
using Android.Content;
using Bit.App;
using Bit.App.Abstractions;
using Firebase.Iid;
using Plugin.Settings.Abstractions;
using XLabs.Ioc;
namespace Bit.Android
{
[Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class FirebaseInstanceIdService : Firebase.Iid.FirebaseInstanceIdService
{
public override void OnTokenRefresh()
{
var settings = Resolver.Resolve<ISettings>();
settings.AddOrUpdateValue(Constants.PushRegisteredToken, FirebaseInstanceId.Instance.Token);
Resolver.Resolve<IPushNotificationService>()?.Register();
}
}
}

View file

@ -0,0 +1,42 @@
using System;
using Android.App;
using Android.Content;
using Bit.App.Abstractions;
using Firebase.Messaging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Xamarin.Forms;
using XLabs.Ioc;
namespace Bit.Android
{
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseMessagingService : Firebase.Messaging.FirebaseMessagingService
{
public override void OnMessageReceived(RemoteMessage message)
{
if(message?.Data == null)
{
return;
}
var data = message.Data.ContainsKey("data") ? message.Data["data"] : null;
if(data == null)
{
return;
}
try
{
var obj = JObject.Parse(data);
var listener = Resolver.Resolve<IPushNotificationListener>();
listener.OnMessage(obj, Device.Android);
}
catch(JsonReaderException ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
}
}

View file

@ -61,55 +61,6 @@ namespace Bit.Android
RegisterActivityLifecycleCallbacks(this); RegisterActivityLifecycleCallbacks(this);
AppContext = ApplicationContext; AppContext = ApplicationContext;
StartPushService();
HandlePushReregistration();
}
private void HandlePushReregistration()
{
var pushNotification = Resolver.Resolve<IPushNotificationService>();
var settings = Resolver.Resolve<ISettings>();
// Reregister for push token based on certain conditions
// ref https://github.com/rdelrosario/xamarin-plugins/issues/65
var reregister = false;
// 1. First time starting the app after a new install
if(settings.GetValueOrDefault(FirstLaunchKey, true))
{
settings.AddOrUpdateValue(FirstLaunchKey, false);
reregister = true;
}
// 2. App version changed (installed update)
var versionCode = Context.ApplicationContext.PackageManager.GetPackageInfo(Context.PackageName, 0).VersionCode;
if(settings.GetValueOrDefault(LastVersionCodeKey, -1) != versionCode)
{
settings.AddOrUpdateValue(LastVersionCodeKey, versionCode);
reregister = true;
}
// 3. In debug mode
if(App.Utilities.Helpers.InDebugMode())
{
reregister = true;
}
// 4. Doesn't have a push token currently
if(string.IsNullOrWhiteSpace(pushNotification.Token))
{
reregister = true;
}
if(reregister)
{
pushNotification.Unregister();
if(Resolver.Resolve<IAuthService>().IsAuthenticated)
{
pushNotification.Register();
}
}
} }
public override void OnTerminate() public override void OnTerminate()
@ -149,30 +100,6 @@ namespace Bit.Android
{ {
} }
public static void StartPushService()
{
AppContext.StartService(new Intent(AppContext, typeof(AndroidPushService)));
if(Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
PendingIntent pintent = PendingIntent.GetService(AppContext, 0, new Intent(AppContext,
typeof(AndroidPushService)), 0);
AlarmManager alarm = (AlarmManager)AppContext.GetSystemService(AlarmService);
alarm.Cancel(pintent);
}
}
public static void StopPushService()
{
AppContext.StopService(new Intent(AppContext, typeof(AndroidPushService)));
if(Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
PendingIntent pintent = PendingIntent.GetService(AppContext, 0, new Intent(AppContext,
typeof(AndroidPushService)), 0);
AlarmManager alarm = (AlarmManager)AppContext.GetSystemService(AlarmService);
alarm.Cancel(pintent);
}
}
public static void SetIoc(Application application) public static void SetIoc(Application application)
{ {
UserDialogs.Init(application); UserDialogs.Init(application);
@ -237,10 +164,8 @@ namespace Bit.Android
container.RegisterSingleton(CrossFingerprint.Current); container.RegisterSingleton(CrossFingerprint.Current);
// Push // Push
var pushListener = new PushNotificationListener(); container.RegisterSingleton<IPushNotificationListener, PushNotificationListener>();
container.RegisterSingleton<IPushNotificationListener>(pushListener); container.RegisterSingleton<IPushNotificationService, AndroidPushNotificationService>();
CrossPushNotification.Initialize(pushListener, "962181367620");
container.RegisterSingleton(CrossPushNotification.Current);
container.Verify(); container.Verify();
Resolver.SetResolver(new SimpleInjectorResolver(container)); Resolver.SetResolver(new SimpleInjectorResolver(container));

View file

@ -8,11 +8,6 @@
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" /> <uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" /> <uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.x8bit.bitwarden.permission.C2D_MESSAGE" />
<permission android:name="com.x8bit.bitwarden.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-feature android:name="android.hardware.camera" android:required="false" /> <uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" /> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
@ -28,6 +23,17 @@
android:resource="@xml/filepaths" /> android:resource="@xml/filepaths" />
</provider> </provider>
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<activity android:name="net.hockeyapp.android.UpdateActivity" android:exported="false" android:icon="@drawable/icon" /> <activity android:name="net.hockeyapp.android.UpdateActivity" android:exported="false" android:icon="@drawable/icon" />
</application> </application>
</manifest> </manifest>

File diff suppressed because it is too large Load diff

View file

@ -1,560 +1,38 @@
using System; using System;
using System.Collections.Generic; using Bit.App;
using Android.App;
using Android.Content;
using Android.OS;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Utilities; using Plugin.Settings.Abstractions;
using System.Threading;
using Xamarin.Forms; using Xamarin.Forms;
using Android.Gms.Gcm.Iid;
using Android.Gms.Gcm;
using Java.IO;
using Newtonsoft.Json.Linq;
using Android.Support.V4.App;
using Android.Media;
using Newtonsoft.Json;
namespace Bit.Android.Services namespace Bit.Android.Services
{ {
public class AndroidPushNotificationService : IPushNotificationService public class AndroidPushNotificationService : IPushNotificationService
{ {
private const string GcmPreferencesKey = "GCMPreferences"; private readonly IPushNotificationListener _pushNotificationListener;
private const string Tag = "PushNotification"; private readonly ISettings _settings;
internal static IPushNotificationListener Listener { get; set; } public AndroidPushNotificationService(
public string Token => GetRegistrationId(); IPushNotificationListener pushNotificationListener,
ISettings settings)
{
_pushNotificationListener = pushNotificationListener;
_settings = settings;
}
public string Token => _settings.GetValueOrDefault(Constants.PushCurrentToken, null);
public void Register() public void Register()
{ {
var registeredToken = _settings.GetValueOrDefault(Constants.PushRegisteredToken, null);
System.Diagnostics.Debug.WriteLine( if(!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != Token)
$"{PushNotificationContants.DomainName} - Register - Registering push notifications");
if(string.IsNullOrEmpty(CrossPushNotification.SenderId))
{ {
System.Diagnostics.Debug.WriteLine( _pushNotificationListener.OnRegistered(registeredToken, Device.Android);
$"{PushNotificationContants.DomainName} - Register - SenderId is missing.");
CrossPushNotification.PushNotificationListener.OnError(
$"{PushNotificationContants.DomainName} - Register - Sender Id is missing.", Device.Android);
}
else
{
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationContants.DomainName} - Register - Registering for Push Notifications");
ThreadPool.QueueUserWorkItem(state =>
{
try
{
Intent intent = new Intent(global::Android.App.Application.Context,
typeof(PushNotificationRegistrationIntentService));
global::Android.App.Application.Context.StartService(intent);
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine($"{Tag} - Error : {ex.Message}");
CrossPushNotification.PushNotificationListener.OnError($"{Tag} - Register - {ex.Message}",
Device.Android);
}
});
} }
} }
public void Unregister() public void Unregister()
{ {
ThreadPool.QueueUserWorkItem(state => // Do we ever need to unregister?
{
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationContants.DomainName} - Unregister - Unregistering push notifications");
try
{
var instanceID = InstanceID.GetInstance(global::Android.App.Application.Context);
instanceID.DeleteToken(CrossPushNotification.SenderId, GoogleCloudMessaging.InstanceIdScope);
CrossPushNotification.PushNotificationListener.OnUnregistered(Device.Android);
StoreRegistrationId(global::Android.App.Application.Context, string.Empty);
}
catch(IOException ex)
{
System.Diagnostics.Debug.WriteLine($"{Tag} - Error : {ex.Message}");
CrossPushNotification.PushNotificationListener.OnError(
$"{Tag} - Unregister - {ex.Message}", Device.Android);
}
});
}
private string GetRegistrationId()
{
var context = global::Android.App.Application.Context;
var prefs = GetGCMPreferences(context);
var registrationId = prefs.GetString(PushNotificationContants.Token, string.Empty);
if(string.IsNullOrEmpty(registrationId))
{
System.Diagnostics.Debug.WriteLine($"{PushNotificationContants.DomainName} - Registration not found.");
return string.Empty;
}
// Check if app was updated; if so, it must clear the registration ID
// since the existing registration ID is not guaranteed to work with
// the new app version.
var registeredVersion = prefs.GetInt(PushNotificationContants.AppVersion, Java.Lang.Integer.MinValue);
var currentVersion = GetAppVersion(context);
if(registeredVersion != currentVersion)
{
System.Diagnostics.Debug.WriteLine($"{PushNotificationContants.DomainName} - App version changed.");
return string.Empty;
}
return registrationId;
}
internal static ISharedPreferences GetGCMPreferences(Context context)
{
return context.GetSharedPreferences(GcmPreferencesKey, FileCreationMode.Private);
}
internal static int GetAppVersion(Context context)
{
try
{
var packageInfo = context.PackageManager.GetPackageInfo(context.PackageName, 0);
return packageInfo.VersionCode;
}
catch(global::Android.Content.PM.PackageManager.NameNotFoundException e)
{
// should never happen
throw new Java.Lang.RuntimeException("Could not get package name: " + e);
}
}
internal static void StoreRegistrationId(Context context, string regId)
{
var prefs = GetGCMPreferences(context);
var appVersion = GetAppVersion(context);
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationContants.DomainName} - Saving token on app version {appVersion}");
var editor = prefs.Edit();
editor.PutString(PushNotificationContants.Token, regId);
editor.PutInt(PushNotificationContants.AppVersion, appVersion);
editor.Commit();
}
}
[Service(Exported = false)]
public class PushNotificationRegistrationIntentService : IntentService
{
private const string Tag = "PushNotificationRegistationIntentService";
private string[] _topics = new string[] { "global" };
private readonly object _syncLock = new object();
protected override void OnHandleIntent(Intent intent)
{
try
{
var extras = intent.Extras;
lock(_syncLock)
{
var instanceID = InstanceID.GetInstance(global::Android.App.Application.Context);
var token = instanceID.GetToken(CrossPushNotification.SenderId,
GoogleCloudMessaging.InstanceIdScope, null);
CrossPushNotification.PushNotificationListener.OnRegistered(token, Device.Android);
AndroidPushNotificationService.StoreRegistrationId(global::Android.App.Application.Context, token);
SubscribeTopics(token);
System.Diagnostics.Debug.WriteLine($"{token} - Device registered, registration ID={Tag}");
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine($"{ex.Message} - Error : {Tag}");
CrossPushNotification.PushNotificationListener.OnError(
$"{ex.ToString()} - Register - {Tag}", Device.Android);
}
}
private void SubscribeTopics(string token)
{
var pubSub = GcmPubSub.GetInstance(this);
foreach(var topic in _topics)
{
pubSub.Subscribe(token, "/topics/" + topic, null);
}
}
}
[Service(Exported = false)]
[IntentFilter(new string[] { "com.google.android.gms.iid.InstanceID" })]
public class PushNotificationInstanceIDListenerService : InstanceIDListenerService
{
private const string Tag = "PushNotificationInstanceIDLS";
public override void OnTokenRefresh()
{
base.OnTokenRefresh();
ThreadPool.QueueUserWorkItem(state =>
{
try
{
var intent = new Intent(global::Android.App.Application.Context,
typeof(PushNotificationRegistrationIntentService));
global::Android.App.Application.Context.StartService(intent);
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine($"{ex.Message} - Error : {Tag}");
CrossPushNotification.PushNotificationListener.OnError(
$"{ex.ToString()} - Register - {Tag}", Device.Android);
}
});
}
}
[Service(Exported = false, Name = "pushnotification.plugin.PushNotificationGcmListener")]
[IntentFilter(new string[] { "com.google.android.c2dm.intent.RECEIVE" },
Categories = new string[] { "com.x8bit.bitwarden" })]
public class PushNotificationGcmListener : GcmListenerService
{
public override void OnMessageReceived(string from, Bundle extras)
{
if(extras != null && !extras.IsEmpty)
{
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationContants.DomainName} - GCM Listener - Push Received");
var parameters = new Dictionary<string, object>();
var values = new JObject();
foreach(var key in extras.KeySet())
{
var value = extras.Get(key).ToString();
if(ValidateJSON(value))
{
values.Add(key, JObject.Parse(value));
}
else
{
values.Add(key, value);
}
parameters.Add(key, extras.Get(key));
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationContants.DomainName} - GCM Listener - Push Params {key} : {extras.Get(key)}");
}
var context = global::Android.App.Application.Context;
CrossPushNotification.PushNotificationListener.OnMessage(values, Device.Android);
try
{
var notifyId = 0;
var title = context.ApplicationInfo.LoadLabel(context.PackageManager);
var message = string.Empty;
var tag = string.Empty;
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTextKey) &&
parameters.ContainsKey(CrossPushNotification.NotificationContentTextKey))
{
message = parameters[CrossPushNotification.NotificationContentTextKey].ToString();
}
else if(parameters.ContainsKey(PushNotificationContants.Alert))
{
message = parameters[PushNotificationContants.Alert].ToString();
}
else if(parameters.ContainsKey(PushNotificationContants.Message))
{
message = parameters[PushNotificationContants.Message].ToString();
}
else if(parameters.ContainsKey(PushNotificationContants.Subtitle))
{
message = parameters[PushNotificationContants.Subtitle].ToString();
}
else if(parameters.ContainsKey(PushNotificationContants.Text))
{
message = parameters[PushNotificationContants.Text].ToString();
}
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTitleKey) &&
parameters.ContainsKey(CrossPushNotification.NotificationContentTitleKey))
{
title = parameters[CrossPushNotification.NotificationContentTitleKey].ToString();
}
else if(parameters.ContainsKey(PushNotificationContants.Title))
{
if(!string.IsNullOrEmpty(message))
{
title = parameters[PushNotificationContants.Title].ToString();
}
else
{
message = parameters[PushNotificationContants.Title].ToString();
}
}
if(string.IsNullOrEmpty(message))
{
var data = (
!string.IsNullOrEmpty(CrossPushNotification.NotificationContentDataKey) &&
values[CrossPushNotification.NotificationContentDataKey] != null) ?
values[CrossPushNotification.NotificationContentDataKey] :
values[PushNotificationContants.Data];
if(data != null)
{
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTextKey) &&
data[CrossPushNotification.NotificationContentTextKey] != null)
{
message = data[CrossPushNotification.NotificationContentTextKey].ToString();
}
else if(data[PushNotificationContants.Alert] != null)
{
message = data[PushNotificationContants.Alert].ToString();
}
else if(data[PushNotificationContants.Message] != null)
{
message = data[PushNotificationContants.Message].ToString();
}
else if(data[PushNotificationContants.Subtitle] != null)
{
message = data[PushNotificationContants.Subtitle].ToString();
}
else if(data[PushNotificationContants.Text] != null)
{
message = data[PushNotificationContants.Text].ToString();
}
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTitleKey) &&
data[CrossPushNotification.NotificationContentTitleKey] != null)
{
title = data[CrossPushNotification.NotificationContentTitleKey].ToString();
}
else if(data[PushNotificationContants.Title] != null)
{
if(!string.IsNullOrEmpty(message))
{
title = data[PushNotificationContants.Title].ToString();
}
else
{
message = data[PushNotificationContants.Title].ToString();
}
}
}
}
if(parameters.ContainsKey(PushNotificationContants.Id))
{
var str = parameters[PushNotificationContants.Id].ToString();
try
{
notifyId = Convert.ToInt32(str);
}
catch(Exception)
{
// Keep the default value of zero for the notify_id, but log the conversion problem.
System.Diagnostics.Debug.WriteLine("Failed to convert {0} to an integer", str);
}
}
if(parameters.ContainsKey(PushNotificationContants.Tag))
{
tag = parameters[PushNotificationContants.Tag].ToString();
}
if(!parameters.ContainsKey(PushNotificationContants.Silent) ||
!System.Boolean.Parse(parameters[PushNotificationContants.Silent].ToString()))
{
if(CrossPushNotification.PushNotificationListener.ShouldShowNotification())
{
CreateNotification(title, message, notifyId, tag, extras);
}
}
}
catch(Java.Lang.Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
catch(Exception ex1)
{
System.Diagnostics.Debug.WriteLine(ex1.ToString());
}
}
}
private void CreateNotification(string title, string message, int notifyId, string tag, Bundle extras)
{
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationContants.DomainName} - PushNotification - Message {title} : {message}");
NotificationCompat.Builder builder = null;
var context = global::Android.App.Application.Context;
if(CrossPushNotification.SoundUri == null)
{
CrossPushNotification.SoundUri = RingtoneManager.GetDefaultUri(RingtoneType.Notification);
}
try
{
if(CrossPushNotification.IconResource == 0)
{
CrossPushNotification.IconResource = context.ApplicationInfo.Icon;
}
else
{
var name = context.Resources.GetResourceName(CrossPushNotification.IconResource);
if(name == null)
{
CrossPushNotification.IconResource = context.ApplicationInfo.Icon;
}
}
}
catch(global::Android.Content.Res.Resources.NotFoundException ex)
{
CrossPushNotification.IconResource = context.ApplicationInfo.Icon;
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
var resultIntent = context.PackageManager.GetLaunchIntentForPackage(context.PackageName);
if(extras != null)
{
resultIntent.PutExtras(extras);
}
// Create a PendingIntent; we're only using one PendingIntent (ID = 0):
const int pendingIntentId = 0;
var resultPendingIntent = PendingIntent.GetActivity(context, pendingIntentId,
resultIntent, PendingIntentFlags.OneShot);
// Build the notification
builder = new NotificationCompat.Builder(context)
.SetAutoCancel(true) // dismiss the notification from the notification area when the user clicks on it
.SetContentIntent(resultPendingIntent) // start up this activity when the user clicks the intent.
.SetContentTitle(title) // Set the title
.SetSound(CrossPushNotification.SoundUri)
.SetSmallIcon(CrossPushNotification.IconResource) // This is the icon to display
.SetContentText(message); // the message to display.
if(Build.VERSION.SdkInt >= BuildVersionCodes.JellyBean)
{
// Using BigText notification style to support long message
var style = new NotificationCompat.BigTextStyle();
style.BigText(message);
builder.SetStyle(style);
}
var notificationManager = (NotificationManager)context.GetSystemService(NotificationService);
notificationManager.Notify(tag, notifyId, builder.Build());
}
private static bool ValidateJSON(string s)
{
try
{
JObject.Parse(s);
return true;
}
catch(JsonReaderException ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
return false;
}
}
}
[BroadcastReceiver(Exported = true, Permission = "com.google.android.c2dm.permission.SEND")]
[IntentFilter(new string[] { "com.google.android.c2dm.intent.RECEIVE" },
Categories = new string[] { "com.x8bit.bitwarden" })]
public class PushNotificationsReceiver : GcmReceiver
{ }
[Service]
public class AndroidPushService : Service
{
public override void OnCreate()
{
base.OnCreate();
System.Diagnostics.Debug.WriteLine("Push Notification Service - Created");
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
System.Diagnostics.Debug.WriteLine("Push Notification Service - Started");
return StartCommandResult.Sticky;
}
public override IBinder OnBind(Intent intent)
{
System.Diagnostics.Debug.WriteLine("Push Notification Service - Binded");
return null;
}
public override void OnDestroy()
{
System.Diagnostics.Debug.WriteLine("Push Notification Service - Destroyed");
base.OnDestroy();
}
}
internal class CrossPushNotification
{
private static Lazy<IPushNotificationService> Implementation = new Lazy<IPushNotificationService>(
() => new AndroidPushNotificationService(),
LazyThreadSafetyMode.PublicationOnly);
public static bool IsInitialized => PushNotificationListener != null;
public static IPushNotificationListener PushNotificationListener { get; private set; }
public static string SenderId { get; set; }
public static string NotificationContentTitleKey { get; set; }
public static string NotificationContentTextKey { get; set; }
public static string NotificationContentDataKey { get; set; }
public static int IconResource { get; set; }
public static global::Android.Net.Uri SoundUri { get; set; }
public static void Initialize<T>(T listener, string senderId) where T : IPushNotificationListener
{
SenderId = senderId;
if(PushNotificationListener == null)
{
PushNotificationListener = listener;
System.Diagnostics.Debug.WriteLine("PushNotification plugin initialized.");
}
else
{
System.Diagnostics.Debug.WriteLine("PushNotification plugin already initialized.");
}
}
public static void Initialize<T>(string senderId) where T : IPushNotificationListener, new()
{
Initialize(new T(), senderId);
}
public static IPushNotificationService Current
{
get
{
if(!IsInitialized)
{
throw new Exception("Not initialized.");
}
var ret = Implementation.Value;
if(ret == null)
{
throw new Exception("Not in PCL");
}
return ret;
}
} }
} }
} }

View file

@ -0,0 +1,42 @@
{
"project_info": {
"project_number": "1093287226212",
"firebase_url": "https://bitwarden-dev.firebaseio.com",
"project_id": "bitwarden-dev",
"storage_bucket": "bitwarden-dev.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:1093287226212:android:f8d67b786db1b844",
"android_client_info": {
"package_name": "com.x8bit.bitwarden"
}
},
"oauth_client": [
{
"client_id": "1093287226212-m4mv8ho387tdgosc9lsltnmruul7ouo0.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyA4Xkn0do7Ky_OLff2L_7MXeNK6s-JVgXg"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

Binary file not shown.

View file

@ -26,7 +26,7 @@
<PackageReference Include="Xam.Plugin.Connectivity" Version="3.0.3" /> <PackageReference Include="Xam.Plugin.Connectivity" Version="3.0.3" />
<PackageReference Include="Xam.Plugins.Settings" Version="3.1.1" /> <PackageReference Include="Xam.Plugins.Settings" Version="3.1.1" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.2.9" /> <PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.2.9" />
<PackageReference Include="Xamarin.Forms" Version="2.4.0.91020" /> <PackageReference Include="Xamarin.Forms" Version="2.5.0.121934" />
<PackageReference Include="XLabs.IoC" Version="2.0.5782" /> <PackageReference Include="XLabs.IoC" Version="2.0.5782" />
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.1.47" /> <PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.1.47" />
</ItemGroup> </ItemGroup>

View file

@ -25,6 +25,8 @@
public const string PushInitialPromptShown = "push:initialPromptShown"; public const string PushInitialPromptShown = "push:initialPromptShown";
public const string PushLastRegistrationDate = "push:lastRegistrationDate"; public const string PushLastRegistrationDate = "push:lastRegistrationDate";
public const string PushCurrentToken = "push:currentToken";
public const string PushRegisteredToken = "push:registeredToken";
public const string ExtensionStarted = "extension:started"; public const string ExtensionStarted = "extension:started";
public const string ExtensionActivated = "extension:activated"; public const string ExtensionActivated = "extension:activated";

View file

@ -6,6 +6,7 @@ using Bit.App.Models;
using Plugin.Settings.Abstractions; using Plugin.Settings.Abstractions;
using System; using System;
using XLabs.Ioc; using XLabs.Ioc;
using Xamarin.Forms;
namespace Bit.App.Services namespace Bit.App.Services
{ {
@ -52,13 +53,21 @@ namespace Bit.App.Services
return; return;
} }
JToken dataToken; PushNotificationDataPayload data = null;
if(!value.TryGetValue("data", StringComparison.OrdinalIgnoreCase, out dataToken) || dataToken == null) if(deviceType == Device.Android)
{
data = value.ToObject<PushNotificationDataPayload>();
}
else
{
if(!value.TryGetValue("data", StringComparison.OrdinalIgnoreCase, out JToken dataToken) ||
dataToken == null)
{ {
return; return;
} }
data = dataToken.ToObject<PushNotificationDataPayload>();
}
var data = dataToken.ToObject<PushNotificationDataPayload>();
if(data?.Payload == null) if(data?.Payload == null)
{ {
return; return;
@ -159,6 +168,10 @@ namespace Bit.App.Services
{ {
Debug.WriteLine("Registered device with server."); Debug.WriteLine("Registered device with server.");
_settings.AddOrUpdateValue(Constants.PushLastRegistrationDate, DateTime.UtcNow); _settings.AddOrUpdateValue(Constants.PushLastRegistrationDate, DateTime.UtcNow);
if(deviceType == Device.Android)
{
_settings.AddOrUpdateValue(Constants.PushCurrentToken, token);
}
} }
else else
{ {