MessageOnTap's Plugin Core Architecture - MessageOnTap/MessageOnTap_API GitHub Wiki

MessageOnTap aims to be extensible to support users as much need as possible. To accomplish this, we design MessageOnTap as a framework for developers to create intelligent plugins for instant messaging apps. An IM plugin is just a normal Android app written on top of MessageOnTap API which is mainly used to communicate with the MessageOnTap-core running on the user's phone.

To work with the MessageOnTap framework, an IM plugin would need to set up the following:

  1. in AndroidManifest.xml.
  2. in build.gradle.
  3. extends a MessageOnTapPlugin service.

Dependency

Add MessageOnTap API dependency to the build.gradle in your local project.

...
    compile 'messageontap:api:0.3.2'
...

AndroidManifest.xml

Your app must declare your plugin service with an intent-filter in its manifest. You can choose your own service name; in the following example, it is "SamplePlugin"

<service android:name=".SamplePlugin"
    android:label="@string/plugin_title">
    <intent-filter>
        <action android:name="edu.cmu.chimps.messageontap_prototype.Plugin" />
    </intent-filter>
    <meta-data android:name="description"
        android:value="@string/plugin_description" />
    <meta-data android:name="settingsActivity"
        android:value=".SamplePluginSettingsActivity" />
</service>

MessageOnTapPlugin Service

There are five steps for creating a new MessageOnTapPlugin Service in your plugin:

  1. Register semantic templates
  2. Create a session
  3. Create a task
  4. Handle task responses
  5. End a session

Register semantic templates

/**
  * Return the semantic templates of this plugin. This will be called when
  * MessageOnTap is started (when this plugin is already enabled) or when
  * this plugin is being enabled.
  *
  * @return semantic templates
*/

@Override
protected Set<SemanticTemplate> semanticTemplates() {
    Set<SemanticTemplate> semanticTemplates = new HashSet<>();    
    /**
      * Semantic Template I:
      * This template can recognize all the incoming messages with phone numbers.
      * Examples: {Mike} just changed his number to {412-100-1000}.
      *           You can call {412-200-2000} for help. 
      * An optional tag will be added in the following code.
      */

    Set<Tag> phoneNumberTags = new HashSet<>();
  
    phoneNumberTags.add(new Tag(ServiceAttributes.Internal.TAG_PHONE_NUMBER,
                new HashSet<String>(), Tag.Type.MANDATORY);

    phoneNumberTags.add(new Tag(ServiceAttributes.Internal.TAG_PERSON, new HashSet<>(), TAG_TYPE.OPTIONAL);
    semanticTemplates.add(new SemanticTemplate(
                  .name("phone_number")
                  .tags(phoneNumberTags)
                  .direction(Direction.INCOMING);
  
    /**
      * Semantic Template II:
      * The following code defines a customized tag with four keywords, "number", "phone", "telephone", "cell".
      * This template can recognize all the incoming messages that request phone numbers. 
      * Examples: What is {your} {phone number}?
      *           Do you have {Mike}'s {telephone number}?
      * A customized tag will be added in the following code.
      */

    Set<Tag> phoneNumberRequestTags = new HashSet<>();

    phoneNumberRequestTags.add(new Tag(ServiceAttributes.Internal.TAG_PERSON, new HashSet<>(), TAG_TYPE.MANDATORY);
    phoneNumberRequestTags.add(new Tag("phone_number_keyword", 
                                   new HashSet<String>(Arrays.asList({"number", "phone", "telephone", "cell"})),
                                   TAG_TYPE.MANDATORY));
    semanticTemplates.add(new SemanticTemplate("phone_number_request",phoneNumberRequestTags, Direction.INCOMING, 
                                           Mood.INTERROGTIVE));
  
    return semanticTemplates;
}

Initialize A New Session

@Override
protected void initNewSession(long sid, HashMap<String, Object> params) throws Exception {
    Log.e(TAG, "Session created here!");
    Log.e(TAG, JSONUtils.hashMapToString(params));
    Log.e(TAG, "parse tree: " + ((ParseTree) JSONUtils.jsonToSimpleObject((String) params.get("tree")
                                               , JSONUtils.TYPE_PARSE_TREE)).toString());
    HashMap<String, Object> reqParams = new HashMap<>();
    reqParams.put("key1", "value1");
    reqParams.put("key2", "value2");
    reqParams.put("key3", "value3");
    // TID is something we might need to implement stateflow inside a plugin.
    long tid = createTask(sid, MethodConstants.PMS_TYPE, "test", params);
}

Now Create A New Task

With a task, you can easily connect make:

  • User Interface showing on user's phone
  • Actions such as making phone call and replying messages
  • PersonGraph Search to get multi-source Personal data
//Example of Showing UI
long sid = SESSION_ID;
createTask(sid, MethodConstants.UI_TYPE,
                MethodConstants.UI_METHOD_SHOW_BUBBLE, params);
//Example of Action Retrieve
long sid = SESSION_ID;
MethodConstants.ACTION_TYPE,
            MethodConstants.ACTION_METHOD_SETTEXT, params)
//Example of Personal Graph Retrieve
long sid = SESSION_ID;
params.put(ServiceAttributes.Graph.SYNTAX_TREE,
                    JSONUtils.simpleObjectToJson(treeForSearch1.get(sid), JSONUtils.TYPE_PARSE_TREE));
createTask(sid, MethodConstants.GRAPH_TYPE, 
           MethodConstants.GRAPH_METHOD_RETRIEVE, params);

Handle the Task Respond

After a task is finished, the function newTaskResponded() will be called. Developers can choose to create a new task again or end this session.

@Override
protected void newTaskResponded(long sid, long tid, HashMap<String, Object> params) throws Exception {
    Log.e(TAG, "Got task response!");
    Log.e(TAG, JSONUtils.hashMapToString(params));
    Log.e(TAG, "Ending session " + sid);
    endSession(sid);
    Log.e(TAG, "Session ended");
}

End Session

If your plugin finished all the tasks and ready for the next time of trigger match, call endSession(long sid) to end the current session.

long sid = SESSION_ID;
endSession(sid);

⚠️ **GitHub.com Fallback** ⚠️