[Huawei Developers] Freemind R:
More articles like this, you can visit HUAWEI Developer Forum and Medium.​

When you want to find a restaurant and your phone receives a push message recommending nearby restaurants to you at the right moment, will you tap open the message? When you're still hesitating on whether to buy a pair of sneakers in a store and an app sends you a coupon offering you a 50% discount, do you still find the ad bothering?

Ads pushed at the right moment meet users' requirements and won't make users feel bothered. More precise advertising not only reduces unnecessary disturbance to users but also improves user satisfaction with your app. Do you want to build such a function for precisely pushing ads to nearby people?

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/006/560/358/2640852000006560358.20200701015015.15581091261579881658805686211646:50510701023219:2800:E6724DD349D770757484260F6E541C2FC16DD6D7D4C9214BFD882AE810AF9B8B.jpg"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/006/560/358/2640852000006560358.20200701015015.15581091261579881658805686211646:50510701023219:2800:E6724DD349D770757484260F6E541C2FC16DD6D7D4C9214BFD882AE810AF9B8B.jpg[/IMG]​

Integrate HUAWEI Nearby Service and use the Nearby Beacon Message feature to implement the function. You need to deploy beacons at the place where you want to push messages to nearby people, for example, a mall. Beacons will provide users' locations relative to them so that when a user gets near a restaurant or store, the user will receive a promotion message (such as a coupon or discount information) preconfigured by the merchant in advance. The function demo is as follows.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/005/796/051/2640852000005796051.20200701012533.06999398869062159440127542333426:50510701023219:2800:A64CA6F491066CDEE8E3B545F1851D30350105D43BAA9EC7DABFEBD6B48978F0.gif"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/005/796/051/2640852000005796051.20200701012533.06999398869062159440127542333426:50510701023219:2800:A64CA6F491066CDEE8E3B545F1851D30350105D43BAA9EC7DABFEBD6B48978F0.gif[/IMG]​

If you are interested in the implementation details, download the source code from GitHub: https://github.com/HMS-Core/hms-nearby-demo/tree/master/NearbyCanteens

1. Getting Started

If you are already a Huawei developer, skip this step. If you are new to Huawei Mobile Services (HMS), you need to configure app information in AppGallery Connect, enable Nearby Service on the HUAWEI Developers console, and integrate the HMS Core SDK. For details, please refer to the documentation.

1.1 Adding Huawei Maven Repository and AppGallery Connect Plug-in Configurations to the Project-Level build.gradle File

Add the following Huawei maven repository and AppGallery Connect plug-in configurations to your project-level build.gradle file directly:

buildscript {
repositories {
google()
jcenter()
maven { url 'http://developer.huawei.com/repo/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
classpath 'com.huawei.agconnect:agcp:1.2.1.301'
}
}

allprojects {
repositories {
google()
jcenter()
maven { url 'http://developer.huawei.com/repo/' }
}
}

1.2 Adding SDK Dependencies to the App-Level build.gradle File

Import the Nearby Service SDKs. The most important SDKs are those starting with com.huawei.

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "com.huawei.hmf:tasks:1.3.1.301"
implementation "com.huawei.hms:network-grs:1.0.9.302"
implementation 'com.huawei.agconnect:agconnect-core:1.2.1.301'
implementation 'com.huawei.hms:nearby:4.0.4.300'
api 'com.google.code.gson:gson:2.8.5'
}

1.3 Applying for the Network, Bluetooth, and Location Permissions in the AndroidManifest.xml File

The following permissions are required. Each permission name can directly express its purpose. For example, android.permission.INTERNET indicates the network permission, and android.permission.BLUETOOTH indicates the Bluetooth permission.

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

2. Code Development

2.1 Initialization and Dynamic Permission Application

The onCreate method is called when the current activity is created. In this method, you can perform some preparations, such as applying for necessary permissions and checking whether the Internet connection, Bluetooth, and GPS are enabled.

@RequiresApi(api = Build.VERSION_CODES.P)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate");
setContentView(R.layout.activity_canteen);
boolean isSuccess = requestPermissions(this, this);
if (!isSuccess) {
return;
}
Log.i(TAG, "requestPermissions success");
if (!NetCheckUtil.isNetworkAvailable(this)) {
showWarnDialog(Constant.NETWORK_ERROR);
return;
}
if (!BluetoothCheckUtil.isBlueEnabled()) {
showWarnDialog(Constant.BLUETOOTH_ERROR);
return;
}
if (!GpsCheckUtil.isGpsEnabled(this)) {
showWarnDialog(Constant.GPS_ERROR);
return;
}
intView();
init();
}

Register a listener and display a message if Bluetooth, GPS, or network disconnection is detected.

The following uses the AlertDialog component of Android as an example:

private void showWarnDialog(String content) {
DialogInterface.OnClickListener onClickListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
android.os.Process.killProcess(android.os.Process.myPid());
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.warn);
builder.setIcon(R.mipmap.warn);
builder.setMessage(content);
builder.setNegativeButton(getText(R.string.btn_confirm), onClickListener);
builder.show();
}

2.2 Beacon Message Reception

The following startScanning method is called in the onStart method to start Bluetooth scanning. In the MessageHandler object, four callback methods are encapsulated: 1. onFound indicates that a beacon message is discovered; 2. OnLost indicates that the message is no longer discoverable; 3. onDistanceChanged indicates the change of the distance between the beacon and the device; 4. onBleSignalChanged indicates that a beacon signal change is detected.

private void startScanning() {
Log.i(TAG, "startScanning");
mMessageHandler =
new MessageHandler() {
@Override
public void onFound(Message message) {
super.onFound(message);
doOnFound(message);
}

@Override
public void onLost(Message message) {
super.onLost(message);
doOnLost(message);
}

@Override
public void onDistanceChanged(Message message, Distance distance) {
super.onDistanceChanged(message, distance);
}

@Override
public void onBleSignalChanged(Message message, BleSignal bleSignal) {
super.onBleSignalChanged(message, bleSignal);
}
};
MessagePicker msgPicker = new MessagePicker.Builder().includeAllTypes().build();
Policy policy = new Policy.Builder().setTtlSeconds(Policy.POLICY_TTL_SECONDS_INFINITE).build();
GetOption getOption = new GetOption.Builder().setPicker(msgPicker).setPolicy(policy).build();
Task<Void> task = messageEngine.get(mMessageHandler, getOption);
task.addOnFailureListener(
new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, "Login failed:", e);
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
int errorStatusCode = apiException.getStatusCode();
if (errorStatusCode == StatusCode.STATUS_MESSAGE_AUTH_FAILED) {
Toast.makeText(mContext, R.string.configuration_error, Toast.LENGTH_SHORT).show();
} else if (errorStatusCode == StatusCode.STATUS_MESSAGE_APP_UNREGISTERED) {
Toast.makeText(mContext, R.string.permission_error, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, R.string.start_get_beacon_message_failed, Toast.LENGTH_SHORT)
.show();
}
} else {
Toast.makeText(mContext, R.string.start_get_beacon_message_failed, Toast.LENGTH_SHORT)
.show();
}
}
});
}

Personalize the display of a detected beacon message.

The most important method during the process is the doOnFound method, which specifies the personalized display processing mode when the client receives a beacon message.

private void doOnFound(Message message) {
if (message == null) {
return;
}
String type = message.getType();
if (type == null) {
return;
}
String messageContent = new String(message.getContent());
Log.d(TAG, "New Message:" + messageContent + " type:" + type);
if (type.equalsIgnoreCase(Constant.CANTEEN)) {
operateOnFoundCanteen(messageContent);
} else if (type.equalsIgnoreCase(Constant.NOTICE)) {
operateOnFoundNotice(messageContent);
}
}

Display the personalized message.

The following code only demonstrates one of the message processing modes, including implementing the banner notification and text display effect.

private void operateOnFoundCanteen(String messageContent) {
CanteenAdapterInfo canteenAdapterInfo =
(CanteenAdapterInfo) JsonUtils.json2Object(messageContent, CanteenAdapterInfo.class);
if (canteenAdapterInfo == null) {
return;
}
String canteenName = canteenAdapterInfo.getCanteenName();
if (canteenName == null) {
return;
}
Log.d(TAG, "canteenName:" + canteenName);
if (!canteenNameList.contains(canteenName)) {
return;
}

String notice = "";
if (receivedNoticeMap.containsKey(canteenName)) {
notice = receivedNoticeMap.get(canteenName);
}
int canteenImage = getCanteenImage(canteenName);
int requestCode = getRequestCode(canteenName);
canteenAdapterInfo.setNotice(notice);
canteenAdapterInfo.setCanteenImage(canteenImage);
canteenAdapterInfo.setShowNotice(true);
canteenAdapterInfo.setRequestCode(requestCode);

canteenAdapterInfoMap.put(canteenName, canteenAdapterInfo);
canteenAdapterInfoList.add(canteenAdapterInfo);

sendNotification(Constant.NOTIFICATION_TITLE, Constant.NOTIFICATION_SUBTITLE, canteenName, requestCode);
runOnUiThread(
new Runnable() {
@Override
public void run() {
searchTipTv.setText(R.string.found_tip);
loadingLayout.setVisibility(View.GONE);
canteenAdapter.setDatas(canteenAdapterInfoList);
}
});
}

Conclusion

This demo uses Bluetooth beacon message subscription function of HUAWEI Nearby Service.

Based on the Nearby Beacon Message capability, you not only can develop an ad push function, but also can implement the following functions:

1. A car or lifestyle app can integrate the capability to identify whether a user is near their car to determine whether to enable keyless access and record the driving track of the car.

2. A business app can integrate the capability to accurately record the locations where employees clock in.

3. A travel or exhibition app can integrate the capability to introduce an exhibit to a user when the user gets near the exhibit.

4. A game app can integrate the capability to make your game interact with the real world, for example, unlocking a game level through a physical object and sending rewards to players who are participating in offline events.

If you are interested and want to learn more, check our development guide at the HUAWEI Developers official website.

Any questions about the process, you can visit HUAWEI Developer Forum.