1. Overview
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.
2. Introduction
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.
- Signing in with Phone by using Auth Service.
- Chatting with people by using Cloud DB.
- Sharing the image and uploading the files by using Cloud Storage and Cloud DB.
- Pushing notifications to the user by using Push Kit.
- Sharing the current location or nearby landmark by using Map Kit, Location Kit and Site Kit.
-
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.

-
The following is the screen shot for the sign-in screen. After the
phone number is entered, the OTP will be sent.
-
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.
- The following screenshorts show the List of saved Contacts, history of the previous chat, and profile update page.
-
The chat window can be used to share images, location, document, and
others.




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:
- Integrate Auth Service to sign in to the app with phone number and OTP.
- Integrate Cloud DB to save user details, for example, the user chat history. Integrate Cloud Storage to save images.
- Integrate Push Kit to push the notification.
- Integration Map Kit, Location Kit, and Site Kit to share the current location.
3. What You Will Need
Hardware Requirements
- A computer (desktop or laptop) that runs the Windows 10 operating system
- A Huawei phone with HMS Core (APK) 5.0.0.300 or later installed
Software Requirements
- Android Studio 3.X
- JDK 1.8 and later
- SDK Platform 23 and later
-
Gradle 4.6 and later
4. Integration Preparations
Enable HUAWEI Service(s) in AppGallery Connect:
To integrate HMS Core kits, you must complete the following
preparations:
- Create an app in AppGallery Connect.
- Create an Android Studio project.
- Generate a signing certificate.
- Generate a signing certificate fingerprint.
- Configure the signing certificate fingerprint.
- Add the app package name and save the configuration file.
- Add the AppGallery Connect plugin and Maven repository in the project-level build.gradle file.
- Configure the signing certificate in Android Studio.
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.
- Auth Service (Phone Number).
- Cloud DB.
- Cloud Storage.
- Push Kit.
- Map Kit
-
Site Kit
5. Development Procedure
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.
-
Add the following dependency for cloudDB.
dependencies { implementation 'com.huawei.agconnect:agconnectdatabase:<cloud_DB_zone>' } -
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.
- Declare the following permission to the android manifest file.
- Add Mapview to your layout resource.
- Implement the following code in the .java files.
<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"/>
<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" />
@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:
- Keyword search: Returns a place list based on keywords entered by the user.
- Nearby place search: Searches for nearby places based on the current location of the user's device.
- Place detail search: Searches for details about a place.
- Place search suggestion: Returns a list of suggested places.
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:
6. Congratulations
Well done. You have successfully built a Chat app and learned how to:
- Store data in the cloud DB such as user details and messages.
- Store images of the user profile and images shared on chat by using Cloud Storage.
- Push notifications using Push Kit.
- Share the current location and nearby landmarks using Location Kit, Map Kit, and Site Kit.
7. References
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.
