How to use Huawei Push Kit on Xamarin.Android - Huawei Developers

More information like this, you can visit HUAWEI Developer Forum​
In this article we are going to take a look at Huawei Mobile Services (HMS) Push Kit Plugin for Xamarin.Android then we will send our first notification and data message by Huawei Console. After that we will also send them by Push Kit APIs.
{
"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"
}
HUAWEI Push Kit
Push Kit, is a messaging service provided by Huawei for developers. It offers sending real time notifications and information messages. This helps developers maintain closer ties with users and increases user awareness and engagement. Furthermore, you are free to use on any different meaningful purposes.
HUAWEI Push Kit Xamarin.Android Integration
First of all, you need to be a Huawei Developer to use Huawei Push Kit. If you do not have a HUAWEI ID, register as a Huawei Developer and complete real-name authentication. For details, see Registering as a Developer.
Next, Sign in to AppGallery Connect using the approved Huawei ID and click on the “My projects” button.
Then click the “Add project” button and enter a name for your project.
After that, set project data storage location. For this Project Setting > General information > Project > Data storage location. As a location choose Germany then submit it. Afterwards, click “Add app” button in the same page then fill all the fields (if you do not know what “Package name” is just copy from Xamarin.Android Solution > Project > Properties >AndroidManifest.xml > package) after that submit.
Adding Push Kit to Your Xamarin.Android App
Firstly, we must generate the “.keystore” file. You can copy following text into batch file and change toolpath and aliasName then run it in “C:\Program Files\Java\jdk1.8.0\bin” folder. For details, see Pre-development Procedure.
Code:
SET toolPath="C:\tmp\tool.jks"
SET aliasName=aliasname
keytool -genkey -keystore %toolPath% -alias %aliasName% -keyalg RSA -dname "o=Huawei"
pause
keytool -list -v -keystore %toolPath%
pause
Keep these Alias name and SHA256 code. Open the project in Visual Studio then Properties of project afterwards fill the fields. It would be better to do this for both Debug and Release because later when you release the project you have to fill these areas.
Now, it is turn to use SHA256 fingerprint. Copy it and paste SHA-256 certificate fingerprint section then click to tick icon to save it.
Next, download “agconnect-services.json” file, afterwards right click on “Assets” folder in your project then Add > Existing Item and choose this file. At last enable the Push Kit.
Adding HUAWEI Push Kit Plugins in Our Project
Firstly, you need to create Android Binding Libraries for Xamarin. For this, see Creating Android Binding Libraries for Xamarin.
Secondly, you need to add these libraries to your project. For this, see Integrating the Xamarin HMS Push Kit Libraries.
Finally, HUAWEI Push Kit requires some permissions to work correctly. For this right click your project >Properties > Android Manifest then scroll to the bottom of the page. Enable following permissions.
· ACCESS_NETWORK_STATE
· INTERNET
· WAKE_LOCK
· DEFAULT (android.intent.category)
· BROWSABLE (android.intent.category)
· DEFAULT (android.permission)
Integrating HMS Core SDK
Add new class as “HmsLazyInputStream.cs” then paste following code.
Code:
using System;
using System.IO;
using Android.Util;
using Android.Content;
using Com.Huawei.Agconnect.Config;
namespace XamarinHmsPushDemo.Hmssample
{
class HmsLazyInputStream : LazyInputStream
{
public HmsLazyInputStream(Context context) : base(context)
{
}
public override Stream Get(Context context)
{
try
{
return context.Assets.Open("agconnect-services.json");
}
catch (Exception e)
{
Log.Error(e.ToString(), "Can't open agconnect file");
return null;
}
}
}
}
Next, add “using Com.Huawei.Agconnect.Config;” library in your MainActivity class afterwards add following code.
Code:
protected override void AttachBaseContext(Context context)
{
base.AttachBaseContext(context);
AGConnectServicesConfig config = AGConnectServicesConfig.FromContext(context);
config.OverlayWith(new HmsLazyInputStream(context));
}
Push Notification
When we send push notification, phone shows this notification on Notification Center. Before we send notification by HUAWEI Push Kit, first the device needs to get a push token.
Add following method into your MainActivity class afterwards call from OnCreate() method then copy the token from Output.
Code:
private void GetToken()
{
System.Threading.Thread thread = new System.Threading.Thread(() =>
{
try
{
string appid = AGConnectServicesConfig.FromContext(this).GetString("client/app_id");
string token = HmsInstanceId.GetInstance(this).GetToken(appid, "HCM");
Log.Info("token", token);
}
catch (Exception e)
{
Log.Info("token", e.ToString());
}
}
);
thread.Start();
}
Open your project in AppGallery Connect then Growing > Push Kit > Add notification. Fill the areas as you wish then click “Test effect” button and paste your token there.
Data Message
When we send data message to a device, OnMessageReceived method will be triggered then we can use this message. In this example we will send data message and show on application.
To receive a push data message and call other related Push Kit APIs, first you need to create a class that implements “HmsMessageService”. You can use all the code in this link but I will just use omitted OnMessageReceived method for clarity. Furthermore, I want to show you this message on Toast therefore we will also add MyBroadcastReceiver class. Moreover you should get instance of MyBroadcastReceiver in OnCreate method.
Code:
using Android.App;
using Android.Content;
using Android.Widget;
using Com.Huawei.Hms.Push;
namespace PushDemo
{
[Service]
[IntentFilter(new[] { "com.huawei.push.action.MESSAGING_EVENT" })]
public class MyMessagingService:HmsMessageService
{
private readonly static string PUSHDEMO_ACTION = "com.companyname.pushdemo.action";
public override void OnMessageReceived(RemoteMessage message)
{
Intent intent = new Intent(PUSHDEMO_ACTION);
intent.PutExtra("msg", message.Data);
SendBroadcast(intent);
}
}
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "com.companyname.pushdemo.action" })]
public class MyBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Toast.MakeText(context, intent.GetStringExtra("msg"), ToastLength.Long).Show();
}
}
}
You will get the Toast, when you click Test effect then OK.
Using Push Kit APIs
We have sent notification and data message by HUAWEI console, now it is time to send it by HUAWEI Push Kit APIs. At this part main point is showing sample code. You can use this sample code on your other application (WFA, Web APIs, and Web Site etc.). Here is a sample code and little bit modify on GetToken and OnCreate :
Code:
using Android.App;
using Android.OS;
using Android.Support.V7.App;
using Android.Runtime;
using Android.Widget;
using Com.Huawei.Agconnect.Config;
using Android.Content;
using XamarinHmsPushDemo.Hmssample;
using Com.Huawei.Hms.Aaid;
using Android.Util;
using System;
using System.Collections.Generic;
using System.Net.Http;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Text;
using System.Threading.Tasks;
namespace PushDemoForArticle
{
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
string tokenFromGetToken;//GetToken()
string appID = "appID";//AppGallery Connect > Project Setting > App information > App ID
string appKey = "APIkey";//AppGallery Connect > Project Setting > App information > App key
private static readonly HttpClient client = new HttpClient();
Button btnGetToken;
Button btnNotification;
Button btnDataMessage;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
btnGetToken = FindViewById<Button>(Resource.Id.btnGetToken);
btnGetToken.Click += (sender, args) => GetToken();
btnNotification = FindViewById<Button>(Resource.Id.btnNotification);
btnNotification.Click += (sender, args) => SendNotification();
btnDataMessage = FindViewById<Button>(Resource.Id.btnDataMessage);
btnDataMessage.Click += (sender, args) => SendDataMessage();
MyBroadcastReceiver myReceiver = new MyBroadcastReceiver();
}
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 GetToken()
{
System.Threading.Thread thread = new System.Threading.Thread(() =>
{
try
{
string appid = AGConnectServicesConfig.FromContext(this).GetString("client/app_id");
tokenFromGetToken = HmsInstanceId.GetInstance(this).GetToken(appid, "HCM");
Log.Info("token", tokenFromGetToken);
RunOnUiThread(() =>
{
btnNotification.Visibility = Android.Views.ViewStates.Visible;
btnDataMessage.Visibility = Android.Views.ViewStates.Visible;
});
}
catch (Exception e)
{
Log.Info("token", e.ToString());
}
}
);
thread.Start();
}
public async Task<string> GetAccessToken()
{
string uri = "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
var values = new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", appID },
{ "client_secret", appKey }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync(uri, content);
var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync()); //Install-Package Newtonsoft.Json
string accessToken = jsonResponse["access_token"].ToString(); //It is valid for 3600 seconds
return accessToken;
}
public async void SendNotification()
{
string uriNot = "https://push-api.cloud.huawei.com/v1/" + appID + "/messages:send";
var jObject = new
{
message = new
{
notification = new
{
title = "This is title",
body = "This is body part"
},
android = new
{
notification = new
{
click_action = new
{
type = 3
}
}
},
token = new[] { tokenFromGetToken }
}
};
string myJson = JsonConvert.SerializeObject(jObject, Formatting.Indented);
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", await GetAccessToken());
var responseData = await client.PostAsync(uriNot, new StringContent(myJson, Encoding.UTF8, "application/json"));
}
public async void SendDataMessage()
{
string uriNot = "https://push-api.cloud.huawei.com/v1/" + appID + "/messages:send";
var jObject = new
{
message = new
{
data = JsonConvert.SerializeObject(new
{
title = "Message Title",
text = "Message Body",
randomKey = "You can write any key and value"
}, Formatting.None),
token = new[] { tokenFromGetToken }
}
};
string myJson = JsonConvert.SerializeObject(jObject, Formatting.Indented);
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", await GetAccessToken());
var responseData = await client.PostAsync(uriNot, new StringContent(myJson, Encoding.UTF8, "application/json"));
}
}
}
copy
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:text="Get Token"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="25px"
android:minHeight="150px"
android:id="@+id/btnGetToken" />
<Button
android:text="Send Notification"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="25px"
android:minHeight="150px"
android:visibility="invisible"
android:id="@+id/btnNotification" />
<Button
android:text="Send Data Message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="25px"
android:minHeight="150px"
android:visibility="invisible"
android:id="@+id/btnDataMessage" />
</LinearLayout>

Re- How to use Huawei Push Kit on Xamarin.Android
This is quite a helpful guide for the integration process.

Related

Account Authorization : Huawei vs Google

More information like this, you can visit HUAWEI Developer Forum​
In this article, we will integrate various sign-in options in our single android application using Huawei Account kit and Google Sign-In.
Use case:
To allow user to sign in either by Huawei account or Google account by if device has HMS or GMS respectively.
Allow user to sign in using Huawei if device has HMS.
Allow user to sign in using Google if device has GMS.
{
"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"
}
Huawei Account kit
Prerequisite
1) You must have Huawei developer account.
2) Huawei phone with HMS 4.0.0.300 or later
3) Android Studio, Jdk 1.8, SDK platform 26 and Gradle 4.6 installed.
Setup:
1) Create a project in android studio.
2) Get the SHA-256 Key.
3) Create an app in the Huawei app gallery connect.
4) Enable account kit setting in Manage APIs section.
5) Provide the SHA-256 Key in App Information Section.
6) Provide storage location.
7) Enable Huwaei account under Auth Service in AGC
8) After completing all the above points we need to download the agconnect-services.json from App Information Section. Place the json file in the app folder of the android project.
9) Enter the below maven url inside the repositories of buildscript and allprojects respectively ( project build.gradle file )
Code:
maven { url 'http://developer.huawei.com/repo/' }
10) Enter the below plugin in the app build.gradle file
Code:
apply plugin: 'com.huawei.agconnect'
dependencies {
implementation 'com.huawei.hms:hwid:4.0.0.300'
}
11) Now Sync the gradle.
Implementation
1) In this article, we will implement Authorization code method.
Code:
HuaweiIdAuthParams authParams = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setAuthorizationCode().createParams();
huaweiservice = HuaweiIdAuthManager.getService(MainActivity.this, authParams);
startActivityForResult(huaweiservice.getSignInIntent(), HUAWEI_SIGNIN);
2) To Process the sign-in result after the authorization is complete.
Code:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == HUAWEI_SIGNIN) {
com.huawei.hmf.tasks.Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data);
if (authHuaweiIdTask.isSuccessful()) {
//The sign-in is successful, and the user's HUAWEI ID information and authorization code are obtained.
AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
Log.i(TAG, "Authorization code:" + huaweiAccount.getAuthorizationCode());
Log.d(TAG, "name: " + huaweiAccount.getDisplayName());
Log.d(TAG, "email: " + huaweiAccount.getEmail());
Log.d(TAG , "avatar : "+huaweiAccount.getAvatarUriString());
launchLoggedinActivity(huaweiAccount.getFamilyName() , huaweiAccount.getGivenName() , huaweiAccount.getAvatarUriString(),"huawei");
} else {
// The sign-in failed.
Log.e(TAG, "sign in failed : " + ((com.huawei.hms.common.ApiException) authHuaweiIdTask.getException()).getStatusCode());
}
}
}
3) To handle silent Sign-in
The authorization is required only on first registration with the HUAWEI ID. Then no approval is required for subsequent registrations with the same HUAWEI ID.
Code:
private void handleHuaweiSilentSignIn() {
HuaweiIdAuthParams authParams = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM).createParams();
HuaweiIdAuthService service = HuaweiIdAuthManager.getService(MainActivity.this, authParams);
com.huawei.hmf.tasks.Task<AuthHuaweiId> task = service.silentSignIn();
task.addOnSuccessListener(new OnSuccessListener<AuthHuaweiId>() {
@Override
public void onSuccess(AuthHuaweiId authHuaweiId) {
// Obtain the user's HUAWEI ID information.
Log.i(TAG, "displayName:" + authHuaweiId.getDisplayName());
launchLoggedinActivity(authHuaweiId.getFamilyName() , authHuaweiId.getGivenName() , authHuaweiId.getAvatarUriString(),"huawei");
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// The sign-in failed. Try to sign in explicitly using getSignInIntent().
if (e instanceof ApiException) {
ApiException apiException = (ApiException)e;
Log.i(TAG, "sign failed status:" + apiException.getStatusCode());
}
}
});
}
4) To sign out from Huawei account
Code:
Task<Void> signOutTask = service.signOut();
signOutTask.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
Log.i(TAG, "signOut complete");
}
});
5) To revoke authorization
Code:
service.cancelAuthorization().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
// Processing after a successful authorization revoking.
Log.i(TAG, "onSuccess: ");
} else {
// Handle the exception.
Exception exception = task.getException();
if (exception instanceof ApiException){
int statusCode = ((ApiException) exception).getStatusCode();
Log.i(TAG, "onFailure: " + statusCode);
}
}
}
Google Sign in
1) To add google play services, In your projects root-level build.gradle file, ensure that Googles Maven repository is included:
Code:
allprojects {
repositories {
google()
// If you're using a version of Gradle lower than 4.1, you must instead use:
// maven {
// url 'https://maven.google.com'
// }:
}
}
2) In app-level build.gradle file, declare Google Play services as a dependency:
Code:
dependencies {
implementation 'com.google.android.gms:play-services-auth:18.1.0'
}
3) To specify your app's SHA-1 fingerprint, go to Settings page of the Firebase console. Refer to Authenticating Your Client for details on how to get your app's SHA-1 fingerprint.
4) Enable Google Sign-In in the Firebase console:
In the Firebase console, open the Auth section.
On Sign in method tab, enable the Google sign-in method and click Save.
5) To Configure Google Sign-in and the GoogleSignInClient object.
Code:
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).requestEmail().build();
// Build a GoogleSignInClient with the options specified by gso.
mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
6) To check for an existing sign-in user.
Code:
// Check for existing Google Sign In account, if the user is already signed in
// the GoogleSignInAccount will be non-null.
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
updateUI(account);
If GoogleSignInAccount instanse is not null, user is already signed in.
7) On click of google sign in button, we will create a sign-in intent with the getSignInIntent method, and starting the intent with startActivityForResult.
Code:
private View.OnClickListener googleClickListner = new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
startActivityForResult(signInIntent, RC_SIGN_IN);
}
};
8) To process the sign in result.
Code:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
if (requestCode == RC_SIGN_IN) {
// The Task returned from this call is always completed, no need to attach
// a listener.
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
handleGoogleSignInResult(task);
}
} private void handleGoogleSignInResult(Task<GoogleSignInAccount> completedTask) {
try {
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
Log.d(TAG, "name: " + account.getDisplayName());
Log.d(TAG, "email: " + account.getEmail());
Log.d(TAG , "avatar : "+account.getPhotoUrl());
launchLoggedinActivity(account.getGivenName(), account.getFamilyName(),account.getPhotoUrl()!=null ? account.getPhotoUrl().toString():null, "google");
} catch (ApiException e) {
// The ApiException status code indicates the detailed failure reason.
// Please refer to the GoogleSignInStatusCodes class reference for more information.
Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
}
}
9) Code snippet for sign out from google is
Code:
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build();
GoogleSignInClient googleSignInClient = GoogleSignIn.getClient(LoggedInActivity.this, gso);
googleSignInClient.signOut();
Huawei and Google Sign in Button Integration
Add the following code in activity_main.xml
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
android:id="@+id/login_layout"
android:orientation="vertical">
<com.google.android.gms.common.SignInButton
android:id="@+id/sign_in_button"
android:layout_width="242dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:visibility="gone"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content" />
<com.huawei.hms.support.hwid.ui.HuaweiIdAuthButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/hwi_sign_in"
android:layout_gravity="center_horizontal"
android:visibility="gone"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:background="#3b5998"
app:hwid_button_theme="hwid_button_theme_full_title"
app:hwid_corner_radius="hwid_corner_radius_small"
/>
</LinearLayout>
</RelativeLayout>
Permissions:
Add the following permissions in manifest.
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
To check if device has HMS or GMS installed.
Code:
public class Utils {
public static boolean isSignedIn = false;
public static boolean isGooglePlayServicesAvailable(Activity activity) {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
int status = googleApiAvailability.isGooglePlayServicesAvailable(activity);
if(status != ConnectionResult.SUCCESS) {
if(googleApiAvailability.isUserResolvableError(status)) {
googleApiAvailability.getErrorDialog(activity, status, 2404).show();
}
return false;
}
return true;
}
public static boolean isHuaweiMobileServicesAvailable(Context context) {
if (HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context) == ConnectionResult.SUCCESS){
return true;
}
return false;
}
public static boolean isDeviceHuaweiManufacturer () {
String manufacturer = Build.MANUFACTURER;
Log.d("Device : " , manufacturer);
if (manufacturer.toLowerCase().contains("huawei")) {
return true;
}
return false;
}
}
Conclusion
We have developed a single application to allow sign in using either by Huawei or google.
Reference
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/introduction-0000001050048870
https://developers.google.com/identity/sign-in/android/start-integrating​​

Create and Monitor Geofences with HuaweiMap in Xamarin.Android Application

{
"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"
}
A geofence is a virtual perimeter set on a real geographic area. Combining a user position with a geofence perimeter, it is possible to know if the user is inside the geofence or if he is exiting or entering the area.
In this article, we will discuss how to use the geofence to notify the user when the device enters/exits an area using the HMS Location Kit in a Xamarin.Android application. We will also add and customize HuaweiMap, which includes drawing circles, adding pointers, and using nearby searches in search places. We are going to learn how to use the below features together:
Geofence
Reverse Geocode
HuaweiMap
Nearby Search
First of all, you need to be a registered Huawei Mobile Developer and create an application in Huawei App Console in order to use HMS Map Location and Site Kits. You can follow there steps in to complete the configuration that required for development.
Configuring App Information in AppGallery Connect --> shorturl.at/rL347
Creating Xamarin Android Binding Libraries --> shorturl.at/rBP46
Integrating the HMS Map Kit Libraries for Xamarin --> shorturl.at/vAHPX
Integrating the HMS Location Kit Libraries for Xamarin --> shorturl.at/dCX07
Integrating the HMS Site Kit Libraries for Xamarin --> shorturl.at/bmDX6
Integrating the HMS Core SDK --> shorturl.at/qBISV
Setting Package in Xamarin --> shorturl.at/brCU1
When we create our Xamarin.Android application in the above steps, we need to make sure that the package name is the same as we entered the Console. Also, don’t forget the enable them in Console.
Manifest & Permissions
We have to update the application’s manifest file by declaring permissions that we need as shown below.
Code:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Also, add a meta-data element to embed your app id in the application tag, it is required for this app to authenticate on the Huawei’s cloud server. You can find this id in agconnect-services.json file.
Code:
<meta-data android:name="com.huawei.hms.client.appid" android:value="appid=YOUR_APP_ID" />
Request location permission
Code:
private void RequestPermissions()
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessCoarseLocation) != (int)Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessFineLocation) != (int)Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.Internet) != (int)Permission.Granted)
{
ActivityCompat.RequestPermissions(this,
new System.String[]
{
Manifest.Permission.AccessCoarseLocation,
Manifest.Permission.AccessFineLocation,
Manifest.Permission.WriteExternalStorage,
Manifest.Permission.ReadExternalStorage,
Manifest.Permission.Internet
},
100);
}
else
GetCurrentPosition();
}
Add a Map
Add a <fragment> element to your activity’s layout file, activity_main.xml. This element defines a MapFragment to act as a container for the map and to provide access to the HuaweiMap object.
Code:
<fragment
android:id="@+id/mapfragment"
class="com.huawei.hms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Implement the IOnMapReadyCallback interface to MainActivity and override OnMapReady method which is triggered when the map is ready to use. Then use GetMapAsync to register for the map callback.
We request the address corresponding to a given latitude/longitude. Also specified that the output must be in JSON format.
Code:
public class MainActivity : AppCompatActivity, IOnMapReadyCallback
{
...
public void OnMapReady(HuaweiMap map)
{
hMap = map;
hMap.UiSettings.MyLocationButtonEnabled = true;
hMap.UiSettings.CompassEnabled = true;
hMap.UiSettings.ZoomControlsEnabled = true;
hMap.UiSettings.ZoomGesturesEnabled = true;
hMap.MyLocationEnabled = true;
hMap.MapClick += HMap_MapClick;
if (selectedCoordinates == null)
selectedCoordinates = new GeofenceModel { LatLng = CurrentPosition, Radius = 30 };
}
}
As you can see above, with the UiSettings property of the HuaweiMap object we set my location button, enable compass, etc. Now when the app launch, directly get the current location and move the camera to it. In order to do that we use FusedLocationProviderClient that we instantiated and call LastLocation API.
LastLocation API returns a Task object that we can check the result by implementing the relevant listeners for success and failure.In success listener we are going to move the map’s camera position to the last known position.
Code:
private void GetCurrentPosition()
{
var locationTask = fusedLocationProviderClient.LastLocation;
locationTask.AddOnSuccessListener(new LastLocationSuccess(this));
locationTask.AddOnFailureListener(new LastLocationFail(this));
}
...
public class LastLocationSuccess : Java.Lang.Object, IOnSuccessListener
{
...
public void OnSuccess(Java.Lang.Object location)
{
Toast.MakeText(mainActivity, "LastLocation request successful", ToastLength.Long).Show();
if (location != null)
{
MainActivity.CurrentPosition = new LatLng((location as Location).Latitude, (location as Location).Longitude);
mainActivity.RepositionMapCamera((location as Location).Latitude, (location as Location).Longitude);
}
}
}
To change the position of the camera, we must specify where we want to move the camera, using a CameraUpdate. The Map Kit allows us to create many different types of CameraUpdate using CameraUpdateFactory.
There are some methods for the camera position changes as we see above. Simply these are:
NewLatLng: Change camera’s latitude and longitude, while keeping other properties
NewLatLngZoom: Changes the camera’s latitude, longitude, and zoom, while keeping other properties
NewCameraPosition: Full flexibility in changing the camera position
We are going to use NewCameraPosition. A CameraPosition can be obtained with a CameraPosition.Builder. And then we can set target, bearing, tilt and zoom properties.
Code:
public void RepositionMapCamera(double lat, double lng)
{
var cameraPosition = new CameraPosition.Builder();
cameraPosition.Target(new LatLng(lat, lng));
cameraPosition.Zoom(1000);
cameraPosition.Bearing(45);
cameraPosition.Tilt(20);
CameraUpdate cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition.Build());
hMap.MoveCamera(cameraUpdate);
}
Creating Geofence
In this part, we will choose the location where we want to set geofence in two different ways. The first is to select the location by clicking on the map, and the second is to search for nearby places by keyword and select one after placing them on the map with the marker.
Set the geofence location by clicking on the map
It is always easier to select a location by seeing it. After this section, we are able to set a geofence around the clicked point when the map’s clicked. We attached the Click event to our map in the OnMapReady method. In this Click event, we will add a marker to the clicked point and draw a circle around it.
Also, we will use the Seekbar at the bottom of the page to adjust the circle radius. We set selectedCoordinates variable when adding the marker. Let’s create the following method to create the marker:
Code:
private void HMap_MapClick(object sender, HuaweiMap.MapClickEventArgs e)
{
selectedCoordinates.LatLng = e.P0;
if (circle != null)
{
circle.Remove();
circle = null;
}
AddMarkerOnMap();
}
void AddMarkerOnMap()
{
if (marker != null) marker.Remove();
var markerOption = new MarkerOptions()
.InvokeTitle("You are here now")
.InvokePosition(selectedCoordinates.LatLng);
hMap.SetInfoWindowAdapter(new MapInfoWindowAdapter(this));
marker = hMap.AddMarker(markerOption);
bool isInfoWindowShown = marker.IsInfoWindowShown;
if (isInfoWindowShown)
marker.HideInfoWindow();
else
marker.ShowInfoWindow();
}
Adding MapInfoWindowAdapter class to our project for rendering the custom info model. And implement HuaweiMap.IInfoWindowAdapter interface to it. When an information window needs to be displayed for a marker, methods provided by this adapter are called in any case.
Now let’s create a custom info window layout and named it as map_info_view.xml
Code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:text="Add geofence"
android:width="100dp"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnInfoWindow" />
</LinearLayout>
And return it after customizing it in GetInfoWindow() method. The full code of the adapter is below:
Code:
internal class MapInfoWindowAdapter : Java.Lang.Object, HuaweiMap.IInfoWindowAdapter
{
private MainActivity activity;
private GeofenceModel selectedCoordinates;
private View addressLayout;
public MapInfoWindowAdapter(MainActivity currentActivity){activity = currentActivity;}
public View GetInfoContents(Marker marker){return null;}
public View GetInfoWindow(Marker marker)
{
if (marker == null)
return null;
selectedCoordinates = new GeofenceModel { LatLng = new LatLng(marker.Position.Latitude, marker.Position.Longitude) };
View mapInfoView = activity.LayoutInflater.Inflate(Resource.Layout.map_info_view, null);
var radiusBar = activity.FindViewById<SeekBar>(Resource.Id.radiusBar);
if (radiusBar.Visibility == Android.Views.ViewStates.Invisible)
{
radiusBar.Visibility = Android.Views.ViewStates.Visible;
radiusBar.SetProgress(30, true);
}
activity.FindViewById<SeekBar>(Resource.Id.radiusBar)?.SetProgress(30, true);
activity.DrawCircleOnMap(selectedCoordinates);
Button button = mapInfoView.FindViewById<Button>(Resource.Id.btnInfoWindow);
button.Click += btnInfoWindow_ClickAsync;
return mapInfoView;
}
}
Now we create a method to arrange a circle around the marker that representing the geofence radius. Create a new DrawCircleOnMap method in MainActivity for this. To construct a circle, we must specify the Center and Radius. Also, I set other properties like StrokeColor etc.
Code:
public void DrawCircleOnMap(GeofenceModel geoModel)
{
if (circle != null)
{
circle.Remove();
circle = null;
}
CircleOptions circleOptions = new CircleOptions()
.InvokeCenter(geoModel.LatLng)
.InvokeRadius(geoModel.Radius)
.InvokeFillColor(Color.Argb(50, 0, 14, 84))
.InvokeStrokeColor(Color.Yellow)
.InvokeStrokeWidth(15);
circle = hMap.AddCircle(circleOptions);
}
private void radiusBar_ProgressChanged(object sender, SeekBar.ProgressChangedEventArgs e)
{
selectedCoordinates.Radius = e.Progress;
DrawCircleOnMap(selectedCoordinates);
}
We will use SeekBar to change the radius of the circle. As the value changes, the drawn circle will expand or shrink.
Reverse Geocoding
Now let’s handle the click event of the info window.
But before open that window, we need to reverse geocoding selected coordinates to getting a formatted address. HUAWEI Site Kit provides us a set of HTTP API including the one that we need, reverseGeocode.
Let’s add the GeocodeManager class to our project and update it as follows:
Code:
public async Task<Site> ReverseGeocode(double lat, double lng)
{
string result = "";
using (var client = new HttpClient())
{
MyLocation location = new MyLocation();
location.Lat = lat;
location.Lng = lng;
var root = new ReverseGeocodeRequest();
root.Location = location;
var settings = new JsonSerializerSettings();
settings.ContractResolver = new LowercaseSerializer();
var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var url = "siteapi.cloud.huawei.com/mapApi/v1/siteService/reverseGeocode?key=" + Android.Net.Uri.Encode(ApiKey);
var response = await client.PostAsync(url, data);
result = response.Content.ReadAsStringAsync().Result;
}
return JsonConvert.DeserializeObject<ReverseGeocodeResponse>(result).sites.FirstOrDefault();
}
In the above code, we request the address corresponding to a given latitude/longitude. Also specified that the output must be in JSON format.
siteapi.cloud.huawei.com/mapApi/v1/siteService/reverseGeocode?key=APIKEY
Click to expand...
Click to collapse
Request model:
Code:
public class MyLocation
{
public double Lat { get; set; }
public double Lng { get; set; }
}
public class ReverseGeocodeRequest
{
public MyLocation Location { get; set; }
}
Note that the JSON response contains three root elements:
“returnCode”: For details, please refer to Result Codes.
“returnDesc”: description
“sites” contains an array of geocoded address information
Generally, only one entry in the “sites” array is returned for address lookups, though the geocoder may return several results when address queries are ambiguous.
Add the following codes to our MapInfoWindowAdapter where we get results from the Reverse Geocode API and set the UI elements.
Code:
private async void btnInfoWindow_ClickAsync(object sender, System.EventArgs e)
{
addressLayout = activity.LayoutInflater.Inflate(Resource.Layout.reverse_alert_layout, null);
GeocodeManager geocodeManager = new GeocodeManager(activity);
var addressResult = await geocodeManager.ReverseGeocode(selectedCoordinates.LatLng.Latitude, selectedCoordinates.LatLng.Longitude);
if (addressResult.ReturnCode != 0)
return;
var address = addressResult.Sites.FirstOrDefault();
var txtAddress = addressLayout.FindViewById<TextView>(Resource.Id.txtAddress);
var txtRadius = addressLayout.FindViewById<TextView>(Resource.Id.txtRadius);
txtAddress.Text = address.FormatAddress;
txtRadius.Text = selectedCoordinates.Radius.ToString();
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.SetView(addressLayout);
builder.SetTitle(address.Name);
builder.SetPositiveButton("Save", (sender, arg) =>
{
selectedCoordinates.Conversion = GetSelectedConversion();
GeofenceManager geofenceManager = new GeofenceManager(activity);
geofenceManager.AddGeofences(selectedCoordinates);
});
builder.SetNegativeButton("Cancel", (sender, arg) => { builder.Dispose(); });
AlertDialog alert = builder.Create();
alert.Show();
}
Now, after selecting the conversion, we can complete the process by calling the AddGeofence method in the GeofenceManager class by pressing the save button in the dialog window.
Code:
public void AddGeofences(GeofenceModel geofenceModel)
{
//Set parameters
geofenceModel.Id = Guid.NewGuid().ToString();
if (geofenceModel.Conversion == 5) //Expiration value that indicates the geofence should never expire.
geofenceModel.Timeout = Geofence.GeofenceNeverExpire;
else
geofenceModel.Timeout = 10000;
List<IGeofence> geofenceList = new List<IGeofence>();
//Geofence Service
GeofenceService geofenceService = LocationServices.GetGeofenceService(activity);
PendingIntent pendingIntent = CreatePendingIntent();
GeofenceBuilder somewhereBuilder = new GeofenceBuilder()
.SetUniqueId(geofenceModel.Id)
.SetValidContinueTime(geofenceModel.Timeout)
.SetRoundArea(geofenceModel.LatLng.Latitude, geofenceModel.LatLng.Longitude, geofenceModel.Radius)
.SetDwellDelayTime(10000)
.SetConversions(geofenceModel.Conversion); ;
//Create geofence request
geofenceList.Add(somewhereBuilder.Build());
GeofenceRequest geofenceRequest = new GeofenceRequest.Builder()
.CreateGeofenceList(geofenceList)
.Build();
//Register geofence
var geoTask = geofenceService.CreateGeofenceList(geofenceRequest, pendingIntent);
geoTask.AddOnSuccessListener(new CreateGeoSuccessListener(activity));
geoTask.AddOnFailureListener(new CreateGeoFailListener(activity));
}
In the AddGeofence method, we need to set the geofence request parameters, like the selected conversion, unique Id and timeout according to conversion, etc. with GeofenceBuilder. We create GeofenceBroadcastReceiver and display a toast message when a geofence action occurs.
Code:
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "com.huawei.hms.geofence.ACTION_PROCESS_ACTIVITY" })]
class GeofenceBroadcastReceiver : BroadcastReceiver
{
public static readonly string ActionGeofence = "com.huawei.hms.geofence.ACTION_PROCESS_ACTIVITY";
public override void OnReceive(Context context, Intent intent)
{
if (intent != null)
{
var action = intent.Action;
if (action == ActionGeofence)
{
GeofenceData geofenceData = GeofenceData.GetDataFromIntent(intent);
if (geofenceData != null)
{
Toast.MakeText(context, "Geofence triggered: " + geofenceData.ConvertingLocation.Latitude +"\n" + geofenceData.ConvertingLocation.Longitude + "\n" + geofenceData.Conversion.ToConversionName(), ToastLength.Long).Show();
}
}
}
}
}
After that in CreateGeoSuccessListener and CreateGeoFailureListener that we implement IOnSuccessListener and IOnFailureListener respectively, we display a toast message to the user like this:
Code:
public class CreateGeoFailListener : Java.Lang.Object, IOnFailureListener
{
public void OnFailure(Java.Lang.Exception ex)
{
Toast.MakeText(mainActivity, "Geofence request failed: " + GeofenceErrorCodes.GetErrorMessage((ex as ApiException).StatusCode), ToastLength.Long).Show();
}
}
public class CreateGeoSuccessListener : Java.Lang.Object, IOnSuccessListener
{
public void OnSuccess(Java.Lang.Object data)
{
Toast.MakeText(mainActivity, "Geofence request successful", ToastLength.Long).Show();
}
}
Set geofence location using Nearby Search
On the main layout when the user clicks the Search Nearby Places button, a search dialog like below appears:
Create search_alert_layout.xml with a search input In Main Activity, create click event of that button and open an alert dialog after it’s view is set to search_alert_layout. And make NearbySearch when clicking the Search button:
Code:
private void btnGeoWithAddress_Click(object sender, EventArgs e)
{
search_view = base.LayoutInflater.Inflate(Resource.Layout.search_alert_layout, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetView(search_view);
builder.SetTitle("Search Location");
builder.SetNegativeButton("Cancel", (sender, arg) => { builder.Dispose(); });
search_view.FindViewById<Button>(Resource.Id.btnSearch).Click += btnSearchClicked;
alert = builder.Create();
alert.Show();
}
private void btnSearchClicked(object sender, EventArgs e)
{
string searchText = search_view.FindViewById<TextView>(Resource.Id.txtSearch).Text;
GeocodeManager geocodeManager = new GeocodeManager(this);
geocodeManager.NearbySearch(CurrentPosition, searchText);
}
We pass search text and Current Location into the GeocodeManager NearbySearch method as parameters. We need to modify GeoCodeManager class and add nearby search method to it.
Code:
public void NearbySearch(LatLng currentLocation, string searchText)
{
ISearchService searchService = SearchServiceFactory.Create(activity, Android.Net.Uri.Encode("YOUR_API_KEY"));
NearbySearchRequest nearbySearchRequest = new NearbySearchRequest();
nearbySearchRequest.Query = searchText;
nearbySearchRequest.Language = "en";
nearbySearchRequest.Location = new Coordinate(currentLocation.Latitude, currentLocation.Longitude);
nearbySearchRequest.Radius = (Integer)2000;
nearbySearchRequest.PageIndex = (Integer)1;
nearbySearchRequest.PageSize = (Integer)5;
nearbySearchRequest.PoiType = LocationType.Address;
searchService.NearbySearch(nearbySearchRequest, new QuerySuggestionResultListener(activity as MainActivity));
}
And to handle the result we must create a listener and implement the ISearchResultListener interface to it.
Code:
public class NearbySearchResultListener : Java.Lang.Object, ISearchResultListener
{
public void OnSearchError(SearchStatus status)
{
Toast.MakeText(context, "Error Code: " + status.ErrorCode + " Error Message: " + status.ErrorMessage, ToastLength.Long);
}
public void OnSearchResult(Java.Lang.Object results)
{
NearbySearchResponse nearbySearchResponse = (NearbySearchResponse)results;
if (nearbySearchResponse != null && nearbySearchResponse.TotalCount > 0)
context.SetSearchResultOnMap(nearbySearchResponse.Sites);
}
}
In OnSearchResult method, NearbySearchResponse object return. We will insert markers to the mapper element in this response. The map will look like this:
In Main Activity create a method named SetSearchResultOnMap and pass IList<Site> as a parameter to insert multiple markers on the map.
Code:
public void SetSearchResultOnMap(IList<Com.Huawei.Hms.Site.Api.Model.Site> sites)
{
hMap.Clear();
if (searchMarkers != null && searchMarkers.Count > 0)
foreach (var item in searchMarkers)
item.Remove();
searchMarkers = new List<Marker>();
for (int i = 0; i < sites.Count; i++)
{
MarkerOptions marker1Options = new MarkerOptions()
.InvokePosition(new LatLng(sites[i].Location.Lat, sites[i].Location.Lng))
.InvokeTitle(sites[i].Name).Clusterable(true);
hMap.SetInfoWindowAdapter(new MapInfoWindowAdapter(this));
var marker1 = hMap.AddMarker(marker1Options);
searchMarkers.Add(marker1);
RepositionMapCamera(sites[i].Location.Lat, sites[i].Location.Lng);
}
hMap.SetMarkersClustering(true);
alert.Dismiss();
}
Now, we add markers as we did above. But here we use SetMarkersClustering(true) to consolidates markers into clusters when zooming out of the map.
You can download the source code from below:
github.com/stugcearar/HMSCore-Xamarin-Android-Samples/tree/master/LocationKit/HMS_Geofence
Also if you have any questions, ask away in Huawei Developer Forums.
Errors
If your location permission set “Allowed only while in use instead” of ”Allowed all the time” below exception will be thrown.
int GEOFENCE_INSUFFICIENT_PERMISSION
Insufficient permission to perform geofence-related operations.
You can see all result codes including errors, in here for Location service.
You can find result codes with details here for Geofence request.

Upload Files to Huawei Drive with WorkManager

{
"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
Hi everyone, In this article, we’ll explore how scheduling a task to upload files to the Huawei Drive with WorkManager. Also, we will develop a demo app using Kotlin in the Android Studio.
Huawei Drive Kit
Drive Kit (the short form of Huawei Drive Kit) allows developers to create apps that use Drive. Drive Kit gives us cloud storage capabilities for our apps, enabling users to store files that are created while using our apps, including photos, videos, and documents.
Some of the main function of the Drive kit:
Obtaining User Information
Managing and Searching for Files
Storing App Data
Performing Batch Operations
You can find more information in the official documentation link.
We’re not going to go into the details of adding Account Kit and Drive Kit to a project. You can follow the instructions to add Drive Kit to your project via official docs or codelab.
WorkManager
WorkManager is an API that makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or the device restarts. WorkManager gives us a guarantee that our action will be taken, regardless of if the app exits. But, our tasks can be deferrable to wait for some constraints to be met or to save battery life. We will go into details on WorkManager while developing the app.
Our Sample Project DriveWithWorkManager
In this project, we’re going to develop a sample app that uploading users’ files to their drive with WorkManager. Developers can use the users’ drive to save their photos, videos, documents, or app data. With the help of WorkManager, we ensure that our upload process continues even if our application is terminated.
Setup the Project
Add the necessary dependencies to build.gradle (app level)
Code:
// HMS Account Kit
implementation 'com.huawei.hms:hwid:5.1.0.301'
// HMS Drive Kit
implementation 'com.huawei.hms:drive:5.0.0.301'
// WorkManager
implementation "androidx.work:work-runtime-ktx:2.5.0"
// Kotlin Coroutines for asynchronously programming
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
Layout File
activity_main.xml is the only layout file in our project. There are two buttons here, one button for login with Huawei ID and one button for creating a work. I should note that apps that support Huawei ID sign-in must comply with the Huawei ID Sign-In Button Usage Rules. Also, we used the Drive icon here. But, the icon must comply with the HUAWEI Drive icon specifications. We can download and customize icons in compliance with the specifications. For more information about the specifications, click here.
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#D0D0D0"
tools:context=".MainActivity">
<com.huawei.hms.support.hwid.ui.HuaweiIdAuthButton
android:id="@+id/btnLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
<Button
android:id="@+id/btnCreateWork"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:backgroundTint="#1f70f2"
android:text="Create a Work"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnLogin" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:text="Upload File to Huawei Drive with WorkManager"
android:textAlignment="center"
android:textColor="#1f70f2"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
app:srcCompat="@drawable/ic_drive" />
</androidx.constraintlayout.widget.ConstraintLayout>
Permission for Storage
We need permission to access the phone’s storage. Let’s add the necessary permissions to our manifest.
Code:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
We have two code file in our application: MainActivity.kt and UploadWorker.kt
MainActivity
MainActivity.kt -> Drive functions strongly depend on Huawei Id. To use Drive functions, users must sign in with their Huawei IDs. In this file, we perform our login process and create new works.
Let’s interpret the functions on this page one by one.
onCreate() - Starting in API 23, we need to request the user for specific permission on runtime. So, we added a simple permission request. Then, we added our button-click listeners.
signIn() - We create a scope list and added the necessary drive permissions. Then, we started the login process. If you want to use other drive functions, ensure to add permission here.
refreshAt() - We can obtain a new accessToken through the HMS Core SDK.
checkDriveCode() - First, we checked whether the unionId and access token are null or an empty string. Then, we construct a DriveCredential.Builder object and returned the code.
onActivityResult() - We get the authorization result and obtain the authorization code from AuthAccount.
createWorkRequest() - I would like to explain this function after a quick explanation of the Work Request.
Creating Work Request
This is an important part of our application. With the creatingWorkRequest function, we create a work request. There are two types of work requests; OneTimeWorkRequest and PeriodicWorkRequest. OneTimeWorkRequest is run only once. We used it for simplicity in our example. PeriodicWorkRequest is used to run tasks that need to be called periodically until canceled.
createWorkRequest() - We created an OneTimeWorkRequest and added input data as accessToken and unionId to the work. We would also like to make sure that our works only run in certain situations such as we have a network and not a low battery. So, we used constraints to achieve this. Finally, we enqueued our uploadWorkRequest to run.
Code:
class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
private val REQUEST_SIGN_IN_CODE = 1001
private val REQUEST_STORAGE_PERMISSION_CODE = 1002
private lateinit var btnLogin: HuaweiIdAuthButton
private lateinit var btnCreateWork: Button
private var accessToken: String = ""
private var unionId: String = ""
private lateinit var driveCredential: DriveCredential
private val PERMISSIONS_STORAGE = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnLogin = findViewById(R.id.btnLogin)
btnCreateWork = findViewById(R.id.btnCreateWork)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(PERMISSIONS_STORAGE, REQUEST_STORAGE_PERMISSION_CODE)
}
btnLogin.setOnClickListener {
signIn()
}
btnCreateWork.setOnClickListener {
if (accessToken.isEmpty() || unionId.isEmpty()) {
showToastMessage("AccessToken or UnionId is empty")
} else {
createWorkRequest(accessToken, unionId)
}
}
}
private fun signIn() {
val scopeList: MutableList<Scope> = ArrayList()
scopeList.apply {
add(Scope(DriveScopes.SCOPE_DRIVE))
add(Scope(DriveScopes.SCOPE_DRIVE_FILE))
add(Scope(DriveScopes.SCOPE_DRIVE_APPDATA))
add(HuaweiIdAuthAPIManager.HUAWEIID_BASE_SCOPE)
}
val authParams = HuaweiIdAuthParamsHelper(
HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM
)
.setAccessToken()
.setIdToken()
.setScopeList(scopeList)
.createParams()
val client = HuaweiIdAuthManager.getService(this, authParams)
startActivityForResult(client.signInIntent, REQUEST_SIGN_IN_CODE)
}
private val refreshAT = DriveCredential.AccessMethod {
/**
* Simplified code snippet for demonstration purposes. For the complete code snippet,
* please go to Client Development > Obtaining Authentication Information > Save authentication information
* in the HUAWEI Drive Kit Development Guide.
**/
[email protected] accessToken
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_SIGN_IN_CODE) {
val authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data)
if (authAccountTask.isSuccessful) {
val authAccount = authAccountTask.result
accessToken = authAccount.accessToken
unionId = authAccount.unionId
val driveCode = checkDriveCode(unionId, accessToken, refreshAT)
when (driveCode) {
DriveCode.SUCCESS -> {
showToastMessage("You are Signed In successfully")
}
DriveCode.SERVICE_URL_NOT_ENABLED -> {
showToastMessage("Drive is not Enabled")
}
else -> {
Log.d(TAG, "onActivityResult: Drive SignIn Failed")
}
}
} else {
Log.e(
TAG,
"Sign in Failed : " + (authAccountTask.exception as ApiException).statusCode
)
}
}
}
private fun checkDriveCode(
unionId: String?,
accessToken: String?,
refreshAccessToken: DriveCredential.AccessMethod?
): Int {
if (StringUtils.isNullOrEmpty(unionId) || StringUtils.isNullOrEmpty(accessToken)) {
return DriveCode.ERROR
}
val builder = DriveCredential.Builder(unionId, refreshAccessToken)
driveCredential = builder.build().setAccessToken(accessToken)
return DriveCode.SUCCESS
}
private fun createWorkRequest(accessToken: String, unionId: String) {
val uploadWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(
workDataOf(
"access_token" to accessToken,
"union_Id" to unionId
)
)
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
)
.setInitialDelay(1, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(applicationContext).enqueue(uploadWorkRequest)
showToastMessage("Work Request is created")
}
private fun showToastMessage(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
UploadWorker
In time, WorkManager will run a worker. To define a worker, we should create a class that extends the Worker class. For Kotlin users, WorkManager provides first-class support for coroutines. So, we can extend our UploadWorker class from CoroutinesWorker. And, it takes two parameters; context and worker parameters.
Then, we need to override the doWork function. doWork is a suspending function, it means that we can run asynchronous tasks and perform network operations. Also, it handles stoppages and cancellations automatically.
Dispatchers.IO is optimized to perform network I/O outside of the main thread. So, we call the withContext(Dispatchers.IO) to create a block that runs on the IO thread pool.
To gets the accessToken and unionId as input data, we used inputData.getString. Then, we checked the drive status. If our access code is still valid, we can use it in the Drive Kit. Otherwise, we have to get renew our code to use Drive Kit.
createAndUploadFile() - First, we created a folder in the Drive named DriveWithWorkManager. We have already put a photo on Android/data/com.huawei.drivewithwork/files as you see in the below image. Note: Don’t forget to replace the package name with yours and put a sample image.
Then, we got the file path of the image on Android.
There are two ways to upload files: direct upload and resumable upload. We used the direct upload in our sample app. Direct upload allows a file of max. 20 MB and, resumable upload doesn’t have a limit. Direct upload is recommended for files smaller than 5 MB and resumable upload for files larger than 5 MB. You can also see the codes of resumable upload as the comment.
Code:
class UploadWorker(val appContext: Context, val params: WorkerParameters) :
CoroutineWorker(appContext, params) {
private val TAG = "UploadWorker"
private var accessToken: String = ""
private var unionId: String = ""
private lateinit var driveCredential: DriveCredential
companion object {
private val MIME_TYPE_MAP: MutableMap<String, String> = HashMap()
init {
MIME_TYPE_MAP.apply {
MIME_TYPE_MAP[".doc"] = "application/msword"
MIME_TYPE_MAP[".jpg"] = "image/jpeg"
MIME_TYPE_MAP[".mp3"] = "audio/x-mpeg"
MIME_TYPE_MAP[".mp4"] = "video/mp4"
MIME_TYPE_MAP[".pdf"] = "application/pdf"
MIME_TYPE_MAP[".png"] = "image/png"
MIME_TYPE_MAP[".txt"] = "text/plain"
}
}
}
override suspend fun doWork(): Result {
return withContext(Dispatchers.IO) {
try {
accessToken = inputData.getString("access_token") ?: ""
unionId = inputData.getString("union_Id") ?: ""
if (accessToken.isEmpty() || unionId.isEmpty()) {
Result.failure()
} else {
val driveCode = checkDriveCode(unionId, accessToken, refreshAT)
if (driveCode == DriveCode.SUCCESS) {
GlobalScope.launch { createAndUploadFile() }
} else {
Log.d(TAG, "onActivityResult: DriveSignIn Failed")
}
Result.success()
}
} catch (error: Throwable) {
Result.failure()
}
}
}
private fun checkDriveCode(
unionId: String?,
accessToken: String?,
refreshAccessToken: DriveCredential.AccessMethod?
): Int {
if (StringUtils.isNullOrEmpty(unionId) || StringUtils.isNullOrEmpty(accessToken)) {
return DriveCode.ERROR
}
val builder = DriveCredential.Builder(unionId, refreshAccessToken)
driveCredential = builder.build().setAccessToken(accessToken)
return DriveCode.SUCCESS
}
private val refreshAT = DriveCredential.AccessMethod {
/**
* Simplified code snippet for demonstration purposes. For the complete code snippet,
* please go to Client Development > Obtaining Authentication Information > Save authentication information
* in the HUAWEI Drive Kit Development Guide.
**/
[email protected] accessToken
}
private fun buildDrive(): Drive? {
return Drive.Builder(driveCredential, appContext).build()
}
private fun createAndUploadFile() {
try {
val appProperties: MutableMap<String, String> =
HashMap()
appProperties["appProperties"] = "property"
val file = com.huawei.cloud.services.drive.model.File()
.setFileName("DriveWithWorkManager")
.setMimeType("application/vnd.huawei-apps.folder")
.setAppSettings(appProperties)
val directoryCreated = buildDrive()?.files()?.create(file)?.execute()
val path = appContext.getExternalFilesDir(null)
val fileObject = java.io.File(path.toString() + "/NatureAndMan.jpg")
appContext.getExternalFilesDir(null)?.absolutePath
val mimeType = mimeType(fileObject)
val content = com.huawei.cloud.services.drive.model.File()
.setFileName(fileObject.name)
.setMimeType(mimeType)
.setParentFolder(listOf(directoryCreated?.id))
buildDrive()?.files()
?.create(content, FileContent(mimeType, fileObject))
?.setFields("*")
?.execute()
// Resumable upload for files larger than 5 MB.
/*
val fileInputStream = FileInputStream(fileObject)
val inputStreamLength = fileInputStream.available()
val streamContent = InputStreamContent(mimeType, fileInputStream)
streamContent.length = inputStreamLength.toLong()
val content = com.huawei.cloud.services.drive.model.File()
.setFileName(fileObject.name)
.setParentFolder(listOf(directoryCreated?.id))
val drive = buildDrive()
drive!!.files().create(content, streamContent).execute()
*/
} catch (exception: Exception) {
Log.d(TAG, "Error when creating file : $exception")
}
}
private fun mimeType(file: File?): String? {
if (file != null && file.exists() && file.name.contains(".")) {
val fileName = file.name
val suffix = fileName.substring(fileName.lastIndexOf("."))
if (MIME_TYPE_MAP.keys.contains(suffix)) {
return MIME_TYPE_MAP[suffix]
}
}
return "*/*"
}
}
Now, everything is ready. We can upload our file to the Drive. Let’s run our app and see what happens.
Launch the app and login with your Huawei Id. Then click the Create A Work Button. After waiting at least a minute, WorkManager will run our work if the conditions are met. And our photo will be uploaded to the drive.
Tips & Tricks
Your app can save app data, such as configuration files and archives in the app folder inside Drive. This folder stores any files with which the user does not have direct interactions. Also, this folder can be accessed only by your app, and the content in the folder is hidden to the user and other apps using Drive.
If the size of the file you download or upload is big, you can use NetworkType.UNMETERED constraint to reduce the cost to the user.
Conclusion
In this article, we have learned how to use Drive Kit with WorkManager. And, we’ve developed a sample app that uploads images to users’ drives. In addition to uploading a file, Drive Kit offers many functions such as reading, writing, and syncing files in Huawei Drive. Please do not hesitate to ask your questions as a comment.
Thank you for your time and dedication. I hope it was helpful. See you in other articles.
References
Huawei Drive Kit Official Documentation
Huawei Drive Kit Official Codelab
WorkManager Official Documentation
Interesting.

Expert: Xamarin Android Online Book Store App Using In-App Purchase and Login with Huawei Id

Overview
In this article, I will create a demo app that highlights an online book store with In-App Purchases. User can easily buy book and make a payment online. I have integrated HMS Account and IAP Kit which is based on Cross-platform Technology Xamarin.
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.
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.
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.
5. Navigate to In-App Purchases and Copy Public Key.
6. Navigate to My apps > Operate, and then enter details in Add Product.
7. Click View and Edit in the above screenshot, enter Product price details, and then click Save.
8. Click Activate for product activation.
Xamarin Account Kit Setup Process
1. Download Xamarin Plugin all the aar and zip files from below URL:
https://developer.huawei.com/consum...y-V1/xamarin-sdk-download-0000001050768441-V1
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:
https://developer.huawei.com/consum...y-V1/xamarin-sdk-download-0000001051011015-V1
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.
7. Navigate to Solution Explore > Project > Reference > Right Click > Add References then Navigate to Browse and add all DLL files from recently added Folder.
8. 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 BookStore
{
[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);
CheckIfIAPAvailable();
// 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);
};
/*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();
}
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();
}
}
}
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();
Intent intent = new Intent(context, typeof(BookStoreActivity));
context.StartActivity(intent);
}
}
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.
}
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.
}
}
}
}
}
}
content_main.xml
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:hwads="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:background="#FFA500">
<TextView
android:id="@+id/txt_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:clickable="true"
android:gravity="bottom"
android:padding="20dp"
android:text="Online Book Store"
android:textColor="#fff"
android:textSize="25dp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical"
android:gravity="center_horizontal"
android:padding="10dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center_horizontal"
android:text="Sign in"
android:textSize="30dp"
android:textColor="#fff" />
<Button
android:id="@+id/btn_huawei_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#dd4b39"
android:text="Huawei Id"
android:textColor="#fff" />
<Button
android:id="@+id/btn_email_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#dd4b39"
android:text="Login with Phone/Email"
android:textColor="#fff" />
</LinearLayout>
<!--<com.huawei.hms.ads.banner.BannerView
android:id="@+id/hw_banner_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
hwads:adId="@string/banner_ad_id"
hwads:bannerSize="BANNER_SIZE_320_50"/>-->
</RelativeLayout>
BookStoreActivity.cs
This activity performs all the operation regarding In-App purchasing and display list of books.
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 BookStore
{
[Activity(Label = "BookStoreActivity", Theme = "@style/AppTheme")]
public class BookStoreActivity : AppCompatActivity, BuyProduct
{
private static String TAG = "BookStoreActivity";
private RecyclerView recyclerView;
private BookStoreAdapter storeAdapter;
IList<ProductInfo> productList;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_store);
recyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerview);
recyclerView.SetLayoutManager(new LinearLayoutManager(this));
recyclerView.SetItemAnimator(new DefaultItemAnimator());
//ADAPTER
storeAdapter = new BookStoreAdapter(this);
storeAdapter.SetData(productList);
recyclerView.SetAdapter(storeAdapter);
GetProducts();
}
private void GetProducts()
{
List<String> productIdList = new List<String>();
productIdList.Add("Book101");
productIdList.Add("Book102");
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 BookStoreActivity storeActivity;
public QueryProductListenerImp(BookStoreActivity storeActivity)
{
this.storeActivity = storeActivity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the result
ProductInfoResult productlistwrapper = (ProductInfoResult)result;
IList<ProductInfo> productList = productlistwrapper.ProductInfoList;
storeActivity.storeAdapter.SetData(productList);
storeActivity.storeAdapter.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 BookStoreActivity storeActivity;
public BuyListenerImp(BookStoreActivity storeActivity)
{
this.storeActivity = storeActivity;
}
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(storeActivity, 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();
}
}
}
}
activity_store.xml
XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"
android:background="#ADD8E6">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
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. User can easily log in and purchase an online book with easy and hassle-free payment.
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
Account Kit
In-App Purchase
Read In Forum

Intermediate: How to Integrating the Game Service in Unity Game

Introduction
In this article, we will cover Integration of Huawei Kit in Unity Project using Official Plugin (Huawei HMS Core App Services). Here we will cover below Kits. With Huawei Game Service, you will have access to a range of development capabilities. You can promote your game quickly and efficiently to Huawei's vast user base by users sign in with their Huawei IDs. You can also use the service to quickly implement achievements, game events, and game addiction prevention functions, build basic game capabilities at a low cost, and perform in-depth game operations based on user and content localization.
Game Service provides the following basic functions for your game apps, with which you can quickly build basic game capabilities.
Game Service Login
Achievements
Leader Board Data
Current Player Info
Game Event begin and end
Development Overview
You need to install Unity software and I assume that you have prior knowledge about the unity and C#.
Hardware Requirements
A computer (desktop or laptop) running Windows 10.
A Huawei phone (with the USB cable), which is used for debugging.
Software Requirements
Java JDK installation package.
Unity software installed.
Visual Studio/Code installed.
HMS Core (APK) 4.X or later.
Follows the steps.
1. Create Unity Project.
Open unity Hub.
Click NEW, select3D, Project Name and Location.
Click CREATE, as follows:
{
"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. Click Asset Store, search Huawei HMS Core App Servicesand click Import, as follows.
3. Once import is successful, verify directory in Assets> Huawei HMS Core App Services path, as follows.
4. Choose Edit > Project Settings > Player and edit the required options in Publishing Settings, as follows.
5. Generate a SHA-256 certificate fingerprint.
To generating SHA-256 certificate fingerprint use below command.
Code:
keytool -list -v -keystore D:\Unity\projects_unity\file_name.keystore -alias alias_name
6. Download agconnect-services.json and copy and paste to Assets > Plugins > Android, as follows.
7. Choose Project Settings > Player and update package name.
8. Open LauncherTemplate.gradle and add below lines.
Java:
apply plugin: 'com.huawei.agconnect'
Java:
implementation 'com.huawei.agconnect:agconnect-core:1.4.2.300'
implementation 'com.huawei.hms:base:5.0.5.300'
implementation 'com.huawei.hms:hwid:5.0.5.301'
implementation 'com.huawei.hms:game:5.0.4.302'
9. Open "baseProjectTemplate.gradle" and add lines, as follows.
Java:
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
Java:
maven {url 'https://developer.huawei.com/repo/'}
10. Open "mainTemplate.gradle" and add lines, as follows.
Java:
implementation 'com.huawei.agconnect:agconnect-core:1.4.2.300'
implementation 'com.huawei.hms:base:5.0.5.300'
implementation 'com.huawei.hms:hwid:5.0.5.301'
implementation 'com.huawei.hms:game:5.0.4.302'
11. Open AndroidManifest file and add below permissions.
Java:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
12. Create Scripts folder and create a class.
HmsGameService.cs
Java:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using HuaweiService;
using UnityEngine.HuaweiAppGallery;
using UnityEngine.HuaweiAppGallery.Listener;
using UnityEngine.HuaweiAppGallery.Model;
using UnityEngine.SceneManagement;
public class HmsGameService : MonoBehaviour
{
// Start is called before the first frame update
private ILoginListener iLoginListener = new LoginListener();
public Text userName;
public Button ButtonLogout;
public Button ButtonLogin;
public Button ButtonGetPlayerInfo;
static string user,sessionId;
static string playerId,guid;
private static List<string> achievementIds = new List<string>();
void Start()
{
HuaweiGameService.AppInit();
}
// Update is called once per frame
void Update()
{
StartCoroutine(UpdateUICoroutine());
}
public void GetAchievements()
{
string guid = System.Guid.NewGuid().ToString();
HuaweiGameService.GetAchievementList(true, new MyGetAchievementListListener());
}
public void GetLeaderBoardData(){
string guid = System.Guid.NewGuid().ToString();
HuaweiGameService.GetAllLeaderboardsIntent(new MyGetLeaderboardIntentListener());
}
public class MyGetLeaderboardIntentListener : IGetLeaderboardIntentListener
{
public void OnSuccess(AndroidJavaObject intent)
{
var msg = "Get leader board intent succeed";
Debug.Log(msg);
user = msg;
}
public void OnFailure(int code, string message)
{
var msg = "Get leaderboard failed, code:" + code + " message:" + message;
Debug.Log(msg);
user = msg;
}
}
public void GetInfo(){
HuaweiGameService.GetCurrentPlayer(true, new MyGetCurrentPlayer());
}
public class MyGetAchievementListListener : IGetAchievementListListener
{
public void OnSuccess(List<Achievement> achievementList)
{
string message = "Achievement count :" + achievementList.Count + "\n";
achievementIds = new List<string>();
foreach (var achievement in achievementList)
{
message += string.Format(
"id:{0}, type:{1}, name:{2}, description:{3} \n",
achievement.AchievementId,
achievement.Type,
achievement.Name,
achievement.Description
);
achievementIds.Add(achievement.AchievementId);
}
user = message;
}
public void OnFailure(int code, string message)
{
string msg = "get achievement list failed, code:" + code + " message:" + message;
user = msg;
}
}
void display(){
userName.text = user;
}
void display(string name)
{
userName.text = name;
}
IEnumerator UpdateUICoroutine() {
//yield on a new YieldInstruction that waits for 5 seconds.
yield return new WaitForSeconds(3);
display();
}
public void onLoginClick()
{
Debug.Log("starting Init");
HuaweiGameService.Init();
Debug.Log("starting login");
HuaweiGameService.Login(iLoginListener);
Debug.Log("finshed login");
}
public class LoginListener : ILoginListener
{
public void OnSuccess(SignInAccountProxy signInAccountProxy)
{
user = "Wel-come "+signInAccountProxy.DisplayName;
}
public void OnFailure(int code, string message)
{
string msg = "login failed, code:" + code + " message:" + message;
Debug.Log(msg);
}
public void OnSignOut(){
}
}
public class MyGetCurrentPlayer : IGetPlayerListener
{
public void OnSuccess(Player player)
{
string msg = "Player ID: " + player.PlayerId +"\nPlayer Name : "+ player.DisplayName;
playerId = player.PlayerId;
Debug.Log(msg);
user = msg;
}
public void OnFailure(int code, string message)
{
string msg = "Get Current Player failed, code:" + code + " message:" + message;
Debug.Log("OnFailure :"+msg);
user = msg;
}
}
public class MyLeaderboardSwitchStatus : ILeaderboardSwitchStatusListener
{
public void OnSuccess(int statusValue)
{
string msg = "LeaderboardSwitchStatus Success: " + statusValue;
Debug.Log(msg);
user = msg;
}
public void OnFailure(int code, string message)
{
string msg = "LeaderboardSwitchStatus failed, code:" + code + " message:" + message;
Debug.Log(msg);
user = msg;
}
}
public class MySubmitPlayerEventBegin : ISubmitPlayerEventListener
{
public void OnSuccess(string jsonRequest)
{
string msg = "submitPlayerEventBegin Success, player info: " + jsonRequest;
ConvertMessageData data = JsonUtility.FromJson<ConvertMessageData>(jsonRequest);
Debug.Log(msg);
sessionId = data.transactionId;
if(sessionId !=null){
HuaweiGameService.GetPlayerExtraInfo(sessionId, new MyGetPlayerExtraInfo());
}
}
public void OnFailure(int code, string message)
{
string msg = "submitPlayerEventBegin failed, code:" + code + " message:" + message;
Debug.Log(msg);
}
public class ConvertMessageData
{
public string transactionId;
}
}
public class MyGetPlayerExtraInfo : IGetPlayerExtraInfoListener
{
public void OnSuccess(AndroidJavaObject jo)
{
string msg = "getPlayerInfo Success, player info: " + jo.ToString();
Debug.Log(msg);
}
public void OnFailure(int code, string message)
{
string msg = "getPlayerInfo failed, code:" + code + " message:" + message;
Debug.Log(msg);
user = msg;
}
public void OnSuccess(PlayerExtraInfo playerExtraInfo)
{
string msg = "getPlayerInfo Success, player info: " + playerExtraInfo.ToString();
Debug.Log(msg);
user = msg;
}
}
}
AndroidManifest.xml
XML:
<?xml version="1.0" encoding="utf-8"?>
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application>
<activity android:name="com.unity3d.player.UnityPlayerActivity"
android:theme="@style/UnityThemeSelector">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
</application>
</manifest>
13. Follow the steps, as shown in image:
a. Assign HmsGameService script to Canvas.
b. Select Button and add onclick event.
c. Assign button to button handler.
14. Onclick Button Handler you find your script HmsGameService (As per your script name) and attach method as per below screen shot.
15. To build apk and run in device, choose File > Build Settings > Build for apk or Build and Run into connected device.
Result
1. Click on Login Button you can see, it will login into game service as per below screenshot.
2. Click on Player Info, Leader Board and Achievement button you and see result as per below screenshot.
Tips and Tricks
Always use the latest version of the library.
Add agconnect-services.json file without fail.
Add SHA-256 fingerprint without fail.
Make sure dependencies added in build files.
Add Achievements and LeaderBoard details before run.
Conclusion
We have learnt integration of HMS Game Service Kit into Unity Game development. You have successfully built a game app and learned how to.
Use Huawei Game Service.
Use Huawei Game Service to develop the game sign-in function.
Use Huawei Game Service to develop the game addiction prevention function.
Thanks for reading the article, please do like and comment your queries or suggestions.
References
Game Service: https://developer.huawei.com/consum.../HMSCore-Guides/introduction-0000001050121216
Unity Manual : https://docs.unity.cn/cn/Packages-cn/[email protected]/manual/gameservices.html
Will it work on non huawei device?

Categories

Resources