More articles like this, you can visit HUAWEI Developer Forum.
Introduction:
Dynamic Ability is a service in which HUAWEI AppGallery implements dynamic loading based on the Android App Bundle technology.
Apps integrated with the Dynamic Ability SDK can dynamically download features or language packages from HUAWEI AppGallery as required, reducing the unnecessary consumption of network traffic and device storage space.
What is Android App Bundle?
It is a new upload format that includes all your app’s compiled code and resources, but defers APK generation and signing to AppGallery. Traditionally, Android apps are distributed using a special file called an Android Package (.apk).
Advantage of using App Bundle format:
· Dynamic Ability Feature: AppGallery’ s new app serving model, called Dynamic Ability, uses your app bundle to generate and serve optimized APKs for each user’s device configuration, so they download only the code and resources they need to run your app. For example, you don’t need other languages strings if you have set English as your default language.
· No need to manually manage multiple APKs: You no longer have to build, sign, and manage multiple APKs to support different devices, and users get smaller, more optimized downloads. For example, now you don’t have to create multiple APKs for devices with different screen resolutions.
· Dynamic Feature Module: These modules contain features and assets that you can decide not to include when users first download and install your app. using the Dynamic Ability SDK, your app can later request to download those modules as dynamic feature APKs. For example, video calling feature and camera filters can be downloaded later on demand.
· Reduced APK size: Using Split APK mechanism, AppGallery can break up a large app into smaller, discrete packages that are installed on a user’s device as required. On average, apps published with app bundles are 20% smaller in size.
Let’s start Development process:
We need to follow the below step in order to achieve Dynamic Ability.
1. Create an App on AppGallery.
2. Create an Android Studio Project.
3. Integrate Dynamic Ability SDK.
4. Launch the app.
Create an App on AppGallery:
We need to create an app:
{
"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"
}
App package manually:
Note: Download json file and add into your project.
Create an Android Studio Project:
Select project templet:
Create app name, package name, and project location:
Now we need to add Dynamic Ability module in our project:
Create module name and package name:
Create module download options:
Sync project and add the following plugin in module’s Gradle file:
apply plugin: 'com.android.dynamic-feature'
Let’s see module’s manifest.xml file:
Code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.huawei.android.dynamicfeaturesplit.splitsamplefeature01">
<dist:module
dist:onDemand="true"
dist:title="@string/title_splitsamplefeature01">
<dist:fusing dist:include="true" />
</dist:module>
<application>
<activity android:name=".FeatureActivity"></activity>
</application>
</manifest>
<dist:module>: This new XML element defines attributes that determine how the module is packaged and distributed as APKs.
distnDemand="true|false": Specifies whether the module should be available as an on demand download.
dist:title="@string/feature_name": Specifies a user-facing title for the module.
<dist:fusing include="true|false" />: Specifies whether to include the module in multi-APKs that target devices running Android 4.4 (API level 20) and lower.
Integrate Dynamic Ability SDK:
1. Open the build.gradle file (usually in the root directory) of your project. Go to allprojects > repositories and configure the Maven repository address for the SDK.
Code:
allprojects{
repositories{
maven {url 'http://developer.huawei.com/repo/'}
...
}
}
2. Add the following code to the build.gradle file (usually app/build.gradle) in the app directory to integrate the Dynamic Ability SDK:
Code:
dependencies {
implementation 'com.huawei.hms:dynamicability:1.0.11.302'
...
}
· Let’s sync the project and start app implementation:
We have created following project structure.
·Let’s see the implementation of Application class:
Code:
import android.app.Application;
import android.content.Context;
import android.util.Log;
import com.huawei.hms.feature.dynamicinstall.FeatureCompat;
public class DynamicFeatureSampleApplication extends Application {
public static final String TAG = DynamicFeatureSampleApplication.class.getSimpleName();
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
FeatureCompat.install(base);
} catch (Exception e) {
Log.w(TAG, "", e);
}
}
}
Configure your app in your Android project, override the attachBaseContext() method in the project, and call FeatureCompat.install to initialize the Dynamic Ability SDK.
· Let’s the implementation of Activity:
Code:
package com.huawei.android.dynamicfeaturesplit;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.huawei.hms.feature.install.FeatureInstallManager;
import com.huawei.hms.feature.install.FeatureInstallManagerFactory;
import com.huawei.hms.feature.listener.InstallStateListener;
import com.huawei.hms.feature.model.FeatureInstallException;
import com.huawei.hms.feature.model.FeatureInstallRequest;
import com.huawei.hms.feature.model.FeatureInstallSessionStatus;
import com.huawei.hms.feature.model.InstallState;
import com.huawei.hms.feature.tasks.FeatureTask;
import com.huawei.hms.feature.tasks.listener.OnFeatureCompleteListener;
import com.huawei.hms.feature.tasks.listener.OnFeatureFailureListener;
import com.huawei.hms.feature.tasks.listener.OnFeatureSuccessListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
public class SampleEntry extends Activity {
private static final String TAG = SampleEntry.class.getSimpleName();
private ProgressBar progressBar;
private FeatureInstallManager mFeatureInstallManager;
private int sessionId = 10086;
private InstallStateListener mStateUpdateListener = new InstallStateListener() {
@Override
public void onStateUpdate(InstallState state) {
Log.d(TAG, "install session state " + state);
if (state.status() == FeatureInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
try {
mFeatureInstallManager.triggerUserConfirm(state, SampleEntry.this, 1);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
return;
}
if (state.status() == FeatureInstallSessionStatus.REQUIRES_PERSON_AGREEMENT) {
try {
mFeatureInstallManager.triggerUserConfirm(state, SampleEntry.this, 1);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
return;
}
if (state.status() == FeatureInstallSessionStatus.INSTALLED) {
Log.i(TAG, "installed success ,can use new feature");
makeToast("installed success , can test new feature ");
return;
}
if (state.status() == FeatureInstallSessionStatus.UNKNOWN) {
Log.e(TAG, "installed in unknown status");
makeToast("installed in unknown status ");
return;
}
if (state.status() == FeatureInstallSessionStatus.DOWNLOADING) {
long process = state.bytesDownloaded() * 100 / state.totalBytesToDownload();
Log.d(TAG, "downloading percentage: " + process);
makeToast("downloading percentage: " + process);
return;
}
if (state.status() == FeatureInstallSessionStatus.FAILED) {
Log.e(TAG, "installed failed, errorcode : " + state.errorCode());
makeToast("installed failed, errorcode : " + state.errorCode());
return;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progress_bar);
mFeatureInstallManager = FeatureInstallManagerFactory.create(this);
}
@Override
protected void onResume() {
super.onResume();
if (mFeatureInstallManager != null) {
mFeatureInstallManager.registerInstallListener(mStateUpdateListener);
}
}
@Override
protected void onPause() {
super.onPause();
if (mFeatureInstallManager != null) {
mFeatureInstallManager.unregisterInstallListener(mStateUpdateListener);
}
}
/**
* install feature
*
* @param view the view
*/
public void installFeature(View view) {
if (mFeatureInstallManager == null) {
return;
}
// start install
FeatureInstallRequest request = FeatureInstallRequest.newBuilder()
.addModule("SplitSampleFeature01")
.build();
final FeatureTask<Integer> task = mFeatureInstallManager.installFeature(request);
task.addOnListener(new OnFeatureSuccessListener<Integer>() {
@Override
public void onSuccess(Integer integer) {
Log.d(TAG, "load feature onSuccess.session id:" + integer);
}
});
task.addOnListener(new OnFeatureFailureListener<Integer>() {
@Override
public void onFailure(Exception exception) {
if (exception instanceof FeatureInstallException) {
int errorCode = ((FeatureInstallException) exception).getErrorCode();
Log.d(TAG, "load feature onFailure.errorCode:" + errorCode);
} else {
exception.printStackTrace();
}
}
});
task.addOnListener(new OnFeatureCompleteListener<Integer>() {
@Override
public void onComplete(FeatureTask<Integer> featureTask) {
if (featureTask.isComplete()) {
Log.d(TAG, "complete to start install.");
if (featureTask.isSuccessful()) {
Integer result = featureTask.getResult();
sessionId = result;
Log.d(TAG, "succeed to start install. session id :" + result);
} else {
Log.d(TAG, "fail to start install.");
Exception exception = featureTask.getException();
exception.printStackTrace();
}
}
}
});
Log.d(TAG, "start install func end");
}
/**
* start feature
*
* @param view the view
*/
public void startFeature01(View view) {
// test getInstallModules
Set<String> moduleNames = mFeatureInstallManager.getAllInstalledModules();
Log.d(TAG, "getInstallModules : " + moduleNames);
if (moduleNames != null && moduleNames.contains("SplitSampleFeature01")) {
try {
startActivity(new Intent(this, Class.forName(
"com.huawei.android.dynamicfeaturesplit.splitsamplefeature01.FeatureActivity")));
} catch (Exception e) {
Log.w(TAG, "", e);
}
}
}
/**
* cancel install task
*
* @param view the view
*/
public void abortInstallFeature(View view) {
Log.d(TAG, "begin abort_install : " + sessionId);
FeatureTask<Void> task = mFeatureInstallManager.abortInstallFeature(sessionId);
task.addOnListener(new OnFeatureCompleteListener<Void>() {
@Override
public void onComplete(FeatureTask<Void> featureTask) {
if (featureTask.isComplete()) {
Log.d(TAG, "complete to abort_install.");
if (featureTask.isSuccessful()) {
Log.d(TAG, "succeed to abort_install.");
} else {
Log.d(TAG, "fail to abort_install.");
Exception exception = featureTask.getException();
exception.printStackTrace();
}
}
}
});
}
/**
* get install task state
*
* @param view the view
*/
public void getInstallState(View view) {
Log.d(TAG, "begin to get session state for: " + sessionId);
FeatureTask<InstallState> task = mFeatureInstallManager.getInstallState(sessionId);
task.addOnListener(new OnFeatureCompleteListener<InstallState>() {
@Override
public void onComplete(FeatureTask<InstallState> featureTask) {
if (featureTask.isComplete()) {
Log.d(TAG, "complete to get session state.");
if (featureTask.isSuccessful()) {
InstallState state = featureTask.getResult();
Log.d(TAG, "succeed to get session state.");
Log.d(TAG, state.toString());
} else {
Log.e(TAG, "failed to get session state.");
Exception exception = featureTask.getException();
exception.printStackTrace();
}
}
}
});
}
/**
* get states of all install tasks
*
* @param view the view
*/
public void getAllInstallStates(View view) {
Log.d(TAG, "begin to get all session states.");
FeatureTask<List<InstallState>> task = mFeatureInstallManager.getAllInstallStates();
task.addOnListener(new OnFeatureCompleteListener<List<InstallState>>() {
@Override
public void onComplete(FeatureTask<List<InstallState>> featureTask) {
Log.d(TAG, "complete to get session states.");
if (featureTask.isSuccessful()) {
Log.d(TAG, "succeed to get session states.");
List<InstallState> stateList = featureTask.getResult();
for (InstallState state : stateList) {
Log.d(TAG, state.toString());
}
} else {
Log.e(TAG, "fail to get session states.");
Exception exception = featureTask.getException();
exception.printStackTrace();
}
}
});
}
/**
* deffer to install features
*
* @param view the view
*/
public void delayedInstallFeature(View view) {
List<String> features = new ArrayList<>();
features.add("SplitSampleFeature01");
FeatureTask<Void> task = mFeatureInstallManager.delayedInstallFeature(features);
task.addOnListener(new OnFeatureCompleteListener<Void>() {
@Override
public void onComplete(FeatureTask<Void> featureTask) {
if (featureTask.isComplete()) {
Log.d(TAG, "complete to delayed_Install");
if (featureTask.isSuccessful()) {
Log.d(TAG, "succeed to delayed_install");
} else {
Log.d(TAG, "fail to delayed_install.");
Exception exception = featureTask.getException();
exception.printStackTrace();
}
}
}
});
}
/**
* uninstall features
*
* @param view the view
*/
public void delayedUninstallFeature(View view) {
List<String> features = new ArrayList<>();
features.add("SplitSampleFeature01");
FeatureTask<Void> task = mFeatureInstallManager.delayedUninstallFeature(features);
task.addOnListener(new OnFeatureCompleteListener<Void>() {
@Override
public void onComplete(FeatureTask<Void> featureTask) {
if (featureTask.isComplete()) {
Log.d(TAG, "complete to delayed_uninstall");
if (featureTask.isSuccessful()) {
Log.d(TAG, "succeed to delayed_uninstall");
} else {
Log.d(TAG, "fail to delayed_uninstall.");
Exception exception = featureTask.getException();
exception.printStackTrace();
}
}
}
});
}
/**
* install languages
*
* @param view the view
*/
public void loadLanguage(View view) {
if (mFeatureInstallManager == null) {
return;
}
// start install
Set<String> languages = new HashSet<>();
languages.add("fr-FR");
FeatureInstallRequest.Builder builder = FeatureInstallRequest.newBuilder();
for (String lang : languages) {
builder.addLanguage(Locale.forLanguageTag(lang));
}
FeatureInstallRequest request = builder.build();
FeatureTask<Integer> task = mFeatureInstallManager.installFeature(request);
task.addOnListener(new OnFeatureSuccessListener<Integer>() {
@Override
public void onSuccess(Integer result) {
Log.d(TAG, "onSuccess callback result " + result);
}
});
task.addOnListener(new OnFeatureFailureListener<Integer>() {
@Override
public void onFailure(Exception exception) {
if (exception instanceof FeatureInstallException) {
Log.d(TAG, "onFailure callback "
+ ((FeatureInstallException) exception).getErrorCode());
} else {
Log.d(TAG, "onFailure callback ", exception);
}
}
});
task.addOnListener(new OnFeatureCompleteListener<Integer>() {
@Override
public void onComplete(FeatureTask<Integer> task) {
Log.d(TAG, "onComplete callback");
}
});
}
private void makeToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
}
· We have integrated sdk based classes and callbacks to achieve Dynamic Ability feature in our activity based class.
· Let’s implement module based activity class:
Code:
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.Toast;
import com.huawei.hms.feature.dynamicinstall.FeatureCompat;
public class FeatureActivity extends Activity {
private static final String TAG = FeatureActivity.class.getSimpleName();
static {
System.loadLibrary("feature-native-lib");
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
try {
FeatureCompat.install(newBase);
} catch (Exception e) {
Log.w(TAG, "", e);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_feature);
ImageView mImageView = findViewById(R.id.iv_load_png);
mImageView.setImageDrawable(getResources().getDrawable(R.mipmap.google));
Toast.makeText(this, "from feature " + stringFromJNI(), Toast.LENGTH_LONG).show();
}
/**
* String from jni string.
*
* @return the string
*/
public native String stringFromJNI();
}
· In an activity of a dynamic feature module, call FeatureCompat.install to initialize the Dynamic Ability SDK.
Launch the app:
· Let’s see the result-
If you have any doubts or queries. Please leave your valuable comment in the comment section
Related
More information like this, you can visit HUAWEI Developer Forum
{
"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"
}
What is it?
Huawei Games is a group of APIs from Huawei to simplify some games basic features like leaderboards, achievements, Events and online matches.
Gaming technologies are constantly evolving. Nevertheless, a lot of core gameplay elements have remained unchanged for decades. High scores, leaderboards, quests, achievements, and multiplayer support are examples. If you are developing a game for the Android platform, you don't have to implement any of those elements manually. You can simply use the Huawei Game services APIs instead.
Features of Huawei Game services
· HUAWEI ID sign-in
· Real-name authentication
· Bulletins
· Achievements
· Events
· Leaderboard
· Saved games
· Player statistics
Let’s start!
You should follow the steps
1) Create an android project in android studio.
2) Get the SHA Key. Create SHA key
3) Create a Game App in the Huawei app galley connect (AGC)
4) Provide the SHA key in the information section.
5) Provide storage location.
6) Enable Game service.
7) After all the steps need to download the agconnect-services.json from the app information section. Copy and paste the json file in the app folder of the android project.
8) Copy and paste the below maven url inside the repositories of buildscript and allprojects ( project build.gradle file )
maven { url 'http://developer.huawei.com/repo/'
Add the classpath in the dependencies path
classpath 'com.huawei.agconnect:agcp:1.2.1.300'
9) Copy and paste the below plugin in the app build.gradle file dependencies section.
apply plugin: 'com.huawei.agconnect'
Add the below 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'
Note: if you are not using the In-App purchase then no need add the following dependency.
1. implementation 'com.huawei.hms:iap:4.0.4.300'
10) Sync App.
In this series of article we will build Tic Tac Toe game. In this article will see the following features.
· Sign In.
· Initialization.
· Getting player info.
· Saving player info.
Sign In
User can sign-in with Huawei account. They can play game.
Code:
public void signIn() {
Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.getService(this, getHuaweiIdParams()).silentSignIn();
authHuaweiIdTask.addOnSuccessListener(new OnSuccessListener<AuthHuaweiId>() {
@Override
public void onSuccess(AuthHuaweiId authHuaweiId) {
showLog("signIn success");
showLog("display:" + authHuaweiId.getDisplayName());
welcomeTv.setVisibility(View.VISIBLE);
welcomeTv.setText("Welcome back " + authHuaweiId.getDisplayName());
signIn.setVisibility(View.INVISIBLE);
showLog("AT:" + authHuaweiId.getAccessToken());
mAuthid = authHuaweiId;
SignInCenter.get().updateAuthHuaweiId(authHuaweiId);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
signIn.setVisibility(View.VISIBLE);
welcomeTv.setVisibility(View.INVISIBLE);
ApiException apiException = (ApiException) e;
showLog("signIn failed:" + apiException.getStatusCode());
showLog("start getSignInIntent");
signInNewWay();
}
}
});
}
public HuaweiIdAuthParams getHuaweiIdParams() {
return new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM_GAME).createParams();
}
public void signInNewWay() {
Intent intent = HuaweiIdAuthManager.getService(SignInActivity.this, getHuaweiIdParams()).getSignInIntent();
startActivityForResult(intent, SIGN_IN_INTENT);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (SIGN_IN_INTENT == requestCode) {
handleSignInResult(data);
} else {
showLog("unknown requestCode in onActivityResult");
}
}
private void handleSignInResult(Intent data) {
if (null == data) {
showLog("signIn inetnt is null");
return;
}
// HuaweiIdSignIn.getSignedInAccountFromIntent(data);
String jsonSignInResult = data.getStringExtra("HUAWEIID_SIGNIN_RESULT");
if (TextUtils.isEmpty(jsonSignInResult)) {
showLog("SignIn result is empty");
return;
}
try {
HuaweiIdAuthResult signInResult = new HuaweiIdAuthResult().fromJson(jsonSignInResult);
if (0 == signInResult.getStatus().getStatusCode()) {
showLog("Sign in success.");
showLog("Sign in result: " + signInResult.toJson());
SignInCenter.get().updateAuthHuaweiId(signInResult.getHuaweiId());
} else {
showLog("Sign in failed: " + signInResult.getStatus().getStatusCode());
}
} catch (JSONException var7) {
showLog("Failed to convert json from signInResult.");
}
}
The above show the sign in with Huawei account.
Initialization
Key steps to launch the game
Add the following code to the onCreate method of the Application to register the callback listening function of the activity.
Code:
package com.android.huawei.tictactoe;
import android.app.Application;
import com.huawei.hms.api.HuaweiMobileServicesUtil;
public class TicTacToeApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HuaweiMobileServicesUtil.setApplication(this);
}
}
Call JosApps.getJosAppsClient to initialize the JosAppsClient object and JosAppsClient.init to initialize the game.
Code:
private void init() {
JosAppsClient appsClient = JosApps.getJosAppsClient(this, null);
appsClient.init();
Log.i(TAG, "init success");
}
Getting player info
Code:
public void getCurrentPlayer() {
PlayersClientImpl client = (PlayersClientImpl) Games.getPlayersClient(this, getAuthHuaweiId());
Task<Player> task = client.getCurrentPlayer();
task.addOnSuccessListener(new OnSuccessListener<Player>() {
@Override
public void onSuccess(Player player) {
String result = "display:" + player.getDisplayName() + "\n"
+ "playerId:" + player.getPlayerId() + "\n"
+ "playerLevel:" + player.getLevel() + "\n"
+ "timestamp:" + player.getSignTs() + "\n"
+ "playerSign:" + player.getPlayerSign();
showLog(result);
playerId = player.getPlayerId();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
showLog(result);
}
}
});
}
The above code gives the player info like playerId, Player name, player level, in which level he/she is playing.
Saving player info.
Player info can be updated using AppPlayerInfo. Player level, role, rank etc. The following code show the saving player info.
To save player info playerId is mandatory.
Code:
public void savePlayerInfo() {
if (TextUtils.isEmpty(playerId)) {
Toast.makeText(this, "Get the current user first", Toast.LENGTH_SHORT).show();
return;
}
PlayersClient client = Games.getPlayersClient(this, getAuthHuaweiId());
AppPlayerInfo appPlayerInfo = new AppPlayerInfo();
appPlayerInfo.area = "2";
appPlayerInfo.rank = "56";
appPlayerInfo.role = "Pro";
appPlayerInfo.sociaty = "Red Cliff III";
appPlayerInfo.playerId = playerId;
Task<Void> task = client.savePlayerInfo(appPlayerInfo);
task.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void v) {
Toast.makeText(SignInActivity.this, "Player info saved successfully ", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
Toast.makeText(SignInActivity.this, result, Toast.LENGTH_SHORT).show();
}
}
});
}
Result
Nice, thank you
Can be useful for online games.
Freemind R said:
More information like this, you can visit HUAWEI Developer Forum
What is it?
Huawei Games is a group of APIs from Huawei to simplify some games basic features like leaderboards, achievements, Events and online matches.
Gaming technologies are constantly evolving. Nevertheless, a lot of core gameplay elements have remained unchanged for decades. High scores, leaderboards, quests, achievements, and multiplayer support are examples. If you are developing a game for the Android platform, you don't have to implement any of those elements manually. You can simply use the Huawei Game services APIs instead.
Features of Huawei Game services
· HUAWEI ID sign-in
· Real-name authentication
· Bulletins
· Achievements
· Events
· Leaderboard
· Saved games
· Player statistics
Let’s start!
You should follow the steps
1) Create an android project in android studio.
2) Get the SHA Key. Create SHA key
3) Create a Game App in the Huawei app galley connect (AGC)
4) Provide the SHA key in the information section.
5) Provide storage location.
6) Enable Game service.
7) After all the steps need to download the agconnect-services.json from the app information section. Copy and paste the json file in the app folder of the android project.
8) Copy and paste the below maven url inside the repositories of buildscript and allprojects ( project build.gradle file )
maven { url 'http://developer.huawei.com/repo/'
Add the classpath in the dependencies path
classpath 'com.huawei.agconnect:agcp:1.2.1.300'
9) Copy and paste the below plugin in the app build.gradle file dependencies section.
apply plugin: 'com.huawei.agconnect'
Add the below 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'
Note: if you are not using the In-App purchase then no need add the following dependency.
1. implementation 'com.huawei.hms:iap:4.0.4.300'
10) Sync App.
In this series of article we will build Tic Tac Toe game. In this article will see the following features.
· Sign In.
· Initialization.
· Getting player info.
· Saving player info.
Sign In
User can sign-in with Huawei account. They can play game.
Code:
public void signIn() {
Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.getService(this, getHuaweiIdParams()).silentSignIn();
authHuaweiIdTask.addOnSuccessListener(new OnSuccessListener<AuthHuaweiId>() {
@Override
public void onSuccess(AuthHuaweiId authHuaweiId) {
showLog("signIn success");
showLog("display:" + authHuaweiId.getDisplayName());
welcomeTv.setVisibility(View.VISIBLE);
welcomeTv.setText("Welcome back " + authHuaweiId.getDisplayName());
signIn.setVisibility(View.INVISIBLE);
showLog("AT:" + authHuaweiId.getAccessToken());
mAuthid = authHuaweiId;
SignInCenter.get().updateAuthHuaweiId(authHuaweiId);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
signIn.setVisibility(View.VISIBLE);
welcomeTv.setVisibility(View.INVISIBLE);
ApiException apiException = (ApiException) e;
showLog("signIn failed:" + apiException.getStatusCode());
showLog("start getSignInIntent");
signInNewWay();
}
}
});
}
public HuaweiIdAuthParams getHuaweiIdParams() {
return new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM_GAME).createParams();
}
public void signInNewWay() {
Intent intent = HuaweiIdAuthManager.getService(SignInActivity.this, getHuaweiIdParams()).getSignInIntent();
startActivityForResult(intent, SIGN_IN_INTENT);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (SIGN_IN_INTENT == requestCode) {
handleSignInResult(data);
} else {
showLog("unknown requestCode in onActivityResult");
}
}
private void handleSignInResult(Intent data) {
if (null == data) {
showLog("signIn inetnt is null");
return;
}
// HuaweiIdSignIn.getSignedInAccountFromIntent(data);
String jsonSignInResult = data.getStringExtra("HUAWEIID_SIGNIN_RESULT");
if (TextUtils.isEmpty(jsonSignInResult)) {
showLog("SignIn result is empty");
return;
}
try {
HuaweiIdAuthResult signInResult = new HuaweiIdAuthResult().fromJson(jsonSignInResult);
if (0 == signInResult.getStatus().getStatusCode()) {
showLog("Sign in success.");
showLog("Sign in result: " + signInResult.toJson());
SignInCenter.get().updateAuthHuaweiId(signInResult.getHuaweiId());
} else {
showLog("Sign in failed: " + signInResult.getStatus().getStatusCode());
}
} catch (JSONException var7) {
showLog("Failed to convert json from signInResult.");
}
}
The above show the sign in with Huawei account.
Initialization
Key steps to launch the game
Add the following code to the onCreate method of the Application to register the callback listening function of the activity.
Code:
package com.android.huawei.tictactoe;
import android.app.Application;
import com.huawei.hms.api.HuaweiMobileServicesUtil;
public class TicTacToeApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HuaweiMobileServicesUtil.setApplication(this);
}
}
Call JosApps.getJosAppsClient to initialize the JosAppsClient object and JosAppsClient.init to initialize the game.
Code:
private void init() {
JosAppsClient appsClient = JosApps.getJosAppsClient(this, null);
appsClient.init();
Log.i(TAG, "init success");
}
Getting player info
Code:
public void getCurrentPlayer() {
PlayersClientImpl client = (PlayersClientImpl) Games.getPlayersClient(this, getAuthHuaweiId());
Task<Player> task = client.getCurrentPlayer();
task.addOnSuccessListener(new OnSuccessListener<Player>() {
@Override
public void onSuccess(Player player) {
String result = "display:" + player.getDisplayName() + "\n"
+ "playerId:" + player.getPlayerId() + "\n"
+ "playerLevel:" + player.getLevel() + "\n"
+ "timestamp:" + player.getSignTs() + "\n"
+ "playerSign:" + player.getPlayerSign();
showLog(result);
playerId = player.getPlayerId();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
showLog(result);
}
}
});
}
The above code gives the player info like playerId, Player name, player level, in which level he/she is playing.
Saving player info.
Player info can be updated using AppPlayerInfo. Player level, role, rank etc. The following code show the saving player info.
To save player info playerId is mandatory.
Code:
public void savePlayerInfo() {
if (TextUtils.isEmpty(playerId)) {
Toast.makeText(this, "Get the current user first", Toast.LENGTH_SHORT).show();
return;
}
PlayersClient client = Games.getPlayersClient(this, getAuthHuaweiId());
AppPlayerInfo appPlayerInfo = new AppPlayerInfo();
appPlayerInfo.area = "2";
appPlayerInfo.rank = "56";
appPlayerInfo.role = "Pro";
appPlayerInfo.sociaty = "Red Cliff III";
appPlayerInfo.playerId = playerId;
Task<Void> task = client.savePlayerInfo(appPlayerInfo);
task.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void v) {
Toast.makeText(SignInActivity.this, "Player info saved successfully ", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
Toast.makeText(SignInActivity.this, result, Toast.LENGTH_SHORT).show();
}
}
});
}
Result
Click to expand...
Click to collapse
Whtat is the difference between sign in and Silent Sign in
Introduction
In this article, will explain how to develop a security application in Lite wearable. To achieve it we have to use the Wear Engine library. It will give us the solution for communication between Harmony wearable and android smartphone.
{
"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"
}
Requirements
1) DevEco IDE.
2) Lite wearable watch.
3) Android Smartphone.
4) Huawei developer account.
Integration process
The integration process contains two parts. Android smartphone side and Wear app side.
Android side
Step 1: Create the android project on Android studio.
Step 2: Generate Android signature files.
Step 3: Generate SHA -256 from the keystore generated. Please refer this link. https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#0
Step 4: Navigate to Huawei developer console. Click on Huawei ID (https://developer.huawei.com/consumer/en/console#/productlist/32).
Step 5: Create new product. Add the SHA-256 as the first signed certificate.
Step 6: Click Wear Engine under App services.
Step 7: Click Apply for Wear Engine, agree to the agreement, and the screen for the data permission application is displayed.
Wait for the approval.
Step 8: Open the project-level build gradle of your Android project.
Step 9: Navigateto buildscript > repositories and add the Maven repository configurations.
Java:
maven {url 'https://developer.huawei.com/repo/'}
Step 10: Navigate to allprojects > repositories and add the Maven repository address
Java:
maven {url 'https://developer.huawei.com/repo/'}
Step 11: Add wear engine sdk on the build gradle.
Java:
implementation 'com.huawei.hms:wearengine:{version}'
Step 12: Add the proguard rules in proguard-rules.pro
Java:
<p>-keepattributes *Annotation*
-keepattributes Signature
-keepattributes InnerClasses
-keepattributes EnclosingMethod
-keep class com.huawei.wearengine.**{*;}
</p>
Step 13: Add code snippet to Search for the available device on the MainActivity.java
Java:
private void searchAvailableDevices() {
DeviceClient deviceClient = HiWear.getDeviceClient(this);
deviceClient.hasAvailableDevices().addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean result) {
checkPermissionGranted();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
Step 14: If the devices are available call for device permission granted or not.
Java:
private void checkPermissionGranted() {
AuthClient authClient = HiWear.getAuthClient(this);
authClient.checkPermission(Permission.DEVICE_MANAGER).addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
if (!aBoolean) {
askPermission();
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
Step 15: If permission is not granted ask for permission.
Java:
private void askPermission() {
AuthClient authClient = HiWear.getAuthClient(this);
AuthCallback authCallback = new AuthCallback() {
@Override
public void onOk(Permission[] permissions) {
if (permissions.length != 0) {
checkCurrentConnectedDevice();
}
}
@Override
public void onCancel() {
}
};
authClient.requestPermission(authCallback, Permission.DEVICE_MANAGER)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void successVoid) {
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
Step 16: Get the connected device object for the communication.
Java:
private void checkCurrentConnectedDevice() {
final List<Device> deviceList = new ArrayList<>();
DeviceClient deviceClient = HiWear.getDeviceClient(this);
deviceClient.getBondedDevices()
.addOnSuccessListener(new OnSuccessListener<List<Device>>() {
@Override
public void onSuccess(List<Device> devices) {
deviceList.addAll(devices);
if (!deviceList.isEmpty()) {
for (Device device : deviceList) {
if (device.isConnected()) {
connectedDevice = device;
}
}
}
if (connectedDevice != null) {
checkAppInstalledInWatch(connectedDevice);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
//Process logic when the device list fails to be obtained
}
});
}
Step 17: Call pingfunction to check if the Wear app is installed on the watch.
Java:
private void checkAppInstalledInWatch(final Device connectedDevice) {
P2pClient p2pClient = HiWear.getP2pClient(this);
String peerPkgName = "com.wearengine.huawei";
p2pClient.setPeerPkgName(peerPkgName);
if (connectedDevice != null && connectedDevice.isConnected()) {
p2pClient.ping(connectedDevice, new PingCallback() {
@Override
public void onPingResult(int errCode) {
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void successVoid) {
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
Step 18: If the ping is success your watch app will launch automatically.
Step 19: Add Password check method on click listener function.
Java:
@Override
public void onClick(View view) {
if (view.getId() == R.id.btLogin) {
if (etPin.getText().toString().equals("1234")) {
sendMessageToWatch("Success", connectedDevice);
} else {
sendMessageToWatch("Wrong password", connectedDevice);
}
}
}
Step 20: Send message to the watch.
Java:
private void sendMessageToWatch(String message, Device connectedDevice) {
P2pClient p2pClient = HiWear.getP2pClient(this);
String peerPkgName = "com.wearengine.huawei";
p2pClient.setPeerPkgName(peerPkgName);
String peerFingerPrint = "com.wearengine.huawei_BALgPWTbV2CKZ9swMfG1n9ReRlQFqiZrEGWyVQp/6UIgCUsgXn7PojLPA4iatPktya1pLAORwvHgHpv/Z5DfMK8=";
p2pClient.setPeerFingerPrint(peerFingerPrint);
Message.Builder builder = new Message.Builder();
builder.setPayload(message.getBytes(StandardCharsets.UTF_8));
Message sendMessage = builder.build();
SendCallback sendCallback = new SendCallback() {
@Override
public void onSendResult(int resultCode) {
}
@Override
public void onSendProgress(long progress) {
}
};
if (connectedDevice != null && connectedDevice.isConnected() && sendMessage != null && sendCallback != null) {
p2pClient.send(connectedDevice, sendMessage, sendCallback)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
//Related processing logic for your app after the send command runs
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
//Related processing logic for your app after the send command fails to run
}
});
}
Step 21: Generate the p2p fingerprint. Please follow this article - https://forums.developer.huawei.com/forumPortal/en/topic/0202466737940270075
The final code for your android application will be as given below.
Java:
package com.phone.wearengine;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.wearengine.HiWear;
import com.huawei.wearengine.auth.AuthCallback;
import com.huawei.wearengine.auth.AuthClient;
import com.huawei.wearengine.auth.Permission;
import com.huawei.wearengine.device.Device;
import com.huawei.wearengine.device.DeviceClient;
import com.huawei.wearengine.p2p.Message;
import com.huawei.wearengine.p2p.P2pClient;
import com.huawei.wearengine.p2p.PingCallback;
import com.huawei.wearengine.p2p.SendCallback;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Device connectedDevice;
private EditText etPin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUi();
searchAvailableDevices();
checkCurrentConnectedDevice();
}
private void initUi() {
etPin = findViewById(R.id.etPin);
findViewById(R.id.btLogin).setOnClickListener(this);
}
private void searchAvailableDevices() {
DeviceClient deviceClient = HiWear.getDeviceClient(this);
deviceClient.hasAvailableDevices().addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean result) {
checkPermissionGranted();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
private void checkPermissionGranted() {
AuthClient authClient = HiWear.getAuthClient(this);
authClient.checkPermission(Permission.DEVICE_MANAGER).addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
if (!aBoolean) {
askPermission();
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
private void askPermission() {
AuthClient authClient = HiWear.getAuthClient(this);
AuthCallback authCallback = new AuthCallback() {
@Override
public void onOk(Permission[] permissions) {
if (permissions.length != 0) {
checkCurrentConnectedDevice();
}
}
@Override
public void onCancel() {
}
};
authClient.requestPermission(authCallback, Permission.DEVICE_MANAGER)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void successVoid) {
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
private void checkCurrentConnectedDevice() {
final List<Device> deviceList = new ArrayList<>();
DeviceClient deviceClient = HiWear.getDeviceClient(this);
deviceClient.getBondedDevices()
.addOnSuccessListener(new OnSuccessListener<List<Device>>() {
@Override
public void onSuccess(List<Device> devices) {
deviceList.addAll(devices);
if (!deviceList.isEmpty()) {
for (Device device : deviceList) {
if (device.isConnected()) {
connectedDevice = device;
}
}
}
if (connectedDevice != null) {
checkAppInstalledInWatch(connectedDevice);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
//Process logic when the device list fails to be obtained
}
});
}
private void checkAppInstalledInWatch(final Device connectedDevice) {
P2pClient p2pClient = HiWear.getP2pClient(this);
String peerPkgName = "com.wearengine.huawei";
p2pClient.setPeerPkgName(peerPkgName);
if (connectedDevice != null && connectedDevice.isConnected()) {
p2pClient.ping(connectedDevice, new PingCallback() {
@Override
public void onPingResult(int errCode) {
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void successVoid) {
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
}
private void sendMessageToWatch(String message, Device connectedDevice) {
P2pClient p2pClient = HiWear.getP2pClient(this);
String peerPkgName = "com.wearengine.huawei";
p2pClient.setPeerPkgName(peerPkgName);
String peerFingerPrint = "com.wearengine.huawei_BALgPWTbV2CKZ9swMfG1n9ReRlQFqiZrEG*******";
p2pClient.setPeerFingerPrint(peerFingerPrint);
Message.Builder builder = new Message.Builder();
builder.setPayload(message.getBytes(StandardCharsets.UTF_8));
Message sendMessage = builder.build();
SendCallback sendCallback = new SendCallback() {
@Override
public void onSendResult(int resultCode) {
}
@Override
public void onSendProgress(long progress) {
}
};
if (connectedDevice != null && connectedDevice.isConnected() && sendMessage != null && sendCallback != null) {
p2pClient.send(connectedDevice, sendMessage, sendCallback)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
//Related processing logic for your app after the send command runs
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
//Related processing logic for your app after the send command fails to run
}
});
}
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btLogin) {
if (etPin.getText().toString().equals("1234")) {
sendMessageToWatch("Success", connectedDevice);
} else {
sendMessageToWatch("Wrong password", connectedDevice);
}
}
}
}
Watch side
Step 1: Create a Lite Wearable project on DevEco studio.
Step 2: Generate the required certificates to run the application. Please refer to this article https://forums.developer.huawei.com/forumPortal/en/topic/0202465210302250053
Step 3: Download and Add the Wear Engine library in the pages folder of the Harmony project. https://developer.huawei.com/consum...ity-Library/litewearable-sdk-0000001053562589
Step 4: Design the UI.
Index.hml
HTML:
<div class="container">
<text class="title">
{{title}}
</text>
</div>
Index.css
CSS:
.container {
display: flex;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 454px;
height: 454px;
background-color: cadetblue;
}
.title {
font-size: 90px;
text-align: center;
width: 300px;
height: 150px;
}
Step 5: Open index.js file and import the wearengine SDK.
JavaScript:
import {P2pClient, Message, Builder} from '../wearengine';
Step 6: Add the receiver code snippet on index.js.
JavaScript:
onInit() {
var _that = this;
_that.setBrightnessKeepScreenOn();
//Step 1: Obtain the point-to-point communication object
var p2pClient = new P2pClient();
var peerPkgName = "com.phone.wearengine";
var peerFinger = "79C3B257672C32974283E712EF7FEC******";
//Step 2: Set your app package name that needs communications on the phone
p2pClient.setPeerPkgName(peerPkgName);
//Step 3: Set the fingerprint information of the app on the phone. (This API is unavailable currently. In this version, you need to set fingerprint mode in the config.json file in Step 5.)
p2pClient.setPeerFingerPrint(peerFinger);
//Step 4: Receive short messages or files from your app on the phone
//Define the receiver
var flash = this;
var receiver = {
onSuccess: function () {
console.info("Recieved message");
//Process the callback function returned when messages or files fail to be received from the phone during registration.
flash.receiveMessageOK = "Succeeded in receiving the message";
},
onFailure: function () {
console.info("Failed message");
//Registering a listener for the callback method of failing to receive messages or files from phone
flash.receiveMessageOK = "Failed to receive the message";
},
onReceiveMessage: function (data) {
if (data && data.isFileType) {
//Process the file sent by your app on the phone
flash.receiveMessgeOK = "file:" + data.name;
} else {
console.info("Got message - " + data);
//Process the message sent from your app on the phone.
flash.receiveMessageOK = "message:" + data;
_that.title = "" + data;
if (data != "Success") {
vibrator.vibrate({
mode: "long"
})
}
}
},
}
p2pClient.registerReceiver(receiver);
},
PeerFingerPrint on watch side is SHA-256 of Android application (Make sure you have removed the colons)
Step 7: Unregister the receiver on destroy of wearable app.
Java:
onDestroy() {
this.p2pClient.unregisterReceiver();
}
Step 8: Add metadata inside of module object of config.json.
JSON:
"metaData": {
"customizeData": [
{
"name": "supportLists",
"value": "com.phone.wearengine:79C3B257672C32974283E756535C86728BE4DF51E*******",
"extra": ""
}
]
}
The final code for your android application given below.
JavaScript:
import {P2pClient, Message, Builder} from '../wearengine';
import brightness from '@system.brightness';
import vibrator from '@system.vibrator';
export default {
data: {
title: 'Enter pin'
},
onInit() {
var _that = this;
_that.setBrightnessKeepScreenOn();
//Step 1: Obtain the point-to-point communication object
var p2pClient = new P2pClient();
var peerPkgName = "com.phone.wearengine";
var peerFinger = "79C3B257672C32974283E756535C*****************";
//Step 2: Set your app package name that needs communications on the phone
p2pClient.setPeerPkgName(peerPkgName);
//Step 3: Set the fingerprint information of the app on the phone. (This API is unavailable currently. In this version, you need to set fingerprint mode in the config.json file in Step 5.)
p2pClient.setPeerFingerPrint(peerFinger);
//Step 4: Receive short messages or files from your app on the phone
//Define the receiver
var flash = this;
var receiver = {
onSuccess: function () {
console.info("Recieved message");
//Process the callback function returned when messages or files fail to be received from the phone during registration.
flash.receiveMessageOK = "Succeeded in receiving the message";
},
onFailure: function () {
console.info("Failed message");
//Registering a listener for the callback method of failing to receive messages or files from phone
flash.receiveMessageOK = "Failed to receive the message";
},
onReceiveMessage: function (data) {
if (data && data.isFileType) {
//Process the file sent by your app on the phone
flash.receiveMessgeOK = "file:" + data.name;
} else {
console.info("Got message - " + data);
//Process the message sent from your app on the phone.
flash.receiveMessageOK = "message:" + data;
_that.title = "" + data;
if (data != "Success") {
vibrator.vibrate({
mode: "long"
})
}
}
},
}
p2pClient.registerReceiver(receiver);
},
setBrightnessKeepScreenOn: function () {
brightness.setKeepScreenOn({
keepScreenOn: true,
success: function () {
console.log("handling set keep screen on success")
},
fail: function (data, code) {
console.log("handling set keep screen on fail, code:" + code);
}
});
},
onDestroy() {
// FeatureAbility.unsubscribeMsg();
this.p2pClient.unregisterReceiver();
}
}
Results
Tips & Tricks
Make sure you are generated the SHA - 256 fingerprint of proper keystore.
Follow the P2P generation steps properly.
Conclusion
In this article, we are learned how to develop a security app using Wear Engine. The wear engine will allow us to communicate between the Android application and the Harmony Wear application.
Reference
Harmony Official document - https://developer.harmonyos.com/en/docs/documentation/doc-guides/harmonyos-overview-0000000000011903
Wear Engine documentation - https://developer.huawei.com/consumer/en/doc/development/connectivity-Guides/service-introduction-0000001050978399
Certificate generation article - https://forums.developer.huawei.com/forumPortal/en/topic/0202465210302250053
P2P generation article - https://forums.developer.huawei.com/forumPortal/en/topic/0202466737940270075
Interesting app.
Nice article
ask011 said:
Introduction
In this article, will explain how to develop a security application in Lite wearable. To achieve it we have to use the Wear Engine library. It will give us the solution for communication between Harmony wearable and android smartphone.
Requirements
1) DevEco IDE.
2) Lite wearable watch.
3) Android Smartphone.
4) Huawei developer account.
Integration process
The integration process contains two parts. Android smartphone side and Wear app side.
Android side
Step 1: Create the android project on Android studio.
Step 2: Generate Android signature files.
Step 3: Generate SHA -256 from the keystore generated. Please refer this link. https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#0
Step 4: Navigate to Huawei developer console. Click on Huawei ID (https://developer.huawei.com/consumer/en/console#/productlist/32).
Step 5: Create new product. Add the SHA-256 as the first signed certificate.
Step 6: Click Wear Engine under App services.
Step 7: Click Apply for Wear Engine, agree to the agreement, and the screen for the data permission application is displayed.
Wait for the approval.
Step 8: Open the project-level build gradle of your Android project.
Step 9: Navigateto buildscript > repositories and add the Maven repository configurations.
Java:
maven {url 'https://developer.huawei.com/repo/'}
Step 10: Navigate to allprojects > repositories and add the Maven repository address
Java:
maven {url 'https://developer.huawei.com/repo/'}
Step 11: Add wear engine sdk on the build gradle.
Java:
implementation 'com.huawei.hms:wearengine:{version}'
Step 12: Add the proguard rules in proguard-rules.pro
Java:
<p>-keepattributes *Annotation*
-keepattributes Signature
-keepattributes InnerClasses
-keepattributes EnclosingMethod
-keep class com.huawei.wearengine.**{*;}
</p>
Step 13: Add code snippet to Search for the available device on the MainActivity.java
Java:
private void searchAvailableDevices() {
DeviceClient deviceClient = HiWear.getDeviceClient(this);
deviceClient.hasAvailableDevices().addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean result) {
checkPermissionGranted();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
Step 14: If the devices are available call for device permission granted or not.
Java:
private void checkPermissionGranted() {
AuthClient authClient = HiWear.getAuthClient(this);
authClient.checkPermission(Permission.DEVICE_MANAGER).addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
if (!aBoolean) {
askPermission();
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
Step 15: If permission is not granted ask for permission.
Java:
private void askPermission() {
AuthClient authClient = HiWear.getAuthClient(this);
AuthCallback authCallback = new AuthCallback() {
@Override
public void onOk(Permission[] permissions) {
if (permissions.length != 0) {
checkCurrentConnectedDevice();
}
}
@Override
public void onCancel() {
}
};
authClient.requestPermission(authCallback, Permission.DEVICE_MANAGER)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void successVoid) {
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
Step 16: Get the connected device object for the communication.
Java:
private void checkCurrentConnectedDevice() {
final List<Device> deviceList = new ArrayList<>();
DeviceClient deviceClient = HiWear.getDeviceClient(this);
deviceClient.getBondedDevices()
.addOnSuccessListener(new OnSuccessListener<List<Device>>() {
@Override
public void onSuccess(List<Device> devices) {
deviceList.addAll(devices);
if (!deviceList.isEmpty()) {
for (Device device : deviceList) {
if (device.isConnected()) {
connectedDevice = device;
}
}
}
if (connectedDevice != null) {
checkAppInstalledInWatch(connectedDevice);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
//Process logic when the device list fails to be obtained
}
});
}
Step 17: Call pingfunction to check if the Wear app is installed on the watch.
Java:
private void checkAppInstalledInWatch(final Device connectedDevice) {
P2pClient p2pClient = HiWear.getP2pClient(this);
String peerPkgName = "com.wearengine.huawei";
p2pClient.setPeerPkgName(peerPkgName);
if (connectedDevice != null && connectedDevice.isConnected()) {
p2pClient.ping(connectedDevice, new PingCallback() {
@Override
public void onPingResult(int errCode) {
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void successVoid) {
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
Step 18: If the ping is success your watch app will launch automatically.
Step 19: Add Password check method on click listener function.
Java:
@Override
public void onClick(View view) {
if (view.getId() == R.id.btLogin) {
if (etPin.getText().toString().equals("1234")) {
sendMessageToWatch("Success", connectedDevice);
} else {
sendMessageToWatch("Wrong password", connectedDevice);
}
}
}
Step 20: Send message to the watch.
Java:
private void sendMessageToWatch(String message, Device connectedDevice) {
P2pClient p2pClient = HiWear.getP2pClient(this);
String peerPkgName = "com.wearengine.huawei";
p2pClient.setPeerPkgName(peerPkgName);
String peerFingerPrint = "com.wearengine.huawei_BALgPWTbV2CKZ9swMfG1n9ReRlQFqiZrEGWyVQp/6UIgCUsgXn7PojLPA4iatPktya1pLAORwvHgHpv/Z5DfMK8=";
p2pClient.setPeerFingerPrint(peerFingerPrint);
Message.Builder builder = new Message.Builder();
builder.setPayload(message.getBytes(StandardCharsets.UTF_8));
Message sendMessage = builder.build();
SendCallback sendCallback = new SendCallback() {
@Override
public void onSendResult(int resultCode) {
}
@Override
public void onSendProgress(long progress) {
}
};
if (connectedDevice != null && connectedDevice.isConnected() && sendMessage != null && sendCallback != null) {
p2pClient.send(connectedDevice, sendMessage, sendCallback)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
//Related processing logic for your app after the send command runs
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
//Related processing logic for your app after the send command fails to run
}
});
}
Step 21: Generate the p2p fingerprint. Please follow this article - https://forums.developer.huawei.com/forumPortal/en/topic/0202466737940270075
The final code for your android application will be as given below.
Java:
package com.phone.wearengine;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.wearengine.HiWear;
import com.huawei.wearengine.auth.AuthCallback;
import com.huawei.wearengine.auth.AuthClient;
import com.huawei.wearengine.auth.Permission;
import com.huawei.wearengine.device.Device;
import com.huawei.wearengine.device.DeviceClient;
import com.huawei.wearengine.p2p.Message;
import com.huawei.wearengine.p2p.P2pClient;
import com.huawei.wearengine.p2p.PingCallback;
import com.huawei.wearengine.p2p.SendCallback;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Device connectedDevice;
private EditText etPin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUi();
searchAvailableDevices();
checkCurrentConnectedDevice();
}
private void initUi() {
etPin = findViewById(R.id.etPin);
findViewById(R.id.btLogin).setOnClickListener(this);
}
private void searchAvailableDevices() {
DeviceClient deviceClient = HiWear.getDeviceClient(this);
deviceClient.hasAvailableDevices().addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean result) {
checkPermissionGranted();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
private void checkPermissionGranted() {
AuthClient authClient = HiWear.getAuthClient(this);
authClient.checkPermission(Permission.DEVICE_MANAGER).addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
if (!aBoolean) {
askPermission();
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
private void askPermission() {
AuthClient authClient = HiWear.getAuthClient(this);
AuthCallback authCallback = new AuthCallback() {
@Override
public void onOk(Permission[] permissions) {
if (permissions.length != 0) {
checkCurrentConnectedDevice();
}
}
@Override
public void onCancel() {
}
};
authClient.requestPermission(authCallback, Permission.DEVICE_MANAGER)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void successVoid) {
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
private void checkCurrentConnectedDevice() {
final List<Device> deviceList = new ArrayList<>();
DeviceClient deviceClient = HiWear.getDeviceClient(this);
deviceClient.getBondedDevices()
.addOnSuccessListener(new OnSuccessListener<List<Device>>() {
@Override
public void onSuccess(List<Device> devices) {
deviceList.addAll(devices);
if (!deviceList.isEmpty()) {
for (Device device : deviceList) {
if (device.isConnected()) {
connectedDevice = device;
}
}
}
if (connectedDevice != null) {
checkAppInstalledInWatch(connectedDevice);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
//Process logic when the device list fails to be obtained
}
});
}
private void checkAppInstalledInWatch(final Device connectedDevice) {
P2pClient p2pClient = HiWear.getP2pClient(this);
String peerPkgName = "com.wearengine.huawei";
p2pClient.setPeerPkgName(peerPkgName);
if (connectedDevice != null && connectedDevice.isConnected()) {
p2pClient.ping(connectedDevice, new PingCallback() {
@Override
public void onPingResult(int errCode) {
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void successVoid) {
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
}
private void sendMessageToWatch(String message, Device connectedDevice) {
P2pClient p2pClient = HiWear.getP2pClient(this);
String peerPkgName = "com.wearengine.huawei";
p2pClient.setPeerPkgName(peerPkgName);
String peerFingerPrint = "com.wearengine.huawei_BALgPWTbV2CKZ9swMfG1n9ReRlQFqiZrEG*******";
p2pClient.setPeerFingerPrint(peerFingerPrint);
Message.Builder builder = new Message.Builder();
builder.setPayload(message.getBytes(StandardCharsets.UTF_8));
Message sendMessage = builder.build();
SendCallback sendCallback = new SendCallback() {
@Override
public void onSendResult(int resultCode) {
}
@Override
public void onSendProgress(long progress) {
}
};
if (connectedDevice != null && connectedDevice.isConnected() && sendMessage != null && sendCallback != null) {
p2pClient.send(connectedDevice, sendMessage, sendCallback)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
//Related processing logic for your app after the send command runs
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
//Related processing logic for your app after the send command fails to run
}
});
}
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btLogin) {
if (etPin.getText().toString().equals("1234")) {
sendMessageToWatch("Success", connectedDevice);
} else {
sendMessageToWatch("Wrong password", connectedDevice);
}
}
}
}
Watch side
Step 1: Create a Lite Wearable project on DevEco studio.
Step 2: Generate the required certificates to run the application. Please refer to this article https://forums.developer.huawei.com/forumPortal/en/topic/0202465210302250053
Step 3: Download and Add the Wear Engine library in the pages folder of the Harmony project. https://developer.huawei.com/consum...ity-Library/litewearable-sdk-0000001053562589
Step 4: Design the UI.
Index.hml
HTML:
<div class="container">
<text class="title">
{{title}}
</text>
</div>
Index.css
CSS:
.container {
display: flex;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 454px;
height: 454px;
background-color: cadetblue;
}
.title {
font-size: 90px;
text-align: center;
width: 300px;
height: 150px;
}
Step 5: Open index.js file and import the wearengine SDK.
JavaScript:
import {P2pClient, Message, Builder} from '../wearengine';
Step 6: Add the receiver code snippet on index.js.
JavaScript:
onInit() {
var _that = this;
_that.setBrightnessKeepScreenOn();
//Step 1: Obtain the point-to-point communication object
var p2pClient = new P2pClient();
var peerPkgName = "com.phone.wearengine";
var peerFinger = "79C3B257672C32974283E712EF7FEC******";
//Step 2: Set your app package name that needs communications on the phone
p2pClient.setPeerPkgName(peerPkgName);
//Step 3: Set the fingerprint information of the app on the phone. (This API is unavailable currently. In this version, you need to set fingerprint mode in the config.json file in Step 5.)
p2pClient.setPeerFingerPrint(peerFinger);
//Step 4: Receive short messages or files from your app on the phone
//Define the receiver
var flash = this;
var receiver = {
onSuccess: function () {
console.info("Recieved message");
//Process the callback function returned when messages or files fail to be received from the phone during registration.
flash.receiveMessageOK = "Succeeded in receiving the message";
},
onFailure: function () {
console.info("Failed message");
//Registering a listener for the callback method of failing to receive messages or files from phone
flash.receiveMessageOK = "Failed to receive the message";
},
onReceiveMessage: function (data) {
if (data && data.isFileType) {
//Process the file sent by your app on the phone
flash.receiveMessgeOK = "file:" + data.name;
} else {
console.info("Got message - " + data);
//Process the message sent from your app on the phone.
flash.receiveMessageOK = "message:" + data;
_that.title = "" + data;
if (data != "Success") {
vibrator.vibrate({
mode: "long"
})
}
}
},
}
p2pClient.registerReceiver(receiver);
},
PeerFingerPrint on watch side is SHA-256 of Android application (Make sure you have removed the colons)
Step 7: Unregister the receiver on destroy of wearable app.
Java:
onDestroy() {
this.p2pClient.unregisterReceiver();
}
Step 8: Add metadata inside of module object of config.json.
JSON:
"metaData": {
"customizeData": [
{
"name": "supportLists",
"value": "com.phone.wearengine:79C3B257672C32974283E756535C86728BE4DF51E*******",
"extra": ""
}
]
}
The final code for your android application given below.
JavaScript:
import {P2pClient, Message, Builder} from '../wearengine';
import brightness from '@system.brightness';
import vibrator from '@system.vibrator';
export default {
data: {
title: 'Enter pin'
},
onInit() {
var _that = this;
_that.setBrightnessKeepScreenOn();
//Step 1: Obtain the point-to-point communication object
var p2pClient = new P2pClient();
var peerPkgName = "com.phone.wearengine";
var peerFinger = "79C3B257672C32974283E756535C*****************";
//Step 2: Set your app package name that needs communications on the phone
p2pClient.setPeerPkgName(peerPkgName);
//Step 3: Set the fingerprint information of the app on the phone. (This API is unavailable currently. In this version, you need to set fingerprint mode in the config.json file in Step 5.)
p2pClient.setPeerFingerPrint(peerFinger);
//Step 4: Receive short messages or files from your app on the phone
//Define the receiver
var flash = this;
var receiver = {
onSuccess: function () {
console.info("Recieved message");
//Process the callback function returned when messages or files fail to be received from the phone during registration.
flash.receiveMessageOK = "Succeeded in receiving the message";
},
onFailure: function () {
console.info("Failed message");
//Registering a listener for the callback method of failing to receive messages or files from phone
flash.receiveMessageOK = "Failed to receive the message";
},
onReceiveMessage: function (data) {
if (data && data.isFileType) {
//Process the file sent by your app on the phone
flash.receiveMessgeOK = "file:" + data.name;
} else {
console.info("Got message - " + data);
//Process the message sent from your app on the phone.
flash.receiveMessageOK = "message:" + data;
_that.title = "" + data;
if (data != "Success") {
vibrator.vibrate({
mode: "long"
})
}
}
},
}
p2pClient.registerReceiver(receiver);
},
setBrightnessKeepScreenOn: function () {
brightness.setKeepScreenOn({
keepScreenOn: true,
success: function () {
console.log("handling set keep screen on success")
},
fail: function (data, code) {
console.log("handling set keep screen on fail, code:" + code);
}
});
},
onDestroy() {
// FeatureAbility.unsubscribeMsg();
this.p2pClient.unregisterReceiver();
}
}
Results
Tips & Tricks
Make sure you are generated the SHA - 256 fingerprint of proper keystore.
Follow the P2P generation steps properly.
Conclusion
In this article, we are learned how to develop a security app using Wear Engine. The wear engine will allow us to communicate between the Android application and the Harmony Wear application.
Reference
Harmony Official document - https://developer.harmonyos.com/en/docs/documentation/doc-guides/harmonyos-overview-0000000000011903
Wear Engine documentation - https://developer.huawei.com/consumer/en/doc/development/connectivity-Guides/service-introduction-0000001050978399
Certificate generation article - https://forums.developer.huawei.com/forumPortal/en/topic/0202465210302250053
P2P generation article - https://forums.developer.huawei.com/forumPortal/en/topic/0202466737940270075
Click to expand...
Click to collapse
Introduction
In this article, we will learn how to integrate Huawei Safety Detect in Pygmy collection finance application.
If are you new to this application please follow my previous articles
Pygmy collection application Part 1 (Account kit)
Intermediate: Pygmy Collection Application Part 2 (Ads Kit)
Intermediate: Pygmy Collection Application Part 3 (Crash service)
Intermediate: Pygmy Collection Application Part 4 (Analytics Kit Custom Events)
Click to expand...
Click to collapse
What is Safety detect?
Safety Detect builds robust security capabilities, including system integrity check (SysIntegrity), app security check (AppsCheck), malicious URL check (URLCheck), fake user detection (UserDetect), and malicious Wi-Fi detection (WifiDetect), into your app, effectively protecting it against security threats.
Why do we need to use safety detect?
Mobile applications capture almost 90% of people’s time on mobile devices, while the rest of the time is spent browsing the web. So basically now a day’s mobile usage is more than the web. Since all the users are using smart mobile phones for daily needs like, reading news, email, online shopping, booking taxi, and wallets to pay, educational and Finance & Banking apps etc. Even for banking transaction there was time people use to stand in the queue to deposit or withdraw, but now everything can be done in mobile application with just 2 to 3 clicks. Since everything happening over the phone definitely we should provide security to mobile apps.
Now let’s see what all are the security features provided by Huawei Safety detect kit.
SysIntegrity
AppsCheck
URLCheck
UserDetect
WifiDetect
Integration of Safety detect
1. Configure application on the AGC.
2. Client application development process.
How to integrate Huawei Safety Detect in Android finance application?
To achieve this you need to follow couple of steps as follows.
1. Configure application on the AGC.
2. Client application development process.
Configure application on the AGC
Follow the steps.
Step 1: We need to register as a developeraccount in AppGallery Connect. If you are already developer ignore this step.
Step 2: Create an app by referring to Creating a Project and Creating an App in the Project
Step 3: Set the data storage location based on current location.
Step 4: Enabling Analytics Kit. Project setting > Manage API > Enable analytics kit toggle button.
Step 5: Generating a Signing Certificate Fingerprint.
Step 6: Configuring the Signing Certificate Fingerprint.
Step 7: Download your agconnect-services.json file, paste it into the app root directory.
Client application development process
Follow the steps.
Step 1: Create Android application in the Android studio (Any IDE which is your favorite)
Step 2: Add the App level gradle dependencies. Choose inside project Android > app > build.gradle.
Code:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
dependencies {
implementation 'com.huawei.hms:safetydetect:5.2.0.300'
}
Root level gradle dependencies.
Code:
maven { url 'https://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
Step 3: Build Application.
SysIntegrity: Checks whether the device is secure( e.g. whether it is rooted)
Java:
import android.app.AlertDialog;
import android.content.Context;
import android.util.Base64;
import android.util.Log;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.safetydetect.SysIntegrityResp;
import com.huawei.hms.support.api.safetydetect.SafetyDetect;
import com.huawei.hms.support.api.safetydetect.SafetyDetectClient;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;
import org.json.JSONException;
import org.json.JSONObject;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class SysIntegrity {
private static final String TAG= "Safety SysIntegrity";
Context context;
String APP_ID;
private SafetyDetectClient client;
public SysIntegrity(SafetyDetectClient client,Context context, String APP_ID) {
this.context = context;
this.APP_ID = APP_ID;
this.client=client;
}
public void invoke() {
// TODO(developer): Change the nonce generation to include your own, used once value,
// ideally from your remote server.
byte[] nonce = new byte[24];
try {
SecureRandom random;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
random = SecureRandom.getInstanceStrong();
} else {
random = SecureRandom.getInstance("SHA1PRNG");
}
random.nextBytes(nonce);
} catch (
NoSuchAlgorithmException e) {
Log.e(TAG, e.getMessage());
}
// TODO(developer): Change your app ID. You can obtain your app ID in AppGallery Connect.
Task task = client.sysIntegrity(nonce, APP_ID);
task.addOnSuccessListener(new OnSuccessListener<SysIntegrityResp>() {
@Override
public void onSuccess(SysIntegrityResp response) {
// Indicates communication with the service was successful.
// Use response.getResult() to get the result data.
String jwsStr = response.getResult();
String[] jwsSplit = jwsStr.split("\\.");
String jwsPayloadStr = jwsSplit[1];
String payloadDetail = new String(Base64.decode(jwsPayloadStr.getBytes(StandardCharsets.UTF_8), Base64.URL_SAFE), StandardCharsets.UTF_8);
try {
final JSONObject jsonObject = new JSONObject(payloadDetail);
final boolean basicIntegrity = jsonObject.getBoolean("basicIntegrity");
String isBasicIntegrity = String.valueOf(basicIntegrity);
String basicIntegrityResult = "Basic Integrity: " + isBasicIntegrity;
showAlert(basicIntegrityResult);
Log.i(TAG, basicIntegrityResult);
if (!basicIntegrity) {
String advice = "Advice: " + jsonObject.getString("advice");
Log.i("Advice log", advice);
}
} catch (JSONException e) {
String errorMsg = e.getMessage();
showAlert(errorMsg + "unknown error");
Log.e(TAG, errorMsg != null ? errorMsg : "unknown error");
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// An error occurred while communicating with the service.
if (e instanceof ApiException) {
// An error with the HMS API contains some
// additional details.
ApiException apiException = (ApiException) e;
// You can retrieve the status code using
// the apiException.getStatusCode() method.
showAlert("Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getMessage());
Log.e(TAG, "Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getMessage());
} else {
// A different, unknown type of error occurred.
showAlert("ERROR:"+e.getMessage());
Log.e(TAG, "ERROR:" + e.getMessage());
}
}
});
}
public void showAlert(String message){
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("SysIntegrity");
alertDialog.setMessage(message);
alertDialog.show();
}
}
AppsCheck: Determinate malicious apps and provides you with a list of malicious apps.
Java:
import android.app.AlertDialog;
import android.content.Context;
import android.util.Log;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.core.CommonCode;
import com.huawei.hms.support.api.entity.safetydetect.MaliciousAppsData;
import com.huawei.hms.support.api.entity.safetydetect.MaliciousAppsListResp;
import com.huawei.hms.support.api.safetydetect.SafetyDetect;
import com.huawei.hms.support.api.safetydetect.SafetyDetectClient;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;
import java.util.List;
public class AppsCheck {
private static final String TAG= "Safety AppsCheck";
Context context;
String APP_ID;
private SafetyDetectClient client;
public AppsCheck(SafetyDetectClient client,Context context, String APP_ID) {
this.context = context;
this.APP_ID = APP_ID;
this.client=client;
}
public void invokeGetMaliciousApps() {
Task task = client.getMaliciousAppsList();
task.addOnSuccessListener(new OnSuccessListener<MaliciousAppsListResp>() {
@Override
public void onSuccess(MaliciousAppsListResp maliciousAppsListResp) {
// Indicates communication with the service was successful.
// Use resp.getMaliciousApps() to get malicious apps data.
List<MaliciousAppsData> appsDataList = maliciousAppsListResp.getMaliciousAppsList();
// Indicates get malicious apps was successful.
if (maliciousAppsListResp.getRtnCode() == CommonCode.OK) {
if (appsDataList.isEmpty()) {
// Indicates there are no known malicious apps.
showAlert("There are no known potentially malicious apps installed.");
Log.i(TAG, "There are no known potentially malicious apps installed.");
} else {
showAlert("Potentially malicious apps are installed!");
Log.i(TAG, "Potentially malicious apps are installed!");
for (MaliciousAppsData maliciousApp : appsDataList) {
Log.i(TAG, "Information about a malicious app:");
// Use getApkPackageName() to get APK name of malicious app.
Log.i(TAG, "APK: " + maliciousApp.getApkPackageName());
// Use getApkSha256() to get APK sha256 of malicious app.
Log.i(TAG, "SHA-256: " + maliciousApp.getApkSha256());
// Use getApkCategory() to get category of malicious app.
// Categories are defined in AppsCheckConstants
Log.i(TAG, "Category: " + maliciousApp.getApkCategory());
}
}
} else {
showAlert("getMaliciousAppsList fialed: " + maliciousAppsListResp.getErrorReason());
Log.e(TAG, "getMaliciousAppsList fialed: " + maliciousAppsListResp.getErrorReason());
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// An error occurred while communicating with the service.
if (e instanceof ApiException) {
// An error with the HMS API contains some
// additional details.
ApiException apiException = (ApiException) e;
// You can retrieve the status code using the apiException.getStatusCode() method.
showAlert("Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getStatusMessage());
Log.e(TAG, "Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getStatusMessage());
} else {
// A different, unknown type of error occurred.
Log.e(TAG, "ERROR: " + e.getMessage());
}
}
});
}
public void showAlert(String message){
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("AppsCheck");
alertDialog.setMessage(message);
alertDialog.show();
}
}
URLCheck: Provide malicious URL detection capabilities.
Java:
import android.app.AlertDialog;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.safetydetect.UrlCheckResponse;
import com.huawei.hms.support.api.entity.safetydetect.UrlCheckThreat;
import com.huawei.hms.support.api.safetydetect.SafetyDetect;
import com.huawei.hms.support.api.safetydetect.SafetyDetectClient;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;
import java.util.List;
public class URLCheck {
private static final String TAG= "Safety URLCheck";
Context context;
String APP_ID;
private SafetyDetectClient client;
public URLCheck(SafetyDetectClient client, Context context, String APP_ID) {
this.client = client;
this.context = context;
this.APP_ID = APP_ID;
}
public void callUrlCheckApi() {
client.urlCheck("https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/SafetyDetectWiFiDetectAPIDevelopment", APP_ID,
// Specify url threat type
UrlCheckThreat.MALWARE,
UrlCheckThreat.PHISHING)
.addOnSuccessListener(new OnSuccessListener<UrlCheckResponse>() {
/**
* Called after successfully communicating with the SafetyDetect API.
* The #onSuccess callback receives an
* {@link com.huawei.hms.support.api.entity.safetydetect.UrlCheckResponse} that contains a
* list of UrlCheckThreat that contains the threat type of the Url.
*/
@Override
public void onSuccess(UrlCheckResponse urlCheckResponse) {
// Indicates communication with the service was successful.
// Identify any detected threats.
// Call getUrlCheckResponse method of UrlCheckResponse then you can get List<UrlCheckThreat> .
// If List<UrlCheckThreat> is empty , that means no threats found , else that means threats found.
List<UrlCheckThreat> list = urlCheckResponse.getUrlCheckResponse();
if (list.isEmpty()) {
// No threats found.
showAlert("No Threats found!");
Log.i(TAG,"No Threats found!");
} else {
// Threats found!
showAlert("Threats found!");
Log.i(TAG,"Threats found!");
}
}
})
.addOnFailureListener(new OnFailureListener() {
/**
* Called when an error occurred when communicating with the SafetyDetect API.
*/
@Override
public void onFailure(Exception e) {
// An error with the Huawei Mobile Service API contains some additional details.
String errorMsg;
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
errorMsg = "Error: " +
SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " +
e.getMessage();
// You can use the apiException.getStatusCode() method to get the status code.
// Note: If the status code is SafetyDetectStatusCodes.CHECK_WITHOUT_INIT, you need to call initUrlCheck().
} else {
// Unknown type of error has occurred.
errorMsg = e.getMessage();
}
showAlert(errorMsg);
Log.d(TAG, errorMsg);
Toast.makeText(context.getApplicationContext(), errorMsg, Toast.LENGTH_SHORT).show();
}
});
}
public void showAlert(String message){
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("URLCheck");
alertDialog.setMessage(message);
alertDialog.show();
}
}
UserDetect: Determinate fake users and bots.
Java:
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.safetydetect.UserDetectResponse;
import com.huawei.hms.support.api.safetydetect.SafetyDetect;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;
public class UserDetect {
private static final String TAG= "Safety User Detect";
Context context;
String APP_ID;
public UserDetect(Context context, String APP_ID) {
this.APP_ID=APP_ID;
this.context=context;
}
public void detect() {
Log.i(TAG, "User detection start.");
SafetyDetect.getClient(context)
.userDetection(APP_ID)
.addOnSuccessListener(new OnSuccessListener<UserDetectResponse>() {
/**
* Called after successfully communicating with the SafetyDetect API.
* The #onSuccess callback receives an
* {@link UserDetectResponse} that contains a
* responseToken that can be used to get user detect result.
*/
@Override
public void onSuccess(UserDetectResponse userDetectResponse) {
// Indicates communication with the service was successful.
Log.i(TAG, "User detection succeed, response = " + userDetectResponse);
boolean verifySucceed = verify(userDetectResponse.getResponseToken());
if (verifySucceed) {
Toast.makeText(context.getApplicationContext(),
"User detection succeed and verify succeed",
Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(context.getApplicationContext(),
"User detection succeed but verify fail,"
+ "please replace verify url with your's server address",
Toast.LENGTH_SHORT)
.show();
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// There was an error communicating with the service.
String errorMsg;
if (e instanceof ApiException) {
// An error with the HMS API contains some additional details.
ApiException apiException = (ApiException) e;
errorMsg = SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode())
+ ": " + apiException.getMessage();
// You can use the apiException.getStatusCode() method to get the status code.
} else {
// Unknown type of error has occurred.
errorMsg = e.getMessage();
}
Log.i(TAG, "User detection fail. Error info: " + errorMsg);
Toast.makeText(context.getApplicationContext(), errorMsg, Toast.LENGTH_SHORT).show();
}
});
}
/**
* Send responseToken to your server to get the result of user detect.
*/
private static boolean verify(final String responseToken) {
try {
return new AsyncTask<String, Void, Boolean>() {
@Override
protected Boolean doInBackground(String... strings) {
String input = strings[0];
JSONObject jsonObject = new JSONObject();
try {
// TODO(developer): Replace the baseUrl with your own server address,better not hard code.
String baseUrl = "https://www.example.com/userdetect/verify";
jsonObject.put("response", input);
String result = sendPost(baseUrl, jsonObject);
JSONObject resultJson = new JSONObject(result);
boolean success = resultJson.getBoolean("success");
// if success is true that means the user is real human instead of a robot.
Log.i(TAG, "verify: result = " + success);
return success;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}.execute(responseToken).get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* post the response token to yur own server.
*/
private static String sendPost(String baseUrl, JSONObject postDataParams) throws Exception {
URL url = new URL(baseUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(20000);
conn.setConnectTimeout(20000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept", "application/json");
try (OutputStream os = conn.getOutputStream(); BufferedWriter writer =
new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) {
writer.write(postDataParams.toString());
writer.flush();
}
int responseCode = conn.getResponseCode(); // To Check for 200
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
break;
}
in.close();
return sb.toString();
}
return null;
}
}
WifiDetect: Check Wifi to be connected is secure.
1: Failed to obtain the Wi-Fi status.
0: No Wi-Fi is connected.
1: The connected Wi-Fi is secure.
2: The connected Wi-Fi is insecure.
Java:
import android.app.AlertDialog;
import android.content.Context;
import android.util.Log;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.safetydetect.WifiDetectResponse;
import com.huawei.hms.support.api.safetydetect.SafetyDetectClient;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;
public class WifiCheck {
private SafetyDetectClient client;
private static final String TAG= "Safety WIFICheck";
Context context;
String APP_ID;
public WifiCheck(SafetyDetectClient client, Context context, String APP_ID) {
this.client = client;
this.context = context;
this.APP_ID = APP_ID;
}
public void invokeGetWifiDetectStatus() {
Log.i(TAG, "Start to getWifiDetectStatus!");
Task task = client.getWifiDetectStatus();
task.addOnSuccessListener(new OnSuccessListener<WifiDetectResponse>() {
@Override
public void onSuccess(WifiDetectResponse wifiDetectResponse) {
int wifiDetectStatus = wifiDetectResponse.getWifiDetectStatus();
showAlert("\n-1: Failed to obtain the Wi-Fi status. \n" + "0: No Wi-Fi is connected. \n" + "1: The connected Wi-Fi is secure. \n" + "2: The connected Wi-Fi is insecure." + "wifiDetectStatus is: " + wifiDetectStatus);
Log.i(TAG, "\n-1: Failed to obtain the Wi-Fi status. \n" + "0: No Wi-Fi is connected. \n" + "1: The connected Wi-Fi is secure. \n" + "2: The connected Wi-Fi is insecure.");
Log.i(TAG, "wifiDetectStatus is: " + wifiDetectStatus);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
Log.e(TAG,
"Error: " + apiException.getStatusCode() + ":"
+ SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": "
+ apiException.getStatusMessage());
showAlert("Error: " + apiException.getStatusCode() + ":"
+ SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": "
+ apiException.getStatusMessage());
} else {
Log.e(TAG, "ERROR! " + e.getMessage());
showAlert("ERROR! " + e.getMessage());
}
}
});
}
public void showAlert(String message){
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("WifiCheck");
alertDialog.setMessage(message);
alertDialog.show();
}
}
Now call the required things
Java:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.buttonSysIntegrity:
sysIntegrity= new SysIntegrity(client,MainActivity.this,APP_ID);
sysIntegrity.invoke();
break;
case R.id.buttonAppsCheck:
appsCheck=new AppsCheck(client,MainActivity.this,APP_ID);
appsCheck.invokeGetMaliciousApps();
break;
case R.id.buttonURLCheck:
urlCheck=new URLCheck(client,MainActivity.this,APP_ID);
urlCheck.callUrlCheckApi();
break;
case R.id.buttonUserDetect:
userDetect=new UserDetect(MainActivity.this,APP_ID);
userDetect.detect();
break;
case R.id.buttonWifiCheck:
wifiCheck=new WifiCheck(client,MainActivity.this,APP_ID);
wifiCheck.invokeGetWifiDetectStatus();
break;
default:
break;
}
}
Result
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Tips and Tricks
Download latest HMS Flutter plugin.
Check dependencies downloaded properly.
Latest HMS Core APK is required.
Set minSDK 19 or later.
WifiDetect function available only in Chinese mainland.
UserDetect function not available in Chinese mainland.
Conclusion
In this article, we have learnt integration of Huawei safety detect and types of security provided by Huawei safety detect kit. Nowadays everything is happening over the phone, so as developer it is our responsibility to provide security to application. Otherwise users do not use the application. Thank Huawei for giving such great Safety detect kit to build secure application.
Reference
Safety detect
Thanks for sharing!!!
Can this app works on offline?
Overview
In this article, I will create a Directory android application using Webrtc Video Calling App in which I will integrate HMS Core kits such as HMS Account, AuthService, Identity Kit, Firebase Auth, Firebase Realtime DB and CloudDB .
App will make use of android MVVM clean architecture using Jetpack components such as DataBinding, AndroidViewModel, Observer, LiveData and much more.
In this article we are going to implement DataBinding using Observable pattern.
Huawei Cloud DB Kit Introduction
Huawei Cloud DB is a device-cloud synergy database product that enables seamless data synchronization between the device and cloud and between devices, and supports offline application operations, helping you quickly develop device-cloud and multi-device synergy applications.
Flexible synchronization modes:
Cloud DB supports cache and local data synchronization modes. In cache mode, data on the device is a subset of data on the cloud. If persistent cache is allowed, query results will be automatically cached on the device. In local mode, data is stored locally and is not synchronized to the cloud.
Powerful query capability:
Cloud DB supports various predicate query methods. Multiple chain filtering conditions can be used to filter and sort returned results, and limit the number of objects in the returned result set. In cache mode, data can be queried from the Cloud DB zone on the cloud or that on the local device. In local mode, data is directly queried from the Cloud DB zone on the local device.
Real-time update:
In cache mode of Cloud DB, you can listen on data as needed and use the data synchronization function of Cloud DB to update changed data between the device and cloud in real time.
Offline operations:
In cache mode of Cloud DB, if persistent cache is allowed, the application query is automatically changed from Cloud DB to the local host after the device gets offline. All data modified locally will be automatically synchronized to Cloud DB after the device gets online.
Scalability:
Cloud DB provides powerful HUAWEI CLOUD infrastructure functions, including automatic multi-region data replication, consistency assurance, atomic batch operations, and transaction support.
Security level:
Cloud DB supports device-cloud data full encryption management, triple authentication by app, user, and service, and role-based permission management to ensure data security.
WebRTC Service Introduction
WebRTC is a free and open-source project providing web browsers and mobile applications with real-time communication via application programming interfaces.
Prerequisite
Huawei Phone EMUI 3.0 or later.
Non-Huawei phones Android 4.4 or later (API level 19 or higher).
HMS Core APK 4.0.0.300 or later
Android Studio
AppGallery Account
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 and download the configuration file.
3. Navigate to General Information, and then provide Data Storage location.
4. Navigate to Build then Enable Cloud DB
5. Navigate to Cloud DB and Create DB:
App Development
Add Required Dependencies:
Launch Android studio and create a new project. Once the project is ready.
Add following dependency for HMS Kits
Code:
//HMS Kits
implementation 'com.huawei.agconnect:agconnect-core:1.5.0.300'
implementation 'com.huawei.hms:hwid:5.3.0.302'
implementation 'com.huawei.hms:identity:5.3.0.300'
implementation 'com.huawei.agconnect:agconnect-cloud-database:1.5.0.300'
//Google Firebase
implementation platform('com.google.firebase:firebase-bom:28.4.1')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-auth'
implementation 'com.google.firebase:firebase-database'
implementation 'com.google.android.gms:play-services-auth:19.2.0'
implementation 'com.airbnb.android:lottie:4.1.0'
implementation 'com.mikhaellopez:circularimageview:4.3.0'
implementation 'com.kaopiz:kprogresshud:1.2.0'
implementation 'com.google.android.gms:play-services-ads:20.4.0' implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
Navigate to the Gradle scripts folder and open build.gradle (project: app)
Code:
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
classpath 'com.huawei.agconnect:agcp:1.4.2.300'
Configure AndroidManifest.xml.
Code:
<meta-data
android:name="install_channel"
android:value="AppGallery" />
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="104460711" />
</application>
Code Implementation
Created following package model, event, viewmodel.
ViewModel: The ViewModel makes it easy to update data changes on the UI.Create a package named viewmodel in your main folder.Then create a new file and name it LoginViewModel.kt along with their FactoryViewModelProviders.
MainActivity.kt:
Code:
package com.hms.directoryclass MainActivity : AppCompatActivity(), ActivityNavigation { private lateinit var viewModel: LoginViewModel
private lateinit var dataBinding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) val viewModel: LoginViewModel by lazy {
val activity = requireNotNull(this) {}
ViewModelProviders.of(this, LoginViewModelFactory(activity.application))
.get(LoginViewModel::class.java)
} dataBinding.loginViewModel = viewModel
dataBinding.lifecycleOwner = this
viewModel.startActivityForResultEvent.setEventReceiver(this, this)
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
viewModel.onResultFromActivity(requestCode, data)
super.onActivityResult(requestCode, resultCode, data)
}}
LoginViewModel.kt:
Code:
package com.hms.directory.viewmodel
@SuppressLint("StaticFieldLeak")
class LoginViewModel(application: Application) : AndroidViewModel(application), Observable { private val context = getApplication<Application>().applicationContext
private var mAuthManager: AccountAuthService? = null
private var mAuthParam: AccountAuthParams? = null val startActivityForResultEvent = LiveMessageEvent<ActivityNavigation>() fun login() {
val intent = Intent(context, OrderActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent) /* mAuthParam = AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM)
.setIdToken()
.setAccessToken()
.createParams()
mAuthManager = AccountAuthManager.getService(Activity(), mAuthParam)
startActivityForResultEvent.sendEvent {
startActivityForResult(
mAuthManager?.signInIntent,
HMS_SIGN_IN
)
}*/
} fun onResultFromActivity(requestCode: Int, data: Intent?) {
when (requestCode) {
HMS_SIGN_IN -> {
val authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data)
onCompleteLogin(authAccountTask)
}
}
} private fun onCompleteLogin(doneTask: Task<AuthAccount>) {
if (doneTask.isSuccessful) {
val authAccount = doneTask.result
Log.d("LoginViewModel", "SigIn Success")
context.startActivity(Intent(context, ContactListActivity::class.java)) } else {
Log.d("LoginViewModel", "SigIn Error")
}
} override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
} override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
}}
ContactActivity.kt:
Code:
public class ContactListActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact_list); // Load contacts from file
Contacts.loadData(this); // Set up recycler view and fill it with all the contacts
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.contact_list);
recyclerView.setAdapter(new ContactListAdapter(this, Contacts.LIST)); }
LoginFireBaseActivity.java
Code:
package com.hms.directory.app.call;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.GoogleAuthProvider;
import com.google.firebase.database.FirebaseDatabase;
import com.hms.corrierapp.R;
import com.hms.directory.app.call.models.User;import org.jetbrains.annotations.NotNull;public class LoginActivity extends AppCompatActivity { GoogleSignInClient mGoogleSignInClient;
int RC_SIGN_IN = 11;
FirebaseAuth mAuth;
FirebaseDatabase database; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_goole); mAuth = FirebaseAuth.getInstance();
if (mAuth.getCurrentUser() != null) {
goToNextActivity();
} database = FirebaseDatabase.getInstance(); GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("1016048264402-439a9aamtpiajbgqeqg24qkum2bb7fmh.apps.googleusercontent.com")
.requestEmail()
.build(); mGoogleSignInClient = GoogleSignIn.getClient(this, gso); findViewById(R.id.loginBtn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = mGoogleSignInClient.getSignInIntent();
startActivityForResult(intent, RC_SIGN_IN);
//startActivity(new Intent(LoginActivity.this, MainActivity.class));
}
});
} void goToNextActivity() {
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
} @Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data); if (requestCode == RC_SIGN_IN) {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
GoogleSignInAccount account = task.getResult();
authWithGoogle(account.getIdToken());
}
} void authWithGoogle(String idToken) {
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull @NotNull Task<AuthResult> task) {
if (task.isSuccessful()) {
FirebaseUser user = mAuth.getCurrentUser();
User firebaseUser = new User(user.getUid(), user.getDisplayName(), user.getPhotoUrl().toString(), "Unknown", 500);
database.getReference()
.child("profiles")
.child(user.getUid())
.setValue(firebaseUser).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull @NotNull Task<Void> task) {
if (task.isSuccessful()) {
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finishAffinity();
} else {
Toast.makeText(LoginActivity.this, task.getException().getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
}
});
//Log.e("profile", user.getPhotoUrl().toString());
} else {
Log.e("err", task.getException().getLocalizedMessage());
}
}
});
}
}
CallConnectingActivity.java
Code:
public class ConnectingActivity extends AppCompatActivity { ActivityConnectingBinding binding;
FirebaseAuth auth;
FirebaseDatabase database;
boolean isOkay = false; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityConnectingBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); auth = FirebaseAuth.getInstance();
database = FirebaseDatabase.getInstance(); String profile = getIntent().getStringExtra("profile");
Glide.with(this)
.load(profile)
.into(binding.profile); String username = auth.getUid(); database.getReference().child("users")
.orderByChild("status")
.equalTo(0).limitToFirst(1)
.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull @NotNull DataSnapshot snapshot) {
if (snapshot.getChildrenCount() > 0) {
isOkay = true;
// Room Available
for (DataSnapshot childSnap : snapshot.getChildren()) {
database.getReference()
.child("users")
.child(childSnap.getKey())
.child("incoming")
.setValue(username);
database.getReference()
.child("users")
.child(childSnap.getKey())
.child("status")
.setValue(1);
Intent intent = new Intent(ConnectingActivity.this, CallActivity.class);
String incoming = childSnap.child("incoming").getValue(String.class);
String createdBy = childSnap.child("createdBy").getValue(String.class);
boolean isAvailable = childSnap.child("isAvailable").getValue(Boolean.class);
intent.putExtra("username", username);
intent.putExtra("incoming", incoming);
intent.putExtra("createdBy", createdBy);
intent.putExtra("isAvailable", isAvailable);
startActivity(intent);
finish();
}
} else {
// Not Available HashMap<String, Object> room = new HashMap<>();
room.put("incoming", username);
room.put("createdBy", username);
room.put("isAvailable", true);
room.put("status", 0); database.getReference()
.child("users")
.child(username)
.setValue(room).addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void unused) {
database.getReference()
.child("users")
.child(username).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull @NotNull DataSnapshot snapshot) {
if (snapshot.child("status").exists()) {
if (snapshot.child("status").getValue(Integer.class) == 1) { if (isOkay)
return; isOkay = true;
Intent intent = new Intent(ConnectingActivity.this, CallActivity.class);
String incoming = snapshot.child("incoming").getValue(String.class);
String createdBy = snapshot.child("createdBy").getValue(String.class);
boolean isAvailable = snapshot.child("isAvailable").getValue(Boolean.class);
intent.putExtra("username", username);
intent.putExtra("incoming", incoming);
intent.putExtra("createdBy", createdBy);
intent.putExtra("isAvailable", isAvailable);
startActivity(intent);
finish();
}
}
} @Override
public void onCancelled(@NonNull @NotNull DatabaseError error) { }
});
}
}); }
} @Override
public void onCancelled(@NonNull @NotNull DatabaseError error) { }
});
}
}
CloudDB:
Code:
import android.content.Context;
import android.util.Log;import com.huawei.agconnect.AGCRoutePolicy;
import com.huawei.agconnect.AGConnectInstance;
import com.huawei.agconnect.AGConnectOptionsBuilder;
import com.huawei.agconnect.auth.AGConnectAuth;
import com.huawei.agconnect.cloud.database.AGConnectCloudDB;
import com.huawei.agconnect.cloud.database.CloudDBZone;
import com.huawei.agconnect.cloud.database.CloudDBZoneConfig;
import com.huawei.agconnect.cloud.database.CloudDBZoneQuery;
import com.huawei.agconnect.cloud.database.exceptions.AGConnectCloudDBException;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;import static android.content.ContentValues.TAG;public class CloudDB { private Context context;
private static CloudDB instance; private AGConnectCloudDB mCloudDB;
private CloudDBZoneConfig mConfig;
private CloudDBZone mCloudDBZone; private CloudDB(Context context) {
this.context=context;
} public static CloudDB getInstance(Context context) {
if (instance==null)instance=new CloudDB(context);
return instance;
} public CloudDB initAGConnectCloudDB() {
AGConnectCloudDB.initialize(context);
return this;
} public CloudDB createCloudDb(){ AGConnectInstance instance = AGConnectInstance.buildInstance(new AGConnectOptionsBuilder().setRoutePolicy(AGCRoutePolicy.CHINA).build(mContext));
mCloudDB = AGConnectCloudDB.getInstance(instance, AGConnectAuth.getInstance(instance));
mCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo()); return this;
} public void configCloudDb(){
mConfig = new CloudDBZoneConfig("QuickStartDemo",
CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC);
mConfig.setPersistenceEnabled(true);
Task<CloudDBZone> openDBZoneTask = mCloudDB.openCloudDBZone2(mConfig, true);
openDBZoneTask.addOnSuccessListener(new OnSuccessListener<CloudDBZone>() {
@Override
public void onSuccess(CloudDBZone cloudDBZone) {
Log.i(TAG, "open cloudDBZone success");
mCloudDBZone = cloudDBZone;
// Add subscription after opening cloudDBZone success
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.w(TAG, "open cloudDBZone failed for " + e.getMessage());
}
});
} public void upsertBookInfos(BookInfo bookInfo) {
if (mCloudDBZone == null) {
Log.w(TAG, "CloudDBZone is null, try re-open it");
return;
}
Task<Integer> upsertTask = mCloudDBZone.executeUpsert(bookInfo);
upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer cloudDBZoneResult) {
Log.i(TAG, "Upsert " + cloudDBZoneResult + " records");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
mUiCallBack.updateUiOnError("Insert book info failed");
}
});
} public void viewCloudDbData(){
private OnSnapshotListener<BookInfo> mSnapshotListener = new OnSnapshotListener<BookInfo>() {
@Override
public void onSnapshot(CloudDBZoneSnapshot<BookInfo> cloudDBZoneSnapshot, AGConnectCloudDBException e) {
if (e != null) {
Log.w(TAG, "onSnapshot: " + e.getMessage());
return;
}
CloudDBZoneObjectList<BookInfo> snapshotObjects = cloudDBZoneSnapshot.getSnapshotObjects();
List<BookInfo> bookInfos = new ArrayList<>();
try {
if (snapshotObjects != null) {
while (snapshotObjects.hasNext()) {
BookInfo bookInfo = snapshotObjects.next();
bookInfos.add(bookInfo);
updateBookIndex(bookInfo);
}
}
mUiCallBack.onSubscribe(bookInfos);
} catch (AGConnectCloudDBException snapshotException) {
Log.w(TAG, "onSnapshot:(getObject) " + snapshotException.getMessage());
} finally {
cloudDBZoneSnapshot.release();
}
}
};
} public void addSubscription() {
if (mCloudDBZone == null) {
Log.w(TAG, "CloudDBZone is null, try re-open it");
return;
} try {
CloudDBZoneQuery<BookInfo> snapshotQuery = CloudDBZoneQuery.where(BookInfo.class)
.equalTo(BookEditFields.SHADOW_FLAG, true);
mRegister = mCloudDBZone.subscribeSnapshot(snapshotQuery,
CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY, mSnapshotListener);
} catch (AGConnectCloudDBException e) {
Log.w(TAG, "subscribeSnapshot: " + e.getMessage());
}
}
}
Xml layout DataBinding
To include data binding in the UI, enclose all content with <layout></layout>.
The ViewModel is introduced to the layout in the <data></data> section, as shown. Ensure that the type value points to the specific folder that has the required ViewModel.
App Build Result
Tips and Tricks
Identity Kit displays the HUAWEI ID registration or sign-in page first. The user can use the functions provided by Identity Kit only after signing in using a registered HUAWEI ID.
Conclusion
In this article, we have learned how to integrate Huawei Cloud DB using Webrtc Video Call in Android application. After completely read this article user can easily implement Huawei CloudDB in the Directory App android application using Kotlin.
Thanks for reading this article. Be sure to like and comment to this article, if you found it helpful. It means a lot to me.
References
HMS Docs:
https://developer.huawei.com/consum.../HMSCore-Guides/introduction-0000001050048870
https://developer.huawei.com/consum...des/agc-clouddb-introduction-0000001054212760
Cloud DB Kit Training Video:
https://developer.huawei.com/consumer/en/training/course/video/101628259491513909
Overview
In this article, I will create a Directory android application using Webrtc Video Calling App in which I will integrate HMS Core kits such as HMS Account, AuthService, Identity Kit, Firebase Auth, Firebase Realtime DB and CloudDB .
App will make use of android MVVM clean architecture using Jetpack components such as DataBinding, AndroidViewModel, Observer, LiveData and much more.
In this article we are going to implement DataBinding using Observable pattern.
Huawei Analytics Kit Introduction
Huawei Analytics Kit is a one-stop user behavior analysis platform for products such as mobile apps, web apps, quick apps, quick games, and mini-programs. It offers scenario-specific data collection, management, analysis, and usage, helping enterprises achieve effective user acquisition, product optimization, precise operations, and business growth..
Flexible synchronization modes:
Cloud DB supports cache and local data synchronization modes. In cache mode, data on the device is a subset of data on the cloud. If persistent cache is allowed, query results will be automatically cached on the device. In local mode, data is stored locally and is not synchronized to the cloud.
Powerful query capability:
Cloud DB supports various predicate query methods. Multiple chain filtering conditions can be used to filter and sort returned results, and limit the number of objects in the returned result set. In cache mode, data can be queried from the Cloud DB zone on the cloud or that on the local device. In local mode, data is directly queried from the Cloud DB zone on the local device.
Real-time update:
In cache mode of Cloud DB, you can listen on data as needed and use the data synchronization function of Cloud DB to update changed data between the device and cloud in real time.
Offline operations:
In cache mode of Cloud DB, if persistent cache is allowed, the application query is automatically changed from Cloud DB to the local host after the device gets offline. All data modified locally will be automatically synchronized to Cloud DB after the device gets online.
Scalability:
Cloud DB provides powerful HUAWEI CLOUD infrastructure functions, including automatic multi-region data replication, consistency assurance, atomic batch operations, and transaction support.
Security level:
Cloud DB supports device-cloud data full encryption management, triple authentication by app, user, and service, and role-based permission management to ensure data security.
WebRTC Service Introduction
WebRTC is a free and open-source project providing web browsers and mobile applications with real-time communication via application programming interfaces.
Prerequisite
Huawei Phone EMUI 3.0 or later.
Non-Huawei phones Android 4.4 or later (API level 19 or higher).
HMS Core APK 4.0.0.300 or later
Android Studio
AppGallery Account
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 and download the configuration file.
3. Navigate to General Information, and then provide Data Storage location.
App Development
Add Required Dependencies:
Launch Android studio and create a new project. Once the project is ready.
Add following dependency for HMS Kits
Code:
//HMS Kits
implementation 'com.huawei.hms:hianalytics:5.0.3.300'
implementation 'com.huawei.agconnect:agconnect-crash:1.4.1.300'
implementation 'com.huawei.agconnect:agconnect-core:1.5.0.300'
implementation 'com.huawei.hms:hwid:5.3.0.302'
implementation 'com.huawei.hms:identity:5.3.0.300'
implementation 'com.huawei.agconnect:agconnect-cloud-database:1.5.0.300'Copy codeCopy code//Google Firebase
implementation platform('com.google.firebase:firebase-bom:28.4.1')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-auth'
implementation 'com.google.firebase:firebase-database'
implementation 'com.google.android.gms:play-services-auth:19.2.0'
implementation 'com.airbnb.android:lottie:4.1.0'
implementation 'com.mikhaellopez:circularimageview:4.3.0'
implementation 'com.kaopiz:kprogresshud:1.2.0'
implementation 'com.google.android.gms:play-services-ads:20.4.0' implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
Navigate to the Gradle scripts folder and open build.gradle (project: app)
Code:
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
classpath 'com.huawei.agconnect:agcp:1.4.2.300'
Configure AndroidManifest.xml.
Code:
<meta-data
android:name="install_channel"
android:value="AppGallery" />
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="104460711" />
</application>
Code Implementation
Created following package model, event, viewmodel.
ViewModel: The ViewModel makes it easy to update data changes on the UI.Create a package named viewmodel in your main folder.Then create a new file and name it LoginViewModel.kt along with their FactoryViewModelProviders.
MainActivity.kt:
Code:
package com.hms.directoryclass MainActivity : AppCompatActivity(), ActivityNavigation { private lateinit var viewModel: LoginViewModel
private lateinit var dataBinding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) val viewModel: LoginViewModel by lazy {
val activity = requireNotNull(this) {}
ViewModelProviders.of(this, LoginViewModelFactory(activity.application))
.get(LoginViewModel::class.java)
} dataBinding.loginViewModel = viewModel
dataBinding.lifecycleOwner = this
viewModel.startActivityForResultEvent.setEventReceiver(this, this)
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
viewModel.onResultFromActivity(requestCode, data)
super.onActivityResult(requestCode, resultCode, data)
}}
LoginViewModel.kt:
Code:
package com.hms.directory.viewmodel
@SuppressLint("StaticFieldLeak")
class LoginViewModel(application: Application) : AndroidViewModel(application), Observable { private val context = getApplication<Application>().applicationContext
private var mAuthManager: AccountAuthService? = null
private var mAuthParam: AccountAuthParams? = null val startActivityForResultEvent = LiveMessageEvent<ActivityNavigation>() fun login() {
val intent = Intent(context, OrderActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent) /* mAuthParam = AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM)
.setIdToken()
.setAccessToken()
.createParams()
mAuthManager = AccountAuthManager.getService(Activity(), mAuthParam)
startActivityForResultEvent.sendEvent {
startActivityForResult(
mAuthManager?.signInIntent,
HMS_SIGN_IN
)
}*/
} fun onResultFromActivity(requestCode: Int, data: Intent?) {
when (requestCode) {
HMS_SIGN_IN -> {
val authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data)
onCompleteLogin(authAccountTask)
}
}
} private fun onCompleteLogin(doneTask: Task<AuthAccount>) {
if (doneTask.isSuccessful) {
val authAccount = doneTask.result
Log.d("LoginViewModel", "SigIn Success")
context.startActivity(Intent(context, ContactListActivity::class.java)) } else {
Log.d("LoginViewModel", "SigIn Error")
}
} override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
} override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
}}
ContactActivity.kt:
Code:
public class ContactListActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact_list); // Load contacts from file
Contacts.loadData(this); // Set up recycler view and fill it with all the contacts
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.contact_list);
recyclerView.setAdapter(new ContactListAdapter(this, Contacts.LIST));
AGConnectCrash.getInstance().enableCrashCollection(false);
//Crash application
AGConnectCrash.getInstance().testIt(this)
}
LoginFireBaseActivity.java
Code:
package com.hms.directory.app.call;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.GoogleAuthProvider;
import com.google.firebase.database.FirebaseDatabase;
import com.hms.corrierapp.R;
import com.hms.directory.app.call.models.User;import org.jetbrains.annotations.NotNull;public class LoginActivity extends AppCompatActivity { GoogleSignInClient mGoogleSignInClient;
int RC_SIGN_IN = 11;
FirebaseAuth mAuth;
FirebaseDatabase database; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_goole); mAuth = FirebaseAuth.getInstance();
if (mAuth.getCurrentUser() != null) {
goToNextActivity();
} database = FirebaseDatabase.getInstance(); GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("1016048264402-439a9aamtpiajbgqeqg24qkum2bb7fmh.apps.googleusercontent.com")
.requestEmail()
.build(); mGoogleSignInClient = GoogleSignIn.getClient(this, gso); findViewById(R.id.loginBtn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = mGoogleSignInClient.getSignInIntent(); startActivityForResult(intent, RC_SIGN_IN);
//startActivity(new Intent(LoginActivity.this, MainActivity.class));
}
});
} void goToNextActivity() {
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
} @Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data); if (requestCode == RC_SIGN_IN) {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
GoogleSignInAccount account = task.getResult();
authWithGoogle(account.getIdToken());
}
} void authWithGoogle(String idToken) {
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull @NotNull Task<AuthResult> task) {
if (task.isSuccessful()) {
FirebaseUser user = mAuth.getCurrentUser();
User firebaseUser = new User(user.getUid(), user.getDisplayName(), user.getPhotoUrl().toString(), "Unknown", 500);
database.getReference()
.child("profiles")
.child(user.getUid())
.setValue(firebaseUser).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull @NotNull Task<Void> task) {
if (task.isSuccessful()) {
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finishAffinity();
} else {
Toast.makeText(LoginActivity.this, task.getException().getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
}
});
//Log.e("profile", user.getPhotoUrl().toString());
} else {
Log.e("err", task.getException().getLocalizedMessage());
}
}
});
}
}
CallConnectingActivity.java
Code:
public class ConnectingActivity extends AppCompatActivity { ActivityConnectingBinding binding;
FirebaseAuth auth;
FirebaseDatabase database;
boolean isOkay = false; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityConnectingBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); auth = FirebaseAuth.getInstance();
database = FirebaseDatabase.getInstance(); String profile = getIntent().getStringExtra("profile");
Glide.with(this)
.load(profile)
.into(binding.profile); String username = auth.getUid(); database.getReference().child("users")
.orderByChild("status")
.equalTo(0).limitToFirst(1)
.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull @NotNull DataSnapshot snapshot) {
if (snapshot.getChildrenCount() > 0) {
isOkay = true;
// Room Available
for (DataSnapshot childSnap : snapshot.getChildren()) {
database.getReference()
.child("users")
.child(childSnap.getKey())
.child("incoming")
.setValue(username);
database.getReference()
.child("users")
.child(childSnap.getKey())
.child("status")
.setValue(1);
Intent intent = new Intent(ConnectingActivity.this, CallActivity.class);
String incoming = childSnap.child("incoming").getValue(String.class);
String createdBy = childSnap.child("createdBy").getValue(String.class);
boolean isAvailable = childSnap.child("isAvailable").getValue(Boolean.class);
intent.putExtra("username", username);
intent.putExtra("incoming", incoming);
intent.putExtra("createdBy", createdBy);
intent.putExtra("isAvailable", isAvailable);
startActivity(intent);
finish();
}
} else {
// Not Available HashMap<String, Object> room = new HashMap<>();
room.put("incoming", username);
room.put("createdBy", username);
room.put("isAvailable", true);
room.put("status", 0); database.getReference()
.child("users")
.child(username)
.setValue(room).addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void unused) {
database.getReference()
.child("users")
.child(username).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull @NotNull DataSnapshot snapshot) {
if (snapshot.child("status").exists()) {
if (snapshot.child("status").getValue(Integer.class) == 1) { if (isOkay)
return; isOkay = true;
Intent intent = new Intent(ConnectingActivity.this, CallActivity.class);
String incoming = snapshot.child("incoming").getValue(String.class);
String createdBy = snapshot.child("createdBy").getValue(String.class);
boolean isAvailable = snapshot.child("isAvailable").getValue(Boolean.class);
intent.putExtra("username", username);
intent.putExtra("incoming", incoming);
intent.putExtra("createdBy", createdBy);
intent.putExtra("isAvailable", isAvailable);
startActivity(intent);
finish();
}
}
} @Override
public void onCancelled(@NonNull @NotNull DatabaseError error) { }
});
}
}); }
} @Override
public void onCancelled(@NonNull @NotNull DatabaseError error) { }
});
}
}
CloudDB:
Code:
import android.content.Context;
import android.util.Log;import com.huawei.agconnect.AGCRoutePolicy;
import com.huawei.agconnect.AGConnectInstance;
import com.huawei.agconnect.AGConnectOptionsBuilder;
import com.huawei.agconnect.auth.AGConnectAuth;
import com.huawei.agconnect.cloud.database.AGConnectCloudDB;
import com.huawei.agconnect.cloud.database.CloudDBZone;
import com.huawei.agconnect.cloud.database.CloudDBZoneConfig;
import com.huawei.agconnect.cloud.database.CloudDBZoneQuery;
import com.huawei.agconnect.cloud.database.exceptions.AGConnectCloudDBException;
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;import static android.content.ContentValues.TAG;public class CloudDB { private Context context;
private static CloudDB instance; private AGConnectCloudDB mCloudDB;
private CloudDBZoneConfig mConfig;
private CloudDBZone mCloudDBZone; private CloudDB(Context context) {
this.context=context;
} public static CloudDB getInstance(Context context) {
if (instance==null)instance=new CloudDB(context);
return instance;
} public CloudDB initAGConnectCloudDB() {
AGConnectCloudDB.initialize(context);
return this;
} public CloudDB createCloudDb(){ AGConnectInstance instance = AGConnectInstance.buildInstance(new AGConnectOptionsBuilder().setRoutePolicy(AGCRoutePolicy.CHINA).build(mContext));
mCloudDB = AGConnectCloudDB.getInstance(instance, AGConnectAuth.getInstance(instance));
mCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo()); return this;
} public void configCloudDb(){
mConfig = new CloudDBZoneConfig("QuickStartDemo",
CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC);
mConfig.setPersistenceEnabled(true);
Task<CloudDBZone> openDBZoneTask = mCloudDB.openCloudDBZone2(mConfig, true);
openDBZoneTask.addOnSuccessListener(new OnSuccessListener<CloudDBZone>() {
@Override
public void onSuccess(CloudDBZone cloudDBZone) {
Log.i(TAG, "open cloudDBZone success");
mCloudDBZone = cloudDBZone;
// Add subscription after opening cloudDBZone success
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.w(TAG, "open cloudDBZone failed for " + e.getMessage());
}
});
} public void upsertBookInfos(BookInfo bookInfo) {
if (mCloudDBZone == null) {
Log.w(TAG, "CloudDBZone is null, try re-open it");
return;
}
Task<Integer> upsertTask = mCloudDBZone.executeUpsert(bookInfo);
upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer cloudDBZoneResult) {
Log.i(TAG, "Upsert " + cloudDBZoneResult + " records");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
mUiCallBack.updateUiOnError("Insert book info failed");
}
});
} public void viewCloudDbData(){
private OnSnapshotListener<BookInfo> mSnapshotListener = new OnSnapshotListener<BookInfo>() {
@Override
public void onSnapshot(CloudDBZoneSnapshot<BookInfo> cloudDBZoneSnapshot, AGConnectCloudDBException e) {
if (e != null) {
Log.w(TAG, "onSnapshot: " + e.getMessage());
return;
}
CloudDBZoneObjectList<BookInfo> snapshotObjects = cloudDBZoneSnapshot.getSnapshotObjects();
List<BookInfo> bookInfos = new ArrayList<>();
try {
if (snapshotObjects != null) {
while (snapshotObjects.hasNext()) {
BookInfo bookInfo = snapshotObjects.next();
bookInfos.add(bookInfo);
updateBookIndex(bookInfo);
}
}
mUiCallBack.onSubscribe(bookInfos);
} catch (AGConnectCloudDBException snapshotException) {
Log.w(TAG, "onSnapshot:(getObject) " + snapshotException.getMessage());
} finally {
cloudDBZoneSnapshot.release();
}
}
};
} public void addSubscription() {
if (mCloudDBZone == null) {
Log.w(TAG, "CloudDBZone is null, try re-open it");
return;
} try {
CloudDBZoneQuery<BookInfo> snapshotQuery = CloudDBZoneQuery.where(BookInfo.class)
.equalTo(BookEditFields.SHADOW_FLAG, true);
mRegister = mCloudDBZone.subscribeSnapshot(snapshotQuery,
CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY, mSnapshotListener);
} catch (AGConnectCloudDBException e) {
Log.w(TAG, "subscribeSnapshot: " + e.getMessage());
}
}
}
Xml layout DataBinding
To include data binding in the UI, enclose all content with <layout></layout>.
The ViewModel is introduced to the layout in the <data></data> section, as shown. Ensure that the type value points to the specific folder that has the required ViewModel.
App Build Result
Tips and Tricks
Identity Kit displays the HUAWEI ID registration or sign-in page first. The user can use the functions provided by Identity Kit only after signing in using a registered HUAWEI ID.
Conclusion
In this article, we have learned how to integrate Huawei Cloud DB using Webrtc Video Call in Android application. After completely read this article user can easily implement Huawei Crash Kit in the Directory App android application using Kotlin.
Thanks for reading this article. Be sure to like and comment to this article, if you found it helpful. It means a lot to me.
References
HMS Docs:
https://developer.huawei.com/consum.../HMSCore-Guides/introduction-0000001050048870
https://developer.huawei.com/consum...des/agc-clouddb-introduction-0000001054212760