My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
Historify  
Event aggregator application that supports openness.
Updated Aug 22, 2011 by berke.an...@gmail.com

Introduction

Historify is an Android application for managing contact-related events gathered from various sources (call log, messaging app, 3rd party applications and extensions). The different events are aggregated seamlessly and displayed in an integrated view. The application also make interaction easier by offering options to create new events (e.g. initiate phone calls, compose a new message).

The key element of Historify is to support openness: with the help of the Historify BridgeLibrary, developers could easily connect their application with Historify's aggregation engine: any application could generate events and provide options for interaction.

The aim of wiki page is to give an introduction for Android developers on how to make use of Historify's services in their works.

Basic concepts

Event aggregation

By event, we means something that occurred in the past between the owner of the device and one of her/his contacts and could be associated with an application that is currently installed on the device. Several examples are: a phone call in the past, an SMS sent by the contact, a message in the Facebook application.

During event aggregation, Historify collects these event's data stored in different ContentProviders, filters for a particular contact and displays them on a time-ordered list (timeline).

By event provider, we mean a single source of events. Basically, there are two kinds of event providers:

  • Internal sources
  • External sources

Internal sources are realized by Historify itself and handle system provided events (Telephony and Messaging). They are added to the list of sources by default.

External sources are realized by 3rd party applications (a.k.a. clients) and added to the list of sources on installing the client application's package. After registering an event source, external applications are able to share their internal events with Historify (this type of source is called a SharedSource).

There is also a special external source called QuickPost (realized by Historify) which is intended to simplify event sharing for client applications: With the help of this provider, the client could simply add an event to the timeline by a single Intent call instead of registering a provider and storing events internally.

The conceptual differences between the two ways of adding data (SharedSource and QuickPost) are described in the next section.

Ways of adding data

Historify provides a Service-based Intent interface (the Bridge) for client applications to add their event data to the aggregated event set.

To facilitate dealing with the Intent calls, the Historify BridgeLibrary have been introduced. The library offers two ways of adding event data to the timeline:

  • QuickPost
  • SharedSource

QuickPost is a simple way of adding an event to the timeline by a single Intent call. It is intended to be used by applications that generate occasional, user-driven events.

Good example could be a video rental service which lets the user set whom the rented movie she watched with. The app could ask the user if she wanted to post the event ("we watched the movie X") to Historify, and if the users allows it, use the QuickPost interface to add the event to the timeline.

Client application could also get connected to Historify by registering a SharedSource, which case all the necessary components (BroadcastReceiver, ContentProvider) are realized by the client application. Since the events are stored and managed by the client itself, Historify only uses the URI-based ContentResolver interface to query the events of a contact when aggregating event data.

This method is intended to be used by applications with a background service that synchronizes large set of event data. Good example could be a Facebook client application.

Both QuickPost and SharedSource let the client application describe the event source with the same data fields including the name of the source, brief description, and an URI of an icon resource which is used when displaying events of the client. Several optional fields are only supported by the SharedSource interface, e.g. by defining a custom icon loading strategy for a SharedSource, a client application could provide customized icons for each event instead of using the source's default icon on every event.

Both QuickPost and SharedSource let the client application describe the shared event with the same data fields including time of the event, the contact its associated with, the originator of the event (user, contact, both) and brief description. Several optional fields are only supported by the SharedSource interface.

Detailed description of the supported fields is available at Historify#Dealing_with_QuickPost and Historify#Dealing_with_a_SharedSource

Note that in case of QuickPosting, both source metadata and event data is passed via the Intent, while a SharedSource's metadata is passed to Historify when registering the source, and only event data is passed on event aggregation.

The following table compares the two described methods by summarizing the differences listed above.

QuickPost SharedSource
Source registration not needed registering source on application install (source metadata)
Adding events Intent call (source metadata + event data) inserting values into the ContentProvider (event data)
Recommended usage occasional, user-driven posts large set of events, synchronized in the background
Persistence events are available until clients get uninstalled client manages events
Custom event icons not supported supported
Source configuration (see next section) not supported supported
Interaction (see next section) supported supported

Source configuration

Source configuration is available for client applications using the SharedSource method. When registering a SharedSource, a client could provide an action of an Intent fired by Historify when the user selects the 'More' button of a the registered source on the 'my sources' Activity. Based on this field, the client application could define an Activity to let the user customize the behavior of the client (e.g. which type of data is shared) and integrate it seamlessly with the source settings of Historify.

Note that QuickPost does not support source configuration.

See Historify#Dealing_with_a_SharedSource for details.

Interaction

Historify offers a set of options for the user to create new events (e.g. initiate phone calls, compose a new message). By providing an optional field supported by both QuickPost and SharedSource, client applications could register their own shortcuts with an Intent for interaction appropriate for the client.

For example, a movie rental app could launch an Activity to rent a movie recommended for the contact based on rental history and also may send a message to the contact to invite her/him to watch the selected movie. A Facebook client could launch the message composer.

The shortcut is described with an action label and the Intent to be fired when the interact action gets selected. The default icon of your event source will be used in the menu when displaying your shortcut.

See Historify#Dealing_with_QuickPost and Historify#Dealing_with_a_SharedSource for details.

BridgeLibrary API documentation

The Historify BridgeLibrary is a Java library allowing Android applications to access Historify's Intent-based interface.

With the help of this package, Android developers could connect their applications with the event aggragetion engine by registering their own event sources, publishing events and adding custom Intents to integrate their applications' functionality into the UI of Historify.

The latest version of the library, javadoc, and sample applications that uses the library's functions are available for download. See Historify#Further_materials.

Usage

To start working with the library, you will need to download the latest jar build available at the Download section. See Historify#Further_materials for list of related downloads.

To set up the library in the Eclipse IDE environment, the following steps are required:

  1. Put .jar file into the libs directory of your Android project.
  2. Add .jar to the project's build path on the Project \ Properties \ Java Build Path dialog.
  3. The library is ready to use.

To see your registered source, generated events, etc. in the event aggregation environment, it is recommended to install the latest version of the Historify application on your testing device / emulator. The .apk is available for download. See Historify#Further_materials.

It is also recommended to add the following permission to your AndroidManifest.xml file:

<uses-permission android:name="org.openintents.historify.permission.USE_BRIDGE" />

This step is optional, however, it is intended to inform the user that your application is able to interact with the services of Historify.

After setting up a project, you could use the services of the Bridge by instantiating a HistorifyBridge class, which is a component responsible for communicating with Historify's Bridge Service.

HistorifyBridge bridge = new HistorifyBridge(R.drawable.icon);

The functions of the Bridge class are described in the forthcoming sections.

Dealing with QuickPost

Purpose

QuickPost is a simple way of adding an event of an external application to the timeline by a single Intent call. It is intended to be used by applications that generate occasional, user-driven events. For a comparison to other ways of creating events, please check out the Historify#Ways_of_adding_data section.

Can I QuickPost?

Writing an application that is intended to use the QuickPost interface, it is elementary to determine that Historify is installed and the Bridge is enabled on the user’s device. If Historify is not present in the current environment, it is unnecessary to set up a QuickPostContext, post events, or even display the UI elements used for QuickPosting. To determine that there is a Service able to handle your QuickPosts, you may use the Bridge's canQuickPost() method:

boolean enableQuickPosting = bridge.canQuickPost(context);
if(enableQuickPosting) {
  //setting up a QuickPostContext
} else {
  //disable functions, hide UI elements, etc.
}

Setting up a QuickPostContext

Before your application could produce QuickPosts, it is required to set up a proper QuickPostContext. The context stores all the information about your application necessary for displaying content in Historify. The following parameters are supported:

Parameter namePurposeTypeMandatory
Source nameThe name of your event source.Stringyes
Source descriptionBrief description of your event source.Stringno
Icon URIThe URI of a drawable resource used as an icon when displaying events in Historify.URIno
Event intentThe action of an Intent that will be fired by Historify when the user selects an event on timeline posted by your application. Note that to let your receiver component identify which event has been selected by the user, Actions.EXTRA_EVENT_KEY will be passed as Intent extra.Stringno
Interact intentThe action of an Intent that will be fired by Historify when the user selects the interaction type associated with your application in the timeline popup menu. Note that to let your receiver component identify which contact has been selected by the user, Actions.EXTRA_CONTACT_LOOKUP_KEY will be passed as Intent extra.Pair of Strings (action, label)no
VersionThe version code of your event source. By incrementing the version value, client classes could update the contextual data stored in Historify. Re-registering a context with the same version value as previously will not take effect.intyes

The following code snippet sets up a context with some basic parameters:

QuickPostContext quickPostContext = new QuickPostContext(
  "My App",
  "This is my app with some cool events.",
  null, //icon URI is null, default app icon will be used
  SOURCE_VERSION);

quickPostContext.setEventIntent("com.example.myapp.ACTION_SHOW_EVENT");
quickPostContext.setInteractIntent("com.example.myapp.ACTION_INTERACT", "Open My App");

bridge.setQuickPostContext(quickPostContext);

Note that you have to set up a context only once (e.g. after instantiating the HistorifyBridge class. All events posted after setting up the context will use the information you provided in setQuickPostContext().

Adding QuickPost events

After successfully setting up a QuickPostContext, your event(s) could be added to the timeline by simply calling HistorifyBridge.quickPost(Context context, EventData eventData).

The eventData parameter used to describe the event that has to be added. If the parameter is valid, the BridgeLibrary passes it to Historify supplemented by the metadata of your source you provided earlier. The data will be saved in Historify considering the following rules:

  • The EventData you provided will be saved and displayed when it is appropriate.
  • If you have not posted events yet, the metadata of your source will be saved including the version code you provided.
  • If you have already posted events, the version code you provided will be checked. If it is greater that the previously stored, your source's metadata will be updated with the newly provided values.

The following table summarizes which parameters are supported by the EventData class hence could be used when posting events.

Parameter namePurposeTypeMandatory
Event keyYou may use this field as a unique identifier for the events your application generates.Stringno
Contact keyLookup key of the Contact which the event is associated with.Stringyes
Published timeThe time of the event.long (Unix UTC time in millis)yes
MessageBrief description of the event.Stringyes
OriginatorThe person whom the event is initiated by.Enum (user, contact, both)yes

A code snippet that illustrates usage:

String contactLookupKey = getSelectedContact(); //provides a contact key 

EventData eventData = new EventData(
  "event_1", 
  contactLookupKey, 
  System.currentTimeMillis(), 
  "This is an incoming event.", 
  Originator.contact);

bridge.quickPost(context, eventData);

Persistence

Your QuickPosts, including the metadata of your source, will be stored persistently by Historify.

Note that you are not able to modify previously added events, however, you are able to modify previously added source metadata by incrementing the version code.

Your events and metadata will be removed if your application's package gets removed from the system.

Dealing with a SharedSource

Purpose

Client application could get connected to Historify by registering a SharedSource. This method of creating events is intended to be used by applications with a background service that synchronizes large set of events. The ContentProvider that stores the data is realized by the application itself, thus it is able to control the full life-cycle of the events. Historify only uses the URI-based ContentResolver interface to query the events of a contact when aggregating event data.

For a comparison to other ways of creating events, please check out the Historify#Ways_of_adding_data section.

Registering a SharedSource

To add an external event provider to Historify, client application have to register the event source via the Historify Bridge Service. The process of the source registration is the following:

  1. User installs Historify.
  2. User installs a 3rd party event provider application that hosts an external source (SharedSource).
  3. Historify receives the ACTION_PACKAGE_ADDED system broadcast intent, which indicates that a new application has been installed.
  4. The application might contain a SharedSource, so Historify addresses the new application to identify itself. This is achieved by broadcasting the BROADCAST_REQUEST_REGISTER_SOURCE Intent. The package name of the newly installed application is added as Intent extra.
  5. The newly installed client declares a Broadcast Intent Receiver which receives this Intent. The package name Intent extra equals to the client's package name which indicates that the Intent is sent to this particular application. (Previously installed clients also receive the Broadcast Intent, but they could filter it out).
  6. The newly installed Client registers itself to Historify (by sending the REGISTER_SOURCE Intent) via the Bridge Service.
  7. The Bridge Service adds the newly registered SharedSource to Historify as a new external source, and posts a Notification to inform the user about the successful registration.

Note that the Bridge also supports the registration of SharedSources that have been installed before Historify. To detect these applications, Historify also sends the BROADCAST_REQUEST_REGISTER_SOURCE on its first startup, but without adding the package name Intent extra. In this case, all clients will respond to the Intent, and they all will be calling the BridgeService to register themselves.

As a conclusion, the client application should declare a Broadcast Receiver in its manifest to detect the BROADCAST_REQUEST_REGISTER_SOURCE Intent, and make a call to the Bridge to register its SharedSource. To facilitate dealing with this process, a special BroadcastReceiver ( HistorifyBridge.RequestReceiver) is included in the library.

The following steps guide you through on the process of adding the Receiver to your project and invoking the method for source registration.

  1. Add a new class to your application which is derived from the RequestReceiver class.
  2. public class HelloReceiver extends HistorifyBridge.RequestReceiver {
    
      @Override
      protected void onRequestRegister(Context context) {
    
        //this method gets called when BROADCAST_REQUEST_REGISTER_SOURCE 
        // is addressed to your application
      }
    }
  3. Do not forget to add the <receiver> tag to your manifest. It is crucial to define an Intent filter for catching the register request.
  4. <receiver android:name=".HelloReceiver">
      <intent-filter>
        <action
          android:name="org.openintents.historify.REQUEST_REGISTER_SOURCE" />
        <category android:name="android.intent.category.DEFAULT" />
       </intent-filter>
    </receiver>
  5. As you might noticed, the RequestReceiver offers an abstract method called onRequestRegister() which is invoked when your application may register its source in Historify. The recommended way of registering is to call the HistorifyBridge.registerSource() method. The method takes a SourceData instance as a parameter, which is for describe your SharedSource. The following table summarizes the fields supported.

Parameter namePurposeTypeMandatory
NameThe name of your event source.Stringyes
AuthorityThe authority of the ContentProvider used by Historify to query the events of your SharedSource.Stringyes
DescriptionBrief description of your event source.Stringno
Icon URIThe URI of a drawable resource used as an icon of your source in Historify.URIno
Event intentThe action of an Intent that will be fired by Historify when the user selects an event on timeline created by your application. Note that to let your receiver component identify which event has been selected by the user, Actions.EXTRA_EVENT_ID and Actions.EXTRA_EVENT_KEY will be passed as Intent extra.Stringno
Config intentThe action of an Intent that will be fired by Historify when the user selects the source on the source configuration activity. Based on this field, the client application could define an Activity to let the user customize the behavior of the client and integrate it seamlessly with the source settings of Historify.Stringno
Interact intentThe action of an Intent that will be fired by Historify when the user selects the interaction type associated with your application in the timeline popup menu. Note that to let your receiver component identify which contact has been selected by the user, Actions.EXTRA_CONTACT_LOOKUP_KEY will be passed as Intent extra.Pair of Strings (action, label)no
Icon loading strategyThis enum field defines that timeline icons for a particular source should be loaded from the source itself, or the custom icon of the event should be used.Enum (useSourceIcon, useEventIcon)no, default value is useSourceIcon
VersionThe version code of your event source. By incrementing the version value, client classes could update the source data stored in Historify. Re-registering a source the same version value as previously will not take effect.intyes

The following code snippet registers a SharedSource with some basic parameters:

public class HelloReceiver extends HistorifyBridge.RequestReceiver {

  @Override
  protected void onRequestRegister(Context context) {

    SourceData sourceData = new SourceData(
      "My Source", 
      YOUR_SHARED_SOURCE_AUTHORITY, 
      "My SharedSource with some cool events.", 
      null, //icon URI is null
      SOURCE_VERSION);
    
    sourceData.setConfigIntent("com.example.myapp.ACTION_CONFIG");
    sourceData.setEventIntent("com.example.myapp.ACTION_SHOW_EVENT");
    sourceData.setInteractIntent(
      "com.example.myapp.ACTION_INTERACT",
      "Open My App");
    sourceData.setIconLoadingStrategy(
      IconLoadingStrategy.useSourceIcon);	
	
    bridge.registerSource(context,sourceData);
  }
}

After your source got registered, Historify's event aggregation engine queries the ContentProvider of which authority have been passed to the register method. See next section for details.

Providing events

After your source got registered, Historify's event aggregation engine queries the ContentProvider of which authority have been passed to the register method. This must be a ContentProvider that contributes the events of the contacts in the format understood by Historify.

The following table summarizes the URIs that a SharedSource's ContentProvider may support by default. Note that only a subset of the URIs are used currently by Historify, the others are added for the client's benefit and for future development.

URIPurposeUsed by event aggregator
content://YOUR_SHARED_SOURCE_AUTHORITY/eventsAll of the events stored in your SharedSource.yes
content://YOUR_SHARED_SOURCE_AUTHORITY/events/#A single event identified by the given # ID.no
content://YOUR_SHARED_SOURCE_AUTHORITY/events/contacts/*The set of events filtered for a particular contact identified the given * lookup key.yes
content://YOUR_SHARED_SOURCE_AUTHORITY/events/event_keys/#A set of events associated with the given # event key.no

The result of a query of the URIs listed above should be a data set containing 0 or more rows of event data arranged in a format defined by the Events class. The supported fields are listed in the following table. The rows are always have to be ordered by the PUBLISHED_TIME field in a descending order, to support fast event aggregation.

Parameter namePurposeTypeMandatory
_IDUnique key field in the provider.longyes
Event keyYou may use this field as a (unique) identifier for the events your application generates.Stringno
Contact keyLookup key of the Contact which the event is associated with.Stringyes
Published timeThe time of the event.long (Unix UTC time in millis)yes
MessageBrief description of the event.Stringyes
OriginatorThe person whom the event is initiated by.Enum (user, contact, both)yes
Icon URIThe URI of a drawable resource used as an icon when displaying this event in Historify. Note that the IconLoadingStrategy field of the source has to be set to useEventIcon to make this field effective.URIno

To help dealing with the URIs and event fields, the EventsProvider and the Events helper class are included in the BridgeLibrary. EventsProvider is ContentProvider which has some predefined URI matching functionality and protected methods for querying the different set of events. Feel free to override these methods to make them suit your needs.

Following the steps listed above, you might be able to use the EventProvider class in your application.

  1. Add a new class to your application which is derived from the EventsProvider class.
  2. public class HelloProvider extends EventsProvider {
    
      public static final String AUTHORITY =
        "com.example.myapp.provider";
    	
      @Override
      protected String getAuthority() {
        return AUTHORITY;
      }
    
      @Override
      protected Cursor queryEventsForContact(String lookupKey) {
        //this gets called when the event aggregator queries
        //your provider for the events associated with a particular
        //contact.
      }
    }
    
  3. Do not forget to add the <provider> tag to your manifest. Define the same authority as you used in the source code.
  4. <provider android:name=".HelloProvider"
      android:authorities="com.example.myapp.provider"
      android:exported="true"> 
    </provider>
  5. Fill up a Cursor with event data in the queryEventsForContact() method. You may use a SQLiteCursor for accessing data stored in a database, or MatrixCursor or any custom implementation available. The following snippet generates a mock event which is happened 'just now':
  6. @Override
    protected Cursor queryEventsForContact(String lookupKey) {
    
      MatrixCursor mc = new MatrixCursor(new String[] {
        Events._ID,
        Events.CONTACT_KEY,
        Events.EVENT_KEY,
        Events.MESSAGE,
        Events.ORIGINATOR,
        Events.PUBLISHED_TIME
      });
    		
      mc.addRow(new Object[] {
        1l, 
        lookupKey, 
        null,
        "Hello, SharedSource!",
        Events.Originator.contact,
        System.currentTimeMillis()
      });	
    
      return mc;
    }
    

Based on the snippets of this and the previous section, you should be able to register a SharedSource and share events with Historify. Check out the Hello, SharedSource! tutorial section for step-by-step instructions.

Notify on change

Historify uses Android's ContentObserver mechanism to refresh aggregated data if the content of an event provider changes. If a cursor is opened via an EventsProvider, its notification URI is set to the URI of the provider's events path by default.

When a component posts a change notification by calling ContentResolver.notifyChange(Uri uri, ContentObserver observer), where the uri is set to the URI of an aggregated EventsProvider, Historify gets notified and the displayed data could be refreshed.

Working with your own SharedSource, you are able to send notification by calling EventsProvider.onEventsChanged() right after updating your EventsProvider.

If the data you provide is not stored in the EventProvider itself, but in an other provider or custom data source, you should call ContentResolver.notifyChange(Uri uri, ContentObserver observer) right after updating the data source, where the uri is set to content://YOUR_SHARED_SOURCE_AUTHORITY/events to get the events of your SharedSource re-queried.

When inserting a recent event for a contact, consider calling ContactsContract.Contact.marksAContacted(ContentResolver resolver, long contactId) to notify the system about the recent interaction. The contact associated with the contactId you provide will be shown at the very beginning of the 'recently contacted' list on Historify's welcome screen. Regarding the method, see Android SDK reference. Note that in case of QuickPosting, markAsContacted() is called by Historify automatically.

Persistence

Your SharedSource's metadata will be stored persistently by Historify.

Note that since a SharedSource's events are managed by the client application itself, you are able to update / delete previously added events and add new events to satisfy on your application's needs. You are also able to modify previously added source metadata by incrementing the source's version code when releasing a new version of your application.

Note that your SharedSource will be unregistered if your application's package gets removed from the system (not considering re-installs).

Tutorials

Hello, QuickPost

This Hello, QuickPost! tutorial is designed to get you started quickly with Historify's QuickPosting interface. It is assumed that:

  • You are using the Eclipse IDE with the ADT plugin and the Android SDK installed.
  • You have already installed Historify on your device / emulator.
  • The contact list of the device / emulator contains a least one person.

  1. The application we are about write is simple Historify client that creates a mock event associated with a contact in the user's Contacts application and post it to Historify. To get started, create a new Android project. The required minimum API level is 5.
  2. Place the .jar file of the Historify BridgeLibrary into the libs directory of the project and add it to the projects's build path on the Project \ Properties \ Java Build Path dialog.
  3. It is recommended to add the following permission to the manifest file, to inform the user that your application is able to interact with the services of Historify.
  4. <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.quickpost"
          android:versionCode="1"
          android:versionName="1.0">
        <uses-sdk android:minSdkVersion="5" />
        
        <uses-permission android:name="org.openintents.historify.permission.USE_BRIDGE" />
    
        <application android:icon="@drawable/icon" android:label="@string/app_name">
    
        ...
    
        </application>
    </manifest>
  5. Now create a simple layout for your application's MainActivity that contains a TextView with the message of the event we are about to post and a Button for sending the post. Add a reference of this views to the MainActivity class.
  6. //main.xml:
    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
    
    <TextView
        android:id="@+id/text"  
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="Hello, QuickPost!"
        />
    
    <Button
        android:id="@+id/button"  
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="Post"
        />
    </LinearLayout>
    
    //MainActivity.java:
    
    public class MainActivity extends Activity {
    	
        private TextView text;
        private Button button;
    	
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            
            text = (TextView)findViewById(R.id.text);
            button = (Button)findViewById(R.id.button);
        }
    }
  7. In this simple example, MainActivity will be responsible for dealing with the actual event posting. We will use the library's main class called HistorifyBridge, so we create an instance in the activity's onCreate() method.
  8. HistorifyBridge bridge;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    
        ...        
        bridge = new HistorifyBridge(R.drawable.icon);
    }
  9. Writing a QuickPost client, it is elementary to determine that Historify is installed on the device. We will use the Bridge's canQuickPost method for this purpose. If the application is unable to find receiver component for your QuickPosts, the state of the Button will set to disabled. If QuickPosting is possible, we set up a QuickPostContext. This contextual data will be used to describe your application when showing your events in Historify. After setting up the context, we also add an OnClickListener for the Button to handle the QuickPost.
  10. private static final int QP_SOURCE_VERSION = 1;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
            
        ...        
        bridge = new HistorifyBridge(R.drawable.icon);
            
        boolean enabledQuickPost = bridge.canQuickPost(this);
        if(enabledQuickPost) {
            	
            QuickPostContext quickPostContext = new QuickPostContext(
                "Hello, QuickPost!",
                "This is my QuickPost test app.",
                null, //icon URI is null, default app icon will be used
                QP_SOURCE_VERSION);
    
            bridge.setQuickPostContext(quickPostContext);
            	
            button.setOnClickListener(new OnPostListener());
            	
        } else {
            button.setEnabled(false);
        }
    }
  11. In the event handler method of the listener, we simply create a new event with the message shown in the TextView, and post it to Historify. Note that in Historify, every event is associated with exactly one contact in the users phonebook / contact list. So when posting events, you must provide a lookup key of a contact whom this event connected to. We write the method getContactKey() for getting a contact in the next step.
  12. private class OnPostListener implements View.OnClickListener {
    
        public void onClick(View view) {
    			
            String contactLookupKey = 
                getContactKey(); //provides a contact key 
    
    	EventData eventData = new EventData(
    	    "event_1", 
    	    contactLookupKey, 
    	    System.currentTimeMillis(), 
    	    text.getText().toString(), 
    	    Originator.contact);
    
    	bridge.quickPost(MainActivity.this, eventData);
        }    	
    }
    
  13. Now we have to provide a lookup key for a contact whom the event will be associated with. In this example, we will simply query the contact list and select the first person available. Note that this only works if the contact list of your device / emulator contains at least one entry.
  14. public String getContactKey() {
    
        Cursor c = null;
        String retval = null;
        try {
    
            c = getContentResolver().query(
    	    Contacts.CONTENT_URI,
    	    new String[] { Contacts.LOOKUP_KEY }, 
                Contacts.IN_VISIBLE_GROUP + " = 1", 
    	    null,
    	    Contacts.DISPLAY_NAME);
    
    	if (c.moveToFirst()) {
    	    retval = c.getString(0);
    	} else {
    	    throw new IllegalStateException("No contacts.");
    	}
        } catch (Exception e) {
    	e.printStackTrace();
        } finally {
    	if (c != null)
    	    c.close();
        }
    
        return retval;
    }
  15. Add the following <uses-permission> tag to your manifest to be able to query the contact list.
  16. <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.quickpost"
          android:versionCode="1"
          android:versionName="1.0">
        <uses-sdk android:minSdkVersion="5" />
        <uses-permission android:name="org.openintents.historify.permission.USE_BRIDGE" />
        <uses-permission android:name="android.permission.READ_CONTACTS" />
        ...
    
    </manifest>
  17. And that's all. You may try out the application by posting your events to Historify. You could also try to uninstall Historify and observe how the Button for QuickPosting becomes disabled if you relaunch the Hello, QuickPost! application.

Hello, SharedSource

This Hello, SharedSource! tutorial is designed to get you started quickly with Historify's SharedSource registration and event sharing mechanism. It is assumed that:

  • You are using the Eclipse IDE with the ADT plugin and the Android SDK installed.
  • You have already installed Historify on your device / emulator.

  1. The application we are about write is simple Historify client that

contains simple SharedSource that provides a mock event for every contact in the user's Contacts application and shares it with Historify. To get started, create a new Android project. The required minimum API level is 5.

  1. Place the .jar file of the Historify BridgeLibrary into the libs directory of the project and add it to the projects's build path on the Project \ Properties \ Java Build Path dialog.
  2. It is recommended to add the following permission to the manifest file, to inform the user that your application is able to interact with the services of Historify.
  3. <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.sharedsource"
          android:versionCode="1"
          android:versionName="1.0">
        <uses-sdk android:minSdkVersion="5" />
        
        <uses-permission android:name="org.openintents.historify.permission.USE_BRIDGE" />
    
        <application android:icon="@drawable/icon" android:label="@string/app_name">
    
        ...
    
        </application>
    </manifest>
  4. To get your application registered as a SharedSource, you must declare a Broadcast Receiver in the manifest to detect the BROADCAST_REQUEST_REGISTER_SOURCE Intent, and make a call to the Bridge to notify Historify about the existence of your event source. To deal with this process, you are able to use the BridgeLibrary's RequestReceiver class. First, add a new receiver to the source derived from RequestReceiver.
  5. package com.example.sharedsource;
    
    import org.openintents.historify.services.bridge.HistorifyBridge.RequestReceiver;
    import android.content.Context;
    
    public class HelloReceiver extends RequestReceiver {
    
        @Override
        protected void onRequestRegister(Context context) {
          // TODO Auto-generated method stub
    		
        }
    }
  6. Add the corresponding <receiver> tag to your manifest. An Intent filter has to be defined to receive the request register broadcasted by Historify.
  7. <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.sharedsource"
          android:versionCode="1"
          android:versionName="1.0">
        <uses-sdk android:minSdkVersion="5" />
        <uses-permission android:name="org.openintents.historify.permission.USE_BRIDGE" />
    
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            ...
    	<receiver android:name=".HelloReceiver">
      	  <intent-filter>
        	    <action
              android:name="org.openintents.historify.REQUEST_REGISTER_SOURCE"  
                />
        	    <category android:name="android.intent.category.DEFAULT" />
       	  </intent-filter>
    	</receiver>
    
        </application>
    </manifest>
  8. The RequestReceiver you declared offers a method called onRequestRegister() which is invoked when your application may register its source in Historify. Place a call of HistorifyBridge.registerSource() into this method to get your SharedSource registered. The registerSource() method takes a SourceData object as parameter, which is for describing your event source. The most important parameter is the Authority, which is for identifying your actual ContentProvider that your events will be queried from. It this example, we will use a ContentProvider named HelloProvider defined in the next step of this tutorial.
  9. private static final int SOURCE_VERSION = 1;
    	
    @Override
    protected void onRequestRegister(Context context) {
    
        SourceData sourceData = new SourceData(
            "Hello, SharedSource!", 
            HelloProvider.AUTHORITY, 
    	"This is my SharedSource test app.", 
    	null, //icon URI is null, default app icon will be used
    	SOURCE_VERSION);
    
        HistorifyBridge bridge = new HistorifyBridge(R.drawable.icon); 
        bridge.registerSource(context,sourceData);
    		
    }
    
  10. The next step is to define ContentProvider for your shared events. The Historify BridgeLibrary contains a handy class named EventsProvider which is ContentProvider with predefined URI matching functionality. Add a new class HelloProvider derived from EventsProvider to your application.
  11. package com.example.sharedsource;
    
    import org.openintents.historify.data.providers.EventsProvider;
    import android.database.Cursor;
    
    public class HelloProvider extends EventsProvider {
    
    	public static final String AUTHORITY =
    		"com.example.sharedsource.provider";
    	
    	@Override
    	protected String getAuthority() {
    		return AUTHORITY;
    	}
    
    	@Override
    	protected Cursor queryEventsForContact(String lookupKey) {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    }
  12. Add the corresponding <provider> tag to your manifest. Define the same authority as you used in the source code above.
  13. <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.sharedsource"
          android:versionCode="1"
          android:versionName="1.0">
        <uses-sdk android:minSdkVersion="5" />
        <uses-permission android:name="org.openintents.historify.permission.USE_BRIDGE" />
    
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            ...
    
    	<provider android:name=".HelloProvider"
      	    android:authorities="com.example.sharedsource.provider"
      	    android:exported="true"> 
    	</provider>
    
        </application>
    </manifest>
  14. Now your event source is ready to use. The only thing left is to return some events in the HelloProvider's queryEventsForContact() method. In this example, we will simply create a mock event regardless which Contact is queried, so the event will shown up on the timeline of each and every Contact.
  15. @Override
    protected Cursor queryEventsForContact(String lookupKey) {
    
        MatrixCursor mc = new MatrixCursor(new String[] {
          Events._ID,
          Events.CONTACT_KEY,
          Events.EVENT_KEY,
          Events.MESSAGE,
          Events.ORIGINATOR,
          Events.PUBLISHED_TIME
        });
    				                
        mc.addRow(new Object[] {
          1l, 
          lookupKey, 
          null,
          "Hello, SharedSource!",
          Events.Originator.contact,
          System.currentTimeMillis()
        });   
    
        return mc;
    
    }
  16. Now you may try out your SharedSource client application. After installing your package, a Notification should appear informing you that the SharedSource has been successfully registered. If you browse the timeline of the contacts in Historify, you may observe the event you shared. You may also notice that your source has been added to the 'my sources' list.

Further materials

Historify 1.0-b1:

  • apk:
  • latest source code:

Historify BridgeLibrary:

  • jar + javadoc:
  • latest source code:

LendMe: QuickPost showcase application:

  • apk:
  • latest source code:
HelloSharedSource: SharedSource demo application:
  • apk:
  • latest source code:
Historify at openintents.org:
  • page:
  • intent registry:

Comment by tomtasche, Sep 27, 2011

A project I've been working on a few months ago is quite similar to what you did (read, aggregating events from the phone). It's called JOPPFM (http://joppfm.tomtasche.at/).

Unfortunately the recent Gmail updates broke almost everything...


Sign in to add a comment
Powered by Google Project Hosting