DEV Community

vivek yadav
vivek yadav

Posted on

 

Expert: BestPal (Chatting) application using Huawei CloudDB, Auth service, Cloud Function and Push Kit

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.
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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

Create 3 object types

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

Image description

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

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 fullscreen mode Exit fullscreen mode

Image description

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

Enter fullscreen mode Exit fullscreen mode

Image description

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

        });
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

Image description

Generate push token

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

Enter fullscreen mode Exit fullscreen mode

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

    }

}

Enter fullscreen mode Exit fullscreen mode

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

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

}

Enter fullscreen mode Exit fullscreen mode

Image description

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

Enter fullscreen mode Exit fullscreen mode

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


}

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

Image description

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

Enter fullscreen mode Exit fullscreen mode

Image description

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/

Top comments (0)

Dark Mode

🌚 Friends don't let friends browse without dark mode.

Good news! You can update to dark mode in your DEV settings.