local push notification implementation from lib

This commit is contained in:
Kyle Spearrin 2017-10-09 23:45:23 -04:00
parent d5da1d6f3f
commit 7c6cc7b246
23 changed files with 851 additions and 59 deletions

View file

@ -164,14 +164,6 @@
<Reference Include="Plugin.Settings.Abstractions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.3.0.1\lib\MonoAndroid10\Plugin.Settings.Abstractions.dll</HintPath>
</Reference>
<Reference Include="PushNotification.Plugin, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.PushNotification.1.2.4\lib\MonoAndroid10\PushNotification.Plugin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PushNotification.Plugin.Abstractions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.PushNotification.1.2.4\lib\MonoAndroid10\PushNotification.Plugin.Abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\..\packages\SimpleInjector.4.0.8\lib\netstandard1.3\SimpleInjector.dll</HintPath>
</Reference>
@ -325,6 +317,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\LogService.cs" />
<Compile Include="Services\MemoryService.cs" />
<Compile Include="Services\PushNotificationImplementation.cs" />
<Compile Include="Services\ReflectionService.cs" />
<Compile Include="Services\SqlService.cs" />
<Compile Include="SplashActivity.cs" />

View file

@ -12,8 +12,6 @@ using Plugin.Connectivity;
using Plugin.CurrentActivity;
using Plugin.Fingerprint;
using Plugin.Settings;
using PushNotification.Plugin;
using PushNotification.Plugin.Abstractions;
using XLabs.Ioc;
using System.Threading.Tasks;
using Plugin.Settings.Abstractions;

View file

@ -0,0 +1,564 @@
using System;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.OS;
using Bit.App.Abstractions;
using Bit.App.Utilities;
using System.Threading;
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 PushNotificationImplementation : IPushNotification
{
private const string GcmPreferencesKey = "GCMPreferences";
private const string Tag = "PushNotification";
internal static IPushNotificationListener Listener { get; set; }
public string Token => GetRegistrationId();
public void Register()
{
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationKey.DomainName} - Register - Registering push notifications");
if(string.IsNullOrEmpty(CrossPushNotification.SenderId))
{
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationKey.DomainName} - Register - SenderId is missing.");
CrossPushNotification.PushNotificationListener.OnError(
$"{PushNotificationKey.DomainName} - Register - Sender Id is missing.", Device.Android);
}
else
{
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationKey.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()
{
ThreadPool.QueueUserWorkItem(state =>
{
System.Diagnostics.Debug.WriteLine(
$"{PushNotificationKey.DomainName} - Unregister - Unregistering push notifications");
try
{
InstanceID 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 retVal = string.Empty;
var context = global::Android.App.Application.Context;
var prefs = GetGCMPreferences(context);
var registrationId = prefs.GetString(PushNotificationKey.Token, string.Empty);
if(string.IsNullOrEmpty(registrationId))
{
System.Diagnostics.Debug.WriteLine($"{PushNotificationKey.DomainName} - Registration not found.");
return retVal;
}
// 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(PushNotificationKey.AppVersion, Java.Lang.Integer.MinValue);
var currentVersion = GetAppVersion(context);
if(registeredVersion != currentVersion)
{
System.Diagnostics.Debug.WriteLine($"{PushNotificationKey.DomainName} - App version changed.");
return retVal;
}
retVal = registrationId;
return retVal;
}
internal static ISharedPreferences GetGCMPreferences(Context context)
{
// This sample app persists the registration ID in shared preferences, but
// how you store the registration ID in your app is up to you.
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(
$"{PushNotificationKey.DomainName} - Saving token on app version {appVersion}");
var editor = prefs.Edit();
editor.PutString(PushNotificationKey.Token, regId);
editor.PutInt(PushNotificationKey.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);
PushNotificationImplementation.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(
$"{PushNotificationKey.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(
$"{PushNotificationKey.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(PushNotificationKey.Alert))
{
message = parameters[PushNotificationKey.Alert].ToString();
}
else if(parameters.ContainsKey(PushNotificationKey.Message))
{
message = parameters[PushNotificationKey.Message].ToString();
}
else if(parameters.ContainsKey(PushNotificationKey.Subtitle))
{
message = parameters[PushNotificationKey.Subtitle].ToString();
}
else if(parameters.ContainsKey(PushNotificationKey.Text))
{
message = parameters[PushNotificationKey.Text].ToString();
}
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTitleKey) &&
parameters.ContainsKey(CrossPushNotification.NotificationContentTitleKey))
{
title = parameters[CrossPushNotification.NotificationContentTitleKey].ToString();
}
else if(parameters.ContainsKey(PushNotificationKey.Title))
{
if(!string.IsNullOrEmpty(message))
{
title = parameters[PushNotificationKey.Title].ToString();
}
else
{
message = parameters[PushNotificationKey.Title].ToString();
}
}
if(string.IsNullOrEmpty(message))
{
var data = (
!string.IsNullOrEmpty(CrossPushNotification.NotificationContentDataKey) &&
values[CrossPushNotification.NotificationContentDataKey] != null) ?
values[CrossPushNotification.NotificationContentDataKey] :
values[PushNotificationKey.Data];
if(data != null)
{
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTextKey) &&
data[CrossPushNotification.NotificationContentTextKey] != null)
{
message = data[CrossPushNotification.NotificationContentTextKey].ToString();
}
else if(data[PushNotificationKey.Alert] != null)
{
message = data[PushNotificationKey.Alert].ToString();
}
else if(data[PushNotificationKey.Message] != null)
{
message = data[PushNotificationKey.Message].ToString();
}
else if(data[PushNotificationKey.Subtitle] != null)
{
message = data[PushNotificationKey.Subtitle].ToString();
}
else if(data[PushNotificationKey.Text] != null)
{
message = data[PushNotificationKey.Text].ToString();
}
if(!string.IsNullOrEmpty(CrossPushNotification.NotificationContentTitleKey) &&
data[CrossPushNotification.NotificationContentTitleKey] != null)
{
title = data[CrossPushNotification.NotificationContentTitleKey].ToString();
}
else if(data[PushNotificationKey.Title] != null)
{
if(!string.IsNullOrEmpty(message))
{
title = data[PushNotificationKey.Title].ToString();
}
else
{
message = data[PushNotificationKey.Title].ToString();
}
}
}
}
if(parameters.ContainsKey(PushNotificationKey.Id))
{
var str = parameters[PushNotificationKey.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(PushNotificationKey.Tag))
{
tag = parameters[PushNotificationKey.Tag].ToString();
}
if(!parameters.ContainsKey(PushNotificationKey.Silent) ||
!System.Boolean.Parse(parameters[PushNotificationKey.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(
$"{PushNotificationKey.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 PushNotificationService : 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<IPushNotification> Implementation = new Lazy<IPushNotification>(
() => new PushNotificationImplementation(),
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 IPushNotification 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

@ -69,7 +69,6 @@
<package id="System.Xml.XDocument" version="4.0.11" targetFramework="monoandroid71" />
<package id="Validation" version="2.3.7" targetFramework="monoandroid60" />
<package id="Xam.Plugin.Connectivity" version="3.0.2" targetFramework="monoandroid71" />
<package id="Xam.Plugin.PushNotification" version="1.2.4" targetFramework="monoandroid60" developmentDependency="true" />
<package id="Xam.Plugins.Settings" version="3.0.1" targetFramework="monoandroid71" />
<package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="23.3.0" targetFramework="monoandroid60" />
<package id="Xamarin.Android.Support.Design" version="23.3.0" targetFramework="monoandroid60" />

View file

@ -0,0 +1,9 @@
namespace Bit.App.Abstractions
{
public interface IPushNotification
{
string Token { get; }
void Register();
void Unregister();
}
}

View file

@ -0,0 +1,13 @@
using Newtonsoft.Json.Linq;
namespace Bit.App.Abstractions
{
public interface IPushNotificationListener
{
void OnMessage(JObject values, string device);
void OnRegistered(string token, string device);
void OnUnregistered(string device);
void OnError(string message, string device);
bool ShouldShowNotification();
}
}

View file

@ -44,6 +44,8 @@
<Compile Include="Abstractions\Repositories\ISettingsRepository.cs" />
<Compile Include="Abstractions\Services\IAppSettingsService.cs" />
<Compile Include="Abstractions\Services\IMemoryService.cs" />
<Compile Include="Abstractions\Services\IPushNotificationListener.cs" />
<Compile Include="Abstractions\Services\IPushNotification.cs" />
<Compile Include="Abstractions\Services\ISettingsService.cs" />
<Compile Include="Abstractions\Services\ITokenService.cs" />
<Compile Include="Abstractions\Services\IHttpService.cs" />
@ -87,6 +89,7 @@
<Compile Include="Controls\PinControl.cs" />
<Compile Include="Controls\VaultAttachmentsViewCell.cs" />
<Compile Include="Controls\VaultListViewCell.cs" />
<Compile Include="Enums\DeviceType.cs" />
<Compile Include="Enums\FieldType.cs" />
<Compile Include="Enums\TwoFactorProviderType.cs" />
<Compile Include="Enums\EncryptionType.cs" />
@ -355,6 +358,7 @@
<Compile Include="Utilities\Extentions.cs" />
<Compile Include="Utilities\ExtendedObservableCollection.cs" />
<Compile Include="Utilities\ApiHttpClient.cs" />
<Compile Include="Utilities\PushNotificationKey.cs" />
<Compile Include="Utilities\TokenHttpRequestMessage.cs" />
</ItemGroup>
<ItemGroup>
@ -528,14 +532,6 @@
<Reference Include="Plugin.Settings.Abstractions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.3.0.1\lib\netstandard1.0\Plugin.Settings.Abstractions.dll</HintPath>
</Reference>
<Reference Include="PushNotification.Plugin, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.PushNotification.1.2.4\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10\PushNotification.Plugin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PushNotification.Plugin.Abstractions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.PushNotification.1.2.4\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10+UAP10\PushNotification.Plugin.Abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Splat.1.6.2\lib\Portable-net45+win+wpa81+wp80\Splat.dll</HintPath>
<Private>True</Private>

View file

@ -0,0 +1,9 @@
namespace Bit.App.Enums
{
public enum DeviceType
{
Android = 0,
iOS = 1,
UWP = 16
}
}

View file

@ -1,5 +1,5 @@
using Bit.App.Abstractions;
using PushNotification.Plugin.Abstractions;
using Bit.App.Enums;
using Xamarin.Forms;
namespace Bit.App.Models.Api

View file

@ -1,5 +1,5 @@
using System;
using PushNotification.Plugin.Abstractions;
using Bit.App.Enums;
using System;
namespace Bit.App.Models.Api
{

View file

@ -7,7 +7,6 @@ using XLabs.Ioc;
using Acr.UserDialogs;
using System.Threading.Tasks;
using Plugin.Settings.Abstractions;
using PushNotification.Plugin.Abstractions;
using Bit.App.Utilities;
namespace Bit.App.Pages

View file

@ -6,7 +6,6 @@ using Xamarin.Forms;
using XLabs.Ioc;
using Acr.UserDialogs;
using System.Threading.Tasks;
using PushNotification.Plugin.Abstractions;
using Bit.App.Models;
using Bit.App.Utilities;
using Bit.App.Enums;

View file

@ -4,10 +4,7 @@ using Bit.App.Resources;
using Xamarin.Forms;
using XLabs.Ioc;
using Bit.App.Controls;
using Acr.UserDialogs;
using Plugin.Settings.Abstractions;
using Plugin.Fingerprint.Abstractions;
using PushNotification.Plugin.Abstractions;
namespace Bit.App.Pages
{

View file

@ -7,7 +7,6 @@ using Bit.App.Controls;
using Acr.UserDialogs;
using Plugin.Settings.Abstractions;
using Plugin.Fingerprint.Abstractions;
using PushNotification.Plugin.Abstractions;
using Bit.App.Utilities;
namespace Bit.App.Pages

View file

@ -9,7 +9,6 @@ using Bit.App.Resources;
using Xamarin.Forms;
using XLabs.Ioc;
using Bit.App.Utilities;
using PushNotification.Plugin.Abstractions;
using Plugin.Settings.Abstractions;
using Plugin.Connectivity.Abstractions;
using System.Collections.Generic;

View file

@ -1,6 +1,4 @@
using PushNotification.Plugin.Abstractions;
using System.Diagnostics;
using PushNotification.Plugin;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Bit.App.Abstractions;
@ -37,7 +35,7 @@ namespace Bit.App.Services
_resolved = true;
}
public void OnMessage(JObject value, DeviceType deviceType)
public void OnMessage(JObject value, string deviceType)
{
Resolve();
@ -144,7 +142,7 @@ namespace Bit.App.Services
}
}
public async void OnRegistered(string token, DeviceType deviceType)
public async void OnRegistered(string token, string deviceType)
{
Resolve();
@ -168,12 +166,12 @@ namespace Bit.App.Services
}
}
public void OnUnregistered(DeviceType deviceType)
public void OnUnregistered(string deviceType)
{
Debug.WriteLine("Push Notification - Device Unnregistered");
}
public void OnError(string message, DeviceType deviceType)
public void OnError(string message, string deviceType)
{
Debug.WriteLine(string.Format("Push notification error - {0}", message));
}

View file

@ -0,0 +1,78 @@
namespace Bit.App.Utilities
{
public static class PushNotificationKey
{
/// <summary>
/// Type
/// </summary>
public const string Type = "type";
/// <summary>
/// Error
/// </summary>
public const string Error = "error";
/// <summary>
/// Domain Name
/// </summary>
public const string DomainName = "CrossPushNotification";
/// <summary>
/// Title
/// </summary>
public const string Title = "title";
/// <summary>
/// Text
/// </summary>
public const string Text = "text";
/// <summary>
/// Subtitle
/// </summary>
public const string Subtitle = "subtitle";
/// <summary>
/// Message
/// </summary>
public const string Message = "message";
/// <summary>
/// Silent
/// </summary>
public const string Silent = "silent";
/// <summary>
/// Alert
/// </summary>
public const string Alert = "alert";
/// <summary>
/// Data
/// </summary>
public const string Data = "data";
/// <summary>
/// Token
/// </summary>
public const string Token = "token";
/// <summary>
/// App Version
/// </summary>
public const string AppVersion = "appVersion";
/// <summary>
/// IntentFromGcmMessage
/// </summary>
public const string IntentFromGcmMessage = "com.google.android.c2dm.intent.RECEIVE";
/// <summary>
/// BackOffMilliseconds
/// </summary>
public const string BackOffMilliseconds = "backoff_ms";
/// <summary>
/// ErrorServiceNotAvailable
/// </summary>
public const string ErrorServiceNotAvailable = "SERVICE_NOT_AVAILABLE";
/// <summary>
/// Tag
/// </summary>
public const string Tag = "tag";
/// <summary>
/// Id
/// </summary>
public const string Id = "id";
/// <summary>
/// RegistrationComplete
/// </summary>
public const string RegistrationComplete = "registrationComplete";
}
}

View file

@ -16,7 +16,6 @@
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="portable45-net45+win8+wpa81" />
<package id="Validation" version="2.3.7" targetFramework="portable45-net45+win8+wpa81" />
<package id="Xam.Plugin.Connectivity" version="3.0.2" targetFramework="portable45-net45+win8+wpa81" />
<package id="Xam.Plugin.PushNotification" version="1.2.4" targetFramework="portable45-net45+win8+wpa81" developmentDependency="true" />
<package id="Xam.Plugins.Settings" version="3.0.1" targetFramework="portable45-net45+win8+wpa81" />
<package id="Xamarin.FFImageLoading" version="2.2.9" targetFramework="portable45-net45+win8+wpa81" />
<package id="Xamarin.FFImageLoading.Forms" version="2.2.9" targetFramework="portable45-net45+win8+wpa81" />

View file

@ -170,9 +170,6 @@
<PackageReference Include="Xam.Plugin.Connectivity">
<Version>3.0.2</Version>
</PackageReference>
<PackageReference Include="Xam.Plugin.PushNotification">
<Version>1.2.4</Version>
</PackageReference>
<PackageReference Include="Xam.Plugins.Settings">
<Version>3.0.1</Version>
</PackageReference>

View file

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using XLabs.Ioc;
using Foundation;
using UIKit;
using Bit.App.Abstractions;
@ -16,7 +13,6 @@ using Plugin.Settings.Abstractions;
using System.Diagnostics;
using Xamarin.Forms;
using Bit.iOS.Core.Services;
using PushNotification.Plugin;
using Plugin.Connectivity.Abstractions;
using Bit.App.Pages;
using HockeyApp.iOS;
@ -81,14 +77,16 @@ namespace Bit.iOS
UINavigationBar.Appearance.ShadowImage = new UIImage();
UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
UIBarButtonItem.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).TintColor = primaryColor;
UIButton.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).SetTitleColor(primaryColor, UIControlState.Normal);
UIButton.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).SetTitleColor(primaryColor,
UIControlState.Normal);
UIButton.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).TintColor = primaryColor;
UIStepper.Appearance.TintColor = grayLight;
UISlider.Appearance.TintColor = primaryColor;
MessagingCenter.Subscribe<Xamarin.Forms.Application, ToolsExtensionPage>(Xamarin.Forms.Application.Current, "ShowAppExtension", (sender, page) =>
MessagingCenter.Subscribe<Xamarin.Forms.Application, ToolsExtensionPage>(
Xamarin.Forms.Application.Current, "ShowAppExtension", (sender, page) =>
{
var itemProvider = new NSItemProvider(new NSDictionary(), iOS.Core.Constants.UTTypeAppExtensionSetup);
var itemProvider = new NSItemProvider(new NSDictionary(), Core.Constants.UTTypeAppExtensionSetup);
var extensionItem = new NSExtensionItem();
extensionItem.Attachments = new NSItemProvider[] { itemProvider };
var activityViewController = new UIActivityViewController(new NSExtensionItem[] { extensionItem }, null);
@ -112,7 +110,8 @@ namespace Bit.iOS
UIApplication.SharedApplication.StatusBarHidden = false;
UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent;
MessagingCenter.Subscribe<Xamarin.Forms.Application, bool>(Xamarin.Forms.Application.Current, "ShowStatusBar", (sender, show) =>
MessagingCenter.Subscribe<Xamarin.Forms.Application, bool>(Xamarin.Forms.Application.Current,
"ShowStatusBar", (sender, show) =>
{
UIApplication.SharedApplication.SetStatusBarHidden(!show, false);
});
@ -196,7 +195,8 @@ namespace Bit.iOS
Debug.WriteLine("WillEnterForeground");
}
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication,
NSObject annotation)
{
return true;
}
@ -217,12 +217,14 @@ namespace Bit.iOS
}
}
public override void DidRegisterUserNotificationSettings(UIApplication application, UIUserNotificationSettings notificationSettings)
public override void DidRegisterUserNotificationSettings(UIApplication application,
UIUserNotificationSettings notificationSettings)
{
application.RegisterForRemoteNotifications();
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo,
Action<UIBackgroundFetchResult> completionHandler)
{
if(CrossPushNotification.Current is IPushNotificationHandler)
{

View file

@ -0,0 +1,152 @@
using Bit.App.Abstractions;
using Bit.App.Utilities;
using Foundation;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using UIKit;
using Xamarin.Forms;
namespace Bit.iOS.Services
{
public class PushNotificationImplementation : IPushNotification, IPushNotificationHandler
{
public string Token => NSUserDefaults.StandardUserDefaults.StringForKey(PushNotificationKey.Token);
public void Register()
{
var userNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge |
UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(userNotificationTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
public void Unregister()
{
UIApplication.SharedApplication.UnregisterForRemoteNotifications();
OnUnregisteredSuccess();
}
private static string DictionaryToJson(NSDictionary dictionary)
{
NSError error;
var json = NSJsonSerialization.Serialize(dictionary, NSJsonWritingOptions.PrettyPrinted, out error);
return json.ToString(NSStringEncoding.UTF8);
}
public void OnMessageReceived(NSDictionary userInfo)
{
var parameters = new Dictionary<string, object>();
var json = DictionaryToJson(userInfo);
var values = JObject.Parse(json);
var keyAps = new NSString("aps");
if(userInfo.ContainsKey(keyAps))
{
var aps = userInfo.ValueForKey(keyAps) as NSDictionary;
if(aps != null)
{
foreach(var apsKey in aps)
{
parameters.Add(apsKey.Key.ToString(), apsKey.Value);
JToken temp;
if(!values.TryGetValue(apsKey.Key.ToString(), out temp))
{
values.Add(apsKey.Key.ToString(), apsKey.Value.ToString());
}
}
}
}
CrossPushNotification.PushNotificationListener.OnMessage(values, Device.iOS);
}
public void OnErrorReceived(NSError error)
{
Debug.WriteLine("{0} - Registration Failed.", PushNotificationKey.DomainName);
CrossPushNotification.PushNotificationListener.OnError(error.LocalizedDescription, Device.iOS);
}
public void OnRegisteredSuccess(NSData token)
{
Debug.WriteLine("{0} - Succesfully Registered.", PushNotificationKey.DomainName);
var trimmedDeviceToken = token.Description;
if(!string.IsNullOrWhiteSpace(trimmedDeviceToken))
{
trimmedDeviceToken = trimmedDeviceToken.Trim('<');
trimmedDeviceToken = trimmedDeviceToken.Trim('>');
trimmedDeviceToken = trimmedDeviceToken.Trim();
trimmedDeviceToken = trimmedDeviceToken.Replace(" ", "");
}
Console.WriteLine("{0} - Token: {1}", PushNotificationKey.DomainName, trimmedDeviceToken);
CrossPushNotification.PushNotificationListener.OnRegistered(trimmedDeviceToken, Device.iOS);
NSUserDefaults.StandardUserDefaults.SetString(trimmedDeviceToken, PushNotificationKey.Token);
NSUserDefaults.StandardUserDefaults.Synchronize();
}
public void OnUnregisteredSuccess()
{
CrossPushNotification.PushNotificationListener.OnUnregistered(Device.iOS);
NSUserDefaults.StandardUserDefaults.SetString(string.Empty, PushNotificationKey.Token);
NSUserDefaults.StandardUserDefaults.Synchronize();
}
}
public interface IPushNotificationHandler
{
void OnMessageReceived(NSDictionary parameters);
void OnErrorReceived(NSError error);
void OnRegisteredSuccess(NSData token);
void OnUnregisteredSuccess();
}
internal class CrossPushNotification
{
private static Lazy<IPushNotification> Implementation = new Lazy<IPushNotification>(
() => new PushNotificationImplementation(),
LazyThreadSafetyMode.PublicationOnly);
public static bool IsInitialized => PushNotificationListener != null;
public static IPushNotificationListener PushNotificationListener { get; private set; }
public static void Initialize<T>(T listener) where T : IPushNotificationListener
{
if(PushNotificationListener == null)
{
PushNotificationListener = listener;
Debug.WriteLine("PushNotification plugin initialized.");
}
else
{
Debug.WriteLine("PushNotification plugin already initialized.");
}
}
public static void Initialize<T>() where T : IPushNotificationListener, new()
{
Initialize(new T());
}
public static IPushNotification 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

@ -238,6 +238,7 @@
<Compile Include="Services\DeviceActionService.cs" />
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="Services\PushNotificationImplementation.cs" />
<Compile Include="Services\ReflectionService.cs" />
<None Include="app.config">
<SubType>Designer</SubType>
@ -324,14 +325,6 @@
<Reference Include="Plugin.Settings.Abstractions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.3.0.1\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll</HintPath>
</Reference>
<Reference Include="PushNotification.Plugin, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.PushNotification.1.2.4\lib\Xamarin.iOS10\PushNotification.Plugin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PushNotification.Plugin.Abstractions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugin.PushNotification.1.2.4\lib\Xamarin.iOS10\PushNotification.Plugin.Abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\..\packages\SimpleInjector.4.0.8\lib\netstandard1.3\SimpleInjector.dll</HintPath>
</Reference>

View file

@ -67,7 +67,6 @@
<package id="Validation" version="2.3.7" targetFramework="xamarinios10" />
<package id="WebP.Touch" version="1.0.3" targetFramework="xamarinios10" />
<package id="Xam.Plugin.Connectivity" version="3.0.2" targetFramework="xamarinios10" />
<package id="Xam.Plugin.PushNotification" version="1.2.4" targetFramework="xamarinios10" developmentDependency="true" />
<package id="Xam.Plugins.Settings" version="3.0.1" targetFramework="xamarinios10" />
<package id="Xamarin.Build.Download" version="0.4.7" targetFramework="xamarinios10" />
<package id="Xamarin.FFImageLoading" version="2.2.9" targetFramework="xamarinios10" />