Monday, June 27, 2011

Handling icons of sources and events

On the timeline (and on the source settings activity) small icons are used to identify the different sources and/or event. These icons could come from various sources (content uri, client application's app icon, default source icon).

Now Historify handles these different situations by defining the following fields:

- A Source could define an icon_uri field, which is a URI identifying the icon drawable associated with that source. Client applications could provide this value while registering their SharedSource. If no icon_uri has been provided by the client, Historify uses the client app's launcher icon.

- In case of certain Sources it might be necessary to use different icons for each event on the timelime. For example, QuickPost is a special source that stores Events from different applications, so it might be useful to display the icon provided by the caller application instead of the default QuickPost icon. To realize scenarios like this, the new icon_loading_strategy field has been added to the sources table, which could be set to useSourceIcon (default) or useEventIcon.

- If the icon loading strategy is set to useEventIcon, the icon shown on the timeline is not loaded from the Source's icon_uri field, but the Event's icon_uri field. So providers that define custom icons for each timelime event (like the QuickPost provider) could provide a value associated with the particular event instead of the source.

Based on these attributes, timeline icons could be fully customized, as shown on the figure below.

The first event is provided by the HelloSharedSource external source (launcher icon is used), while the second one is provided by the internal QuickPost provider (the icon of the LendMe app is used, since the post came from that application).

Thursday, June 23, 2011

QuickPost use case demo

Finished developing the functional demo of  LendMe.

LedMe is lending recorder application in which the user could keep record of her borrowed / lent belongings and also able to send generated threatening reminder messages to ask the contact to return the item she borrowed.

The aim was to build an application that actually uses the Historify QuickPost interface, it's kind of a demonstrational app. The events which are posted are the start and the end of a lending session. See screenshots, they are self-explanatory.




Sunday, June 19, 2011

Hello, QuickPost!

Now Historify supports the second method of information sharing: QuickPost. This method provides a simple and easy-to-use interface for 3rd party app developers: there’s no need to register any receivers or providers like in case of SharedSources, since the event data is posted via a single intent call fired by the HistorifyBridge helper class and handled by the BridgeService.
The posted events are stored in an internal event provider of Historify (QuickPostsProvider) which shall be handled as any other internal providers.
To test the QuickPost function, a simple demo (HelloQuickPost) app is available which posts events to the timeline of a user-specified contact.

What to do in the remaining 2 weeks of this development period?
-          Basic lending recorder app to demonstrate the use cases of QuickPost
-          Review the different interfaces, check consistency, create client library (.jar)
-          Improve Historify’s functionality (source configuration, detailed event info, refreshing UI as new data is posted, etc.)
-          Create a special SharedSource for managing the data of Timescape extensions.

Tuesday, June 14, 2011

Hello, SharedSource!

I've spent the last few days working on methods for registering and unregistering external content providers. Here is the scenario what I came up with:

1. User installs Historify.
2. User installs a 3rd party event provider application (aka Client) that hosts an external source (aka 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 called 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 BridgeService, which is an IntentService declared in Historify's package.
7. The BridgeService adds the newly registered SharedSource to Historify as a new external source, and posts a Notification to notify the user about the successful registration.

And what if the user installs several Clients BEFORE installing 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.

The only issue with that is regarding permissions. I got the idea to define my own permission called .permission.USE_BRDIGE that all Clients should hold if they want to use the BridgeService. So every Client that adds the <uses-permission .... /> tag to its manifest, could use the BridgeService, and if an unauthorized application calls the Service, it gets a SecurityException.

The problem is that if the user installs a Client first, way before installing Historify, the <uses-permission> tag will be IGNORED by the system's package manager, since the USE_BRIDGE permission is not registered in the system, and later the permission will not be granted. You could read about this at http://stackoverflow.com/questions/4567812/define-a-permission-for-third-party-apps-to-use-in-android/4569137#4569137

So, to notify the user about this issue, if REGISTER_SOURCE gets a SecurityException, a notification will be displayed which says that the user need to reinstall the Client to get it work with Historify.  When reinstalling the Client, its <uses-permission> will not be ignored this time (since Historify registered it in the system's permission collection), and it will work fine with Historify (You could test this behaviour, see below).

And finally, what if the user uninstalls a Client? In this case, Historify should automatically delete the SharedSource associated with the removed application. Here's the scenario for this:

1. User uninstalls a Client.
2. Historify receives the ACTION_PACKAGE_REMOVED Broadcast Intent.
3. The UID (UNIX user id) of the uninstalled package is provided as Intent extra. Based on that information, Historify could detect that there is an existing registered Source associated with that UID (since UID is stored in the SourcesProvider).
4. The SharedSource will be deleted from the SourcesProvider.

To test the described scenarios, I added a sample project called HelloSharedSource, which is an application that contains a simple event provider.

To try out source registration:
1. Install Historify
2. Install HelloSharedSource --> it will be registered automatically, and a notification will appear.

To try out the issue with permissions:
1. Uninstall everything.
2. Install HelloSharedSource
3. Install Historify --> the notification about the error will appear.
4. Reinstall HelloSharedSource --> it will be registered.

Note that HelloSharedSource also contains the helper classes that future Client applications shall use to interact with Historify. Currently these classes are added to the project as .java files; however, after finalizing the source registration mechanism, they should be placed in a .jar file.

Friday, June 10, 2011

Event Aggregation

The main achievement of the last few days was the implementation of the event aggregation. Let’s see how it works:


All the different source providers make their content (list of events) accessible via a Cursor that contains all the columns defined in .data.providers.Events, ordered by the PUBLISHED_TIME field.  The EventAggregator’s responsibility is to turn these different event lists into a single sequence of events and pass it to the TimeLineAdapter to convert them into View objects.

The method that realizes this functionality is based on the MergedCursor class. MergedCursor is basically a wrapper around an array of the different cursors. It is derived from AbstractCursor, so it could be integrated into the existing architecture seamlessly. It emulates a virtual cursor that contains the union of the rows of the aggregated cursors, while its content is sorted by a certain numerical field (in our case Events.PUBLISHED_TIME). Sorting is accomplished by a modified version of the Merge sort algorithm.

Aggregation is tested with the already implemented internal providers, and special provider added for test purposes. This provider (called FactoryTest) generates a certain number of events for every contact:



The first version of the sorting algorithm turned out to be surprisingly fast in practice, however, further improvements necessary for a scalable solution (for users with may providers and thousands of aggregated events).

Fragment support

Now the application supports the Fragment API via the Compatibility Libraries. All UI event handling and business logic defined in the different activities was moved to the corresponding fragments (e.g. ContactsActivity --> ContactsListFragment). I also added a few extra lines in Activity's onCreate() to make sure that the Intent extras passed to the Activity are easily accessible from the Fragment as arguments:

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

if (savedInstanceState == null) {

ContactsListFragment fragment = new ContactsListFragment();
Bundle arguments = getIntent().getExtras();
fragment.setArguments(arguments == null ? new Bundle() : arguments);
getSupportFragmentManager().beginTransaction().add(
android.R.id.content, fragment).commit();
}
}
So adding fragment support was pretty straightforward, and no new UI elements have been added. The aim was to make the application easier to port for Android 3.0+.

Monday, June 6, 2011

EventIntent

Today a few modifications have been done to facilitate working with different sources.
  • EventsProvider has been added as a base class for all the providers. The aim is to handle URI matching in the base class, so derived providers only have to override the methods doing the actual queries. The existing two internal providers (Messaging, Telephony) are good examples how this concept could be applied.
  • New columns have been added to the SourcesProvider's SourcesTable:
    • description, 
    • icon_uri for an  image to be displayed in the settings as well as on the timeline,
    • authority of the associated provider,
    • and the event_intent field.
A few notes regarding the last one: EventIntent is the action string of an Intent to be fired if the user selects an event on the timeline. Every Event has an associated Source, and in case of the user clicks on an event, the applications simply checks what Source it came from, and fires the Intent defined by that Source. In order to identify the actual selected Event, the value of the _ID and the EVENT_KEY fields are passed as Intent extras.

Friday, June 3, 2011

Functionality so far

After almost two weeks of development, maybe it's time to summarize the functionality of the app so far.

Historify now has a basic UI for displaying timeline events. Handling of internal and external sources has been realized as well as source filtering: User could enable or disable sources for all contacts or a particular contact.

As the matter of sources, the two basic internal source providers (Telephony for call log, Messaging for SMS) have been implemented. Since the current version does not support event aggregation yet, only Messaging has been added to the database table containing source providers.

Here are some screenshots to give a clue.




So what's left for the rest of the week:
- Expand model classes with advanced attributes (icon uri for sources, intent ot be fired if user selects an event)
- Integrate Fragments API to facilitate porting for 3.0+.
- Some code polishing.

In the next 4 weeks the two major function in the focus of the development will be the event aggregation algorithm and the handling of external sources.

Wednesday, June 1, 2011

Some terminology

Let's introduce some terminology.


Contact: High level abstraction of a Contact entry in the user's contactlist.

TimeLine: The time-ordered list of events (see image of prev post). Currently a TimeLine is always associated with a single contact, so only interactions between the user and a particular contact are shown.

Event: Item on the timeline. Its attributes are used the describe a particular event. An event could be a phone call, a short message, photo tagging, etc. Some of the common attributes are the time of the event, a message, an Intent to be fired if the user selects it, and the originator of the event (user, contact, both), which is used to display the list of events in a conversational form.

Source: The abstraction of the source which the events came from. An Event is always associated with exactly one Source. There are two kinds of sources: internal and external. Internal sources are defined and handled by the Historify application (e.g. Messaging, Telephony). External sources are defined by 3rd party app developers. External sources must be registered via the REGISTER_SOURCE Intent before use. All registered sources could be enabled or disabled by the user. Events associated with disabled sources won't show up in the timeline.

Provider: The Content Provider which stores all event data associated with a single source. Historify collects event data from the concrete providers during event aggregation. Internal sources have internal providers defined in Historify's manifest XML, while external providers are defined by 3rd party applications.

Filter: A source filter modifies the enabled/disabled state of a particular Source. The aim is to let the user customize the event aggregation behaviour for a particular contact. The user could define filters for any contact-source pairs, however, when Historify aggregates events data on the timeline, only the filters defined for the currenly shown contact are taken into account.*

* That's why the diagram shows that a Source could only have one Filter.

Introduction

Problem scope
Mobile phones give their users the opportunity to communicate in many different ways. Phone calls, messaging, general social networking sites (Facebook), and applications designed around a particular interest (like Last.fm for music fans) are all great way to get in touch with people. However, all the different technologies and services have their own mobile client application and user interface. The mobile platforms usually lack of a generalized interface, where the user could browse the history of all her interactions with her contacts.
Recently, Sony Ericsson released the Timescape API, which lets developers write their extensions to SE’s Timescape software, which provides a user interface listing aggregated content from the different applications. However, Timescape is closed-source, and available on a limited set of devices (Xperia).
Solution
The aim of the project is to design and implement an application called Historify (Contact History), which would be an Android application that displays the list of interactions (phone calls, SMS, FB messages, etc.) between the phone's user and a selected contact, in a conversational form. The application would seamlessly aggregate the content of the different data sources into a single view.
The key element of the project would be to support openness: The application would provide a mechanism allowing 3rd party apps to post events on the timeline (via shared content providers or intent calls).

Plans for this summer
To achieve the described goals, I intended to cover the following areas as part of my contribution to the project:
·         Create a skeleton application for displaying the user’s contact timelines.

·         Design and implement internal content providers to collect SMS and telephony events from the user’s contacts, and an event aggregator to create a timeline of the events loaded from the different providers. Finally, an adapter to convert the data into view objects displayable on the user interface.

·         Design and implement an Intent-based interface that other apps could use to:
a.       Post single events.
b.      Register their own content providers as a new data source. The 3rd party content provider should be realized similar to the internal providers to facilitate event aggregation.

Note that it is also necessary to study the solution Timescape uses in its own API for Intent registering. Ideally, Historify could use a similar mechanism, so full compatibility could be achieved.

·         Create a sample application that uses the Intent-interface. E.g. a Facebook-based application, that collects a few types of interaction events (e.g. event invitations), and shares it with the Historify application.