Jan 14, 2013

Fixing the rotation - Instant Mustache #8

This article is part of a series of articles about the development process of Instant Mustache, a fun camera app that adds mustaches to all faces using face detection. Click here to get a chronological list of all published articles about Instant Mustache.

Wrong orientation

If you run the current version of Instant Mustache and take some pictures you'll notice something odd: The orientation of the taken pictures is sometimes wrong. This may depend on the device you are using. When using a Galaxy Nexus the picture will be rotated 90° to the left when taking a picture in portrait mode but will be rotated correctly when taking a picture in landscape mode.

Wrong orientation of photo that has been taken in portrait mode

How does this happen? You may remember that we've used Camera.setDisplayOrientation() in one of the previous articles to explicitly set the display rotation. First, this setting only affects the preview picture. The picture passed to the Camera.ShutterCallback isn't affected by this setting. And second, we still have to account into how the device is rotated in the moment of taking the picture.

Detecting and remembering the orientation

What we need to do in our code is to register an OrientationEventListener to get notified whenever the orientation changes. We'll remember this orientation and use this to rotate the taken image once the callback returns.

Whenever the orientation changes onOrientationChanged(int) of the listener will be called. The orientation will be passed to the method in degrees, ranging from 0 to 359. We need to normalize this value as we are only interested in 90° steps for rotating the picture.

CameraOrientationListener.normalize()
Another method called rememberOrientation() will be used to save the orientation of the device in the moment of the user pressing the shutter button.

CameraFragment.takePicture()

Rotating the picture

Now we just need to rotate the Bitmap. We do this by creating a new Bitmap object and applying a rotated Matrix to the pixels. The rotation angle is calculated by summing the remembered orientation, the display orientation and the natural rotation of the device.

CameraFragment.onPictureTaken()

Result

Photos rotated correctly in portrait and landscape mode

Dec 22, 2012

Android 2012


After Google Zeitgeist and YouTube looked back on 2012 it’s time to do the same for Android.

January

The year 2011 has just ended and the Galaxy Nexus is the current flagship phone. 12 days later on January 12th the Android Design website launched. Followed by the Android developers Google+ page on January 30th.

February

The first numbers of the year are published. 850,000 Android phones are activated every day. 300 million devices have been activated so far.

March

On March 5th expansion files are introduced and Android apps break the 50MB barrier expanding the size limit to 4GB. The Android market retires and is reborn as Google play on March 6th. The same month on March 21st the SDK tools and ADT revision 17 are released, adding an emulator that supports running x86 system images on Windows and Mac OS X. An update to the Android Developer Console on March 29th allows multiple users to manage published Android apps.

April

The emulator gets even more faster on April 9th by adding GPU support. On April 20th the first episode of Friday App Review airs and is later called The app clinic.

May

On May 4th Wolfram Rittmeyer publishes the first posting on his blog Grokking Android. Followed by the first article published on Android Zeitgeist on May 27th. 3 days before on May 24th In-app Subscriptions are launched on Google Play.

June

The Google I/O takes place for three days from June 27th to 29th. There are now 900,000 Android devices activated every day and 400 million devices have been activated up to now. Android 4.1 (Jelly Bean) is publicly shown for the first time on June 27th. The same day the Android 4.1 SDK is released. In addition to that the first tablet by Google is unveiled: The Nexus 7. On the second day of the Google I/O the Android SDK tools are updated to revision 20. At the end of the Google I/O there have been 3.5 million live streams seen from 170 countries.

July

On July 3rd the Ouya, an Android based console, is unveiled and a Kickstarter campaign is started on July 10th. On July 9th the Android 4.1 source code is published as part of the Android Open Source Project (AOSP).

August

The funding phase for the Ouya is completed. The campaign collected $8,596,475. That’s 904% more than the initial campaign goal.

September

New numbers are released. There are now 1.3 million devices activated every a day. About 70,000 of these devices are tablets. 480 million devices have been activated up to now. On September 9th the first episode of This week in Android development airs. A day later the first episode of Android Design in Action is uploaded to YouTube.

October

Till mid October 3 million Nexus 7 units have been sold. Starting from October 15th the new Google Play Developer Console is available to everyone. Google planned a launch event on October 29th in New York but it has been cancelled due to Hurricane Sandy. Nevertheless the Nexus 4 and Nexus 10 are introduced online this day. These are the first devices to run Android 4.2.

November

The first episode of (╯°□°)╯︵ ┻━┻ airs on November 8th. On November 13th the Nexus 4 and Nexus 10 went on sale and are sold out in minutes. Later that day the Android 4.2 SDK platform is released. Another day later the Android SDK tools revision 21 are released.

December

Google releases a new Google Maps API for Android on December 3rd. On December 10th a new version of the In-App billing API is released.

The Android team releases their Happy Holidays video:



2013?

What has been your Android highlight in 2012 and what are your wishes for 2013?

Dec 11, 2012

Mind the gap: String.isEmpty()

Articles labeled "Mind the gap" are short articles mostly about simple problems that arise from using different API levels of Android. They are more short trivia postings than big teachings about Android development.

I try to write code as readable as possible. That's the reason why I don't want to compare a String to an other empty String object or check its length when I want to know if a String is empty.


In the book "Clean code" by Robert C. Martin you can read Grady Booch saying: "Clean code reads like well-written prose". So I try to use String.isEmpty() for that reason. Internally it may do a length check as well (I stopped my investigation at the native keyword) but when reading the following snippet it is absolutely obvious what I intend to do.


Even though isEmpty() has been introduced in Java 1.6 it hasn't been available in Android until API level 9 (Android 2.3) so I accidentally caused some crashes on earlier versions of Android. Nowadays lint thankfully saves me from doing this error.

So what to do now? I don't know if someone at Google felt the same but there is a class in the Android framework that solves that problem: TextUtils. This class also has a lot of other helpful methods like join() to join an array of elements to a String using a delimiter or getReverse() to reverse a String (Take that interview question!).

In addition to that the TextUtils class has a method isEmpty() that is available since API level 1.


Phew! By the way: Internally isEmpty() checks if the length of the String is 0 (and does a null check).

Nov 5, 2012

Examining the ViewPager #3

This article is part of a series of articles about the ViewPager component. Click here to see a list of all articles of this series.

Horizontal scrolling pages

Have you ever tried putting horizontal scrolling components inside a ViewPager? Well, since revision 9 of the support library this is supported by the ViewPager. As long as the inner component can scroll horizontally this component will be scrolled. Whenever the component can't be further scrolled the ViewPager will handle the touch events and you start to switch to the next page. This works out-of-the-box for scrolling view components of Android like the WebView.

Internally the ViewPager uses ViewCompat.canScrollHorizontally(View v, int direction) to determine if a child view can be scrolled horizontally and should receive the according touch events. Unfortunately this method is only implemented for Android 4.0 (API level 14) and above. For all earlier versions this method will always return false and therefore never scroll the components inside the ViewPager.

Bezel swipe

Allowing horizontal scrolling components introduces a new problem: What if you want to switch pages but not scroll every component to its horizontal end? When you start the swipe at the phone's bezel (or actually from the edge of the ViewPager) you'll switch pages instead of scrolling the page's content. This gesture is called bezel swipe.

For reading more about the bezel swipe gesture from a UI point of view read "Bezel swipe, a Solution to Pan and Swipe Confusion?" on Android UI Patterns.

The good news is again: The ViewPager supports bezel swipe out of the box. But as you can't horizontally scroll inside pages on devices running an Android version lower than 4.0 (API level 14) bezel swipe isn't of any use on these as well.

The area to start a bezel swipe has a width of either 16dp or a 10th of the total width of the ViewPager depending on which one is smaller.


Fake dragging

The ViewPager supports fake dragging. Fake dragging can be used to simulate a dragging event/animation, e.g. for detecting drag events on a different component and delegating these to the ViewPager.

You have to signal the ViewPager when to start or end a fake drag by calling beginFakeDrag() and endFakeDrag() on it. After starting a fake drag you can use fakeDragBy(float) to drag the ViewPager by the given amount of pixels along the x axis (negative values to the left and positive values to the right).

The following example uses a SeekBar whose current progress state is used to fake drag a ViewPager by the given percentage.

SeekBarPagerListener.java

This video shows the fake drag in action:

Oct 30, 2012

Displaying the taken picture – Instant Mustache #7

This article is part of a series of articles about the development process of Instant Mustache, a fun camera app that adds mustaches to all faces using face detection. Click here to get a chronological list of all published articles about Instant Mustache.

Writing the PhotoActivity

In the last article we wrote the code to take a camera picture and save it on the external storage. After saving the file the activity will be finished and a Toast will show up. This is not really user-friendly so now we'll write our next activity which will display the taken picture and later offer the option to share this picture.

We'll start by creating an empty activity called PhotoActivity and add it to the manifest of our application. For now the layout will only contain an ImageView to display the picture:

activity_photo.xml

Instead of showing a toast in our CameraActivity we create an Intent to start the PhotoActivity and use setData(Uri) on the Intent object to pass a Uri pointing to the picture file:

onPictureTaken() - CameraActivity.java

In onCreate(Bundle) of the PhotoActivity we'll retrieve the Uri from the Intent and pass it to the ImageView. The ImageView will take care of loading the picture from the external storage and displaying it.

onCreate() - PhotoActivity.java

And that's already all the code we need for the first version of the PhotoActivity.


CameraActivity (left) and PhotoActivity (right)

Oct 26, 2012

Taking a picture – Instant Mustache #6

This article is part of a series of articles about the development process of Instant Mustache, a fun camera app that adds mustaches to all faces using face detection. Click here to get a chronological list of all published articles about Instant Mustache.

After writing the necessary code to display a camera preview in the last article it's now time to actually take a picture and save it on the external storage of the device.

We start by extending the layout of the CameraActivity to include a button for taking a picture. We assign a method to it to be called when the user clicks on the button via the attribute android:onClick. Another option would be to assign an OnClickListener to the view in code. Defining the method in the XML results in less code but has the disadvantage of not being checked by the compiler. Since one of the last releases of the Android SDK the lint tool is able to check the onClick attributes for correctness. So we will use the XML attribute here.

activity_camera.xml

As we encapsulated the code handling the Camera object inside the CameraFragment class the activity just calls takePicture() on the fragment when the user presses the button.

takePicutre() - CameraActivity.java

The fragment calls takePicture() on the Camera object. It's possible to pass up to four callbacks to this method: A shutter callback, a raw callback, a postview callback and a jpeg callback. According to the documentation their usage is:

  • The shutter callback occurs after the image is captured. This can be used to trigger a sound to let the user know that image has been captured.
  • The raw callback occurs when the raw image data is available.
  • The postview callback occurs when a scaled, fully processed postview image is available.
  • The jpeg callback occurs when the compressed image is available.

We are only interested in the JPEG image. The fragment itself is also the callback so we'll implement the Camera.PictureCallback interface.

Once the picture is taken onPictureTaken() will be called with a byte array. We decode the given byte array and create a Bitmap object and pass it via the CameraFragmentListener interface to our CameraActivity. Later we will use this bitmap to draw the mustaches on it.

onPictureTaken() - CameraFragment.java

Saving the picture to the external storage

Once the activity receives the bitmap object it needs to do a bunch of things:
  • Determine the directory to save the picture to and create it if necessary
  • Create a unique file for the picture inside the directory and save the image data to it
  • Notify the MediaScanner that we created a new file
  • Show a toast that the picture has been saved successfully (For now until we've written the activity to display the taken picture).

Determining the directory

We want to save the picture into a directory on the external storage that is visible for all other applications. By calling Environment.getExternalStoragePublicDirectory() and passing Environment.DIRECTORY_PICTURES we get the public directory for pictures (Available since API Level 8). Inside this directory we'll create a directory with the name of our application (if it doesn't exist already).

onPictureTaken() - CameraActivity.java

 Saving the file

We'll create a file with a unique file name containing a timestamp, e.g.: MUSTACHE_20121031_235959.jpg. The compress() method of the bitmap object is used to save the picture into the file.

onPictureTaken() - CameraActivity.java

Notifying the MediaScanner

Scanning the SD card for changes is costly. Therefore most Android versions only scan the whole card if the card is re-inserted or was mounted by another device. This seems to be different in different vendor versions of Android but nevertheless you can't assume a file to be seen by other applications (for example the gallery) until it has been scanned by the MediaScanner. For that reason we'll notify the MediaScanner about the file we've created.

onPictureTaken() - CameraActivity.java

By now we've already created a simple camera application. We can take pictures and they show up in the gallery of the device. Pretty cool so far, huh?

Oct 25, 2012

Examining the ViewPager #2

This article is part of a series of articles about the ViewPager component. Click here to see a list of all articles of this series.

Offscreen pages

The ViewPager doesn't create all its pages at once. When using a lot of pages this would be horribly slow and even unnecessary if the user would never swipe through all these pages. By default the ViewPager only creates the current page as well as the offscreen pages to the left and right of the current page.



If you only use a small amount of pages you may get a better performance by creating them all at once. You can use setOffscreenPageLimit(int limit) to set the number of pages that will be created and retained. Note that the limit applies to both sides of the current page. So if you set the offscreen page limit to 2 the ViewPager will retain 5 pages: The current page plus 2 pages to the left and right.

Responding to changing states

You can get notified whenever the displayed page changes or is incrementally scrolled. To listen to these state changes you can implement the OnPageChangeListener interface or extend the SimpleOnPageChangeListener class if you do not intent to override every method of the interfacce.

The following example listener updates the title of the activity according to the title of the currently selected page:

UpdateTitleListener.java

Margin between pages

When scrolling through pages they all look like glued together. You can use setPageMargin(int pixels) to define a margin between pages. The gap is filled with the background color of the ViewPager.

The following screenshots are showing the same ViewPager during switching pages. The left screenshot shows the ViewPager with no page margin set. In the right screenshot the margin has been set to 20 pixels. Notice that the method expects the margin to be defined in pixels. To use the same physical margin on all kind of screens independent from their pixel density define the margin in density independent pixels (dp) and convert them to the actual number of pixels for the current screen.


It's also possible to define a drawable that will be used to fill the margin between two pages using setPageMarginDrawable(int resId) or setPageMarginDrawable(Drawable d). The best approach is to use a Nine-patch that be scaled dynamically by the system to fill the space between the pages.

Switching pages programmatically

You can also switch between pages programmatically. Using setCurrentItem(int position, boolean smoothScroll) you can switch to the given position. If the second parameter is true a smooth animated transition is being performed. Using false the ViewPager will switch to the given page without any animation. If you always want to switch pages with an animation you can also leave the second parameter and use setCurrentItem(int position).