In my Droidcon 2011 presentation I tried to highlight the battery cost of the continuous sensor sampling which is necessary for detecting motion patterns. While the general case still requires some sort of improvement over the current Android sensor architecture e.g. the use of the "wake on motion" feature of the acceleration sensors, it is possible to decrease the battery consumption if the motion to be detected is longer than 5-10 seconds. This is still not suitable for recognising very short events like fall, tap, shake, etc. but can be suitable to "wake up" a step counter when the motion starts. Some steps would be missed but this may be acceptable if the battery life is increased.
Click here to download the example program.
The essence of the workaround is to use the AlarmManager and to generate periodic wakeup events. When the wakeup event is received, sensor sampling is started and some samples are collected. Then we have to figure out using some heuristics whether there is some movement. This implementation calculates the average value of the acceleration sample absolute values and looks for values deviating from the average. If there is movement detected, the sampling continues. If there is no movement detected, the program gives up the wake lock and goes back sleeping.-
I have made some measurements using my Nexus 1. I switched the phone into airplane mode and ran the program during the night, for 7-8 hours. The battery percentage consumed during an hour can be seen below as a function of wakeup period (check out WAKEUP_PERIOD constant in AlarmSleep.java).
5 sec - 1.14%/hour
10 sec - 1.10%/hour
20 sec - 0.56%/hour
30 sec - 0.27%/hour
The battery consumption with the last timeout value - 30 sec - is very close to the standby consumption, 0.25%. If you can tolerate that long "dead period", then you can bring the battery consumption in "no motion" state very close to the phone's normal standby consumption. For a general "tap" or "shake" detector, however, this is not an adequate solution. I have received encouraging mails that a proper solution relying on the sensor's low-power mode may be deployed soon.
Saturday, October 22, 2011
Tuesday, October 11, 2011
Foreca Weather released
Let me advertise an application that has just appeared on Android Market.
Why? Because I was the Android programmer on this project working in cooperation with the designers of Foreca, a Finland-based weather forecast company. As you may remember, I am aesthetically challenged so the good looks of this application are all due to the thorough polishing of the Foreca guys. Foreca's vast weather network makes this application handy anywhere in the world. Weather stations can be selected by incrementally searching list or from a map, the application even finds the current location. Locations can be favourited and Foreca offers 10 days of detailed forecast. There is a widget too.
Click on the button and go to Android Market to fetch it!
Some screenshots.
Why? Because I was the Android programmer on this project working in cooperation with the designers of Foreca, a Finland-based weather forecast company. As you may remember, I am aesthetically challenged so the good looks of this application are all due to the thorough polishing of the Foreca guys. Foreca's vast weather network makes this application handy anywhere in the world. Weather stations can be selected by incrementally searching list or from a map, the application even finds the current location. Locations can be favourited and Foreca offers 10 days of detailed forecast. There is a widget too.
Click on the button and go to Android Market to fetch it!
Some screenshots.
Monday, October 10, 2011
Battery cost of sensor sampling
While at Droidcon UK 2011, I was asked to elaborate my claims about the battery cost of sensor sampling in a blog post. These claims can be found in my conference presentation but we thought it would help to describe them more in detail.
Continuous accelerometer sensor sampling introduces significant battery cost in Android devices. For example if you want to write an application that samples the sensor in the background and figures out, whether somebody double-tapped the body of the phone (not the active touch screen but anywhere on the phone's body), then the CPU of the phone can never sleep. You need to grab a partial wake lock to ensure continuous sampling otherwise the processing of the samples will stop when the device goes to sleep - typically some minutes after the keyguard activates. If you obtain partial wake lock, however, then you have to calculate with 1.5-4% battery consumption per hour (depending on sampling speed) which does not look like a lot but if you multiply it with 24 hours, you can see that you cannot sample the accelerometer continuously without spoiling the phone's usability.
Microsoft proposes a low-power co-processor for these background processing jobs with low computational complexity (accelerometer is typically sampled around 10-30 samples per second - you don't need a supercomputer to do that kind of processing). While this approach definitely solves the battery problem, there is the issue of an additional programming model (those low-power microcontrollers don't have full-blown programming environments) and it is very likely that application programmers will not be able to insert pieces of code to run on this microcontroller.
My proposal is to exploit low-power features of the accelerometer sensors widely used in Android devices. For example the very popular Bosch Sensortec BMA150 accelerometer sensor which can be found in variety of HTC devices (and probably others) has a wake up on motion mode.
In its data sheet, this mode is described like the following.
In general BMA150 is attributed to low power applications and can contribute to the system power management.
The BMA150 provides the possibility to wake up a system master when specific acceleration values are detected. Therefore the BMA150 stays in an ultra low power mode and periodically evaluates the acceleration data with respect to interrupt criteria defined by the user. An interrupt output can be generated and trigger the system master. The wake-up mode is used for ultra-low power applications where inertial factors can be an indicator to change the activity mode of the system.
This would allow the main CPU to go into sleep mode and to be woken up by the sensor only if there are movements. So if the device is laying on the table, there would be basically no power consumption due to sensor sampling. This would enable production-quality implementation of a range of applications, for example the Activity Level Estimator which is being researched at the University of Geneva.
The attractive property of this approach is that even though implementing it in Android devices and in the framework is not trivial, it is not very complicated either. The hardware is already in the devices, maybe the sensor's interrupt pin has to be wired up with the main processor. SensorManager needs to be extended with some functions that allows applications to activate this wake up on motion feature. Application model would remain consistent with the current Android application model, no need to fiddle with low-level microcontroller code.
Now there just need to be a device manufacturer that carries this through.
Continuous accelerometer sensor sampling introduces significant battery cost in Android devices. For example if you want to write an application that samples the sensor in the background and figures out, whether somebody double-tapped the body of the phone (not the active touch screen but anywhere on the phone's body), then the CPU of the phone can never sleep. You need to grab a partial wake lock to ensure continuous sampling otherwise the processing of the samples will stop when the device goes to sleep - typically some minutes after the keyguard activates. If you obtain partial wake lock, however, then you have to calculate with 1.5-4% battery consumption per hour (depending on sampling speed) which does not look like a lot but if you multiply it with 24 hours, you can see that you cannot sample the accelerometer continuously without spoiling the phone's usability.
Microsoft proposes a low-power co-processor for these background processing jobs with low computational complexity (accelerometer is typically sampled around 10-30 samples per second - you don't need a supercomputer to do that kind of processing). While this approach definitely solves the battery problem, there is the issue of an additional programming model (those low-power microcontrollers don't have full-blown programming environments) and it is very likely that application programmers will not be able to insert pieces of code to run on this microcontroller.
My proposal is to exploit low-power features of the accelerometer sensors widely used in Android devices. For example the very popular Bosch Sensortec BMA150 accelerometer sensor which can be found in variety of HTC devices (and probably others) has a wake up on motion mode.
In its data sheet, this mode is described like the following.
In general BMA150 is attributed to low power applications and can contribute to the system power management.
- Current consumption 200μA operational
- Current consumption 1μA sleep mode
- Wake-up time 1ms
- Start-up time 3ms
- Data ready indicator to reduce unnecessary interface communication
- Wake-up mode to trigger a system wake-up (interrupt output when motion detected Low current consumption in wake-up mode to master)
The BMA150 provides the possibility to wake up a system master when specific acceleration values are detected. Therefore the BMA150 stays in an ultra low power mode and periodically evaluates the acceleration data with respect to interrupt criteria defined by the user. An interrupt output can be generated and trigger the system master. The wake-up mode is used for ultra-low power applications where inertial factors can be an indicator to change the activity mode of the system.
This would allow the main CPU to go into sleep mode and to be woken up by the sensor only if there are movements. So if the device is laying on the table, there would be basically no power consumption due to sensor sampling. This would enable production-quality implementation of a range of applications, for example the Activity Level Estimator which is being researched at the University of Geneva.
The attractive property of this approach is that even though implementing it in Android devices and in the framework is not trivial, it is not very complicated either. The hardware is already in the devices, maybe the sensor's interrupt pin has to be wired up with the main processor. SensorManager needs to be extended with some functions that allows applications to activate this wake up on motion feature. Application model would remain consistent with the current Android application model, no need to fiddle with low-level microcontroller code.
Now there just need to be a device manufacturer that carries this through.
Friday, October 7, 2011
My presentation about motion recognition at Droidcon 2011
I finished on time!
Per popular demand, here is the presentation.
Per popular demand, here is the presentation.
Friday, September 2, 2011
Returning to Droidcon London
It was almost 2 years ago that I presented my beloved garage project, Dedexer at Droidcon 2009. A lot of things has happened since. First and foremost I returned to my home country, Hungary. Those Londroid meetups and Droidcon are not a Tube-ride away anymore. Then there was the step count cooperation with the University of Geneva folks that resulted in a fine paper.
In short, it is just about time to return to Droidcon.
My presentation will be a developer-centric introduction to the world of accelerometer signal processing on Android devices, representing my second employer, Sfonge Ltd. Have you ever thought about introducing features into your apps that can be activated by body movements? Maybe you haven't so it is about time you start thinking about it. Let's meet at Droidcon 2011 and I will explain to you, how simple it is to add such a feature.
Well, that was a lie. Motion recognition is not trivial but exactly that's why it is worth learning about it.
In short, it is just about time to return to Droidcon.
My presentation will be a developer-centric introduction to the world of accelerometer signal processing on Android devices, representing my second employer, Sfonge Ltd. Have you ever thought about introducing features into your apps that can be activated by body movements? Maybe you haven't so it is about time you start thinking about it. Let's meet at Droidcon 2011 and I will explain to you, how simple it is to add such a feature.
Well, that was a lie. Motion recognition is not trivial but exactly that's why it is worth learning about it.
Friday, August 19, 2011
Focus problems with list rows and ImageButtons
There has been lot of discussions on this blog about some widgets that steal focus from others - creating behaviour that sounds completely illogical for the uninitiated. So far we have seen how Buttons steal the focus from list rows. The same monster rears its ugly head in this slightly different example. Here we have a simple ListView with list rows. Each list row has a text widget and an ImageButton in it. Now if we code this example innocently without being aware of the tricky focus-stealing effect of ImageButton, list rows cannot be selected.
Click here to download the example program.
The example program as it is works nicely. If you click the row, you will get one sort of Toast message. If you click the ImageButton within the row, you will get another Toast message. Now let's observe this XML attribute in res/layout/item.xml:
android:descendantFocusability="blocksDescendants"
Try removing this attribute and notice, that selecting list items does not work anymore. What happened? As explained previously, ImageButton steals the focus from the view group (the list row) it is in. This means that when you click the list row, it is always the ImageButton that captures the event but as it is outside of its screen area, the event is discarded. Therefore we prevented the ImageButton to capture the focus.
Previously I recommended preventing the capture of the focus by making the button non-focusable (android:focusable="false"). Curiously, it does not work in this example so I had to find another way. That's why I shared this example.
Click here to download the example program.
The example program as it is works nicely. If you click the row, you will get one sort of Toast message. If you click the ImageButton within the row, you will get another Toast message. Now let's observe this XML attribute in res/layout/item.xml:
android:descendantFocusability="blocksDescendants"
Try removing this attribute and notice, that selecting list items does not work anymore. What happened? As explained previously, ImageButton steals the focus from the view group (the list row) it is in. This means that when you click the list row, it is always the ImageButton that captures the event but as it is outside of its screen area, the event is discarded. Therefore we prevented the ImageButton to capture the focus.
Previously I recommended preventing the capture of the focus by making the button non-focusable (android:focusable="false"). Curiously, it does not work in this example so I had to find another way. That's why I shared this example.
Wednesday, July 6, 2011
Book on Android testing
Oh, those were the days, when Android was pre-1.0! Documentation and training material was scarce, platform sources were not yet released, no device in sight. It was then when Diego Torres Milano first published a post about unit testing on Android. 3 short years later Android is the dominant smartphone platform but there has been no book dedicated to Android testing. Who else could have written that book than Diego?
Let's start with a disclaimer: I was a reviewer of this book. I almost met Diego once when we both presented at Droidcon London 2009 but I had to leave early and we didn't. Believe or not, at the end of 2009 Android enthusiasts could still fit into a medium-sized presentation hall.
The book starts with a general introduction on testing then it starts to focus on Android testing. Android has JUnit integrated but the Android-specific classes are not over-documented. The Android test case classes are explained one by one with examples then we learn, how the principles of Test Driven Development can be applied to our Android projects. The second half of the book deals with useful techniques like automatic test execution, ways of performance and coverage testing and different tools for Behaviour Driven Development and Continuous Integration.
All in all, there is plenty of information in this book that is difficult to find or figure out. I can warmly recommend this book to everyone who builds production-quality software for the Android platform.
Let's start with a disclaimer: I was a reviewer of this book. I almost met Diego once when we both presented at Droidcon London 2009 but I had to leave early and we didn't. Believe or not, at the end of 2009 Android enthusiasts could still fit into a medium-sized presentation hall.
The book starts with a general introduction on testing then it starts to focus on Android testing. Android has JUnit integrated but the Android-specific classes are not over-documented. The Android test case classes are explained one by one with examples then we learn, how the principles of Test Driven Development can be applied to our Android projects. The second half of the book deals with useful techniques like automatic test execution, ways of performance and coverage testing and different tools for Behaviour Driven Development and Continuous Integration.
All in all, there is plenty of information in this book that is difficult to find or figure out. I can warmly recommend this book to everyone who builds production-quality software for the Android platform.
Thursday, June 9, 2011
Hiding group indicator for empty groups
Another topic that keeps returning on this blog is the expandable list view. It seems those troublesome expandable lists always have some surprises in store. This time I ran into the issue of group indicators and empty groups.
Group indicators are those small arrows in front of the group rows that indicate, whether the group is expanded or collapsed. Trouble is that those indicators are always visible, even if the group is empty. This misleads users who think that they can expand the group. The group indeed expands but as it is empty, no child rows appear which can be extremely annoying.
Some internet sources propose wizardry with the variable drawable that backs the group indicator. If you follow the advice, the group indicator will disappear for all the collapsed groups (even for those which have child rows). This surprising behaviour is by design. Comment in the source of ExpandableListView says: "[if] the group is collapsed so we consider it empty for performance reasons". The method where this behaviour is wired in is private, it is not possible to overload it by subclassing ExpandableListView.
Click here to download the example program.
Fortunately the solution is very simple, even if a bit tricky. We will completely disable the group indicator logic in ExpandableListView and provide our own indicator from the adapter backing the expandable list. The group indicator is disabled by setting it to a transparent color in main.xml.
android:groupIndicator="@android:color/transparent"
Then we override getGroupView in the adapter (this time it was a SimpleExpandableListAdapter but the method works for any adapter). We had to manually add an ImageView to the group layout - this is normally done automagically by ExpandableListView but we have just disabled that functionality. Once getGroupView returns the group row, we post-manipulate the ImageView that acts as group indicator considering the child count for the group beside its expanded/collapsed state. This unfortunately means that you have to have custom expanded/collapsed icons in your program - those icons are extremely ugly in the example program, I leave it to you as a homework to beautify them.
Group indicators are those small arrows in front of the group rows that indicate, whether the group is expanded or collapsed. Trouble is that those indicators are always visible, even if the group is empty. This misleads users who think that they can expand the group. The group indeed expands but as it is empty, no child rows appear which can be extremely annoying.
Some internet sources propose wizardry with the variable drawable that backs the group indicator. If you follow the advice, the group indicator will disappear for all the collapsed groups (even for those which have child rows). This surprising behaviour is by design. Comment in the source of ExpandableListView says: "[if] the group is collapsed so we consider it empty for performance reasons". The method where this behaviour is wired in is private, it is not possible to overload it by subclassing ExpandableListView.
Click here to download the example program.
Fortunately the solution is very simple, even if a bit tricky. We will completely disable the group indicator logic in ExpandableListView and provide our own indicator from the adapter backing the expandable list. The group indicator is disabled by setting it to a transparent color in main.xml.
android:groupIndicator="@android:color/transparent"
Then we override getGroupView in the adapter (this time it was a SimpleExpandableListAdapter but the method works for any adapter). We had to manually add an ImageView to the group layout - this is normally done automagically by ExpandableListView but we have just disabled that functionality. Once getGroupView returns the group row, we post-manipulate the ImageView that acts as group indicator considering the child count for the group beside its expanded/collapsed state. This unfortunately means that you have to have custom expanded/collapsed icons in your program - those icons are extremely ugly in the example program, I leave it to you as a homework to beautify them.
Sunday, May 29, 2011
Workshop paper
Last year I blogged about my experiences with acceleration signal processing (here , here and here) then the topic disappeared from this blog. Disappeared from the blog but not from my life because the research continued with some partners from the University of Geneva. The paper (free registration is needed for access) will be presented at the 1st International Workshop on Frontiers in Activity Recognition using Pervasive Sensing, a workshop of Pervasive 2011. The prototype was created on Android, you can access it here. Be warned: this is just a research prototype and is not guaranteed to work on anything else than on Nexus One and even on that phone it has some issues. If you happen to be on Pervasive 2011, I am happy to explain its operation personally.
Update: the workshop presentation has been done, the slideset is also available at the same link where the paper is.
Update: the workshop presentation has been done, the slideset is also available at the same link where the paper is.
Friday, April 29, 2011
Book on Android UI development
Packt Publishing has just released this book about Android User Interface Development from Jason Morris and I got a mail from them whether I could write something about the book in this blog. Why not, responded I and there came an e-book. Of course, I had a hidden agenda too. I am aesthetically challenged, so to say and therefore UI design is not really my strength. I intended to learn from a master.
First and foremost, as the subtitle says, this book is for beginners and the author takes this mission seriously. The book follows a rigorous, step-by-step methodology. The reader is expected to start from an empty project and extend it line by line while the book explains, what is going on. It is best to demonstrate with an excerpt:
The author made some decisions that I found a bit strange. First and foremost, even though the title is Android User Interface Development, it does not discuss 2D (Canvas and co.) and 3D (OpenGL) at all. I am sure that some readers will miss these parts. I am big fan of command-line development environments but maybe Eclipse is better for beginners.
All in all, I enjoyed this book and I even learnt from it. The book does not cover all the aspects of Android development but it addresses a very important application type. It is a good place to start with Android development.
First and foremost, as the subtitle says, this book is for beginners and the author takes this mission seriously. The book follows a rigorous, step-by-step methodology. The reader is expected to start from an empty project and extend it line by line while the book explains, what is going on. It is best to demonstrate with an excerpt:
- Start by creating an empty Button element below our answers ViewGroup LinearLayout (but still within the root LinearLayout element). Assign itthe ID skip, so that we can reference it in Java:
android:id="@+id/skip" - Create some padding between the answers and the new button by using a margin:
android:layout_marginTop="12sp" - Give it the display label Skip Question:
android:text="Skip Question" - Like all of the previous widgets, the width should be fill_parent and the height should be wrap_content: android:layout_width="fill_parent" android:layout_height="wrap_content"
The author made some decisions that I found a bit strange. First and foremost, even though the title is Android User Interface Development, it does not discuss 2D (Canvas and co.) and 3D (OpenGL) at all. I am sure that some readers will miss these parts. I am big fan of command-line development environments but maybe Eclipse is better for beginners.
All in all, I enjoyed this book and I even learnt from it. The book does not cover all the aspects of Android development but it addresses a very important application type. It is a good place to start with Android development.
Tuesday, February 22, 2011
3-level expandable lists
I got a question in a comment whether 3-level expandable list would be possible. I responded that the ExpandableListView implementation is wired to have two levels but then another comment mentioned that it may be possible by embedding one ExpandableListView into another. I was busy then but now I found time to take the challenge and to implement a test program.
Click here to download the example program.
Our three-level list is a variation of an earlier example program but I inserted another level (somewhat without reason :-)).
The principle of the implementation is simple. The first-level list is backed by an ExpandableListAdapter. The child views generated by this adapter are ExpandableListAdapters themselves that provide the second-level groups and the eventual child elements. Listeners are set for the group events (expand/collapse) of the second-level lists. When a group event occurs on a second-level list, the layout of the first-level list is also recalculated to accomodate for the size changes of second-level expandable lists.
This sounds simple but there are some tricky points that made me spent more time on this prototype than I expected. The first issue is with the individual rows of the second-level lists and the view recycler. Check out getChildView() in ColorExpListAdapter.java. When the layout of the list is recalculated, the old views are offered to the list adapter for reuse. The getChildView() method gets the old view instance in the convertView parameter. If convertView is not null, the adapter has the option of reusing the old view instance. In this case the adapter does not instantiate a new view but sets the old view instance appropriately, decreasing garbage (Click here if you want to read more about the importance of less garbage collection in mobile environments). Now our problem is that we cannot just set up an ExpandableListView instance without destroying its internal state, i.e. the expanded/collapsed state of the groups. For this reason, it is extremely important to preserve the views and to prevent the first-level ExpandableListView of rearranging the second-level lists (handing out a second-level ExpandableListView in a certain position as a convertView at a different position). For this reason I implemented a cache in ColorExpListAdapter that makes sure that only one second-level ExpandableListView is generated for each child position in the first-level list and that second-level ExpandableListView instance is consistently returned for the same position. This guarantees that the collapsed/expanded state of the second-level views are preserved when the layout of the first-level list is recalculated.
The other tricky issue is the size of the second-level lists. The layout recalculation of the first-level list is triggered by a group event of a second-level list. Due to the way expandable lists are implemented, when this event occurs, the items of the second-level list which was clicked are not yet added/removed according to the expand/collapse action. This means that the size of the list will be incorrect when the first-level list layout is recalculated.
To solve this problem, observe the row count calculation in ColorExpListAdapter's calculateRowCount method and the way the row count is used in DebugExpandableListView's onMeasure method. DebugExpandableListView originally started to exist so that I can observe the layout of the second-level lists then it turned out that it has an important function: override the onMeasure method of the original ExpandableListView (actually inherited from ListView). Lazily, I kept the class name and the debug messages in it so that you can observe the rather complex operation of the layout process if you feel like.
Click here to download the example program.
Our three-level list is a variation of an earlier example program but I inserted another level (somewhat without reason :-)).
The principle of the implementation is simple. The first-level list is backed by an ExpandableListAdapter. The child views generated by this adapter are ExpandableListAdapters themselves that provide the second-level groups and the eventual child elements. Listeners are set for the group events (expand/collapse) of the second-level lists. When a group event occurs on a second-level list, the layout of the first-level list is also recalculated to accomodate for the size changes of second-level expandable lists.
This sounds simple but there are some tricky points that made me spent more time on this prototype than I expected. The first issue is with the individual rows of the second-level lists and the view recycler. Check out getChildView() in ColorExpListAdapter.java. When the layout of the list is recalculated, the old views are offered to the list adapter for reuse. The getChildView() method gets the old view instance in the convertView parameter. If convertView is not null, the adapter has the option of reusing the old view instance. In this case the adapter does not instantiate a new view but sets the old view instance appropriately, decreasing garbage (Click here if you want to read more about the importance of less garbage collection in mobile environments). Now our problem is that we cannot just set up an ExpandableListView instance without destroying its internal state, i.e. the expanded/collapsed state of the groups. For this reason, it is extremely important to preserve the views and to prevent the first-level ExpandableListView of rearranging the second-level lists (handing out a second-level ExpandableListView in a certain position as a convertView at a different position). For this reason I implemented a cache in ColorExpListAdapter that makes sure that only one second-level ExpandableListView is generated for each child position in the first-level list and that second-level ExpandableListView instance is consistently returned for the same position. This guarantees that the collapsed/expanded state of the second-level views are preserved when the layout of the first-level list is recalculated.
The other tricky issue is the size of the second-level lists. The layout recalculation of the first-level list is triggered by a group event of a second-level list. Due to the way expandable lists are implemented, when this event occurs, the items of the second-level list which was clicked are not yet added/removed according to the expand/collapse action. This means that the size of the list will be incorrect when the first-level list layout is recalculated.
To solve this problem, observe the row count calculation in ColorExpListAdapter's calculateRowCount method and the way the row count is used in DebugExpandableListView's onMeasure method. DebugExpandableListView originally started to exist so that I can observe the layout of the second-level lists then it turned out that it has an important function: override the onMeasure method of the original ExpandableListView (actually inherited from ListView). Lazily, I kept the class name and the debug messages in it so that you can observe the rather complex operation of the layout process if you feel like.
Wednesday, January 26, 2011
Plugins with user interface
A while ago I wrote about plugins and now I received a mail from somebody asking about plugins that extend the UI of an application. From one side, these "plugins" are the cornerstone of Android application design: activity invocation can be considered a "UI plugin mechanism". But what if somebody wants to plug in UI elements into an application's screen so that the UI elements provided by the plugin appear on the application's own screen? Limited screen estate makes these sorts of plugins less relevant than in case of PC applications but there is a precedent: widgets plug into the Launcher's screen this way. I decided therefore to play with the idea and created a prototype.
Click here to download the application.
You will find 3 subdirectories in the package (respluginapp, resplugin1, resplugin2), each is a separate Android project. Compile and install respluginapp and launch the application. You will see something like this:
Now compile and install resplugin1 and resplugin2. You will notice that the screen of respluginapp dynamically changes as you install the plugin packages. Eventually it will look like this:
The prototype idea is simple. We have 4 rows on the screen. Each row has a default content (some text) but if a plugin is installed, the default row content is replaced by the UI elements provided by the plugin.
Sounds simple but it isn't.
On the other hand, applications that change their screen layout when you install plugins are kind of cool as the Android widgets demonstrate. Decide yourself.
Update: this issue caused compilation errors in the projects. As raw Map is not supported anymore on the AIDL interface, I decided to follow the advice and replaced Maps with Bundles. I also converted the projects into Eclipse projects.
Click here to download the updated code.
Click here to download the application.
You will find 3 subdirectories in the package (respluginapp, resplugin1, resplugin2), each is a separate Android project. Compile and install respluginapp and launch the application. You will see something like this:
Now compile and install resplugin1 and resplugin2. You will notice that the screen of respluginapp dynamically changes as you install the plugin packages. Eventually it will look like this:
The prototype idea is simple. We have 4 rows on the screen. Each row has a default content (some text) but if a plugin is installed, the default row content is replaced by the UI elements provided by the plugin.
Sounds simple but it isn't.
- Getting the layout from the newly installed plugin package and recognizing the plugin package is the easy part. The plugin package is recognized because it exposes a service with aexp.intent.action.PICK_RESPLUGIN intent action. That service will be used to handle plugin events. There is already a hack here: the name of a layout is available only to the application in the package because the resource compiler generates R.java that maps layout names to layout IDs. As the plugin host application cannot access this R.java, it has no access to layout names. Our plugin host application accesses the plugin's row layout by ID (0x7f030000 is hardcoded). This ID is allocated to the layout whose name is the first alphabetically among the layouts. As our plugins have only one layout in the package, it is not really an issue but this hack can cause quite a nightmare in larger projects.
- The next hack happens with widget IDs. The resource compiler assigns IDs sequentially. Unfortunately it uses the same ID base for the plugin host application and the plugins so chances are that the IDs in the plugin host application and in the layouts of the plugins overlap. The plugin host application solves it by adding a row-specific offset to the IDs of the UI elements when they are loaded from the layout provided by the plugin package. But the downside is that from this moment, IDs used by the plugin host application and IDs used by the plugin will be different and the plugin host application has to constantly convert back and forth when it sends events to the plugin event handler service.
- While the UI elements reside in the plugin host application's screen, the event handling is provided by a service exposed by the plugin. The event handling looks like the following: the event is intercepted in the plugin host application, the state of the plugin UI elements is serialized, the plugin event handler service is invoked that processes the event using the serialized plugin UI element state and spits out actions, how the plugin UI elements should be updated. Only some basic functionality is implemented in this prototype. Only button click events are intercepted and the state capture/update only works for TextViews and descendants. But you get the idea how complex it would be to do it properly. While fiddling with this part, I ran into a dead end street because I thought RemoteViews could be used to implement the serialization/deserialization. RemoteViews, however, does not support EditText so this approach had to be abandoned.
On the other hand, applications that change their screen layout when you install plugins are kind of cool as the Android widgets demonstrate. Decide yourself.
Update: this issue caused compilation errors in the projects. As raw Map is not supported anymore on the AIDL interface, I decided to follow the advice and replaced Maps with Bundles. I also converted the projects into Eclipse projects.
Click here to download the updated code.
Subscribe to:
Posts (Atom)