[Huawei Developers] vivek_yadav:
Introduction

In this article, we can learn that chat option between two people, they can share text between each other. The application needs to have instant messaging so once a user sends the message to a friend over the application, the friend will receive the push notification at the given time. The quintessence of an app like instant application is available and react to ongoing actions. Also, push notifications can be an excellent promotional tool that can be used to inform users about updates and new functionalities.

Huawei Kits Used​

Huawei Cloud DB

Huawei Auth Service

Huawei Cloud function.

Huawei Push Kit

Huawei API Used​

Huawei CloudDB API - Cloud DB is used to store users data, users chat and also used to manage users chat history with other users.

a) Upsert

i) Insert data of the users from the profile.

ii) Create and insert room id, room id is consider as a reference between two users chat. Using room id will store all the respective chat data in the DB.

iii) Insert Chat data between two users based on the room id.

b) Query

i) Get list of Contacts for chat.

ii) Get list of users with whom logged in user chatted before.

ii) Get details of the chat screen with all the chat messages which includes images, text and location.

Huawei Auth Service – Using the Auth Service we are registering the user on the Ecosystem. We are using the Phone number auth service for the same to receive the OTP and verify the user here.

Huawei Cloud function – We are triggering the Huawei Push notification system using cloud function for the same.

Huawei Push kit - Push kit is used to push notification of message to other user. So when one user send message it will notify other user through push notification only.

Used the rest end point for the cloud function to send the push notification once the message is end, trigger from the device.

On HMSMessage Received This is once parsing the data as per our need on the implementation, so we need to parse image and location when shared by other success.

Database structure​

View attachment 5527967

Now it's time to create project on Huawei console and development​

Integration Preparations​

You must complete the following preparations:

Register as a developer on Huawei console.

Create a project and an app in AppGallery Connect.

Generate and configure the signing certificate fingerprint.

Enable Auth service, Push and Cloud DB.

For details, refer to Configuring App Information in AppGallery Connect for HMS

First create cloud DB Zones

Create 3 object types

ChatRoomId: contain all chatting room id.

User: all register user details.

UserChat: all users chat details.

Let's start development with Login Page

We will login with Phone number using HMS Auth service.

Enable Phone number Authentication mode as shown in below image.

Add dependency

// HMS dependencies
implementation "com.huawei.agconnect:agconnect-database:$rootProject.ext.agdatabase"
implementation "com.huawei.agconnect:agconnect-auth:$rootProject.ext.agauth"
implementation "com.huawei.hms:push:$rootProject.ext.pushkit"

Enter the valid phone number, we will get OTP on same number using below method.

GET OTP

VerifyCodeSettings settings = new VerifyCodeSettings.Builder()
.action(VerifyCodeSettings.ACTION_REGISTER_LOGIN)
.sendInterval(30)
.locale(Locale.getDefault())
.build();

Task<VerifyCodeResult> task = AGConnectAuth.getInstance().requestVerifyCode(countryCodeStr, phoneNumberStr, settings);
task.addOnSuccessListener(TaskExecutors.immediate(), verifyCodeResult -> {
if (null != verifyCodeResult) {
verifyCodeResultMutableLiveData.postValue(verifyCodeResult);
}
});
task.addOnFailureListener(e ->
AppLog.logE(TAG, "onFailure: " + e.getCause()));

Verify Contact details

PhoneUser phoneUser = new PhoneUser.Builder()
.setCountryCode(countryCodeStr)
.setPhoneNumber(phoneNumberStr)
.setVerifyCode(code)
.build();
AGConnectAuth.getInstance().createUser(phoneUser)
.addOnSuccessListener(signInResult -> {
if (signInResult != null) {
User user = new User();
user.setUsername(signInResult.getUser().getDisplayName());
user.setPhoneNumber(phoneNumberStr);
userMutableLiveData.postValue(user);
}
})
.addOnFailureListener(e -> {
Log.e(TAG, "verifyContactDetails: " + e.getStackTrace());
User user = new User();
user.setPhoneNumber(phoneNumberStr);
userMutableLiveData.setValue(user);

});

After verify, user can authenticate successfully.

Now we have to complete the user profile, as in below picture you can see we have only phone number.

Create/ Update profile

public void saveUser(User user, Context context) {
CloudDBHelper.getInstance().openDb((isConnected, cloudDBZone) -> {
if (isConnected && cloudDBZone != null) {
if (cloudDBZone == null) {
return;
} else {
Task<Integer> insertTask = cloudDBZone.executeUpsert(user);
insertTask.addOnSuccessListener(integer -> {
userMutableLiveData.setValue(true);
CloudDBHelper.getInstance().closeDb(context);
}).addOnFailureListener(e -> {
userMutableLiveData.setValue(false);
CloudDBHelper.getInstance().closeDb(context);
});
}
}
});
}

Generate push token

GetToken getToken = new GetToken(app_id, UserProfileActivity.this);
getToken.setGetTokenListener(this);
getToken.execute();

GetToken class is a service class to generate push token

public class GetToken extends AsyncTask<Void, Void, String> {

private static final String TAG = GetToken.class.getSimpleName();
private Context context;
private String appId;

private GetTokenListener getTokenListener;

public GetToken(String appId, Context context) {
this.appId = appId;
this.context = context;
}

public void setGetTokenListener(GetTokenListener getTokenListener) {
this.getTokenListener = getTokenListener;
}

@Override
protected void onPreExecute() {
super.onPreExecute();
}

@Override
protected String doInBackground(Void... voids) {
try {
String pushToken = HmsInstanceId.getInstance(context).getToken(appId, HmsMessaging.DEFAULT_TOKEN_SCOPE);
AppLog.logD(TAG, pushToken);
getTokenListener.getToken(pushToken);
return pushToken;
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}

@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);

}

}

It will create the user profile and update data into Cloud DB.

Start chat for first time

We have to create room id for chatting between two people.

public void callCreateRoomId(final String userMobileTo, final String userMobileFrom) {

CloudDBHelper.getInstance().openDb(new OnDBZoneOpen() {
@Override
public void isDBZoneOpen(boolean isConnected, CloudDBZone cloudDBZone) {
if (cloudDBZone != null) {
ChatRoomId roomId = new ChatRoomId();
mRoomDataIndex = mRoomDataIndex + 1;
AppLog.logE(TAG, "New ROOM IS WILL BE ===> " + mRoomDataIndex);
roomId.setRoom_id(String.valueOf(mRoomDataIndex));
roomId.setUser_mobile_to(userMobileTo);
roomId.setUser_mobile_from(userMobileFrom);
roomId.setUpdate_shadow_flag(true);
Task<Integer> insertTask = cloudDBZone.executeUpsert(roomId);
insertTask.addOnSuccessListener(insertSuccessListener(roomId))
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
AppLog.logE(TAG, "Exception in creating room id " + e.getLocalizedMessage());
}
}).addOnCanceledListener(new OnCanceledListener() {
@Override
public void onCanceled() {
AppLog.logE(TAG, "Inside on cancel listener");
}
});
} else {
if (mOnApiError != null) {
mOnApiError.onError("Cloud database zone is null", new Throwable("CloudDBZone is null"));
}
}
}
});

}

Get previous chats from cloudDB

public void getUserChatByRoomID(String roomId, Context context) {
CloudDBZoneQuery<UserChat> query = CloudDBZoneQuery.where(UserChat.class).equalTo(DBConstants.roomId, roomId).orderByAsc(DBConstants.MESSAGE_TIMESTAMP);
getUserChat(query, context);
}

private void getUserChat(CloudDBZoneQuery<UserChat> userQuery, Context context) {
CloudDBHelper.getInstance().openDb((isConnected, cloudDBZone) -> {
Task<CloudDBZoneSnapshot<UserChat>> queryTask = cloudDBZone.executeQuery(userQuery,
CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY);
queryTask.addOnSuccessListener(userChatCloudDBZoneSnapshot -> {
processSnapShot(userChatCloudDBZoneSnapshot.getSnapshotObjects(), context);
});
});
}

private void processSnapShot(CloudDBZoneObjectList<UserChat> userCloudDBZoneSnapshot, Context context) {
if (userCloudDBZoneSnapshot != null) {
ArrayList<UserChat> users = new ArrayList<>();
while (userCloudDBZoneSnapshot.hasNext()) {
UserChat user = null;
try {
user = userCloudDBZoneSnapshot.next();
users.add(user);
} catch (AGConnectCloudDBException e) {
e.printStackTrace();
CloudDBHelper.getInstance().closeDb(context);
}

}
userChatMutableLiveData.setValue(users);
CloudDBHelper.getInstance().closeDb(context);
}
}

Start Sending chat messages

messageType: user can send message in text, image, location or in video Types.

private void setMessage(String messageType) {

Util.showProgressBar(MessageActivity.this);
UserChat userChat = new UserChat();
userChat.setRoom_id(roomId);
userChat.setMessage_timestamp(Long.parseLong(Util.getTimeStamp()));
userChat.setChat_id(Util.getRandomNumber());
userChat.setReceiver_name(receiverText);
userChat.setReceiver_phone(receiverPhoneNumber);
userChat.setSender_name(ChitChatSharedPref.getInstance().getString(Constants.USER_NAME, ""));
userChat.setSender_phone(ChitChatSharedPref.getInstance().getString(Constants.PHONE_NUMBER, ""));

userChat.setMessage_type(messageType);
switch (messageType) {

case Constants.MESSAGE_TYPE_TEXT:
userChat.setMessage_data(textSend.getText().toString());
messageViewModel.saveUserChat(userChat);
messageViewModel.userUpdatedSuccessfully.observe(MessageActivity.this, aBoolean -> {
if (aBoolean) {
Util.stopProgressBar();
getChatList();
} else {
Util.stopProgressBar();
}
});
break;
}
messageViewModel.queryForToken(receiverPhoneNumber, MessageActivity.this);

}

It will save user data into cloud dB

public void saveUserChat(UserChat userChat) {
CloudDBHelper.getInstance().openDb((isConnected, cloudDBZone) -> {
if (cloudDBZone != null) {
Task<Integer> insertTask = cloudDBZone.executeUpsert(userChat);
insertTask
.addOnSuccessListener(integer ->
userUpdatedSuccessfully.setValue(true))
.addOnFailureListener(e -> {
userUpdatedSuccessfully.setValue(false);
AppLog.logE(TAG, e.getMessage());
});

} else {
userUpdatedSuccessfully.setValue(false);
}
});
}

It's time to send push notification

public void queryForToken(String phoneNumber, Context context) {
CloudDBZoneQuery<User> query = CloudDBZoneQuery.where(User.class).equalTo(DBConstants.userNumber, phoneNumber);
processNumberCheck(query, context);
}

messageViewModel.tokenMutableLiveData.observe(MessageActivity.this, s -> {
PushApis pushApis = new PushApis(MessageActivity.this);
if (messageType.equalsIgnoreCase(Constants.MESSAGE_TYPE_TEXT)) {
pushApis.sendPushNotification(roomId, messageType, "104739093", MessageActivity.this.textSend.getText().toString(), s);
textSend.setText("");
}
});

Setting up push messaging API's

public class PushApis {

private Context context;

public PushApis(Context context) {
this.context = context;
}

public void sendPushNotification(String chatId, String message, String appId, String messageData, String userPushTokens) {
try {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
String response = "";
URL url = new URL(Constants.TOKEN_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("POST", "/oauth2/v3/token HTTP/1.1");
connection.setRequestProperty("Host", "oauth-login.cloud.huawei.com");
HashMap<String, String> params = new HashMap<>();
params.put("grant_type", "client_credentials");
params.put("client_secret", Constants.CLIENT_SECRET);
params.put("client_id", Constants.CLIENT_ID);
String postDataLength = getDataString(params);
OutputStream os = connection.getOutputStream();
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));
writer.write(postDataLength);

writer.flush();
writer.close();
os.close();
int responseCode = connection.getResponseCode();

if (responseCode == HttpsURLConnection.HTTP_OK) {
String line;

BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
while ((line = br.readLine()) != null) {
response += line;
}
} else {
response = "";

}
AppLog.logE("Response", response);
Gson gson = new Gson();
BearerRequest bearerRequest = gson.fromJson(response, BearerRequest.class);
triggerPush(bearerRequest.getAccess_token(), appId, chatId, message, messageData, userPushTokens);
} catch (Exception e) {
e.printStackTrace();
}
}

private String getDataString(HashMap<String, String> params) throws UnsupportedEncodingException {
StringBuilder result = new StringBuilder();
boolean first = true;
for (Map.Entry<String, String> entry : params.entrySet()) {
if (first)
first = false;
else
result.append("&");
result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
result.append("=");
result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
}
return result.toString();
}

private void triggerPush(String bearer, String appId, String chatId, String messageType, String messageData, String userPushTokens) {
try {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
String response = null;
URL url = new URL("https://push-api.cloud.huawei.com/v1/" + appId + "/messages:send");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Authorization", "Bearer " + bearer);
connection.setRequestProperty("Host", "oauth-login.cloud.huawei.com");
connection.setRequestProperty("POST", "/oauth2/v2/token HTTP/1.1");
OutputStream os = connection.getOutputStream();
Data data = new Data();
data.message = messageType;
data.roomId = chatId;
data.messageData = messageData;
data.sender_name = senderName;
data.sender_phone = senderPhone;
data.title = context.getResources().getString(R.string.app_name);

ArrayList<String> token = new ArrayList<>();
token.add(userPushTokens);

Message message = new Message();
message.tokens = token;
message.data = data.toString();

PushMessageRequest pushMessageRequest = new PushMessageRequest();

pushMessageRequest.message = message;
pushMessageRequest.validate_only = false;

BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));

Gson gson = new Gson();
JSONObject jsonObject = new JSONObject(gson.toJson(pushMessageRequest, PushMessageRequest.class));
writer.write(jsonObject.toString());
writer.flush();
writer.close();
os.close();
int responseCode = connection.getResponseCode();
String line = null;
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
while ((line = br.readLine()) != null) {
response += line;
}
AppLog.logE("Response", response);
} catch (Exception e) {
e.getStackTrace();
}
}
}

Conclusion

In this article, we have learned how we can create a simple messaging application with cloud dB, auth service and push kit. We can also use cloud storage for store profile picture, location, documents or audio and video files.

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.

Reference

https://developer.huawei.com/consumer/en/agconnect/cloud-base/

https://developer.huawei.com/consum...-Guides/service-introduction-0000001050040060

https://developer.huawei.com/consumer/en/agconnect/auth-service/