Developing a Serverless architecture requires the backend service tools. AppGallery Connect provides these tools such as Cloud Storage, Cloud DB, Cloud Function, and with these common tools we can build up a chat app by enabling our Pay-as-you-go in AppGallery Connect.

Service Scenario Description

Chat app will give you an insight about the Serverless components from Huawei Ecosystem such as Auth Service, Cloud Storage, Cloud DB, and Cloud Functions. Apart from that you will also get an idea about Location Kit, Map Kit, and Site Kit that can be used to the share the location, which is an important feature, on the app. The registration process involves the sign-in with the phone number and the OTP, and the save user profile on the database.

In this codelab we are implementing the following features with HMS kits.

Features

HMS Kits

Sign in with phone number

Auth Serivce

Chat with two different people

Cloud DB

Share the image

Cloud Storage and Cloud DB

Push notifications to the user

Push Kit

Share the current location or nearby landmark.

Map Kit, Location Kit, and Site Kit

What You Will Create

In this codelab, you will create a chat app demo project, use the APIs of some Huawei services such as Push Kit, Location Kit, Map Kit, Site Kit, Cloud storage, Cloud DB, and learn the MVVM concept. The message and files that can be shared between two different people are stored in the Cloud DB.

  1. The following figures are the screenshots of the chat app. The splash screen is displayed first followed by an onboarding screen, and then the sign-in screen is displayed.
  2. The following is the screen shot for the sign-in screen. After the phone number is entered, the OTP will be sent.
  3. As shown in the following figure, once the sign-in is successful, the user have to complete the following information so that the app can have the details of the user.
  4. The following screenshorts show the List of saved Contacts, history of the previous chat, and profile update page.
  5. The chat window can be used to share images, location, document, and others.
  6. Once the site, location, or image is updated, the chat window will be like this.

    A notification is pushed to the person once the message is shared, as shown in the following screenshot.

    Note: To maintain user privacy, we have blurred the portion of the screenshots wherever the user personal information is exposed throughout this document

Process flow Diagram

What You Will Learn

In this codelab, you will learn how to:

Hardware Requirements

Software Requirements

Enable HUAWEI Service(s) in AppGallery Connect:
To integrate HMS Core kits, you must complete the following preparations:

For details, please refer to Preparations for Integrating HUAWEI HMS Core.

Go to Project Setting > Manage APIs, and enable the API permission for the following kits.

Integrating Auth service

Auth service provides multiple modes of sign-in such as mobile number, Facebook, and Google. Here we will enable mobile number sign-in mode so that we can receive the OTP.

Step 1: Enable Auth service in AppGallery Connect.

Add the following dependancy:

implementation 'com.huawei.agconnect:agconnect-auth:<auth_service_version>'

Step 2: Check the mobile number.

A verification code is required for mobile number–based registration. The verification code will be sent to the users mobile number to ensure that the mobile number belongs to this user. Call VerifyCodeSettings.Builder to set the verification code, and call AGConnectAuth.requestVerifyCode to request a verification code.

Import required classes for Auth Service.

import com.huawei.agconnect.auth.AGConnectAuth; import com.huawei.agconnect.auth.PhoneUser; import com.huawei.agconnect.auth.VerifyCodeResult; import com.huawei.agconnect.auth.VerifyCodeSettings;

AuthServiceViewModel.java

public void getOTP(String countryCodeStr, String phoneNumberStr) { VerifyCodeSettings settings = new VerifyCodeSettings.Builder() .action(VerifyCodeSettings.ACTION_REGISTER_LOGIN) .sendInterval(30) .locale(Locale.getDefault()) .build(); Task 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())); }

LoginFragment. Java

Use the following snippnet for the sign-in button.

Util.showProgressBar(getActivity()); bundle.putString(Constants.PHONE_NUMBER, etPhoneNumber.getText().toString()); bundle.putString(Constants.COUNTRY_CODE, "+91"); authServiceViewModel.getOTP("+91", etPhoneNumber.getText().toString()); authServiceViewModel.verifyCodeResultMutableLiveData.observe(requireActivity(), verifyCodeResult -> { loginPhoneFragmentListener.setLoginPhoneFragmentListener(bundle); Util.stopProgressBar(); }); } else { Toast.makeText(requireContext(), "Please enter number", Toast.LENGTH_SHORT).show(); }

Step 3: Send the OTP to the phone after the phone number is set up.

AuthServiceViewModel.java

public void verifyContactDetails(String countryCodeStr, String phoneNumberStr, String code) { 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 -> { AppLog.logE(TAG, "verifyContactDetails: " + e.getStackTrace()); User user = new User(); user.setPhoneNumber(phoneNumberStr); userMutableLiveData.setValue(user); }); }

Step 4: Implement verification of OTP on fragment level.

LoginWithOTPFragment.java

Util.showProgressBar(getActivity()); Bundle bundle = new Bundle(); bundle.putString(Constants.COUNTRY_CODE, strCountryCode); bundle.putString(Constants.PHONE_NUMBER, strPhone); if (otpEditText.length() == 6) { authServiceViewModel.verifyContactDetails(strCountryCode, strPhone, otpEditText.getText().toString()); authServiceViewModel.userMutableLiveData.observe(requireActivity(), user -> { if (user != null) { loginWithOTPFragmentListener.setLoginWithOTPFragmentListener(bundle); } Util.stopProgressBar(); }); } });

If the number is correct:

@Override public void setLoginWithOTPFragmentListener(Bundle bundle) { String number = bundle.getString(Constants.PHONE_NUMBER); Intent intent = new Intent(LoginActivity.this, UserProfileActivity.class); intent.putExtra(Constants.PHONE_NUMBER, number); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); ActivityCompat.finishAffinity(LoginActivity.this); }

The app page will be navigated to user profile screen where the user will enter all the details such as name, mobile number and status.

Integrating Cloud DB

Step 1: Add the dependency.

  1. Add the following dependency for cloudDB.
    dependencies { implementation 'com.huawei.agconnect:agconnectdatabase:<cloud_DB_zone>' }
  2. After integrating the SDK in either mode, add the following information under apply plugin: 'com.android.application' in the file header of build.gradle (app):
    apply plugin: 'com.huawei.agconnect'

Step 2: Create a User object with certain fields.

In Java

Create a User class to communicate the user details with Cloud DB.

package com.huawei.chitchatapp.dbcloud; import com.huawei.agconnect.cloud.database.CloudDBZoneObject; import com.huawei.agconnect.cloud.database.Text; import com.huawei.agconnect.cloud.database.annotations.DefaultValue; import com.huawei.agconnect.cloud.database.annotations.NotNull; import com.huawei.agconnect.cloud.database.annotations.Indexes; import com.huawei.agconnect.cloud.database.annotations.PrimaryKeys; @PrimaryKeys({"user_phone"}) @Indexes({"user_id:user_id", "user_phone_id:user_phone,user_id"}) public final class User extends CloudDBZoneObject { private Integer user_id; private String user_phone; private String user_name; private String user_status; private String user_login_status; private String user_push_token; private String user_profile_url; public User() { super(User.class); } public void setUserId(Integer user_id) { this.user_id = user_id; } public Integer getUserId() { return user_id; } public void setUserPhone(String user_phone) { this.user_phone = user_phone; } public String getUserPhone() { return user_phone; } public void setUserName(String user_name) { this.user_name = user_name; } public String getUserName() { return user_name; } public void setUserStatus(String user_status) { this.user_status = user_status; } public String getUserStatus() { return user_status; } public void setUserLoginStatus(String user_login_status) { this.user_login_status = user_login_status; } public String getUserLoginStatus() { return user_login_status; } public void setUserPushToken(String user_push_token) { this.user_push_token = user_push_token; } public String getUserPushToken() { return user_push_token; } public void setUserProfileUrl(String user_profile_url) { this.user_profile_url = user_profile_url; } public String getUserProfileUrl() { return user_profile_url; } }

Step 3: Connect the utility class with CloudDB.

CloudDBHelper.java

import android.content.Context; 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.exceptions.AGConnectCloudDBException; import com.huawei.chitchatapp.dbcloud.ObjectTypeInfoHelper; import com.huawei.chitchatapp.utils.AppLog; import com.huawei.chitchatapp.utils.Constants; import com.huawei.chitchatapp.utils.OnDBZoneOpen; import com.huawei.hmf.tasks.Task;
private AGConnectCloudDB agConnectCloudDB; private CloudDBZone cloudDBZone; private static final String TAG = CloudDBHelper.class.getSimpleName(); private static CloudDBHelper cloudDBHelper; public static CloudDBHelper getInstance() { if (cloudDBHelper == null) { cloudDBHelper = new CloudDBHelper(); } return cloudDBHelper; }

Step 5: Initialze the Cloud DB SDK. After that, call the following method in the Application class.

public void init(Context context) { AGConnectCloudDB.initialize(context); try { agConnectCloudDB = AGConnectCloudDB.getInstance(); agConnectCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo()); } catch (AGConnectCloudDBException e) { e.printStackTrace(); } }

Step 6: Establish the connection with Cloud DB.

public void openDb(OnDBZoneOpen onDBZoneOpen) { CloudDBZoneConfig mConfig = new CloudDBZoneConfig(Constants.DB_ZONE_NAME, CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE, CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC); mConfig.setPersistenceEnabled(true); Task openDBZoneTask = agConnectCloudDB.openCloudDBZone2(mConfig, true); openDBZoneTask.addOnSuccessListener(cloudDBZone -> { AppLog.logI(TAG, "cloudDBZOne Open"); this.cloudDBZone = cloudDBZone; onDBZoneOpen.isDBZoneOpen(true, this.cloudDBZone); }).addOnFailureListener(e -> { AppLog.logW(TAG, "open cloudDBZone failed for " + e.getMessage()); onDBZoneOpen.isDBZoneOpen(false, null); }); }

Step 7: Close the connection with CloudDB.

public void closeDb(Context context) { try { agConnectCloudDB.closeCloudDBZone(cloudDBZone); } catch (AGConnectCloudDBException e) { AppLog.logW(TAG,"close the DBZone "+e.getMessage()); } }

Step 8: Enter user details.

private void updateProfileData(String name, String status, Uri profileUri) { ChitChatSharedPref.initializeInstance(UserProfileActivity.this); User user = new User(); user.setUserId(userId); user.setUserPushToken(pushToken); user.setUserPhone(phoneNumber); user.setUserName(name); user.setUserLoginStatus(Constants.STATUS_ONLINE); user.setUserStatus(status); user.setUserProfileUrl(String.valueOf(profileUri)); userProfile.saveUser(user, UserProfileActivity.this); userProfile.userMutableLiveData.observe(UserProfileActivity.this, aBoolean -> { if (aBoolean) { ChitChatSharedPref.initializeInstance(getApplicationContext()); ChitChatSharedPref.getInstance().putString(Constants.PHONE_NUMBER, phoneNumber); ChitChatSharedPref.getInstance().putString(Constants.USER_NAME, name); ChitChatSharedPref.getInstance().putString(Constants.ALREADY_LOGIN, "1"); Toast.makeText(UserProfileActivity.this, getString(R.string.showMessageSuccess), Toast.LENGTH_SHORT).show(); Intent intent = new Intent(UserProfileActivity.this, MainActivity.class); startActivity(intent); } else { Toast.makeText(UserProfileActivity.this, getString(R.string.showMessageFailed), Toast.LENGTH_SHORT).show(); } }); }

Step 9: Save the user details and check if it is properly saved or not.

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

Screenshots

Results after user details in the User Details.

Integrating Cloud Storage.

Step 1: Add the dependency.

Adding the following dependency:

dependencies{ implementation "com.huawei.agconnect:agconnect-storage:" }

Step 2: Initialize the Cloud Storage SDK.

final StorageReference storageReference = ChitChatApplication.getStorageManagement().getStorageReference(fileName + name);

Step 3: Implement a ViewModel for Cloud Storage.

public class ChitChatStorageViewModel extends ViewModel { private static final String TAG = "ChitChatStorageViewModel"; private MutableLiveData<Uri> uploadFileLiveData; private OnApiError onApiError; public LiveData<Uri> uploadFileLiveData() { if (uploadFileLiveData == null) { uploadFileLiveData = new MutableLiveData<>(); } return uploadFileLiveData; } public void uploadFile(StorageReference reference, String fileName, File filePath, OnApiError onApiError) { this.onApiError = onApiError; UploadTask task = reference.putFile(filePath); task.addOnSuccessListener(uploadSuccessListener) .addOnFailureListener(uploadFailureListener); } OnSuccessListener<UploadTask.UploadResult> uploadSuccessListener = new OnSuccessListener<UploadTask.UploadResult>() { @Override public void onSuccess(UploadTask.UploadResult uploadResult) { uploadResult.getStorage().getDownloadUrl().addOnSuccessListener(downloadLink) .addOnFailureListener(downloadUriFailureListener); } }; OnSuccessListener<Uri> downloadLink = new OnSuccessListener<Uri>() { @Override public void onSuccess(Uri uri) { uploadFileLiveData.postValue(uri); } }; OnFailureListener uploadFailureListener = new OnFailureListener() { @Override public void onFailure(Exception e) { if (onApiError != null) { onApiError.onError("Error in uploading file to server", e); } } }; OnFailureListener downloadUriFailureListener = new OnFailureListener() { @Override public void onFailure(Exception e) { onApiError.onError("Failed in getting uri", e); } }; }

Step 4: Upload files to Cloud Storage.

The following code is the place where we need to upload the file.

public void uploadImage(File path) { userProfileImage.uploadFileLiveData().observe(UserProfileActivity.this, uri -> { updateProfileData(mName.getText().toString(), mStatus.getText().toString(), uri); }); final String name = phoneNumber + ".png"; final StorageReference storageReference = ChitChatApplication.getStorageManagement().getStorageReference("chitchatapp/profile_pic/" + name); userProfileImage.uploadFile(storageReference, phoneNumber + ".png", path, (errorMessage, e) -> AppLog.logE("ProfileFragment", "filePAth--->Error" + e)); }

Step 5: Check the results.

Integrating Push kit.

Push Kit is a messaging service provided for you. It establishes a messaging channel from the cloud to devices. By integrating Push Kit, you can send messages to your apps on users' devices in real time. This helps you maintain closer ties with users and increases user awareness of and engagement with your apps. The basic process of push kit is as show in the below image.

Step 1: Enable Push kit in AppGallery Connect.

Step 2: Add the dependency.

Add the following dependency:

dependencies { implementation 'com.huawei.hms:push:<lastest_push_kit_version>' }

Step 3: Configure the Manifest file.

You need to register service in application in the AndroidManifest.xml file to receive data messages or obtain tokens. Service extends the HmsMessageService class and overrides the methods in the class. The following uses the service with the class named DemoHmsMessageService class as an example. You can customize the class name as needed.
In Android 11, the way for an app to query other apps on the user device and interact with them is changed. If targetSdkVersion is 30 or later for your app, add the queries element in the manifest element in AndroidManifest.xml to allow your app to access HMS Core (APK).

Step 4: Retrive token in application.


Obtain a token.
Add <meta-data> under application in the AndroidManifest.xml file.
<meta-data android:name="push_kit_auto_init_enabled" android:value="true" />

The Push SDK version must be 4.0.0.300 or later.
The Push SDK will obtain a token when the app is launched. The app just receives the token.name and the value in meta-data cannot be modified.

Receive a token.

The ChitChatPushService class extends the HmsMessageService class. In ChitChatPushService, the onNewToken(String token, Bundle bundle) method is overridden to obtain a token.
If the version of the Push SDK you integrated is earlier than 5.0.4.302, override the onNewToken(String token) method.

public class ChitChatPushService extends HmsMessageService { private static final String TAG = ChitChatPushService.class.getSimpleName(); private static final String CHANNEL1 = "Push_Channel_01"; private Data data = null; private Bitmap mBitmap; @Override public void onNewToken(String s) { super.onNewToken(s); } }

You can use the following code base, which can also be used to retrieve token on android 10 and above. The asynctask can be called in an activity.

public class GetToken extends AsyncTask { private static final String TAG = GetToken.class.getSimpleName(); @SuppressLint("StaticFieldLeak") 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) { AppLog.logE(TAG,e.getMessage()); } return null; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); } }

Step 5: Retrive notification in application.

Once the token is retrieved you can implement the following code which will process the notification for message and image notification.

public class ChitChatPushService extends HmsMessageService { private static final String TAG = ChitChatPushService.class.getSimpleName(); private static final String CHANNEL1 = "Push_Channel_01"; private Data data = null; private Bitmap mBitmap; @Override public void onNewToken(String s) { super.onNewToken(s); } @Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); AppLog.logE(TAG, remoteMessage.getData()); try { JSONObject jsonObject = new JSONObject(remoteMessage.getData()); data = new Gson().fromJson(jsonObject.toString(), Data.class); } catch (JSONException e) { AppLog.logE(TAG,e.getMessage()); } NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { int importance = NotificationManager.IMPORTANCE_HIGH; NotificationChannel mChannel = new NotificationChannel( CHANNEL1, CHANNEL1, importance); if (mNotificationManager != null) { mNotificationManager.createNotificationChannel(mChannel); if (Objects.requireNonNull(remoteMessage.getDataOfMap().get("message")).equalsIgnoreCase(Constants.MESSAGE_TYPE_TEXT)) { Intent notificationIntent = new Intent(getApplicationContext(), MessageActivity.class); notificationIntent.putExtra("roomId", remoteMessage.getDataOfMap().get("roomId")); notificationIntent.putExtra("name",remoteMessage.getDataOfMap().get("sender_name")); notificationIntent.putExtra("phone",remoteMessage.getDataOfMap().get("sender_phone")); notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent intent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0); NotificationCompat.Builder mBuilder; mBuilder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL1) .setSmallIcon(R.drawable.profile) .setContentTitle(remoteMessage.getDataOfMap().get("sender_name")) .setContentText(remoteMessage.getDataOfMap().get("message_data")); mBuilder.setContentIntent(intent); mBuilder.setAutoCancel(true); mNotificationManager.notify(0, mBuilder.build()); } else if (Objects.requireNonNull(remoteMessage.getDataOfMap().get("message")).equalsIgnoreCase(Constants.MESSAGE_TYPE_IMAGE)) { new GeneratePictureStyleNotification(this, data).execute(); } else if (Objects.requireNonNull(remoteMessage.getDataOfMap().get("message")).equalsIgnoreCase(Constants.MESSAGE_TYPE_MAP)) { Intent notificationIntent = new Intent(getApplicationContext(), MessageActivity.class); Bundle passBundle = new Bundle(); passBundle.putString("roomId", remoteMessage.getDataOfMap().get("roomId")); PendingIntent intent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0); NotificationCompat.Builder mBuilder; mBuilder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL1) .setSmallIcon(R.drawable.profile) .setContentTitle(remoteMessage.getDataOfMap().get("sender_name")) .setContentText(Constants.LOCATION_DATA); mBuilder.setContentIntent(intent); mBuilder.setAutoCancel(true); mNotificationManager.notify(0, mBuilder.build()); } } } } private class GeneratePictureStyleNotification extends AsyncTask<String, Void, Bitmap< { Context mContext; private Data data; public GeneratePictureStyleNotification(Context context, Data data) { super(); this.mContext = context; this.data = data; } @Override protected Bitmap doInBackground(String... params) { Glide.with(mContext) .asBitmap() .load(data.messageData) .into(new CustomTarget<Bitmap>() { @Override public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { mBitmap = resource; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { showNotification(data); } } @Override public void onLoadCleared(@Nullable Drawable placeholder) { } }); return null; } @Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); } } private void showNotification(Data data) { NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); int notificationId = 1; // String channelName = CHANNEL1; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { int importance = NotificationManager.IMPORTANCE_HIGH; NotificationChannel mChannel = new NotificationChannel( CHANNEL1, CHANNEL1, importance); if (mNotificationManager != null) { mNotificationManager.createNotificationChannel(mChannel); Intent notificationIntent = new Intent(getApplicationContext(), MessageActivity.class); Bundle passValue = new Bundle(); passValue.putString("roomId", data.getRoomId()); notificationIntent.setAction("android.intent.action.MAIN"); notificationIntent.addCategory("android.intent.category.LAUNCHER"); notificationIntent.putExtras(passValue); notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent intent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder mBuilder; if (mBitmap != null) { NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle() .bigPicture(mBitmap); mBuilder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL1) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle(getResources().getString(R.string.app_name)) .setLargeIcon(mBitmap) .setStyle(style) .setContentText(data.getMessageData()); } else { mBuilder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL1) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle(getResources().getString(R.string.app_name)) .setContentText(data.getMessageData()); } mBuilder.setContentIntent(intent); mBuilder.setAutoCancel(true); mNotificationManager.notify(notificationId, mBuilder.build()); } } else { Intent notificationIntent = new Intent(getApplicationContext(), MessageActivity.class); Bundle passValue = new Bundle(); passValue.putString("roomId", data.getRoomId()); notificationIntent.setAction("android.intent.action.MAIN"); notificationIntent.addCategory("android.intent.category.LAUNCHER"); notificationIntent.putExtras(passValue); notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent intent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this); Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); if (mBitmap != null) { NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle() .bigPicture(mBitmap); mBuilder.setSmallIcon(R.mipmap.ic_launcher).setContentTitle(getString(R.string.app_name)) .setStyle(style) .setLargeIcon(mBitmap).setAutoCancel(true).setSound(soundUri); } else { mBuilder.setSmallIcon(R.mipmap.ic_launcher).setContentTitle(getString(R.string.app_name)) .setStyle(new NotificationCompat.BigTextStyle() .bigText(data.message)).setContentText(data.getMessage()).setAutoCancel(true).setSound(soundUri); } mBuilder.setAutoCancel(true); NotificationCompat.InboxStyle inBoxStyle = new NotificationCompat.InboxStyle(); inBoxStyle.setBigContentTitle(getString(R.string.app_name)); mBuilder.setContentIntent(intent); mBuilder.setStyle(inBoxStyle); Notification notification = mBuilder.build(); notification.flags |= Notification.FLAG_AUTO_CANCEL; mNotificationManager.notify(notificationId, notification); } } }

Step 6: Send the push notification to the Android users.

The below code will help you to send push notification to others.

public class PushApis { private Context context; private static final String TAG = PushApis.class.getSimpleName(); 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.getAccessToken(), appId, chatId, message, messageData, userPushTokens); } catch (Exception e) { AppLog.logE(TAG, e.getMessage()); } } 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 = "xxxx xxxx"; data.sender_phone = "xxxxxxxx"; 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(TAG, response); } catch (Exception e) { AppLog.logE(TAG, e.getMessage()); } } }

Push kit results:

Integrate Location Kit

Step 1: Add the dependency.

Add the following dependency for Location Kit:

dependencies { implementation "com.huawei.hms:location:" }

Step 2: Configure the Manifest file.

Add the following permissions to the Manifest file.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />; <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Add the runtime permission.

if (checkLocationPermission()) { Intent intent = new Intent(MessageActivity.this, LocationActivity.class); launcherForLocation.launch(intent); } else { requestPermissionForLocation(); }

Add the check permission for checkLocationPermission() and request premission for requestPermissionforLocation().


checkLocationPermission():

private boolean checkLocationPermission() { int locationPermission = ContextCompat.checkSelfPermission(this.getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION); int coursePermission = ContextCompat.checkSelfPermission(this.getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION); return locationPermission == PackageManager.PERMISSION_GRANTED && coursePermission == PackageManager.PERMISSION_GRANTED; }

requestPermissionForLocation():

private void requestPermissionForLocation() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }, Constants.LOCATION_PERMISSION); } }

Handle the permission.

@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case Constants.LOCATION_PERMISSION: if (grantResults.length > 0) { boolean fineLoc = grantResults[0] == PackageManager.PERMISSION_GRANTED; boolean coarseLoc = grantResults[1] == PackageManager.PERMISSION_GRANTED; if (fineLoc && coarseLoc) { Intent intent = new Intent(MessageActivity.this, LocationActivity.class); launcherForLocation.launch(intent); } else { Toast.makeText(this, getResources().getString(R.string.allow_location_permission), Toast.LENGTH_SHORT).show(); } } } }

Step 3: Add the following code base for retrieving location.

public void getCurrentLocation(Context context) {

public void getCurrentLocation(Context context) { FusedLocationProviderClient mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context); SettingsClient mSettingsClient = LocationServices.getSettingsClient(context); LocationRequest mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(5000); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); mFusedLocationProviderClient.getLastLocation().addOnSuccessListener(location -> { AppLog.logD(TAG, "Lat long--->Fushed" + location.getLongitude() + "," + location.getLatitude() + "," + location.getAccuracy()); if (location != null) { locationMutableLiveData.postValue(location); } }).addOnFailureListener(e -> { AppLog.logE(TAG, "error" + e.getMessage()); }); }

Location Kit integration results:

The blue marker on the map indicatesthe current location.

Integrating Map kit

The Map SDK for Android provides a set of APIs for map development in Android. The map data covers most countries and regions outside the Chinese mainland, and supports multiple languages. Map Kit uses the WGS 84 GPS coordinate system, which can meet most map development requirements outside the Chinese mainland. You can easily add map-related functions in your Android app

Step 1: Enable Map kit in AppGallery Connect.

1. Sign in to AppGallery Connect and click My projects.
2. Find your project from the project list and click the app for which you need to enable Map Kit on the project card.
3. Go to Project settings > Manage APIs and enable Map Kit.

Step 2: Add the dependency to the project.

Add the following dependency:

dependencies { implementation "com.huawei.hms:maps:" }

Step 3 Integrate Map Kit.

  1. Declare the following permission to the android manifest file.
  2. <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
  3. Add Mapview to your layout resource.
  4. <com.huawei.hms.maps.MapView xmlns:map="http://schemas.android.com/apk/res-auto" android:id="@+id/mapView" android:layout_width="match_parent" android:layout_height="match_parent" map:cameraTargetLat="51" map:cameraTargetLng="10" map:cameraZoom="8.5" map:mapType="normal" map:uiCompass="true" map:uiZoomControls="true" />
  5. Implement the following code in the .java files.
  6. @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_location); stMapView(savedInstanceState); } private void stMapView(Bundle savedInstanceState) { Bundle mapViewBundle = null; if (savedInstanceState != null) { mapViewBundle = savedInstanceState.getBundle(Constants.MAP_VIEW_BUNDLE_KEY); } MapsInitializer.setApiKey(Constants.MAP_VIEW_API_KEY); mMapView.onCreate(mapViewBundle); mMapView.getMapAsync(this); } @Override public void onMapReady(HuaweiMap huaweiMap) { hMap = huaweiMap; hMap.setMyLocationEnabled(true); hMap.getUiSettings().setMyLocationButtonEnabled(true); }

Results:

Integrating Site Kit

The Site SDK provides place search services that allow your users to use location-based services more conveniently, helping you quickly acquire users. Site Kit provides the following core capabilities for you to quickly build apps with which your users can explore the world around them:

Here we are going to use nearby place search.

Step 1: Add the dependency.

Add the following dependency for Site Kit:

dependencies { implementation "com.huawei.hms:site:<site_kit_lastest_code>' }

Step 2: Enable the Site Kit.

Step 3: Integrate the search service.

Create a search object.

private SearchService searchService; try { searchService = SearchServiceFactory.create(this, URLEncoder.encode(Constants.MAP_VIEW_API_KEY, "utf-8")); } catch (UnsupportedEncodingException e) { AppLog.logE(TAG, "encode apikey error"); }

Create the function to access all the nearby areas.

public void getNearbyData(double latitude, double longitude, SearchService searchService, String locationType) { NearbySearchRequest request = new NearbySearchRequest(); Coordinate location = new Coordinate(latitude, longitude); request.setLocation(location); request.setQuery(locationType); request.setRadius(5); request.setHwPoiType(HwLocationType.ADDRESS); request.setLanguage("en"); request.setPageIndex(1); request.setPageSize(10); request.setStrictBounds(false); SearchResultListener<NearbySearchResponse> resultListener = new SearchResultListener<NearbySearchResponse>() { @Override public void onSearchResult(NearbySearchResponse results) { arrayListMutableLiveData.postValue(new ArrayList<>(results.getSites())); } @Override public void onSearchError(SearchStatus status) { AppLog.logE("TAG", "Error : " + status.getErrorCode() + " " + status.getErrorMessage()); } }; searchService.nearbySearch(request, resultListener); }

}

Access the LiveData in the activity after getting the location from the site Kit.

private void updateDetails(Location location) { float zoom = 14.0f; LatLng latLng1 = new LatLng(location.getLatitude(), location.getLongitude()); CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng1, zoom); hMap.animateCamera(cameraUpdate); currentLocationAccuracy.setText(String.format("Accurate to %s meters", location.getAccuracy())); Util.stopProgressBar(); }

Site Kit integration results:

Well done. You have successfully built a Chat app and learned how to:

For more details, please refer to the following official documentations.

Disclaimer: "This codelab is a reference to implement combination of multiple HMS kits in a single project. The developer should verify and ensure the legal and security compliance of the relevant open source code".

You can download the source code here.

Code copied