Continuing with my series of boardgame-related side projects, I've written a small holo-themed app for determining who goes first called (unsurprisingly) Who Goes First. As with most side projects it's also open source. I released this app a while ago but thought I'd mention it here.
There wasn't much complex about it; mostly I was pleased with how easy it is to make a good-looking app with holo theming (even if you're not very talented in the design department).
The only interesting thing I learned relate to the arrow pointing towards who goes first. If you need more than six arrows, then it'll just show "X#" inside of the arrow instead. This required scaling the text size depending on how long the text was. I ended up using a Region for the arrow (defined via the Path used to construct the arrow) and another Region for the text and test if they overlapped; if they did, shrink text size until they don't. You can check out what I'm talking about in the actual ArrowView.
The one problem I've run into is when there are multiple people at the table with this app. The app does not use networking to sync their random seeds, so you can end up with multiple people going first. I guess my next project will have to be a "Who Runs Who Goes First First" app. :)
I post random things I've learned while coding with the hope that it will save people time and effort.
Showing posts with label apps. Show all posts
Showing posts with label apps. Show all posts
Wednesday, December 18, 2013
Monday, November 25, 2013
Sample Android Project: Movies
There's a sample application that I worked on recently with talented designer Chris Arvin for a talk we gave at DroidCon UK 2013. I also repurposed part of it for the talk about animations I gave at AnDevCon a few weeks later.
The app is open source and you can check it out here: https://github.com/dlew/android-movies-demo
You can get the APK here: https://github.com/dlew/android-movies-demo/releases/
The interactions required a bit of interesting engineering so I wanted to discuss some of it here.
ViewPager and Decor
One of the key interactions we wanted was the app to feel like a ViewPager so that it was an interaction users were familiar with except that the result isn't quite the same - instead of paging, content would slide out from underneath other content. This is not something ViewPager is inherently designed to do.
There were two options open to me. One was to rip out all of ViewPager's event handling code and make my own custom View, which was not an appealing prospect. The other was to find some way to manipulate ViewPager itself.
It turns out there is an interface ViewPager finds special called Decor. If a View implements Decor, then ViewPager treats it as a special View that can remain on top of it the ViewPager. This is normally for implementing your own tabs, but in this case I imported my own copy of ViewPager (since Decor is hidden normally) and made my entire UI a Decor View.
It's a neat trick that would work for any app that wants ViewPager event handling without paging Views, though I think if you wanted a less hacky solution you'd write your own event code.
Custom Views Everywhere
I've gone from avoiding custom Views like the plague to fully embracing them.
The key realization is that custom Views do not need to handle every situation. If you're looking at framework custom Views as reference you will be overwhelmed quickly. For example, during measurement framework Views have to handle WRAP_CONTENT, MATCH_PARENT, and everything in between; but if you know your View is always going to be MATCH_PARENT you can greatly simplify all your code.
The movies sample app is a pile of custom Views. First there's SlidingRevealViewGroup, which generically shows one View slide out from another. On top of that is built MovieRowView, which has the specific Views that we want to display. Then there's SlidingPairView, which is a set of two SlidingRevealViewGroups that creates the side-by-side effect seen in the app.
I also needed a few other custom Views to shore up some other issues. CenteringRelativeLayout just adjusts the film cover so that, as it shrinks in size, it still looks centered. SlidingListView was required for performance; we needed to manipulate the rows directly when sliding, instead of constantly notifying of data changed.
Performance Tricks
The coolest part of the whole app is the slide: how the cover becomes smaller and the content slides out from underneath. All of this was achieved through translation of Views. I took advantage of the fact that ViewGroups don't render their children outside of their own bounds. By translating content I could hide them outside the clip bounds.
When a slide starts, it throws practically everything into hardware layers then just slides Views left/right. Moving around pre-rendered content is fast and as a result the paging is silky smooth.
Nowadays I'm a huge fan of those basic properties of Views (translation, scale, rotation and alpha). They're the core reason to support ICS+ only; with these properties you can take your interactions to the next level.
Drawbacks
The drawback of the solution I came up with is that it creates a ton of overdraw. This hurts performance, especially (as far as I can tell) when rendering a new row while scrolling up/down. I sacrificed scrolling performance for paging performance. Perhaps there is a way to achieve both, but I'm pretty much done with this sample for now.
The rounded corners could've been implemented in a much better fashion than an overlaid Drawable, but with the limited time before the presentation I had, I could not come up with a better solution.
The app is open source and you can check it out here: https://github.com/dlew/android-movies-demo
You can get the APK here: https://github.com/dlew/android-movies-demo/releases/
The interactions required a bit of interesting engineering so I wanted to discuss some of it here.
ViewPager and Decor
One of the key interactions we wanted was the app to feel like a ViewPager so that it was an interaction users were familiar with except that the result isn't quite the same - instead of paging, content would slide out from underneath other content. This is not something ViewPager is inherently designed to do.
There were two options open to me. One was to rip out all of ViewPager's event handling code and make my own custom View, which was not an appealing prospect. The other was to find some way to manipulate ViewPager itself.
It turns out there is an interface ViewPager finds special called Decor. If a View implements Decor, then ViewPager treats it as a special View that can remain on top of it the ViewPager. This is normally for implementing your own tabs, but in this case I imported my own copy of ViewPager (since Decor is hidden normally) and made my entire UI a Decor View.
It's a neat trick that would work for any app that wants ViewPager event handling without paging Views, though I think if you wanted a less hacky solution you'd write your own event code.
Custom Views Everywhere
I've gone from avoiding custom Views like the plague to fully embracing them.
The key realization is that custom Views do not need to handle every situation. If you're looking at framework custom Views as reference you will be overwhelmed quickly. For example, during measurement framework Views have to handle WRAP_CONTENT, MATCH_PARENT, and everything in between; but if you know your View is always going to be MATCH_PARENT you can greatly simplify all your code.
The movies sample app is a pile of custom Views. First there's SlidingRevealViewGroup, which generically shows one View slide out from another. On top of that is built MovieRowView, which has the specific Views that we want to display. Then there's SlidingPairView, which is a set of two SlidingRevealViewGroups that creates the side-by-side effect seen in the app.
I also needed a few other custom Views to shore up some other issues. CenteringRelativeLayout just adjusts the film cover so that, as it shrinks in size, it still looks centered. SlidingListView was required for performance; we needed to manipulate the rows directly when sliding, instead of constantly notifying of data changed.
Performance Tricks
The coolest part of the whole app is the slide: how the cover becomes smaller and the content slides out from underneath. All of this was achieved through translation of Views. I took advantage of the fact that ViewGroups don't render their children outside of their own bounds. By translating content I could hide them outside the clip bounds.
When a slide starts, it throws practically everything into hardware layers then just slides Views left/right. Moving around pre-rendered content is fast and as a result the paging is silky smooth.
Nowadays I'm a huge fan of those basic properties of Views (translation, scale, rotation and alpha). They're the core reason to support ICS+ only; with these properties you can take your interactions to the next level.
Drawbacks
The drawback of the solution I came up with is that it creates a ton of overdraw. This hurts performance, especially (as far as I can tell) when rendering a new row while scrolling up/down. I sacrificed scrolling performance for paging performance. Perhaps there is a way to achieve both, but I'm pretty much done with this sample for now.
The rounded corners could've been implemented in a much better fashion than an overlaid Drawable, but with the limited time before the presentation I had, I could not come up with a better solution.
Thursday, November 21, 2013
Is findViewById() Slow?
One of the presenters at Droidcon UK 2013 made the point that View.findViewById() has been unjustly villainized and that the ViewHolder pattern recommended by Google is unnecessary. After hearing that I studied the code (in View and ViewGroup) and did not observe any heinous performance issues contained therein. It's a pretty simple recursive lookup that walks the View hierarchy for an id.
As this article points out, findViewById() is necessarily slower than ViewHolder because it's O(n) vs. O(1). But that doesn't mean findViewById() is slow. If the operation is sufficiently fast and n is low then it doesn't matter if it's O(n). Who cares if I have to do a 1 nanosecond operation a thousand times?
My hypothesis is that findViewById() is fast enough as to be negligible. With that in mind, I cooked up a sample application that tests findViewById(): https://github.com/dlew/android-findviewbyid
You can dynamically create a hierarchy of Views, either adding to the depth or the number of children at each node. When you hit "run" it searches for the furthest away id a number of times, then averages the time taken. I ran the test on my Nexus 1, to somewhat recreate the situation described when Google explicitly recommended the ViewHolder pattern three years ago.
Here's the results from a test of many possible depth/views per node values: http://goo.gl/dK2vo3
As can be seen from the chart, the time taken to use findViewById() is roughly linear with the number of Views in the hierarchy. What's key here, though, is that for a low number of Views it barely takes any time at all - usually a matter of several microseconds. And remember, this is on a rather old phone, the Nexus 1.
That said, if you were to call findViewById() many, many times it could cause a problem. Admittedly this part of the post is going to be a bit more hand-wavy than the rest, but: I tried out scrolling on a ListView on my Nexus 1 - there is an upper bound to how fast I can scroll and I can't seem to break 60 getViews() per second. That means that getView() is called once per 16ms frame. Given that we're measuring findViewById() in microseconds, even calling it a dozen times (with a reasonable number of Views) shouldn't cause performance issues.
So my conclusion is that findViewById() is harmless in most practical circumstances. I'll probably still use ViewHolder to avoid casting Views all the time, but performance will not be the main purpose anymore.
Disclaimer: I'm not a performance expert so I suspect there's something I'm missing (especially since I'm going directly against Google advice here). If someone knows of a missing link let me know!
As this article points out, findViewById() is necessarily slower than ViewHolder because it's O(n) vs. O(1). But that doesn't mean findViewById() is slow. If the operation is sufficiently fast and n is low then it doesn't matter if it's O(n). Who cares if I have to do a 1 nanosecond operation a thousand times?
My hypothesis is that findViewById() is fast enough as to be negligible. With that in mind, I cooked up a sample application that tests findViewById(): https://github.com/dlew/android-findviewbyid
You can dynamically create a hierarchy of Views, either adding to the depth or the number of children at each node. When you hit "run" it searches for the furthest away id a number of times, then averages the time taken. I ran the test on my Nexus 1, to somewhat recreate the situation described when Google explicitly recommended the ViewHolder pattern three years ago.
Here's the results from a test of many possible depth/views per node values: http://goo.gl/dK2vo3
As can be seen from the chart, the time taken to use findViewById() is roughly linear with the number of Views in the hierarchy. What's key here, though, is that for a low number of Views it barely takes any time at all - usually a matter of several microseconds. And remember, this is on a rather old phone, the Nexus 1.
That said, if you were to call findViewById() many, many times it could cause a problem. Admittedly this part of the post is going to be a bit more hand-wavy than the rest, but: I tried out scrolling on a ListView on my Nexus 1 - there is an upper bound to how fast I can scroll and I can't seem to break 60 getViews() per second. That means that getView() is called once per 16ms frame. Given that we're measuring findViewById() in microseconds, even calling it a dozen times (with a reasonable number of Views) shouldn't cause performance issues.
So my conclusion is that findViewById() is harmless in most practical circumstances. I'll probably still use ViewHolder to avoid casting Views all the time, but performance will not be the main purpose anymore.
Disclaimer: I'm not a performance expert so I suspect there's something I'm missing (especially since I'm going directly against Google advice here). If someone knows of a missing link let me know!
Tuesday, May 14, 2013
Sentinels of the Multiverse Randomizer App
My latest side project is a randomizer application for the board game Sentinels of the Multiverse.
You can grab the app here: https://play.google.com/store/apps/details?id=com.idunnolol.sotm
You can check out the source here: https://github.com/dlew/android-sotm
I have to admit that writing it was a gigantic waste of time (the benefit is minuscule in comparison to the time it took to write). However, it was a ton of fun to make; there's something very liberating about writing an app from scratch with no baggage to worry about. It was also liberating to write an app that uses modern APIs with complete disregard to backwards compatibility. As such, it's only available on ICS+.
In other news, I will be at Google I/O again this week. In particular I will be helping man Expedia's booth in the Android sandbox; feel free to come by and say hello.
You can grab the app here: https://play.google.com/store/apps/details?id=com.idunnolol.sotm
You can check out the source here: https://github.com/dlew/android-sotm
I have to admit that writing it was a gigantic waste of time (the benefit is minuscule in comparison to the time it took to write). However, it was a ton of fun to make; there's something very liberating about writing an app from scratch with no baggage to worry about. It was also liberating to write an app that uses modern APIs with complete disregard to backwards compatibility. As such, it's only available on ICS+.
In other news, I will be at Google I/O again this week. In particular I will be helping man Expedia's booth in the Android sandbox; feel free to come by and say hello.
Tuesday, February 26, 2013
Resistance/Avalon App
I've once again taken a detour from my more serious Android development to do some silly side projects.
The first of them is an app for the board game The Resistance and The Resistance: Avalon. I've recently become obsessed with this game because it's just a ton of fun. The game involves a lot of hidden roles, so there's a lengthy boot-up sequence where each person's allegiances are determined. When you use all the roles available, it gets to be a bit of chore. So I've written a dumb app that uses Android's TTS to speak the setup out loud, making the process a tiny bit easier.
The application can be found here: https://play.google.com/store/apps/details?id=com.idunnolol.resistance
Open source code here: https://github.com/dlew/android-resistance
As is usually the case with side projects, the time I put into the project vastly outweighs the time I'll ever save by using the app. As such, I took this as an opportunity to try out two things: Android's TextToSpeech capabilities, and Maven Android builds.
TextToSpeech
I found TextToSpeech to be far easier to use than I expected. It took me almost no time to get it up and running. The only snag I ran into was using the OnUtteranceCompletedListener. You need to give an utterance id to something you play before the listener will fire:
Maven Android Builds
I've been woefully behind the times with regards to Android build technology. For years I've seen open source github projects using Maven but I've always ignored it because I'm scared of the big angry pom.xml files. So I determined that I would use this simple app to teach myself Maven (via maven-android-plugin).
I found the initial setup of Maven to be pretty simple. I had the samples up and running in no time using the "getting started" section of the site. I even got Eclipse building the application using Maven using m2e-android. So far so good.
I ran into a brick wall when I tried to add a library (in particular, ActionBarSherlock). The command line Maven worked just fine when I added the library dependency, but I happen to enjoy the amenities of a modern IDE so it must work in Eclipse. But in Eclipse, it wouldn't build - it complained about a missing dependency. It turns out that you need to still manually do stuff for each library anyways if you're using Eclipse + Maven (unless I'm mis-reading the state of apklib, which is entirely possible). Wasn't that the whole reason I started to use Maven in the first place? To simplify my build process?
I think I'll keep making pom.xml for command line building/testing, but for actual dev in Eclipse it actually sets me back to use Maven. Perhaps it integrates better with IntelliJ? That alone may be reason to switch. But at this point I'm far more excited for the upcoming Gradle builds.
One More Thing
If there was one cool thing I did with the code, it was the setup of Config.java. Originally I had it with a bunch of booleans, one for each option; but this led to a lot of switch-like code that just felt repetitive. By converting it to an enum keyed-boolean store, I was able to automate a lot of app. I always love it when you can greatly simplify and condense the code at the same time.
The first of them is an app for the board game The Resistance and The Resistance: Avalon. I've recently become obsessed with this game because it's just a ton of fun. The game involves a lot of hidden roles, so there's a lengthy boot-up sequence where each person's allegiances are determined. When you use all the roles available, it gets to be a bit of chore. So I've written a dumb app that uses Android's TTS to speak the setup out loud, making the process a tiny bit easier.
The application can be found here: https://play.google.com/store/apps/details?id=com.idunnolol.resistance
Open source code here: https://github.com/dlew/android-resistance
As is usually the case with side projects, the time I put into the project vastly outweighs the time I'll ever save by using the app. As such, I took this as an opportunity to try out two things: Android's TextToSpeech capabilities, and Maven Android builds.
TextToSpeech
I found TextToSpeech to be far easier to use than I expected. It took me almost no time to get it up and running. The only snag I ran into was using the OnUtteranceCompletedListener. You need to give an utterance id to something you play before the listener will fire:
HashMap<String, String> params = new HashMap<String, String>();
endParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "myUtteranceId");
mTTS.speak("Say Something", TextToSpeech.QUEUE_ADD, params);
Maven Android Builds
I've been woefully behind the times with regards to Android build technology. For years I've seen open source github projects using Maven but I've always ignored it because I'm scared of the big angry pom.xml files. So I determined that I would use this simple app to teach myself Maven (via maven-android-plugin).
I found the initial setup of Maven to be pretty simple. I had the samples up and running in no time using the "getting started" section of the site. I even got Eclipse building the application using Maven using m2e-android. So far so good.
I ran into a brick wall when I tried to add a library (in particular, ActionBarSherlock). The command line Maven worked just fine when I added the library dependency, but I happen to enjoy the amenities of a modern IDE so it must work in Eclipse. But in Eclipse, it wouldn't build - it complained about a missing dependency. It turns out that you need to still manually do stuff for each library anyways if you're using Eclipse + Maven (unless I'm mis-reading the state of apklib, which is entirely possible). Wasn't that the whole reason I started to use Maven in the first place? To simplify my build process?
I think I'll keep making pom.xml for command line building/testing, but for actual dev in Eclipse it actually sets me back to use Maven. Perhaps it integrates better with IntelliJ? That alone may be reason to switch. But at this point I'm far more excited for the upcoming Gradle builds.
One More Thing
If there was one cool thing I did with the code, it was the setup of Config.java. Originally I had it with a bunch of booleans, one for each option; but this led to a lot of switch-like code that just felt repetitive. By converting it to an enum keyed-boolean store, I was able to automate a lot of app. I always love it when you can greatly simplify and condense the code at the same time.
Wednesday, June 2, 2010
Headset Blocker
If you own a Nexus One, you may be familiar with this scenario: you load up your N1 with some rocking tunes. You plug in your headphones and go out exercising with it. Suddenly, the tunes start skipping. Why?
The problem is that the headset jack is interpreting what you are doing as a control signal. The exact cause hasn't been shown to me yet; there are a number of theories from a lack of software noise filtering to the difference between TRS and TRRS connectors. Regardless, it's a serious issue - some people can't even listen to music in their cars with the N1 because it skips so much.
Luckily, in the last few days I was shown an answer. The control commands from headsets are actually sent out as a chain broadcast, MEDIA_BUTTON. As with any chain broadcast, your receiver can abort the broadcast. So the simple solution is to make a BroadcastReceiver with an ultra-high priority that intercepts and aborts the MEDIA_BUTTON broadcast.
To that end, I've put a small app on the Market, Headset Blocker. It's a small, free widget that allows you to enable/disable blocking of the MEDIA_BUTTON broadcast. You can get it here:

The source code is here.
One interesting choice I had to make when writing this app was how to toggle the blocking. I had one of two options:
1. Use SharedPreferences to track when blocking is enabled. Capture all MEDIA_BUTTON broadcasts, but only abort when the preference is enabled.
2. Enable/disable my app's BroadcastReceiver. It will always abort when enabled, but is only enabled via the widget.
I went with the latter option, as I felt that would save just a little bit more battery when disabled (as the BroadcastReceiver itself would be disabled).
The problem is that the headset jack is interpreting what you are doing as a control signal. The exact cause hasn't been shown to me yet; there are a number of theories from a lack of software noise filtering to the difference between TRS and TRRS connectors. Regardless, it's a serious issue - some people can't even listen to music in their cars with the N1 because it skips so much.
Luckily, in the last few days I was shown an answer. The control commands from headsets are actually sent out as a chain broadcast, MEDIA_BUTTON. As with any chain broadcast, your receiver can abort the broadcast. So the simple solution is to make a BroadcastReceiver with an ultra-high priority that intercepts and aborts the MEDIA_BUTTON broadcast.
To that end, I've put a small app on the Market, Headset Blocker. It's a small, free widget that allows you to enable/disable blocking of the MEDIA_BUTTON broadcast. You can get it here:
The source code is here.
One interesting choice I had to make when writing this app was how to toggle the blocking. I had one of two options:
1. Use SharedPreferences to track when blocking is enabled. Capture all MEDIA_BUTTON broadcasts, but only abort when the preference is enabled.
2. Enable/disable my app's BroadcastReceiver. It will always abort when enabled, but is only enabled via the widget.
I went with the latter option, as I felt that would save just a little bit more battery when disabled (as the BroadcastReceiver itself would be disabled).
Subscribe to:
Posts (Atom)

