Overview
In this article, I will create HMS Map Kit based Demo App which highlights the use case of Basic Map and Polyline. I have integrated HMS Map Kit which is based on Cross-platform Technology Xamarin.
HMS Map Kit Service Introduction
HMS Core Map SDK is a set of APIs for map development in Android. The map data covers most countries outside China and supports multiple languages. The Map SDK uses the WGS 84 GPS coordinate system, which can meet most requirements of map development outside China. You can easily add map-related functions in your Android app, including:
1. Map display: Displays buildings, roads, water systems, and Points of Interest (POIs).
2. Map interaction: Controls the interaction gestures and buttons on the map.
3. Map drawing: Adds location markers, map layers, overlays, and various shapes
Prerequisite
1. Xamarin Framework
2. Huawei phone
3. Visual Studio 2019
App Gallery Integration process
1. Sign In and Create or Choose a project on AppGallery Connect portal.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
2. Navigate to Project settings > download the configuration file and Add SHA-256 key.
3. Navigate to General Information > Data Storage location.
4. Navigate to Manage APIs > Enable APIs to require by an application.
Xamarin Map Kit Setup Process
1. Download Xamarin Plugin all the aar and zip files.
2. Open the XHms-Map-Kit-Library-Project.sln solution in Visual Studio.
3. Navigate to Solution Explorer and Right-click on jar Add > Existing Item and choose aar file which downloads in Step 1.
4. Right-click on added aar file then choose Properties > Build Action > LibraryProjectZip.
Note: Repeat Step 3 and 4 for all aar file.
5. Build the Library and make dll files.
Xamarin App Development
1. Open Visual Studio 2019 and Create a new project.
2. Navigate to Solution Explore > Project > Assets > Add Json file.
3. Navigate to Solution Explore > Project > Add > Add New Folder.
4. Navigate to Folder(created) > Add > Add Existing and add all DLL files.
5. Right click > Properties > Build Action > None.
6. Navigate to Solution Explore > Project > Reference > Right Click > Add References then Navigate to Browse and add all DLL files from the recently added Folder.
7. Added reference, then click Ok.
MainActivity.cs
This activity display type of map in 2 buttons.
C#:
using Android.App;
using Android.OS;
using Android.Support.V7.App;
using Android.Runtime;
using Android.Widget;
using Android.Views;
using Android;
using Android.Content;
using Android.Support.V4.App;
using Android.Content.PM;
using Android.Util;
using Com.Huawei.Hms.Maps.Model;
namespace XamarinMap
{
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity, View.IOnClickListener
{
private const string TAG = "MainActivity";
//Required permissions
private static readonly string[] RUNTIME_PERMISSIONS = {
Manifest.Permission.WriteExternalStorage,
Manifest.Permission.ReadExternalStorage,
Manifest.Permission.AccessCoarseLocation,
Manifest.Permission.AccessFineLocation,
Manifest.Permission.Internet
};
//Permission request code.
private const int REQUEST_CODE = 100;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
//Check permissions
if (!HasPermissions(this, RUNTIME_PERMISSIONS))
{
ActivityCompat.RequestPermissions(this, RUNTIME_PERMISSIONS, REQUEST_CODE);
}
Button BasicMap = (Button)FindViewById(Resource.Id.BasicMap);
Button PolylineDemo = (Button)FindViewById(Resource.Id.PolylineDemo);
BasicMap.SetOnClickListener(this);
PolylineDemo.SetOnClickListener(this);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
public void OnClick(View v)
{
switch (v.Id)
{
case Resource.Id.BasicMap:
Log.Info(TAG, "OnClick: BasicMap");
StartActivity(new Intent(this, typeof(BasicMapDemoActivity)));
break;
case Resource.Id.PolylineDemo:
Log.Info(TAG, "OnClick: PolylineDemo");
StartActivity(new Intent(this, typeof(PolylineDemoActivity)));
break;
default:
break;
}
}
/// <summary>
/// Check whether permissions are granted
/// </summary>
private static bool HasPermissions(Context context, string[] permissions)
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.M && permissions != null)
{
foreach (string permission in permissions)
{
if (ActivityCompat.CheckSelfPermission(context, permission) != Permission.Granted)
{
return false;
}
}
}
return true;
}
}
}
MapViewDemoActivity.cs
This activity display a basic view of Huawei Map.
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Hms.Maps;
using Com.Huawei.Hms.Maps.Model;
namespace XamarinMap
{
[Activity(Label = "MapViewDemoActivity")]
public class MapViewDemoActivity : Activity, IOnMapReadyCallback
{
private const string TAG = "MapViewDemoActivity";
private const string MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private HuaweiMap hMap;
private MapView mMapView;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_mapview_demo);
mMapView = (MapView)FindViewById(Resource.Id.mapView);
Bundle mapViewBundle = null;
if (savedInstanceState != null)
{
mapViewBundle = savedInstanceState.GetBundle(MAPVIEW_BUNDLE_KEY);
}
MapsInitializer.SetApiKey(Constants.API_KEY);
mMapView.OnCreate(mapViewBundle);
mMapView.GetMapAsync(this);
}
protected override void OnStart()
{
base.OnStart();
mMapView.OnStart();
}
protected override void OnStop()
{
base.OnStop();
mMapView.OnStop();
}
protected override void OnDestroy()
{
base.OnDestroy();
mMapView.OnDestroy();
}
protected override void OnPause()
{
base.OnPause();
mMapView.OnPause();
}
protected override void OnResume()
{
base.OnResume();
mMapView.OnResume();
}
public override void OnLowMemory()
{
base.OnLowMemory();
mMapView.OnLowMemory();
}
public void OnMapReady(HuaweiMap map)
{
Log.Debug(TAG, "onMapReady: ");
hMap = map;
hMap.MyLocationEnabled = false;
hMap.MoveCamera(CameraUpdateFactory.NewLatLngZoom(new LatLng(48.893478, 2.334595), 10));
}
}
}
PolylineDemoActivity.cs
This activity draws a polyline between the two points in the Huawei map.
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Hms.Maps;
using Com.Huawei.Hms.Maps.Model;
using Java.Lang;
using static Com.Huawei.Hms.Maps.HuaweiMap;
namespace XamarinMap
{
[Activity(Label = "PolylineDemoActivity")]
public class PolylineDemoActivity : AppCompatActivity, IOnMapReadyCallback, IOnPolylineClickListener
{
private const string TAG = "PolylineDemoActivity";
private MapFragment mSupportMapFragment;
private HuaweiMap hMap;
private Polyline mPolyline;
private TextView polylineShown;
private EditText oneLatitude;
private EditText oneLongtitude;
private EditText polylineStokeWidth;
private EditText polylineTag;
private IList<LatLng> points = new List<LatLng>();
protected override void OnCreate(Bundle savedInstanceState)
{
Log.Debug(TAG, "OnCreate: ");
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_polyline_demo);
mSupportMapFragment = (MapFragment)FragmentManager.FindFragmentById(Resource.Id.mapInPolyline);
mSupportMapFragment.GetMapAsync(this);
polylineShown = (TextView)FindViewById(Resource.Id.polylineShown);
oneLatitude = (EditText)FindViewById(Resource.Id.oneLatitude);
oneLongtitude = (EditText)FindViewById(Resource.Id.oneLongtitude);
polylineStokeWidth = (EditText)FindViewById(Resource.Id.polylineStokeWidth);
polylineTag = (EditText)FindViewById(Resource.Id.polylineTag);
points.Add(Constants.HUAWEI_CENTER);
points.Add(Constants.APARTMENT_CENTER);
points.Add(Constants.EPARK_CENTER);
}
public void OnMapReady(HuaweiMap map)
{
Log.Debug(TAG, "OnMapReady: ");
hMap = map;
hMap.MyLocationEnabled = true;
hMap.MoveCamera(CameraUpdateFactory.NewLatLngZoom(new LatLng(48.893478, 2.334595), 10));
}
/// <summary>
/// Add polyline to the map.
/// </summary>
[Java.Interop.Export("AddPolyline")]
public void AddPolyline(View view)
{
if (hMap == null)
{
return;
}
if (mPolyline != null)
{
mPolyline.Remove();
}
mPolyline = hMap.AddPolyline(
new PolylineOptions().Add(Constants.FRANCE, Constants.FRANCE1, Constants.FRANCE2, Constants.FRANCE3)
.InvokeColor(Color.Blue)
.InvokeWidth(3));
hMap.SetOnPolylineClickListener(this);
}
public void OnPolylineClick(Polyline polyline)
{
Log.Info(TAG, "OnMapReady:OnPolylineClick ");
Toast.MakeText(ApplicationContext, "Polyline is clicked.", ToastLength.Long).Show();
}
/// <summary>
/// Remove the polyline
/// </summary>
[Java.Interop.Export("RemovePolyline")]
public void RemovePolyline(View view)
{
if (null != mPolyline)
{
mPolyline.Remove();
}
points.Clear();
points.Add(Constants.HUAWEI_CENTER);
points.Add(Constants.APARTMENT_CENTER);
points.Add(Constants.EPARK_CENTER);
}
/// <summary>
/// Set the point position information of the polyline.
/// </summary>
[Java.Interop.Export("SetOnePoint")]
public void SetOnePoint(View v)
{
if (null != mPolyline)
{
string latitude = oneLatitude.Text.ToString().Trim();
string longtitude = oneLongtitude.Text.ToString().Trim();
if (CheckUtils.CheckIsEdit(latitude) || CheckUtils.CheckIsEdit(longtitude))
{
Toast.MakeText(this, "Please make sure the latitude & longtitude is Edited", ToastLength.Short).Show();
}
else
{
if (!CheckUtils.CheckIsRight(latitude) || !CheckUtils.CheckIsRight(longtitude))
{
Toast.MakeText(this, "Please make sure the latitude & longtitude is right", ToastLength.Short).Show();
}
else
{
points.Add(new LatLng(System.Double.Parse(latitude), System.Double.Parse(longtitude)));
mPolyline.Points = points;
}
}
}
}
/// <summary>
/// Get the point position information of the polyline.
/// </summary>
[Java.Interop.Export("GetPoints")]
public void GetPoints(View v)
{
if (null != mPolyline)
{
System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
for (int i = 0; i < mPolyline.Points.ToArray().Length; i++)
{
stringBuilder.Append(mPolyline.Points.ElementAt(i).ToString());
}
Toast.MakeText(this, "Polyline points is " + stringBuilder, ToastLength.Long).Show();
}
}
/// <summary>
/// Set the outline color of the polyline.
/// </summary>
[Java.Interop.Export("SetStokeColor")]
public void SetStokeColor(View v)
{
if (null != mPolyline)
{
mPolyline.Color = Color.Yellow;
}
}
/// <summary>
/// Get the outline color of the polyline.
/// </summary>
[Java.Interop.Export("GetStokeColor")]
public void GetStokeColor(View v)
{
if (null != mPolyline)
{
polylineShown.Text = "Polyline color is " + Integer.ToHexString(mPolyline.Color);
}
}
/// <summary>
/// Set the width of the polyline.
/// </summary>
[Java.Interop.Export("SetWidth")]
public void SetWidth(View v)
{
if (null != mPolyline)
{
string width = polylineStokeWidth.Text.ToString().Trim();
if (CheckUtils.CheckIsEdit(width))
{
Toast.MakeText(this, "Please make sure the width is Edited", ToastLength.Short).Show();
}
else
{
if (!CheckUtils.CheckIsRight(width))
{
Toast.MakeText(this, "Please make sure the width is right", ToastLength.Short).Show();
}
else
{
if (Single.Parse(width) < 0.0F)
{
Toast.MakeText(this,
"Please make sure the width is right, this value must be non-negative",
ToastLength.Short).Show();
return;
}
mPolyline.Width = Single.Parse(width);
}
}
}
}
/// <summary>
/// Get the width of the polyline.
/// </summary>
[Java.Interop.Export("GetWidth")]
public void GetWidth(View v)
{
if (null != mPolyline)
{
polylineShown.Text = ("Polyline width is " + mPolyline.Width);
}
}
/// <summary>
/// Set the tag of the polyline.
/// </summary>
[Java.Interop.Export("SetTag")]
public void SetTag(View v)
{
if (null != mPolyline)
{
string tag = polylineTag.Text.ToString().Trim();
if (CheckUtils.CheckIsEdit(tag))
{
Toast.MakeText(this, "Please make sure the tag is Edited", ToastLength.Short).Show();
}
else
{
mPolyline.Tag = tag;
}
}
}
/// <summary>
/// Get the tag of the polyline.
/// </summary>
[Java.Interop.Export("GetTag")]
public void GetTag(View v)
{
if (null != mPolyline)
{
polylineShown.Text = ((mPolyline.Tag == null ? "Tag is null" : mPolyline.Tag).ToString());
}
}
/// <summary>
/// Add polyline click event.
/// </summary>
[Java.Interop.Export("AddClickEvent")]
public void AddClickEvent(View v)
{
if (null != mPolyline)
{
hMap.SetOnPolylineClickListener(this);
}
}
/// <summary>
/// Set polyline clickable.
/// </summary>
[Java.Interop.Export("SetClickableTrue")]
public void SetClickableTrue(View v)
{
if (null != mPolyline)
{
mPolyline.Clickable = true;
}
}
/// <summary>
/// Set polyline are not clickable.
/// </summary>
[Java.Interop.Export("SetClickableFalse")]
public void SetClickableFalse(View v)
{
if (null != mPolyline)
{
mPolyline.Clickable = false;
}
}
}
}
Xamarin App Build Result
1. Navigate to Solution Explore > Project > Right Click > Archive/View Archive to generate SHA-256 for build release and Click on Distribute.
2. Choose Distribution Channel > Ad Hoc to sign apk.
3. Choose Demo Keystore to release apk.
4. Build succeed and Save apk file.
5. Final Result.
Tips and Tricks
1. Xamarin requires the ADB daemon to be started over port 5037. If the ADB daemon runs on a different port, Visual Studio will not be able to detect your device.
2. Map Kit needs to read appid and other attributes from the AppGallery Connect configuration file. In some conditions, Map Kit can't read appid.
Conclusion
In this article, we have learned how to integrate HMS Map Kit in Xamarin based Android application. In this application, I have explained the use case of Map kit with Polyline draw.
Thanks for reading this article.
Be sure to like and comments on this article, if you found it helpful. It means a lot to me.
References
Xamarin Map Kit Plugin
Original Source
Related
More information like this, you can visit HUAWEI Developer Forum
Original link: https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201319969591990258&fid=0101187876626530001
Hello everyone,
In this article we are going to take a look at Huawei Mobile Services (HMS) integration Plugin for Unity. This article not going through the details of kits, we will focus on integration part.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
We will use account, In App purchases and push kit for this article. This plugin for now support five main kits.
Plugin Features :
1- Account Kit: Its provides developers with simple, secure, and quick sign-in and authorization functions. Instead of entering accounts and passwords and waiting for authorization, users can just tap the Sign In with HUAWEI ID button to quickly and securely sign in to your app. For details you read this article.
2- In App purchases: This service allows you to offer in-app purchases and facilitates in-app payment. Users can purchase a variety of virtual products, including one-time virtual products and subscriptions, directly within your app. For details you can read this article. Plugin supports the following types of products: Consumables, Non-consumables, Subscriptions
3- Huawei Ads: This Publisher Service utilizes Huawei’s vast user base and extensive data capabilities to deliver targeted, high quality ad content to users. With this service, your app will be able to generate revenues while bringing your users content which is relevant to them. For details you can read this article. Plugin supports the following types: Interstitial and rewarded videos
4- Push notifications: HUAWEI Push Kit is a messaging service provided by Huawei for developers. It establishes a messaging channel from the cloud to devices. By integrating HUAWEI Push Kit, developers can send messages to apps on users’ devices in real time. For details you can read this article.
5- Game kit: This kit provide player info, leaderboards and achievements.For details you can read this article.
HMS Plugin Work Flow:
Prerequisites
Step 1
Create an application on HUAWEI Developer(use this link for details).
Enable the HUAWEI services.
Step 2
This plugin have two branch for Unity versions.(version 2019 used)
Download plugin from this for Unity version 2019.x.
Download plugin from this for Unity version 2018.x .
Step 3
Import package to unity.
Step 4
Update “AndroidManifest file” and “agconnect.json” file.
1- Open project_path\Assets\Plugins\Android\ AndoridManifest.
Update App ID, CP ID and package name, this informations exist in agconnect.json file.
2- Open project_path\Assets\Huawei\agconnect.json replace with you downloanded from AGC.
Integration
This plugin have some demos scenes. You can drictly use this scenes. We will create empty scene, after that we will add objects.
1. Account kit:
Create empty object and add account manager script from Huawei package component.
(Object name is important, if you want to give diffrent name from “AccountManager” update ~~\Assets\Huawei\Account\AccountManager.cs -> line 11)
Add new script “AccountSignIn” to this object.
On AccountSignIn.cs:
Code:
using HuaweiMobileServices.Id;
using HuaweiMobileServices.Utils;
using UnityEngine;
using UnityEngine.UI;
using HmsPlugin;
public class AccountSignIn : MonoBehaviour
{
private const string NOT_LOGGED_IN = "No user logged in";
private const string LOGGED_IN = "{0} is logged in";
private const string LOGIN_ERROR = "Error or cancelled login";
private Text loggedInUser;
private AccountManager accountManager;
// Start is called before the first frame update
void Start()
{
loggedInUser = GameObject.Find("LoggedUserText").GetComponent<Text>();
loggedInUser.text = NOT_LOGGED_IN;
accountManager = AccountManager.GetInstance();
accountManager.OnSignInSuccess = OnLoginSuccess;
accountManager.OnSignInFailed = OnLoginFailure;
LogIn();
}
public void LogIn()
{
accountManager.SignIn();
}
public void LogOut()
{
accountManager.SignOut();
loggedInUser.text = NOT_LOGGED_IN;
}
public void OnLoginSuccess(AuthHuaweiId authHuaweiId)
{
loggedInUser.text = string.Format(LOGGED_IN, authHuaweiId.DisplayName);
}
public void OnLoginFailure(HMSException error)
{
loggedInUser.text = LOGIN_ERROR;
}
}
We can create sign in and sign out buttons, this buttons call “AccountSignIn” functions.
2. In App purchases:
Create empty object ( Object name is important, if you want to give diffrent name from “IapManager” update ~~\Assets\Huawei\IAP\IapManager.cs -> line 11).
Add new script “IapController.cs” to this object.
Add Account kit manager to IapManager object, its needed for using IAP services.
In App Purchases have three type of product.
1- Type 0: Consumables
Description:Consumables are used once, are depleted, and can be purchased again.
Example:Extra lives and gems in a game.
Getting Consumables products from Huawei AGC with HMS Unity plugin:
Code:
public void ObtainProductConsumablesInfo(IList<string> productIdConsumablesList)
{
if (iapAvailable != true)
{
OnObtainProductInfoFailure?.Invoke(IAP_NOT_AVAILABLE);
return;
}
ProductInfoReq productInfoReq = new ProductInfoReq
{
PriceType = 0,
ProductIds = productIdConsumablesList
};
iapClient.ObtainProductInfo(productInfoReq).AddOnSuccessListener((type0) =>
{
Debug.Log("[HMSPlugin]:" + type0.ErrMsg + type0.ReturnCode.ToString());
Debug.Log("[HMSPlugin]: Found " + type0.ProductInfoList.Count + "consumable products");
OnObtainProductInfoSuccess?.Invoke(new List<ProductInfoResult> { type0});
}).AddOnFailureListener((exception) =>
{
Debug.Log("[HMSPlugin]: ERROR Consumable ObtainInfo" + exception.Message);
OnObtainProductInfoFailure?.Invoke(exception);
});
}
2- Type 1: Non-consumables
Description: Non-consumables are purchased once and do not expire.
Example:Extra game levels in a game or permanent membership of an app
3- Type 2: Subscriptions
Description:Users can purchase access to value-added functions or content in a specified period of time. The subscriptions are automatically renewed on a recurring basis until users decide to cancel.
Example:Non-permanent membership of an app, such as a monthly video membership
On IAPController.cs
Code:
# define DEBUG
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HuaweiConstants;
using HuaweiMobileServices.Base;
using HuaweiMobileServices.IAP;
using System;
using UnityEngine.Events;
using HuaweiMobileServices.Id;
using HmsPlugin;
public class IapController : MonoBehaviour
{
public string[] ConsumableProducts;
public string[] NonConsumableProducts;
public string[] SubscriptionProducts;
[HideInInspector]
public int numberOfProductsRetrieved;
List<ProductInfo> productInfoList = new List<ProductInfo>();
List<string> productPurchasedList = new List<string>();
private IapManager iapManager;
private AccountManager accountManager;
UnityEvent loadedEvent;
void Awake()
{
Debug.Log("[HMSPlugin]: IAPP manager Init");
loadedEvent = new UnityEvent();
}
// Start is called before the first frame update
/// <summary>
///
/// </summary>
void Start()
{
Debug.Log("[HMS]: Started");
accountManager = GetComponent<AccountManager>();
Debug.Log(accountManager.ToString());
accountManager.OnSignInFailed = (error) =>
{
Debug.Log($"[HMSPlugin]: SignIn failed. {error.Message}");
};
accountManager.OnSignInSuccess = SignedIn;
accountManager.SignIn();
Debug.Log("[HMS]: Started2");
}
private void SignedIn(AuthHuaweiId authHuaweiId)
{
Debug.Log("[HMS]: SignedIn");
iapManager = GetComponent<IapManager>();
iapManager.OnCheckIapAvailabilitySuccess = LoadStore;
iapManager.OnCheckIapAvailabilityFailure = (error) =>
{
Debug.Log($"[HMSPlugin]: IAP check failed. {error.Message}");
};
iapManager.CheckIapAvailability();
}
private void LoadStore()
{
Debug.Log("[HMS]: LoadStorexxx");
// Set Callback for ObtainInfoSuccess
iapManager.OnObtainProductInfoSuccess = (productInfoResultList) =>
{
Debug.Log("[HMS]: LoadStore1");
if (productInfoResultList != null)
{
Debug.Log("[HMS]: LoadStore2");
foreach (ProductInfoResult productInfoResult in productInfoResultList)
{
foreach (ProductInfo productInfo in productInfoResult.ProductInfoList)
{
productInfoList.Add(productInfo);
}
}
}
loadedEvent.Invoke();
};
// Set Callback for ObtainInfoFailure
iapManager.OnObtainProductInfoFailure = (error) =>
{
Debug.Log($"[HMSPlugin]: IAP ObtainProductInfo failed. {error.Message},,, {error.WrappedExceptionMessage},,, {error.WrappedCauseMessage}");
};
// Call ObtainProductInfo
if (!IsNullOrEmpty(ConsumableProducts))
{
iapManager.ObtainProductConsumablesInfo(new List<string>(ConsumableProducts));
}
if (!IsNullOrEmpty(NonConsumableProducts))
{
iapManager.ObtainProductNonConsumablesInfo(new List<string>(NonConsumableProducts));
}
if (!IsNullOrEmpty(SubscriptionProducts))
{
iapManager.ObtainProductSubscriptionInfo(new List<string>(SubscriptionProducts));
}
}
private void RestorePurchases()
{
iapManager.OnObtainOwnedPurchasesSuccess = (ownedPurchaseResult) =>
{
productPurchasedList = (List<string>)ownedPurchaseResult.InAppPurchaseDataList;
};
iapManager.OnObtainOwnedPurchasesFailure = (error) =>
{
Debug.Log("[HMS:] RestorePurchasesError" + error.Message);
};
iapManager.ObtainOwnedPurchases();
}
public ProductInfo GetProductInfo(string productID)
{
return productInfoList.Find(productInfo => productInfo.ProductId == productID);
}
public void showHidePanelDynamically(GameObject yourObject)
{
Debug.Log("[HMS:] showHidePanelDynamically");
var getCanvasGroup = yourObject.GetComponent<CanvasGroup>();
if (getCanvasGroup.alpha == 0)
{
getCanvasGroup.alpha = 1;
getCanvasGroup.interactable = true;
}
else
{
getCanvasGroup.alpha = 0;
getCanvasGroup.interactable = false;
}
}
public void BuyProduct(string productID)
{
iapManager.OnBuyProductSuccess = (purchaseResultInfo) =>
{
// Verify signature with purchaseResultInfo.InAppDataSignature
// If signature ok, deliver product
// Consume product purchaseResultInfo.InAppDataSignature
iapManager.ConsumePurchase(purchaseResultInfo);
};
iapManager.OnBuyProductFailure = (errorCode) =>
{
switch (errorCode)
{
case OrderStatusCode.ORDER_STATE_CANCEL:
// User cancel payment.
Debug.Log("[HMS]: User cancel payment");
break;
case OrderStatusCode.ORDER_STATE_FAILED:
Debug.Log("[HMS]: order payment failed");
break;
case OrderStatusCode.ORDER_PRODUCT_OWNED:
Debug.Log("[HMS]: Product owned");
break;
default:
Debug.Log("[HMS:] BuyProduct ERROR" + errorCode);
break;
}
};
var productInfo = productInfoList.Find(info => info.ProductId == productID);
var payload = "test";
iapManager.BuyProduct(productInfo, payload);
}
public void addListener(UnityAction action)
{
if (loadedEvent != null)
{
loadedEvent.AddListener(action);
}
}
public bool IsNullOrEmpty(Array array)
{
return (array == null || array.Length == 0);
}
}
After getting all product informations from AGC, user can buy product with “BuyProduct” function with send product ids.
Buy Product Button Settings:
Select Prefab:
Select Function:
Add Parameter:
IapManager.cs:
Code:
# define DEBUG
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HuaweiConstants;
using HuaweiMobileServices.Base;
using HuaweiMobileServices.IAP;
using System;
using UnityEngine.Events;
using HuaweiMobileServices.Id;
using HmsPlugin;
public class IapController : MonoBehaviour
{
public string[] ConsumableProducts;
public string[] NonConsumableProducts;
public string[] SubscriptionProducts;
[HideInInspector]
public int numberOfProductsRetrieved;
List<ProductInfo> productInfoList = new List<ProductInfo>();
List<string> productPurchasedList = new List<string>();
private IapManager iapManager;
private AccountManager accountManager;
UnityEvent loadedEvent;
void Awake()
{
Debug.Log("[HMSPlugin]: IAPP manager Init");
loadedEvent = new UnityEvent();
}
// Start is called before the first frame update
/// <summary>
///
/// </summary>
void Start()
{
Debug.Log("[HMS]: Started");
accountManager = GetComponent<AccountManager>();
Debug.Log(accountManager.ToString());
accountManager.OnSignInFailed = (error) =>
{
Debug.Log($"[HMSPlugin]: SignIn failed. {error.Message}");
};
accountManager.OnSignInSuccess = SignedIn;
accountManager.SignIn();
Debug.Log("[HMS]: Started2");
}
private void SignedIn(AuthHuaweiId authHuaweiId)
{
Debug.Log("[HMS]: SignedIn");
iapManager = GetComponent<IapManager>();
iapManager.OnCheckIapAvailabilitySuccess = LoadStore;
iapManager.OnCheckIapAvailabilityFailure = (error) =>
{
Debug.Log($"[HMSPlugin]: IAP check failed. {error.Message}");
};
iapManager.CheckIapAvailability();
}
private void LoadStore()
{
Debug.Log("[HMS]: LoadStorexxx");
// Set Callback for ObtainInfoSuccess
iapManager.OnObtainProductInfoSuccess = (productInfoResultList) =>
{
Debug.Log("[HMS]: LoadStore1");
if (productInfoResultList != null)
{
Debug.Log("[HMS]: LoadStore2");
foreach (ProductInfoResult productInfoResult in productInfoResultList)
{
foreach (ProductInfo productInfo in productInfoResult.ProductInfoList)
{
productInfoList.Add(productInfo);
}
}
}
loadedEvent.Invoke();
};
// Set Callback for ObtainInfoFailure
iapManager.OnObtainProductInfoFailure = (error) =>
{
Debug.Log($"[HMSPlugin]: IAP ObtainProductInfo failed. {error.Message},,, {error.WrappedExceptionMessage},,, {error.WrappedCauseMessage}");
};
// Call ObtainProductInfo
if (!IsNullOrEmpty(ConsumableProducts))
{
iapManager.ObtainProductConsumablesInfo(new List<string>(ConsumableProducts));
}
if (!IsNullOrEmpty(NonConsumableProducts))
{
iapManager.ObtainProductNonConsumablesInfo(new List<string>(NonConsumableProducts));
}
if (!IsNullOrEmpty(SubscriptionProducts))
{
iapManager.ObtainProductSubscriptionInfo(new List<string>(SubscriptionProducts));
}
}
private void RestorePurchases()
{
iapManager.OnObtainOwnedPurchasesSuccess = (ownedPurchaseResult) =>
{
productPurchasedList = (List<string>)ownedPurchaseResult.InAppPurchaseDataList;
};
iapManager.OnObtainOwnedPurchasesFailure = (error) =>
{
Debug.Log("[HMS:] RestorePurchasesError" + error.Message);
};
iapManager.ObtainOwnedPurchases();
}
public ProductInfo GetProductInfo(string productID)
{
return productInfoList.Find(productInfo => productInfo.ProductId == productID);
}
public void showHidePanelDynamically(GameObject yourObject)
{
Debug.Log("[HMS:] showHidePanelDynamically");
var getCanvasGroup = yourObject.GetComponent<CanvasGroup>();
if (getCanvasGroup.alpha == 0)
{
getCanvasGroup.alpha = 1;
getCanvasGroup.interactable = true;
}
else
{
getCanvasGroup.alpha = 0;
getCanvasGroup.interactable = false;
}
}
public void BuyProduct(string productID)
{
iapManager.OnBuyProductSuccess = (purchaseResultInfo) =>
{
// Verify signature with purchaseResultInfo.InAppDataSignature
// If signature ok, deliver product
// Consume product purchaseResultInfo.InAppDataSignature
iapManager.ConsumePurchase(purchaseResultInfo);
};
iapManager.OnBuyProductFailure = (errorCode) =>
{
switch (errorCode)
{
case OrderStatusCode.ORDER_STATE_CANCEL:
// User cancel payment.
Debug.Log("[HMS]: User cancel payment");
break;
case OrderStatusCode.ORDER_STATE_FAILED:
Debug.Log("[HMS]: order payment failed");
break;
case OrderStatusCode.ORDER_PRODUCT_OWNED:
Debug.Log("[HMS]: Product owned");
break;
default:
Debug.Log("[HMS:] BuyProduct ERROR" + errorCode);
break;
}
};
var productInfo = productInfoList.Find(info => info.ProductId == productID);
var payload = "test";
iapManager.BuyProduct(productInfo, payload);
}
public void addListener(UnityAction action)
{
if (loadedEvent != null)
{
loadedEvent.AddListener(action);
}
}
public bool IsNullOrEmpty(Array array)
{
return (array == null || array.Length == 0);
}
}
3. Push notifications:
First add your project PushKitManager prefab, this prefab using PushKitManager.cs for creating tokens.
For push kit, we have a two choice,
We can send notification to all devices without token.
We can get tokens from devices and we can use this token to send notification sepicified devices.
AGC server Push Scope for sending notification:
On PushKitManager.cs
Code:
using HuaweiMobileServices.Base;
using HuaweiMobileServices.Id;
using HuaweiMobileServices.Push;
using HuaweiMobileServices.Utils;
using System;
using UnityEngine;
using UnityEngine.UI;
namespace HmsPlugin
{
public class PushKitManager : MonoBehaviour, IPushListener
{
public Action<string> OnTokenSuccess { get; set; }
public Action<Exception> OnTokenFailure { get; set; }
public Action<RemoteMessage> OnMessageReceivedSuccess { get; set; }
// Start is called before the first frame update
void Start()
{
PushManager.Listener = this;
var token = PushManager.Token;
Debug.Log($"[HMS] Push token from GetToken is {token}");
if (token != null)
{
OnTokenSuccess?.Invoke(token);
}
}
public void OnNewToken(string token)
{
Debug.Log($"[HMS] Push token from OnNewToken is {token}");
if (token != null)
{
OnTokenSuccess?.Invoke(token);
}
}
public void OnTokenError(Exception e)
{
Debug.Log("Error asking for Push token");
Debug.Log(e.StackTrace);
OnTokenFailure?.Invoke(e);
}
public void OnMessageReceived(RemoteMessage remoteMessage)
{
OnMessageReceivedSuccess?.Invoke(remoteMessage);
}
}
}
Push Notification result:
Thank you reading this article.
I hope this gives you a starting point for Huawei Mobile Services and Unity integration.
Introduction
This article is based on Huawei Mobile Services application. I have developed Trip Booking Android app. We can provide the solution for HMS based multiple kits such as Account Kit, Huawei Ads, Huawei Map, and Huawei Analysts to use in Trip Booking. So users can book any trip.
In this application, users can plan trips and book their trips. It will provide the ongoing trip cities wise with weather forecasting so that user can easily plan a trip.
In this article, I will integrate Weather API, Huawei Map, and Huawei Map Direction API, so that users can check the route and plan their trips, and book with the trip weather forecast.
Huawei Map
HMS Core Map SDK is a set of APIs for map development in Android. The map data covers most countries outside China and supports multiple languages. The Map SDK uses the WGS 84 GPS coordinate system, which can meet most requirements of map development outside China. You can easily add map-related functions in your Android app, including:
1. Map display: Displays buildings, roads, water systems, and Points of Interest (POIs).
2. Map interaction: Controls the interaction gestures and buttons on the map.
3. Map drawing: Adds location markers, map layers, overlays, and various shapes.
Prerequisite
1. A computer (desktop or laptop)
2. A Huawei phone, which is used to debug the developed app
3. HUAWEI Analytics Kit 5.0.3
4. Android SDK applicable to devices using Android API-Level 19 (Android 4.4 KitKat) or higher
5. Android Studio
6. Java JDK 1.7 or later (JDK 1.8 recommended).
Things Need To Be Done
To integrate HUAWEI HMS Core services, you must complete the following preparations:
1. Create an app in AppGallery Connect.
2. Create an Android Studio project.
3. Add the app package name and save the configuration file.
4. Configure the Maven repository address and AppGallery Connect gradle plug-in.
Integration
1. Sign in to AppGallery Connect and select my projects.
2. Navigate to app to enable Map Kit.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
3. Navigate to project setting and download the configuration file.
4. Add the Maven repository address to repositories.
Code:
buildscript {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
classpath 'com.huawei.agconnect:agcp:1.2.0.300'
}
}
allprojects {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
google()
jcenter()
}
}
5. Add the AppGallery Connect dependency to dependencies.
Code:
//map
implementation 'com.huawei.hms:maps:4.0.0.301'
6. I have created the following class in which I have implemented Map Kit.
Code:
public class PolylineActivity extends AppCompatActivity implements OnMapReadyCallback {
public static final String TAG = "PolylineActivity";
private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private HuaweiMap hmap;
private MapView mMapView;
private Marker mMarker;
private List<LatLng> latLngList;
private MapApiViewModel mapApiViewModel;
@Override
protected void onStart() {
super.onStart();
mMapView.onStart();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_polyline);
mapApiViewModel = ViewModelProviders.of(this).get(MapApiViewModel.class);
mMapView = findViewById(R.id.mapview_mapviewdemo);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(PolylineActivity.this);
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
protected void onStop() {
super.onStop();
mMapView.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
@Override
public void onMapReady(HuaweiMap map) {
hmap = map;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
hmap.setMyLocationEnabled(false);
hmap.setTrafficEnabled(true);
hmap.getUiSettings().setRotateGesturesEnabled(true);
hmap.getUiSettings().setCompassEnabled(false);
hmap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLngList.get(0), 12.0f));
hmap.addMarker(new MarkerOptions().position(latLngList.get(0)));
mapApiViewModel.getPolylineLiveData(getPolylineBody()).observe(this, result -> {
Log.d(TAG, result.toString());
getPolylineData(result);
});
}
Huawei Map Direction API
Huawei Map provides Direction API, so that user can access all the information related to Map in RESTful API.
Huawei has provide the following API endpoint to access Direction API.
https://mapapi.cloud.huawei.com/mapApi/v1
Huawei provide the following direction API:
1. Walking Route Planning
2. Bicycling Route Planning
3. Driving Route Planning
I have implemented the Driving Route API with the help of Retrofit and MVVM.
Retrofit Client
I have created MapApiClient class for accessing the Direction API.
Code:
public class MapApiClient {
private final static HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
private static OkHttpClient okHttpClient;
public static Service getClient() {
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Consants.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
return retrofit.create(Service.class);
}
public interface Service {
@POST("mapApi/v1/routeService/driving")
Single<PolylineResponse> getPolylines(
@Query("key") String apiKey,
@Body PolylineBody polylineBody);
}
}
API Repository
I have created MapApiRepo class for accessing the API client.
Code:
public class MapApiRepo {
private MapApiClient.Service mService;
public MapApiRepo() {
this.mService = MapApiClient.getClient();
}
public Single<PolylineResponse> executeMapApi(PolylineBody polylineBody) {
return mService.getPolylines(Consants.API_KEY, polylineBody);
}
}
ViewModel
I have created MapApiViewModel class for handling the API calls.
Code:
public class MapApiViewModel extends ViewModel {
private final CompositeDisposable disposables = new CompositeDisposable();
private MapApiRepo mapApiRepo = new MapApiRepo();
private MutableLiveData<PolylineResponse> mPolylineLiveData = new MutableLiveData<>();
public LiveData<PolylineResponse> getPolylineLiveData(PolylineBody body) {
disposables.add(mapApiRepo.executeMapApi(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mPolylineLiveData.setValue(result),
throwable -> mPolylineLiveData.setValue(null)
));
return mPolylineLiveData;
}
@Override
protected void onCleared() {
disposables.clear();
}
}
Drawing Polyline
I have implemented this functionality in the following activity.
Code:
mapApiViewModel.getPolylineLiveData(getPolylineBody()).observe(this, result -> {
Log.d(TAG, result.toString());
getPolylineData(result);
}); private PolylineBody getPolylineBody() {
PolylineBody polylineBody = new PolylineBody();
Origin origin = new Origin();
origin.setLat("30.0444");
origin.setLng("31.2357");
Destination destination = new Destination();
destination.setLat("30.0131");
destination.setLng("31.2089");
polylineBody.setDestination(destination);
polylineBody.setOrigin(origin);
return polylineBody;
}
public void getPolylineData(PolylineResponse polylineResponse) {
List<Routes> routesList = polylineResponse.getRoutes();
List<Paths> paths = new ArrayList<>();
List<Steps> steps = new ArrayList<>();
List<Polyline> polylines = new ArrayList<>();
latLngList = new ArrayList<>();
for (int x = 0; x < routesList.size(); x++) {
//here we can access each array list with main.get(x).
for (Paths paths1 : routesList.get(x).getPaths()) {
paths.add(paths1);
}
for (int y = 0; y < paths.size(); y++) {
for (Steps step :
paths.get(y).getSteps()) {
steps.add(step);
}
}
for (int i = 0; i < steps.size(); i++) {
for (Polyline polyline :
steps.get(i).getPolyline()) {
polylines.add(polyline);
}
}
}
for (int i = 0; i < polylines.size(); i++) {
latLngList.add(new LatLng(Double.valueOf(polylines.get(i).getLat())
, Double.valueOf(polylines.get(i).getLng())));
}
hmap.addPolyline(new PolylineOptions()
.addAll(latLngList)
.color(Color.BLUE)
.width(3));
}
Weather API
I have used weatherstack api to get city weather condition.
https://api.weatherstack.com/
WeatherRetrofit Client
I have implemented Weather API using retrofit library with RxJava2.
Code:
public class Client {
private final static HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
private static OkHttpClient okHttpClient;
public static Service getClient() {
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Utils.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
return retrofit.create(Service.class);
}
public interface Service {
@GET("current")
Single<CurrentWeather> getCurrentWeather(
@Query("access_key") String apiKey,
@Query("query") String cityName);
@GET("forecast")
Single<ForcastWeather> getForecastWeather(
@Query("access_key") String apiKey,
@Query("query") String cityName,
@Query("forecast_days") String days);
}
}
App Development
I have created the following package inside the project. In which I have integrated Huawei Id Login, Huawei Analytics, Huawei Banner Ads, Weather API, Huawei Map, and Huawei Direction APIs.
LoginActivity
In this screen, I have integrated login functionality with Huawei Id along with Analytics Kit which logs the event.
Code:
if (authHuaweiIdTask.isSuccessful()) {
AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
Log.i(TAG, huaweiAccount.getDisplayName() + " signIn success ");
Log.i(TAG, "AccessToken: " + huaweiAccount.getAccessToken());
Bundle bundle = new Bundle();
bundle.putString(TAG,huaweiAccount.getDisplayName() + " signIn success ");
Analystics.getInstance(this).setEvent("login",bundle);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("user", huaweiAccount.getDisplayName());
startActivity(intent);
this.finish();
}
HomeFragment
In this screen, I have implemented Huawei Ads and Analytics kit.
Which helps to log the user activity and shown banner ads.
Loading Banner Ads
Code:
private void initAds(View view) {
HwAds.init(getActivity());
hwBannerView = view.findViewById(R.id.huawei_banner_view);
hwBannerView.setVisibility(View.VISIBLE);
AdParam adParam = new AdParam.Builder().build();
hwBannerView.loadAd(adParam);
hwBannerView.setAdListener(adListener);
}
Log User Events
Code:
Bundle bundle = new Bundle();
bundle.putString(TAG,"City loaded");
Analystics.getInstance(getActivity()).setEvent("City",bundle);
cityList.setLayoutManager(new GridLayoutManager(getActivity(), 2));
cityList.setAdapter(new CityAdapter(cities, (item) -> {
Bundle bundle1 = new Bundle();
bundle.putString(TAG,"City Clicked"+item.getCityName());
Analystics.getInstance(getActivity()).setEvent("City",bundle1);
PopularCity popularCity = item;
Intent intent = new Intent(getActivity(), CityInfoDetailActivity.class);
intent.putExtra("name", popularCity.getCityName());
intent.putExtra("url", popularCity.getImageUrl());
startActivity(intent);
}));
CityInfoDetailActivity
In this screen, I have implemented the Huawei Banner ads and Huawei Analytics.
Loading Banner Ads
Code:
HwAds.init(this);
hwBannerView = findViewById(R.id.huawei_banner_view);
hwBannerView.setVisibility(View.VISIBLE);
AdParam adParam = new AdParam.Builder().build();
hwBannerView.loadAd(adParam);
hwBannerView.setAdListener(adListener);
Log User Events
Code:
if (extras != null) {
String name = extras.getString("name");
String imageUrl = extras.getString("url");
setTitle(name);
Glide.with(this).load(imageUrl).into(cityImage);
Bundle bundle = new Bundle();
bundle.putString(TAG,"City Info");
Analystics.getInstance(this).setEvent("City Details",bundle);
}
AllTripActivity
Code:
public class AllTripActivity extends AppCompatActivity {
private RecyclerView tripList;
private static final String TAG= AllTripActivity.class.getName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alltrips);
init();
}
private void init() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
tripList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true));
tripList.setAdapter(new TripAdapter(list, (item) -> {
Intent intent = new Intent(this, PolylineActivity.class);
startActivity(intent);
}));
}
}
TripAdapter
Code:
public class TripAdapter extends RecyclerView.Adapter<TripAdapter.ViewHolder> {
private List<TripModel> list;
private ItemTripBinding mBinding;
private OnItemClickListener<TripModel> mOnItemClickListener;
public TripAdapter(List<TripModel> list, OnItemClickListener<TripModel> onItemClickListener) {
this.list = list;
this.mOnItemClickListener = onItemClickListener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
R.layout.item_trip, parent, false);
return new ViewHolder(mBinding);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(list.get(position), mOnItemClickListener);
}
@Override
public int getItemCount() {
return list.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private ItemTripBinding cityBinding;
public ViewHolder(@NonNull ItemTripBinding cityBinding) {
super(cityBinding.getRoot());
this.cityBinding = cityBinding;
}
public void bind(TripModel item, OnItemClickListener<TripModel> listener) {
cityBinding.setData(item);
itemView.setOnClickListener(v -> listener.onItemClick(item));
}
}
}
WeatherDetailActivity
In this screen, I have implemented the weather related information so that users can identify the city weather condition.
Code:
public class WeatherDetailActivity extends AppCompatActivity {
private ActivityWeatherBinding mMainBinding;
private WeatherViewModel mWeatherViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_weather);
mWeatherViewModel = ViewModelProviders.of(this).get(WeatherViewModel.class);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String name = extras.getString("name");
mMainBinding.txtCityName.setText(name);
mMainBinding.txtTemperature.setText("21" + "\u00B0");
fetchWeatherDetails(name, false);
setForcastData();
}
}
@SuppressLint("SetTextI18n")
private void fetchWeatherDetails(String cityName, boolean isSearching) {
mWeatherViewModel.getCurrentWeatherLiveData(cityName).observeForever(result -> {
if (result != null) {
mMainBinding.txtCityName.setText(result.getLocation().getName());
mMainBinding.txtTemperature.setText(result.getCurrent().getTemperature() + "\u00B0");
}
});
}
private void setForcastData() {
List<CurrentWeather.Current> currents = new ArrayList<>();
for (int i = 1; i <= 7; i++) {
CurrentWeather.Current current = new CurrentWeather().new Current();
current.setObservationTime("Day");
current.setTemperature(21);
current.setIsDay(url);
currents.add(current);
}
mMainBinding.recycleWeeklyWeather.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true));
mMainBinding.recycleWeeklyWeather.setAdapter(new ForcastAdapter(currents));
}
}
WeatherViewModel
Code:
public class WeatherViewModel extends ViewModel {
private WeatherRepository mWeatherRepository = new WeatherRepository();
private final CompositeDisposable disposables = new CompositeDisposable();
private MutableLiveData<CurrentWeather> mWeatherLiveData = new MutableLiveData<>();
private MutableLiveData<ForcastWeather> mForcastWeatherLiveData = new MutableLiveData<>();
public LiveData<CurrentWeather> getCurrentWeatherLiveData(String city) {
disposables.add(mWeatherRepository.executeCurrentWeatherApi(city)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mWeatherLiveData.setValue(result),
throwable -> mWeatherLiveData.setValue(null)
));
return mWeatherLiveData;
}
public LiveData<ForcastWeather> getForcastWeatherLiveData(String city) {
disposables.add(mWeatherRepository.executeForcastWeatherApi(city)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mForcastWeatherLiveData.setValue(result),
throwable -> mForcastWeatherLiveData.setValue(null)
));
return mForcastWeatherLiveData;
}
@Override
protected void onCleared() {
disposables.clear();
}
}
Launch the application
Let us launch our application, see the result
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
Hello everyone, In this article, I will talk about how we can use together Geofence and Push Kit. When the device enters a set location, we will send a notification to the user using Push Kit.
Geofence : It is an important feature in the Location Kit. Geofence is actually used to draw a geographic virtual boundary.
Push Kit : Push kit is essentially a messaging service. There are two different message types. These are notification and data messages. We will use the notification messages in this article.
1- Huawei Core Integration
To use Geofence and Push kit services, you must first integrate the necessary kits into your project. You can use the document in the link to easily integrate the Location and Push kit into your project.
2- Add Permissions
After the HMS Core integration is finished, we need to add permissions to the AndroidManifest.xml file in order to access the user’s location and internet.
XML:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
3- Developing the Push Kit Part
To send a notification to the device using a push kit, firstly the device must receive a push token.
Java:
private void getPushToken() {
new Thread() {
@Override
public void run() {
super.run();
try {
String appId = AGConnectServicesConfig.fromContext(MainActivity.this).getString("client/app_id");
String token = HmsInstanceId.getInstance(MainActivity.this).getToken(appId, "HCM");
if (!TextUtils.isEmpty(token)) {
DataStore.pushToken = token;
}
} catch (ApiException e) {
Log.e("TokenFailed", "get token failed" + e);
}
}
}.start();
}
We have received a push token, now we need to reach the access token, and we will do this through the service. We will obtain access token through the service, you must also complete the Retrofit implementations. Add Retrofit libraries app level build.gradle
Code:
implementation "com.squareup.retrofit2:retrofit:2.3.0"
implementation "com.squareup.retrofit2:converter-gson:2.3.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
In order to send access token, first we should prepare our request. This request should have grant_type ,client_id ,client_secret and will return AccessToken. Then, we will use this AccessToken for out further requests.
Java:
public interface AccessTokenInterface {
@FormUrlEncoded
@POST("v2/token")
Call<AccessToken> GetAccessToken(
@Field("grant_type") String grantType,
@Field("client_id") int clientId,
@Field("client_secret") String clientSecret);
}
Now let’s handle the method by which we will obtain the access token. We need a Base URL to use in this method. The variable defined as OAUTH_BASE_URL represents our base URL. Do not forget to fill in client_credentials, YOUR_CLIENT_ID, YOUR_CLIENT_SECRET parts according to your project. This getAccessToken() was created using Synchronous Call. You can do this with Asynchronous Call according to the needs of your own project.
Java:
public void getAccessToken() {
String YOUR_CLIENT_SECRET =" ";
int YOUR_CLIENT_ID = ;
AccessInterface apiInterface = RetrofitClient.getClient(OAUTH_BASE_URL).create(AccessInterface.class);
Call<AccessToken> call = apiInterface.GetAccessToken("client_credentials",YOUR_CLIENT_ID , YOUR_CLIENT_SECRET);
try{
Response<AccessToken> response = call.execute();
accessToken = String.format("Bearer %s",response.body().getAccessToken());
}catch (IOException e){
e.printStackTrace();
}
}
After obtaining the access token, we create an interface to send a notification with the push kit. Do not forget to fill the {YOUR_APP_ID} part of your project app ID.
Java:
public interface NotificationInterface {
@Headers("Content-Type:application/json")
@POST("{YOUR_APP_ID}/messages:send")
Call<PushParameter> sendNotification(
@Header("Authorization") String authorization,
@Body NotificationMessage notificationMessage);
}
Java:
public void sendNotification(String accesstoken, String geofenceDetail) {
NotificationInterface notiInterface = RetrofitClient.getClient(PUSH_API_URL).create(NotificationInterface.class);
NotificationMessage notificationMessage = (new NotificationMessage
.Builder("Title of Notification", geofenceDetail, DataStore.pushToken, "1"))
.build();
Call<PushParameter> callNoti = notiInterface.sendNotification(accesstoken, notificationMessage);
callNoti.enqueue(new Callback<PushParameter>() {
@Override
public void onResponse(Call<PushParameter> call, Response<PushParameter> response) {
Log.i("SendNotification", response.body().getMsg());
}
@Override
public void onFailure(Call<PushParameter> call, Throwable t) {
Log.i("SendNotification Failure", t.toString());
}
});
}
4- Developing the Geofence Part
We have set up the push kit to send notifications, now let’s see how we will send these notifications for geofence. First we create a broadcast receiver for geofence.
Java:
public class GeofenceBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
GeofenceNotification.enqueueWork(context,intent);
}
}
When the Broadcast Receiver is triggered, our geofence notifications will be sent through this class. You can see the accessToken and sendNotification methods we use for push kit in this class.
Java:
public class GeofenceNotification extends JobIntentService {
public static final String PUSH_API_URL = "https://push-api.cloud.huawei.com/v1/";
public static final String OAUTH_BASE_URL = "https://login.cloud.huawei.com/oauth2/";
private String accessToken;
public static void enqueueWork(Context context, Intent intent) {
enqueueWork(context, GeofenceNotification.class, 573, intent);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
GeofenceData geofenceData = GeofenceData.getDataFromIntent(intent);
if (geofenceData != null) {
int conversion = geofenceData.getConversion();
ArrayList<Geofence> geofenceTransition = (ArrayList<Geofence>) geofenceData.getConvertingGeofenceList();
String geofenceTransitionDetails = getGeofenceTransitionDetails(conversion,
geofenceTransition);
getAccessToken();
sendNotification(accessToken, geofenceTransitionDetails);
}
}
private String getGeofenceTransitionDetails(int conversion, ArrayList<Geofence> triggeringGeofences) {
String geofenceConversion = getConversionString(conversion);
ArrayList<String> triggeringGeofencesIdsList = new ArrayList<>();
for (Geofence geofence : triggeringGeofences) {
triggeringGeofencesIdsList.add(geofence.getUniqueId());
}
String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList);
return String.format("%s: %s",geofenceConversion,triggeringGeofencesIdsString);
}
private String getConversionString(int conversionType) {
switch (conversionType) {
case Geofence.ENTER_GEOFENCE_CONVERSION:
return getString(R.string.geofence_transition_entered);
case Geofence.EXIT_GEOFENCE_CONVERSION:
return getString(R.string.geofence_transition_exited);
case Geofence.DWELL_GEOFENCE_CONVERSION:
return getString(R.string.geofence_transition_dwell);
default:
return getString(R.string.unknown_geofence_transition);
}
}
public void sendNotification(String accesstoken, String geofenceDetail) {
NotificationInterface notiInterface = RetrofitClient.getClient(PUSH_API_URL).create(NotificationInterface.class);
NotificationMessage notificationMessage = (new NotificationMessage.Builder("Title of Notification", geofenceDetail, DataClass.pushToken, "1")).build();
Call<PushParameter> callNoti = notiInterface.sendNotification(accesstoken, notificationMessage);
callNoti.enqueue(new Callback<PushParameter>() {
@Override
public void onResponse(Call<PushParameter> call, Response<PushParameter> response) {
Log.i("SendNotification", response.body().getMsg());
}
@Override
public void onFailure(Call<PushParameter> call, Throwable t) {
Log.i("SendNotification Failure", t.toString());
}
});
}
public void getAccessToken() {
String YOUR_CLIENT_SECRET =" ";
int YOUR_CLIENT_ID = ;
AccessInterface apiInterface = RetrofitClient.getClient(OAUTH_BASE_URL).create(AccessInterface.class);
Call<AccessToken> call = apiInterface.GetAccessToken("client_credentials",YOUR_CLIENT_ID , YOUR_CLIENT_SECRET);
try{
Response<AccessToken> response = call.execute();
accessToken = String.format("Bearer %s",response.body().getAccessToken());
}catch (IOException e){
e.printStackTrace();
}
}
}
Then we add the methods we use to create a geofence list. In this project, I have defined geofences as static. You can adjust these geofence information according to the needs of your application. For example, if your location information is kept in the database, you can use geofence locations from the database. When adding geofences in the completeGeofenceList method, pay attention to the unique id part. If you try to add geofences with the same ids, you will get an error.
Java:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = "MainActivity";
private FusedLocationProviderClient fusedLocationProviderClient;
private PendingIntent geofencePendingIntent;
private ArrayList<Geofence> geofenceList;
private GeofenceService geofenceService;
private SettingsClient settingsClient;
LocationCallback locationCallback;
LocationRequest locationRequest;
private String pushToken;
private Marker mMarker;
private MapView mapView;
private HuaweiMap hMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
permissionCheck();
mapView = findViewById(R.id.mapView);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle("MapViewBundleKey");
}
mapView.onCreate(mapViewBundle);
mapView.getMapAsync(this);
geofenceService = LocationServices.getGeofenceService(getApplicationContext());
getPushToken();
completeGeofenceList();
createGeofence();
}
public void onMapReady(HuaweiMap huaweiMap) {
hMap = huaweiMap;
hMap.setMyLocationEnabled(true);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1){
if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSION successful");
} else {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSSION failed");
}
}
else if (requestCode == 2) {
if (grantResults.length > 2 && grantResults[2] == PackageManager.PERMISSION_GRANTED
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION successful");
} else {
Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION failed");
}
}
}
private void permissionCheck(){
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
Log.i(TAG, "sdk < 28 Q");
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings =
{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, strings, 1);
}
} else {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION};
ActivityCompat.requestPermissions(this, strings, 2);
}
}
}
private GeofenceRequest getGeofencingRequest() {
return new GeofenceRequest.Builder()
.setInitConversions(GeofenceRequest.ENTER_INIT_CONVERSION)
.createGeofenceList(geofenceList)
.build();
}
private PendingIntent getGeofencePendingIntent() {
Intent intent = new Intent(MainActivity.this, GeofenceBroadcast.class);
geofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return geofencePendingIntent;
}
private void completeGeofenceList() {
Geofence.Builder geoBuild = new Geofence.Builder();
geofenceList = new ArrayList<>();
geofenceList.add(geoBuild.setUniqueId("Home").setRoundArea(39.617841289998736,27.429383486070098,200).setValidContinueTime(Geofence.GEOFENCE_NEVER_EXPIRE).setConversions(Geofence.ENTER_GEOFENCE_CONVERSION).setDwellDelayTime(1000).build());
geofenceList.add(geoBuild.setUniqueId("Office").setRoundArea(38.14893633264862,26.82832426954628,200).setValidContinueTime(Geofence.GEOFENCE_NEVER_EXPIRE).setConversions(Geofence.ENTER_GEOFENCE_CONVERSION).setDwellDelayTime(1000).build());
}
private void createGeofence() {
geofenceService.createGeofenceList(getGeofencingRequest(), getGeofencePendingIntent());
}
private void getPushToken() {
new Thread() {
@Override
public void run() {
super.run();
try {
String appId = AGConnectServicesConfig.fromContext(MainActivity.this).getString("client/app_id");
String token = HmsInstanceId.getInstance(MainActivity.this).getToken(appId, "HCM");
if (!TextUtils.isEmpty(token)) {
DataStore.pushToken1 = token;
}
} catch (ApiException e) {
Log.e("TokenFailed", "get token failed" + e);
}
}
}.start();
}
}
Sample application outputs for the use of push kit with geofence are as follows;
Conclusion
By using the push kit features, you can personalize your notifications according to the needs of your application. In this article I explained how to use the Push kit for Geofence notifications. I hope you will like it. Thank you for reading. If you have any questions, you can leave a comment.
References
Geofence Service
Push Kit
Overview
In this article, I will create a College Campus Placement Centre Demo App which highlights ongoing college placement with all listed companies and their details. Student can easily apply and redeem points through IAP. I have integrated HMS Account and IAP Kit which is based on Cross-platform Technology Xamarin.
HMS IAP Service Introduction
HMS In-App Purchase Kit allows purchasing any product from the application with highly secure payment. Users can purchase a variety of products or services, including common virtual products and subscriptions, directly within your app. It also provides a product management system (PMS) for managing the prices and languages of in-app products (including games) in multiple locations.
These are the following 3 types of in-app products supported by the IAP:
1. Consumable: Consumables are used once, are depleted, and can be purchased again.
2. Non-consumable: Non-consumables are purchased once and do not expire.
3. Auto-renewable subscriptions: Users can purchase access to value-added functions or content in a specified period of time. The subscriptions are automatically renewed on a recurring basis until users decide to cancel.
Account Kit Service Introduction
HMS Account Kit allows you to connect to the Huawei ecosystem using your HUAWEI ID from a range of devices, such as mobile phones, tablets, and smart screens.
It’s a simple, secure, and quick sign-in and authorization functions. Instead of entering accounts and passwords and waiting for authentication.
Complies with international standards and protocols such as OAuth2.0 and OpenID Connect, and supports two-factor authentication (password authentication and mobile number authentication) to ensure high security.
Prerequisite
1. Xamarin Framework
2. Huawei phone
3. Visual Studio 2019
App Gallery Integration process
1. Sign In and Create or Choose a project on AppGallery Connect portal.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
2. Navigate to Project settings > download the configuration file.
3. Navigate to General Information > Data Storage location.
4. Navigate to Manage APIs > enable APIs to require by an application.
5. Navigate to My apps > Operate, and then enter details in Add Product.
6. Click View and Edit in the above screenshot, enter Product price details, and then click Save.
7. Click Activate for product activation.
Xamarin Account Kit Setup Process
1. Download Xamarin Plugin all the aar and zip files from below URL:
Document
developer.huawei.com
2. Open the XHwid-5.03.302.sln solution in Visual Studio.
Xamarin IAP Kit Setup Process
1. Download Xamarin Plugin all the aar and zip files from below URL:
Document
developer.huawei.com
2. Open the XIAP-5.0.2.300.sln solution in Visual Studio.
3. Navigate to Solution Explorer and Right-click on jar Add > Existing Item and choose aar file which downloads in Step 1.
4. Right-click on added aar file then choose Properties > Build Action > LibraryProjectZip.
Note: Repeat Step 3 & 4 for all aar file.
5. Build the Library and make dll files.
Xamarin App Development
1. Open Visual Studio 2019 and Create a new project.
2. Navigate to Solution Explore > Project > Assets > Add JSON file.
3. Navigate to Solution Explore > Project > Add > Add New Folder.
4. Navigate to Folder(created) > Add > Add Existing and add all DLL files.
5. Right click > Properties > Build Action > None.
6. Navigate to Solution Explore > Project > Reference > Right Click > Add References then Navigate to Browse and add all DLL files from the recently added Folder.
7. Added reference then click Ok.
MainActivity.cs
This activity performs all the operation regarding login with Huawei Id.
Java:
using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using Android.Support.V7.App;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Agconnect.Config;
using Com.Huawei.Hmf.Tasks;
using Com.Huawei.Hms.Common;
using Com.Huawei.Hms.Iap;
using Com.Huawei.Hms.Iap.Entity;
using Com.Huawei.Hms.Support.Hwid;
using Com.Huawei.Hms.Support.Hwid.Request;
using Com.Huawei.Hms.Support.Hwid.Result;
using Com.Huawei.Hms.Support.Hwid.Service;
namespace PlacementApp
{
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
private Button btnLoginWithHuaweiId;
private HuaweiIdAuthParams mAuthParam;
public static IHuaweiIdAuthService mAuthManager;
private static String TAG = "MainActivity";
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
btnLoginWithHuaweiId = FindViewById<Button>(Resource.Id.btn_huawei_id);
// Write code for Huawei id button click
mAuthParam = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DefaultAuthRequestParam)
.SetIdToken().SetEmail()
.SetAccessToken()
.CreateParams();
mAuthManager = HuaweiIdAuthManager.GetService(this, mAuthParam);
// Click listener for each button
btnLoginWithHuaweiId.Click += delegate
{
StartActivityForResult(mAuthManager.SignInIntent, 1011);
};
CheckIfIAPAvailable();
/*FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
fab.Click += FabOnClick;*/
//check permissions
checkPermission(new string[] { Android.Manifest.Permission.Internet,
Android.Manifest.Permission.AccessNetworkState,
Android.Manifest.Permission.ReadSms,
Android.Manifest.Permission.ReceiveSms,
Android.Manifest.Permission.SendSms,
Android.Manifest.Permission.BroadcastSms}, 100);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 1011)
{
//login success
Task authHuaweiIdTask = HuaweiIdAuthManager.ParseAuthResultFromIntent(data);
if (authHuaweiIdTask.IsSuccessful)
{
AuthHuaweiId huaweiAccount = (AuthHuaweiId)authHuaweiIdTask.TaskResult();
Log.Info(TAG, "signIn get code success.");
Log.Info(TAG, "ServerAuthCode: " + huaweiAccount.AuthorizationCode);
Toast.MakeText(Android.App.Application.Context, "SignIn Success", ToastLength.Short).Show();
ManageHomeScreen(huaweiAccount, true);
}
else
{
Log.Info(TAG, "signIn failed: " + ((ApiException)authHuaweiIdTask.Exception).StatusCode);
Toast.MakeText(Android.App.Application.Context, ((ApiException)authHuaweiIdTask.Exception).StatusCode.ToString(), ToastLength.Short).Show();
Toast.MakeText(Android.App.Application.Context, "SignIn Failed", ToastLength.Short).Show();
ManageHomeScreen(null, false);
}
}
}
public void ManageHomeScreen(AuthHuaweiId data, Boolean loginStatus)
{
if (loginStatus)
{
btnLoginWithHuaweiId.Visibility = ViewStates.Gone;
}
else
{
btnLoginWithHuaweiId.Visibility = ViewStates.Visible;
}
}
public void checkPermission(string[] permissions, int requestCode)
{
foreach (string permission in permissions)
{
if (ContextCompat.CheckSelfPermission(this, permission) == Permission.Denied)
{
ActivityCompat.RequestPermissions(this, permissions, requestCode);
}
}
}
/*private void FabOnClick(object sender, EventArgs eventArgs)
{
View view = (View) sender;
Snackbar.Make(view, "Replace with your own action", Snackbar.LengthLong)
.SetAction("Action", (Android.Views.View.IOnClickListener)null).Show();
}*/
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected override void AttachBaseContext(Context context)
{
base.AttachBaseContext(context);
AGConnectServicesConfig config = AGConnectServicesConfig.FromContext(context);
config.OverlayWith(new HmsLazyInputStream(context));
}
private void CancelAuthorisation()
{
Task cancelAuthorizationTask = mAuthManager.CancelAuthorization();
Log.Info(TAG, "Cancel Authorisation");
cancelAuthorizationTask.AddOnCompleteListener(
new OnCompleteListener
(
this, "Cancel Authorization Success",
"Cancel Authorization Failed"
)
);
}
public void SignOut()
{
Task signOutTask = mAuthManager.SignOut();
signOutTask.AddOnSuccessListener(new OnSuccessListener(this, "SignOut Success"))
.AddOnFailureListener(new OnFailureListener("SignOut Failed"));
}
public class OnCompleteListener : Java.Lang.Object, IOnCompleteListener
{
//Message when task is successful
private string successMessage;
//Message when task is failed
private string failureMessage;
MainActivity context;
public OnCompleteListener(MainActivity context, string SuccessMessage, string FailureMessage)
{
this.context = context;
this.successMessage = SuccessMessage;
this.failureMessage = FailureMessage;
}
public void OnComplete(Task task)
{
if (task.IsSuccessful)
{
//do some thing while cancel success
Log.Info(TAG, successMessage);
//context.SignOut();
}
else
{
//do some thing while cancel failed
Exception exception = task.Exception;
if (exception is ApiException)
{
int statusCode = ((ApiException)exception).StatusCode;
Log.Info(TAG, failureMessage + ": " + statusCode);
}
//context.ManageHomeScreen(null, true);
}
}
}
public class OnSuccessListener : Java.Lang.Object, Com.Huawei.Hmf.Tasks.IOnSuccessListener
{
//Message when task is successful
private string successMessage;
MainActivity context;
public OnSuccessListener(MainActivity context, string SuccessMessage)
{
this.successMessage = SuccessMessage;
this.context = context;
}
public void OnSuccess(Java.Lang.Object p0)
{
Log.Info(TAG, successMessage);
Toast.MakeText(Android.App.Application.Context, successMessage, ToastLength.Short).Show();
context.ManageHomeScreen(null, false);
}
}
public class OnFailureListener : Java.Lang.Object, Com.Huawei.Hmf.Tasks.IOnFailureListener
{
//Message when task is failed
private string failureMessage;
public OnFailureListener(string FailureMessage)
{
this.failureMessage = FailureMessage;
}
public void OnFailure(Java.Lang.Exception p0)
{
Log.Info(TAG, failureMessage);
Toast.MakeText(Android.App.Application.Context, failureMessage, ToastLength.Short).Show();
}
}
public void CheckIfIAPAvailable()
{
IIapClient mClient = Iap.GetIapClient(this);
Task isEnvReady = mClient.IsEnvReady();
isEnvReady.AddOnSuccessListener(new ListenerImp(this)).AddOnFailureListener(new ListenerImp(this));
}
class ListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private MainActivity mainActivity;
public ListenerImp(MainActivity mainActivity)
{
this.mainActivity = mainActivity;
}
public void OnSuccess(Java.Lang.Object IsEnvReadyResult)
{
// Obtain the execution result.
Intent intent = new Intent(mainActivity, typeof(ComapnyActivity));
mainActivity.StartActivity(intent);
}
public void OnFailure(Java.Lang.Exception e)
{
Toast.MakeText(Android.App.Application.Context, "Feature Not available for your country", ToastLength.Short).Show();
if (e.GetType() == typeof(IapApiException))
{
IapApiException apiException = (IapApiException)e;
if (apiException.Status.StatusCode == OrderStatusCode.OrderHwidNotLogin)
{
// Not logged in.
//Call StartResolutionForResult to bring up the login page
}
else if (apiException.Status.StatusCode == OrderStatusCode.OrderAccountAreaNotSupported)
{
// The current region does not support HUAWEI IAP.
}
}
}
}
}
}
CompanyActivity.cs
This activity performs all the operation In-App purchasing and display list of company with package details.
Java:
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Support.V7.Widget;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Hmf.Tasks;
using Com.Huawei.Hms.Iap;
using Com.Huawei.Hms.Iap.Entity;
using Org.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PlacementApp
{
[Activity(Label = "ComapnyActivity", Theme = "@style/AppTheme")]
public class ComapnyActivity : AppCompatActivity, BuyProduct
{
private static String TAG = "ComapnyActivity";
private RecyclerView recyclerView;
private CompanyAdapter adapter;
IList<ProductInfo> productList;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_company);
recyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerview);
recyclerView.SetLayoutManager(new LinearLayoutManager(this));
recyclerView.SetItemAnimator(new DefaultItemAnimator());
//ADAPTER
adapter = new CompanyAdapter(this);
adapter.SetData(productList);
recyclerView.SetAdapter(adapter);
GetProducts();
}
private void GetProducts()
{
List<String> productIdList = new List<String>();
productIdList.Add("Nokia");
productIdList.Add("Hyperlink");
productIdList.Add("Tata");
productIdList.Add("Infosys");
productIdList.Add("Wipro");
ProductInfoReq req = new ProductInfoReq();
// PriceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
req.PriceType = 0;
req.ProductIds = productIdList;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).ObtainProductInfo(req);
task.AddOnSuccessListener(new QueryProductListenerImp(this)).AddOnFailureListener(new QueryProductListenerImp(this));
}
class QueryProductListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private ComapnyActivity activity;
public QueryProductListenerImp(ComapnyActivity activity)
{
this.activity = activity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the result
ProductInfoResult productlistwrapper = (ProductInfoResult)result;
IList<ProductInfo> productList = productlistwrapper.ProductInfoList;
activity.adapter.SetData(productList);
activity.adapter.NotifyDataSetChanged();
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
}
}
public void OnBuyProduct(ProductInfo pInfo)
{
//Toast.MakeText(Android.App.Application.Context, pInfo.ProductName, ToastLength.Short).Show();
CreatePurchaseRequest(pInfo);
}
private void CreatePurchaseRequest(ProductInfo pInfo)
{
// Constructs a PurchaseIntentReq object.
PurchaseIntentReq req = new PurchaseIntentReq();
// The product ID is the same as that set by a developer when configuring product information in AppGallery Connect.
// PriceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
req.PriceType = pInfo.PriceType;
req.ProductId = pInfo.ProductId;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).CreatePurchaseIntent(req);
task.AddOnSuccessListener(new BuyListenerImp(this)).AddOnFailureListener(new BuyListenerImp(this));
}
protected override void OnActivityResult(int requestCode, Android.App.Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 6666)
{
if (data == null)
{
Log.Error(TAG, "data is null");
return;
}
//"this" in the code is a reference to the current activity
PurchaseResultInfo purchaseIntentResult = Iap.GetIapClient(this).ParsePurchaseResultInfoFromIntent(data);
switch (purchaseIntentResult.ReturnCode)
{
case OrderStatusCode.OrderStateCancel:
// User cancel payment.
Toast.MakeText(Android.App.Application.Context, "Payment Cancelled", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateFailed:
Toast.MakeText(Android.App.Application.Context, "Order Failed", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderProductOwned:
// check if there exists undelivered products.
Toast.MakeText(Android.App.Application.Context, "Undelivered Products", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateSuccess:
// pay success.
Toast.MakeText(Android.App.Application.Context, "Payment Success", ToastLength.Short).Show();
// use the public key of your app to verify the signature.
// If ok, you can deliver your products.
// If the user purchased a consumable product, call the ConsumeOwnedPurchase API to consume it after successfully delivering the product.
String inAppPurchaseDataStr = purchaseIntentResult.InAppPurchaseData;
MakeProductReconsumeable(inAppPurchaseDataStr);
break;
default:
break;
}
return;
}
}
private void MakeProductReconsumeable(String InAppPurchaseDataStr)
{
String purchaseToken = null;
try
{
InAppPurchaseData InAppPurchaseDataBean = new InAppPurchaseData(InAppPurchaseDataStr);
if (InAppPurchaseDataBean.PurchaseStatus != InAppPurchaseData.PurchaseState.Purchased)
{
return;
}
purchaseToken = InAppPurchaseDataBean.PurchaseToken;
}
catch (JSONException e) { }
ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
req.PurchaseToken = purchaseToken;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).ConsumeOwnedPurchase(req);
task.AddOnSuccessListener(new ConsumListenerImp()).AddOnFailureListener(new ConsumListenerImp());
}
class ConsumListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the result
Log.Info(TAG, "Product available for purchase");
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Log.Info(TAG, "Product available for purchase API Failed");
}
}
class BuyListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private ComapnyActivity activity;
public BuyListenerImp(ComapnyActivity activity)
{
this.activity = activity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the payment result.
PurchaseIntentResult InResult = (PurchaseIntentResult)result;
if (InResult.Status != null)
{
// 6666 is an int constant defined by the developer.
InResult.Status.StartResolutionForResult(activity, 6666);
}
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Toast.MakeText(Android.App.Application.Context, "Purchase Request Failed !", ToastLength.Short).Show();
}
}
}
}
Xamarin App Build Result
1. Navigate to Solution Explore > Project > Right Click > Archive/View Archive to generate SHA-256 for build release and Click on Distribute.
2. Choose Distribution Channel > Ad Hoc to sign apk.
3. Choose Demo Keystore to release apk.
4. Build succeed and Save apk file.
5. Finally here is the result.
Tips and Tricks
1. It is recommended that the app obtains the public payment key from your server in real-time. Do not store it on the app to prevent app version incompatibility caused by the subsequent key upgrade.
2. The sandbox testing function can be used only when the following conditions are met: A sandbox testing account is successfully added, and the value of versionCode of the test package is greater than that of the released package. In the HMS Core IAP SDK 4.0.2, the isSandboxActivated API is added to check whether the current sandbox testing environment is available. If not, the API returns the reason why the environment is unavailable.
Conclusion
In this article, we have learned how to integrate HMS In-App Purchase and Account Kit in Xamarin based Android application. Student can easily apply in a listed company which offers campus placement.
Be sure to like and comments on this article, if you found it helpful. It means a lot to me.
References
Document
developer.huawei.com
Document
developer.huawei.com
Read In Forum
Overview
In this article, I will create a College Campus Placement Centre Demo App which highlights ongoing pool college placement with all listed companies and their details. Student can easily apply and register with available food facility on the campus through IAP. I have integrated HMS Account, Ads, Analytics and IAP Kit which is based on Cross-platform Technology Xamarin.
Ads Kit Service Introduction
HMS Ads kit is powered by Huawei which allows the developer to monetize services such as Banner, Splash, Reward and Interstitial Ads. HUAWEI Ads Publisher Service is a monetization service that leverages Huawei's extensive data capabilities to display targeted, high-quality ad content in your application to the vast user base of Huawei devices.
Analytics Kit Service Introduction
Analytics kit is powered by Huawei which allows rich analytics models to help you clearly understand user behavior and gain in-depth insights into users, products, and content. As such, you can carry out data-driven operations and make strategic decisions about app marketing and product optimization.
Analytics Kit implements the following functions using data collected from apps:
1. Provides data collection and reporting APIs for collection and reporting custom events.
2. Sets up to 25 user attributes.
3. Supports automatic event collection and session calculation as well as predefined event IDs and parameters.
HMS IAP Service Introduction
HMS In-App Purchase Kit allows purchasing any product from the application with highly secure payment. Users can purchase a variety of products or services, including common virtual products and subscriptions, directly within your app. It also provides a product management system (PMS) for managing the prices and languages of in-app products (including games) in multiple locations.
These are the following 3 types of in-app products supported by the IAP:
1. Consumable: Consumables are used once, are depleted, and can be purchased again.
2. Non-consumable: Non-consumables are purchased once and do not expire.
3. Auto-renewable subscriptions: Users can purchase access to value-added functions or content in a specified period of time. The subscriptions are automatically renewed on a recurring basis until users decide to cancel.
Account Kit Service Introduction
HMS Account Kit allows you to connect to the Huawei ecosystem using your HUAWEI ID from a range of devices, such as mobile phones, tablets, and smart screens.
It’s a simple, secure, and quick sign-in and authorization functions. Instead of entering accounts and passwords and waiting for authentication.
Complies with international standards and protocols such as OAuth2.0 and OpenID Connect, and supports two-factor authentication (password authentication and mobile number authentication) to ensure high security.
Prerequisite
1. Xamarin Framework
2. Huawei phone
3. Visual Studio 2019
App Gallery Integration process
1. Sign In and Create or Choose a project on AppGallery Connect portal.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
2. Navigate to Project settings > download the configuration file.
3. Navigate to General Information > Data Storage location.
4. Navigate to Manage APIs > enable APIs to require by an application.
5. Navigate to My apps > Operate, and then enter details in Add Product.
6. Click Activate for product activation.
7. Navigate to Huawei Analytics > Overview > Custom dashboard > Enable Analytics.
Xamarin Ads Kit Setup Process
1. Download Xamarin Plugin of all the aar and zip files from the URL:
2. Open the XAdsIdentifier-3.4.35.300.sln solution in Visual Studio.
3. Navigate to Solution Explorer and right-click on jar Add > Existing Item and choose aar file which download in Step 1.
4. Choose aar file from download location.
5. Right-click on added aar file, then choose Properties > Build Action > LibraryProjectZip.
Note: Repeat Step 3 and 4 for all aar file.
6. Build the Library and make DLL files.
Xamarin Analytics Kit Setup Process
1. Download Xamarin Plugin of all the aar and zip files from the URL, see the similar image in Ads kit setup Step 1:
2. Open the XHiAnalytics-5.0.5.300.sln solution in Visual Studio, see the similar image in Ads kit setup Step 2.
3. Navigate to Solution Explorer and right-click on jar Add > Existing Item and choose aar file which download in Step 1.
4. Choose aar file from download location.
5. Right-click on added aar file, then choose Properties > Build Action > LibraryProjectZip.
Note: Repeat Step 3 and 4 for all aar file.
6. Build the Library and make DLL files.
Xamarin Account Kit Setup Process
1. Download Xamarin Plugin all the aar and zip files from the URL, see the similar image in Ads kit setup Step 1:
2. Open the XHwid-5.03.302.sln solution in Visual Studio, see the similar image in Ads kit setup Step 2.
Xamarin IAP Kit Setup Process
1. Download Xamarin Plugin all the aar and zip files from the URL, see the similar image in Ads kit setup Step 1:
2. Open the XIAP-5.0.2.300.sln solution in Visual Studio, see the similar image in Ads kit setup Step 2.
3. Navigate to Solution Explorer and Right-click on jar Add > Existing Item and choose aar file which downloads in Step 1.
4. Right-click on added aar file then choose Properties > Build Action > LibraryProjectZip.
Note: Repeat Step 3 and 4 for all aar file.
5. Build the Library and make dll files.
Xamarin App Development
1. Open Visual Studio 2019 and Create a new project.
2. Navigate to Solution Explore > Project > Assets > Add JSON file.
3. Navigate to Solution Explore > Project > Add > Add New Folder.
4. Navigate to Folder(created) > Add > Add Existing and add all DLL files.
5. Right click > Properties > Build Action > None.
6. Navigate to Solution Explore > Project > Reference > Right Click > Add References, then Navigate to Browse and add all DLL files from the recently added Folder.
7. Added reference then click Ok.
MainActivity.cs
This activity performs all the operation regarding login with Huawei Id.
Java:
using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using Android.Support.V7.App;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Agconnect.Config;
using Com.Huawei.Hmf.Tasks;
using Com.Huawei.Hms.Common;
using Com.Huawei.Hms.Ads.Banner;
using Com.Huawei.Hms.Analytics;
using Com.Huawei.Hms.Iap;
using Com.Huawei.Hms.Iap.Entity;
using Com.Huawei.Hms.Support.Hwid;
using Com.Huawei.Hms.Support.Hwid.Request;
using Com.Huawei.Hms.Support.Hwid.Result;
using Com.Huawei.Hms.Support.Hwid.Service;
namespace PlacementApp
{
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
private Button btnLoginWithHuaweiId;
private HuaweiIdAuthParams mAuthParam;
public static IHuaweiIdAuthService mAuthManager;
private static String TAG = "MainActivity";
public static String name, email;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
btnLoginWithHuaweiId = FindViewById<Button>(Resource.Id.btn_huawei_id);
// Write code for Huawei id button click
mAuthParam = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DefaultAuthRequestParam)
.SetIdToken().SetEmail()
.SetAccessToken()
.CreateParams();
mAuthManager = HuaweiIdAuthManager.GetService(this, mAuthParam);
HiAnalyticsTools.EnableLog();
instance = HiAnalytics.GetInstance(this);
instance.SetAnalyticsEnabled(true);
// Click listener for each button
btnLoginWithHuaweiId.Click += delegate
{
StartActivityForResult(mAuthManager.SignInIntent, 1011);
string text = "Login Clicked";
Toast.MakeText(Android.App.Application.Context, text, ToastLength.Short).Show();
// Initiate Parameters
Bundle bundle = new Bundle();
bundle.PutString("text", text);
instance.OnEvent("ButtonClickEvent", bundle);
};
CheckIfIAPAvailable();
/*FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
fab.Click += FabOnClick;*/
//check permissions
checkPermission(new string[] { Android.Manifest.Permission.Internet,
Android.Manifest.Permission.AccessNetworkState,
Android.Manifest.Permission.ReadSms,
Android.Manifest.Permission.ReceiveSms,
Android.Manifest.Permission.SendSms,
Android.Manifest.Permission.BroadcastSms}, 100);
}
private void loadBannerAds()
{
// Obtain BannerView based on the configuration in layout
BannerView bottomBannerView = FindViewById<BannerView>(Resource.Id.hw_banner_view);
bottomBannerView.AdListener = new AdsListener();
AdParam adParam = new AdParam.Builder().Build();
bottomBannerView.LoadAd(adParam);
// Obtain BannerView using coding
BannerView topBannerview = new BannerView(this);
topBannerview.AdId = "testw6vs28auh3";
topBannerview.BannerAdSize = BannerAdSize.BannerSize32050;
topBannerview.LoadAd(adParam);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 1011)
{
//login success
Task authHuaweiIdTask = HuaweiIdAuthManager.ParseAuthResultFromIntent(data);
if (authHuaweiIdTask.IsSuccessful)
{
AuthHuaweiId huaweiAccount = (AuthHuaweiId)authHuaweiIdTask.TaskResult();
Log.Info(TAG, "signIn get code success.");
Log.Info(TAG, "ServerAuthCode: " + huaweiAccount.AuthorizationCode);
Toast.MakeText(Android.App.Application.Context, "SignIn Success", ToastLength.Short).Show();
ManageHomeScreen(huaweiAccount, true);
}
else
{
Log.Info(TAG, "signIn failed: " + ((ApiException)authHuaweiIdTask.Exception).StatusCode);
Toast.MakeText(Android.App.Application.Context, ((ApiException)authHuaweiIdTask.Exception).StatusCode.ToString(), ToastLength.Short).Show();
Toast.MakeText(Android.App.Application.Context, "SignIn Failed", ToastLength.Short).Show();
ManageHomeScreen(null, false);
}
}
}
public void ManageHomeScreen(AuthHuaweiId data, Boolean loginStatus)
{
if (loginStatus)
{
btnLoginWithHuaweiId.Visibility = ViewStates.Gone;
name = data.DisplayName;
email = data.Email;
}
else
{
btnLoginWithHuaweiId.Visibility = ViewStates.Visible;
}
}
public void checkPermission(string[] permissions, int requestCode)
{
foreach (string permission in permissions)
{
if (ContextCompat.CheckSelfPermission(this, permission) == Permission.Denied)
{
ActivityCompat.RequestPermissions(this, permissions, requestCode);
}
}
}
/*private void FabOnClick(object sender, EventArgs eventArgs)
{
View view = (View) sender;
Snackbar.Make(view, "Replace with your own action", Snackbar.LengthLong)
.SetAction("Action", (Android.Views.View.IOnClickListener)null).Show();
}*/
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected override void AttachBaseContext(Context context)
{
base.AttachBaseContext(context);
AGConnectServicesConfig config = AGConnectServicesConfig.FromContext(context);
config.OverlayWith(new HmsLazyInputStream(context));
}
private void CancelAuthorisation()
{
Task cancelAuthorizationTask = mAuthManager.CancelAuthorization();
Log.Info(TAG, "Cancel Authorisation");
cancelAuthorizationTask.AddOnCompleteListener(
new OnCompleteListener
(
this, "Cancel Authorization Success",
"Cancel Authorization Failed"
)
);
}
public void SignOut()
{
Task signOutTask = mAuthManager.SignOut();
signOutTask.AddOnSuccessListener(new OnSuccessListener(this, "SignOut Success"))
.AddOnFailureListener(new OnFailureListener("SignOut Failed"));
}
public class OnCompleteListener : Java.Lang.Object, IOnCompleteListener
{
//Message when task is successful
private string successMessage;
//Message when task is failed
private string failureMessage;
MainActivity context;
public OnCompleteListener(MainActivity context, string SuccessMessage, string FailureMessage)
{
this.context = context;
this.successMessage = SuccessMessage;
this.failureMessage = FailureMessage;
}
public void OnComplete(Task task)
{
if (task.IsSuccessful)
{
//do some thing while cancel success
Log.Info(TAG, successMessage);
//context.SignOut();
}
else
{
//do some thing while cancel failed
Exception exception = task.Exception;
if (exception is ApiException)
{
int statusCode = ((ApiException)exception).StatusCode;
Log.Info(TAG, failureMessage + ": " + statusCode);
}
//context.ManageHomeScreen(null, true);
}
}
}
public class OnSuccessListener : Java.Lang.Object, Com.Huawei.Hmf.Tasks.IOnSuccessListener
{
//Message when task is successful
private string successMessage;
MainActivity context;
public OnSuccessListener(MainActivity context, string SuccessMessage)
{
this.successMessage = SuccessMessage;
this.context = context;
}
public void OnSuccess(Java.Lang.Object p0)
{
Log.Info(TAG, successMessage);
Toast.MakeText(Android.App.Application.Context, successMessage, ToastLength.Short).Show();
context.ManageHomeScreen(null, false);
}
}
public class OnFailureListener : Java.Lang.Object, Com.Huawei.Hmf.Tasks.IOnFailureListener
{
//Message when task is failed
private string failureMessage;
public OnFailureListener(string FailureMessage)
{
this.failureMessage = FailureMessage;
}
public void OnFailure(Java.Lang.Exception p0)
{
Log.Info(TAG, failureMessage);
Toast.MakeText(Android.App.Application.Context, failureMessage, ToastLength.Short).Show();
}
}
public void CheckIfIAPAvailable()
{
IIapClient mClient = Iap.GetIapClient(this);
Task isEnvReady = mClient.IsEnvReady();
isEnvReady.AddOnSuccessListener(new ListenerImp(this)).AddOnFailureListener(new ListenerImp(this));
}
class ListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private MainActivity mainActivity;
public ListenerImp(MainActivity mainActivity)
{
this.mainActivity = mainActivity;
}
public void OnSuccess(Java.Lang.Object IsEnvReadyResult)
{
// Obtain the execution result.
Intent intent = new Intent(mainActivity, typeof(ComapnyActivity));
mainActivity.StartActivity(intent);
}
public void OnFailure(Java.Lang.Exception e)
{
Toast.MakeText(Android.App.Application.Context, "Feature Not available for your country", ToastLength.Short).Show();
if (e.GetType() == typeof(IapApiException))
{
IapApiException apiException = (IapApiException)e;
if (apiException.Status.StatusCode == OrderStatusCode.OrderHwidNotLogin)
{
// Not logged in.
//Call StartResolutionForResult to bring up the login page
}
else if (apiException.Status.StatusCode == OrderStatusCode.OrderAccountAreaNotSupported)
{
// The current region does not support HUAWEI IAP.
}
}
}
}
}
CompanyActivity.cs
This activity performs all the operation In-App purchasing and display list of company with package details.
Java:
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Support.V7.Widget;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Hmf.Tasks;
using Com.Huawei.Hms.Iap;
using Com.Huawei.Hms.Iap.Entity;
using Org.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PlacementApp
{
[Activity(Label = "ComapnyActivity", Theme = "@style/AppTheme")]
public class ComapnyActivity : AppCompatActivity, BuyProduct
{
private static String TAG = "ComapnyActivity";
private RecyclerView recyclerView;
private CompanyAdapter adapter;
IList<ProductInfo> productList;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_company);
recyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerview);
recyclerView.SetLayoutManager(new LinearLayoutManager(this));
recyclerView.SetItemAnimator(new DefaultItemAnimator());
//ADAPTER
adapter = new CompanyAdapter(this);
adapter.SetData(productList);
recyclerView.SetAdapter(adapter);
GetProducts();
}
private void GetProducts()
{
List<String> productIdList = new List<String>();
productIdList.Add("Nokia");
productIdList.Add("Hyperlink");
productIdList.Add("Tata");
productIdList.Add("Infosys");
productIdList.Add("Wipro");
ProductInfoReq req = new ProductInfoReq();
// PriceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
req.PriceType = 0;
req.ProductIds = productIdList;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).ObtainProductInfo(req);
task.AddOnSuccessListener(new QueryProductListenerImp(this)).AddOnFailureListener(new QueryProductListenerImp(this));
}
class QueryProductListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private ComapnyActivity activity;
public QueryProductListenerImp(ComapnyActivity activity)
{
this.activity = activity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the result
ProductInfoResult productlistwrapper = (ProductInfoResult)result;
IList<ProductInfo> productList = productlistwrapper.ProductInfoList;
activity.adapter.SetData(productList);
activity.adapter.NotifyDataSetChanged();
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
}
}
public void OnBuyProduct(ProductInfo pInfo)
{
//Toast.MakeText(Android.App.Application.Context, pInfo.ProductName, ToastLength.Short).Show();
CreatePurchaseRequest(pInfo);
}
private void CreatePurchaseRequest(ProductInfo pInfo)
{
// Constructs a PurchaseIntentReq object.
PurchaseIntentReq req = new PurchaseIntentReq();
// The product ID is the same as that set by a developer when configuring product information in AppGallery Connect.
// PriceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
req.PriceType = pInfo.PriceType;
req.ProductId = pInfo.ProductId;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).CreatePurchaseIntent(req);
task.AddOnSuccessListener(new BuyListenerImp(this)).AddOnFailureListener(new BuyListenerImp(this));
}
protected override void OnActivityResult(int requestCode, Android.App.Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 6666)
{
if (data == null)
{
Log.Error(TAG, "data is null");
return;
}
//"this" in the code is a reference to the current activity
PurchaseResultInfo purchaseIntentResult = Iap.GetIapClient(this).ParsePurchaseResultInfoFromIntent(data);
switch (purchaseIntentResult.ReturnCode)
{
case OrderStatusCode.OrderStateCancel:
// User cancel payment.
Toast.MakeText(Android.App.Application.Context, "Payment Cancelled", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateFailed:
Toast.MakeText(Android.App.Application.Context, "Order Failed", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderProductOwned:
// check if there exists undelivered products.
Toast.MakeText(Android.App.Application.Context, "Undelivered Products", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateSuccess:
// pay success.
Toast.MakeText(Android.App.Application.Context, "Payment Success", ToastLength.Short).Show();
// use the public key of your app to verify the signature.
// If ok, you can deliver your products.
// If the user purchased a consumable product, call the ConsumeOwnedPurchase API to consume it after successfully delivering the product.
String inAppPurchaseDataStr = purchaseIntentResult.InAppPurchaseData;
MakeProductReconsumeable(inAppPurchaseDataStr);
break;
default:
break;
}
return;
}
}
private void MakeProductReconsumeable(String InAppPurchaseDataStr)
{
String purchaseToken = null;
try
{
InAppPurchaseData InAppPurchaseDataBean = new InAppPurchaseData(InAppPurchaseDataStr);
if (InAppPurchaseDataBean.PurchaseStatus != InAppPurchaseData.PurchaseState.Purchased)
{
return;
}
purchaseToken = InAppPurchaseDataBean.PurchaseToken;
}
catch (JSONException e) { }
ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
req.PurchaseToken = purchaseToken;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).ConsumeOwnedPurchase(req);
task.AddOnSuccessListener(new ConsumListenerImp()).AddOnFailureListener(new ConsumListenerImp());
}
class ConsumListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the result
Log.Info(TAG, "Product available for purchase");
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Log.Info(TAG, "Product available for purchase API Failed");
}
}
class BuyListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private ComapnyActivity activity;
public BuyListenerImp(ComapnyActivity activity)
{
this.activity = activity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the payment result.
PurchaseIntentResult InResult = (PurchaseIntentResult)result;
if (InResult.Status != null)
{
// 6666 is an int constant defined by the developer.
InResult.Status.StartResolutionForResult(activity, 6666);
}
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Toast.MakeText(Android.App.Application.Context, "Purchase Request Failed !", ToastLength.Short).Show();
}
}
public void OnRegister(int position)
{
//Toast.MakeText(Android.App.Application.Context, "Position is :" + position, ToastLength.Short).Show();
Intent intent = new Intent(this, typeof(RegistrationActivity));
ProductInfo pInfo = productList[position];
intent.PutExtra("price_type", pInfo.PriceType);
intent.PutExtra("product_id", pInfo.ProductId);
intent.PutExtra("price", pInfo.Price);
StartActivity(intent);
}
}
}
RegistrationActivity.cs
This activity performs register student data then redirect to the payment screen through In-App purchasing.
Java:
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Hmf.Tasks;
using Com.Huawei.Hms.Iap;
using Com.Huawei.Hms.Iap.Entity;
using Org.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PlacementApp
{
[Activity(Label = "Registration", Theme = "@style/AppTheme")]
class RegistrationActivity : AppCompatActivity
{
private int priceType;
private String productId, price;
private EditText stdName, stdEmail, phoneNo, place;
private TextView regFee;
private Button btnRegister;
private static String TAG = "RegistrationActivity";
private Spinner spinner, spinnerGender;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_registration);
productId = Intent.GetStringExtra("product_id");
priceType = Intent.GetIntExtra("price_type", 0);
price = Intent.GetStringExtra("price");
stdName = FindViewById<EditText>(Resource.Id.name);
stdEmail = FindViewById<EditText>(Resource.Id.email);
phoneNo = FindViewById<EditText>(Resource.Id.phone);
place = FindViewById<EditText>(Resource.Id.place);
regFee = FindViewById<TextView>(Resource.Id.reg_fee);
btnRegister = FindViewById<Button>(Resource.Id.register);
spinner = FindViewById<Spinner>(Resource.Id.branch);
spinner.ItemSelected += SpinnerItemSelected;
spinnerGender = FindViewById<Spinner>(Resource.Id.year);
ArrayAdapter yearAdapter = ArrayAdapter.CreateFromResource(this, Resource.Array.year_array, Android.Resource.Layout.SimpleSpinnerItem);
yearAdapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
spinnerGender.Adapter = yearAdapter;
ArrayAdapter adapter = ArrayAdapter.CreateFromResource(this, Resource.Array.branch_array, Android.Resource.Layout.SimpleSpinnerItem);
adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
spinner.Adapter = adapter;
stdName.Text = MainActivity.name;
stdEmail.Text = MainActivity.email;
regFee.Text = "Breakfast Fee : " + price;
btnRegister.Click += delegate
{
CreateRegisterRequest();
};
}
private void SpinnerItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
if (e.Position != 0)
{
Spinner spinner = (Spinner)sender;
string name = spinner.GetItemAtPosition(e.Position).ToString();
Toast.MakeText(Android.App.Application.Context, name, ToastLength.Short).Show();
}
}
private void CreateRegisterRequest()
{
// Constructs a PurchaseIntentReq object.
PurchaseIntentReq req = new PurchaseIntentReq();
// The product ID is the same as that set by a developer when configuring product information in AppGallery Connect.
// PriceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
req.PriceType = priceType;
req.ProductId = productId;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).CreatePurchaseIntent(req);
task.AddOnSuccessListener(new BuyListenerImp(this)).AddOnFailureListener(new BuyListenerImp(this));
}
class BuyListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private RegistrationActivity regActivity;
public BuyListenerImp(RegistrationActivity regActivity)
{
this.regActivity = regActivity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the payment result.
PurchaseIntentResult InResult = (PurchaseIntentResult)result;
if (InResult.Status != null)
{
// 6666 is an int constant defined by the developer.
InResult.Status.StartResolutionForResult(regActivity, 6666);
}
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Toast.MakeText(Android.App.Application.Context, "Purchase Request Failed !", ToastLength.Short).Show();
}
}
protected override void OnActivityResult(int requestCode, Android.App.Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 6666)
{
if (data == null)
{
Log.Error(TAG, "data is null");
return;
}
//"this" in the code is a reference to the current activity
PurchaseResultInfo purchaseIntentResult = Iap.GetIapClient(this).ParsePurchaseResultInfoFromIntent(data);
switch (purchaseIntentResult.ReturnCode)
{
case OrderStatusCode.OrderStateCancel:
// User cancel payment.
Toast.MakeText(Android.App.Application.Context, "Registration Cancelled", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateFailed:
Toast.MakeText(Android.App.Application.Context, "Registration Failed", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderProductOwned:
// check if there exists undelivered products.
Toast.MakeText(Android.App.Application.Context, "Undelivered Products", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateSuccess:
// pay success.
Toast.MakeText(Android.App.Application.Context, "Registration Success", ToastLength.Short).Show();
// use the public key of your app to verify the signature.
// If ok, you can deliver your products.
// If the user purchased a consumable product, call the ConsumeOwnedPurchase API to consume it after successfully delivering the product.
String inAppPurchaseDataStr = purchaseIntentResult.InAppPurchaseData;
MakeProductReconsumeable(inAppPurchaseDataStr);
break;
default:
break;
}
return;
}
}
private void MakeProductReconsumeable(String InAppPurchaseDataStr)
{
String purchaseToken = null;
try
{
InAppPurchaseData InAppPurchaseDataBean = new InAppPurchaseData(InAppPurchaseDataStr);
if (InAppPurchaseDataBean.PurchaseStatus != InAppPurchaseData.PurchaseState.Purchased)
{
return;
}
purchaseToken = InAppPurchaseDataBean.PurchaseToken;
}
catch (JSONException e) { }
ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
req.PurchaseToken = purchaseToken;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).ConsumeOwnedPurchase(req);
task.AddOnSuccessListener(new ConsumListenerImp(this)).AddOnFailureListener(new ConsumListenerImp(this));
}
class ConsumListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private RegistrationActivity registrationActivity;
public ConsumListenerImp(RegistrationActivity registrationActivity)
{
this.registrationActivity = registrationActivity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the result
Log.Info(TAG, "Product available for purchase");
registrationActivity.Finish();
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Log.Info(TAG, "Product available for purchase API Failed");
}
}
}
}
Xamarin App Build Result
1. Navigate to Solution Explore > Project > Right Click > Archive/View Archive to generate SHA-256 for build release and Click on Distribute.
2. Choose Distribution Channel > Ad Hoc to sign apk.
3. Choose Demo Keystore to release apk.
4. Build succeed and Save apk file.
5. Final result.
Analytics Report
1. Navigate to Huawei Analytics > Overview > Real-time Overview.
2. Navigate to Huawei Analytics > Overview > Real-time Overview, then check Event analysis.
3. Navigate to App debugging, then track your events.
Tips and Tricks
1. It is recommended that the app obtains the public payment key from your server in real-time. Do not store it on the app to prevent app version incompatibility caused by the subsequent key upgrade.
2. The sandbox testing function can be used only when the following conditions are met: A sandbox testing account is successfully added, and the value of versionCode of the test package is greater than that of the released package. In the HMS Core IAP SDK 4.0.2, the isSandboxActivated API is added to check whether the current sandbox testing environment is available. If not, the API returns the reason why the environment is unavailable.
3. On mobile phones whose value of targetSdkVersion is 28 or later, ad video assets may fail to be downloaded. In this case, you need to configure the app to allow HTTP network requests. For details, please refer to Configuring Network Permissions.
4. Xamarin requires the ADB daemon to be started over port 5037. If the ADB daemon runs on a different port, Visual Studio will not be able to detect your device.
Conclusion
In this article, we have learned how to integrate HMS In-App Purchase, Ads, Analytics and Account Kit in Xamarin based Android application. Student can easily apply in a listed company which offers campus placement.
Be sure to like and comments on this article, if you found it helpful. It means a lot to me.
References
1. Banner Ads Integration Procedure
2. Reward Ads Integration Procedure
3. Interstitial Ads Integration Procedure
4. Initializing Analytics Kit Procedure
Original Source