[Huawei Developers] bugra7:
Hello everyone, in this article we will learn how to use Huawei Awareness Kit with foreground service to send notification when certain condition is met.

View attachment 5153561

Huawei Awareness Kit enables us to observe some environmental factors such as time, location, behavior, audio device status, ambient light, weather and nearby beacons. So, why don’t we create our own conditions to be met and observe them even when the application is not running?

First of all, we need to do HMS Core integration to be able to use Awareness Kit. I will not go into the details of that because it is already covered here.

If you are done with the integration, let’s start coding.

Activity Class

We will keep our activity class pretty simple to prevent any confusion. It will only be responsible for starting the service:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Intent serviceStartIntent = new Intent(this, MyService.class);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceStartIntent);
}
else {
startService(serviceStartIntent);
}
}
}

However, we need to add the following permission to start a service correctly:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Service Class

Now, let’s talk about the service class. We are about to create a service which will run even when the application is killed. However, this comes with some restrictions. Since the Android Oreo, if an application wants to start a foreground service, it must inform the user by a notification which needs to be visible during the lifetime of the foreground service. Also, this notification needs to be used to start foreground. Therefore, our first job in the service class is to create this notification and call the startForeground() method with it:

Notification notification;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
notification = createCustomNotification();
else
notification = new Notification();

startForeground(1234, notification);

And here is how we create the information notification we need for SDK versions later than 25:

@RequiresApi(api = Build.VERSION_CODES.O)
private Notification createCustomNotification() {
NotificationChannel notificationChannel = new NotificationChannel("1234", "name", NotificationManager.IMPORTANCE_HIGH);

NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(notificationChannel);

NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "com.awarenesskit.demo");

return notificationBuilder
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("Observing headset status")
.setPriority(NotificationManager.IMPORTANCE_HIGH)
.build();
}

Note: You should replace the application id above with the application id of your application.

Now, it is time to prepare the parameters to create a condition to be met called barrier:

PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

headsetBarrierReceiver = new HeadsetBarrierReceiver();
registerReceiver(headsetBarrierReceiver, new IntentFilter(barrierReceiverAction));

AwarenessBarrier headsetBarrier = HeadsetBarrier.connecting();

createBarrier(this, HEADSET_BARRIER_LABEL, headsetBarrier, pendingIntent);

Here we have sent the required parameters to a method which will create a barrier for observing headset status. If you want, you can use other awareness barriers too.

Creating a barrier is a simple and standard process which will be taken care of the following method:

private void createBarrier(Context context, String barrierLabel, AwarenessBarrier barrier, PendingIntent pendingIntent) {
BarrierUpdateRequest.Builder builder = new BarrierUpdateRequest.Builder();

BarrierUpdateRequest request = builder.addBarrier(barrierLabel, barrier, pendingIntent).build();
Awareness.getBarrierClient(context).updateBarriers(request)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void void1) {
System.out.println("Barrier Create Success");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
System.out.println("Barrier Create Fail");
}
});
}

We will be done with the service class after adding the following methods:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}

@Override
public void onDestroy() {
unregisterReceiver(headsetBarrierReceiver);
super.onDestroy();
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

And of course, we shouldn’t forget to add our service to the manifest file:

<service
android:name=".MyService"
android:enabled="true"
android:exported="true" />

Broadcast Receiver Class

Lastly, we need to create a broadcast receiver where we will observe and handle the changes in the headset status:

public class HeadsetBarrierReceiver extends BroadcastReceiver {

public static final String HEADSET_BARRIER_LABEL = "HEADSET_BARRIER_LABEL";

@Override
public void onReceive(Context context, Intent intent) {
BarrierStatus barrierStatus = BarrierStatus.extract(intent);
String barrierLabel = barrierStatus.getBarrierLabel();
int barrierPresentStatus = barrierStatus.getPresentStatus();

if (HEADSET_BARRIER_LABEL.equals(barrierLabel)) {
if (barrierPresentStatus == BarrierStatus.TRUE) {
System.out.println("The headset is connected.");
createNotification(context);
}
else if (barrierPresentStatus == BarrierStatus.FALSE) {
System.out.println("The headset is disconnected.");
}
}
}

When a change occurs in the headset status, this method will receive the information. Here, the value of barrierPresentStatus will determine if headset is connected or disconnected.

At this point, we can detect that headset is just connected, so it is time to send a notification. The following method will take care of that:

private void createNotification(Context context) {
// Create PendingIntent to make user open the application when clicking on the notification
PendingIntent pendingIntent = PendingIntent.getActivity(context, 1234, new Intent(context, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, "channelId")
.setSmallIcon(R.drawable.ic_headset)
.setContentTitle("Cool Headset!")
.setContentText("Want to listen to some music ?")
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel("channelId", "ChannelName", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(mChannel);
}
notificationManager.notify(1234, notificationBuilder.build());
}

Output

When headset is connected, the following notification will be created even if the application is not running:

View attachment 5153591
​

Final Thoughts

We have learned how to use Barrier API of Huawei Awareness Kit with a foreground service to observe the changes in environmental factors even when the application is not running.

As you may notice, the permanent notification indicating that the application is running in the background is not dismissible by the user which can be annoying. Even though these notifications can be dismissed by some third party applications, not every user has those applications, so you should be careful when deciding to build such services.

In this case, observing the headset status was just an example, so it might not be the best scenario for this use case, but Huawei Awareness Kit has many other great features that you can use with foreground services in your projects.

References

You can check the complete project on GitHub.

Note that, you will not be able to run this project because you don’t have the agconnect-services.json file for it. Therefore, you should only take it as a reference to create your own project.