[Huawei Developers] XDARoni:
Ever wanted your app to know how bright it is in a room, so it can dynamically change its UI? How about whether one of your users is close to a certain location?

If so, Huawei's Awareness Kit SDK is for you. As long as you're targeting Huawei devices, this SDK makes it easy to query and detect various contextual situations.

Let's get started.

Preparation

First up, make sure you have a Huawei Developer Account. This process can take a couple days, and you'll need one to use this SDK, so be sure to start that as soon as possible. You can sign up at https://developer.huawei.com.

Next, you'll want to obtain the SHA-256 representation of your app's signing key. If you don't have a signing key yet, be sure to create one before continuing. To obtain your signing key's SHA-256, you'll need to use Keytool which is part of the JDK installation. Keytool is a command-line program. If you're on Windows, open CMD. If you're on Linux, open Terminal.

On Windows, you'll need to "cd" into the directory containing the Keytool executable. For example, if you have JDK 1.8 v231 installed, Keytool will be located at the following path:

C:\Program Files\Java\jdk1.8.0_231\bin\

Once you find the directory, "cd" into it:

C: #Make sure you're in the right drive
cd C:\Program Files\Java\jdk1.8.0_231\bin\

Next, you need to find the location of your keystore. Using Android's debug keystore as an example, where the Android SDK is hosted on the "E:" drive in Windows, the path will be as follows:

E:\AndroidSDK\.android\debug.keystore

(Keytool also supports JKS-format keystores.)

Now you're ready to run the command. On Windows, it'll look something like this:

keytool -list -v -keystore E:\AndroidSDK\.android\debug.keystore

On Linux, the command should be similar, just using UNIX-style paths instead.

Enter the keystore password, and the key name (if applicable), and you'll be presented with something similar to the following:

Make note of the SHA256 field.

SDK Setup

Now we're ready to add the Awareness Kit SDK to your Android Studio project. Go to your Huawei Developer Console and click the HUAWEI AppGallery tile. Agree to the terms of use if prompted.

Click the "My projects" tile here. If you haven't already added your project to the AppGallery, add it now. You'll be asked for a project name. Make it something descriptive so you know what it's for.

Now, you should be on a screen that looks something like the following:

Click the "Add app" button. Here, you'll need to provide some details about your app, like its name and package name.

Once you click OK, some SDK setup instructions will be displayed. Follow them to get everything added to your project. You'll also need to add the following to the "dependencies" section of your app-level build.gradle file:

implementation 'com.huawei.hms:awareness:1.0.4.301'

If you ever need to come back to these instructions, you can always click the "Add SDK" button after "App information" on the "Project setting" page.

Now you should be back on the "Project setting" page. Find the "SHA-256 certificate fingerprint" field under "App information," click the "+" button, and paste your SHA-256.

Now, go to the Manage APIs tab on the "Project setting" page. Scroll down until you find "Awareness Kit" and make sure it's enabled.

Now, if you're using obfuscation in your app, you'll need to whitelist a few things for HMS to work properly.

For ProGuard:

-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}

For AndResGuard:

"R.string.hms*",
"R.string.agc*",
"R.string.connect_server_fail_prompt_toast",
"R.string.getting_message_fail_prompt_toast",
"R.string.no_available_network_prompt_toast",
"R.string.third_app_*",
"R.string.upsdk_*",
"R.layout.hms*",
"R.layout.upsdk_*",
"R.drawable.upsdk*",
"R.color.upsdk*",
"R.dimen.upsdk*",
"R.style.upsdk*

That's it! The Awareness Kit SDK should now be available in your project.

Basic Usage

There are quite a few awareness "modules" in this SDK: Time Awareness, Location Awareness, Behavior Awareness, Beacon Awareness, Audio Device Status Awareness, Ambient Light Awareness, and Weather Awareness. Read on to find out how and when to use them.

Each of these modules has two modes: capture, which is an on-demand information retrieval; and barrier, which triggers an action when a specified condition is met.

Time Awareness

Time awareness is a pretty simple API. You can use it to obtain things like the current time for a specific location, or holidays in the area.

Before you start using it, though, you need to have the user grant permission for your app to retrieve the device's location. Declare one of the following permissions depending on your use-case.

<!-- Use this permission if you're only going to be using IP-based location -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- Use this permission if you need more precise location access, such as GPS. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Since both of these permissions are dangerous-level, you'll need to request them at runtime on devices running Marshmallow or later.

Here's some code showing you the basics of capturing time values:

val captureClient = Awareness.getCaptureClient(context)

//Retrieve current time info.
captureClient
//The below run{} block shows possible methods for retrieving
//the current time info. Use the one that best suits your needs.
.run {
//Retrieve the time categories for the current location
//(requires FINE_LOCATION).
timeCategories
//Retrieve the time categories for the current location by IP
//(requires COARSE_LOCATION).
timeCategoriesByIP
//Retrieve the time categories for a specific country.
getTimeCategoriesByCountryCode("US")
//Retrieve the time for the specific lat and long.
//Minimum precision is 2 decimal places.
getTimeCategoriesByUser(12.00, 12.00)
//Retrieve the time categories for the current location
//sometime in the future. The supplied Long should be
//the Unix epoch time in milliseconds. The timestamp
//has to be in the current year.
//Requires COARSE_LOCATION.
getTimeCategoriesForFuture(System.currentTimeMillis() + 1000L)
}
.addOnSuccessListener {
//Retrieval succeeded.

//Get the time categories. A category is either a rough
//indicator of what time it is (morning, afternoon, etc),
//whether or not it's a weekday,
//or whether or not it's currently a holiday.
//The IntArray here should contain three values:
//a time, the weekday status, and the holiday status.
//e.g:
//times[0] == TimeBarrier.TIME_CATEGORY_MORNING
//times[1] == TimeBarrier.TIME_CATEGORY_WEEKDAY
//times[2] == TimeBarrier.TIME_CATEGORY_HOLIDAY
val categories: TimeCategories = it.timeCategories
val times: IntArray = categories.timeCategories
}
.addOnFailureListener {
//Something went wrong!
}

Here's some code showing you how to set up a barrier trigger based on the time:

val barrierClient = Awareness.getBarrierClient(context)

//Create a period barrier from 11AM to 1PM.
//The barrier will trigger when entering and exiting the specified period.
//You can also listen for when the time is currently in a sunrise/sunset state,
//when a user enters and exits a specific Unix timestamp period, when the user
//is in a certain period of the week, and when the user is in a specific time
//category (defined by TimeBarrier).
val periodOfDayBarrier = TimeBarrier.duringPeriodOfDay(TimeZone.getDefault(), 11 * 60 * 60 * 1000L, 13 * 60 * 60 * 1000L)

//Create a receiver Intent for when this barrier is triggered.
//Your PendingIntent should have an explicit BroadcastReceiver class.
//Your receiver should use the BarrierStatus.extract(Intent) method
//in onReceive() to obtain the status.
val periodPendingIntent = PendingIntent.getBroadcast(
context,
1,
Intent("PERIOD_OF_DAY_BARRIER_ACTION"),
PendingIntent.FLAG_UPDATE_CURRENT
)

//Create an update request to add this barrier.
val periodRequest = BarrierUpdateRequest.Builder()
.addBarrier("barrier label", periodOfDayBarrier, periodPendingIntent)
.build()
barrierClient.updateBarriers(periodRequest)

Weather Awareness

Weather awareness allows you to query the current weather state of the user's location or a specified location.

Before you start using it, though, you need to have the user grant permission for your app to retrieve the device's location. Declare the following permission:

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

Since this permission is dangerous-level, you'll need to request it at runtime on devices running Marshmallow or later. Location access is optional if you're just getting weather for a pre-defined position.

Here's some code showing the different ways to query the weather:

val captureClient = Awareness.getCaptureClient(context)

//Retrieve current weather info.
captureClient
//The below run{} block shows various ways to retrieve the
//current weather info.
.run {
//Use the device IP to retrieve weather info.
//Requires COARSE_LOCATION.
weatherByIP
//Use the device's exact location to retrieve
//weather info.
//Requires FINE_LOCATION.
weatherByDevice
//Get the weather for a specified position.
getWeatherByPosition(
WeatherPosition().apply {
//The city parameter is required. The others
//are optional, but can help with accuracy if
//supplied.
this.city = "Paris"
this.country = "FR"
// this.province = "N/A"
// this.county = "N/A"
// this.district = "N/A"
this.locale = "fr_FR"
}
)
}
.addOnSuccessListener {
val status = it.weatherStatus

//Get the weather AQI, like CO, NO2, PM10, PM2.5, SO2, etc.
val aqi = status.aqi

//Get daily weather info for the next 7 days.
val daily: MutableList<DailyWeather> = status.dailyWeather
//Get hourly weather info for the next 24 hours.
val hourly: MutableList<HourlyWeather> = status.hourlyWeather
//Get the living index for the current day and 1-2 days in the future.
val live: MutableList<LiveInfo> = status.liveInfo

//Get the current weather situation.
val situation = status.weatherSituation

val currentAqiValue = aqi.aqiValue
val currentCOLevel = aqi.co
val currentNO2Level = aqi.no2
val currentO3Level = aqi.o3
val currentPM10Level = aqi.pm10
val currentPM25Level = aqi.pm25
val currentSO2Level = aqi.so2

daily.forEach {
//Get the AQI value for this day.
val aqiValue = it.aqiValue
//Get the Unix-epoch timestamp for this date.
val time = it.dateTimeStamp
//Get the max temperature for this day.
val maxTempC = it.maxTempC
val maxTempF = it.maxTempF
//Get the min temperature for this day.
val minTempC = it.minTempC
val minTempF = it.minTempF
}

hourly.forEach {
//Whether this hour is daytime or nighttime.
//true for daytime, false for nighttime.
val isDayNight = it.isDayNight
//Probability it will rain. Percentage.
val rainPropbability = it.rainprobability
//Temperature at this hour.
val tempC = it.tempC
val tempF = it.tempF
//Weather type at this hour.
//Possible values can be found in the WeatherId
//interface.
val weatherType = it.weatherId
}

live.forEach {
//Get the type of living index for this LiveInfo.
//1: dressing index
//2: sports index
//3: coldness index
//4: car washing index
//5: tourism index
//6: UV index
val type = it.code
//A list of index levels for the current day and the next 1-2.
it.levelList.forEach {
val time = it.dateTimeStamp
val level = it.level
}
}

//The city this Situation applies to.
val city = situation.city
val cityCode = city.cityCode
val cityName = city.name
val cityProvince = city.provinceName
val cityTimezone = city.timeZone
situation.situation.apply {
//Possible information you can get from the current situation.
this.humidity
this.pressure
this.realFeelC
this.realFeelF
this.temperatureC
this.temperatureF
this.updateTime
this.uvIndex
this.windDir
this.windLevel
this.windSpeed
}
}

Behavior Awareness

Behavior Awareness allows you detect what the user is currently doing, and trigger events based on behavior changes.

Before you start using it, though, you need to have the user grant permission for your app to recognize the user's activity. Declare the following permissions:

<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="com.huawei.hms.permission.ACTIVITY_RECOGNITION" />

Since both of these permissions are dangerous-level, you'll need to request them at runtime on devices running Marshmallow or later.

Here's an example of how you can query the user's current behavior:

val captureClient = Awareness.getCaptureClient(context)

//Check the most likely behavior of the user.
//Requires ACTIVITY_RECOGNITION
captureClient.behavior.addOnSuccessListener {
//Check the list of probable behaviors.
it.behaviorStatus.probableBehavior.forEach {
//Types can be found in the BehaviorBarrier class.
it.type
//How likely this behavior is.
it.confidence
}
//Get the most likely user behavior.
it.behaviorStatus.mostLikelyBehavior.apply {
//Types can be found in the BehaviorBarrier class.
this.type
//How likely this behavior is.
this.confidence
}
//Get the time in milliseconds used for this detection.
it.behaviorStatus.elapsedRealtimeMillis
//Get the detection time in Unix-epoch form.
it.behaviorStatus.time
}

Here's an exmaple of how you can set up an event trigger based on specific user behavior:

val barrierClient = Awareness.getBarrierClient(context)

//Create a behavior barrier. In this case, detect
//if the user is keeping still.
//You can also set up barriers for "beginning" and "ending"
//any behaviors defined in BehaviorBarrier.
val keepStillBarrier = BehaviorBarrier.keeping(BehaviorBarrier.BEHAVIOR_STILL)

//Create a receiver Intent for when this barrier is triggered.
//Your PendingIntent should have an explicit BroadcastReceiver class.
//Your receiver should use the BarrierStatus.extract(Intent) method
//in onReceive() to obtain the status.
val keepStillPendingIntent = PendingIntent.getBroadcast(
context,
3,
Intent("KEEP_STILL_BARRIER_ACTION"),
PendingIntent.FLAG_UPDATE_CURRENT
)

//Create an update request to add this barrier.
val keepStillRequest = BarrierUpdateRequest.Builder()
.addBarrier("barrier label", keepStillBarrier, keepStillPendingIntent)
.build()
barrierClient.updateBarriers(keepStillRequest)

Location Awareness[SIZE]

Location Awareness allows you to query the user's current or last-known location, and set up triggers based on where the user goes.

Before you start using it, though, you need to have the user grant permission for your app to retrieve the device's location. Declare the following permission:

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

Since this permission is dangerous-level, you'll need to request it at runtime on devices running Marshmallow or later.

Here's how to retrieve the user's location:

val captureClient = Awareness.getCaptureClient(context)

//Get the user's location.
//Requires FINE_LOCATION.
captureClient
.run {
//Get the cached last-known location of the user.
location
//Get the current location of the user.
currentLocation
}
.addOnSuccessListener {
val location = it.location
//The Location class has a bunch of properties
//for judging the user's location.
}

Here's how to set up a trigger based on the user's location:

val barrierClient = Awareness.getBarrierClient(context)

//Create a location barrier for a specified radius around
//a certain location. In this case, the barrier will trigger
//when the user enters the specified range.
//You can also define an "exit" barrier.
val enterBarrier = LocationBarrier.enter(12.3456, 12.3456, 200.0)

//Create a receiver Intent for when this barrier is triggered.
//Your PendingIntent should have an explicit BroadcastReceiver class.
//Your receiver should use the BarrierStatus.extract(Intent) method
//in onReceive() to obtain the status.
val enterPendingIntent = PendingIntent.getBroadcast(
context,
2,
Intent("ENTER_LOCATION_BARRIER_ACTION"),
PendingIntent.FLAG_UPDATE_CURRENT
)

//Create an update request to add this barrier.
val enterRequest = BarrierUpdateRequest.Builder()
.addBarrier("barrier label", enterBarrier, enterPendingIntent)
.build()
barrierClient.updateBarriers(enterRequest)

Headset Awareness

Headset Awareness allows you to check if headphones are currently connected, and carry out actions when the connection state changes.

Before you start using it, though, you need permission for your app to manage Bluetooth connections. Declare the following permission:

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

Here's how you can check the current headset connection state:

val captureClient = Awareness.getCaptureClient(context)

//Get whether the user has headphones connected.
captureClient.headsetStatus
.addOnSuccessListener {
//The current headset status (connected, disconnected, unknown).
//Constants are in the HeadsetStatus interface.
val status = it.headsetStatus.status
}

Here's how you can set up a trigger based on the connection state:

val barrierClient = Awareness.getBarrierClient(context)

//Create a headset connection barrier. This will trigger when
//a headset is connecting. You can also detect a "disconnecting"
//event and a "keeping" event.
val headsetConnectingBarrier = HeadsetBarrier.connecting()

//Create a receiver Intent for when this barrier is triggered.
//Your PendingIntent should have an explicit BroadcastReceiver class.
//Your receiver should use the BarrierStatus.extract(Intent) method
//in onReceive() to obtain the status.
val headsetConnectingPendingIntent = PendingIntent.getBroadcast(
context,
4,
Intent("HEADSET_CONNECTING_BARRIER_ACTION"),
PendingIntent.FLAG_UPDATE_CURRENT
)

//Create an update request to add this barrier.
val headsetConnectingRequest = BarrierUpdateRequest.Builder()
.addBarrier("barrier label", headsetConnectingBarrier, headsetConnectingPendingIntent)
.build()
barrierClient.updateBarriers(headsetConnectingRequest)

Bluetooth Car Stereo Awareness

This Awareness module allows you to detect when the user is connected to a Bluetooth car stereo, and carry out actions when that connection state changes.

Before you start using it, though, you need permission for your app to manage Bluetooth connections. Declare the following permission:

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

Here's how you can check if the user is currently connected to a car stereo:

val captureClient = Awareness.getCaptureClient(context)

//Get the current Bluetooth status for a specific
//device type.
//e.g: 0 is a car stereo.
captureClient.getBluetoothStatus(/* deviceType */ 0)
.addOnSuccessListener {
//Get the current connection status for the
//device type.
//Values can be found in the BluetoothStatus interface.
it.bluetoothStatus.status
}

Here's how to set up a trigger event:

val barrierClient = Awareness.getBarrierClient(context)

//Create a Bluetooth connection barrier. This will trigger when
//a matching Bluetooth device is connecting. You can also detect
//a "disconnecting" event.
val btConnectingBarrier = BluetoothBarrier.connecting(/* deviceType */ 0)

//Create a receiver Intent for when this barrier is triggered.
//Your PendingIntent should have an explicit BroadcastReceiver class.
//Your receiver should use the BarrierStatus.extract(Intent) method
//in onReceive() to obtain the status.
val btConnectingPendingIntent = PendingIntent.getBroadcast(
context,
4,
Intent("BT_CONNECTING_BARRIER_ACTION"),
PendingIntent.FLAG_UPDATE_CURRENT
)

//Create an update request to add this barrier.
val btConnectingRequest = BarrierUpdateRequest.Builder()
.addBarrier("barrier label", btConnectingBarrier, btConnectingPendingIntent)
.build()
barrierClient.updateBarriers(btConnectingRequest)

Ambient Light Awareness

Ambient Light Awareness allows you to detect the current ambient light level, and set up events to trigger when that level reaches certain values.

Here's how to query the ambient light level:

val captureClient = Awareness.getCaptureClient(context)

//Get the current light intensity.
captureClient.lightIntensity
.addOnSuccessListener {
//The current light intensity, as a floating point
//illuminance value.
it.ambientLightStatus.lightIntensity
}

Here's how to set up triggers based on light level changes:

val barrierClient = Awareness.getBarrierClient(context)

//Create a light level barrier to detect when the
//light level goes above a certain value in lux.
//You can also detect a "below" event and a "range"
//event.
val aboveLightBarrier = AmbientLightBarrier.above(2500f)

//Create a receiver Intent for when this barrier is triggered.
//Your PendingIntent should have an explicit BroadcastReceiver class.
//Your receiver should use the BarrierStatus.extract(Intent) method
//in onReceive() to obtain the status.
val aboveLightPendingIntent = PendingIntent.getBroadcast(
context,
4,
Intent("ABOVE_LIGHT_CONNECTING_BARRIER_ACTION"),
PendingIntent.FLAG_UPDATE_CURRENT
)

//Create an update request to add this barrier.
val aboveLightRequest = BarrierUpdateRequest.Builder()
.addBarrier("barrier label", aboveLightBarrier, aboveLightPendingIntent)
.build()
barrierClient.updateBarriers(aboveLightRequest)

Beacon Awareness

Beacon Awareness allows you to detect whether beacons that match a specified filter are in range, or trigger an event when a beacon appears or disappears.

Before you start using it, though, you need to have the user grant permission for your app to retrieve the device's location, and request permission to use Bluetooth. Declare the following permissions:

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

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

Since the location permission is dangerous-level, you'll need to request it at runtime on devices running Marshmallow or later.

Here's how you can query beacons:

val captureClient = Awareness.getCaptureClient(context)

//Query nearby beacon status based on a filter.
//Requires FINE_LOCATION and BLUETOOTH.
captureClient.getBeaconStatus(BeaconStatus.Filter.match("namespace", "type", /* content */ ByteArray(1)))
.addOnSuccessListener {
//Loop through all the beacons found.
it.beaconStatus.beaconData.forEach {
it.content
it.namespace
it.type
}
}

Here's how you can set up events based on beacon availability:

//Create a beacon barrier. This will trigger when
//a matching beacon is discovered.
//You can also trigger barriers based on whether a matching
//beacon is kept or missed.
val beaconBarrier = BeaconBarrier.discover(BeaconStatus.Filter.match("namespace", "type", /* content */ ByteArray(1)))

//Create a receiver Intent for when this barrier is triggered.
//Your PendingIntent should have an explicit BroadcastReceiver class.
//Your receiver should use the BarrierStatus.extract(Intent) method
//in onReceive() to obtain the status.
val beaconPendingIntent = PendingIntent.getBroadcast(
context,
4,
Intent("BEACON_BARRIER_ACTION"),
PendingIntent.FLAG_UPDATE_CURRENT
)

//Create an update request to add this barrier.
val beaconRequest = BarrierUpdateRequest.Builder()
.addBarrier("barrier label", beaconBarrier, beaconPendingIntent)
.build()
barrierClient.updateBarriers(beaconRequest)

Conclusion

And that's it!

The Awareness Kit is quite a comprehensive detection and event SDK. If you're developing a context-aware app for Huawei devices, this is definitely the library for you.

Make sure to check out Huawei's full documentation for updates and more details.