This tutorial provides very basic information on how to use HeaRTDrodi in Android application. There however more resources on-line, which were made by the third-party, who used HeaRTDroid in their solutions. Some of them are listed below:
The tutorial below was prepared by Krzysztof Honkisz.
This tutorial presents a way to create a simple Android application, which utilizes HeaRTDroid - a rule-based inference engine for Android mobile devices, that is based on HeaRT inference engine. Using HeaRTDroid, we will create a simple application, that changes the device audio settings, according to the day of the week and hour.
Clone the repository with application source code: git clone https://KrzysHon@bitbucket.org/KrzysHon/heartdroid-tutorial.git
Repository has two versions of our application, which can be changed using tag functionality.
BASIC
– this version omits the implementation of a few application parts. We strongly encourage you to use this version and learn how to use HeaRTDroid. We will provide some helpful hints to guide you.FINAL
– this version provides a full example of application. If you want to see a fully functional application you can use this version.Note: to change source code using git tags, type command git checkout <tag-name>
Make sure that the downloaded project has few important files:
AndroidManifest.xml
– the most important file in application. The manifest file provides essential information about your app to the Android system. It also defines what kind of permissions the application requires. We already provided all required permission settings for our app, but if you want to improve the basic app with some new functionality (for example something involving locations), make sure that your app has all required permissions.MainActivity.java
– this file includes all the source code of our example. If you use BASIC tag version, you will have to implement some parts of applications, to get a feeling how HeaRTDroid works.layout/activity_main.xml
– this file defines the graphical layout of main activity. If you want to add some more visual elements, this is the file you need to modify.profile_change_rules.hmr
– this file consists the rules used by HeaRTDroid inference engine. If you want to learn more about HMR be sure to check out this page (HMR language quickstart). You can load the file using HWed editor (http://glados.kis.agh.edu.pl/hwed) and analyse this simple decision model.HeaRTDroid provides special entities, which allows to bind the HMR models with external data sources, or to provide a way of interaction with the user. Those entities are:
Callbacks and actions are represented as classes, that implements appropriate Java interfaces (Callback and Action respectively). You can read more about them here.
Starting with BASIC version, you will have to add your implementation of some functionalities:
runTheInference
– this method creates an XTT state and calls rule inference engine to deduce the final state,ScheduledTask
class – this method schedules the use of runTheInference
method in given time intervals and updates the application view to provide information about inferred state and audio settings.As it was said earlier, callback is a Java class that implements interface Callback. This interface has only one method called “execute”. This method runs every time the attribute value is needed. To bind the attribute with a callback, you need to modify the HMR model:
xattr [name: hour, type: hour_type, class: simple, comm: in, callback: 'pl.edu.agh.heartdroid.callbacks.GetSystemHourCallback', abbrev: hour1 ].
In this example, the attribute “hour” is binded with a callback class “GetSystemHourCallback” (remember - you have to provide full name of the class, including the package).
The “execute” method simply reads the current hour and passes the value to given attribute.
public void execute(Attribute attribute, WorkingMemory workingMemory) { Log.i("MYAPP", "Executing GetSystemHourCallback for " + attribute.getName()); int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); try { workingMemory.setAttributeValue(attribute, new SimpleNumeric((double) hour), false); } catch (AttributeNotRegisteredException e) { Debug.debug("CALLBACK", Debug.Level.WARNING, "Callback failed to set value of" + attribute.getName() + ", as the attribute is not registered in the Working Memory."); } catch (NotInTheDomainException e) { Debug.debug("CALLBACK", Debug.Level.WARNING, "Callback failed to set value of" + attribute.getName() + ", as the obtained value was not in the domain of attribute."); } }
In BASIC version you will have to add implementation of GetSystemDayOfWeekCallback
. We've already provided some helper function for
First thing, we need to create a state object. Thanks to the use of callbacks, we don't have to implicitly create input attributes. The only thing to do is creating a new State.
After that, we can run the inference engine. There are three methods we can use. All of them takes the same types of parameters, but their meaning is a bit different:
fixedOrderInference
- we provide all tables names, in an order in which they should be fired,dataDrivenInference
- we provide only the starting tables names. The algorithm crawls the table network and fires only the necessary tablesgoalDrivenInference
- we only give the table which produces the attribute value that we are interested in. The algorithm crawls the table network and fires only the necessary tables.
In our example we use dataDrivenInference
, but you can experiment with other methods.
Since inference methods can throw some exceptions, you need to handle them in some way - we suggest to use Android Log class to print information about the error.
//Creating a new XTTState object state = new State(); try { HeaRT.dataDrivenInference(model, new String[]{"DayTime", "Today"}, new Configuration.Builder() .setInitialState(state) .build()); } catch (AttributeNotRegisteredException e) { Log.e("MYAPP", "exception", e); } catch (UnsupportedOperationException e) { Log.e("MYAPP", "exception", e); }
Finally, you can print the inferred state on the console to check the state attributes:
System.out.println("Printing current state (after inference"); state = HeaRT.getWm().getCurrentState(model); for (StateElement se : state) { System.out.println("Attribute " + se.getAttributeName() + " = " + se.getValue()); }
Similarly to the Callbacks, Actions are binded with rules by HMR model modification. You have to use operator **> to bind rule with actions.
xrule 'Actions'/6: [ daytime eq any, today eq any ] ==> [ action set normal ] **> [ 'pl.edu.agh.heartdroid.actions.SetNormalProfileAction' ] .#1
In this case, the rule is bound with action SetNormalProfileAction
.
Let's take a look at the action implementation:
@Override public void execute(State state) { Log.i("MYAPP", "Executing SetNormalProfileAction"); AudioManager myAudioManager = (AudioManager) Config.context.getSystemService(Context.AUDIO_SERVICE); int systemMaxVolume = myAudioManager.getStreamMaxVolume(AudioManager.STREAM_SYSTEM); int notificationMaxVolume = myAudioManager.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION); int ringMaxVolume = myAudioManager.getStreamMaxVolume(AudioManager.STREAM_RING); myAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, systemMaxVolume, 0); myAudioManager.setStreamVolume(AudioManager.STREAM_NOTIFICATION, notificationMaxVolume, 0); myAudioManager.setStreamVolume(AudioManager.STREAM_RING, ringMaxVolume, 0); myAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL); }
In the first line, an access to AudioManager
is obtained. AudioManager
class is provided by Android OS to allow audio settings manipulation. Your application also needs to obtain proper security permissions, but we have already set everything in AndroidManifest.xml
file.
The rest of the action changes the audio settings to the “normal” profile. Basically, we use those three methods:
int ringMaxVolume = myAudioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
myAudioManager.setStreamVolume(AudioManager.STREAM_RING, ringMaxVolume, 0);
myAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
In BASIC version, there are two actions, without “execute” method implementation. Using the above implementation as basis, provide the missing implementation.
To wrap everything up, we are gonna create a simple implementation of Runnable interface. ScheduledTask is set to run every hour. It calls the runInference method and updates the information printed on screen.
You just have to read the attributes of a state and print the information on screen:
TextView dayView = (TextView) findViewById(R.id.dayView); String dayValue = ((SimpleSymbolic) state.getValueOfAttribute("day")).getValue(); dayView.setText("Day of week: " + dayValue);
There are four TextViews
in MainActivity
layout - dayView
, hourView
, modeView
and volumeView. You can use them to print out all of the information that you find important. Of course, you can add more layout elements if you like.
After that, you can run the application on your device or emulator (note: Android Studio emulator sometimes do not prints all of the TextViews
on the first run – just rerun the app and everything should work). You should see that the device audio settings are changed and that the state information is printed on screen.
If you use FINAL
version of this application, you should see this screen with current information:
And that's it! You've just made your first mobile app that uses the HeaRTDroid library! This simple example was intended to give you some basic information about usage of HeaRTDroid and HMR rules. Feel free to improve it further with your ideas or to create your own project.