{
"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"
}
Hello everyone, in this article, we’ll develop an android application using the Huawei Health kit’s data controller feature. Lets get start it.
About the Service
HUAWEI Health Kit (Health Kit for short) allows ecosystem apps to access fitness and health data of users based on their HUAWEI ID and authorization. For consumers, Health Kit provides a mechanism for fitness and health data storage and sharing based on flexible authorization. For developers and partners, Health Kit provides a data platform and fitness and health open capabilities, so that they can build related apps and services based on a multitude of data types. Health Kit connects the hardware devices and ecosystem apps to provide consumers with health care, workout guidance, and ultimate service experience.
Configure your project on AppGallery Connect
Registering a Huawei ID
You need to register a Huawei ID to use the plugin. If you don’t have one, follow the instructions here.
Preparations for Integrating HUAWEI HMS Core
First of all, you need to integrate Huawei Mobile Services with your application. I will not get into details about how to integrate your application but you can use this tutorial as step by step guide.
Add required dependency to the app-level build.gradle file.
Code:
defaultConfig {
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resConfigs "en", "zh-rCN", "tr"
}
}
dependencies {
implementation 'com.huawei.hms:health:5.0.3.300'
}
Lets add the required permissions to the AndroidManifest.xml file.
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Applying for Health Kit
You should select the data access permissions that must be applied for the product.
For more detail you should visit: https://developer.huawei.com/consumer/en/doc/apply-kitservice-0000001050071707-V5
Developing Your App
Signing In and Applying for Scopes
The developer’s app calls the related APIs to display HUAWEI ID sign-in screen and authorization screen. The app can only access data upon user authorization. The user can select the data types to be authorized and grant only some data permissions.
Code:
public class HealthkitActivity extends AppCompatActivity {
private static final String TAG = "KitConnectActivity";
// Request code for displaying the authorization screen using the startActivityForResult method.
// The value can be defined by developers.
private static final int REQUEST_SIGN_IN_LOGIN = 1002;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_healthkit);
signIn();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Handle the sign-in response.
handleSignInResult(requestCode, data);
}
private void signIn() {
Log.i(TAG, "begin sign in");
List<Scope> scopeList = new ArrayList<>();
// Add scopes to apply for. The following only shows an example.
// Developers need to add scopes according to their specific needs.
// View and save steps in HUAWEI Health Kit.
scopeList.add(new Scope(Scopes.HEALTHKIT_STEP_BOTH));
// View and save height and weight in HUAWEI Health Kit.
scopeList.add(new Scope(Scopes.HEALTHKIT_HEIGHTWEIGHT_BOTH));
// View and save the heart rate data in HUAWEI Health Kit.
scopeList.add(new Scope(Scopes.HEALTHKIT_HEARTRATE_BOTH));
// Used for recording real-time steps in HUAWEI Health Kit.
// scopeList.add(new Scope(Scopes.HEALTHKIT_STEP_REALTIME));
// Used for recording real-time heartRate in HUAWEI Health Kit.
//scopeList.add(new Scope(Scopes.HEALTHKIT_HEARTRATE_REALTIME));
// View and save activityRecord in HUAWEI Health Kit.
// scopeList.add(new Scope(Scopes.HEALTHKIT_ACTIVITY_RECORD_BOTH));
// Configure authorization parameters.
HuaweiIdAuthParamsHelper authParamsHelper =
new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM);
HuaweiIdAuthParams authParams =
authParamsHelper.setIdToken().setAccessToken().setScopeList(scopeList).createParams();
// Initialize the HuaweiIdAuthService object.
final HuaweiIdAuthService authService = HuaweiIdAuthManager.getService(getApplicationContext(), authParams);
Task<AuthHuaweiId> authHuaweiIdTask = authService.silentSignIn();
final Context context = this;
// Add the callback for the call result.
authHuaweiIdTask.addOnSuccessListener(new OnSuccessListener<AuthHuaweiId>() {
@Override
public void onSuccess(AuthHuaweiId huaweiId) {
// The silent sign-in is successful.
Log.i(TAG, "silentSignIn success");
Toast.makeText(context, "silentSignIn success", Toast.LENGTH_LONG).show();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception exception) {
// The silent sign-in fails.
// This indicates that the authorization has not been granted by the current account.
if (exception instanceof ApiException) {
ApiException apiException = (ApiException) exception;
Log.i(TAG, "sign failed status:" + apiException.getStatusCode());
Toast.makeText(context, "sign failed status:" + apiException.getStatusCode(), Toast.LENGTH_LONG).show();
Log.i(TAG, "begin sign in by intent");
Toast.makeText(context, "begin sign in by intent", Toast.LENGTH_LONG).show();
// Call the sign-in API using the getSignInIntent() method.
Intent signInIntent = authService.getSignInIntent();
startActivityForResult(signInIntent, REQUEST_SIGN_IN_LOGIN);
}
}
});
}
private void handleSignInResult(int requestCode, Intent data) {
// Handle only the authorized responses
if (requestCode != REQUEST_SIGN_IN_LOGIN) {
return;
}
// Obtain the authorization response from the intent.
HuaweiIdAuthResult result = HuaweiIdAuthAPIManager.HuaweiIdAuthAPIService.parseHuaweiIdFromIntent(data);
if (result != null) {
Log.d(TAG, "handleSignInResult status = " + result.getStatus() + ", result = " + result.isSuccess());
Toast.makeText(this, "handleSignInResult status = "+ result.getStatus() + ", result = " + result.isSuccess(), Toast.LENGTH_LONG).show();
if (result.isSuccess()) {
Log.d(TAG, "sign in is success");
Toast.makeText(this, "sign in is success", Toast.LENGTH_LONG).show();
// Obtain the authorization result.
HuaweiIdAuthResult authResult =
HuaweiIdAuthAPIManager.HuaweiIdAuthAPIService.parseHuaweiIdFromIntent(data);
}
}
}
}
For details about the sign-in process, please refer to HUAWEI Account Kit Development Guide.
DataController
After integrating Health Kit, the app is able to call ten methods in DataController to perform operations on the fitness and health data. The methods include:
insert: inserts data.
delete: deletes data.
update: updates data.
read: reads data.
readTodaySummation: queries the statistical data of the current day.
readDailySummation: queries the statistical data of multiple days.
clearAll: clears data of the app from the device and cloud.
Inserting the User’s Fitness and Health Data
Insert the user’s fitness and health data into the Health platform.
Code:
public class HealthDataControllerActivity extends AppCompatActivity {
private static final String TAG = "DataController";
// Object of controller for fitness and health data, providing APIs for read/write, batch read/write, and listening
private DataController dataController;
// Internal context object of the activity
private Context context;
// PendingIntent, required when registering or unregistering a listener within the data controller
private PendingIntent pendingIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_health_data_controller);
context = this;
logInfoView = (TextView) findViewById(R.id.data_controller_log_info);
logInfoView.setMovementMethod(ScrollingMovementMethod.getInstance());
initDataController();
syncAllData = findViewById(R.id.syncAllData);
syncAllData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
syncAllData(dataController);
}
});
}
/**
* Initialize a data controller object.
*/
private void initDataController() {
// Obtain and set the read & write permissions for DT_CONTINUOUS_STEPS_DELTA and DT_INSTANTANEOUS_HEIGHT.
// Use the obtained permissions to obtain the data controller object.
HiHealthOptions hiHealthOptions = HiHealthOptions.builder()
.addDataType(DataType.DT_CONTINUOUS_STEPS_DELTA, HiHealthOptions.ACCESS_READ)
.addDataType(DataType.DT_CONTINUOUS_STEPS_DELTA, HiHealthOptions.ACCESS_WRITE)
.addDataType(DataType.DT_INSTANTANEOUS_HEIGHT, HiHealthOptions.ACCESS_READ)
.addDataType(DataType.DT_INSTANTANEOUS_HEIGHT, HiHealthOptions.ACCESS_WRITE)
.build();
AuthHuaweiId signInHuaweiId = HuaweiIdAuthManager.getExtendedAuthResult(hiHealthOptions);
dataController = HuaweiHiHealth.getDataController(context, signInHuaweiId);
}
/**
* Use the data controller to add a sampling dataset.
*
* @param view (indicating a UI object)
* @throws ParseException (indicating a failure to parse the time string)
*/
public void insertData(View view) throws ParseException {
// 1. Build a DataCollector object.
DataCollector dataCollector = new DataCollector.Builder().setPackageName(context)
.setDataType(DataType.DT_CONTINUOUS_STEPS_DELTA)
.setDataStreamName("STEPS_DELTA")
.setDataGenerateType(DataCollector.DATA_TYPE_RAW)
.build();
// 2. Create a sampling dataset set based on the data collector.
final SampleSet sampleSet = SampleSet.create(dataCollector);
// 3. Build the start time, end time, and incremental step count for a DT_CONTINUOUS_STEPS_DELTA sampling point.
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date startDate = dateFormat.parse("2020-09-23 09:00:00");
//Date startDate = dateFormat.parse(start_Date.getText().toString());
Date endDate = dateFormat.parse("2020-09-23 09:05:00");
//Date startDate = dateFormat.parse(end_Date.getText().toString());
/*try {
// Enter the start time and end time. The standard UNIX timestamp is used for storage, without considering the time zone differences.
startDate = dateFormat.parse("2020-03-17 09:00:00");
endDate = dateFormat.parse("2020-03-17 09:05:00");
} catch (ParseException e) {
logger("Time parsing error");
}*/
int stepsDelta = 1000;
// 4. Build a DT_CONTINUOUS_STEPS_DELTA sampling point.
SamplePoint samplePoint = sampleSet.createSamplePoint()
.setTimeInterval(startDate.getTime(), endDate.getTime(), TimeUnit.MILLISECONDS);
samplePoint.getFieldValue(Field.FIELD_STEPS_DELTA).setIntValue(stepsDelta);
// 5. Save a DT_CONTINUOUS_STEPS_DELTA sampling point to the sampling dataset.
// You can repeat steps 3 through 5 to add more sampling points to the sampling dataset.
sampleSet.addSample(samplePoint);
// 6. Call the data controller to insert the sampling dataset into the Health platform.
Task<Void> insertTask = dataController.insert(sampleSet);
// 7. Calling the data controller to insert the sampling dataset is an asynchronous operation.
// Therefore, a listener needs to be registered to monitor whether the data insertion is successful or not.
insertTask.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void result) {
logger("Success insert an SampleSet into HMS core");
showSampleSet(sampleSet);
logger(SPLIT);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
printFailureMessage(e, "insert");
}
});
}
Deleting the User’s Fitness and Health Data
Only historical data that has been inserted by the current app can be deleted from the Health platform.
Code:
/**
* Use the data controller to delete the sampling data by specific criteria.
*
* @param view (indicating a UI object)
* @throws ParseException (indicating a failure to parse the time string)
*/
public void deleteData(View view) throws ParseException {
// 1. Build the condition for data deletion: a DataCollector object.
DataCollector dataCollector = new DataCollector.Builder().setPackageName(context)
.setDataType(DataType.DT_CONTINUOUS_STEPS_DELTA)
.setDataStreamName("STEPS_DELTA")
.setDataGenerateType(DataCollector.DATA_TYPE_RAW)
.build();
// 2. Build the time range for the deletion: start time and end time.
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date startDate = dateFormat.parse("2020-08-27 09:00:00");
Date endDate = dateFormat.parse("2020-08-27 09:05:00");
// 3. Build a parameter object as the conditions for the deletion.
DeleteOptions deleteOptions = new DeleteOptions.Builder().addDataCollector(dataCollector)
.setTimeInterval(startDate.getTime(), endDate.getTime(), TimeUnit.MILLISECONDS)
.build();
// 4. Use the specified condition deletion object to call the data controller to delete the sampling dataset.
Task<Void> deleteTask = dataController.delete(deleteOptions);
// 5. Calling the data controller to delete the sampling dataset is an asynchronous operation.
// Therefore, a listener needs to be registered to monitor whether the data deletion is successful or not.
deleteTask.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void result) {
logger("Success delete sample data from HMS core");
logger(SPLIT);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
String errorCode = e.getMessage();
String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode));
logger(errorCode + ": " + errorMsg);
printFailureMessage(e, "delete");
}
});
}
Updating the User’s Fitness and Health Data
Code:
/**
* Use the data controller to modify the sampling data by specific criteria.
*
* @param view (indicating a UI object)
* @throws ParseException (indicating a failure to parse the time string)
*/
public void updateData(View view) throws ParseException {
// 1. Build the condition for data update: a DataCollector object.
DataCollector dataCollector = new DataCollector.Builder().setPackageName(context)
.setDataType(DataType.DT_CONTINUOUS_STEPS_DELTA)
.setDataStreamName("STEPS_DELTA")
.setDataGenerateType(DataCollector.DATA_TYPE_RAW)
.build();
// 2. Build the sampling dataset for the update: create a sampling dataset
// for the update based on the data collector.
SampleSet sampleSet = SampleSet.create(dataCollector);
// 3. Build the start time, end time, and incremental step count for
// a DT_CONTINUOUS_STEPS_DELTA sampling point for the update.
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date startDate = dateFormat.parse("2020-08-27 09:00:00");
Date endDate = dateFormat.parse("2020-08-27 09:05:00");
int stepsDelta = 2000;
// 4. Build a DT_CONTINUOUS_STEPS_DELTA sampling point for the update.
SamplePoint samplePoint = sampleSet.createSamplePoint()
.setTimeInterval(startDate.getTime(), endDate.getTime(), TimeUnit.MILLISECONDS);
samplePoint.getFieldValue(Field.FIELD_STEPS_DELTA).setIntValue(stepsDelta);
// 5. Add an updated DT_CONTINUOUS_STEPS_DELTA sampling point to the sampling dataset for the update.
// You can repeat steps 3 through 5 to add more updated sampling points to the sampling dataset for the update.
sampleSet.addSample(samplePoint);
// 6. Build a parameter object for the update.
// Note: (1) The start time of the modified object updateOptions cannot be greater than the minimum
// value of the start time of all sample data points in the modified data sample set
// (2) The end time of the modified object updateOptions cannot be less than the maximum value of the
// end time of all sample data points in the modified data sample set
UpdateOptions updateOptions =
new UpdateOptions.Builder().setTimeInterval(startDate.getTime(), endDate.getTime(), TimeUnit.MILLISECONDS)
.setSampleSet(sampleSet)
.build();
// 7. Use the specified parameter object for the update to call the
// data controller to modify the sampling dataset.
Task<Void> updateTask = dataController.update(updateOptions);
// 8. Calling the data controller to modify the sampling dataset is an asynchronous operation.
// Therefore, a listener needs to be registered to monitor whether the data update is successful or not.
updateTask.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void result) {
logger("Success update sample data from HMS core");
logger(SPLIT);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
printFailureMessage(e, "update");
String errorCode = e.getMessage();
String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode));
logger(errorCode + ": " + errorMsg);
}
});
}
Querying the User’s Fitness and Health Data
To read historical data from the Health platform, for example, to read the number of steps taken within a period of time, you can specify the read conditions in ReadOptions. For example, you can specify the data collector, data type, and detailed data. The dataset that matches the query criteria will be returned.
Code:
/**
* Use the data controller to query the sampling dataset by specific criteria.
*
* @param view (indicating a UI object)
* @throws ParseException (indicating a failure to parse the time string)
*/
public void readData(View view) throws ParseException {
// 1. Build the condition for data query: a DataCollector object.
DataCollector dataCollector = new DataCollector.Builder().setPackageName(context)
.setDataType(DataType.DT_CONTINUOUS_STEPS_DELTA)
.setDataStreamName("STEPS_DELTA")
.setDataGenerateType(DataCollector.DATA_TYPE_RAW)
.build();
// 2. Build the time range for the query: start time and end time.
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date startDate = dateFormat.parse("2020-08-27 09:00:00");
Date endDate = dateFormat.parse("2020-08-27 09:05:00");
try {
// Enter the start time and end time. The standard UNIX timestamp is used for storage, without considering the time zone differences. Data points within the specified timestamp range will be queried.
startDate = dateFormat.parse("2020-03-17 09:00:00");
endDate = dateFormat.parse("2020-03-17 09:05:00");
} catch (ParseException exception) {
logger("Time parsing error");
}
// 3. Build the condition-based query objec
ReadOptions readOptions = new ReadOptions.Builder().read(dataCollector)
.setTimeRange(startDate.getTime(), endDate.getTime(), TimeUnit.MILLISECONDS)
.build();
// 4. Use the specified condition query object to call the data controller to query the sampling dataset.
Task<ReadReply> readReplyTask = dataController.read(readOptions);
// 5. Calling the data controller to query the sampling dataset is an asynchronous operation.
// Therefore, a listener needs to be registered to monitor whether the data query is successful or not.
readReplyTask.addOnSuccessListener(new OnSuccessListener<ReadReply>() {
@Override
public void onSuccess(ReadReply readReply) {
logger("Success read an SampleSets from HMS core");
for (SampleSet sampleSet : readReply.getSampleSets()) {
showSampleSet(sampleSet);
}
logger(SPLIT);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
printFailureMessage(e, "read");
String errorCode = e.getMessage();
String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode));
logger(errorCode + ": " + errorMsg);
}
});
}
Querying the Statistical Fitness and Health Data of the User of the Day
Code:
/**
* Use the data controller to query the summary data of the current day by data type.
*
* @param view (indicating a UI object)
*/
public void readToday(View view) {
// 1. Use the specified data type (DT_CONTINUOUS_STEPS_DELTA) to call the data controller to query
// the summary data of this data type of the current day.
Task<SampleSet> todaySummationTask = dataController.readTodaySummation(DataType.DT_CONTINUOUS_STEPS_DELTA);
// 2. Calling the data controller to query the summary data of the current day is an
// asynchronous operation. Therefore, a listener needs to be registered to monitor whether
// the data query is successful or not.
// Note: In this example, the inserted data time is fixed at 2020-08-27 09:05:00.
// When commissioning the API, you need to change the inserted data time to the current date
// for data to be queried.
todaySummationTask.addOnSuccessListener(new OnSuccessListener<SampleSet>() {
@Override
public void onSuccess(SampleSet sampleSet) {
logger("Success read today summation from HMS core");
if (sampleSet != null) {
showSampleSet(sampleSet);
}
logger(SPLIT);
}
});
todaySummationTask.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
printFailureMessage(e, "readTodaySummation");
String errorCode = e.getMessage();
String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode));
logger(errorCode + ": " + errorMsg);
}
});
}
Querying the Statistical Fitness and Health Data of the User of Multiple Days
Code:
/**
* Querying the Summary Fitness and Health Data of the User on the Local Device of the Current Day
*
* @param view (indicating a UI object)
*/
public void currentDay(View view) {
//Call the DataController to query the statistical value of the DT_CONTINUOUS_STEPS_DELTA data type of the current day.
// The query time range starts from 00:00:00 of the day and ends at the system timestamp when the API is called.
// Calling this API will query all data points with the start time or end time being in the specified time range.
// The sum value of the queried data points will be returned.
int endTime = 20200827;
int startTime = 20200818;
Task<SampleSet> daliySummationTask =dataController.readDailySummation(DataType.DT_CONTINUOUS_STEPS_DELTA, startTime, endTime);
//Calling the data controller to query the summary data of the current day is an asynchronous operation.
// Therefore, a listener needs to be registered to monitor whether the data query is successful or not.
daliySummationTask.addOnSuccessListener(new OnSuccessListener<SampleSet>() {
@Override
public void onSuccess(SampleSet sampleSet) {
logger("Success read daily summation from HMS core");
if (sampleSet != null) {
showSampleSet(sampleSet);
}
logger(SPLIT);
}
});
daliySummationTask.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
logger("readTodaySummation" + e.toString());
}
});
}
Clearing the User’s Fitness and Health Data from the Device and Cloud
Call the clearAll method of the DataController to delete data inserted by the current app from the device and cloud
Code:
/**
* Clear all user data from the device and cloud.
*
* @param view (indicating a UI object)
*/
public void clearCloudData(View view) {
// 1. Call the clearAll method of the data controller to delete data
// inserted by the current app from the device and cloud.
Task<Void> clearTask = dataController.clearAll();
// 2. Calling the data controller to clear user data from the device and cloud is an asynchronous operation.
// Therefore, a listener needs to be registered to monitor whether the clearance is successful or not.
clearTask.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void result) {
logger("clearAll success");
logger(SPLIT);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
printFailureMessage(e, "clearAll");
}
});
}
We successfully integrated Huawei Health Kit’s Data Controller feature into our project. Here’s the result.
Resources:
https://developer.huawei.com/consumer/en/doc/datacontroller-develop-0000001050071677-V5
Related Links
Original post: https://medium.com/huawei-developers/health-kit-data-controller-sample-a9d29b3ba651
Hi Nice information, does Huawei Health Kit provide auto sync data from health devices ( fit bit ) or we need to gather data and provide them to HMS health kit.
Related
Before using HUAWEI IAP on your app, send an isEnvReady request from your app to HUAWEI IAP to check whether the currently signed-in HUAWEI ID is located in a country or region where HUAWEI IAP is available.
If your app does not connect to the HUAWEI ID sign-in API, connect to this API to sign in.
Perform the following steps for your development:
Send an isEnvReady request and set two callback listeners to receive the request result.
● If the request result is Succeeded, your app will obtain an IsEnvReadyResult instance, indicating that the country or region where the currently signed-in HUAWEI ID is located supports HUAWEI IAP.
● If the request result is Failed, HUAWEI IAP will return an Exception object. If the object is IapApiException, use its getStatusCode() method to obtain the result code of the request.
If the result code is OrderStatusCode.ORDER_HWID_NOT_LOGIN (no HUAWEI ID signed in), use status in the IapApiException object to bring up the HUAWEI ID sign-in screen. Then, obtain the result in the onActivityResult method of Activity. Parse returnCode from the intent returned by onActivityResult. If returnCode is OrderStatusCode.SUCCESS, the country or region where the currently signed-in HUAWEI ID is located supports HUAWEI IAP. If returnCode is not OrderStatusCode.SUCCESS, an exception occurs.
Code:
// To get the Activity instance that calls this API.
Activity activity = getActivity();
Task<IsEnvReadyResult> task = Iap.getIapClient(activity).isEnvReady();
task.addOnSuccessListener(new OnSuccessListener<IsEnvReadyResult>() {
@Override
public void onSuccess(IsEnvReadyResult result) {
// Obtain the execution result.
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof IapApiException) {
IapApiException apiException = (IapApiException) e;
Status status = apiException.getStatus();
if (status.getStatusCode() == OrderStatusCode.ORDER_HWID_NOT_LOGIN) {
// Not logged in.
if (status.hasResolution()) {
try {
// 6666 is an int constant defined by the developer.
status.startResolutionForResult(getActivity(), 6666);
} catch (IntentSender.SendIntentException exp) {
}
}
} else if (status.getStatusCode() == OrderStatusCode.ORDER_ACCOUNT_AREA_NOT_SUPPORTED) {
// The current region does not support HUAWEI IAP.
}
}
}
});
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 6666) {
if (data != null) {
// Obtain the execution result.
int returnCode = data.getIntExtra("returnCode", 1);
}
}
}
More information like this, you can visit HUAWEI Developer Forum
Introduction
Huawei Game service provides a centralized place for you to manage game services and configure metadata for authorizing and authenticating your game. Using Huawei game service, developer can access range of capabilities to help develop your games more efficiently.
Features
1. Developers can promote their game efficiently and quickly.
2. Developers can easily build the foundation of game by implementing features like achievements and events.
3. Developers can perform in-depth operations tailored to their game and their users.
In this article, we will implement leaderboard feature provided by Huawei game service in Tic tac toe game.
To understand event feature provided by Huawei game service, please refer my last article.
Prerequisites
1. Developer has created an application on App Gallery Connect. Follow Creating an App.
2. Integrating app gallery connect SDK. Please refer to AppGallery Connect Service Getting Started.
3. Developer has integrated Huawei Account kit. Follow this tutorial to integrate account kit.
4. HUAWEI mobile phone with HMS Core 4.0.0.300 or later installed.
{
"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"
}
Setup:
1. Enable Huawei Game service in Manage APIS. Please refer to Service Enabling.
2. Add appgallery connect plug-in in app-level build.gradle
Code:
apply plugin: 'com.huawei.agconnect'
3. Add following dependencies in app-level build.gradle and click on Sync Now and wait till synchronization is done.
Code:
dependencies {
implementation 'com.huawei.hms:base:4.0.4.301'
implementation 'com.huawei.hms:hwid:4.0.4.300'
implementation 'com.huawei.hms:iap:4.0.4.300'
implementation 'com.huawei.hms:game:4.0.3.301'
}
Initialization
Once Initial set up is done, let’s implement Huawei game service in Tic tac toe game
1. Add the following code in onCreate() method of Application class.
Code:
public class GameServiceApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HuaweiMobileServicesUtil.setApplication(this);
}
@Override
public void onTerminate() {
super.onTerminate();
}
}
2. To initialize the game
Code:
private void init() {
JosAppsClient appsClient = JosApps.getJosAppsClient(this, null);
appsClient.init();
Log.i(TAG, "initialization success");
}
Sign in
1. When app is launched, Huawei sign-in page is displayed.
2. User enters Huawei Huawei ID and password to sign in.
3. After a successful sign in, the app obtains player information corresponding to Huawei ID.
Please refer to this article for sign in implementation.
Game Leaderboards
The term Leaderboard is often used in gaming platform to signify rank among people who play various titles. Players can be ranked against other players based on their skills. Overall, leaderboards can provide an incentive for players to improve as they give many a sense of superiority or accomplishment.
Creating a Leaderboard in AppGallery Connect
1. Sign in to AppGallery Connect and select My apps.
2. Select an app from the app list to create an event.
3. Click the Operate tab and go to Products > Leaderboard. Click Create.
4. Configure the leaderboard information and click Save.
5. Check your leaderboard ID on the event list and properly save the ID for development.
Development
To initialize leaderboard , we need to obtain instance of RankingsClient
Code:
RankingsClient client = Games.getRankingsClient(this, mAuthHuaweiId);
mAuthHuaweiId is obtained during sign in.
2. To allow player's score to be displayed on leaderboard, call setRankingSwitchStauts() method, and pass 1 as parameter. the player's leaderboard switch is set to 0
Code:
private void enableRankingSwitchStatus (int status) {
Task<Integer> task = rankingsClient.setRankingSwitchStatus(status);
task.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer statusValue) {
// success to set the value,the server will reponse the latest value.
Log.d(TAG, "setRankingSwitchStatus success : " +statusValue) ;
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// errCode information
if (e instanceof ApiException) {
String result = "Err Code:" + ((ApiException) e).getStatusCode();
Log.e(TAG , "setRankingSwitchStatus error : " + result);
}
}
});
}
3. To check the leaderboard switch settings
Code:
Task<Integer> task = rankingsClient.getRankingSwitchStatus();
task.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer statusValue) {
//success to get the latest value
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// errCode information
if (e instanceof ApiException) {
String result = "err :" + ((ApiException) e).getStatusCode();
}
}
});
onSuccess() will return either 0 or 1. 0 indicates leaderboard is not enabled for players. If 1 is returned, your game submits the updated score of the player.
4. To submit the score
Code:
private void submitRanking(int score) {
rankingsClient.submitRankingScore(LEADERBOARD_ID, score);
}
LEADERBOARD_ID indicates the ID of the leaderboard which is generated while defining the event in AppGallery connect.
5. To obtain leaderboard, we need to call getCurrentRankingSummary(id, isRealTime)
Code:
Task<Ranking> task = rankingsClient.getRankingSummary(id, isRealTime);
task.addOnSuccessListener(new OnSuccessListener<Ranking>() {
@Override
public void onSuccess(Ranking s) {
showLog( " success. ");
if (task.getResult() == null) {
Log.d(TAG, "Ranking result is null");
tvLeaderboard.setText("Ranking result is null");
return;
}
Ranking ranking = task.getResult();
StringBuffer buffer = new StringBuffer();
buffer.append("-------Ranking-------\n");
if (ranking == null) {
buffer.append("ranking is null");
} else {
buffer.append("\n DisplayName:" + ranking.getRankingDisplayName());
iv.setVisibility(View.VISIBLE);
Glide.with(this).load(ranking.getRankingImageUri()).into(iv);
buffer.append("\n ScoreOrder:" + ranking.getRankingScoreOrder());
if (ranking.getRankingVariants() != null) {
buffer.append("\n Variants.size:" + ranking.getRankingVariants().size());
if (ranking.getRankingVariants().size() > 0) {
showRankingVariant(ranking.getRankingVariants() , buffer);
}
}
}
}
});
isRealTime is a Boolean which indicates whether to obtain result from huawei server (true) or local cache.
id indicates the ID of the leaderboard which is generated while defining the event in AppGallery connect.
6. To display only specified number of top rankings, we need to call getRankingTopScores(id, timeDimension, maxResult, offsetPlayerRank, pageDirection)
Code:
Task<RankingsClient.RankingScores> task
= rankingsClient.getRankingTopScores(id, timeDimension, maxResults, offsetPlayerRank, pageDirection);
task.addOnSuccessListener(new OnSuccessListener<RankingsClient.RankingScores>() {
@Override
public void onSuccess(RankingsClient.RankingScores s) {
showLog(" success. ");
Ranking ranking = task.getResult().getRanking();
List<RankingScore> scoresBuffer = task.getResult().getRankingScores();
for (int i = 0; i < scoresBuffer.size(); i++) {
printRankingScore(scoresBuffer.get(i), i);
}
}
});
private void printRankingScore(RankingScore s, int index) {
StringBuffer buffer = new StringBuffer();
buffer.append("------RankingScore " + index + "------\n");
if (s == null) {
buffer.append("rankingScore is null\n");
return;
}
String displayScore = s.getRankingDisplayScore();
buffer.append(" DisplayScore: " + displayScore).append("\n");
buffer.append(" TimeDimension: " + s.getTimeDimension()).append("\n");
buffer.append(" RawPlayerScore: " + s.getPlayerRawScore()).append("\n");
buffer.append(" PlayerRank: " + s.getPlayerRank()).append("\n");
String displayRank = s.getDisplayRank();
buffer.append(" getDisplayRank: " + displayRank).append("\n");
buffer.append(" ScoreTag: " + s.getScoreTips()).append("\n");
buffer.append(" updateTime: " + s.getScoreTimestamp()).append("\n");
String playerDisplayName = s.getScoreOwnerDisplayName();
buffer.append(" PlayerDisplayName: " + playerDisplayName).append("\n");
buffer.append(" PlayerHiResImageUri: " + s.getScoreOwnerHiIconUri()).append("\n");
buffer.append(" PlayerIconImageUri: " + s.getScoreOwnerIconUri()).append("\n\n");
Log.d(TAG , buffer.toString());
tvLeaderboard.setText(buffer.toString());
}
id indicates the ID of the leaderboard which is generated while defining the event in AppGallery connect.
timeDimension indicates time frame. Provide 0 for daily, 1 for weekly, 2 for all time .
maxResults indicates maximum number of records on each page. The value is an integer ranging from 1 to 21.
offsetPlayerRank specifies rank by offsetPlayerRank.For example, if offsetPlayerRank is set to 5 and pageDirection is set to 0, one additional page of scores starting from the fifth rank downwards will be loaded.
pageDirection indicates data obtaing direction. Currently 0 is supported, indicating data of next page is obtained.
7. To display the score of current player , we need to call getCurrentPlayerRankingScore(id, timeDimension)
Code:
Task<RankingScore> task = rankingsClient.getCurrentPlayerRankingScore(id, timeDimension);
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
showLog(" failure. exception: " + e);
}
});
task.addOnSuccessListener(new OnSuccessListener<RankingScore>() {
@Override
public void onSuccess(RankingScore s) {
showLog(" success. ");
StringBuffer buffer = new StringBuffer();
if (task.getResult() == null) {
buffer.append("RankingScore result is null");
return;
}
buffer.append("=======RankingScore=======\n");
RankingScore s = task.getResult();
printRankingScore(s, 0);
}
});
task.addOnCanceledListener(new OnCanceledListener() {
@Override
public void onCanceled() {
showLog(method + " canceled. ");
}
});
private void printRankingScore(RankingScore s, int index) {
StringBuffer buffer = new StringBuffer();
buffer.append("------RankingScore " + index + "------\n");
if (s == null) {
buffer.append("rankingScore is null\n");
return;
}
String displayScore = s.getRankingDisplayScore();
buffer.append(" DisplayScore: " + displayScore).append("\n");
buffer.append(" TimeDimension: " + s.getTimeDimension()).append("\n");
buffer.append(" RawPlayerScore: " + s.getPlayerRawScore()).append("\n");
buffer.append(" PlayerRank: " + s.getPlayerRank()).append("\n");
String displayRank = s.getDisplayRank();
buffer.append(" getDisplayRank: " + displayRank).append("\n");
buffer.append(" ScoreTag: " + s.getScoreTips()).append("\n");
buffer.append(" updateTime: " + s.getScoreTimestamp()).append("\n");
String playerDisplayName = s.getScoreOwnerDisplayName();
buffer.append(" PlayerDisplayName: " + playerDisplayName).append("\n");
buffer.append(" PlayerHiResImageUri: " + s.getScoreOwnerHiIconUri()).append("\n");
buffer.append(" PlayerIconImageUri: " + s.getScoreOwnerIconUri()).append("\n\n");
Log.d(TAG , buffer.toString());
tvLeaderboard.setText(buffer.toString());
}
id indicates the ID of the leaderboard which is generated while defining the event in AppGallery connect.
timeDimension indicates time frame. Provide 0 for daily, 1 for weekly, 2 for all time .
Screenshots
Thank you so much for such a nice information. I think, that will be very helpful for developers who are going to use Huawei Game Service
More information like this, you can visit HUAWEI Developer Forum
Original link: https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201319969591990258&fid=0101187876626530001
Hello everyone,
In this article we are going to take a look at Huawei Mobile Services (HMS) integration Plugin for Unity. This article not going through the details of kits, we will focus on integration part.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
We will use account, In App purchases and push kit for this article. This plugin for now support five main kits.
Plugin Features :
1- Account Kit: Its provides developers with simple, secure, and quick sign-in and authorization functions. Instead of entering accounts and passwords and waiting for authorization, users can just tap the Sign In with HUAWEI ID button to quickly and securely sign in to your app. For details you read this article.
2- In App purchases: This service allows you to offer in-app purchases and facilitates in-app payment. Users can purchase a variety of virtual products, including one-time virtual products and subscriptions, directly within your app. For details you can read this article. Plugin supports the following types of products: Consumables, Non-consumables, Subscriptions
3- Huawei Ads: This Publisher Service utilizes Huawei’s vast user base and extensive data capabilities to deliver targeted, high quality ad content to users. With this service, your app will be able to generate revenues while bringing your users content which is relevant to them. For details you can read this article. Plugin supports the following types: Interstitial and rewarded videos
4- Push notifications: HUAWEI Push Kit is a messaging service provided by Huawei for developers. It establishes a messaging channel from the cloud to devices. By integrating HUAWEI Push Kit, developers can send messages to apps on users’ devices in real time. For details you can read this article.
5- Game kit: This kit provide player info, leaderboards and achievements.For details you can read this article.
HMS Plugin Work Flow:
Prerequisites
Step 1
Create an application on HUAWEI Developer(use this link for details).
Enable the HUAWEI services.
Step 2
This plugin have two branch for Unity versions.(version 2019 used)
Download plugin from this for Unity version 2019.x.
Download plugin from this for Unity version 2018.x .
Step 3
Import package to unity.
Step 4
Update “AndroidManifest file” and “agconnect.json” file.
1- Open project_path\Assets\Plugins\Android\ AndoridManifest.
Update App ID, CP ID and package name, this informations exist in agconnect.json file.
2- Open project_path\Assets\Huawei\agconnect.json replace with you downloanded from AGC.
Integration
This plugin have some demos scenes. You can drictly use this scenes. We will create empty scene, after that we will add objects.
1. Account kit:
Create empty object and add account manager script from Huawei package component.
(Object name is important, if you want to give diffrent name from “AccountManager” update ~~\Assets\Huawei\Account\AccountManager.cs -> line 11)
Add new script “AccountSignIn” to this object.
On AccountSignIn.cs:
Code:
using HuaweiMobileServices.Id;
using HuaweiMobileServices.Utils;
using UnityEngine;
using UnityEngine.UI;
using HmsPlugin;
public class AccountSignIn : MonoBehaviour
{
private const string NOT_LOGGED_IN = "No user logged in";
private const string LOGGED_IN = "{0} is logged in";
private const string LOGIN_ERROR = "Error or cancelled login";
private Text loggedInUser;
private AccountManager accountManager;
// Start is called before the first frame update
void Start()
{
loggedInUser = GameObject.Find("LoggedUserText").GetComponent<Text>();
loggedInUser.text = NOT_LOGGED_IN;
accountManager = AccountManager.GetInstance();
accountManager.OnSignInSuccess = OnLoginSuccess;
accountManager.OnSignInFailed = OnLoginFailure;
LogIn();
}
public void LogIn()
{
accountManager.SignIn();
}
public void LogOut()
{
accountManager.SignOut();
loggedInUser.text = NOT_LOGGED_IN;
}
public void OnLoginSuccess(AuthHuaweiId authHuaweiId)
{
loggedInUser.text = string.Format(LOGGED_IN, authHuaweiId.DisplayName);
}
public void OnLoginFailure(HMSException error)
{
loggedInUser.text = LOGIN_ERROR;
}
}
We can create sign in and sign out buttons, this buttons call “AccountSignIn” functions.
2. In App purchases:
Create empty object ( Object name is important, if you want to give diffrent name from “IapManager” update ~~\Assets\Huawei\IAP\IapManager.cs -> line 11).
Add new script “IapController.cs” to this object.
Add Account kit manager to IapManager object, its needed for using IAP services.
In App Purchases have three type of product.
1- Type 0: Consumables
Description:Consumables are used once, are depleted, and can be purchased again.
Example:Extra lives and gems in a game.
Getting Consumables products from Huawei AGC with HMS Unity plugin:
Code:
public void ObtainProductConsumablesInfo(IList<string> productIdConsumablesList)
{
if (iapAvailable != true)
{
OnObtainProductInfoFailure?.Invoke(IAP_NOT_AVAILABLE);
return;
}
ProductInfoReq productInfoReq = new ProductInfoReq
{
PriceType = 0,
ProductIds = productIdConsumablesList
};
iapClient.ObtainProductInfo(productInfoReq).AddOnSuccessListener((type0) =>
{
Debug.Log("[HMSPlugin]:" + type0.ErrMsg + type0.ReturnCode.ToString());
Debug.Log("[HMSPlugin]: Found " + type0.ProductInfoList.Count + "consumable products");
OnObtainProductInfoSuccess?.Invoke(new List<ProductInfoResult> { type0});
}).AddOnFailureListener((exception) =>
{
Debug.Log("[HMSPlugin]: ERROR Consumable ObtainInfo" + exception.Message);
OnObtainProductInfoFailure?.Invoke(exception);
});
}
2- Type 1: Non-consumables
Description: Non-consumables are purchased once and do not expire.
Example:Extra game levels in a game or permanent membership of an app
3- Type 2: Subscriptions
Description:Users can purchase access to value-added functions or content in a specified period of time. The subscriptions are automatically renewed on a recurring basis until users decide to cancel.
Example:Non-permanent membership of an app, such as a monthly video membership
On IAPController.cs
Code:
# define DEBUG
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HuaweiConstants;
using HuaweiMobileServices.Base;
using HuaweiMobileServices.IAP;
using System;
using UnityEngine.Events;
using HuaweiMobileServices.Id;
using HmsPlugin;
public class IapController : MonoBehaviour
{
public string[] ConsumableProducts;
public string[] NonConsumableProducts;
public string[] SubscriptionProducts;
[HideInInspector]
public int numberOfProductsRetrieved;
List<ProductInfo> productInfoList = new List<ProductInfo>();
List<string> productPurchasedList = new List<string>();
private IapManager iapManager;
private AccountManager accountManager;
UnityEvent loadedEvent;
void Awake()
{
Debug.Log("[HMSPlugin]: IAPP manager Init");
loadedEvent = new UnityEvent();
}
// Start is called before the first frame update
/// <summary>
///
/// </summary>
void Start()
{
Debug.Log("[HMS]: Started");
accountManager = GetComponent<AccountManager>();
Debug.Log(accountManager.ToString());
accountManager.OnSignInFailed = (error) =>
{
Debug.Log($"[HMSPlugin]: SignIn failed. {error.Message}");
};
accountManager.OnSignInSuccess = SignedIn;
accountManager.SignIn();
Debug.Log("[HMS]: Started2");
}
private void SignedIn(AuthHuaweiId authHuaweiId)
{
Debug.Log("[HMS]: SignedIn");
iapManager = GetComponent<IapManager>();
iapManager.OnCheckIapAvailabilitySuccess = LoadStore;
iapManager.OnCheckIapAvailabilityFailure = (error) =>
{
Debug.Log($"[HMSPlugin]: IAP check failed. {error.Message}");
};
iapManager.CheckIapAvailability();
}
private void LoadStore()
{
Debug.Log("[HMS]: LoadStorexxx");
// Set Callback for ObtainInfoSuccess
iapManager.OnObtainProductInfoSuccess = (productInfoResultList) =>
{
Debug.Log("[HMS]: LoadStore1");
if (productInfoResultList != null)
{
Debug.Log("[HMS]: LoadStore2");
foreach (ProductInfoResult productInfoResult in productInfoResultList)
{
foreach (ProductInfo productInfo in productInfoResult.ProductInfoList)
{
productInfoList.Add(productInfo);
}
}
}
loadedEvent.Invoke();
};
// Set Callback for ObtainInfoFailure
iapManager.OnObtainProductInfoFailure = (error) =>
{
Debug.Log($"[HMSPlugin]: IAP ObtainProductInfo failed. {error.Message},,, {error.WrappedExceptionMessage},,, {error.WrappedCauseMessage}");
};
// Call ObtainProductInfo
if (!IsNullOrEmpty(ConsumableProducts))
{
iapManager.ObtainProductConsumablesInfo(new List<string>(ConsumableProducts));
}
if (!IsNullOrEmpty(NonConsumableProducts))
{
iapManager.ObtainProductNonConsumablesInfo(new List<string>(NonConsumableProducts));
}
if (!IsNullOrEmpty(SubscriptionProducts))
{
iapManager.ObtainProductSubscriptionInfo(new List<string>(SubscriptionProducts));
}
}
private void RestorePurchases()
{
iapManager.OnObtainOwnedPurchasesSuccess = (ownedPurchaseResult) =>
{
productPurchasedList = (List<string>)ownedPurchaseResult.InAppPurchaseDataList;
};
iapManager.OnObtainOwnedPurchasesFailure = (error) =>
{
Debug.Log("[HMS:] RestorePurchasesError" + error.Message);
};
iapManager.ObtainOwnedPurchases();
}
public ProductInfo GetProductInfo(string productID)
{
return productInfoList.Find(productInfo => productInfo.ProductId == productID);
}
public void showHidePanelDynamically(GameObject yourObject)
{
Debug.Log("[HMS:] showHidePanelDynamically");
var getCanvasGroup = yourObject.GetComponent<CanvasGroup>();
if (getCanvasGroup.alpha == 0)
{
getCanvasGroup.alpha = 1;
getCanvasGroup.interactable = true;
}
else
{
getCanvasGroup.alpha = 0;
getCanvasGroup.interactable = false;
}
}
public void BuyProduct(string productID)
{
iapManager.OnBuyProductSuccess = (purchaseResultInfo) =>
{
// Verify signature with purchaseResultInfo.InAppDataSignature
// If signature ok, deliver product
// Consume product purchaseResultInfo.InAppDataSignature
iapManager.ConsumePurchase(purchaseResultInfo);
};
iapManager.OnBuyProductFailure = (errorCode) =>
{
switch (errorCode)
{
case OrderStatusCode.ORDER_STATE_CANCEL:
// User cancel payment.
Debug.Log("[HMS]: User cancel payment");
break;
case OrderStatusCode.ORDER_STATE_FAILED:
Debug.Log("[HMS]: order payment failed");
break;
case OrderStatusCode.ORDER_PRODUCT_OWNED:
Debug.Log("[HMS]: Product owned");
break;
default:
Debug.Log("[HMS:] BuyProduct ERROR" + errorCode);
break;
}
};
var productInfo = productInfoList.Find(info => info.ProductId == productID);
var payload = "test";
iapManager.BuyProduct(productInfo, payload);
}
public void addListener(UnityAction action)
{
if (loadedEvent != null)
{
loadedEvent.AddListener(action);
}
}
public bool IsNullOrEmpty(Array array)
{
return (array == null || array.Length == 0);
}
}
After getting all product informations from AGC, user can buy product with “BuyProduct” function with send product ids.
Buy Product Button Settings:
Select Prefab:
Select Function:
Add Parameter:
IapManager.cs:
Code:
# define DEBUG
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HuaweiConstants;
using HuaweiMobileServices.Base;
using HuaweiMobileServices.IAP;
using System;
using UnityEngine.Events;
using HuaweiMobileServices.Id;
using HmsPlugin;
public class IapController : MonoBehaviour
{
public string[] ConsumableProducts;
public string[] NonConsumableProducts;
public string[] SubscriptionProducts;
[HideInInspector]
public int numberOfProductsRetrieved;
List<ProductInfo> productInfoList = new List<ProductInfo>();
List<string> productPurchasedList = new List<string>();
private IapManager iapManager;
private AccountManager accountManager;
UnityEvent loadedEvent;
void Awake()
{
Debug.Log("[HMSPlugin]: IAPP manager Init");
loadedEvent = new UnityEvent();
}
// Start is called before the first frame update
/// <summary>
///
/// </summary>
void Start()
{
Debug.Log("[HMS]: Started");
accountManager = GetComponent<AccountManager>();
Debug.Log(accountManager.ToString());
accountManager.OnSignInFailed = (error) =>
{
Debug.Log($"[HMSPlugin]: SignIn failed. {error.Message}");
};
accountManager.OnSignInSuccess = SignedIn;
accountManager.SignIn();
Debug.Log("[HMS]: Started2");
}
private void SignedIn(AuthHuaweiId authHuaweiId)
{
Debug.Log("[HMS]: SignedIn");
iapManager = GetComponent<IapManager>();
iapManager.OnCheckIapAvailabilitySuccess = LoadStore;
iapManager.OnCheckIapAvailabilityFailure = (error) =>
{
Debug.Log($"[HMSPlugin]: IAP check failed. {error.Message}");
};
iapManager.CheckIapAvailability();
}
private void LoadStore()
{
Debug.Log("[HMS]: LoadStorexxx");
// Set Callback for ObtainInfoSuccess
iapManager.OnObtainProductInfoSuccess = (productInfoResultList) =>
{
Debug.Log("[HMS]: LoadStore1");
if (productInfoResultList != null)
{
Debug.Log("[HMS]: LoadStore2");
foreach (ProductInfoResult productInfoResult in productInfoResultList)
{
foreach (ProductInfo productInfo in productInfoResult.ProductInfoList)
{
productInfoList.Add(productInfo);
}
}
}
loadedEvent.Invoke();
};
// Set Callback for ObtainInfoFailure
iapManager.OnObtainProductInfoFailure = (error) =>
{
Debug.Log($"[HMSPlugin]: IAP ObtainProductInfo failed. {error.Message},,, {error.WrappedExceptionMessage},,, {error.WrappedCauseMessage}");
};
// Call ObtainProductInfo
if (!IsNullOrEmpty(ConsumableProducts))
{
iapManager.ObtainProductConsumablesInfo(new List<string>(ConsumableProducts));
}
if (!IsNullOrEmpty(NonConsumableProducts))
{
iapManager.ObtainProductNonConsumablesInfo(new List<string>(NonConsumableProducts));
}
if (!IsNullOrEmpty(SubscriptionProducts))
{
iapManager.ObtainProductSubscriptionInfo(new List<string>(SubscriptionProducts));
}
}
private void RestorePurchases()
{
iapManager.OnObtainOwnedPurchasesSuccess = (ownedPurchaseResult) =>
{
productPurchasedList = (List<string>)ownedPurchaseResult.InAppPurchaseDataList;
};
iapManager.OnObtainOwnedPurchasesFailure = (error) =>
{
Debug.Log("[HMS:] RestorePurchasesError" + error.Message);
};
iapManager.ObtainOwnedPurchases();
}
public ProductInfo GetProductInfo(string productID)
{
return productInfoList.Find(productInfo => productInfo.ProductId == productID);
}
public void showHidePanelDynamically(GameObject yourObject)
{
Debug.Log("[HMS:] showHidePanelDynamically");
var getCanvasGroup = yourObject.GetComponent<CanvasGroup>();
if (getCanvasGroup.alpha == 0)
{
getCanvasGroup.alpha = 1;
getCanvasGroup.interactable = true;
}
else
{
getCanvasGroup.alpha = 0;
getCanvasGroup.interactable = false;
}
}
public void BuyProduct(string productID)
{
iapManager.OnBuyProductSuccess = (purchaseResultInfo) =>
{
// Verify signature with purchaseResultInfo.InAppDataSignature
// If signature ok, deliver product
// Consume product purchaseResultInfo.InAppDataSignature
iapManager.ConsumePurchase(purchaseResultInfo);
};
iapManager.OnBuyProductFailure = (errorCode) =>
{
switch (errorCode)
{
case OrderStatusCode.ORDER_STATE_CANCEL:
// User cancel payment.
Debug.Log("[HMS]: User cancel payment");
break;
case OrderStatusCode.ORDER_STATE_FAILED:
Debug.Log("[HMS]: order payment failed");
break;
case OrderStatusCode.ORDER_PRODUCT_OWNED:
Debug.Log("[HMS]: Product owned");
break;
default:
Debug.Log("[HMS:] BuyProduct ERROR" + errorCode);
break;
}
};
var productInfo = productInfoList.Find(info => info.ProductId == productID);
var payload = "test";
iapManager.BuyProduct(productInfo, payload);
}
public void addListener(UnityAction action)
{
if (loadedEvent != null)
{
loadedEvent.AddListener(action);
}
}
public bool IsNullOrEmpty(Array array)
{
return (array == null || array.Length == 0);
}
}
3. Push notifications:
First add your project PushKitManager prefab, this prefab using PushKitManager.cs for creating tokens.
For push kit, we have a two choice,
We can send notification to all devices without token.
We can get tokens from devices and we can use this token to send notification sepicified devices.
AGC server Push Scope for sending notification:
On PushKitManager.cs
Code:
using HuaweiMobileServices.Base;
using HuaweiMobileServices.Id;
using HuaweiMobileServices.Push;
using HuaweiMobileServices.Utils;
using System;
using UnityEngine;
using UnityEngine.UI;
namespace HmsPlugin
{
public class PushKitManager : MonoBehaviour, IPushListener
{
public Action<string> OnTokenSuccess { get; set; }
public Action<Exception> OnTokenFailure { get; set; }
public Action<RemoteMessage> OnMessageReceivedSuccess { get; set; }
// Start is called before the first frame update
void Start()
{
PushManager.Listener = this;
var token = PushManager.Token;
Debug.Log($"[HMS] Push token from GetToken is {token}");
if (token != null)
{
OnTokenSuccess?.Invoke(token);
}
}
public void OnNewToken(string token)
{
Debug.Log($"[HMS] Push token from OnNewToken is {token}");
if (token != null)
{
OnTokenSuccess?.Invoke(token);
}
}
public void OnTokenError(Exception e)
{
Debug.Log("Error asking for Push token");
Debug.Log(e.StackTrace);
OnTokenFailure?.Invoke(e);
}
public void OnMessageReceived(RemoteMessage remoteMessage)
{
OnMessageReceivedSuccess?.Invoke(remoteMessage);
}
}
}
Push Notification result:
Thank you reading this article.
I hope this gives you a starting point for Huawei Mobile Services and Unity integration.
More information like this, you can visit HUAWEI Developer Forum
Original link: https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201345979826040147&fid=0101187876626530001
In this article we will learn to use Huawei Health Kit in a workout app.
Huawei Health Kit
Health Kit based on users Huawei Id and authorization, provide best service by accessing their health and fitness data. Using this service, developers do not need to worry about capturing their user’s data from any Bluetooth device like fitness tracker watch or Fitbit and record them. It helps developers to create a hassle-free, effective and productive application to their users.
HMS Health Kit Services
1) DataController: Developers can use this API to insert, delete, update, and read data, as well as listen to data updates by registering a listener.
2) SensorsController: Developers can use this API to receiving data reported by the sensor like steps count.
3) AutoRecordController: Developers can use this API to automatically record sensor data, stop recording sensor data, and obtain the record information.
4) ActivityRecordsController: Developers can use this API to create and manage user activities.
Here in this article we will use two services provided by HMS Health Kit and that is SensorsController and ActivityRecordsController.
Use case
To lose weight, you can walk or run. When you walk or run will lose some calories from our body. So better to track our daily steps, if you track daily steps, then you can be active and also to avoid health problems like heart attacks or broken bones, a new study suggests.
Increasing our walking and maintaining the steps can reduce our risk of heart attacks, strokes and fractures over the next few years. Pedometers can be helpful for us to use, as they give us a clear idea of how much we are doing (self-monitoring) and can be used to set realistic goals for increasing our walking gradually.
HMS Health Kit works as a pedometer, count our steps and provide the information directly to us. We can also record our steps using HMS Health Kit and use it for self-monitoring.
Demo
{
"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"
}
Prerequisite
1) Must have a Huawei Developer Account.
2) Must have a Huawei phone with HMS 4.0.0.300 or later.
3) Must have a laptop or desktop with Android Studio, Jdk 1.8, SDK platform 26 and Gradle 4.6 installed.
Things need to be done
1) Create a project in android studio.
2) Get the SHA Key. For getting the SHA key, refer this article.
3) Create an app in the Huawei AppGallery connect.
4) Click on Health kit in console.
5) Apply for Health Kit
6) Select Product Type as Mobile App, APK Name as your project package name and check the required permission required for your application to work, as shown below:
Finally click on submit button to apply for health kit.
7) Provide the SHA Key in App Information Section.
8) Provide storage location.
9) Add the app ID generated when the creating the app on HUAWEI Developers to the application section
Code:
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="YOUR_APP_ID" />
10) After completing all the above points we need to download the agconnect-services.json from App Information Section. Copy and paste the Json file in the app folder of the android project.
11) Enter the below maven url inside the repositories of buildscript and allprojects (project build.gradle file):
Code:
maven { url ‘http://developer.huawei.com/repo/’ }
12) Enter the below plugin in the app build.gradle file:
Code:
apply plugin: ‘com.huawei.agconnect’
13) Enter the below Health Kit, account kit and auth service dependencies in the dependencies section:
Code:
implementation 'com.huawei.agconnect:agconnect-core:1.4.0.300'
implementation 'com.huawei.agconnect:agconnect-auth:1.4.0.300'
implementation 'com.huawei.hms:hwid:4.0.4.300'
implementation 'com.huawei.hms:hihealth-base:5.0.0.300'
14) Enter the below permission in android manifest file
Code:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
15) Now Sync the gradle.
Huawei ID authentication
Sign in with Huawei ID authentication and apply for the scope to obtain the permissions to access the Health Kit APIs. Different scopes correspond to different permissions. Developers can apply for permissions based on service requirements.
Code:
public void doLogin(View view) {
Log.i(TAG, "begin sign in");
List<Scope> scopeList = new ArrayList<>();
scopeList.add(new Scope(Scopes.HEALTHKIT_STEP_BOTH));
scopeList.add(new Scope(Scopes.HEALTHKIT_HEIGHTWEIGHT_BOTH));
scopeList.add(new Scope(Scopes.HEALTHKIT_ACTIVITY_BOTH));
HuaweiIdAuthParamsHelper authParamsHelper = new HuaweiIdAuthParamsHelper(
HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM);
mHuaweiIdAuthParams = authParamsHelper.setIdToken()
.setAccessToken()
.setScopeList(scopeList)
.createParams();
mHuaweiIdAuthService = HuaweiIdAuthManager.getService(SplashActivity.this, mHuaweiIdAuthParams);
startActivityForResult(mHuaweiIdAuthService.getSignInIntent(), REQUEST_SIGN_IN_LOGIN);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_SIGN_IN_LOGIN) {
Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data);
if (authHuaweiIdTask.isSuccessful()) {
AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
Log.i("TAG", "accessToken:" + huaweiAccount.getAccessToken());
AGConnectAuthCredential credential = HwIdAuthProvider.credentialWithToken(huaweiAccount.getAccessToken());
AGConnectAuth.getInstance().signIn(credential).addOnSuccessListener(new OnSuccessListener<SignInResult>() {
@Override
public void onSuccess(SignInResult signInResult) {
mUser = AGConnectAuth.getInstance().getCurrentUser();
Intent intent = new Intent(SplashActivity.this,InstructionActivity.class);
startActivity(intent);
finish();
}
});
} else {
Log.e("TAG", "sign in failed : " + ((ApiException) authHuaweiIdTask.getException()).getStatusCode());
}
}
}
Below is the link to know more about the mapping between scopes and permissions:
https://developer.huawei.com/consumer/en/doc/HMSCore-References-V5/scopes-0000001050092713-V5
SensorController
As the name suggested it will gather information using sensor of android mobile device. Using SensorController of HMS Health Kit:
1) We can connect to a built-in sensor of the phone such as a pedometer to obtain data.
2) We can connect to an external BLE device such as heart rate strap to obtain data.
Here we will use SensorController to count the number of steps taken by our users. In order to that we need to directly call the register method of SensorsController to register a listener to obtain the data reported by the built-in sensor.
Obtain SensorController object
Code:
HiHealthOptions options = HiHealthOptions.builder().build();
AuthHuaweiId signInHuaweiId = HuaweiIdAuthManager.getExtendedAuthResult(options);
sensorsController = HuaweiHiHealth.getSensorsController(this, signInHuaweiId);
Register a listener to listen to the step count
Code:
private OnSamplePointListener onStepPointListener = new OnSamplePointListener() {
@Override
public void onSamplePoint(SamplePoint samplePoint) {
// The step count, time, and type data reported by the pedometer is called back to the app through
// samplePoint.
showSamplePoint();
mCurrentSamplePoint = samplePoint;
if (mLastSamplePoint == null) {
mLastSamplePoint = samplePoint;
}
}
};
public void registerSteps() {
if (sensorsController == null) {
Toast.makeText(StepsTrackerActivity.this, "SensorsController is null", Toast.LENGTH_LONG).show();
return;
}
DataCollector dataCollector = new DataCollector.Builder()
.setDataType(DataType.DT_CONTINUOUS_STEPS_TOTAL)
.setDataGenerateType(DataCollector.DATA_TYPE_RAW)
.setPackageName(StepsTrackerActivity.this)
.setDeviceInfo(new DeviceInfo("hw", "hw", "hw", 0))
.build();
SensorOptions.Builder builder = new SensorOptions.Builder();
builder.setDataType(DataType.DT_CONTINUOUS_STEPS_TOTAL);
builder.setDataCollector(dataCollector);
sensorsController.register(builder.build(), onStepPointListener)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Toast.makeText(StepsTrackerActivity.this, "Register Success", Toast.LENGTH_LONG).show();
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Toast.makeText(StepsTrackerActivity.this, "Register Failed", Toast.LENGTH_LONG).show();
}
});
}
Show SamplePoint data
Code:
private void showSamplePoint() {
if (mLastSamplePoint != null && mCurrentSamplePoint != null) {
SamplePoint samplePoint = mCurrentSamplePoint;
System.out.println("STEPS >>>" + (samplePoint.getFieldValue(Field.FIELD_STEPS).asIntValue()
- mLastSamplePoint.getFieldValue(Field.FIELD_STEPS).asIntValue()));
runOnUiThread(new Runnable() {
@Override
public void run() {
txtSteps.setText(String.valueOf(Integer.parseInt(txtSteps.getText().toString()) + samplePoint.getFieldValue(Field.FIELD_STEPS).asIntValue()
- mLastSamplePoint.getFieldValue(Field.FIELD_STEPS).asIntValue()));
if(selectedWeight.equalsIgnoreCase("Kg")) {
txtCalorie.setText(String.format("%.2f", Integer.parseInt(weight.getText().toString()) * Long.parseLong(txtSteps.getText().toString()) * 0.4 * 0.001 * 1.036) + " Kcal");
}else{
txtCalorie.setText(String.format("%.2f", Integer.parseInt(weight.getText().toString()) * 2.2046226218 * Long.parseLong(txtSteps.getText().toString()) * 0.4 * 0.001 * 1.036) + " Kcal");
}
mLastSamplePoint = samplePoint;
}
});
}
}
Un-Register a listener to stop the step count
Code:
public void unregisterSteps() {
if (sensorsController == null) {
Toast.makeText(StepsTrackerActivity.this, "SensorsController is null", Toast.LENGTH_LONG).show();
return;
}
// Unregister the listener for the step count.
sensorsController.unregister(onStepPointListener).addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
Toast.makeText(StepsTrackerActivity.this, "UnregisterSteps Succeed ...", Toast.LENGTH_LONG).show();
mLastSamplePoint = null;
mCurrentSamplePoint = null;
txtSteps.setText("0");
txtCalorie.setText("0");
btnStartStop.setText("Start Tracking");
isStartStop = true;
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Toast.makeText(StepsTrackerActivity.this, "UnregisterSteps Failed ...", Toast.LENGTH_LONG).show();
}
});
}
Result
ActivityRecordsController
As the name suggested it will record / insert the activity data of user. Here we will insert the steps count taken by user.
Start time and end time for ActivityRecords
We need start and end time to insert the user steps data as show below:
Code:
public static long getEndTime() {
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
return endTime;
}
public static long getStartTime() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, -1);
long startTime = cal.getTimeInMillis();
return startTime;
}
Insert ActivityRecords
Using SamplePoint of HMS Health Kit we will insert the step count of user as shown below:
Code:
private void insertActivityRecords(){
HiHealthOptions hihealthOptions = HiHealthOptions.builder().build();
AuthHuaweiId signInHuaweiId = HuaweiIdAuthManager.getExtendedAuthResult(hihealthOptions);
ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(StepsTrackerActivity.this, signInHuaweiId);
ActivityRecord activityRecord = new ActivityRecord.Builder()
.setName("AddStepsRecord")
.setDesc("This is Steps record")
.setId("StepId")
.setActivityTypeId(HiHealthActivities.ON_FOOT)
.setStartTime(startTime, TimeUnit.MILLISECONDS)
.setEndTime(endTime, TimeUnit.MILLISECONDS)
.build();
DataCollector dataCollector = new DataCollector.Builder()
.setDataType(DataType.DT_CONTINUOUS_STEPS_DELTA)
.setDataGenerateType(DataCollector.DATA_TYPE_RAW)
.setPackageName(getApplicationContext())
.setDataCollectorName("AddStepsRecord")
.build();
SampleSet sampleSet = SampleSet.create(dataCollector);
SamplePoint samplePoint = sampleSet.createSamplePoint().setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
samplePoint.getFieldValue(Field.FIELD_STEPS_DELTA).setIntValue(Integer.parseInt(txtSteps.getText().toString()));
sampleSet.addSample(samplePoint);
ActivityRecordInsertOptions insertOption =
new ActivityRecordInsertOptions.Builder().setActivityRecord(activityRecord).addSampleSet(sampleSet).build();
Task<Void> addTask = activityRecordsController.addActivityRecord(insertOption);
addTask.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
unregisterSteps();
Log.i("ActivityRecords","ActivityRecord add was successful!");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
String errorCode = e.getMessage();
String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode));
Log.i("ActivityRecords",errorCode + ": " + errorMsg);
}
});
}
DatePicker
Code:
private void getDatePicker(EditText edtVal){
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
datePickerVal = "";
DatePickerDialog datePickerDialog = new DatePickerDialog(GetRecords.this,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
// edtVal.setText(dayOfMonth + "-" + (monthOfYear + 1) + "-" + year);
edtVal.setText(year + "-" + (monthOfYear + 1) + "-" + dayOfMonth);
// datePickerVal = dayOfMonth + "-" + (monthOfYear + 1) + "-" + year;
}
}, mYear, mMonth, mDay);
datePickerDialog.show();
}
TimePicker
Code:
private void getTimePicker(EditText edtVal){
final Calendar c = Calendar.getInstance();
mHour = c.get(Calendar.HOUR_OF_DAY);
mMinute = c.get(Calendar.MINUTE);
// Launch Time Picker Dialog
TimePickerDialog timePickerDialog = new TimePickerDialog(GetRecords.this,
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay,
int minute) {
edtVal.setText(hourOfDay + ":" + minute);
}
}, mHour, mMinute, false);
timePickerDialog.show();
}
Start & End Time milliseconds conversion
Code:
private long getDateTime(String dateval,String timeval) {
String dateStr = dateval+" "+timeval+":00";
long milliseconds = 0;
try {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = dateFormat.parse(dateStr);
System.out.println("DATE TIME >>>"+date.getTime());
milliseconds = date.getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return milliseconds;
}
Reading ActivityRecords and Associated Data
Here we can obtain all ActivityRecords within a specific period of time for particular data, or obtain a specific ActivityRecord by name or ID as shown below:
Code:
private void readActivityRecords(){
HiHealthOptions hihealthOptions = HiHealthOptions.builder().build();
AuthHuaweiId signInHuaweiId = HuaweiIdAuthManager.getExtendedAuthResult(hihealthOptions);
ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(getApplicationContext(), signInHuaweiId);
ActivityRecordReadOptions readOption =
new ActivityRecordReadOptions.Builder().setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
.readActivityRecordsFromAllApps()
.read(DataType.DT_CONTINUOUS_STEPS_DELTA)
.build();
Task<ActivityRecordReply> getTask = activityRecordsController.getActivityRecord(readOption);
getTask.addOnSuccessListener(new OnSuccessListener<ActivityRecordReply>() {
@Override
public void onSuccess(ActivityRecordReply activityRecordReply) {
Log.i("ActivityRecords","Get ActivityRecord was successful!");
//Print ActivityRecord and corresponding activity data in the result.
List<ActivityRecord> activityRecordList = activityRecordReply.getActivityRecords();
for (ActivityRecord activityRecord : activityRecordList) {
DateFormat dateFormat = DateFormat.getDateInstance();
DateFormat timeFormat = DateFormat.getTimeInstance();
for (SampleSet sampleSet : activityRecordReply.getSampleSet(activityRecord)) {
for (SamplePoint dp : sampleSet.getSamplePoints()) {
String values = "Start: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)) + " " + timeFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS))+"\n"+
"End: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS))+ " " + timeFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS));
for (Field field : dp.getDataType().getFields()) {
/*Log.i("ActivityRecordSample",
"\tField: " + field.toString() + " Value: " + dp.getFieldValue(field));*/
values = values +"\n"+"Step Taken: " + dp.getFieldValue(field);
}
samplePointList.add(values);
}
}
}
showTheList();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
String errorCode = e.getMessage();
String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode));
Log.i("ActivityRecords",errorCode + ": " + errorMsg);
}
});
}
Result
What we learn?
We learn how to make the usage of two beautiful services provided by HMS Health Kit and also situation where we need the most, in this case fetching user steps and inserting/fetching the data when we need to show in the app.
GitHub
Very soon I am going to post the entire code on GitHub. So come back again to check it out.
For more reference
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/service-introduction-0000001050071661-V5
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/sensorscontroller-develop-0000001050069728-V5
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/activityrecord-develop-0000001050069730-V5
Overview
In this article, I will create a College Campus Placement Centre Demo App which highlights ongoing pool college placement with all listed companies and their details. Student can easily apply and register with available food facility on the campus through IAP. I have integrated HMS Account, Ads, Analytics and IAP Kit which is based on Cross-platform Technology Xamarin.
Ads Kit Service Introduction
HMS Ads kit is powered by Huawei which allows the developer to monetize services such as Banner, Splash, Reward and Interstitial Ads. HUAWEI Ads Publisher Service is a monetization service that leverages Huawei's extensive data capabilities to display targeted, high-quality ad content in your application to the vast user base of Huawei devices.
Analytics Kit Service Introduction
Analytics kit is powered by Huawei which allows rich analytics models to help you clearly understand user behavior and gain in-depth insights into users, products, and content. As such, you can carry out data-driven operations and make strategic decisions about app marketing and product optimization.
Analytics Kit implements the following functions using data collected from apps:
1. Provides data collection and reporting APIs for collection and reporting custom events.
2. Sets up to 25 user attributes.
3. Supports automatic event collection and session calculation as well as predefined event IDs and parameters.
HMS IAP Service Introduction
HMS In-App Purchase Kit allows purchasing any product from the application with highly secure payment. Users can purchase a variety of products or services, including common virtual products and subscriptions, directly within your app. It also provides a product management system (PMS) for managing the prices and languages of in-app products (including games) in multiple locations.
These are the following 3 types of in-app products supported by the IAP:
1. Consumable: Consumables are used once, are depleted, and can be purchased again.
2. Non-consumable: Non-consumables are purchased once and do not expire.
3. Auto-renewable subscriptions: Users can purchase access to value-added functions or content in a specified period of time. The subscriptions are automatically renewed on a recurring basis until users decide to cancel.
Account Kit Service Introduction
HMS Account Kit allows you to connect to the Huawei ecosystem using your HUAWEI ID from a range of devices, such as mobile phones, tablets, and smart screens.
It’s a simple, secure, and quick sign-in and authorization functions. Instead of entering accounts and passwords and waiting for authentication.
Complies with international standards and protocols such as OAuth2.0 and OpenID Connect, and supports two-factor authentication (password authentication and mobile number authentication) to ensure high security.
Prerequisite
1. Xamarin Framework
2. Huawei phone
3. Visual Studio 2019
App Gallery Integration process
1. Sign In and Create or Choose a project on AppGallery Connect portal.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
2. Navigate to Project settings > download the configuration file.
3. Navigate to General Information > Data Storage location.
4. Navigate to Manage APIs > enable APIs to require by an application.
5. Navigate to My apps > Operate, and then enter details in Add Product.
6. Click Activate for product activation.
7. Navigate to Huawei Analytics > Overview > Custom dashboard > Enable Analytics.
Xamarin Ads Kit Setup Process
1. Download Xamarin Plugin of all the aar and zip files from the URL:
2. Open the XAdsIdentifier-3.4.35.300.sln solution in Visual Studio.
3. Navigate to Solution Explorer and right-click on jar Add > Existing Item and choose aar file which download in Step 1.
4. Choose aar file from download location.
5. Right-click on added aar file, then choose Properties > Build Action > LibraryProjectZip.
Note: Repeat Step 3 and 4 for all aar file.
6. Build the Library and make DLL files.
Xamarin Analytics Kit Setup Process
1. Download Xamarin Plugin of all the aar and zip files from the URL, see the similar image in Ads kit setup Step 1:
2. Open the XHiAnalytics-5.0.5.300.sln solution in Visual Studio, see the similar image in Ads kit setup Step 2.
3. Navigate to Solution Explorer and right-click on jar Add > Existing Item and choose aar file which download in Step 1.
4. Choose aar file from download location.
5. Right-click on added aar file, then choose Properties > Build Action > LibraryProjectZip.
Note: Repeat Step 3 and 4 for all aar file.
6. Build the Library and make DLL files.
Xamarin Account Kit Setup Process
1. Download Xamarin Plugin all the aar and zip files from the URL, see the similar image in Ads kit setup Step 1:
2. Open the XHwid-5.03.302.sln solution in Visual Studio, see the similar image in Ads kit setup Step 2.
Xamarin IAP Kit Setup Process
1. Download Xamarin Plugin all the aar and zip files from the URL, see the similar image in Ads kit setup Step 1:
2. Open the XIAP-5.0.2.300.sln solution in Visual Studio, see the similar image in Ads kit setup Step 2.
3. Navigate to Solution Explorer and Right-click on jar Add > Existing Item and choose aar file which downloads in Step 1.
4. Right-click on added aar file then choose Properties > Build Action > LibraryProjectZip.
Note: Repeat Step 3 and 4 for all aar file.
5. Build the Library and make dll files.
Xamarin App Development
1. Open Visual Studio 2019 and Create a new project.
2. Navigate to Solution Explore > Project > Assets > Add JSON file.
3. Navigate to Solution Explore > Project > Add > Add New Folder.
4. Navigate to Folder(created) > Add > Add Existing and add all DLL files.
5. Right click > Properties > Build Action > None.
6. Navigate to Solution Explore > Project > Reference > Right Click > Add References, then Navigate to Browse and add all DLL files from the recently added Folder.
7. Added reference then click Ok.
MainActivity.cs
This activity performs all the operation regarding login with Huawei Id.
Java:
using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using Android.Support.V7.App;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Agconnect.Config;
using Com.Huawei.Hmf.Tasks;
using Com.Huawei.Hms.Common;
using Com.Huawei.Hms.Ads.Banner;
using Com.Huawei.Hms.Analytics;
using Com.Huawei.Hms.Iap;
using Com.Huawei.Hms.Iap.Entity;
using Com.Huawei.Hms.Support.Hwid;
using Com.Huawei.Hms.Support.Hwid.Request;
using Com.Huawei.Hms.Support.Hwid.Result;
using Com.Huawei.Hms.Support.Hwid.Service;
namespace PlacementApp
{
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
private Button btnLoginWithHuaweiId;
private HuaweiIdAuthParams mAuthParam;
public static IHuaweiIdAuthService mAuthManager;
private static String TAG = "MainActivity";
public static String name, email;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
btnLoginWithHuaweiId = FindViewById<Button>(Resource.Id.btn_huawei_id);
// Write code for Huawei id button click
mAuthParam = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DefaultAuthRequestParam)
.SetIdToken().SetEmail()
.SetAccessToken()
.CreateParams();
mAuthManager = HuaweiIdAuthManager.GetService(this, mAuthParam);
HiAnalyticsTools.EnableLog();
instance = HiAnalytics.GetInstance(this);
instance.SetAnalyticsEnabled(true);
// Click listener for each button
btnLoginWithHuaweiId.Click += delegate
{
StartActivityForResult(mAuthManager.SignInIntent, 1011);
string text = "Login Clicked";
Toast.MakeText(Android.App.Application.Context, text, ToastLength.Short).Show();
// Initiate Parameters
Bundle bundle = new Bundle();
bundle.PutString("text", text);
instance.OnEvent("ButtonClickEvent", bundle);
};
CheckIfIAPAvailable();
/*FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
fab.Click += FabOnClick;*/
//check permissions
checkPermission(new string[] { Android.Manifest.Permission.Internet,
Android.Manifest.Permission.AccessNetworkState,
Android.Manifest.Permission.ReadSms,
Android.Manifest.Permission.ReceiveSms,
Android.Manifest.Permission.SendSms,
Android.Manifest.Permission.BroadcastSms}, 100);
}
private void loadBannerAds()
{
// Obtain BannerView based on the configuration in layout
BannerView bottomBannerView = FindViewById<BannerView>(Resource.Id.hw_banner_view);
bottomBannerView.AdListener = new AdsListener();
AdParam adParam = new AdParam.Builder().Build();
bottomBannerView.LoadAd(adParam);
// Obtain BannerView using coding
BannerView topBannerview = new BannerView(this);
topBannerview.AdId = "testw6vs28auh3";
topBannerview.BannerAdSize = BannerAdSize.BannerSize32050;
topBannerview.LoadAd(adParam);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 1011)
{
//login success
Task authHuaweiIdTask = HuaweiIdAuthManager.ParseAuthResultFromIntent(data);
if (authHuaweiIdTask.IsSuccessful)
{
AuthHuaweiId huaweiAccount = (AuthHuaweiId)authHuaweiIdTask.TaskResult();
Log.Info(TAG, "signIn get code success.");
Log.Info(TAG, "ServerAuthCode: " + huaweiAccount.AuthorizationCode);
Toast.MakeText(Android.App.Application.Context, "SignIn Success", ToastLength.Short).Show();
ManageHomeScreen(huaweiAccount, true);
}
else
{
Log.Info(TAG, "signIn failed: " + ((ApiException)authHuaweiIdTask.Exception).StatusCode);
Toast.MakeText(Android.App.Application.Context, ((ApiException)authHuaweiIdTask.Exception).StatusCode.ToString(), ToastLength.Short).Show();
Toast.MakeText(Android.App.Application.Context, "SignIn Failed", ToastLength.Short).Show();
ManageHomeScreen(null, false);
}
}
}
public void ManageHomeScreen(AuthHuaweiId data, Boolean loginStatus)
{
if (loginStatus)
{
btnLoginWithHuaweiId.Visibility = ViewStates.Gone;
name = data.DisplayName;
email = data.Email;
}
else
{
btnLoginWithHuaweiId.Visibility = ViewStates.Visible;
}
}
public void checkPermission(string[] permissions, int requestCode)
{
foreach (string permission in permissions)
{
if (ContextCompat.CheckSelfPermission(this, permission) == Permission.Denied)
{
ActivityCompat.RequestPermissions(this, permissions, requestCode);
}
}
}
/*private void FabOnClick(object sender, EventArgs eventArgs)
{
View view = (View) sender;
Snackbar.Make(view, "Replace with your own action", Snackbar.LengthLong)
.SetAction("Action", (Android.Views.View.IOnClickListener)null).Show();
}*/
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected override void AttachBaseContext(Context context)
{
base.AttachBaseContext(context);
AGConnectServicesConfig config = AGConnectServicesConfig.FromContext(context);
config.OverlayWith(new HmsLazyInputStream(context));
}
private void CancelAuthorisation()
{
Task cancelAuthorizationTask = mAuthManager.CancelAuthorization();
Log.Info(TAG, "Cancel Authorisation");
cancelAuthorizationTask.AddOnCompleteListener(
new OnCompleteListener
(
this, "Cancel Authorization Success",
"Cancel Authorization Failed"
)
);
}
public void SignOut()
{
Task signOutTask = mAuthManager.SignOut();
signOutTask.AddOnSuccessListener(new OnSuccessListener(this, "SignOut Success"))
.AddOnFailureListener(new OnFailureListener("SignOut Failed"));
}
public class OnCompleteListener : Java.Lang.Object, IOnCompleteListener
{
//Message when task is successful
private string successMessage;
//Message when task is failed
private string failureMessage;
MainActivity context;
public OnCompleteListener(MainActivity context, string SuccessMessage, string FailureMessage)
{
this.context = context;
this.successMessage = SuccessMessage;
this.failureMessage = FailureMessage;
}
public void OnComplete(Task task)
{
if (task.IsSuccessful)
{
//do some thing while cancel success
Log.Info(TAG, successMessage);
//context.SignOut();
}
else
{
//do some thing while cancel failed
Exception exception = task.Exception;
if (exception is ApiException)
{
int statusCode = ((ApiException)exception).StatusCode;
Log.Info(TAG, failureMessage + ": " + statusCode);
}
//context.ManageHomeScreen(null, true);
}
}
}
public class OnSuccessListener : Java.Lang.Object, Com.Huawei.Hmf.Tasks.IOnSuccessListener
{
//Message when task is successful
private string successMessage;
MainActivity context;
public OnSuccessListener(MainActivity context, string SuccessMessage)
{
this.successMessage = SuccessMessage;
this.context = context;
}
public void OnSuccess(Java.Lang.Object p0)
{
Log.Info(TAG, successMessage);
Toast.MakeText(Android.App.Application.Context, successMessage, ToastLength.Short).Show();
context.ManageHomeScreen(null, false);
}
}
public class OnFailureListener : Java.Lang.Object, Com.Huawei.Hmf.Tasks.IOnFailureListener
{
//Message when task is failed
private string failureMessage;
public OnFailureListener(string FailureMessage)
{
this.failureMessage = FailureMessage;
}
public void OnFailure(Java.Lang.Exception p0)
{
Log.Info(TAG, failureMessage);
Toast.MakeText(Android.App.Application.Context, failureMessage, ToastLength.Short).Show();
}
}
public void CheckIfIAPAvailable()
{
IIapClient mClient = Iap.GetIapClient(this);
Task isEnvReady = mClient.IsEnvReady();
isEnvReady.AddOnSuccessListener(new ListenerImp(this)).AddOnFailureListener(new ListenerImp(this));
}
class ListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private MainActivity mainActivity;
public ListenerImp(MainActivity mainActivity)
{
this.mainActivity = mainActivity;
}
public void OnSuccess(Java.Lang.Object IsEnvReadyResult)
{
// Obtain the execution result.
Intent intent = new Intent(mainActivity, typeof(ComapnyActivity));
mainActivity.StartActivity(intent);
}
public void OnFailure(Java.Lang.Exception e)
{
Toast.MakeText(Android.App.Application.Context, "Feature Not available for your country", ToastLength.Short).Show();
if (e.GetType() == typeof(IapApiException))
{
IapApiException apiException = (IapApiException)e;
if (apiException.Status.StatusCode == OrderStatusCode.OrderHwidNotLogin)
{
// Not logged in.
//Call StartResolutionForResult to bring up the login page
}
else if (apiException.Status.StatusCode == OrderStatusCode.OrderAccountAreaNotSupported)
{
// The current region does not support HUAWEI IAP.
}
}
}
}
}
CompanyActivity.cs
This activity performs all the operation In-App purchasing and display list of company with package details.
Java:
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Support.V7.Widget;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Hmf.Tasks;
using Com.Huawei.Hms.Iap;
using Com.Huawei.Hms.Iap.Entity;
using Org.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PlacementApp
{
[Activity(Label = "ComapnyActivity", Theme = "@style/AppTheme")]
public class ComapnyActivity : AppCompatActivity, BuyProduct
{
private static String TAG = "ComapnyActivity";
private RecyclerView recyclerView;
private CompanyAdapter adapter;
IList<ProductInfo> productList;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_company);
recyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerview);
recyclerView.SetLayoutManager(new LinearLayoutManager(this));
recyclerView.SetItemAnimator(new DefaultItemAnimator());
//ADAPTER
adapter = new CompanyAdapter(this);
adapter.SetData(productList);
recyclerView.SetAdapter(adapter);
GetProducts();
}
private void GetProducts()
{
List<String> productIdList = new List<String>();
productIdList.Add("Nokia");
productIdList.Add("Hyperlink");
productIdList.Add("Tata");
productIdList.Add("Infosys");
productIdList.Add("Wipro");
ProductInfoReq req = new ProductInfoReq();
// PriceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
req.PriceType = 0;
req.ProductIds = productIdList;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).ObtainProductInfo(req);
task.AddOnSuccessListener(new QueryProductListenerImp(this)).AddOnFailureListener(new QueryProductListenerImp(this));
}
class QueryProductListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private ComapnyActivity activity;
public QueryProductListenerImp(ComapnyActivity activity)
{
this.activity = activity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the result
ProductInfoResult productlistwrapper = (ProductInfoResult)result;
IList<ProductInfo> productList = productlistwrapper.ProductInfoList;
activity.adapter.SetData(productList);
activity.adapter.NotifyDataSetChanged();
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
}
}
public void OnBuyProduct(ProductInfo pInfo)
{
//Toast.MakeText(Android.App.Application.Context, pInfo.ProductName, ToastLength.Short).Show();
CreatePurchaseRequest(pInfo);
}
private void CreatePurchaseRequest(ProductInfo pInfo)
{
// Constructs a PurchaseIntentReq object.
PurchaseIntentReq req = new PurchaseIntentReq();
// The product ID is the same as that set by a developer when configuring product information in AppGallery Connect.
// PriceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
req.PriceType = pInfo.PriceType;
req.ProductId = pInfo.ProductId;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).CreatePurchaseIntent(req);
task.AddOnSuccessListener(new BuyListenerImp(this)).AddOnFailureListener(new BuyListenerImp(this));
}
protected override void OnActivityResult(int requestCode, Android.App.Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 6666)
{
if (data == null)
{
Log.Error(TAG, "data is null");
return;
}
//"this" in the code is a reference to the current activity
PurchaseResultInfo purchaseIntentResult = Iap.GetIapClient(this).ParsePurchaseResultInfoFromIntent(data);
switch (purchaseIntentResult.ReturnCode)
{
case OrderStatusCode.OrderStateCancel:
// User cancel payment.
Toast.MakeText(Android.App.Application.Context, "Payment Cancelled", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateFailed:
Toast.MakeText(Android.App.Application.Context, "Order Failed", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderProductOwned:
// check if there exists undelivered products.
Toast.MakeText(Android.App.Application.Context, "Undelivered Products", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateSuccess:
// pay success.
Toast.MakeText(Android.App.Application.Context, "Payment Success", ToastLength.Short).Show();
// use the public key of your app to verify the signature.
// If ok, you can deliver your products.
// If the user purchased a consumable product, call the ConsumeOwnedPurchase API to consume it after successfully delivering the product.
String inAppPurchaseDataStr = purchaseIntentResult.InAppPurchaseData;
MakeProductReconsumeable(inAppPurchaseDataStr);
break;
default:
break;
}
return;
}
}
private void MakeProductReconsumeable(String InAppPurchaseDataStr)
{
String purchaseToken = null;
try
{
InAppPurchaseData InAppPurchaseDataBean = new InAppPurchaseData(InAppPurchaseDataStr);
if (InAppPurchaseDataBean.PurchaseStatus != InAppPurchaseData.PurchaseState.Purchased)
{
return;
}
purchaseToken = InAppPurchaseDataBean.PurchaseToken;
}
catch (JSONException e) { }
ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
req.PurchaseToken = purchaseToken;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).ConsumeOwnedPurchase(req);
task.AddOnSuccessListener(new ConsumListenerImp()).AddOnFailureListener(new ConsumListenerImp());
}
class ConsumListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the result
Log.Info(TAG, "Product available for purchase");
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Log.Info(TAG, "Product available for purchase API Failed");
}
}
class BuyListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private ComapnyActivity activity;
public BuyListenerImp(ComapnyActivity activity)
{
this.activity = activity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the payment result.
PurchaseIntentResult InResult = (PurchaseIntentResult)result;
if (InResult.Status != null)
{
// 6666 is an int constant defined by the developer.
InResult.Status.StartResolutionForResult(activity, 6666);
}
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Toast.MakeText(Android.App.Application.Context, "Purchase Request Failed !", ToastLength.Short).Show();
}
}
public void OnRegister(int position)
{
//Toast.MakeText(Android.App.Application.Context, "Position is :" + position, ToastLength.Short).Show();
Intent intent = new Intent(this, typeof(RegistrationActivity));
ProductInfo pInfo = productList[position];
intent.PutExtra("price_type", pInfo.PriceType);
intent.PutExtra("product_id", pInfo.ProductId);
intent.PutExtra("price", pInfo.Price);
StartActivity(intent);
}
}
}
RegistrationActivity.cs
This activity performs register student data then redirect to the payment screen through In-App purchasing.
Java:
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.App;
using Android.Util;
using Android.Views;
using Android.Widget;
using Com.Huawei.Hmf.Tasks;
using Com.Huawei.Hms.Iap;
using Com.Huawei.Hms.Iap.Entity;
using Org.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PlacementApp
{
[Activity(Label = "Registration", Theme = "@style/AppTheme")]
class RegistrationActivity : AppCompatActivity
{
private int priceType;
private String productId, price;
private EditText stdName, stdEmail, phoneNo, place;
private TextView regFee;
private Button btnRegister;
private static String TAG = "RegistrationActivity";
private Spinner spinner, spinnerGender;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_registration);
productId = Intent.GetStringExtra("product_id");
priceType = Intent.GetIntExtra("price_type", 0);
price = Intent.GetStringExtra("price");
stdName = FindViewById<EditText>(Resource.Id.name);
stdEmail = FindViewById<EditText>(Resource.Id.email);
phoneNo = FindViewById<EditText>(Resource.Id.phone);
place = FindViewById<EditText>(Resource.Id.place);
regFee = FindViewById<TextView>(Resource.Id.reg_fee);
btnRegister = FindViewById<Button>(Resource.Id.register);
spinner = FindViewById<Spinner>(Resource.Id.branch);
spinner.ItemSelected += SpinnerItemSelected;
spinnerGender = FindViewById<Spinner>(Resource.Id.year);
ArrayAdapter yearAdapter = ArrayAdapter.CreateFromResource(this, Resource.Array.year_array, Android.Resource.Layout.SimpleSpinnerItem);
yearAdapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
spinnerGender.Adapter = yearAdapter;
ArrayAdapter adapter = ArrayAdapter.CreateFromResource(this, Resource.Array.branch_array, Android.Resource.Layout.SimpleSpinnerItem);
adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
spinner.Adapter = adapter;
stdName.Text = MainActivity.name;
stdEmail.Text = MainActivity.email;
regFee.Text = "Breakfast Fee : " + price;
btnRegister.Click += delegate
{
CreateRegisterRequest();
};
}
private void SpinnerItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
if (e.Position != 0)
{
Spinner spinner = (Spinner)sender;
string name = spinner.GetItemAtPosition(e.Position).ToString();
Toast.MakeText(Android.App.Application.Context, name, ToastLength.Short).Show();
}
}
private void CreateRegisterRequest()
{
// Constructs a PurchaseIntentReq object.
PurchaseIntentReq req = new PurchaseIntentReq();
// The product ID is the same as that set by a developer when configuring product information in AppGallery Connect.
// PriceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
req.PriceType = priceType;
req.ProductId = productId;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).CreatePurchaseIntent(req);
task.AddOnSuccessListener(new BuyListenerImp(this)).AddOnFailureListener(new BuyListenerImp(this));
}
class BuyListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private RegistrationActivity regActivity;
public BuyListenerImp(RegistrationActivity regActivity)
{
this.regActivity = regActivity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the payment result.
PurchaseIntentResult InResult = (PurchaseIntentResult)result;
if (InResult.Status != null)
{
// 6666 is an int constant defined by the developer.
InResult.Status.StartResolutionForResult(regActivity, 6666);
}
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Toast.MakeText(Android.App.Application.Context, "Purchase Request Failed !", ToastLength.Short).Show();
}
}
protected override void OnActivityResult(int requestCode, Android.App.Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 6666)
{
if (data == null)
{
Log.Error(TAG, "data is null");
return;
}
//"this" in the code is a reference to the current activity
PurchaseResultInfo purchaseIntentResult = Iap.GetIapClient(this).ParsePurchaseResultInfoFromIntent(data);
switch (purchaseIntentResult.ReturnCode)
{
case OrderStatusCode.OrderStateCancel:
// User cancel payment.
Toast.MakeText(Android.App.Application.Context, "Registration Cancelled", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateFailed:
Toast.MakeText(Android.App.Application.Context, "Registration Failed", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderProductOwned:
// check if there exists undelivered products.
Toast.MakeText(Android.App.Application.Context, "Undelivered Products", ToastLength.Short).Show();
break;
case OrderStatusCode.OrderStateSuccess:
// pay success.
Toast.MakeText(Android.App.Application.Context, "Registration Success", ToastLength.Short).Show();
// use the public key of your app to verify the signature.
// If ok, you can deliver your products.
// If the user purchased a consumable product, call the ConsumeOwnedPurchase API to consume it after successfully delivering the product.
String inAppPurchaseDataStr = purchaseIntentResult.InAppPurchaseData;
MakeProductReconsumeable(inAppPurchaseDataStr);
break;
default:
break;
}
return;
}
}
private void MakeProductReconsumeable(String InAppPurchaseDataStr)
{
String purchaseToken = null;
try
{
InAppPurchaseData InAppPurchaseDataBean = new InAppPurchaseData(InAppPurchaseDataStr);
if (InAppPurchaseDataBean.PurchaseStatus != InAppPurchaseData.PurchaseState.Purchased)
{
return;
}
purchaseToken = InAppPurchaseDataBean.PurchaseToken;
}
catch (JSONException e) { }
ConsumeOwnedPurchaseReq req = new ConsumeOwnedPurchaseReq();
req.PurchaseToken = purchaseToken;
//"this" in the code is a reference to the current activity
Task task = Iap.GetIapClient(this).ConsumeOwnedPurchase(req);
task.AddOnSuccessListener(new ConsumListenerImp(this)).AddOnFailureListener(new ConsumListenerImp(this));
}
class ConsumListenerImp : Java.Lang.Object, IOnSuccessListener, IOnFailureListener
{
private RegistrationActivity registrationActivity;
public ConsumListenerImp(RegistrationActivity registrationActivity)
{
this.registrationActivity = registrationActivity;
}
public void OnSuccess(Java.Lang.Object result)
{
// Obtain the result
Log.Info(TAG, "Product available for purchase");
registrationActivity.Finish();
}
public void OnFailure(Java.Lang.Exception e)
{
//get the status code and handle the error
Log.Info(TAG, "Product available for purchase API Failed");
}
}
}
}
Xamarin App Build Result
1. Navigate to Solution Explore > Project > Right Click > Archive/View Archive to generate SHA-256 for build release and Click on Distribute.
2. Choose Distribution Channel > Ad Hoc to sign apk.
3. Choose Demo Keystore to release apk.
4. Build succeed and Save apk file.
5. Final result.
Analytics Report
1. Navigate to Huawei Analytics > Overview > Real-time Overview.
2. Navigate to Huawei Analytics > Overview > Real-time Overview, then check Event analysis.
3. Navigate to App debugging, then track your events.
Tips and Tricks
1. It is recommended that the app obtains the public payment key from your server in real-time. Do not store it on the app to prevent app version incompatibility caused by the subsequent key upgrade.
2. The sandbox testing function can be used only when the following conditions are met: A sandbox testing account is successfully added, and the value of versionCode of the test package is greater than that of the released package. In the HMS Core IAP SDK 4.0.2, the isSandboxActivated API is added to check whether the current sandbox testing environment is available. If not, the API returns the reason why the environment is unavailable.
3. On mobile phones whose value of targetSdkVersion is 28 or later, ad video assets may fail to be downloaded. In this case, you need to configure the app to allow HTTP network requests. For details, please refer to Configuring Network Permissions.
4. Xamarin requires the ADB daemon to be started over port 5037. If the ADB daemon runs on a different port, Visual Studio will not be able to detect your device.
Conclusion
In this article, we have learned how to integrate HMS In-App Purchase, Ads, Analytics and Account Kit in Xamarin based Android application. Student can easily apply in a listed company which offers campus placement.
Be sure to like and comments on this article, if you found it helpful. It means a lot to me.
References
1. Banner Ads Integration Procedure
2. Reward Ads Integration Procedure
3. Interstitial Ads Integration Procedure
4. Initializing Analytics Kit Procedure
Original Source