Mark L. Murphy's Blog, page 72
September 4, 2012
Android's Lessons for New Mobile Operating Systems: Audience
While Android and iOS presently have the smartphone market largely sewn up, other mobile operating systems are in the works or will arise over time. For example, Mozilla is creating Firefox Mobile OS as their entrant into this space.
In this blog post series, I will explore some lessons that I think we can learn from Android’s successes and failures that can help new mobile OSes as they try to break into this space. Today, I’d like to focus on audience.
By “audience” I mean the target market for the mobile OS. This may sound a bit odd, and you might think that the target market for a mobile OS is “everybody”. After all, it’s not like Android or iOS are targeted solely at left-handed Alaskan pipe-welders.
However, it is rare that a new entrant to an established market will be able to pull off that sort of mass-market appeal. Android and iOS defined a new model for smartphones, emphasizing ease of access (e.g., finger-friendly touchscreens), integrated app markets, and the like. This was a fairly substantial departure from the devices that dominated the landscape before, one that “crossed the chasm” into mass-market appeal. However, until and unless a similar massive model change occurs, everyone else is playing in Android’s and iOS’s sandbox. Even mighty Microsoft is having an uphill climb trying to get Windows Phone anywhere near the status that Windows Mobile enjoyed before Android and iOS arrived on the scene. Just because Android succeeded in the mass market does not mean that every other mobile OS will do the same.
New players are better served considering targeting a niche, expanding the niche, attacking adjacent niches, and so on, as a means to establish a beachhead in the marketplace. While a new mobile OS might not be able to offer a substantially improved experience for everyone , it is reasonably likely that it can do so for someone. The makers of that OS simply have to define who “someone” is, know what that someone values that the OS can deliver, and know how to reach that someone.
For example:
A mobile OS could say that children, and their parents and/or educators, are the “someone”. The mobile OS might bake in particular capabilities that make it substantially better for that audience, such as separation between the user and the administrator of the device, or OLPC-style on-device app development. The rollout of the mobile OS could emphasize the educational aspects, including getting lots of educational content into its app distribution channels and partnering with educational institutions or organizations (e.g., create a KhanPhone or a TEDTablet). And so on.
A mobile OS could say that “netizens” — defined here as Internet-savvy proponents of freedom — are the “someone”. The mobile OS might bake in particular capabilities that make it substantially better for that audience, such as a security model that is tighter than competitors, or built-in cryptographically-secure communications options. The rollout of the mobile OS could emphasize the “freedom” aspects, including working with groups from the Guardian Project to the EFF to ensure that those in need of highly-secure devices know where to turn. Later niches could then include others concerned about security breaches by operators or others, such as enterprises.
Of course, there are other possible “someones” that the mobile OS could target. The key is to choose one and make sure that the mobile OS — and the broader experience around that OS — fit that niche. Having a longer-term plan for a possible chain of niches is nice, but you have to start somewhere. Simply creating what you think is a better mousetrap is not a guarantee that much of the world will beat a path to your door. Android and iOS are reasonable mousetraps for most people — you need to find a place where your mousetrap can be clearly and definitively superior for somebody (though, presumably, not for the mice).
Tomorrow, I will take a look at security, and how another mobile OS can learn from Android’s results to date in this area.
August 27, 2012
The Busy Coder's Guide to Android Development Version 4.1 Released
(yeah, I know, it’s a bit before the first of the month, but I didn’t think you’d mind…)
Subscribers now have access to the latest release of The Busy Coder’s Guide to Android Development, known as Version 4.1, in all formats. Just log into your Warescription page and download away, or set up an account and subscribe!
This update includes:
New chapters on Google Cloud Messaging (GCM) and GridLayout
A small chapter on the basics of widget-based UI frameworks, for those reading the book without much experience in that style of UI
A new chapter in the Widget Catalog for ExpandableListView
Coverage of the two-up ViewPager example that I mentioned in a recent blog post
Minor updates to coverage on the Kindle Fire (SDK add-on now available with Fire-specific emulator images) and notifications (switched from NotificationCompat2 to the new Android Support package)
Lots and lots of errata fixes
Those using the APK version of the book have two small improvements to the EmPub reader app:
You can zoom in a bit more, which might be useful on some devices. Note that this is via the app preferences, not via pinch-to-zoom, lest I get sued by a fruit-flavored firm for patent infringement (next issue: is my face a rounded rectangle?).
You have an option in the preferences to have the BACK button return you to the previous chapter you had visited this session (ignoring the cover and Table of Contents). By default, the BACK button exits the app.
If you encounter any problems with this update or your Warescription in general, please contact wares@commonsware.com, and be sure to provide your Warescription user ID.
August 20, 2012
Multiple-View ViewPager Options
On Friday, I tweeted what I thought was a shot in the dark:
Has anyone seen any forks of ViewPager that show multiple children at once? Thinking 2-3 at a time for landscape tablets. Thx!
Juhani Lehtimaeki chimed in with what I was thinking:
It would make responsive design on UIs like that very easy.
After all, if the objective of using fragments is to be able to use them individually on smaller screens and in aggregate on larger screens, it stands to reason that we might want to do the same sort of thing with the contents of a ViewPager.
I received a number of responses, including three separate implementations of what I requested. Curiously, none of those implementations bore much resemblance to each other, beyond the basics of having multiple visible pages in a ViewPager.
In this post, I will examine each of the three approaches, so you can see how they work and what visual results you get.
The approach that most closely met what I had in mind was pointed out in tweets from Lucio Maciel, and Hello, Android’s Ed Burnette, and to use getPageWidth() on PagerAdapter.
getPageWidth() returns a floating-point number, between 0 and 1, representing the portion of the width of the ViewPager that a given page should take up. By default, the page width is 1, but by overriding this, you can have multiple pages on the screen simultaneously.
I have uploaded a project that demonstrates this to a GitHub repo, but let’s take a look at the important bits here.
The key, of course, is to override getPageWidth() in your PagerAdapter:
@Override
public float getPageWidth(int position) {
return(0.5f);
}
It is probably also a good idea to call setOffscreenPageLimit() on the ViewPager. By default (and at minimum), ViewPager will cache three pages: the one presently visible, and one on either side. However, if you are showing more than one at a time, you should bump the limit to be 3 times the number of simultaneous pages. For a page width of 0.5f — meaning two pages at a time — you would want to call setOffscreenPageLimit(6), to make sure that you had enough pages cached for both the current visible contents and one full swipe to either side.
This will give you two pages at a time:
ViewPager even handles “partial swipes” — a careful swipe can slide the right-hand page into the left-hand position and slide in a new right-hand page. And ViewPager stops when you run out of pages, so the last page will always be on the right, no matter how many pages at a time and how many total pages you happen to have.
The biggest downside to this approach is that it will not work well with the current crop of indicators. PagerTitleStrip and PagerTabStrip (and, possibly, Jake Wharton’s ViewPagerIndicator library, though I have not tried that here) assume that there is a single selected page. While the indicator will adjust properly, the visual representation shows that the left-hand page is the one selected (e.g., the tab with the highlight), even though two or more pages are visible. You can probably overcome this with a custom indicator (e.g., highlight the selected tab and the one to its right).
Also note that this approach collides a bit with setPageMargin() on ViewPager. setPageMargin() indicates an amount of whitespace that should go in a gutter between pages. In principle, this would work great with showing multiple simultaneous pages in a ViewPager. However, ViewPager does not take the gutter into account when interpreting the getPageWidth() value. For example, suppose getPageWidth() returns 0.5f and we setPageMargin(20). On a 480-pixel-wide ViewPager, we will actually use 500 pixels: 240 for the left page, 240 for the right page, and 20 for the gutter. As a result, 20 pixels of our right-hand page are off the edge of the pager. Ideally, ViewPager would subtract out the page margin before applying the page width. One workaround is for you to derive the right getPageWidth() value based upon the ViewPager size and gutter yourself, rather than hard-coding a value. Or, build in your gutter into your page contents (e.g., using android:layout_marginLeft and android:layout_marginRight) and skip setPageMargin() entirely.
The second approach comes courtesy of Nicholas Klein, who advocated managing the sub-pages within a page yourself. While his code sample seemed to be written for perhaps a ListView (it uses a ListAdapter-style getView()), the concept can certainly be translated for ViewPager.
Once again, I have uploaded a project that demonstrates this to a GitHub repo.
With Nicholas’ approach, you put two children in each “page” layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/an..."
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<TextView
android:id="@+id/text2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
In your PagerAdapter instantiateItem() logic, you need to convert positions in ViewPager pages to positions in sub-pages, and update both of your sub-pages in the inflated layout, handling the case where there is an odd number of sub-pages, and so you could have only a left-hand sub-page:
@Override
public Object instantiateItem(ViewGroup container, int position) {
View page=
getLayoutInflater().inflate(R.layout.page, container, false);
TextView tv=(TextView)page.findViewById(R.id.text);
position=position * 2;
populateTextView(tv, position);
position++;
tv=(TextView)page.findViewById(R.id.text2);
if (position < getRealCount()) {
populateTextView(tv, position);
tv.setVisibility(View.VISIBLE);
}
else {
tv.setVisibility(View.INVISIBLE);
}
container.addView(page);
return(page);
}
Visually, this looks very similar to the first approach. However:
You cannot swipe by sub-page, only by full page. This may be a feature or a bug, depending upon your objectives.
It is more code.
It would be easier to adapt to indicators (e.g., PagerTitleStrip), as really the indicators would be indicating the page, not the set of sub-pages, and you could style and title those indicators as needed.
The third approach comes from Dave Smith, co-author of the well-regarded book Android Recipes. He went in a very different direction, using a custom container that disabled children clipping to show more than one page at a time.
His published sample code shows the whole thing in action. His container (com.example.pagercontainer.PagerContainer) wraps the ViewPager and calls setClipChildren(false); on itself, so even though the ViewPager is focused on one selected page, other pages that have coordinates beyond the ViewPager bounds are still visible, so long as they fit within the PagerContainer. By sizing the ViewPager to be smaller than the PagerContainer, the ViewPager can size its pages to that size, leaving room for other pages to be seen. PagerContainer, though, needs to help out a bit with touch events, as ViewPager will only handle swipe events on its own visible bounds, ignoring any pages visible to the sides.
Visually, his results depart significantly from the first two:
While this was not what I had in mind from my Twitter inquiry, Dave’s approach does solve a different problem: how does one really replace Gallery, now that it is deprecated? Dave’s code mimics Gallery’s look fairly closely, minus selection events (which could probably be added without a ton of difficulty).
Many thanks to all who responded to my tweet!
August 14, 2012
About Those @Override Errors
Since this question comes up a lot on StackOverflow, I figured that I would write up a blog post about it…
If you are trying to import some existing Android code into an application — such as an Android library project — and you encounter all sorts of errors about “XXX must override or implement a supertype method”, try commenting out the @Override annotation on that method. If the error for that method goes away, then your real problem is in your Eclipse configuration.
But, first, some history.
Annotations were introduced into Java with Java 1.5. One annotation in particular that was added was @Override. In Java 1.5, this annotation was a hint to the compiler that you were intending to override a method from a superclass. The hint was so that the compiler would yell at you if you actually were not overriding a method, for any number of possible reasons:
typo in the method name
wrong parameters
wrong return type
etc.
In Java 1.6, they extended the definition of @Override to allow it to be used in cases where you were not technically “overriding” a superclass method, but you were implementing a method required by an interface. This usage has proven to be popular, and you will find it in many bits of Android code.
Eclipse has a “Java compiler compliance” concept, technically independent of what actual Java compiler you are using for your project builds. Eclipse enforces that your code meets whatever Java version you declare in your “compliance” setting. If that setting is set to 1.5, then Eclipse will complain about @Override used on methods implemented to satisfy an interface and not overriding a superclass method.
There are two ways to fix this.
For an individual project, you can go into Project > Properties, choose the “Java Compiler” category on the left, and change the value in the “Compiler compliance level” drop-down. Sometimes, this will be disabled, because you are using your workspace-level setting and are not overriding it at the project level. Sometimes, though, the project might be set to override it at the project level and have it set to 1.5. If you would prefer to not override it on a project where it is overridden, you can uncheck the “Enable Project Specific Settings” checkbox at the top of the dialog.
For your entire workspace, you can go to the Preferences window and choose Java > Compiler from the category tree on the left. Here, you will see another “Compiler compliance level” drop-down, one that controls the default for all of your projects.
Maintaining a consistent 1.6 compiler compliance level will clear up these @Override complaints.
August 3, 2012
Plan Your Production Keystore Strategy
Of course, the first rule of Android development is: back up your production keystore.
:: insert obligatory Fight Club reference here ::
However, there is more to planning out your production development work than merely having an adequate backup regimen. You also need to think about who needs that production keystore.
This might seem simple. For solo development efforts, only the developer needs the keystore. For teams, whoever (or whatever) is responsible for creating the production APK builds needs the keystore. And, so your apps do not get accidentally hijacked, open source projects need to ensure that the world does not have the production keystore (i.e., don’t put it in version control).
However, there are other scenarios to consider:
What about a consultant?
What about an employee, creating an Android app for an employer, in addition to creating other apps on personal time?
What if you want to sell your app’s rights to somebody else?
In these cases, saying that there is a single production keystore is probably not the best answer. My general recommendation is:
Whoever owns the APK needs to own the keystore that signed that APK
Hence, in the case of the consultant, use individual keystores per client, or possibly per app. That way, the client can own the keystore(s) associated with the apps they hired the consultant to create, and if the client elects to go elsewhere for maintenance, they can.
In the case of the employee, please don’t use a personal keystore for business work. Generate a second keystore to use at the office. It’s not like they cost anything.
If you are making a line of apps, and you think that there might be a case where one is a hit and you wish to sell the rights to that one app (versus selling the whole business), you might consider using individual keystores per app. That way, when you sell rights to the app, its own keystore can go along for the ride.
I am sure that there are other scenarios as well – these are just three. Just make sure that you do not wind up in a situation where multiple businesses both need rights to the same keystore, as that can get messy quickly.
Oh, and, please back up your keystores. You’ll thank me later.
August 2, 2012
Learning From the Kies Incident
As some of you may already be aware, a couple of exploits were uncovered (then fixed by Samsung) that would allow apps with zero permissions to download and install apps without user confirmation on the Galaxy S3.
Most apps do not have to worry about leaking the INSTALL_PACKAGES capability, simply because most apps do not have that permission in the first place. However, there are some basic app security lessons that can be learned by what befell Samsung in this case.
Lesson #1: Beware the Intent Filter
There is little question that Android’s Intent system, for allowing loose coupling of applications, is drop-dead sexy (in programming terms, that is). And there are some Android developers that advocate that all integration, even within an app, should be done via public Intents and filters. I couldn’t disagree more, and the Kies incident is one example why.
Every time you publish an <intent-filter>, or otherwise export a component (e.g., write a ContentProvider and fail to lock it down), you are exposing an API. Just because the API comes in the form of Intent messages instead of some sort of function calls does not make it any less of an API. And every time you expose an API you need to think about whether you are adequately securing that API.
Hence, I only recommend exporting components and publishing filters for cases where you explicitly want third party code to access your components. For internal purposes, do not use <intent-filter>, and use explicit, component-based Intents instead. Note that you can still put an action string and stuff on an explicit Intent, so it is not like you are going to lose some of your “payload” options for delivering data with the Intent.
Lesson #2: Controlled Exports Should Require Signatures
In the Kies incident, Samsung published a BroadcastReceiver that supported a range of Intent actions unique to Samsung. Ideally, those would not exist.
But, let’s pretend for a moment that they really are needed, that some other app (e.g., another piece of the OS) needs to send those broadcasts for this receiver to pick up.
In that case, you should do what Samsung apparently eventually did: require a signature-level permission.
This is fairly straightforward to implement:
In both apps, have the same <permission> element, with the same permission name, plus android:protectionLevel="signature"
On the <receiver> element, add the android:permission attribute, naming this shared permission
There is no step #3
Instamagically, you lock down these broadcasts. Nobody else can spoof the receiver by sending fake broadcasts, because they will not be signed by the same signing key. And, as a bonus, for SDK apps, the install order does not matter, because they both define the same permission.
Lesson #3: Consider Self-Obfuscation
ProGuard does a yeoman’s job of obfuscating code. However, it will not obfuscate things like action strings on published <intent-filter> elements. Hence, in the Kies incident, it was pretty obvious that this this one BroadcastReceiver could perhaps install apps on demand, because it supported a com.intent.action.KIES_START_RESTORE_APK action. Had this been named com.samsung.action.UNICORNS, or even com.samsung.action.asdw3rioadjsf34e, it would have been less obvious that this component would have been a weak link.
Admittedly, this is “security through obscurity”, which is not really much of a defense. That being said, every speed bump you put between your app and somebody trying to break into it makes it that much less likely that somebody will want to scratch that particular itch.
Lesson #4: Public Stuff is Public
In the Kies incident, the app would, on request, grab everything from /sdcard/restore/ and install it.
This is stupid beyond words. Never blindly assume that stuff on external storage is intrinsically safe, particularly for something as important as finding APK files to install.
Miscellaneous Non-Security Lessons
Don’t name an Intent action something like com.intent.action.KIES_APP_START unless you actually own the intent.com domain name. Based on the whois data for that domain, I don’t think Samsung owns it.
If the blog post describing this incident is accurate in its portrayal of the source code, Samsung hard-coded /sdcard. Don’t do that. Use the proper Environment methods.
August 1, 2012
The Busy Coder's Guide to Android Development Version 4.0 Released
Subscribers now have access to the latest release of The Busy Coder’s Guide to Android Development, known as Version 4.0, in all formats.
This update includes:
Coverage of some new stuff in Jelly Bean, included expanded notifications and file transfer via Android Beam
New chapters on Lint and emulator tricks
A restored chapter on creating custom dialogs and preferences, lost as part of The Big Book Reboot
Lots and lots of errata fixes
If you encounter any problems with this update or your Warescription in general, please contact wares@commonsware.com, and be sure to provide your Warescription user ID.
July 30, 2012
External Storage Moved. Again.
For those of you still in the habit of hard-coding in root paths like /sdcard and /mnt/sdcard, please be advised that on Jelly Bean devices, external storage is now at /storage/sdcard0. Links (not sure if they are symlinks or hardlinks) are set up so the legacy paths still work.
Of course, using the methods on Environment are the preferred way of dealing with this in production apps. However, if you are scripting anything (e.g., automated adb push from your desktop), you will need to take this into account. Notably, adb push does not seem to follow the links, so you will need to actually change the adb push commands (and, presumably, adb pull as well).
On the bright side, the fact that there is actually a digit in the name suggests that in some future Android release (K?), multiple volumes of external storage will be officially supported by the SDK, much to the delight of developers who have been hacking workarounds for getting at alternate data stores (e.g., USB thumb drives).
July 26, 2012
Beware the 1% Solution
Some Android developers (e.g., Jake Wharton) are at the top 1% of their profession.
Some locations (e.g., Silicon Valley) are at the top 1% of infrastructure (e.g., Internet access, clean water).
Some Android apps (e.g., Square) are at the top 1% of complexity.
It is important to remember that 1%-ers make up approximately 1% of the Android ecosystem. For every Jake Wharton, there are 99 others with less skill, less talent, or less experience. And, for every Silcon Valley/Alley/whatever, there are 99 other areas with fewer available resources. And, for every Square app, there are 99 simpler apps out there — some in the Play Store, some private for individuals, businesses, etc.
I bring this up in light of Jake Wharton’s post regarding Android build systems and follow-up tweet.
On the surface, what the post requests seems like goodness and apple pie. After all, who wouldn’t want an empowering, enabling, dynamic build system?
My concern is less about what the post requests as much as what costs the solution might incur. A solution that benefits the 1% at the expense of the 99% is good for the 1% and bad for the Android developer ecosystem overall, IMHO.
I work with the 99% every day – teaching, answering questions, and so forth. One of the reasons I do this is to keep me grounded, so that I keep the 99% in mind in what I do. Sometimes I fail in that, as I am very, very human.
Android already is pushing the envelope on complexity for the 99%. People turn away from Android development because they cannot climb the hill of the resource system, or slide backwards when the SDK deprecates formerly suggested patterns, or get stuck when the tools look different than they did a month ago, or any number of other issues. Much of the 99% have little Java experience. Some have little programming experience.
Much of what the core Android tools team — Xav and Tor and the rest — have been focusing on over the past year or so have been to benefit everyone (e.g., faster emulators) or to benefit the 99% (e.g., drag-and-drop GUI building). In their own way, they have been trying to improve Android development inclusiveness, and therefore have not been radically changing the build system, at least in terms of what the 99% perceive.
This is not to say that improvements to the build system should not be made, whether by a wholly-independent effort or by people contributing to the tools on the AOSP. However, if the core build system for Android is to change, please keep in mind:
Requiring more visible dependencies for starting development (e.g., Gradle and Groovy) is like requiring more permissions in your Android app — some percentage of your user base will decline to install and will move on to something else.
Requiring more setup to build your project (e.g., mandatory version control) may lose other developers who lack experience with such technology and get overwhelmed with everything they need to learn to create “Hello, world!”
Requiring reliable Internet access will be a problem for developers in areas where reliable Internet is a dream more than a reality.
And so forth.
Again, this is not a knock on what Jake Wharton asked for. I have absolutely no problem with having a better build system. Instead, this is a warning for potential implementations: make sure that changes are at least neutral for the 99% while they benefit the 1%, or can operate in parallel to a solution that serves the 99%.
After all, once upon a time, 1%-ers were members of the 99%.
(except for folks like Romain Guy and Dianne Hackborn, who were 1%-ers on Android before there was an Android… :-)
July 25, 2012
Think About READ_EXTERNAL_STORAGE Now
Now that more Jelly Bean devices are cropping up, you really need to think about setting your build target to API Level 16 and adding the READ_EXTERNAL_STORAGE permission to your application if you read (and do not write) from external storage.
Users of Jelly Bean devices can go into Developer Options in the Settings app and check the “Protect USB storage” preference. In theory, only developers will do this. In practice, other people will do so. After all, who wouldn’t want to protect their USB storage? Besides, the confirmation dialog that appears when checking that preference says “some apps may not work until updated by their developers”, putting the blame on lazy developers.
Hence, even before this protection is enabled by default – perhaps as early as the “K” release – a tiny percentage of your users will check this checkbox, and a subset of them will blame you if your app does not work. You will see “EACCES (Permission denied)”-flavored FileNotFoundException entries in your logs when trying to access external storage.
If you hold WRITE_EXTERNAL_STORAGE, you do not need to also hold READ_EXTERNAL_STORAGE – write implies read, in this case.
However:
You do not appear to be grandfathered into this permission based on android:targetSdkVersion or android:minSdkVersion, as was the case when WRITE_EXTERNAL_STORAGE was added a long time ago. Hence, all apps reading (but not writing) external storage will need this permission.
The 4.1 emulator appears broken, insofar as it does not check the Developer Options preference, and therefore grants access to external storage even if you lack the permission. The R20.0.1 patch release to the tools does not seem to fix this. I have filed an issue about this problem.
The problem affects apps that might not realize that they are reading files from external storage, because they are being handed Uri values from an Intent or a ContentProvider query – using a ContentResolver to open a file:// Uri means that you need this permission, as was discussed in an android-developers thread.
Note, though, that the permission needs to be held by the process opening the file, not the one reading it. Hence, a ContentProvider that serves files from external storage via openFile(), and is accessed by consumers via content:// Uri values, will work fine if the ContentProvider has the permission – the consumer does not need it in that case. Hence, if you implement a ContentProvider that returns file:// Uri values, consider switching to returning content:// Uri values that point back to your ContentProvider and openFile(), as that will prevent your content from fouling up other apps that have not yet added this permission.
The biggest issue for some is that you have to switch to a build target of API Level 16 for the toolchain to recognize this permission. Fortunately, the improvements in Lint make it much easier to have your build target set higher than your android:minSdkVersion yet still catch any places where you might have messed up and used too-new APIs without a version guard block in place.


