mirror of
https://github.com/bitwarden/android.git
synced 2024-11-01 07:35:52 +03:00
switch from GCM to FCM
This commit is contained in:
parent
2b4ffaa357
commit
582e6ee322
12 changed files with 4875 additions and 4406 deletions
|
@ -81,6 +81,8 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="AutofillActivity.cs" />
|
||||
<Compile Include="AutofillCredentials.cs" />
|
||||
<Compile Include="FirebaseInstanceIdService.cs" />
|
||||
<Compile Include="FirebaseMessagingService.cs" />
|
||||
<Compile Include="Autofill\Field.cs" />
|
||||
<Compile Include="Autofill\FieldCollection.cs" />
|
||||
<Compile Include="Autofill\AutofillService.cs" />
|
||||
|
@ -103,13 +105,14 @@
|
|||
<Compile Include="Controls\ExtendedPickerRenderer.cs" />
|
||||
<Compile Include="Controls\ExtendedEntryRenderer.cs" />
|
||||
<Compile Include="MyVaultTileService.cs" />
|
||||
<Compile Include="Services\AndroidPushNotificationService.cs" />
|
||||
<Compile Include="Services\GoogleAnalyticsService.cs" />
|
||||
<Compile Include="Services\HttpService.cs" />
|
||||
<Compile Include="Services\AndroidKeyStoreStorageService.cs" />
|
||||
<Compile Include="Services\LocalizeService.cs" />
|
||||
<Compile Include="MainApplication.cs" />
|
||||
<Compile Include="Resources\Resource.Designer.cs" />
|
||||
<Compile Include="Services\DeviceInfoService.cs" />
|
||||
<Compile Include="Services\GoogleAnalyticsService.cs" />
|
||||
<Compile Include="Services\AppInfoService.cs" />
|
||||
<Compile Include="Services\DeviceActionService.cs" />
|
||||
<Compile Include="Services\BouncyCastleKeyDerivationService.cs" />
|
||||
|
@ -117,7 +120,6 @@
|
|||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\LogService.cs" />
|
||||
<Compile Include="Services\MemoryService.cs" />
|
||||
<Compile Include="Services\AndroidPushNotificationService.cs" />
|
||||
<Compile Include="Services\ReflectionService.cs" />
|
||||
<Compile Include="Services\SqlService.cs" />
|
||||
<Compile Include="SplashActivity.cs" />
|
||||
|
@ -127,6 +129,8 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="8bit.keystore.enc" />
|
||||
<GoogleServicesJson Include="google-services.json" />
|
||||
<None Include="google-services.json.enc" />
|
||||
<None Include="increment-version.ps1" />
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
<None Include="Assets\AboutAssets.txt" />
|
||||
|
@ -891,11 +895,11 @@
|
|||
<PackageReference Include="SimpleInjector">
|
||||
<Version>4.0.12</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Analytics">
|
||||
<Version>29.0.0.2</Version>
|
||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||
<Version>42.1021.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Gcm">
|
||||
<Version>29.0.0.2</Version>
|
||||
<PackageReference Include="Xamarin.GooglePlayServices.Analytics">
|
||||
<Version>42.1021.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="XLabs.IoC.SimpleInjector" Version="2.0.5782" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.1.3" />
|
||||
|
|
23
src/Android/FirebaseInstanceIdService.cs
Normal file
23
src/Android/FirebaseInstanceIdService.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
42
src/Android/FirebaseMessagingService.cs
Normal file
42
src/Android/FirebaseMessagingService.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -61,55 +61,6 @@ namespace Bit.Android
|
|||
|
||||
RegisterActivityLifecycleCallbacks(this);
|
||||
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()
|
||||
|
@ -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)
|
||||
{
|
||||
UserDialogs.Init(application);
|
||||
|
@ -237,10 +164,8 @@ namespace Bit.Android
|
|||
container.RegisterSingleton(CrossFingerprint.Current);
|
||||
|
||||
// Push
|
||||
var pushListener = new PushNotificationListener();
|
||||
container.RegisterSingleton<IPushNotificationListener>(pushListener);
|
||||
CrossPushNotification.Initialize(pushListener, "962181367620");
|
||||
container.RegisterSingleton(CrossPushNotification.Current);
|
||||
container.RegisterSingleton<IPushNotificationListener, PushNotificationListener>();
|
||||
container.RegisterSingleton<IPushNotificationService, AndroidPushNotificationService>();
|
||||
|
||||
container.Verify();
|
||||
Resolver.SetResolver(new SimpleInjectorResolver(container));
|
||||
|
|
|
@ -8,11 +8,6 @@
|
|||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<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="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.autofocus" android:required="false" />
|
||||
|
@ -28,6 +23,17 @@
|
|||
android:resource="@xml/filepaths" />
|
||||
</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" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
8482
src/Android/Resources/Resource.Designer.cs
generated
8482
src/Android/Resources/Resource.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -1,560 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Bit.App;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Utilities;
|
||||
using System.Threading;
|
||||
using Plugin.Settings.Abstractions;
|
||||
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
|
||||
{
|
||||
public class AndroidPushNotificationService : IPushNotificationService
|
||||
{
|
||||
private const string GcmPreferencesKey = "GCMPreferences";
|
||||
private const string Tag = "PushNotification";
|
||||
private readonly IPushNotificationListener _pushNotificationListener;
|
||||
private readonly ISettings _settings;
|
||||
|
||||
internal static IPushNotificationListener Listener { get; set; }
|
||||
public string Token => GetRegistrationId();
|
||||
public AndroidPushNotificationService(
|
||||
IPushNotificationListener pushNotificationListener,
|
||||
ISettings settings)
|
||||
{
|
||||
_pushNotificationListener = pushNotificationListener;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public string Token => _settings.GetValueOrDefault(Constants.PushCurrentToken, null);
|
||||
|
||||
public void Register()
|
||||
{
|
||||
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
$"{PushNotificationContants.DomainName} - Register - Registering push notifications");
|
||||
|
||||
if(string.IsNullOrEmpty(CrossPushNotification.SenderId))
|
||||
var registeredToken = _settings.GetValueOrDefault(Constants.PushRegisteredToken, null);
|
||||
if(!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != Token)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
$"{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);
|
||||
}
|
||||
});
|
||||
_pushNotificationListener.OnRegistered(registeredToken, Device.Android);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unregister()
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(state =>
|
||||
{
|
||||
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;
|
||||
}
|
||||
// Do we ever need to unregister?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
42
src/Android/google-services.json
Normal file
42
src/Android/google-services.json
Normal 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"
|
||||
}
|
BIN
src/Android/google-services.json.enc
Normal file
BIN
src/Android/google-services.json.enc
Normal file
Binary file not shown.
|
@ -26,7 +26,7 @@
|
|||
<PackageReference Include="Xam.Plugin.Connectivity" Version="3.0.3" />
|
||||
<PackageReference Include="Xam.Plugins.Settings" Version="3.1.1" />
|
||||
<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="ZXing.Net.Mobile.Forms" Version="2.1.47" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
public const string PushInitialPromptShown = "push:initialPromptShown";
|
||||
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 ExtensionActivated = "extension:activated";
|
||||
|
|
|
@ -6,6 +6,7 @@ using Bit.App.Models;
|
|||
using Plugin.Settings.Abstractions;
|
||||
using System;
|
||||
using XLabs.Ioc;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Services
|
||||
{
|
||||
|
@ -52,13 +53,21 @@ namespace Bit.App.Services
|
|||
return;
|
||||
}
|
||||
|
||||
JToken dataToken;
|
||||
if(!value.TryGetValue("data", StringComparison.OrdinalIgnoreCase, out dataToken) || dataToken == null)
|
||||
PushNotificationDataPayload data = null;
|
||||
if(deviceType == Device.Android)
|
||||
{
|
||||
return;
|
||||
data = value.ToObject<PushNotificationDataPayload>();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!value.TryGetValue("data", StringComparison.OrdinalIgnoreCase, out JToken dataToken) ||
|
||||
dataToken == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
data = dataToken.ToObject<PushNotificationDataPayload>();
|
||||
}
|
||||
|
||||
var data = dataToken.ToObject<PushNotificationDataPayload>();
|
||||
if(data?.Payload == null)
|
||||
{
|
||||
return;
|
||||
|
@ -159,6 +168,10 @@ namespace Bit.App.Services
|
|||
{
|
||||
Debug.WriteLine("Registered device with server.");
|
||||
_settings.AddOrUpdateValue(Constants.PushLastRegistrationDate, DateTime.UtcNow);
|
||||
if(deviceType == Device.Android)
|
||||
{
|
||||
_settings.AddOrUpdateValue(Constants.PushCurrentToken, token);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue