Expert: BestPal (Chatting) application using Huawei CloudDB, Auth service, Cloud Function and Push Kit
Tags: huawei, cloud, push, java
Published: 2022-02-04 09:05:45

**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**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9gkqbkxiq371sikog21.PNG)
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**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2da6fk13g56qgd9enec.png)

**Create 3 object types**

ChatRoomId: contain all chatting room id.
User: all register user details.
UserChat: all users chat details. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2sup3ze3r9eq8rctfjc8.png)

**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.  

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xo0j1b1804y1h7g4lqgp.png)

**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"
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ju0f6n7qp93ojsiib6h3.png)

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()));

```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tdl42h1e2tlb7qwu68n8.png)

**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);
                });
            }
        }
    });
}

```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6cn6zfogz77xe2u8qydc.png)

**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.**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0wu458yi8hslq7kjjv22.png)

**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"));
                }
            }
        }
    });

}

```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggsi3jndmdu0jdoatyx3.png)

**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("");
    }
});

```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qb86lmsyyizh1itwcr6m.png)

**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();
        }
    }
}

```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qcpyg0j00gn97ekr8j33.png)

**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/consumer/en/doc/development/HMSCore-Guides/service-introduction-0000001050040060

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