As a busy developer, I can hardly spare the time to go to the gym, but I know that I should. Then I came across the videos of Pamela Reif, a popular fitness blogger, which gave me the idea of working out from home. I followed a home workout regimen, but found it hard to track my training load systematically, such through heart rate and calories burned. And that's exactly how my app, Fitness Manager came into being. I developed this app by harnessing the extended capabilities in HMS Core Health Kit. Next, I'll show you how you can do the same!
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"
}
About Health KitHealth Kit offers both basic and extended capabilities to be integrated. Its basic capabilities allow your app to add, delete, modify, and query user fitness and health data upon obtaining the user's authorization, so that you can provide a rich array of fitness and health services. Its extended capabilities open a greater range of real-time fitness and health data and solutions.
Fitness Manager was solely developed from the extended capabilities in Health Kit.
Development ProcessEnvironment RequirementsAndroid platform:
Android Studio: 3.X or later
JDK 1.8.211 or later
SDK and Gradle:
minSdkVersion 24
targetSdkVersion 29
compileSdkVersion 29
Gradle: 4.6 or later
Test device: You'll need a Huawei phone that runs Android 6.0 or later, and has installed the HUAWEI Health app.
Development ProcedureHere I'll detail the entire process for developing an app using the extended capabilities mentioned above.
Before getting started, register and apply for the HUAWEI ID service, and then apply for the Health Kit service on HUAWEI Developers. You can skip this step if you have already created an app using the kit's basic capabilities. Then, apply for the data read and write scopes you need for your app. If you have any special needs, send an email to [email protected].
Now, integrate the SDK for the extended capabilities to your project in Android Studio. Before building the APK, make sure that you have configured the obfuscation script to prevent the HMS Core SDK from being obfuscated. Once the integration is complete, test your app against the test cases, and submit it for review. After passing the review, your app will obtain the formal scopes, and can be finally released.
Now, I'll show you how to implement some common features in your app using the kit's capabilities.
Starting and Stopping a WorkoutTo control workouts and obtain real-time workout data, call the following APIs in sequence:
registerSportData: Starts obtaining real-time workout data.
startSport: Starts a workout.
stopSport: Stops a workout.
unregisterSportData: Stops obtaining real-time workout data.
Key Code1. Starting obtaining real-time workout data
Call the registerSportData method of the HiHealthDataStore object to start obtaining real-time workout data.
Obtain the workout data through HiSportDataCallback.
Code:
HiHealthDataStore.registerSportData(context, new HiSportDataCallback() {
@Override
public void onResult(int resultCode) {
// API calling result.
Log.i(TAG, "registerSportData onResult resultCode:" + resultCode);
}
@Override
public void onDataChanged(int state, Bundle bundle) {
// Real-time data change callback.
Log.i(TAG, "registerSportData onChange state: " + state);
StringBuffer stringBuffer = new StringBuffer("");
if (state == HiHealthKitConstant.SPORT_STATUS_RUNNING) {
Log.i(TAG, "heart rate : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_HEARTRATE));
Log.i(TAG, "distance : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_DISTANCE));
Log.i(TAG, "duration : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_DURATION));
Log.i(TAG, "calorie : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_CALORIE));
Log.i(TAG, "totalSteps : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_TOTAL_STEPS));
Log.i(TAG, "totalCreep : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_TOTAL_CREEP));
Log.i(TAG, "totalDescent : " + bundle.getInt(HiHealthKitConstant.BUNDLE_KEY_TOTAL_DESCENT));
}
}
});
2. Starting a workout
The following table lists supported workout constants.
Open Data TypeConstantOutdoor walkingHiHealthKitConstant.SPORT_TYPE_WALKOutdoor runningHiHealthKitConstant.SPORT_TYPE_RUNOutdoor cyclingHiHealthKitConstant.SPORT_TYPE_BIKEIndoor runningHiHealthKitConstant.SPORT_TYPE_TREADMILL
Call the startSport method of the HiHealthDataStore object to start a specific type of workout.
Obtain the calling result through ResultCallback.
Code:
// Outdoor running.
int sportType = HiHealthKitConstant.SPORT_TYPE_RUN;
HiHealthDataStore.startSport(context, sportType, new ResultCallback() {
@Override
public void onResult(int resultCode, Object message) {
if (resultCode == HiHealthError.SUCCESS) {
Log.i(TAG, "start sport success");
}
}
});
3. Stopping a workout
Call the stopSport method of the HiHealthDataStore object to stop a specific type of workout.
Obtain the calling result through ResultCallback.
Code:
HiHealthDataStore.stopSport(context, new ResultCallback() {
@Override
public void onResult(int resultCode, Object message) {
if (resultCode == HiHealthError.SUCCESS) {
Log.i(TAG, "stop sport success");
}
}
});
4. Stopping obtaining real-time workout data
Call the unregisterSportData method of the HiHealthDataStore object to stop obtaining the real-time workout data.
Obtain the calling result through HiSportDataCallback.
Code:
HiHealthDataStore.unregisterSportData(context, new HiSportDataCallback() {
@Override
public void onResult(int resultCode) {
// API calling result.
Log.i(TAG, "unregisterSportData onResult resultCode:" + resultCode);
}
@Override
public void onDataChanged(int state, Bundle bundle) {
// The API is not called at the moment.
}
});
Querying Daily ActivitiesYou can allow your users to query their daily activities in your app, such as step count details and statistics, distance, calories burned, and medium- and high-intensity activities. These data comes from Huawei phones or Huawei wearable devices. Before data query, you'll need to apply for the corresponding permissions, and obtain authorization from users. Otherwise, your API calling will fail.
1. Querying daily activity data by calling execQuery
Call the execQuery method of the HiHealthDataStore object to query user's daily activities.
Obtain the query result through ResultCallback.
The following takes querying step statistics as an example:
Code:
int timeout = 0;
// Query the step count of the current day.
Calendar currentDate = Calendar.getInstance();
currentDate.set(Calendar.HOUR_OF_DAY, 0);
currentDate.set(Calendar.MINUTE, 0);
currentDate.set(Calendar.SECOND, 0);
long startTime = currentDate.getTimeInMillis();
long endTime = System.currentTimeMillis();
// Query the step count.
HiHealthDataQuery hiHealthDataQuery = new HiHealthDataQuery(HiHealthPointType.DATA_POINT_STEP_SUM, startTime,
endTime, new HiHealthDataQueryOption());
HiHealthDataStore.execQuery(context, hiHealthDataQuery, timeout, new ResultCallback() {
@Override
public void onResult(int resultCode, Object data) {
Log.i(TAG, "query steps resultCode: " + resultCode);
if (resultCode == HiHealthError.SUCCESS && data instanceof List) {
List dataList = (ArrayList) data;
for (Object obj : dataList) {
HiHealthPointData pointData = (HiHealthPointData) obj;
Log.i(TAG, "start time : " + pointData.getStartTime());
Log.i(TAG, "query steps : " + String.valueOf(pointData.getValue()));
}
}
}
});
Parameters required for query and the query results
Open Data TypeParameter for QueryMethod for Obtaining the ResultResult Value TypeResult DescriptionDaily activitiesStep count statisticsHiHealthPointType.DATA_POINT_STEP_SUMHiHealthPointData.getValue()intStep count (unit: step). For the current day, the value is updated in real time. For each of the previous days, the value is the total step count of that day.Step count detailsHiHealthPointType.DATA_POINT_STEPStep count per minute (unit: step).DistanceHiHealthPointType.DATA_POINT_DISTANCE_SUMDistance (unit: meter). For the current day, the value is updated in real time. For each of the previous days, the value is the total distance of that day.Calories burnedHiHealthPointType.DATA_POINT_CALORIES_SUMCalories burned (unit: kcal). For the current day, the value is updated in real time. For each of the previous days, the value is the total calories burned of that day.Medium- and high-intensity activitiesHiHealthPointType.DATA_POINT_EXERCISE_INTENSITYIntensity (unit: minute). For the current day, the value is updated in real time. For each of the previous days, the value is the total intensity of that day.
Querying Workout RecordsThe following is an example of querying workout records in the last 30 days:
Call the execQuery method of the HiHealthDataStore object to query user's workout records.
Obtain the query result through ResultCallback.
Code:
int timeout = 0;
long endTime = System.currentTimeMillis();
// The time range for the query is the past 30 days.
long startTime = endTime - 1000 * 60 * 60 * 24 * 30L;
// Query the running data.
HiHealthDataQuery hiHealthDataQuery = new HiHealthDataQuery(HiHealthSetType.DATA_SET_RUN_METADATA, startTime,
endTime, new HiHealthDataQueryOption());
HiHealthDataStore.execQuery(context, hiHealthDataQuery, timeout, new ResultCallback() {
@Override
public void onResult(int resultCode, Object data) {
if (resultCode == HiHealthError.SUCCESS && data instanceof List){
List dataList = (List) data;
for (Object obj : dataList) {
HiHealthSetData hiHealthData = (HiHealthSetData) obj;
Map map = hiHealthData.getMap();
Log.i(TAG, "start time : " + hiHealthData.getStartTime());
Log.i(TAG, "total_time : " + map.get(HiHealthKitConstant.BUNDLE_KEY_TOTAL_TIME));
Log.i(TAG, "total_distance : " + map.get(HiHealthKitConstant.BUNDLE_KEY_TOTAL_DISTANCE));
Log.i(TAG, "total_calories : " + map.get(HiHealthKitConstant.BUNDLE_KEY_TOTAL_CALORIES));
Log.i(TAG, "step : " + map.get(HiHealthKitConstant.BUNDLE_KEY_STEP));
Log.i(TAG, "average_pace : " + map.get(HiHealthKitConstant.BUNDLE_KEY_AVERAGEPACE));
Log.i(TAG, "average_speed : " + map.get(HiHealthKitConstant.BUNDLE_KEY_AVERAGE_SPEED));
Log.i(TAG, "average_step_rate : " + map.get(HiHealthKitConstant.BUNDLE_KEY_AVERAGE_STEP_RATE));
Log.i(TAG, "step_distance : " + map.get(HiHealthKitConstant.BUNDLE_KEY_STEP_DISTANCE));
Log.i(TAG, "average_heart_rate : " + map.get(HiHealthKitConstant.BUNDLE_KEY_AVERAGE_HEART_RATE));
Log.i(TAG, "total_altitude : " + map.get(HiHealthKitConstant.BUNDLE_KEY_TOTAL_ALTITUDE));
Log.i(TAG, "total_descent : " + map.get(HiHealthKitConstant.BUNDLE_KEY_TOTALDESCENT));
Log.i(TAG, "data source : " + map.get(HiHealthKitConstant.BUNDLE_KEY_DATA_SOURCE));
}
}
}
});
References
HUAWEI Developers
HUAWEI Health Kit
Related
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=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
{
"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.
{
"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, I would like to tell you about Cloud DB, which online storage that Huawei offers to developers.
What is Cloud DB ?
Although Cloud DB is still in beta version, it is a successful and seamless database structure. In addition to the ease of use, attracts developers with its management and a user-friendly interface. In addition to providing data availability, consistency and security, CloudDB provides seamless data synchronization between the device and the cloud.
If you do not have a server when developing applications, Cloud DB server easily solves our data storage, maintenance and distribution. Also CloudDB is free.
Cloud DB provides 500 GB of data volume for each application, and supports 2000 connections. Looking at their counterparts, it is understood how large and how important these numbers are.
Cloud DB Structure
Object Type: It represents each table in the standard database. In other words, each table containing data and columns is called Object Type.
Cloud DB Zone : Represents the data zone on the cloud side. According to the classic database, Cloud DB Zone is the name of the database or schema name.
Data Entires : It is the area that shows the added data. Here, you can add, update and delete data. When you add data, you will realize that the tables you are used to are in the same way. And while using this technology, it prevents you from suffering from strangeness.
How To Using Cloud DB ?
Now let’s see how to use Cloud DB. Since Cloud DB is still in beta, you have to send a mail to activate this service in your app. To use Cloud DB, you need to create an app after creating your Huawei Developer account. After completing all the necessary steps, you have to request the activation of the service by sending an e-mail to [email protected] with the sample header below. The service will be activated within 1–3 business days after your mail arrives. And you can start using it freely.
Cloud DB –Company Name — Developer ID — App ID
After your service activated, log in to AppGallery Connect and select your app under the heading “My Apps”. You can then access the “Cloud DB” panel under the “Build” tab from the menu on the left side of the page, by moving to the “Develop” tab in the upper left. After the page is loaded, activate the service by clicking the “Enable Now” button in the upper right corner.
A Cloud DB Zone must first be created. After then object types should be created. When creating the object type, column names must be entered and the primary key must be identified in the window that opens. Also, at this step, you can edit access control options for the object type. In the image of the below you can find, which users have which permissions should be given. After creating DB Zone and Object Type, Cloud DB provides to export data models and Helper class as JSON or Java for use them in the app without wasting time. After exporting the model classes as Java, you have to add these classes to the relevant directory of the app and start communicate with Cloud DB.
Cloud DB library should be added to the build.gradle file under the project’s app directory and the compatibility mode of the Java source code should be set as 1.8. For this, the following codes should be added to the gradle file and wait downloading the necessary dependencies by click Sync Now.
Code:
dependencies {
implementation 'com.huawei.agconnect:agconnect-database:1.2.1.301'
}
compileOptions {
targetCompatibility = 1.8
}
Let’s create a class named CloudDBZoneWrapper for all database operations. By defining all the upsert, query operations in this class, call these methods in the activity/fragment to be used. Thanks to this metods you can coding your app without clutter.
Firstly, Cloud DB objects should created to be used in this class.
Code:
private AGConnectCloudDB mCloudDB;
private CloudDBZone mCloudDBZone;
private ListenerHandler mRegister;
private CloudDBZoneConfig mConfig;
After then create an instance from the AGConnectCloudDB object in a constructor method.
Code:
public CloudDBZoneWrapper() {
mCloudDB = AGConnectCloudDB.getInstance();
}
Then, initAGConnectCloudDB method must be created for calling on the app landing page. This method must be run before the app is opened, before starting all DB operations. I follow the code by adding a log in every process step to follow the errors more easily. In this way, you can easily find out which line is wrong.
Code:
public static void initAGConnectCloudDB(Context context) {
AGConnectCloudDB.initialize(context);
Log.w(Constants.DB_ZONE_WRAPPER, "initAGConnectCloudDB" );
}
Next, creating Object Type, and open/close DBZone operations should be coding. These methods will be used before upsert and query operations for open DBZone and create Object Types.
Code:
public void createObjectType() {
try {
mCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo());
Log.w(Constants.DB_ZONE_WRAPPER, "createObjectTypeSuccess " );
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "createObjectTypeError: " + e.getMessage());
}
}
public void openCloudDBZone() {
mConfig = new CloudDBZoneConfig("DB ZONE NAME HERE",
CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC);
mConfig.setPersistenceEnabled(true);
Log.w(Constants.DB_ZONE_WRAPPER, "openCloudDBZoneSuccess " );
try {
mCloudDBZone = mCloudDB.openCloudDBZone(mConfig, true);
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "openCloudDBZoneError: " + e.getMessage());
}
}
public void closeCloudDBZone() {
try {
mCloudDB.closeCloudDBZone(mCloudDBZone);
Log.w(Constants.DB_ZONE_WRAPPER, "closeCloudDBZoneSuccess " );
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "closeCloudDBZoneError: " + e.getMessage());
}
}
Now, the necessary methods for upsert and query operations should be written. But first, a few callbacks have to be added to get the results of these actions to the activities and fragments where the actions are operated. In this way, all DB operations will be gathered in a single class, without the activity being too tired and without crowd of code.
Code:
public interface UiCallBack {
void onAddOrQuery(List<TableUser> userList);
void isLastID(int lastID);.
void isDataUpsert(Boolean state);
}
public void addCallBacks(UiCallBack uiCallBack) {
mUiCallBack = uiCallBack;
}
Now the necessary method for upsert operation should be written. Upsert contains both insert and update operations. If upsert with a certain ID, the data in the related line will update. If it is upsert by new ID, a new line will added. So, both insert and update are carried out with the same method.
First, it should be checked whether DBZone is created or not. If DBZone has an error, will not upsert data. Then, upsert with CloudDBZoneTask object. Since I will add data to the User table, I gave the user object as a parameter to this method. If you need to add data to other tables, you should create a new method and give the object of the related table as a parameter. When upsert operation complated , if upsert is successful, it must return true, if occured an error, return false. For this, at the beginning of the method, a variable named Boolean state was defined and its first value was set as false. Then, if upsert is successful, state is set to true, and if error occurs, method will return false.
Code:
public void insertUser(TableUser user) {
boolean state = false;
if (mCloudDBZone == null) {
Log.w(Constants.DB_ZONE_WRAPPER, "INSERT USER : CloudDBZone is null, try re-open it");
return;
}
CloudDBZoneTask<Integer> upsertTask = mCloudDBZone.executeUpsert(user);
if (mUiCallBack == null) {
return;
}
upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer cloudDBZoneResult) {
state = true;
Log.w(Constants.DB_ZONE_WRAPPER, "INSERT USER : upsert " + cloudDBZoneResult + " records");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
state = false;
mUiCallBack.updateUiOnError("INSERT USER : Insert user info failed");
}
});
if (mUiCallBack != null) {
mUiCallBack.isDataUpsert(state);
}
}
Now, let’s make a query in the this class. For this, I will get the list of users which I added to the database with the same model class. Two methods are required when making query request. The first is the getAllUsers method for DB operations, and the other is called userListResult method, for add data to array. Firstly, CloudDBZone control should be done in getAllUsers method. Then, query request will make by creating a task. If the request is successful, the userListResult method is calling with the user object. If the request is successful, the userListResult method will called with the user object. An arrayList is created in the userListResult method, and all results are thrown into this list. Then, by adding a callback, the results can be called up in the activity or fragment.
Code:
public void getAllUsers() {
if (mCloudDBZone == null) {
Log.w(Constants.DB_ZONE_WRAPPER, "GET USER DETAIL : CloudDBZone is null, try re-open it");
return;
}
CloudDBZoneTask<CloudDBZoneSnapshot<TableUser>> queryTask = mCloudDBZone.executeQuery(
CloudDBZoneQuery.where(TableUser.class),
CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY);
queryTask.addOnSuccessListener(new OnSuccessListener<CloudDBZoneSnapshot<TableUser>>() {
@Override
public void onSuccess(CloudDBZoneSnapshot<TableUser> snapshot) {
userListResult (snapshot);
Log.w(Constants.DB_ZONE_WRAPPER, "GET USER DETAIL : GoResults: ");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (mUiCallBack != null) {
mUiCallBack.updateUiOnError("GET USER DETAIL : Query user list from cloud failed");
}
}
});
}
private void userListResult (CloudDBZoneSnapshot<TableUser> snapshot) {
CloudDBZoneObjectList<TableUser> userInfoCursor = snapshot.getSnapshotObjects();
List<TableUser> userInfoList = new ArrayList<>();
try {
while (userInfoCursor.hasNext()) {
TableUser userInfo = userInfoCursor.next();
userInfoList.add(userInfo);
Log.w(Constants.DB_ZONE_WRAPPER, "USER DETAIL RESULT : processQueryResult: " + userInfo.getUser_city());
}
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "USER DETAIL RESULT : processQueryResult: " + e.getMessage());
}
snapshot.release();
if (mUiCallBack != null) {
mUiCallBack.onAddOrQuery(userInfoList);
}
}
Thus, all database operations within the CloudDBZoneWrapper class have been completed. Now let’s examine how to data upsert or query in activity or fragment.
The UiCallBack method in the CloudDBZoneWrapper class should be implement as the CloudDBZoneWrapper.UiCallBack in the class you which want to do database operations. In this way, all added call back methods will override in this class. Then the CloduDBZoneWrapper object and a new Handler need to be created in the activity. The CloudDBZoneWrapper object must be called within the constructor method. Sample codes are as follows.
Code:
private MyHandler mHandler = new MyHandler();
private CloudDBZoneWrapper mCloudDBZoneWrapper;
private static final class MyHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
// dummy
}
}
public ProfileFragment() {
mCloudDBZoneWrapper = new CloudDBZoneWrapper();
}
Then the object type must be created in onCreate and Cloud DB Zone must be opened. If query is not related to an event, if the data should be loaded while the page is opening, call the getAllUsers method after creating the object type and opening the DB Zone in onCreate.
Code:
mHandler.post(() -> {
mCloudDBZoneWrapper.addCallBacks(ProfileFragment.this);
mCloudDBZoneWrapper.createObjectType();
mCloudDBZoneWrapper.openCloudDBZone();
mCloudDBZoneWrapper.getAllUsers();
});
The callback method, which was added to the getAllUsers method in the CloudDBZoneWrapper class, was override in the fragment. In this override method, all data can be used. For example, if wants to access the information of a user with ID = 3 in the user list, the turned list by insert a for loop, user information with ID = 3 is obtained.
Code:
@Override
public void onAddOrQuery(List<TableUser> userList) {
for(int i = 0; i <= userList.size()-1; i++){
if(userList.get(i).getId().equals(“3”)){
userName = userList.get(i).getUser_name());
userPhone = userList.get(i).getUser_phone();
userMail = userList.get(i).getUser_mail();
userAge = userList.get(i).getUser_age();
userGender = userList.get(i).getUser_gender();
}
}
}
Now let’s make an upsert. As I writed before, Upsert includes update and insert operations. Both operations are uses the same method. If you want to update a row of data, you must post with the ID information of the data in that row. If a new data is to be added, it should be posted with a new ID.
At this point, Cloud DB has a lack. Unfortunately, the auto increment don’t have when creating the object type. In other words, ID value does not increase automatically when data is added. It has to be given manually. I solved this problem by getting the last ID in the table and increasing it.
Now, create a method called updateUser to update and send back the user information I have previously got in this fragment. Next, a new user object should created here and the values should set. If there is an not to be changed data (as in the example, age and gender ), old values must be set in them. Finally, make post request by calling the insertUser method created in the CloudDBZoneWrapper class.
Code:
public void updateProfile(){
TableUser user = new TableUser();
user.setUser_id(“3”);
user.setUser_name(“Yeni İsim”));
user.setUser_phone(“Yeni Telefon”);
user.setUser_mail(“Yeni Mail”);
user.setUser_age(userAge);
user.setUser_gender(userGender);
mHandler.post(() -> {
mCloudDBZoneWrapper.insertUser(user);
});
}
In the fragment, help from call back methods should be obtained to find out whether the transaction is successful. Status check can be done in call back method added to insertUser method.
Code:
@Override
public void isDataUpsert(Boolean state) {
if(state){
//successful
}else{
//unsuccessful
}
}
Finally, it is worth mentioning that there is an authentication requirement to upsert. Since Cloud DB is still in beta, absolutely has some errors. But as you can see, all of them are solved easily. For authentication, Auth Service offered by Huawei to developers should be used. A service that is very easy to use. You can find the Auth Service link on the below. After the authentication, your upsert will work. If authentication is not done, the result of upsert will return false.
"https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-auth-service-introduction"
Well explained, can we store large amount of data into cloud Db, is there any limitations.
sujith.e said:
Well explained, can we store large amount of data into cloud Db, is there any limitations.
Click to expand...
Click to collapse
Yes, Cloud DB has a limit but I think you can store large data in Cloud DB. Because Cloud DB provides 500 GB of data volume for each application, and supports 2000 connections.
Push messaging, with the proliferation of mobile Internet, has become a very effective way for mobile apps to achieve business success. It improves user engagement and stickiness by allowing developers to send messages to a wide range of users in a wide range of scenarios: taking the subway or bus, having a meal in a restaurant, having a chat... you name it. No matter what the scenario is, a push message is always a great helper for you to directly "talk" to your users, and for your users to know something informative.
Such great benefits brought by push messages, however, can be dampened by a challenge: the variety of mobile phone manufacturers. This is because usually each manufacturer has their own push messaging channels, which increases the difficulty for uniformly sending your app's push messages to mobile phones of different manufacturers. Of course there is an easy solution for this: sending your push messages to mobile phones of only one manufacturer, but this can limit your user base and prevent you from obtaining your desired messaging effects.
Then this well explains why we developers usually need to find a solution for our apps to be able to push their messages to devices of different brands.
I don't know about you, but the solution I found for my app is HMS Core Push Kit. Going on, I will demonstrate how I have integrated this kit and used its ability to aggregate third-party push messaging channels to implement push messaging on mobile phones made by different manufacturers, expecting greater user engagement and stickiness. Let's move on to the implementation.
PreparationsBefore integrating the SDK, make the following preparations:
1. Sign in to the push messaging platform of a specific manufacturer, create a project and app on the platform, and save the JSON key file of the project. (The requirements may vary depending on the manufacturer, so refer to the specific manufacturer's documentation to learn about their requirements.)
2. Configure app information in AppGallery Connet, but use the following build dependency instead when configuring the build dependencies:
Code:
dependencies {
implementation 'com.huawei.hms:push-fcm:6.3.0.304'
}
3. On the platform mentioned in the previous step, click My projects, find the app in the project, and go to Grow > Push Kit > Settings. On the page displayed, click Enable next to Configure other Android-based push, and then copy the key in the saved JSON key file and paste it in the Authentication parameters text box.
{
"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"
}
Development ProcedureNow, let's go through the development procedure.
1. Disable the automatic initialization of the SDK.
To do so, open the AndroidManifest.xml file, and add the <meta-data> element to the <application> element. Note that in the element, the name parameter has a fixed value of push_kit_auto_init_enabled. As for the value parameter, you can set it to false, indicating that the automatic initialization is disabled.
Code:
<manifest ...>
...
<application ...>
<meta-data
android:name="push_kit_auto_init_enabled"
android:value="false"/>
...
</application>
...
</manifest>
2. Initialize the push capability in either of the following ways:
Set value corresponding to push_kit_proxy_init_enabled in the <meta-data> element to true.
Code:
<application>
<meta-data
android:name="push_kit_proxy_init_enabled"
android:value="true" />
</application>
Explicitly call FcmPushProxy.init in the onCreate method of the Application class.
3. Call the getToken method to apply for a token.
Code:
private void getToken() {
// Create a thread.
new Thread() {
@Override
public void run() {
try {
// Obtain the app ID from the agconnect-services.json file.
String appId = "your APP_ID";
// Set tokenScope to HCM.
String tokenScope = "HCM";
String token = HmsInstanceId.getInstance(MainActivity.this).getToken(appId, tokenScope);
Log.i(TAG, "get token: " + token);
// Check whether the token is empty.
if(!TextUtils.isEmpty(token)) {
sendRegTokenToServer(token);
}
} catch (ApiException e) {
Log.e(TAG, "get token failed, " + e);
}
}
}.start();
}
private void sendRegTokenToServer(String token) {
Log.i(TAG, "sending token to server. token:" + token);
}
4. Override the onNewToken method.
After the SDK is integrated and initialized, the getToken method will not return a token. Instead, you'll need to obtain a token by using the onNewToken method.
Code:
@Override
public void onNewToken(String token, Bundle bundle) {
Log.i(TAG, "onSubjectToken called, token:" + token );
}
5. Override the onTokenError method.
This method will be called if the token fails to be obtained.
Code:
@Override
public void onTokenError(Exception e, Bundle bundle) {
int errCode = ((BaseException) e).getErrorCode();
String errInfo = e.getMessage();
Log.i(TAG, "onTokenError called, errCode:" + errCode + ",errInfo=" + errInfo );
}
6. Override the onMessageReceived method to receive data messages.
Code:
@Override
public void onMessageReceived(RemoteMessage message) {
Log.i(TAG, "onMessageReceived is called");
// Check whether the message is empty.
if (message == null) {
Log.e(TAG, "Received message entity is null!");
return;
}
// Obtain the message content.
Log.i(TAG, "get Data: " + message.getData()
+ "\n getFrom: " + message.getFrom()
+ "\n getTo: " + message.getTo()
+ "\n getMessageId: " + message.getMessageId()
+ "\n getSentTime: " + message.getSentTime()
+ "\n getDataMap: " + message.getDataOfMap()
+ "\n getMessageType: " + message.getMessageType()
+ "\n getTtl: " + message.getTtl()
+ "\n getToken: " + message.getToken());
Boolean judgeWhetherIn10s = false;
// Create a job to process a message if the message is not processed within 10 seconds.
if (judgeWhetherIn10s) {
startWorkManagerJob(message);
} else {
// Process the message within 10 seconds.
processWithin10s(message);
}
}
private void startWorkManagerJob(RemoteMessage message) {
Log.d(TAG, "Start new job processing.");
}
private void processWithin10s(RemoteMessage message) {
Log.d(TAG, "Processing now.");
}
7. Send downlink messages.
Currently, you can only use REST APIs on the server to send downlink messages through a third-party manufacturer's push messaging channel.
The following is the URL for calling the API using HTTPS POST:
Code:
POST https://push-api.cloud.huawei.com/v1/[appId]/messages:send
The request header looks like the following:
Code:
Content-Type: application/json; charset=UTF-8
Authorization: Bearer CF3Xl2XV6jMKZgqYSZFws9IPlgDvxqOfFSmrlmtkTRupbU2VklvhX9kC9JCnKVSDX2VrDgAPuzvNm3WccUIaDg==
An example of the notification message body is as follows:
Code:
{
"validate_only": false,
"message": {
"android": {
"notification": {
"title": "test title",
"body": "test body",
"click_action": {
"type": 3
}
}
},
"token": ["pushtoken1"]
}
}
And just like that, my app has got the ability to send its push messages to mobile phones of different manufacturers — without any other configurations. Easy-peasy, right?
ConclusionToday's highly developed mobile Internet has made push messaging an important and effective way for mobile apps to improve user engagement and stickiness. A great obstacle for push messaging to effectively play its role is the highly diversified mobile phone market that is inundated with various manufacturers.
In this article, I demonstrated my solution to aggregate the push channels of different manufacturers, which allowed my app to push messages in a unified way to devices made by those manufacturers. As proven, the whole implementation process is both straightforward and cost-effective, delivering a better messaging effect of push messages by ensuring that they can reach a bigger user base supported by various manufacturers.