Getting started with Huawei Mobile Services
In this section we will do our best to introduce you the process of integration with HMS.
Huawei ID registration
In order to start working with HMS first you need to register your Huawei Developer account, if you don't already have it. You just need to follow instructions from this link.
AppGallery Connect settings
After you've created your huawei ID, now you can log in into Huawei Developer and Huawei Developer Console.
On the AppGallery Connect (AGC) console of HUAWEI Developer, you need to create a project and an app. To do this, you can refer to this links:
Next step is to create a signing certificate for your application. After you do that, you should copy the generated SHA256 fingerprint and add it in AppInformation section of your project settings on AppGallery.
To enable desired APIs for your application, go to Manage APIs.
This link describing the steps mentioned above could be useful.
Integrating the HMS Core SDK
Download agconnect-services.json, add it in your project's Assets directory and check if the BuildAction is set to AndroidAsset.
Then, add a new class to the root of your project, which will implement LazyInputStream and read the agconnect-services.json file.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using AssecoSEE.DEM.Core;
using Huawei.Agconnect.Config;
namespace AssecoSEE.DEMO.Droid
{
public class HmsLazyInputStream : LazyInputStream
{
public HmsLazyInputStream(Context context)
: base(context)
{
}
public override Stream Get(Context context)
{
try
{
return context.Assets.Open("agconnect-services.json");
}
catch (Exception e)
{
DEMApplication.Current.Logger.Log(ADebugLevel.Error, $"Failed to get input stream " + e.Message);
return null;
}
}
}
}
Next to HmsLazyInputStream add XamarinCustomProvider:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Database;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Huawei.Agconnect.Config;
namespace AssecoSEE.DEMO.Droid
{
[ContentProvider(new string[] { "com.xam.demomap.XamarinCustomProvider" })]
public class XamarinCustomProvider : ContentProvider
{
public override int Delete(Android.Net.Uri uri, string selection, string[] selectionArgs)
{
throw new NotImplementedException();
}
public override string GetType(Android.Net.Uri uri)
{
throw new NotImplementedException();
}
public override Android.Net.Uri Insert(Android.Net.Uri uri, ContentValues values)
{
throw new NotImplementedException();
}
public override bool OnCreate()
{
AGConnectServicesConfig config = AGConnectServicesConfig.FromContext(Context);
config.OverlayWith(new HmsLazyInputStream(Context));
return false;
}
public override ICursor Query(Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder)
{
throw new NotImplementedException();
}
public override int Update(Android.Net.Uri uri, ContentValues values, string selection, string[] selectionArgs)
{
throw new NotImplementedException();
}
}
}
Android manifest changes
Under the application element add this line. Copy your app ID from AppGalleryConnect project settings.
<meta-data android:name="com.huawei.hms.client.appid" android:value="appid=YOUR_APP_ID" />
You will also need to declare this permission just above the application element of the manifest.
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA" />
You can refer again to Huawei documentation or to our Huawei project for everything.
Nuget packages
We installed a few NuGet packages (Xamarin Binding Libraries for Huawei SDKs that we want to use) to our project:
Huawei MAPS integration
In your project's Resources directory inside values add a new .axml file. We have activity_main.axml, you can name it whatever you want.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.huawei.hms.maps.MapView
android:id="@+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cameraZoom="11"
/>
</RelativeLayout>
To the root of project we added Renderers folder and a new class in it - HuaweiCustomMapRenderer.cs:
using Android.Content;
using Android.OS;
using AssecoSEE.DEM.Components.Controls;
using AssecoSEE.DEM.Core;
using System.ComponentModel;
using Huawei.Hms.Maps;
using Huawei.Hms.Maps.Model;
using System;
using System.Collections.Generic;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Platform.Android;
using Map = Xamarin.Forms.Maps.Map;
namespace AssecoSEE.DEMO.Droid.Renderers
{
public class HuaweiCustomMapRenderer : ViewRenderer<Map, MapView>, IOnMapReadyCallback
{
protected HuaweiMap MapHuawei;
protected MapView ViewMap;
protected CustomMap FormsMap;
protected IList<Pin> Pins;
protected ISelectLocation LocationVM;
public HuaweiCustomMapRenderer(Context context) : base(context)
{
Android.Views.LayoutInflater li = Android.Views.LayoutInflater.From(context);
var view = li.Inflate(Resource.Layout.activity_main, null);
ViewMap = view.FindViewById<MapView>(Resource.Id.mapview);
//ViewMap = FindViewById<MapView>(Resource.Id.mapview);
Bundle mapViewBundle = null;
ViewMap?.OnCreate(mapViewBundle);
ViewMap?.GetMapAsync(this);
}
public HuaweiCustomMapRenderer(Context context, bool flag) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
MapHuawei.InfoWindowClick -= OnInfoWindowClick;
MapHuawei.MarkerClick -= HuaweiMap_MarkerClick;
}
if (e.NewElement != null)
{
if (Control == null)
{
try
{
if (ViewMap.Parent != null)
{
ViewMap.RemoveFromParent();
}
SetNativeControl(ViewMap);
}
catch (Exception ex)
{
}
}
FormsMap = (CustomMap)e.NewElement;
Pins = FormsMap.PinList;
FormsMap.PinsChanged += DrawPins;
((MapView)Control)?.GetMapAsync(this);
}
}
protected virtual void OnInfoWindowClick(object sender, HuaweiMap.InfoWindowClickEventArgs e)
{
if (LocationVM != null)
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(async () =>
{
await LocationVM.GetSelecteditemFromMap(
new Position(e.P0.Position.Latitude, e.P0.Position.Longitude));
});
}
else
{
}
}
private void HuaweiMap_MarkerClick(object sender, HuaweiMap.MarkerClickEventArgs e)
{
e.P0.ShowInfoWindow();
e.Handled = true;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (LocationVM == null)
{
var customMap = (CustomMap)sender;
if (customMap.BindingContext is ISelectLocation locationVM)
{
LocationVM = locationVM;
}
}
}
public void OnMapReady(HuaweiMap p0)
{
try
{
//base.OnMapReady(p0);
MapHuawei = p0;
MapHuawei.MyLocationEnabled = true;
MapHuawei.UiSettings.MyLocationButtonEnabled = true;
MapHuawei.UiSettings.CompassEnabled = true;
MapHuawei.UiSettings.RotateGesturesEnabled = true;
MapHuawei.UiSettings.ScrollGesturesEnabled = true;
MapHuawei.UiSettings.ScrollGesturesEnabledDuringRotateOrZoom = true;
MapHuawei.UiSettings.TiltGesturesEnabled = true;
MapHuawei.UiSettings.ZoomControlsEnabled = true;
MapHuawei.UiSettings.ZoomGesturesEnabled = true;
MapHuawei.UiSettings.IndoorLevelPickerEnabled = true;
MapHuawei.UiSettings.MapToolbarEnabled = true;
MapHuawei.UiSettings.SetAllGesturesEnabled(true);
MapHuawei.MapType = 1;
var location = (FormsMap as Xamarin.Forms.Maps.Map).LastMoveToRegion;
CameraPosition build = new CameraPosition.Builder().Target(new LatLng(location.Center.Latitude, location.Center.Longitude)).Zoom(11).Build();
CameraUpdate cameraUpdate = CameraUpdateFactory.NewCameraPosition(build);
MapHuawei.AnimateCamera(cameraUpdate);
DrawPins();
MapHuawei.InfoWindowClick += OnInfoWindowClick;
MapHuawei.MarkerClick += HuaweiMap_MarkerClick;
}
catch (Exception)
{
//TO - DO
}
}
protected virtual void DrawPins()
{
Pins = FormsMap?.PinList;
if (MapHuawei != null && Pins != null)
{
MapHuawei.Clear();
foreach (var pin in Pins)
{
var marker = new MarkerOptions();
marker.InvokePosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
marker.InvokeTitle(pin.Label);
marker.InvokeSnippet(pin.Address);
marker.InvokeIcon(GetPinIcon(pin));
MapHuawei.AddMarker(marker).ShowInfoWindow();
}
}
}
protected virtual Huawei.Hms.Maps.Model.BitmapDescriptor GetPinIcon(Pin pin)
{
string asset = Xamarin.Forms.Application.Current.Resources["Branch"].ToString();
if (pin.Type == PinType.Place)
{
asset = Xamarin.Forms.Application.Current.Resources["Branch"].ToString();
}
else if (pin.Type == PinType.Generic)
{
asset = Xamarin.Forms.Application.Current.Resources["ATM"].ToString();
}
else if (pin.Type == PinType.SavedPin)
{
asset = Xamarin.Forms.Application.Current.Resources["BankClosed"].ToString();
}
return BitmapDescriptorFactory.FromAsset(asset);
}
}
}
In AssemblyInfo class, we set this new renderer as renderer for CustomMap control.
[assembly: Xamarin.Forms.ExportRenderer(typeof(AssecoSEE.DEM.Components.Controls.CustomMap), typeof(AssecoSEE.DEMO.Droid.Renderers.HuaweiCustomMapRenderer))]
In order to Map be successfully displayed, the application needs to be signed with the keystore which SHA-256 fingerprint is added to AppInformation section of project settings in AppGallery.
Useful links:
- Creating a Map
- Setting Package Information in Xamarin
- Using Huawei Mobile Services with Xamarin
- Xamarin.Android.Huawei.Hms.Demo
- Integration of Huawei Map Kit in Xamarin(Android)
Huawei PUSH integration
First thing we need to do in order to get a push notification is to obtain a token.
In MainApplication class we added an override of RegisterPush method:
protected override void RegisterPush()
{
System.Threading.Thread thread = new System.Threading.Thread(() =>
{
try
{
var token = Huawei.Hms.Aaid.HmsInstanceId.GetInstance(Application.Context).GetToken("rs.assecosee.dem.droid.intdev", HmsMessaging.DefaultTokenScope);
System.Threading.Tasks.Task.Run(async () => {
await Xamarin.Essentials.SecureStorage.SetAsync(Global.SECURESTORAGE_KEY_PUSH_NOTIFICATION_TOKEN, token);
});
}
catch (Exception e)
{
}
});
thread.Start();
}
If you have enabled Push Kit in Manage APIs section of your project settings on AppGallery, done the steps described in section 'Integrating the HMS Core SDK', installed corresponding package and signed the application with corresponding keystore, you should be able to get a token with this code and send a test push message form AppGallery to determine that Push Kit is working with your app.
To do that, go to your project on AppGallery Connect and then, from the section 'Grow' select 'PushKit'.
From there you can send a test notification message to your device by clicking 'Add notification' button, entering the message details and copying the obtained token.
To fully support and manage push messaging, it's also needed a class that extends 'HmsMessageService' and implements its methods, so in our project under Services folder, we added a new one named Push and a new class in it:
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Huawei.Hms.Push;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssecoSEE.DEMO.Droid.Services.Push
{
[Service]
[IntentFilter(new[] { "com.huawei.push.action.MESSAGING_EVENT" })]
public class HMSPushMessageService : HmsMessageService
{
public override void OnNewToken(string token)
{
HMSInstanceIdService.Instance?.HMSOnNewToken(token);
}
public override void OnNewToken(string token, Bundle bundle)
{
HMSInstanceIdService.Instance?.HMSOnNewToken(token);
}
public override void OnTokenError(Java.Lang.Exception exception, Bundle bundle)
{
HMSInstanceIdService.Instance?.HMSOnTokenError(((BaseException)exception).Message);
}
public override void OnMessageReceived(RemoteMessage message)
{
HMSInstanceIdService.Instance?.HMSOnMessageReceived(message.Data);
}
}
}
HMSInstanceIdService is a class that we also added in Services/Push, which implements a new interface IHMSInstanceId, from Core package.
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using AssecoSEE.DEM.Core;
using AssecoSEE.DEM.Core.Interfaces;
using Huawei.Hms.Aaid;
using Huawei.Hms.Aaid.Entity;
using Huawei.Hms.Push;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Essentials;
namespace AssecoSEE.DEMO.Droid.Services.Push
{
public class HMSInstanceIdService : IHMSInstanceId
{
internal static HMSInstanceIdService Instance { get; private set; }
private HmsInstanceId Client { get; set; }
public event OnNewTokenHandler OnNewToken;
public event OnTokenErrorHandler OnTokenError;
public event OnMessageReceivedHandler OnMessageReceived;
public void Initialize()
{
Instance = this;
Client = HmsInstanceId.GetInstance(Application.Context);
}
public void GetToken()
{
Thread thread = new Thread(() =>
{
try
{
string token = Client.GetToken("rs.assecosee.dem.droid.intdev", HmsMessaging.DefaultTokenScope);
Task.Run(async () => {
await SecureStorage.SetAsync(Global.SECURESTORAGE_KEY_PUSH_NOTIFICATION_TOKEN, token);
});
}
catch (Exception e)
{
}
});
thread.Start();
}
public async Task<string> GetAAIDAsync()
{
AAIDResult Result = await Client.GetAAIDAsync();
return Result.Id;
}
public void DeleteAAID()
{
Thread thread = new Thread(() =>
{
Client.DeleteAAID();
});
thread.Start();
}
public void DeleteToken()
{
Thread thread = new Thread(() =>
{
Client.DeleteToken("rs.assecosee.dem.droid.intdev", HmsMessaging.DefaultTokenScope);
});
thread.Start();
}
public void HMSOnNewToken(string token)
{
OnNewToken?.Invoke(token);
}
public void HMSOnMessageReceived(string message)
{
OnMessageReceived?.Invoke(message);
}
public void HMSOnTokenError(string errorMessage)
{
OnTokenError?.Invoke(new Exception(errorMessage));
}
}
}
In AssecoSEE.DEM.Core we added new interface:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace AssecoSEE.DEM.Core.Interfaces
{
public interface IHMSInstanceId : IApplicationService
{
event OnNewTokenHandler OnNewToken;
event OnTokenErrorHandler OnTokenError;
event OnMessageReceivedHandler OnMessageReceived;
void Initialize();
void GetToken();
Task<string> GetAAIDAsync();
void DeleteAAID();
void DeleteToken();
}
public delegate void OnNewTokenHandler(string token);
public delegate void OnTokenErrorHandler(Exception exception);
public delegate void OnMessageReceivedHandler(string message);
}
HMSInstanceIdService needs to be registered in DemoDroidInitializer.cs-u.
typeCatalog.Add(new AppServiceInfo<Push.HMSInstanceIdService, IHMSInstanceId>() { IsSingleInstance = true });
In AssecoSEE.DEM.Core/Services/Push we added a new folder named HMS and a new class in it - HMSPushNotificationSubscriber:
using AssecoSEE.DEM.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Essentials;
namespace AssecoSEE.DEM.Core.Services.Push.HMS
{
public class HMSPushNotificationSubscriber : IPushNotificationSubscriber
{
private readonly static HMSPushNotificationSubscriber _instance;
private readonly static object pushNotificationSubscriberLock = new object();
private readonly IPushService pushService;
private readonly IHMSInstanceId hmsInstanceId;
static HMSPushNotificationSubscriber()
{
if (_instance == null)
{
_instance = new HMSPushNotificationSubscriber();
}
}
private HMSPushNotificationSubscriber()
{
pushService = DEMApplication.Current.ResolveService<IPushService>();
hmsInstanceId = DEMApplication.Current.ResolveService<IHMSInstanceId>();
hmsInstanceId.Initialize();
hmsInstanceId.OnNewToken += HMSInstanceIdOnNewToken;
hmsInstanceId.OnTokenError += HMSInstanceIdOnTokenError;
hmsInstanceId.OnMessageReceived += HMSMessageReceived;
}
public static HMSPushNotificationSubscriber Instance
{
get
{
lock (pushNotificationSubscriberLock)
{
return _instance;
}
}
}
private IApplication App
{
get { return DEMApplication.Current; }
}
private string lastUsedToken;
public void OnLogin()
{
if (pushService == null || !pushService.IsEnabled())
{
return;
}
//hmsInstanceId.GetToken();
Task.Run(async () => {
if (String.IsNullOrWhiteSpace(lastUsedToken))
{
lastUsedToken = await SecureStorage.GetAsync(Global.SECURESTORAGE_KEY_PUSH_NOTIFICATION_TOKEN).ConfigureAwait(false);
}
pushService.TokenUpdate(lastUsedToken);
});
}
private void HMSInstanceIdOnNewToken(string token)
{
if (!String.IsNullOrWhiteSpace(token))
{
System.Diagnostics.Debug.WriteLine("HMS Push Token", token);
lastUsedToken = token;
if (pushService == null || !pushService.IsEnabled())
{
return;
}
Task.Run(async () => {
await SecureStorage.SetAsync(Global.SECURESTORAGE_KEY_PUSH_NOTIFICATION_TOKEN, lastUsedToken);
});
pushService.TokenUpdate(lastUsedToken);
}
}
private void HMSMessageReceived(string message)
{
if (pushService == null || !pushService.IsEnabled() || String.IsNullOrWhiteSpace(message))
{
return;
}
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<IDictionary<string, string>>(message);
pushService.CheckPushMessage(data);
}
private void HMSInstanceIdOnTokenError(Exception exception)
{
System.Diagnostics.Debug.WriteLine("OnTokenError", exception.Message);
}
}
}
We added a new Feature flag which controls behavior in our packages and its default value is false.
{
"Name": "HMS",
"IsEnabled": false
}
So, in the OpenAsync method of SessionState we now have:
if (featureManager != null && featureManager.IsEnabled(nameof(Features.HMS)))
{
Core.Services.Push.HMS.HMSPushNotificationSubscriber.Instance.OnLogin();
}
else
{
Core.Services.PushNotificationSubscriber.Instance.OnLogin();
}
As you can see from the code above, we added one more service to AssecoSEE.DEM.Core/Services/Push - PushService. We have moved to it all the common methods for PushNotificationSubscriber and HMSPushNotificationSubscriber, such as IsEnabled(), TokenUpdate(string token), CheckPushMessage(IDictionary<string, string> data)...
HmsMessageService
OnMessageReceived(RemoteMessage message) is called when you receive a push data message or when a notification message is sent using the REST API and message.android.notification.foreground_show is set to false. In that case, with this parameter set to false, when app is in foreground and receives a notification message, the message won't be displayed in Notification Center, but will be handled through this method.
We are obtaining token with GetToken method from MainApplication, but it's necessary to declare also OnNewToken methods in the code to ensure that the token can be returned. The app calls the getToken method in HmsInstanceId to obtain a token from the server. If the server does not return the token at this time, the token will be returned using the onNewToken(String token) method later. The main scenarios are as follows:
- If the getToken method fails to be called, Push Kit will automatically call the method again. If the call is successful, the requested token will be returned through the onNewToken(String token) method.
- If the EMUI version of a Huawei device is earlier than 10.0, the token is returned through the onNewToken(String token) method.
Alternatively, use can use automatic initialization of the HMS Core Push SDK to automatically obtain the token, so if you add this in application element of your manifest, the token will be automaticaly obtained and returned from OnNewToken method.
<meta-data android:name="push_kit_auto_init_enabled" android:value="true" />
When application is closed and we receive a notification, we handle it in the project's main launcher, in our case - SplashActivity. The parameters of the message will be in Intent.Extras, so we added:
protected void Show()
{
Intent intent = new Intent(this, typeof(MainActivity));
if (Intent.Extras != null)
{
intent.PutExtras(Intent.Extras);
IDictionary<string, object> keyValuePairs = new Dictionary<string, object>();
foreach (var key in intent.Extras.KeySet())
{
var value = intent.Extras.GetString(key);
keyValuePairs.Add(new KeyValuePair<string, object>(key, value));
}
AssecoSEE.DEM.Core.Global.OnOpenedPushPayoad = keyValuePairs;
}
... the rest of your code ...
}
We also made some changes in StartViewModel's CheckRemotePushMessages() method:
public virtual async Task<bool> CheckRemotePushMessages()
{
if (AssecoSEE.DEM.Core.Global.OnOpenedPushPayoad != null)
{
if (App.IsFeatureEnabled(nameof(Features.HMS)))
{
var payload = AssecoSEE.DEM.Core.Global.OnOpenedPushPayoad as IDictionary<string, object>;
if (payload != null)
{
return await GetRemotePushMessage(payload);
}
}
else
{
var p = AssecoSEE.DEM.Core.Global.OnOpenedPushPayoad as FirebasePushNotificationResponseEventArgs;
if (p.Data != null)
{
return await GetRemotePushMessage(p.Data);
}
}
}
return false;
}
All logic from this method regarding check of message parameters and navigation to corresponding views is now moved to GetRemotePushMessage.
public virtual async Task<bool> GetRemotePushMessage(IDictionary<string, object> messagePayload)
{
//commented code left for debugging
//AssecoSEE.DEM.Core.Global.OnOpenedPushPayoadDetails = new Dictionary<string, string>();
var onPushSenderString = AssecoSEE.DEM.Core.Global.OnOpenedPushSender?.ToString();
var onPushPayloadString = AssecoSEE.DEM.Core.Global.OnOpenedPushPayoad.ToString();
//var onPushPayloadDetails = "";
//var extras = "";
//var extrasNewIntent = "";
//var containsBody = AssecoSEE.DEM.Core.Global.ContainsBody.ToString();
if (messagePayload != null)
{
if (messagePayload.Keys?.Count() != 0 && messagePayload.Where(x => x.Key.Contains("jwtType")).Any())
{
//foreach (var data in p.Data.Where(x => x.Key != null))
//{
// AssecoSEE.DEM.Core.Global.OnOpenedPushPayoadDetails.Add(data.Key, data.Value.ToString());
//}
//if (AssecoSEE.DEM.Core.Global.OnOpenedPushPayoadDetails != null && AssecoSEE.DEM.Core.Global.OnOpenedPushPayoadDetails.Count > 0)
//{
// foreach (var detail in AssecoSEE.DEM.Core.Global.OnOpenedPushPayoadDetails)
// {
// onPushPayloadDetails += "\n" + detail.Key + ":" + detail.Value;
// }
//}
//if (AssecoSEE.DEM.Core.Global.ExtrasList != null && AssecoSEE.DEM.Core.Global.ExtrasList.Count > 0)
//{
// foreach (var detail in AssecoSEE.DEM.Core.Global.ExtrasList)
// {
// extras += "\n" + detail;
// }
//}
//if (AssecoSEE.DEM.Core.Global.ExtrasListNewIntent != null && AssecoSEE.DEM.Core.Global.ExtrasListNewIntent.Count > 0)
//{
// foreach (var detail in AssecoSEE.DEM.Core.Global.ExtrasListNewIntent)
// {
// extrasNewIntent += "\n" + detail;
// }
//}
//await App.Device.DisplayMessageAsync("Push sender: " + onPushSenderString + " \nPushPayload: " + onPushPayloadString + " \nPayloadDetails: " + onPushPayloadDetails + " \nExtras: " + extras + " \nNewIntentExtras: " + extrasNewIntent + " " +
// " \n", "Opened");
//-------------------------------------------------------------------------
string type = messagePayload.Where(x => x.Key.Contains("jwtType")).FirstOrDefault().Value?.ToString() ?? String.Empty;
if (!String.IsNullOrWhiteSpace(type))
{
if (type == "MTM_LOGIN_BY_TOKEN" && messagePayload.Where(x => x.Key.Contains("jsonWebToken")).Any())
{
var token = messagePayload.Where(x => x.Key.Contains("jsonWebToken")).FirstOrDefault().Value?.ToString();
var body = messagePayload.Where(x => x.Key.Contains("body")).FirstOrDefault().Value?.ToString() ?? String.Empty;
var title = messagePayload.Where(x => x.Key.Contains("title")).FirstOrDefault().Value?.ToString() ?? String.Empty;
if (String.IsNullOrWhiteSpace(token))
{
return false;
}
else
{
await DEMApplication.Current.Navigation.NavigateToAsync("LoginToWebWithPushViewModel", new PushTokenNavigationParameters() { JsonWebToken = token, PushBody = body, PushTitle = title });
return true;
}
}
if (type == "MTM_MAC" && messagePayload.Where(x => x.Key.Contains("jsonWebToken")).Any())
{
var token = messagePayload.Where(x => x.Key.Contains("jsonWebToken")).FirstOrDefault().Value?.ToString();
var body = messagePayload.Where(x => x.Key.Contains("body")).FirstOrDefault().Value?.ToString() ?? String.Empty;
var title = messagePayload.Where(x => x.Key.Contains("title")).FirstOrDefault().Value?.ToString() ?? String.Empty;
if (String.IsNullOrWhiteSpace(token))
{
return false;
}
else
{
await DEMApplication.Current.Navigation.NavigateToAsync("TransactionAuthorizationWithPushViewModel", new PushTokenNavigationParameters() { JsonWebToken = token, PushBody = body, PushTitle = title, Type = type });
return true;
}
}
else
{
return false;
}
}
return !String.IsNullOrWhiteSpace(type);
}
}
AssecoSEE.DEM.Core.Global.OnOpenedPushSender = null;
AssecoSEE.DEM.Core.Global.OnOpenedPushPayoad = null;
//AssecoSEE.DEM.Core.Global.OnOpenedPushPayoadDetails = new Dictionary<string, string>();
return false;
}
Useful links:
- Service Introduction
- HmsMessageService
- Obtaining and Deleting a Token
- Creating Message Service
- How To Use HMS Push Kit On Xamarin.Android
- HUAWEI Push Kit and Xamarin.Android
- How to Expose HUAWEI Push Kit features in Xamarin.Forms
Huawei AppLinking integration
As for Maps and Push, the application needs to be signed with the keystore which SHA-256 fingerprint is added to AppInformation section of project settings in AppGallery in order to AppLinking work.
Also, you need to enable it on AppGallery, if it isn't already enabled. To check this, open your project on AppGallery Connect and under Grow you will find AppLinking.
If you click Enable now button and you have not set a data processing location, a dialog box will be displayed, prompting you to set it. You probably have this set, but in case that this dialog shows - just set it and then click enable again.
Then, the settings in agconnect-services.json will be automatically updated accordingly. So, you need to download this updated json and add it to your project (if you already have agconnect-services.json in your project, you need to replace that old one with this new, just downloaded one).
Next, you need to request a URL prefix in AppGallery Connect. In your project settings, under Grow > AppLinking click the URL prefixes tab and click New URL prefix.
After you have successfully created your URL prefix and you see it in the prefix list like in the picture above, you must add an intent filter and all deep link logic that we have in another's android project main activity.
So, in Huawei's SplashActivity:
[IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.action.VIEW", "android.intent.category.BROWSABLE", "android.intent.category.DEFAULT" }, DataSchemes = new[] { "https", "http" }, DataHost = "halkbankatest.24x7.rs", DataPathPrefix = "/ips/ek/fl/", AutoVerify = true)]
public class SplashActivity : Activity, Animator.IAnimatorListener
{
protected void Show()
{
... your code ...
OnNewIntent(Intent);
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
if (intent.DataString != null)
{
GetDeepLinkData(intent.DataString);
}
}
protected void GetDeepLinkData(string dataString)
{
if (!dataString.ToLower().Contains(Global.URI_APP_AUTHORITY.ToLower()) && !dataString.ToLower().Contains(Global.URI_APP_DataPath))
{
return;
}
if (!dataString.Split('?')[0].EndsWith(Global.URI_APP_DataPath) || !dataString.Split('?')[1].StartsWith("data=") || !dataString.ToLower().Contains("callback=") || !dataString.Split("callback")[0].EndsWith("&"))
{
Global.IsWrongDeepLink = true;
}
else
{
Global.HasDeepLinkData = true;
Uri uri = new Uri(dataString);
var query = LabelHtml.Forms.Plugin.Abstractions.HttpUtility.ParseQueryString(uri);
Global.DeepLinkParams = query.Get("data").FirstOrDefault();
if (dataString.Split('&')[0].EndsWith("==") && !Global.DeepLinkParams.EndsWith("=="))
{
Global.DeepLinkParams += "==";
}
else if (dataString.Split('&')[0].EndsWith("=") && !Global.DeepLinkParams.EndsWith("="))
{
Global.DeepLinkParams += "=";
}
Global.DeepLinkCallback = query.Get("callback").FirstOrDefault();
}
}
protected override void OnCreate(Bundle savedInstanceState)
{
if (!String.IsNullOrWhiteSpace(Intent.DataString) && App != null && App.Session?.UserIsLoggedIn == true)
{
base.OnCreate(savedInstanceState);
GetDeepLinkData(Intent.DataString);
if (Global.HasDeepLinkData)
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(async () =>
{
await App.Device?.SetLoadingAsync(() => App.Navigation?.NavigateToDeepLinkPage(), App.Translation["core_loading"]);
});
}
if (Global.IsWrongDeepLink)
{
Global.IsWrongDeepLink = false;
Xamarin.Forms.Device.BeginInvokeOnMainThread(async () =>
{
await Task.Delay(1000);
await App.Device?.DisplayMessageAsync(App.Translation?["payment_loaded_data_invalid_for_ips"], App.Translation?["core_error"]);
});
}
SetResult(Result.Canceled);
Finish();
return;
}
}
All about deep link implementation and description of this code, you can find at this link.
Now you should be able to successfully open the application by clicking on the deep link.
If you try to open the app by clicking on a deep link, whose structure is known to us so far:
from, for example - Notes application, your app will react on this link, but when you click on this link from the mobile browser - this won't work.
The deep link for Huawei devices with HMS should be like this:
https://halkbankatest24x7.dre.agconnect.link is URL prefix that we created on AppGallery Connect and the desired deep link is then passed through the deep link parameter. Note that parameters such as query must be URL-encoded.
When user clicks on this link, the value on the deep link parameter is passed to our application through the Intent.DataString, just like so far with the links without URL prefix, so our logic remains the same.
Useful links: