Mark L. Murphy's Blog, page 52

April 1, 2015

Maps Sans Play Services

One of the bigger reasons why developers depend upon Google Play
Services is for MapsV2, the native Android mapping engine. Lots of
developers are comfortable with just implementing MapsV2, on the grounds
that most Android devices are part of the Google Play ecosystem and
therefore can use MapsV2.



This, of course, makes manufacturers outside the Play ecosystem sad.



Some of those manufacturers have elected to provide their own mapping
solution. Amazon, for example, offers maps with an API that is very
similar to MapsV1 (the old API) and MapsV2. However, this still requires
developers to code to these distinct APIs, perhaps employing product
flavors in Android Studio and Gradle for Android to isolate the ecosystem-specific
code bits. After all, Amazon’s maps only work on Amazon hardware, so you
still need something that can work elsewhere.



One way to try to deal with this is to use a compatibility layer.
Airbnb went down this road, and last week they released
AirMapView based on their work.



AirMapView is designed to give you a map, regardless of the device that
you are running on. It tries MapsV2 and falls back to maps powered
by WebView and the Web edition of Google Maps. Airbnb indicates that
support for Amazon’s mapping engine is forthcoming. I hope that somebody
contributes an OpenStreetMap extension for it as well. And, they too offer
an API that is close to MapsV2. If it works as advertised, you can get
the best possible map for a given device, without constraining yourself
to only devices that offer MapsV2.



The 1.1.2 AAR for AirMapView clocks in at ~100KB, so it will not grow
your APK too much. AirMapView is definitely a project to keep
track of, and to evaluate when it makes sense for your team. Having
the ability to support non-Play devices may prove important to you in
the long run. Anything you can do to isolate the Play-specific code
and have options for replacing it would be good, all else being equal.

 •  0 comments  •  flag
Share on Twitter
Published on April 01, 2015 07:24

RecyclerView Webinars

I have added a pair of one-hour webinars on RecyclerView to
my roster of webinars.



“RecyclerView Basics”
is an introduction to RecyclerView and how to use it in lieu of a
ListView or GridView. The webinar will cover how to set up RecyclerView
adapters and view holders, how to respond to click events, and so on.



“RecyclerView, Beyond the Basics”
gets into more advanced topics, such as how to have more than one type
of item (e.g., headers and detail), how to support choice modes and
action modes, and how to modify the contents of a RecyclerView
(complete with basic animations).



Note that these, plus my 30-minute overview of
JUnit4 on Android,
will be held in a private BigBlueButton
meeting space, as I switch over to that and away from the
soon-to-be-defunct Dozeo.

 •  0 comments  •  flag
Share on Twitter
Published on April 01, 2015 07:24

March 23, 2015

AlarmManager Regression in Android 5.1

Manny Karyampudi pointed out to me
a problem with some of my book samples, that turned out to indicate
a regression in the behavior of AlarmManager on Android 5.1.



(book sample apps are useful canaries in the Android coal mine…)



Up through Android 5.0, the repeat interval for setRepeating()
and setInexactRepeating() could be fairly low. Some of my book samples,
like this one,
go as low as 5000ms, mostly so developers can see the results without
waiting too long. In the book itself, I point out that
frequent polling will be bad for CPU usage, battery life, etc.



In Android 4.4, they made setRepeating() inexact, but only if your
targetSdkVersion is 19 or higher. My samples have a targetSdkVersion
below this, once again so developers can see the results in an expected
fashion. Using inexact alarms, so AlarmManager events can be batched,
is very useful for battery life. However, for development purposes,
they are aggravating, in part because
we have no idea when the alarms will go off.



In Android 5.1, they appear to have set a limit, where
low polling periods are rounded up to 60000ms (one minute).
Repeat intervals higher than 60000ms are left alone. This shows up
in the adb shell dumpsys alarm output as well as in the actual
results when running the code. This affects setRepeating() at minimum,
and it appears to affect apps with any targetSdkVersion.



As a user, I don’t mind this, as it means that I should get better
battery life, in the face of apps that are too aggressive with their
use of AlarmManager. As a developer, I know that I should not be
using AlarmManager for anything rapid-fire like that, in part because
I should not be trying to do frequent work in the background, and
frequent work in the UI layer is better handled by
postDelayed() loops
instead.



However, also as a developer, I am once again dismayed to see that there
has been a fundamental change to a key piece of Android that went
unannounced and undocumented. I am hoping that Google will publish
something to the Android Developers Blog that explains what is going
on.



Many thanks to Mr. Karyampudi for pointing out this change in behavior!

 •  0 comments  •  flag
Share on Twitter
Published on March 23, 2015 20:25

March 12, 2015

Developer Trust, and the XCode Hack

As many of you may already be aware,
a report
was published early
Tuesday morning, indicating that the CIA has created a “whacked” version
of XCode — Apple’s IDE and development toolchain — that can
leak developer private information or inject malware into apps created
with the altered IDE.



While that particular report does not get into Android, I feel fairly
confident that Android developers are wide open for targeted attacks
via development tools and development processes. And these attacks may
not require CIA-level “dark arts”, but would be more within reach of
other nations or organized groups. Some of those attackers will be less
interested in affecting our apps and more interested in peering inside
our office networks.



We need to do a better job, overall, of making sure that developers can
trust the tools that they use. We need to trust that the tools were not
written with malicious intent in the first place. We need to trust
that what we download and use is really what was published by the tools’
authors, not some “whacked” version. And we need to trust that the various
services that we use, from ad networks to distribution channels, are not
having similar impacts.



Personally, I need to climb the learning curve on OpenPGP signing of
Maven artifacts, both to sign my CWAC libraries and to advise developers on
how they can be validating artifact signatures as part of the build process.
I am hoping that, in the coming days and weeks, the publishers of the
major tools and ecosystems that we as Android developers use will explain to us
what is being done to help prevent, or at least detect, XCode-style attacks.

 •  0 comments  •  flag
Share on Twitter
Published on March 12, 2015 21:15

March 11, 2015

Random Musings on the Android 5.1 SDK

With each Android SDK release, Google issues an API differences report,
outlining things that were added, changed, or removed in a new API
level compared to the previous one. Android 5.1 is no exception,
with its differences report
available for analysis.



I always review this to see what’s different beyond the sorts
of changes that get more disclosure.



Android 5.1 is tiny, in terms of updates. In fact, most of the API changes
stem from one thing: making HttpClient officially deprecated. If you
are still using this, consider switching to
OkHttp and their HttpClient
compatibility layer, or consider switching to
Apache’s separate Android edition of HttpClient.



Beyond that, here are a few other tidbits that I found interesting:





The official multi-SIM support will be welcome, as that is a popular line
of inquiry on Stack Overflow, given the prevalence of multi-SIM devices
in various parts of the world.




One of my issues
with Android itself was addressed, as
boolean support was moved from Bundle to BaseBundle,
making them available to PersistableBundle. Many thanks to whoever did that!




A new version of createChooser() on Intent
was added, providing a hook to find out what the user chose in the chooser.
This has been a popular question out on Stack Overflow.




Activity now has a getReferrer() method
that will return a Uri identifying what app or Web page started this activity.
Bear in mind that this may still return null, and the value can be spoofed,
so do not rely upon this, particularly for any sort of security stuff.




We have a couple of new Settings action strings,
to try to launch the battery-saver mode settings and the notification
listener settings.





As I said, Android 5.1 is pretty small overall. That being said, these are
generally welcome changes. And, I don’t have to race to push a book update
out the door, which is always nice. :-)

 •  0 comments  •  flag
Share on Twitter
Published on March 11, 2015 15:39

March 3, 2015

The Busy Coder's Guide to Android Development Version 6.5 Released

Subscribers now have
access to the latest release of The Busy Coder’s Guide to Android Development,
known as Version 6.5, in all formats. Just log into
your Warescription page and download
away, or set up an account and subscribe!



This update is dominated by a rather large chapter on RecyclerView.
It covers how to get back all of the stuff that RecyclerView lost
from ListView and GridView, plus some things that RecyclerView
can do that its predecessors cannot. Basically, it covers the
RecyclerView equivalents of pretty much everything the book has
on ListView and GridView, including how to support choice modes
(e.g., checklists, activated states) and action modes. It also
demonstrates CardView along the way.



In addition, there is a new chapter on Parcelable (what it is,
how to make your own classes Parcelable, and Parcelable
problems) and various other minor improvements.



The next update is tentatively scheduled for the second half of April.

 •  0 comments  •  flag
Share on Twitter
Published on March 03, 2015 07:23

February 11, 2015

Webinars Woster

(with suitable apologies to
Elmer J. Fudd
for the blog post title)



Here is the current set of webinars
that I am running the next couple of months:





Android Studio for Eclipse Users
is a quick tour of Android Studio, pointing out key differences between
how Eclipse worked and how Android Studio works.




What’s New with Notifications
covers some of the changes that arose in 2014 related to the use of
Notifications, such as customizing them for Android Wear and Android 5.0’s
new behaviors.




The Leanback BrowseFragment
is a short presentation on how to use the leanback-v17 library to create
a TV-centric UI, focusing on the BrowseFragment for a two-dimensional
nested-list UI for browsing categories of content.




Much Manifest Merging
will sift through the rules for how your Android app’s manifest is
generated from your Gradle build files, your main sourceset, your
sourcesets for build types and product flavors, and your third-party
library projects and AARs.





Subscribers
should visit the “Other Services > Webinars” link in
the Warescription site for 80%
discounts on these webinars.

 •  0 comments  •  flag
Share on Twitter
Published on February 11, 2015 20:46

February 6, 2015

Ripples from a Touch Point

Google has started to
point out to app developers
when they use ripple animations in Material Design-styled UIs, where those
ripples do not seem to emanate from the touch point that triggered
the animation.



Using Google’s search engine,
the only thing that I found that seemed
to explain how to make this work was
a comment on an issue on Lucas Rocha’s TwoWayView library.
Fortunately, that comment was enough for me to create something that
seems to work.



As Mr. Butcher hints in his tweet, the key appears to be
the setHotspot() method on Drawable.
Added in API Level 21, this teaches the drawable a “hot spot”, and
RippleDrawable apparently uses this as the emanation point for the
ripple effect. setHotspot() take a pair of float values, presumably
with an eye towards using setHotspot() inside of an OnTouchListener,
as the MotionEvent reports X/Y positions of the touch event with
float values.



This sample app
demonstrates the use of setHotspot() in the context of a RecyclerView,
as part of a 50+ page chapter that I am working on for the next book
update. This RecyclerView is using a LinearLayoutManager, to replicate
the basic structure of a classic ListView. My rows are based on a
CardView:



<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/an..."
xmlns:cardview="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
cardview:cardCornerRadius="4dp">

<LinearLayout
android:id="@+id/row_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground">

<!-- other widgets in here -->

</LinearLayout>

</LinearLayout>

</android.support.v7.widget.CardView>


The LinearLayout that the CardView wraps has
?android:attr/selectableItemBackground as its background, pulling
in a theme attribute. If this app runs on API Level 21, that background
will be a RippleDrawable.



When I set up the row, I attach an OnTouchListener to set the
hotspot, but only on API Level 21+ devices (as setHotspot() does
not exist before then):



if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
row.setOnTouchListener(new View.OnTouchListener() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onTouch(View v, MotionEvent event) {
v
.findViewById(R.id.row_content)
.getBackground()
.setHotspot(event.getX(), event.getY());

return(false);
}
});
}


So, if the user touches a row, I propagate the touch point to the
background as its hotspot. I specifically return false to indicate
that we are not consuming the touch event, so it will continue on to
the rest of the event handlers, if needed.



Without this setHotspot() call, the RippleDrawable seems to default
to the drawable’s center. So, in the case of a row for a list-style
RecyclerView, the ripple effect would emanate from the center of the
row. setHotspot(), tied to the touch event, changes the emanation point.



This seems to work. I have no idea if it is the official right answer,
insofar as I have not seen any code from Google for how to implement this
(e.g., a search of the SDK samples for setHotspot() turns up
nothing). In particular, calling setHotspot() directly on the background
worries me (should we be calling mutate() first?).
So, imagine a grain of salt, measuring approximately 15cm on
a side, and take that grain of salt with this implementation.

 •  0 comments  •  flag
Share on Twitter
Published on February 06, 2015 09:42

January 22, 2015

Webinars for Meetups

While I am still on
my presentation sabbatical,
there are still ways that I can help your meetup, GDG, or similar
collection of Android developers: webinars.



I hold webinars
several times a month, each usually lasting an hour. While there is a
nominal fee for attending, that fee is per connection, not per person.
A collection of people are welcome to attend via some shared connection
to the Dozeo web conference,
whether that collection represents a team at a firm in a conference
room or an independent group like a meetup in an, um, larger conference room. :-)



For groups, I can schedule additional webinars — on current or
past topics — at times that fit your group’s schedule. So, for
example, if your meetup meets on the third Tuesday of every month, we
can set up a webinar fitting that time slot. Times from 8am to 8pm (US
Eastern) are available.



If you are interested in scheduling a webinar as part of
an event for your group, contact me
and we can work out the details.

 •  0 comments  •  flag
Share on Twitter
Published on January 22, 2015 22:01

January 20, 2015

The Busy Coder's Guide to Android Development Version 6.4 Released

Subscribers now have
access to the latest release of The Busy Coder’s Guide to Android Development,
known as Version 6.4, in all formats. Just log into
your Warescription page and download
away, or set up an account and subscribe!



This update is focused on a bunch of TV-related stuff that I had worked
on prior to Android Studio 1.0 shipping, causing the TV stuff to get
put aside temporarily. The TV material includes:





Preliminary coverage of the leanback-v17 library, focused on
BrowseFragment for providing a two-dimensional, D-pad-friendly
navigation option for browsing a content catalog




A chapter on Android TV, converted from the Google TV chapter




Mention of the Fire TV Stick in the coverage of Fire TV




A new chapter reviewing a large sample app, Decktastic, which presents
conference-style presentations using an external display (HDMI, MHL,
Miracast, etc.), Chromecast (and other
RemotePlaybackDevice devices), and direct-to-TV devices (Android TV,
Fire TV, etc.)





This update also contains:





A new chapter on AndroidJUnitRunner and JUnit4 support. Espresso
support should be covered in some future edition of the book.




A revised VideoList sample in the chapter on the MediaStore,
switching out SmartImageView with the Universal Image Loader for
asynchronously loading video thumbnails.




A merged chapter on the basics of Gradle and the manifest, as having
those spread over two chapters was too confusing.




Updated material on publishing AARs, focusing now on the official
maven plugin.




Various bug fixes and such.





The next update is tentatively slated for mid-March 2015.

 •  0 comments  •  flag
Share on Twitter
Published on January 20, 2015 09:53