tag:blogger.com,1999:blog-15318295164270133332017-09-24T00:17:22.034+02:00Android ZeitgeistAndroid DevelopmentSebastian Kasparinoreply@blogger.comBlogger24125tag:blogger.com,1999:blog-1531829516427013333.post-12185607409081279642016-04-15T09:14:00.000+02:002016-04-15T09:15:08.160+02:00Lessons learned: Support for runtime permissions in Firefox for Android<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="https://medium.com/firefox-mobile-engineering/lessons-learned-adding-support-for-runtime-permissions-to-firefox-for-android-c30565288b81" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Medium.com: Lessons leard - Adding support for runtime permissions to Firefox for Android" border="0" height="640" src="https://3.bp.blogspot.com/-2doK63KYh1o/VxCUdxEIYtI/AAAAAAAAtzI/tlZ-uAo0QGArggye-HTUN7Y5revDLlMSACLcB/s640/medium_article.png" title="" width="531" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://medium.com/firefox-mobile-engineering/lessons-learned-adding-support-for-runtime-permissions-to-firefox-for-android-c30565288b81">This article has been published on Medium.com</a></td></tr></tbody></table><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/KCioBasbXCQ" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com0http://www.androidzeitgeist.com/2016/04/runtime-permissions-firefox-android.htmltag:blogger.com,1999:blog-1531829516427013333.post-44357026163017838692015-09-17T19:46:00.000+02:002015-09-17T19:46:08.239+02:00Support for restricted profiles in Firefox 42One of our goals for <a href="https://www.mozilla.org/en-US/firefox/android/">Firefox for Android</a> 42.0 was to create a kid and parent-friendly web experience (Project <i>KinderFox / KidFox</i>): The browser should be easy to use for a kid and at the same time parents want to be in control and decide what the kid can do with it.<br /><br />There are a lot of things you can do to create a kid-friendly browsing experience. In this first version we focused on making the browser simpler by hiding complex or kid-unfriendly features and utilizing the parental controls of the Android system: <i>Restricted profiles</i>.<br /><br /><h3>What are restricted profiles?</h3><br />Restricted Profiles have been introduced in Android 4.3. The device administrator can create these profiles and restrict access to apps and features on the device. In addition to that restrictions inside an app can be configured if supported by the app.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-2nsGZS3PMBQ/VeSXR42xZcI/AAAAAAAAluA/eXr4RUP6QQs/s1600/restricted-profiles-settings.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="http://4.bp.blogspot.com/-2nsGZS3PMBQ/VeSXR42xZcI/AAAAAAAAluA/eXr4RUP6QQs/s640/restricted-profiles-settings.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Configuring which apps the restricted profile can acccess.</td></tr></tbody></table><br />A unique feature of restricted profiles is that they share the Google account of the device owner. It does not allow full-access to everything connected to the account but it allows the restricted user to watch content (e.g. movies and music) bought with that account or use paid applications. Of course only if the device owner explicitly allowed this. <br /><br />Unfortunately Restricted Profiles are only supported on tablets so far. It is a pity because in the meantime Google allowed to create full-featured and guest profiles on phones too.<br /><br />The following DevBytes episodes gives a good overview about the Restricted Profiles APIs:<br /><br /><iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/pdUcANNm72o" width="560"></iframe> <br /><br /><br /><h3>Being in control</h3><br />Our final list of restrictable features for Firefox 42 contains 10 items:<br /><br /><ul></ul><ul><li><i>Disable add-on installation </i></li><li><i>Disable 'Import from Android' (Bookmark import) </i></li><li><i>Disable developer tools </i></li><li><i>Disable Home customization (<a href="https://hacks.mozilla.org/2014/07/building-firefox-hub-add-ons-for-firefox-for-android/">Home panels</a>) </i></li><li><i>Disable Private Browsing </i></li><li><i>Disable Location Services (Contributing to <a href="https://location.services.mozilla.com/">Mozilla's Location Service</a>) </i></li><li><i>Disable Display settings </i></li><li><i>Disable 'Clear browsing history' </i></li><li><i>Disable master password </i></li><li><i>Disable Guest Browsing </i></li></ul><ul></ul><br />Limiting access to features is a very personal decision. Not every parent wants to control every aspect of the browsing experience. That's why we decided to make these restrictions configurable by the parent. By implementing a broadcast receiver that listens to <a href="https://developer.android.com/reference/android/content/Intent.html#ACTION_GET_RESTRICTION_ENTRIES">ACTION_GET_RESTRICTION_ENTRIES</a> actions it's possible to send a list of restrictions to the system and so they will show up in the admin interface:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-9loC85f7nwM/VfrYo9ck7lI/AAAAAAAAmXo/ffox2Np3nZk/s1600/app-restrictions.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="http://1.bp.blogspot.com/-9loC85f7nwM/VfrYo9ck7lI/AAAAAAAAmXo/ffox2Np3nZk/s640/app-restrictions.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Configuring restrictions of an application.</td></tr></tbody></table>Later the application can query the <a href="http://developer.android.com/reference/android/os/UserManager.html">UserManager</a> to ask which restrictions have been enabled or disabled.<br /><br /><h3>Technical details</h3><br /><b>User restrictions vs. application restrictions</b><br />There are two kinds of restrictions. <a href="http://developer.android.com/reference/android/os/UserManager.html#getUserRestrictions%28%29">User restrictions</a> are imposed on the user by the system and <a href="http://developer.android.com/reference/android/os/UserManager.html#getApplicationRestrictions%28java.lang.String%29">application restrictions</a> are added by an application via the broadcast mechanism mentioned above. An application can query the <a href="http://developer.android.com/reference/android/os/UserManager.html">UserManager</a> only for its own and global user restrictions.<br /><br /><b>Detecting restricted profiles</b><br />One of the first things you might want to do in your app is to detect if the current user is using a restricted or a normal profile. There's no API method to do that and the video linked above suggests to query the user restrictions from the <a href="http://developer.android.com/reference/android/os/UserManager.html">UserManager</a> and if the returned bundle is not empty then you are in a restricted profile.<br /><br />This worked fine until we deployed the application to a phone running an Android M preview build. On this phone - that doesn't even support restricted profiles - the <a href="http://developer.android.com/reference/android/os/UserManager.html">UserManager</a> always returned a restriction. Whoops, suddenly everyone with Android M on their phones had a very limited <a href="https://nightly.mozilla.org/">Firefox Nightly</a>. We then switched to iterating over the returned Bundles (for application and user restrictions) and only assuming we are in a restricted profile if at least one restriction in those bundles is enabled (<i>getBoolean()</i> returns true).<br /><br />In general it is a better approach to never detect whether you are in a restricted profile or not but instead always check whether a specific (application) restriction is enabled or not.<b>&nbsp;</b><br /><br /><b>A resource qualifier would be nice</b><br />Hiding features and UI in restricted profiles will add a lot of <i>if</i> statements to the code base. It would have been nice to have a <a href="http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources">resource qualifier</a> for restricted profiles to load different layouts, drawables and other configurations. Besides that this would also solve the "detect restricted profile" problem quite elegant.<br /><br /><b>Strict mode: Disk read violation</b><br />At runtime we noticed that we have been triggering a lot of disk read violations. Looking at <a href="http://androidxref.com/5.1.1_r6/xref/frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java#readApplicationRestrictionsLocked">Android's source code</a> it turns out that <a href="http://developer.android.com/reference/android/os/UserManager.html#getApplicationRestrictions%28java.lang.String%29">UserManager.getApplicationRestrictions()</a> reads and parses an XML file on every call. Without caching anything like <a href="http://developer.android.com/reference/android/content/SharedPreferences.html">SharedPreferences</a> do. We worked around that by implementing our own memory cache and refreshing the list of restrictions whenever the application is resumed. To update restrictions the user will always have to switch to the admin profile and therefore leave (and later resume) the application.<br /><br /><b>No Fun</b><br /><div class="separator" style="clear: both; text-align: left;">Android Marshmallow (6.0) introduced a new <i>Easter egg</i> system restriction: <a href="http://developer.android.com/reference/android/os/UserManager.html#DISALLOW_FUN">UserManager.DISALLOW_FUN</a> - <i>Specifies if the user is not allowed to have fun. In some cases, the device owner may wish to prevent the user from experiencing amusement or joy while using the device. The default value is false</i>.&nbsp;</div><div class="separator" style="clear: both; text-align: left;"><br /></div><h3>What's next?</h3><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-8hdHsE2sf6Q/Vfr6lwJChxI/AAAAAAAAmZA/lSpXgbzrKsQ/s1600/kidbrowser.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="http://3.bp.blogspot.com/-8hdHsE2sf6Q/Vfr6lwJChxI/AAAAAAAAmZA/lSpXgbzrKsQ/s640/kidbrowser.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Simplified browser UI with custom theme using a restricted profile (Firefox 42).</td></tr></tbody></table><br />With the current set of restrictions parents can create a kid-friendly and simplified browsing experience. Of course there are a lot of more possible features around parental controls that come to mind like block lists and restricting Web APIs (Microphone, Webcam). Some of these ideas have already been filed (<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1125710">Bug 1125710</a>) and in addition to that we just started planning features for the next version (<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1205615">Bug 1205615</a>). More ideas are definitely welcome!<br /><br /><h3>Testing and feedback</h3><br />At the time of this writing support for restricted profiles is available in <a href="https://ftp.mozilla.org/pub/mozilla.org/mobile/nightly/latest-mozilla-aurora-android-api-11/">Aurora</a> (42.0) and <a href="https://nightly.mozilla.org/">Nightly</a> (43.0) builds of Firefox. Restricted Profiles serve a very specific use case and therefore do not get as much usage coverage like other browser features. If you do have a tablet and are interested in restricted profiles then help us testing it! :)<br /><br /><h3>Contributing </h3><br />Firefox for Android is open-source software and contributors are very welcome!&nbsp;You can find me on IRC (irc.mozilla.org) in #mobile (my nickname is "sebastian"), on <a href="https://twitter.com/Anti_Hype">Twitter</a> and <a href="http://plus.google.com/+SebastianKaspari">Google+</a>. <a href="https://wiki.mozilla.org/Mobile/Get_Involved">Get involved with Firefox for Android</a>.<br /><br /><h3>More resources about Project <i>KidFox</i></h3><ul></ul><br /><ul><li><a href="https://wiki.mozilla.org/Mobile/Projects/Kinderfox">Mozilla Wiki: Project KinderFox</a> </li><li><a href="https://wiki.mozilla.org/Mobile/Projects/Kid_browsing">Mozilla Wiki: KidFox proposal</a></li><li><a href="https://wiki.mozilla.org/Mobile/Briefs/Kidfox">Mozilla wiki: KidFox brief</a></li><li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1125710">Generic meta bug </a></li><li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1125984">Bugzilla meta bug for v1</a></li><li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1205615">Bugzilla meta bug for v2</a></li></ul><ul></ul><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/xaSicfGuwOU" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com0http://www.androidzeitgeist.com/2015/09/support-restricted-profiles-firefox.htmltag:blogger.com,1999:blog-1531829516427013333.post-18339368837510202122014-11-20T08:30:00.001+01:002014-11-20T08:32:09.334+01:00Introducing Android Network Intents<a href="https://github.com/pocmo/Android-Network-Intents">Android Network Intents</a> is a library that I wrote for <a href="http://landsofruin.com/">Lands of Ruin</a> - a game that two friends and I are developing. To avoid a complicated network setup to play the game against a friend, we needed a way to discover games running on the local network. Android offers a <a href="http://developer.android.com/training/connect-devices-wirelessly/nsd.html">Network Service Discovery (NSD)</a> since API level 16 (Android 4.1) but we kept running into problems using it. This lead to writing this library.<br /><br /><span style="font-size: large;"><b>What does the library do?</b></span><br />The library allows you to send <a href="http://developer.android.com/reference/android/content/Intent.html">Intents</a> to listening clients on the local network (WiFi) without knowing who these clients are. Sender and receiver do not need to connect to each other. Therefore the library can be used to write custom discovery protocols.<br /><br /><span style="font-size: large;"><b>Sending Intents (Transmitter)</b></span><br />An <i>Intent</i> is sent by using the&nbsp;<i>Transmitter</i>&nbsp;class. A&nbsp;<i>TransmitterException</i>&nbsp;is thrown in case of error.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-r6N5E7HFdJc/VGyPPqvem4I/AAAAAAAAbn8/aJ_XtPS7lkU/s1600/Screen%2BShot%2B2014-11-19%2Bat%2B13.33.29.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-r6N5E7HFdJc/VGyPPqvem4I/AAAAAAAAbn8/aJ_XtPS7lkU/s1600/Screen%2BShot%2B2014-11-19%2Bat%2B13.33.29.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Network-Intents/wiki/Sending-Intents">Sending an Intent</a></td></tr></tbody></table><br /><span style="font-size: large;"><b>Receiving Intents (Receiver)</b></span><br />Intents are received using the&nbsp;<i>Discovery</i>&nbsp;class. Once started by calling&nbsp;<i>enable()</i>&nbsp;the&nbsp;<i>Discovery</i>&nbsp;class will spawn a background thread that will wait for incoming&nbsp;<i>Intent</i>&nbsp;objects. A&nbsp;<i>DiscoveryListener</i>&nbsp;instance will be notified about every incoming&nbsp;<i>Intent</i>.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-fNRqCrKBhbg/VGyPnT00VdI/AAAAAAAAboE/XnejMaKLuyw/s1600/Screen%2BShot%2B2014-11-19%2Bat%2B13.37.46.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-fNRqCrKBhbg/VGyPnT00VdI/AAAAAAAAboE/XnejMaKLuyw/s1600/Screen%2BShot%2B2014-11-19%2Bat%2B13.37.46.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Network-Intents/wiki/Receiving-Intents">Writing a DiscoveryListener to receive events.</a></td></tr></tbody></table><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-_GOcgSh-4qY/VGyPpC2sA-I/AAAAAAAAboM/TVMtxnj-zKk/s1600/Screen%2BShot%2B2014-11-19%2Bat%2B13.37.55.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-_GOcgSh-4qY/VGyPpC2sA-I/AAAAAAAAboM/TVMtxnj-zKk/s1600/Screen%2BShot%2B2014-11-19%2Bat%2B13.37.55.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Network-Intents/wiki/Receiving-Intents">Starting and stoping the discovery.</a></td></tr></tbody></table><br /><span style="font-size: large;"><b>Things you should know</b></span><br />The Intents are sent as <a href="http://en.wikipedia.org/wiki/Multicast">UDP multicast</a> packets. Unlike TCP the UDP protocol does not guarantee that a sent packet will be received and there is no confirmation or retry mechanism. Even though losing a packet only happens rarely in a stable WiFi, the library is not intended for using as a stable <i>communication</i> protocol. Instead you can use it to find other clients (by sending an Intent in a periodic interval) and then establish a stable TCP connection for communication.<br /><br />On GitHub you can find a <a href="https://github.com/pocmo/Android-Network-Intents/tree/master/samples">chat sample application</a> using the library. While this is a convenient example, it is not a good use of the library for the reasons state above. You obviously do not want to lose chat messages.<br /><br />We are using the library for almost two years in Lands of Ruin and didn't observe any problems. However the game only runs on tablets so far. In theory the library should run on all Android versions back to API level 3 (Android 1.5) but this has obviously never been tested.<br /><br />You can find <a href="https://github.com/pocmo/Android-Network-Intents">Android Network Intents on GitHub</a>.<img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/frg0Ba2z4H0" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com0http://www.androidzeitgeist.com/2014/11/introducing-android-network-intents17.htmltag:blogger.com,1999:blog-1531829516427013333.post-86540312464992561122013-12-24T15:24:00.001+01:002013-12-24T15:24:38.598+01:00Hello World Immersion - Developing for Google Glass #2This article describes how to create a simple hello world application for Google Glass using the&nbsp;Glass Development Kit (GDK). As described in the <a href="http://www.androidzeitgeist.com/2013/12/mirror-api-gdk-developing-google-glass.html">previous article</a> you have two options how your Glassware should show up on the device: As a <i>live card</i> that is part of the timeline or as an <i>immersion</i>&nbsp;that is displayed outside of the context of the timeline. This article focuses on how to write an immersion.<br /><b><span style="font-size: large;"><br /></span></b><b><span style="font-size: large;">What is an immersion?</span></b><br />An immersion is basically an Android activity. The name immersion implies that it is not part of the normal Glass timeline. Instead it takes full control of the device - except for the back gesture (Swipe down). To go back to the timeline you need to leave the immersion.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-75TfF3lNoWs/UrhgTb9OPZI/AAAAAAAASH4/wESuu-yTUy4/s1600/glass_timeline_immersion.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-75TfF3lNoWs/UrhgTb9OPZI/AAAAAAAASH4/wESuu-yTUy4/s1600/glass_timeline_immersion.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Once started an immersion takes full control of the screen.</td></tr></tbody></table><br /><b><span style="font-size: large;">Project setup</span></b><br />Create a normal Android project with the following settings:<br /><br /><ul><li>Set <i>minSdkVersion</i> and <i>targetSdkVersion</i> to 15 (Android 4.0.3)</li><li>Set <i>compileSdkVersion</i> to&nbsp;<i>"Google Inc.:Glass Development Kit Sneak Peek:15"</i></li><li>Do not assign a theme to your application or derive your own theme from&nbsp;<i>Theme.DeviceDefault</i></li></ul><div><i><br /></i></div><span style="font-size: large;"><b>Creating the immersion</b></span><br />Let's create a simple activity. The <a href="https://developers.google.com/glass/develop/gdk/reference/com/google/android/glass/app/Card">Card</a> class helps us to create a layout that looks like a timeline card.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-Fj2t5BJi7yk/Urlo4Z4_FYI/AAAAAAAASI0/4qv2fF4ElX8/s1600/helloworldactivity.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-Fj2t5BJi7yk/Urlo4Z4_FYI/AAAAAAAASI0/4qv2fF4ElX8/s1600/helloworldactivity.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/Glass/HelloWorldImmersion/HelloWorld/src/main/java/de/androidzeitgeist/glass/helloworld/immersion/HelloWorldActivity.java">HelloWorldActivity.java</a></td></tr></tbody></table><br /><b><span style="font-size: large;">Launching the Glassware - Voice commands</span></b><br />After creating the activity we need a way to start our Glassware. A common way to launch Glassware is to use a voice trigger. Let's add a simple voice trigger to start our <i>hello world</i> activity.<br /><br />First we need to declare a string resource for our voice command.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-MnJseZKHYxY/Url83ImZEZI/AAAAAAAASJc/Lrxu_J2gZ8w/s1600/resource_hello_world.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-MnJseZKHYxY/Url83ImZEZI/AAAAAAAASJc/Lrxu_J2gZ8w/s1600/resource_hello_world.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/Glass/HelloWorldImmersion/HelloWorld/src/main/res/values/strings.xml">strings.xml</a></td></tr></tbody></table><br />The next step is to create an XML resource file for the voice trigger using the previously created string value.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-8y21kaJD3fI/Url839921yI/AAAAAAAASJw/HUZ4Po3-lr4/s1600/trigger_hello_world.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-8y21kaJD3fI/Url839921yI/AAAAAAAASJw/HUZ4Po3-lr4/s1600/trigger_hello_world.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/Glass/HelloWorldImmersion/HelloWorld/src/main/res/xml/voice_trigger.xml">voice_trigger.xml</a></td></tr></tbody></table><br />Now we can add an intent filter for the VOICE_TRIGGER action to our activity. A meta-data tag links it to the XML file we wrote above.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-GfHAXMZTDas/Url83illC8I/AAAAAAAASJs/oBmo7h4_Tuk/s1600/hello_world_activity.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-GfHAXMZTDas/Url83illC8I/AAAAAAAASJs/oBmo7h4_Tuk/s1600/hello_world_activity.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/Glass/HelloWorldImmersion/HelloWorld/src/main/AndroidManifest.xml">AndroidManifest.xml</a></td></tr></tbody></table><br />The developer guide requires you to add an icon for the touch menu to the activity (white in color on transparent background, 50x50 pixels). The&nbsp;<a href="http://glass-asset-utils.appspot.com/icons-submission.html">Glass Asset Studio</a>&nbsp;is a helpful tool to generate these icons.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-MWCSEYXWpUQ/UrmYIdPJp6I/AAAAAAAASKE/h2c7egiWuNs/s1600/50x50_icon.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-MWCSEYXWpUQ/UrmYIdPJp6I/AAAAAAAASKE/h2c7egiWuNs/s1600/50x50_icon.png" /></a></div><br /><span style="font-size: large;"><b>The final Glassware</b></span><br />Now we can start our Glassware by saying "<i>ok glass, show hello world</i>":<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-R3N3nwLXIIg/UrlsMbHPgkI/AAAAAAAASJA/9P6foH55OUc/s1600/hello_world_glassware.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="340" src="http://4.bp.blogspot.com/-R3N3nwLXIIg/UrlsMbHPgkI/AAAAAAAASJA/9P6foH55OUc/s400/hello_world_glassware.png" width="400" /></a></div><br />Another option to start our Glassware is to use the touch menu and scroll to the "<i>show hello world</i>" command:<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-5KjJBjb5qn4/UrlupI3MTuI/AAAAAAAASJM/j6-nNCKgWJM/s1600/hello_world_glassware_menu.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="340" src="http://2.bp.blogspot.com/-5KjJBjb5qn4/UrlupI3MTuI/AAAAAAAASJM/j6-nNCKgWJM/s400/hello_world_glassware_menu.png" width="400" /></a></div><br />The source code for this <a href="https://github.com/pocmo/Android-Zeitgeist-Samples/tree/master/Glass/HelloWorldImmersion">Hello World Glassware is available on GitHub</a>.<img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/fr-WO1v1SIU" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com0http://www.androidzeitgeist.com/2013/12/google-glass-immersion-hello-world.htmltag:blogger.com,1999:blog-1531829516427013333.post-56055365831116637352013-12-22T13:03:00.001+01:002013-12-22T13:03:25.308+01:00Android 2013<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-swoL-7rAksk/UrbIgrRXS0I/AAAAAAAASEk/FuB-KSnuxuU/s1600/android_2013.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-swoL-7rAksk/UrbIgrRXS0I/AAAAAAAASEk/FuB-KSnuxuU/s1600/android_2013.png" /></a></div><br />It's the end of the year - <a href="http://www.youtube.com/watch?v=H7jtC8vjXw8">YouTube</a> and <a href="http://www.youtube.com/watch?v=Lv-sY_z8MNs">Google Zeitgeist</a> have posted their reviews. Let's have a look on what happened in the Android world in 2013.<br /><br /><br /><a href="http://4.bp.blogspot.com/-S2ecXdl66_I/UrbQgaKZPxI/AAAAAAAASFE/qChEPdoly-w/s1600/nexus4.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="200" src="http://4.bp.blogspot.com/-S2ecXdl66_I/UrbQgaKZPxI/AAAAAAAASFE/qChEPdoly-w/s200/nexus4.png" width="200" /></a><span style="font-size: large;"><b>January</b></span><br />2012 is over and the Nexus 4 is the current flagship phone made by Google and LG.<br /><br /><span style="font-size: large;"><b>February</b></span><br /><a href="http://android-developers.blogspot.de/2013/02/google-sign-in-now-part-of-google-play.html">Google+ Sign-In is integrated</a> into the Google Play Services and Google starts accepting <a href="http://www.nytimes.com/2013/02/21/technology/google-looks-to-make-its-computer-glasses-stylish.html?_r=0">applications for the Google Glass Explorer program</a>.<br /><span style="font-size: large;"><b><br /></b></span><span style="font-size: large;"><b>March</b></span><br />The new <a href="http://android-developers.blogspot.de/2013/03/now-is-time-to-switch-to-new-google.html">Android developer console is out of preview</a>. While <a href="http://officialandroid.blogspot.de/2013/03/celebrating-google-plays-first-birthday.html">Google Play celebrates it first birthday</a>, the <a href="http://techcrunch.com/2013/07/01/android-led-by-samsung-continues-to-storm-the-smartphone-market-pushing-a-global-70-market-share/?ncid=tcdaily">market share of Android hits 64%</a>.<br /><br /><span style="font-size: large;"><b>April</b></span><br />The <a href="http://android-developers.blogspot.de/2013/04/update-on-tablet-app-guidelines-and.html">tablet guidelines are updated</a> and the Android developer console starts to <a href="http://android-developers.blogspot.de/2013_04_01_archive.html">show tablet optimization tips</a>. Google pushes a <a href="http://android-developers.blogspot.de/2013/04/new-look-new-purchase-flow-in-google.html">Google Play app update</a> that features a redesigned UI. Samsung releases it new flaship phone - <a href="http://en.wikipedia.org/wiki/Samsung_Galaxy_S4">the Samsung Galaxy S4</a>.<br /><span style="font-size: large;"><b><br /></b></span><span style="font-size: large;"><b>May</b></span><br /><a href="http://3.bp.blogspot.com/-wZeaMyx7VUs/UrbPAzv69mI/AAAAAAAASE4/-VEbnrV9MF4/s1600/io2013.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://3.bp.blogspot.com/-wZeaMyx7VUs/UrbPAzv69mI/AAAAAAAASE4/-VEbnrV9MF4/s1600/io2013.png" /></a>The <a href="https://developers.google.com/events/io/">Google I/O</a>&nbsp;takes place for three days from May 15th to 17th. This time there won't be a new Android release. Instead Google releases <a href="http://android-developers.blogspot.de/2013/05/social-gaming-location-and-more-in.html">new game services and a new location API</a>. At the Google I/O a new IDE for Android development is introduced: <a href="http://android-developers.blogspot.de/2013/05/android-studio-ide-built-for-android.html">Android Studio</a>. Since then every couple of weeks a new Android Studio update is pushed to the developer community.<br /><br /><br /><span style="font-size: large;"><b><br /></b></span> <span style="font-size: large;"><b>July</b></span><br />A new flavor of Android Jelly Bean is released: <a href="http://android-developers.blogspot.de/2013/07/android-43-and-updated-developer-tools.html">Android 4.3</a>. Open GL ES 3.0 and support for&nbsp;low-power Bluetooth Smart devices are some of the new features. Furthermore a new version of the Nexus 7 is released. Together with the new tablet Google <a href="http://officialandroid.blogspot.de/2013/07/from-tvs-to-tablets-everything-you-love.html">releases the Chromecast dongle</a> and the <a href="http://googledevelopers.blogspot.de/2013/07/cast-content-from-your-apps-to-tv-with.html">Google Cast SDK preview</a>.<br /><span style="font-size: large;"><b><br /></b></span><span style="font-size: large;"><b>August</b></span><br />Google releases <a href="http://android-developers.blogspot.de/2013/08/google-play-services-32.html">version 3.2 of the Google Play Services</a>. The update&nbsp;includes several enhancements to the Location Based Services. With the r18 release of the support library Google&nbsp;released a new backward-compatible <a href="http://android-developers.blogspot.de/2013/08/actionbarcompat-and-io-2013-app-source.html">Action Bar implementation called ActionBarCompat</a>. Motorola is releasing the <a href="http://en.wikipedia.org/wiki/Moto_X">Moto X</a> - its first phone since the company has been acquired by Google. The same month <a href="https://plus.google.com/u/0/+HugoBarra/posts/BzZMqRht1xQ">Hugo Barra announces to leave Google</a> after 5½ years to join the Xiaomi team in China.<br /><span style="font-size: large;"><b><br /></b></span><span style="font-size: large;"><b>September</b></span><br /><a href="http://android-developers.blogspot.de/2013/09/renderscript-in-android-support-library.html">RenderScript is now part of the support library</a> and can be used&nbsp;on plaform versions all the way back to Android 2.2 (Froyo). Jean-Baptiste Queru, who worked on the Android Open Source Project at Google, <a href="http://www.androidpolice.com/2013/09/17/jean-baptiste-queru-now-at-yahoo-as-senior-principal-engineer-working-on-mobile-apps/">starts a new job at Yahoo</a>. Google launches the <a href="http://officialandroid.blogspot.de/2013/08/find-your-lost-phone-with-android.html">Android device manager website</a> to&nbsp;locate, lock and ring misplaced devices.<br /><br /> <a href="http://1.bp.blogspot.com/-qksojJjcafk/UrbQ_JNFI_I/AAAAAAAASFM/mOaNvKPz2d0/s1600/nexus5.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="200" src="http://1.bp.blogspot.com/-qksojJjcafk/UrbQ_JNFI_I/AAAAAAAASFM/mOaNvKPz2d0/s200/nexus5.png" width="200" /></a><span style="font-size: large;"><b>October</b></span><br />After a lot of leaks and rumors <a href="https://www.google.com/nexus/5/">a new Nexus phone is released</a> on Halloween. Together with the Nexus 5 a new Android version - <a href="http://www.android.com/kitkat/">Android 4.4 KitKat</a> - is published.&nbsp;Full-screen immersive mode, a new transitions framework, a printing framework and a storage access framework are some of the many new features. In addition to that he <a href="http://android-developers.blogspot.de/2013/10/google-play-services-40.html">Google Play Services are updated to version 4.0</a>. With <a href="https://plus.google.com/+RomainGuy/posts/faCzPs6GKtg">Romain Guy another popular Android team member is leaving</a> - but remaining at Google.<br /><span style="font-size: large;"><b><br /></b></span><span style="font-size: large;"><b>November</b></span><br />The <a href="http://android-developers.blogspot.de/2013/11/app-translation-service-now-available.html">App Translation Service</a>, announced at Google I/O, is now available for every developer. Motorola releases a second phone - the <a href="http://en.wikipedia.org/wiki/Moto_G">Moto G</a>. Android hits a new record with <a href="http://www.highlightpress.com/android-tops-80-global-smartphone-market-share-windows-phone-up-156-year-on-year/6708/tharper">80% market share</a>. The Google Glass team releases a first sneak peek version of the <a href="https://developers.google.com/glass/develop/gdk/">Glass development kit (GDK)</a>.<br /><span style="font-size: large;"><b><br /></b></span><span style="font-size: large;"><b>December</b></span><br />Two small updates for Android KitKat are released: <a href="http://en.wikipedia.org/wiki/Android_version_history#Android_4.4_KitKat_.28API_level_19.29">Android 4.4.1 and 4.4.2</a>. The Android device manager <a href="http://techcrunch.com/2013/12/11/google-android-device-manager-play-store/">is now available as an app</a>.<br /><br />The <i>Android Design in Action</i> team releases its 2013 Recap:<br /><br /><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/ajO2zFEtEYs" width="560"></iframe></div><br /><br /><b><span style="font-size: large;">2014?</span></b><br />What has been your Android highlight in 2013 and what are your wishes for 2014?<img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/f-NE_F-73GY" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com0http://www.androidzeitgeist.com/2013/12/android-2013.htmltag:blogger.com,1999:blog-1531829516427013333.post-31392841734804830992013-12-16T21:21:00.004+01:002013-12-16T21:21:56.392+01:00Mirror API and GDK - Developing for Google Glass #1I recently got my hands on<a href="http://www.google.com/glass/start/"> Google Glass</a> and decided to write some articles about developing applications for Glass. After all it's Android that is running on Glass.<br /><br /><span style="font-size: large;"><b>What is Glass?</b></span><br />It's very complicated to explain Google Glass just using text. Only wearing and using it will give you this aha moment. However the following video, made by Google, gives you a good impression about how it feels like.<br /><br /><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/v1uyQZNg2vE" width="560"></iframe></div><br /><span style="font-size: large;"><b>What is Glass from a developer's point of view?</b></span><br />Google Glass is an Android device running Android 4.0.3. What you see through Glass is basically a customized <i>Launcher</i> / <i>Home screen</i> application (a timeline of cards about current and past events) and a slightly different theme. This makes it really interesting for Android developers to develop for Glass: You can use almost all the familiar Android framework APIs. However wearing Glass feels totally different than using a mobile phone. So there's a big difference in designing applications. But not only the UI is different: You can't just port an existing application to Glass. Use cases have to be designed especially for Glass. Some features of your app might not make sense on Glass. Some other interesting features might only be possible on Glass. It's almost impossible to get a feeling for that without using Glass for some days.<br /><div><br /></div><div>Back to writing code.. Currently we can decide between two ways to develop for Glass: The Mirror API or an early preview of the Glass Development Kit (GDK). Let's have a look at both and see what they are capable of.</div><div><br /></div><div><b><span style="font-size: large;">The Mirror API</span></b></div><div><div>The Mirror API has been the first API that has been introduced by the Glass team. It's a server-side API meaning the applications don't run on Glass itself but on your server and it's your server that interacts with Glass.</div><div><br /></div><div>The Mirror API is great for pushing cards to the timeline of Glass and sharing content from Glass with your server application.</div><div><br /></div><div>Some examples of applications that could use the Mirror API:</div></div><div><ul><li><b>Twitter client</b>: The server pushes interesting tweets to the timeline of the Glass owner. The user can share photos and messages with the application and they will be posted to the Twitter timeline.</li><li><b>Context-aware notifications</b>: Your server subscribes to the user's location. Every now and then your server will receive the latest user location. You use this location to post interesting and related cards to the timeline of the user.</li></ul><div><b><br /></b><b>More about the Mirror API</b>:</div></div><div><ul><li><a href="https://developers.google.com/glass/develop/mirror/quickstart/">Google Developers: Mirror API Quick Start</a></li><li><a href="http://www.youtube.com/watch?v=CxB1DuwGRqk">YouTube: Google I/O 2013 - Building Glass Services with the Google Mirror API</a></li></ul><div><span style="font-size: large;"><b><br /></b></span><span style="font-size: large;"><b>The Glass Development Kit (GDK)</b></span></div></div><div>With the GDK you can build Android applications that run directly on Glass. Think of the GDK as Android 4.0.3 SDK with some extra APIs for Google Glass. It's worth mentioning that the GDK is currently in an early preview state. The API is not complete and some important parts are missing.</div><div><br /></div><div>When developing Glass you have two options how your application should show up on Glass:</div><div><br /></div><div><b>Live Cards</b><br /><b><br /></b><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-wpH3qxEjT_Y/Uq9XLpCVIJI/AAAAAAAAR4A/my3NMMsA1SQ/s1600/glass_timeline_livecard.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-wpH3qxEjT_Y/Uq9XLpCVIJI/AAAAAAAAR4A/my3NMMsA1SQ/s1600/glass_timeline_livecard.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">How a live card shows up in the Glass timeline.</td></tr></tbody></table><br />Your application shows up as a card in the timeline (left of the Glass clock). You have again two options how to render these cards:</div><div><ul><li><b>Low-Frequency Rendering</b>: Your card is rendered using <a href="https://developer.android.com/reference/android/widget/RemoteViews.html">Remote Views</a>. Think of it as a Home screen widget on Android phones. A background service is responsible for updating these views. You only update the views every now and then.</li><li><b>High Frequency Rendering</b>: Your background service renders directly on the live card's surface. You can draw anything and are not limited to Android views. Furthermore you can update the card many times a second.</li></ul><div><b>Immersion</b><br /><b><br /></b><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-9Wv_J7QV5AM/Uq9XdQLAvuI/AAAAAAAAR4I/AwZ0p8RzYPM/s1600/glass_timeline_immersion.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-9Wv_J7QV5AM/Uq9XdQLAvuI/AAAAAAAAR4I/AwZ0p8RzYPM/s1600/glass_timeline_immersion.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">An Immersion is not part of the timeline but "replaces" it.</td></tr></tbody></table><b><br /></b><b><br /></b></div></div><div><div>An immersion is at the bottom a regular Android activity. For your activity to look like a timeline card:</div></div><div><ul><li>Don't assign a theme to your activity or use the DeviceDefault theme as base for your customization.</li><li>Even though you can use the touch pad of Glass almost like a d-pad: Try to avoid most input-related Android widgets. They don't make much sense on Glass because you are not using a touch screen. Instead try to use gestures with the <a href="https://developers.google.com/glass/develop/gdk/reference/com/google/android/glass/touchpad/GestureDetector">GestureDetector</a>&nbsp;class or <a href="https://developers.google.com/glass/develop/gdk/input/voice">voice input</a>.</li><li>Use the <a href="https://developers.google.com/glass/develop/gdk/reference/com/google/android/glass/app/Card">Card class</a> and its <a href="https://developers.google.com/glass/develop/gdk/reference/com/google/android/glass/app/Card#toView()">toView()</a> method to create a view that looks like regular Glass card.</li></ul><div><b><br /></b><b>More about the GDK</b></div></div><div><ul><li><a href="https://developers.google.com/glass/develop/gdk/quick-start">Google Developers: GDK Quick Start</a></li><li><a href="https://www.youtube.com/watch?v=oZSLKtpgQkc">YouTube: Glass Development Kit Sneak Peek</a></li></ul></div><div><br /></div><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/JXGHhmdRusw" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com0http://www.androidzeitgeist.com/2013/12/mirror-api-gdk-developing-google-glass.htmltag:blogger.com,1999:blog-1531829516427013333.post-30961758642896545122013-08-22T12:43:00.002+02:002013-08-22T12:43:53.597+02:00Read the code: IntentService<span style="font-size: x-small;">In the new category <b>Read the code</b> I’m going to show the internals of the Android framework. Reading the code of the framework can give you a good impression about what’s going on under the hood. In addition to that knowing how the framework developers solved common problems can help you to find the best solutions when facing problems in your own app code.</span><br /><br /><span style="font-size: large;"><b>What is the IntentService class good for?</b></span><br />This article is about the <a href="https://developer.android.com/reference/android/app/IntentService.html">IntentService</a> class of Android. Extending the IntentService class is the best solution for implementing a background service that is going to process something in a queue-like fashion. You can pass data via <a href="https://developer.android.com/reference/android/content/Intent.html">Intents</a> to the IntentService and it will take care of queuing and processing the Intents on a worker thread one at a time. When writing your IntentService implementation you are required to override the <a href="https://developer.android.com/reference/android/app/IntentService.html#onHandleIntent(android.content.Intent)">onHandleIntent()</a> method to process the data of the supplied Intents.<br /><br />Let’s take a look at a simple example: This DownloadService class receives Uris to download data from. It will download only one thing at a time with the other requests waiting in a queue.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-VJ13VmPcpx4/UhXgpEhsitI/AAAAAAAANSk/ZLzJYR-7ypE/s1600/DownloadService.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-VJ13VmPcpx4/UhXgpEhsitI/AAAAAAAANSk/ZLzJYR-7ypE/s1600/DownloadService.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/IntentService/DownloadService.java">DownloadService</a></td></tr></tbody></table><br /><br /><b><span style="font-size: large;">The components</span></b><br />Before we dip into the source code of the IntentService class, let's first take a look at the different components that we need to know in order to understand the source code.<br /><br /><b>Handler</b>&nbsp;(<a href="https://developer.android.com/reference/android/os/Handler.html">documentation</a>) (<a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/os/Handler.java">source code</a>)<br />You may already have used Handler objects. When a Handler is created on the UI thread, messages can be posted to it and these messages will be processed on the UI thread.<br /><br /><b>ServiceHandler</b>&nbsp;(<a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#58">source code</a>)<br />The ServiceHandler inner-class is a helper class extending the Handler class to delegate the Intent wrapped inside a Message object to the IntentService for processing.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/--OVWzXz_dlQ/UhXZNL7nZII/AAAAAAAANR0/3pWwyp_Iw3g/s1600/ServiceHandler.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/--OVWzXz_dlQ/UhXZNL7nZII/AAAAAAAANR0/3pWwyp_Iw3g/s1600/ServiceHandler.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#58">ServiceHandler inner class of&nbsp;android.app.IntentService</a></td></tr></tbody></table><br /><b>Looper</b>&nbsp;(<a href="https://developer.android.com/reference/android/os/Looper.html">documentation</a>) (<a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/os/Looper.java#Looper">source code</a>)<br />The Looper class has a MessageQueue object attached to it and blocks the current thread until a Message is received. This message will be passed to the assigned Handler. After that the Looper processes the next message in the queue or blocks again until a message is received.<br /><br /><b>HandlerThread</b>&nbsp;(<a href="https://developer.android.com/reference/android/os/HandlerThread.html">documentation</a>) (<a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/os/HandlerThread.java#HandlerThread">source code</a>)<br />A HandlerThread is a Thread implementation that does all the Looper setup for you. By creating and starting a HandlerThread instance you will have a running thread with a Looper attached to it waiting for messages to process.<br /><br /><span style="font-size: large;"><b>Read the code!</b></span><br /><br />Now we know enough about all the components to understand the <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java">IntentService code</a>.<br /><br /><b>onCreate()</b><br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-YHAG_7OKKhI/UhXazQgkmkI/AAAAAAAANSA/57KqOScdP9k/s1600/IntentService_oncreate.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-YHAG_7OKKhI/UhXazQgkmkI/AAAAAAAANSA/57KqOScdP9k/s1600/IntentService_oncreate.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#101">IntentService.onCreate()</a></td></tr></tbody></table><br />At first a HandlerThread is created and started. We now have a background thread running that already has a Looper assigned. This Looper is waiting on the background thread for messages to process.<br /><br />Next a ServiceHandler is created for this Looper. The Handler’s <a href="https://developer.android.com/reference/android/os/Handler.html#handleMessage(android.os.Message)">handleMessage</a>() method will be called for every message received by the Looper. The ServiceHandler obtains the Intent object from the Message and passes it to the onHandleIntent() method of the IntentService.<br /><br /><b>onStart()</b><br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-C91WSSI62cw/UhXbfPQsAJI/AAAAAAAANSI/k5Hyb0QB9qI/s1600/IntentService_onstart.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-C91WSSI62cw/UhXbfPQsAJI/AAAAAAAANSI/k5Hyb0QB9qI/s1600/IntentService_onstart.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#115">IntentService.onStart()</a></td></tr></tbody></table><br />The onStart() method is called every time <a href="https://developer.android.com/reference/android/content/Context.html#startService(android.content.Intent)">startService()</a> is called. We wrap the Intent in a Message object and post it to the Handler. The Handler will enqueue it in the message queue of the Looper. The onStart() method is deprecated since API level 5 (Android 2.0). Instead onStartCommand() should be implemented.<br /><br /><b>onStartCommand()</b><br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-HB0fPJbsY8A/UhXcBOrAYAI/AAAAAAAANSQ/pfU3UUXUmE8/s1600/IntentService_onstartcommand.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-HB0fPJbsY8A/UhXcBOrAYAI/AAAAAAAANSQ/pfU3UUXUmE8/s1600/IntentService_onstartcommand.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#129">IntentService.onStartCommand()</a></td></tr></tbody></table>In onStartCommand() we call onStart() to enqueue the Intent. We return <a href="https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT">START_REDELIVER_INTENT</a> or <a href="https://developer.android.com/reference/android/app/Service.html#START_NOT_STICKY">START_NOT_STICK</a> depending on what the child class has set via&nbsp;<a href="https://developer.android.com/reference/android/app/IntentService.html#setIntentRedelivery(boolean)">setIntentRedelivery()</a>. Depending on this setting an Intent will be redelivered to the service if the process dies before onHandleIntent() returns or the Intent will die as well.<br /><br /><b>onDestroy()</b><br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-GkK4xWmOWu0/UhXcmqOLkQI/AAAAAAAANSY/M8oZn2GF5FY/s1600/IntentService_ondestroy.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-GkK4xWmOWu0/UhXcmqOLkQI/AAAAAAAANSY/M8oZn2GF5FY/s1600/IntentService_ondestroy.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#134">IntentService.onDestroy()</a></td></tr></tbody></table>In onDestroy() we just need to stop the Looper.<br /><br /><span style="font-size: large;"><b>Conclusion</b></span><br /><br />The IntentService code is quite short and simple, yet a powerful pattern. With the <a href="https://developer.android.com/reference/android/os/Handler.html">Handler</a>, <a href="https://developer.android.com/reference/android/os/Looper.html">Looper</a> and <a href="https://developer.android.com/reference/java/lang/Thread.html">Thread</a> class you can easily build your own simple processing queues.<br /><br />Oh, and if you are looking for an exercise. The code of the onCreate() method contains a TODO comment that I omitted above:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-QlS7sJHfj1I/UhXp96c9r_I/AAAAAAAANS0/iRXyCb9E7NI/s1600/oncreate_todo.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-QlS7sJHfj1I/UhXp96c9r_I/AAAAAAAANS0/iRXyCb9E7NI/s1600/oncreate_todo.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#101">TODO in onCreate()</a></td></tr></tbody></table><br /><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/ohV1Ybma6A4" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com0http://www.androidzeitgeist.com/2013/08/read-code-intentservice.htmltag:blogger.com,1999:blog-1531829516427013333.post-50503744328427817632013-05-27T19:31:00.001+02:002013-05-27T19:31:31.801+02:00Sharing the taken picture - Instant Mustache #9<div><span style="font-size: x-small;">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.&nbsp;<a href="http://www.androidzeitgeist.com/p/instant-mustache.html">Click here</a>&nbsp;to get a chronological list of all published&nbsp;<a href="http://www.androidzeitgeist.com/p/instant-mustache.html">articles about Instant Mustache</a>.</span></div><br /><a href="http://www.androidzeitgeist.com/p/instant-mustache.html">Up to now</a> our app can take and view pictures. The next step is to share the taken picture with other Android apps. This is done via <a href="https://developer.android.com/guide/components/intents-filters.html">Intents</a>. The Intent system is one of the most powerful features of Android. It allows us to interact with any app that accepts images with almost no extra afford.<br /><br />We could create an <a href="https://developer.android.com/guide/topics/ui/actionbar.html">ActionBar</a> item and when clicked launch an <a href="https://developer.android.com/reference/android/content/Intent.html">Intent</a>&nbsp;to share the image but instead we are going to use a <a href="http://developer.android.com/training/sharing/shareaction.html">ShareActionProvider</a>. The ShareActionProvider adds a share icon to the ActionBar as well as the icon of the app that the user has shared pictures the most with. By clicking this icon the user can share directly with this app. In addition to that the ShareActionProvider shows a sub menu with more apps that the given picture can be shared with.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-pDiZ5iw7DMw/UaOT6GqMHuI/AAAAAAAALjk/oLbP5J6_nok/s1600/shareactionprovider.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-pDiZ5iw7DMw/UaOT6GqMHuI/AAAAAAAALjk/oLbP5J6_nok/s1600/shareactionprovider.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">A ShareActionProvider with Google+ as default share action.</td></tr></tbody></table><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-L_-glPQZGF8/UaOXqTIYImI/AAAAAAAALkE/ofFypChT2Ew/s1600/shareactionprovider_menu.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="285" src="http://4.bp.blogspot.com/-L_-glPQZGF8/UaOXqTIYImI/AAAAAAAALkE/ofFypChT2Ew/s320/shareactionprovider_menu.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Sub menu of a ShareActionProvider.</td></tr></tbody></table><br /><b>If sharing is a key feature of your activity, you should consider using the ShareActionProvider.</b><br /><br />We start by creating an XML menu file for adding the share action. For legacy reasons the ActionBar uses the same approach for creating action items as the menu in Android 2.x.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-KWMveO6nNuo/UaOTQWFlM1I/AAAAAAAALjc/OXqcUuxPmhg/s1600/activity_photo_menu.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-KWMveO6nNuo/UaOTQWFlM1I/AAAAAAAALjc/OXqcUuxPmhg/s1600/activity_photo_menu.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-09/res/menu/activity_photo.xml">activity_photo.xml</a></td></tr></tbody></table><br />Once we inflated the menu in <a href="https://developer.android.com/reference/android/app/Activity.html#onCreateOptionsMenu(android.view.Menu)">onCreateOptionsMenu()</a> we need to set the Intent used to share the photo.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-0eTGVnhBo_0/UaOVjb4aX-I/AAAAAAAALj0/mXc2KKjxi0k/s1600/initialize_share_action.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-0eTGVnhBo_0/UaOVjb4aX-I/AAAAAAAALj0/mXc2KKjxi0k/s1600/initialize_share_action.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-09/src/com/androidzeitgeist/mustache/activity/PhotoActivity.java#L45">PhotoActivity.initializeShareAction()</a></td></tr></tbody></table><br />Let's take a look at the different components of the Intent:<br /><ul><li><b>ACTION_SEND</b>: The default action used for “sending” data to an other unspecified activity.</li><li><b>MIME</b> type: The MIME type of the data being sent. Other apps can define multiple MIME types they accept. We are sending a JPEG image and therefore we are using the MIME type “image/jpeg”. To learn more about MIME types start with the <a href="https://en.wikipedia.org/wiki/Internet_media_type">"Internet media type" Wikipedia article</a>.</li><li><b>EXTRA_STREAM</b>: <a href="https://developer.android.com/reference/android/net/Uri.html">Uri</a> that points to the data that should be sent. In our case the Uri is pointing to the image file on the external storage.</li></ul><div><br />That's it already. For all changes done to the code base, see the <a href="https://github.com/pocmo/Instant-Mustache/commit/4bded2002dfd56f63245d59e07a44e71deb04172">repository on GitHub</a>. In the next article we'll polish some aspects of the app before we start implementing the Face detection feature.</div><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/APLuQCN5xxA" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com0http://www.androidzeitgeist.com/2013/05/sharing-taken-picture-instant-mustache-9.htmltag:blogger.com,1999:blog-1531829516427013333.post-83536581659699564412013-01-14T20:47:00.000+01:002013-01-14T20:47:04.700+01:00Fixing the rotation - Instant Mustache #8<div><span style="font-size: x-small;">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.&nbsp;<a href="http://www.androidzeitgeist.com/p/instant-mustache.html">Click here</a>&nbsp;to get a chronological list of all published&nbsp;<a href="http://www.androidzeitgeist.com/p/instant-mustache.html">articles about Instant Mustache</a>.</span></div><div></div><br /><b><span style="font-size: large;">Wrong orientation</span></b><br /><br />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.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-RDkIH8Iw8VY/UPRZTnyZA2I/AAAAAAAAH0Q/BU7sRAlQQOw/s1600/rotation_error.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="239" src="http://1.bp.blogspot.com/-RDkIH8Iw8VY/UPRZTnyZA2I/AAAAAAAAH0Q/BU7sRAlQQOw/s320/rotation_error.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Wrong orientation of photo that has been taken in portrait mode</td></tr></tbody></table><br />How does this happen? You may remember that we've used <a href="https://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)">Camera.setDisplayOrientation()</a> in one of the <a href="http://www.androidzeitgeist.com/2012/10/displaying-camera-preview-instant.html">previous articles</a> to explicitly set the display rotation. First, this setting only affects the preview picture. The picture passed to the <a href="https://developer.android.com/reference/android/hardware/Camera.ShutterCallback.html">Camera.ShutterCallback</a> 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.<br /><br /><b><span style="font-size: large;">Detecting and remembering the orientation</span></b><br /><br />What we need to do in our code is to register an <a href="https://developer.android.com/reference/android/view/OrientationEventListener.html">OrientationEventListener</a> to get notified whenever the orientation changes. We'll remember this orientation and use this to rotate the taken image once the callback returns.<br /><br />Whenever the orientation changes <a href="https://developer.android.com/reference/android/view/OrientationEventListener.html#onOrientationChanged(int)">onOrientationChanged(int)</a> 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.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-JQ2Rm-uZ0gQ/UPRahx8kf2I/AAAAAAAAH0c/v7NIwzqReBM/s1600/cameraorientationlistener_normalize.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-JQ2Rm-uZ0gQ/UPRahx8kf2I/AAAAAAAAH0c/v7NIwzqReBM/s1600/cameraorientationlistener_normalize.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-08/src/com/androidzeitgeist/mustache/listener/CameraOrientationListener.java#L31">CameraOrientationListener.normalize()</a></td></tr></tbody></table>Another method called <a href="https://github.com/pocmo/Instant-Mustache/blob/article-08/src/com/androidzeitgeist/mustache/listener/CameraOrientationListener.java#L51">rememberOrientation()</a> will be used to save the orientation of the device in the moment of the user pressing the shutter button.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-rqdv0tzl52Y/UPRbbrb1vBI/AAAAAAAAH00/2Ldj2OrPXBM/s1600/camerafragment_takepicture.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-rqdv0tzl52Y/UPRbbrb1vBI/AAAAAAAAH00/2Ldj2OrPXBM/s1600/camerafragment_takepicture.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-08/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L231">CameraFragment.takePicture()</a></td></tr></tbody></table><span style="font-size: large;"><b><br /></b></span><span style="font-size: large;"><b>Rotating the picture</b></span><br /><br />Now we just need to rotate the Bitmap. We do this by creating a new Bitmap object and applying a rotated <a href="http://developer.android.com/reference/android/graphics/Matrix.html">Matrix</a> to the pixels. The rotation angle is calculated by summing the remembered orientation, the display orientation and the natural rotation of the device.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-Kp9GQ7CSHik/UPRcKSjRiBI/AAAAAAAAH1I/cTRdA_NYgjI/s1600/camerafragment_onpicturetaken.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-Kp9GQ7CSHik/UPRcKSjRiBI/AAAAAAAAH1I/cTRdA_NYgjI/s1600/camerafragment_onpicturetaken.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-08/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L241">CameraFragment.onPictureTaken()</a></td></tr></tbody></table><br /><span style="font-size: large;"><b>Result</b></span><br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-3lBP-pDo5cQ/UPRd8jZp-LI/AAAAAAAAH14/kJaLh3-Mdak/s1600/result_rotation.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="242" src="http://3.bp.blogspot.com/-3lBP-pDo5cQ/UPRd8jZp-LI/AAAAAAAAH14/kJaLh3-Mdak/s320/result_rotation.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Photos rotated correctly in portrait and landscape mode</td></tr></tbody></table><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/17oriJk6I8w" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com1http://www.androidzeitgeist.com/2013/01/fixing-rotation-camera-picture.htmltag:blogger.com,1999:blog-1531829516427013333.post-15980312326138766372012-12-22T14:21:00.000+01:002012-12-22T14:27:19.476+01:00Android 2012<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-dKfHvBySVQk/UNWsnfVSK6I/AAAAAAAAHR8/FYWEUfw0yeU/s1600/android2012.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-dKfHvBySVQk/UNWsnfVSK6I/AAAAAAAAHR8/FYWEUfw0yeU/s1600/android2012.png" /></a></div><br />After <a href="http://www.youtube.com/watch?v=xY_MUB8adEQ">Google Zeitgeist</a> and&nbsp;<a href="http://www.youtube.com/watch?v=iCkYw3cRwLo">YouTube</a>&nbsp;looked back on 2012 it’s time to do the same for Android.<span id="goog_2128203782"></span><br /><br /><span style="font-size: large;"><b>January</b></span><br /><div><br /></div><a href="http://4.bp.blogspot.com/-4cV7er0VgPI/UNWw0GXYL5I/AAAAAAAAHSQ/nDFaspNqWns/s1600/androiddesign.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="157" src="http://4.bp.blogspot.com/-4cV7er0VgPI/UNWw0GXYL5I/AAAAAAAAHSQ/nDFaspNqWns/s200/androiddesign.png" width="200" /></a>The year 2011 has just ended and the <b>Galaxy Nexus</b> is the current flagship phone. 12 days later on January 12th the <b><a href="http://developer.android.com/design/index.html">Android Design</a></b> website launched. Followed by the <b><a href="https://plus.google.com/u/0/+AndroidDevelopers/posts">Android developers Google+ page</a></b> on January 30th.<br /><br /><span style="font-size: large;"><b>February</b></span><br /><br />The first numbers of the year are published. <b>850,000</b> Android phones are activated every day. <b>300 million </b>devices have been activated so far.<br /><br /><span style="font-size: large;"><b>March</b></span><br /><br />On March 5th <b><a href="http://android-developers.blogspot.de/2012/03/android-apps-break-50mb-barrier.html">expansion files</a></b> are introduced and <b>Android apps break the 50MB barrier</b> expanding the size limit to <b>4GB</b>. The Android market retires and is reborn as <b>Google play</b> on March 6th. The same month on March 21st the <b>SDK tools and ADT revision 17</b> 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 <b>multiple users</b> to manage published Android apps.<br /><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-qPqhAtg_PVg/UNWxPkos_2I/AAAAAAAAHSY/ivgQD4D4Nmo/s1600/appclinic.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="150" src="http://1.bp.blogspot.com/-qPqhAtg_PVg/UNWxPkos_2I/AAAAAAAAHSY/ivgQD4D4Nmo/s200/appclinic.jpg" width="200" /></a></div><br /><span style="font-size: large;"><b>April</b></span><br /><br />The emulator gets even more faster on April 9th by adding <b>GPU support</b>. On April 20th the first episode of <b><a href="http://www.youtube.com/playlist?list=PLB7B9B23D864A55C3">Friday App Review</a></b> airs and is later called <b><a href="http://www.youtube.com/playlist?list=PLB7B9B23D864A55C3">The app clinic</a></b>.<br /><br /><b><span style="font-size: large;">May</span></b><br /><br />On May 4th Wolfram Rittmeyer publishes the first posting on his blog <b><a href="http://www.grokkingandroid.com/">Grokking Android</a></b>. Followed by the first article published on <a href="http://www.androidzeitgeist.com/"><b>Android Zeitgeist</b></a> on May 27th. 3 days before on May 24th <b><a href="http://android-developers.blogspot.de/2012/05/in-app-subscriptions-in-google-play.html">In-app Subscriptions</a></b> are launched on Google Play.<br /><br /><span style="font-size: large;"><b>June</b></span><br /><a href="http://1.bp.blogspot.com/-HvAwcdnUaq4/UNWxlZCN7XI/AAAAAAAAHSg/jM3slhIq--k/s1600/google-io-logo.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="40" src="http://1.bp.blogspot.com/-HvAwcdnUaq4/UNWxlZCN7XI/AAAAAAAAHSg/jM3slhIq--k/s200/google-io-logo.png" width="200" /></a><br />The <b><a href="https://developers.google.com/events/io/">Google I/O</a></b> takes place for three days from June 27th to 29th. There are now <b>900,000</b> Android devices activated every day and <b>400 million</b> devices have been activated up to now. <b>Android 4.1 (Jelly Bean)</b> is publicly shown for the first time on June 27th. The same day the <b>Android 4.1 SDK</b> is released. In addition to that the first tablet by Google is unveiled: The <b>Nexus 7</b>. On the second day of the Google I/O the <b>Android SDK tools</b> are updated to <b>revision 20</b>. At the end of the Google I/O there have been 3.5 million live streams seen from 170 countries.<br /><br /><span style="font-size: large;"><b>July</b></span><br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-iu-C9xVT-_U/UNWyPKppPJI/AAAAAAAAHS4/JfRWJvOp7dg/s1600/ouya.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="200" src="http://3.bp.blogspot.com/-iu-C9xVT-_U/UNWyPKppPJI/AAAAAAAAHS4/JfRWJvOp7dg/s200/ouya.png" width="200" /></a></div>On July 3rd the <b><a href="http://www.ouya.tv/">Ouya</a></b>, an Android based console, is unveiled and a Kickstarter campaign is started on July 10th. On July 9th the <b>Android 4.1 source code</b> is published as part of the Android Open Source Project (AOSP).<br /><br /><span style="font-size: large;"><b>August</b></span><br /><br />The funding phase for the <b>Ouya</b> is completed. The campaign collected $8,596,475. That’s 904% more than the initial campaign goal.<br /><br /><span style="font-size: large;"><b>September</b></span><br /><br />New numbers are released. There are now <b>1.3 million devices</b> activated every a day. About <b>70,000</b> of these devices are tablets. <b>480 million</b> devices have been activated up to now. On September 9th the first episode of <b><a href="http://www.youtube.com/playlist?list=PLWz5rJ2EKKc9Wam5jE-9oY8l6RpeAx-XM">This week in Android development</a></b> airs. A day later the first episode of <b><a href="http://www.youtube.com/playlist?list=PLWz5rJ2EKKc8j2B95zGMb8muZvrIy-wcF">Android Design in Action</a></b> is uploaded to YouTube.<br /><br /><span style="font-size: large;"><b>October</b></span><br /><br /><a href="http://3.bp.blogspot.com/-iMCBRXK31yE/UNWx_vXgIjI/AAAAAAAAHSw/9PXrh3tQBs4/s1600/nexus4.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="167" src="http://3.bp.blogspot.com/-iMCBRXK31yE/UNWx_vXgIjI/AAAAAAAAHSw/9PXrh3tQBs4/s200/nexus4.png" width="200" /></a>Till mid October <b>3 million Nexus 7</b> units have been sold. Starting from October 15th the <b>new Google Play Developer Console</b> is available to everyone. Google planned a <b>launch event</b> on October 29th in New York but it has been cancelled due to Hurricane Sandy. Nevertheless the <b>Nexus 4</b> and <b>Nexus 10</b> are introduced online this day. These are the first devices to run <b>Android 4.2</b>.<br /><br /><span style="font-size: large;"><b>November</b></span><br /><br />The first episode of <a href="http://www.youtube.com/playlist?list=PLWz5rJ2EKKc9loen4OjS03gdjI0JhF4cW">(╯°□°）╯︵ ┻━┻</a> airs on November 8th. On November 13th the <b>Nexus 4</b> and <b>Nexus 10</b> went on sale and are sold out in minutes. Later that day the <b>Android 4.2 SDK platform</b> is released. Another day later the <b>Android SDK tools revision 21</b> are released.<br /><br /><span style="font-size: large;"><b>December</b></span><br /><br />Google releases a new <b><a href="http://android-developers.blogspot.de/2012/12/new-google-maps-android-api-now-part-of.html">Google Maps API</a></b> for Android on December 3rd. On December 10th a new version of the<b> <a href="http://android-developers.blogspot.de/2012/12/in-app-billing-version-3.html">In-App billing API</a></b> is released.<br /><br />The Android team releases their <b>Happy Holidays</b> video:<br /><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" height="315" src="http://www.youtube.com/embed/967nio2LF7s" width="560"></iframe><br /><br /><span style="font-size: large;"><b>2013?</b></span><br /><br /><b>What has been your Android highlight in 2012 and what are your wishes for 2013?</b><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/6_wXwDjJRdc" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com1http://www.androidzeitgeist.com/2012/12/android-2012.htmltag:blogger.com,1999:blog-1531829516427013333.post-87340048280907552042012-12-11T20:41:00.000+01:002012-12-11T20:41:51.969+01:00Mind the gap: String.isEmpty()<span style="font-size: x-small;">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.</span><br /><br />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.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-FWLirpvHLAM/UMbfKHp3tUI/AAAAAAAAHCI/NPnjM4Qx5Pk/s1600/isemptyvariants.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-FWLirpvHLAM/UMbfKHp3tUI/AAAAAAAAHCI/NPnjM4Qx5Pk/s1600/isemptyvariants.png" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"></div>In the book "<a href="http://books.google.de/books?id=dwSfGQAACAAJ&amp;dq=isbn:0132350882y">Clean code</a>" by <a href="http://en.wikipedia.org/wiki/Robert_Cecil_Martin">Robert C. Martin</a> you can read&nbsp;<a href="http://en.wikipedia.org/wiki/Grady_Booch">Grady Booch</a> saying: "Clean code reads like well-written prose". So I try to use <a href="http://developer.android.com/reference/java/lang/String.html#isEmpty()">String.isEmpty()</a> for that reason. Internally it may do a length check as well (I stopped my investigation at the <i>native</i> keyword)&nbsp;but when reading the following snippet it is absolutely obvious what I intend to do.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-fPosAzNzRYc/UMbecA_7d3I/AAAAAAAAHB4/K6lPAMAkrIs/s1600/isEmptyMethod.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-fPosAzNzRYc/UMbecA_7d3I/AAAAAAAAHB4/K6lPAMAkrIs/s1600/isEmptyMethod.png" /></a></div><br />Even though <a href="http://developer.android.com/reference/java/lang/String.html#isEmpty()">isEmpty()</a> 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 <a href="http://tools.android.com/tips/lint">lint</a>&nbsp;thankfully&nbsp;saves me from doing this error.<br /><br />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: <a href="http://developer.android.com/reference/android/text/TextUtils.html">TextUtils</a>. This class also has a lot of other helpful methods like <a href="http://developer.android.com/reference/android/text/TextUtils.html#join(java.lang.CharSequence, java.lang.Object[])">join()</a> to join an array of elements to a String using a&nbsp;delimiter&nbsp;or <a href="http://developer.android.com/reference/android/text/TextUtils.html#getReverse(java.lang.CharSequence, int, int)">getReverse() </a>to reverse a String (Take that interview question!).<br /><br />In addition to that the TextUtils class has a method <a href="http://developer.android.com/reference/android/text/TextUtils.html#isEmpty(java.lang.CharSequence)">isEmpty()</a> that is available since API level 1.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-nzyWV2TAHaM/UMbelfdSHCI/AAAAAAAAHCA/Z2lLIH_YddU/s1600/TextUtils.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-nzyWV2TAHaM/UMbelfdSHCI/AAAAAAAAHCA/Z2lLIH_YddU/s1600/TextUtils.png" /></a></div><br />Phew! By the way: <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/text/TextUtils.java#TextUtils.isEmpty%28java.lang.CharSequence%29">Internally</a> isEmpty() checks if the length of the String is 0 (and does a null check).<img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/jbePXNFlMnA" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com5http://www.androidzeitgeist.com/2012/12/string-is-empty.htmltag:blogger.com,1999:blog-1531829516427013333.post-45102601454214192362012-11-05T20:40:00.000+01:002012-11-05T20:40:10.181+01:00Examining the ViewPager #3<span style="font-size: x-small;">This article is part of a series of articles about the ViewPager component.&nbsp;</span><a href="http://www.androidzeitgeist.com/p/viewpager.html" style="font-size: small;">Click here</a><span style="font-size: x-small;">&nbsp;to see a list of&nbsp;</span><a href="http://www.androidzeitgeist.com/p/viewpager.html" style="font-size: small;">all articles of this series</a><span style="font-size: x-small;">.</span><br /><br /><b><span style="font-size: large;">Horizontal scrolling pages</span></b><br /><br />Have you ever tried putting horizontal scrolling components inside a <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html">ViewPager</a>? Well, since revision 9 of the <a href="http://developer.android.com/tools/extras/support-library.html">support library</a> 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 <a href="http://developer.android.com/reference/android/webkit/WebView.html">WebView</a>.<br /><br />Internally the ViewPager uses <a href="http://developer.android.com/reference/android/support/v4/view/ViewCompat.html#canScrollHorizontally(android.view.View, int)">ViewCompat.canScrollHorizontally(View v, int direction)</a>&nbsp;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.<br /><br /><b><span style="font-size: large;">Bezel swipe</span></b><br /><br />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 <i>bezel swipe</i>.<br /><br />For reading more about the bezel swipe gesture from a UI point of view read "<a href="http://www.androiduipatterns.com/2012/02/bezel-swipe-solution-to-pan-and-swipe.html">Bezel swipe, a Solution to Pan and Swipe Confusion?</a>" on <a href="http://www.androiduipatterns.com/">Android UI Patterns</a>.<br /><br />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.<br /><br />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.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-dNYV1OuZvdQ/UJgLXTptXHI/AAAAAAAAGS4/Zt0OXlBwbaM/s1600/viewpager_bezel_swipe.png" imageanchor="1"><img border="0" src="http://1.bp.blogspot.com/-dNYV1OuZvdQ/UJgLXTptXHI/AAAAAAAAGS4/Zt0OXlBwbaM/s1600/viewpager_bezel_swipe.png" /></a></div><br /><span style="font-size: large;"><b>Fake dragging</b></span><br /><br />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.<br /><br />You have to signal the ViewPager when to start or end a fake drag by calling <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html#beginFakeDrag()">beginFakeDrag()</a> and <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html#endFakeDrag()">endFakeDrag()</a> on it. After starting a fake drag you can use <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html#fakeDragBy(float)">fakeDragBy(float)</a> 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).<br /><br />The following example uses a <a href="http://developer.android.com/reference/android/widget/SeekBar.html">SeekBar</a>&nbsp;whose current progress state is used to fake drag a ViewPager by the given percentage.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-SFr1EyEfN4Q/UJgNTawCuSI/AAAAAAAAGTA/Pq_75XpuIes/s1600/SeekBarPagerListener.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-SFr1EyEfN4Q/UJgNTawCuSI/AAAAAAAAGTA/Pq_75XpuIes/s1600/SeekBarPagerListener.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/ViewPager/03/SeekBarPagerListener.java">SeekBarPagerListener.java</a></td></tr></tbody></table><br />This video shows the fake drag in action:<br /><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" height="360" src="http://www.youtube.com/embed/us8w2g9YXC4?rel=0" width="480"></iframe><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/GsNXoeK7N6Y" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com1http://www.androidzeitgeist.com/2012/11/examining-viewpager-3.htmltag:blogger.com,1999:blog-1531829516427013333.post-53618283758571209542012-10-30T23:35:00.000+01:002012-10-30T23:35:59.679+01:00Displaying the taken picture – Instant Mustache #7<div><span style="font-size: x-small;">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. <a href="http://www.androidzeitgeist.com/p/instant-mustache.html">Click here</a> to get a chronological list of all published <a href="http://www.androidzeitgeist.com/p/instant-mustache.html">articles about Instant Mustache</a>.</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: large;"><b>Writing the PhotoActivity</b></span></div><div><br /></div>In the <a href="http://www.androidzeitgeist.com/2012/10/taking-picture-instant-mustache-6.html">last article</a> 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.<br /><br />We'll start by creating an empty activity called <a href="https://github.com/pocmo/Instant-Mustache/blob/article-07/src/com/androidzeitgeist/mustache/activity/PhotoActivity.java">PhotoActivity</a> and add it to the <a href="https://github.com/pocmo/Instant-Mustache/blob/article-07/AndroidManifest.xml#L30">manifest</a> of our application. For now the layout will only contain an <a href="http://developer.android.com/reference/android/widget/ImageView.html">ImageView</a> to display the picture:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-KnA5sDjgZds/UJBOSqCkiZI/AAAAAAAAF2k/q3_2TKxMJ8I/s1600/layout_activity_photo.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-KnA5sDjgZds/UJBOSqCkiZI/AAAAAAAAF2k/q3_2TKxMJ8I/s1600/layout_activity_photo.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-07/res/layout/activity_photo.xml">activity_photo.xml</a></td></tr></tbody></table><br />Instead of showing a toast in our <a href="https://github.com/pocmo/Instant-Mustache/blob/article-07/src/com/androidzeitgeist/mustache/activity/CameraActivity.java">CameraActivity</a> we create an Intent to start the PhotoActivity and use <a href="http://developer.android.com/reference/android/content/Intent.html#setData(android.net.Uri)">setData(Uri)</a> on the Intent object to pass a <a href="http://developer.android.com/reference/android/net/Uri.html">Uri</a> pointing to the picture file:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-VSkxvSyGYTU/UJBOjJYq-aI/AAAAAAAAF2s/q1DBQt-Ib9A/s1600/intent_start_photo_activity.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-VSkxvSyGYTU/UJBOjJYq-aI/AAAAAAAAF2s/q1DBQt-Ib9A/s1600/intent_start_photo_activity.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-07/src/com/androidzeitgeist/mustache/activity/CameraActivity.java#L116">onPictureTaken() - CameraActivity.java</a></td></tr></tbody></table><br />In <a href="https://github.com/pocmo/Instant-Mustache/blob/article-07/src/com/androidzeitgeist/mustache/activity/PhotoActivity.java#L19">onCreate(Bundle)</a> 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.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-tE-1b0nu5Ic/UJBO0nSip_I/AAAAAAAAF20/Thmkq7JwsmM/s1600/photo_activity_on_create.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-tE-1b0nu5Ic/UJBO0nSip_I/AAAAAAAAF20/Thmkq7JwsmM/s1600/photo_activity_on_create.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-07/src/com/androidzeitgeist/mustache/activity/PhotoActivity.java#L19">onCreate() - PhotoActivity.java</a></td></tr></tbody></table><br />And that's already all the code we need for the first version of the <a href="https://github.com/pocmo/Instant-Mustache/blob/article-07/src/com/androidzeitgeist/mustache/activity/PhotoActivity.java">PhotoActivity</a>.<br /><br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-sX-QuJWGZtQ/UJBVljLcABI/AAAAAAAAF3I/XEL1UJHnjiI/s1600/camera_and_photo_screenshot.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-sX-QuJWGZtQ/UJBVljLcABI/AAAAAAAAF3I/XEL1UJHnjiI/s1600/camera_and_photo_screenshot.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">CameraActivity (left) and PhotoActivity (right)</td></tr></tbody></table><br /><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/bA7tWUcBxoc" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com3http://www.androidzeitgeist.com/2012/10/displaying-taken-picture-instant.htmltag:blogger.com,1999:blog-1531829516427013333.post-738290383239656172012-10-26T20:59:00.000+02:002012-10-30T23:17:25.534+01:00Taking a picture – Instant Mustache #6<span style="font-size: x-small;">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.&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">Click here</a><span style="font-size: x-small;">&nbsp;to get a chronological list of all published&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">articles about Instant Mustache</a><span style="font-size: x-small;">.</span><br /><br />After writing the necessary code to display a camera preview in <a href="http://www.androidzeitgeist.com/2012/10/displaying-camera-preview-instant.html">the last article</a> it's now time to actually take a picture and save it on the external storage of the device.<br /><br />We start by extending the layout of the <a href="https://github.com/pocmo/Instant-Mustache/blob/article-06/src/com/androidzeitgeist/mustache/activity/CameraActivity.java">CameraActivity</a> 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 <a href="http://developer.android.com/reference/android/view/View.html#attr_android:onClick">android:onClick</a>. Another option would be to assign an <a href="http://developer.android.com/reference/android/view/View.OnClickListener.html">OnClickListener</a> 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 <a href="http://tools.android.com/tips/lint">lint</a> tool is able to check the onClick attributes for correctness. So we will use the XML attribute here.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-hmHriQ9ppUo/UIqjpzAIkZI/AAAAAAAAFpQ/PJZvXOEn5XE/s1600/activity_camera.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-hmHriQ9ppUo/UIqjpzAIkZI/AAAAAAAAFpQ/PJZvXOEn5XE/s1600/activity_camera.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-06/res/layout/activity_camera.xml">activity_camera.xml</a></td></tr></tbody></table><br />As we encapsulated the code handling the <a href="http://developer.android.com/reference/android/hardware/Camera.html">Camera</a> object inside the <a href="https://github.com/pocmo/Instant-Mustache/blob/article-06/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java">CameraFragment</a> class the activity just calls takePicture() on the fragment when the user presses the button.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-FE7ro562XUI/UIqkAHnPvzI/AAAAAAAAFpY/-IZ0ncr7CbU/s1600/CameraActivity_takePicture.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-FE7ro562XUI/UIqkAHnPvzI/AAAAAAAAFpY/-IZ0ncr7CbU/s1600/CameraActivity_takePicture.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-06/src/com/androidzeitgeist/mustache/activity/CameraActivity.java#L64">takePicutre() - CameraActivity.java</a></td></tr></tbody></table><br />The fragment calls <a href="http://developer.android.com/reference/android/hardware/Camera.html#takePicture(android.hardware.Camera.ShutterCallback, android.hardware.Camera.PictureCallback, android.hardware.Camera.PictureCallback, android.hardware.Camera.PictureCallback)">takePicture()</a> 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:<br /><br /><ul><li>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.</li><li>The raw callback occurs when the raw image data is available.</li><li>The postview callback occurs when a scaled, fully processed postview image is available.</li><li>The jpeg callback occurs when the compressed image is available.</li></ul><br />We are only interested in the JPEG image. The fragment itself is also the callback so we'll implement the Camera.PictureCallback interface.<br /><br />Once the picture is taken <a href="http://developer.android.com/reference/android/hardware/Camera.PictureCallback.html#onPictureTaken(byte[], android.hardware.Camera)">onPictureTaken()</a> will be called with a byte array. We decode the given byte array and create a <a href="http://developer.android.com/reference/android/graphics/Bitmap.html">Bitmap</a> object and pass it via the <a href="https://github.com/pocmo/Instant-Mustache/blob/article-06/src/com/androidzeitgeist/mustache/listener/CameraFragmentListener.java">CameraFragmentListener</a> interface to our CameraActivity. Later we will use this bitmap to draw the mustaches on it.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-HmhBl2pWTlo/UIqkdSoODnI/AAAAAAAAFpg/cJ69yQZOIeg/s1600/CameraFragment_onPictureTaken.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-HmhBl2pWTlo/UIqkdSoODnI/AAAAAAAAFpg/cJ69yQZOIeg/s1600/CameraFragment_onPictureTaken.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-06/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L224">onPictureTaken() - CameraFragment.java</a></td></tr></tbody></table><br /><b><span style="font-size: large;">Saving the picture to the external storage</span></b><br /><br />Once the activity receives the bitmap object it needs to do a bunch of things:<br /><ul><li>Determine the directory to save the picture to and create it if necessary</li><li>Create a unique file for the picture inside the directory and save the image data to it</li><li>Notify the MediaScanner that we created a new file</li><li>Show a toast that the picture has been saved successfully (For now until we've written the activity to display the taken picture).</li></ul><br /><span style="font-size: large;">Determining the directory</span><br /><br />We want to save the picture into a directory on the external storage that is visible for all other applications. By calling <a href="http://developer.android.com/reference/android/os/Environment.html#getExternalStoragePublicDirectory(java.lang.String)">Environment.getExternalStoragePublicDirectory()</a> and passing <a href="http://developer.android.com/reference/android/os/Environment.html#DIRECTORY_PICTURES">Environment.DIRECTORY_PICTURES</a> 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).<br /><br /><div class="separator" style="clear: both; text-align: center;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-etMHinejAMU/UIqlvdRKOlI/AAAAAAAAFpo/QlH_Lu4dUOY/s1600/determining_directory.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-etMHinejAMU/UIqlvdRKOlI/AAAAAAAAFpo/QlH_Lu4dUOY/s1600/determining_directory.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-06/src/com/androidzeitgeist/mustache/activity/CameraActivity.java#L77">onPictureTaken() - CameraActivity.java</a></td></tr></tbody></table><br />&nbsp;<span style="font-size: large;">Saving the file</span><br /><br />We'll create a file with a unique file name containing a timestamp, e.g.: MUSTACHE_20121031_235959.jpg. The <a href="http://developer.android.com/reference/android/graphics/Bitmap.html#compress(android.graphics.Bitmap.CompressFormat, int, java.io.OutputStream)">compress()</a> method of the bitmap object is used to save the picture into the file.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-yNhLaUTQOs8/UIqlxhiP9tI/AAAAAAAAFp0/HlPPxj7_L9w/s1600/saving_picture.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-yNhLaUTQOs8/UIqlxhiP9tI/AAAAAAAAFp0/HlPPxj7_L9w/s1600/saving_picture.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-06/src/com/androidzeitgeist/mustache/activity/CameraActivity.java#L92">onPictureTaken() - CameraActivity.java</a></td></tr></tbody></table><br /><span style="font-size: large;">Notifying the MediaScanner</span><br /><br />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 <a href="http://developer.android.com/reference/android/media/MediaScannerConnection.html">MediaScanner</a>. For that reason we'll notify the MediaScanner about the file we've created.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-BKiT4GJmj8s/UIqlw9LsiFI/AAAAAAAAFpw/K1_jEkvhX98/s1600/notify_mediascanner.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-BKiT4GJmj8s/UIqlw9LsiFI/AAAAAAAAFpw/K1_jEkvhX98/s1600/notify_mediascanner.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-06/src/com/androidzeitgeist/mustache/activity/CameraActivity.java#L107">onPictureTaken() - CameraActivity.java</a></td></tr></tbody></table><br />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?<img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/c-K683UuF5g" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com1http://www.androidzeitgeist.com/2012/10/taking-picture-instant-mustache-6.htmltag:blogger.com,1999:blog-1531829516427013333.post-87275454949960597172012-10-25T21:24:00.000+02:002012-11-05T19:31:06.680+01:00Examining the ViewPager #2<span style="font-size: x-small;">This article is part of a series of articles about the ViewPager component.&nbsp;</span><a href="http://www.androidzeitgeist.com/p/viewpager.html" style="font-size: small;">Click here</a><span style="font-size: x-small;">&nbsp;to see a list of&nbsp;</span><a href="http://www.androidzeitgeist.com/p/viewpager.html" style="font-size: small;">all articles of this series</a><span style="font-size: x-small;">.</span><br /><br /><b><span style="font-size: large;">Offscreen pages</span></b><br /><br />The <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html">ViewPager</a> 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.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-HzlJvCZIyQc/UIkzJpr1neI/AAAAAAAAFls/jfMZCwnu30I/s1600/offscreen_pages.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-HzlJvCZIyQc/UIkzJpr1neI/AAAAAAAAFls/jfMZCwnu30I/s1600/offscreen_pages.png" /></a></div><br /><br />If you only use a small amount of pages you may get a better performance by creating them all at once. You can use <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setOffscreenPageLimit(int)">setOffscreenPageLimit(int limit)</a> 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.<br /><br /><b><span style="font-size: large;">Responding to changing states</span></b><br /><br />You can get notified whenever the displayed page changes or is incrementally scrolled. To listen to these state changes you can implement the <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener.html">OnPageChangeListener</a> interface or extend the <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.SimpleOnPageChangeListener.html">SimpleOnPageChangeListener</a> class if you do not intent to override every method of the interfacce.<br /><br />The following example listener updates the title of the activity according to the title of the currently selected page:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-xgUfOiVWc_g/UIkzQ_OCF8I/AAAAAAAAFl0/Fp_eXNYlt4g/s1600/UpdateTitleListener.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-xgUfOiVWc_g/UIkzQ_OCF8I/AAAAAAAAFl0/Fp_eXNYlt4g/s1600/UpdateTitleListener.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/ViewPager/02/UpdateTitleListener.java">UpdateTitleListener.java</a></td></tr></tbody></table><br /><b><span style="font-size: large;">Margin between pages</span></b><br /><br />When scrolling through pages they all look like glued together. You can use <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setPageMargin(int)">setPageMargin(int pixels)</a> to define a margin between pages. The gap is filled with the background color of the ViewPager.<br /><br />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 <a href="http://stackoverflow.com/a/6327095/234908">convert them to the actual number of pixels</a> for the current screen.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-fS9q-7IO-Uc/UIkzWvgeSoI/AAAAAAAAFl8/GESmB7vau7c/s1600/page_margin.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-fS9q-7IO-Uc/UIkzWvgeSoI/AAAAAAAAFl8/GESmB7vau7c/s1600/page_margin.png" /></a></div><br />It's also possible to define a drawable that will be used to fill the margin between two pages using <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setPageMarginDrawable(int)">setPageMarginDrawable(int resId)</a> or <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setPageMarginDrawable(android.graphics.drawable.Drawable)">setPageMarginDrawable(Drawable d)</a>. The best approach is to use a <a href="http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch">Nine-patch</a> that be scaled dynamically by the system to fill the space between the pages.<br /><br /><b><span style="font-size: large;">Switching pages programmatically</span></b><br /><br />You can also switch between pages programmatically. Using <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setCurrentItem(int, boolean)">setCurrentItem(int position, boolean smoothScroll)</a> you can switch to the given position. If the second parameter is <span style="color: #660000;">true</span> a smooth animated transition is being performed. Using <span style="color: #660000;">false</span> 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 <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setCurrentItem(int)">setCurrentItem(int position)</a>.<img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/EUMG4ssJLOA" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com1http://www.androidzeitgeist.com/2012/10/examining-viewpager-2.htmltag:blogger.com,1999:blog-1531829516427013333.post-16185864477175936152012-10-22T18:30:00.000+02:002012-10-30T23:18:43.358+01:00Examining the ViewPager #1<span style="font-size: x-small;">This article is part of a series of articles about the ViewPager component. <a href="http://www.androidzeitgeist.com/p/viewpager.html">Click here</a> to see a list of <a href="http://www.androidzeitgeist.com/p/viewpager.html">all articles of this series</a>.</span><br /><br />The <a href="http://developer.android.com/tools/extras/support-library.html">Android support library</a> offers a great UI component for horizontal scrolling pages: <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html">The ViewPager</a>. Over the last iterations of the support library more and more functionality has been added to the ViewPager silently. For that reason I decided to study the various features of the ViewPager more closely. This will be a series of articles covering several of these features.<br /><br /><span style="font-size: large;"><b>Disclaimer upfront</b></span><br /><br />According to the <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html">documentation of the ViewPager</a> the implementation and API of the class may change in future releases. Therefore also this blog posting may not be up-to-date if you are reading this a long time after the published date. Check the <a href="http://developer.android.com/reference/android/support/v4/view/ViewPager.html">documentation</a> if some of the examples may not work anymore as I described them here.<br /><br /><span style="font-size: large;"><b>The basics</b></span><br /><br />The ViewPager is a <a href="http://developer.android.com/reference/android/view/ViewGroup.html">ViewGroup</a> that displays by default one page at a time. The user can switch between these pages by swiping horizontally. A <a href="http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html">PagerAdapter</a> dynamically provides these pages which can be just views or fragments. However the ViewPager is not an AdapterView like the ListView or the GridView. Therefore you need to implement a specific adapter class in order to use the ViewPager class.<br /><br />The following video shows a ViewPager with different colored pages:<br /><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" height="360" src="http://www.youtube.com/embed/tcnGyRc9t0M?rel=0" width="480"></iframe> <br /><br /><span style="font-size: large;"><b>Adapter using fragments</b></span><br /><br />The easiest way to write an adapter for a ViewPager is to use fragments and let your adapter implementation extend the <a href="http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html">FragmentPagerAdapter</a> class. You only need to implement <a href="http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html#getItem(int)">getItem(int position)</a> to return a fragment for the page at the given position and <a href="http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getCount()">getCount()</a> to return the number of pages to display.<br /><br />The following implementation shows the FragmentPagerAdapter used for the video above:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-CkknYiuF5vQ/UIQMY-pOFOI/AAAAAAAAFjY/es0HrjuPA0U/s1600/SampleFragmentPagerAdapter.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-CkknYiuF5vQ/UIQMY-pOFOI/AAAAAAAAFjY/es0HrjuPA0U/s1600/SampleFragmentPagerAdapter.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/ViewPager/01/SampleFragmentPagerAdapter.java">SampleFragmentPagerAdapter.java</a></td></tr></tbody></table><br /><span style="font-size: large;"><b>Saving fragment states</b></span><br /><br />When using a <a href="http://developer.android.com/reference/android/support/v13/app/FragmentPagerAdapter.html">FragmentPagerAdapter</a> and swiping through the pages the ViewPager may eventually have created fragments for all the pages. Depending on the number of pages this may need a large amount of memory just for holding all the offscreen pages. To solve this problem the support library offers the <a href="http://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html">FragmentStatePagerAdapter</a> class. This adapter will destroy fragments not visible to the user when needed. Whenever this happens the adapter saves the fragment's current state using <a href="http://developer.android.com/reference/android/app/Fragment.html#onSaveInstanceState(android.os.Bundle)">onSaveInstanceState(Bundle outState) </a>of the fragment class to restore it when the fragment for this page gets recreated.<br /><br />Implementing an adapter extending the FragmentStatePagerAdapter is exactly the same as when using the FragmentPagerAdapter class. Just your fragment needs to take care of saving its state when getting destroyed. Take a look at the <a href="http://developer.android.com/reference/android/app/Fragment.html#onSaveInstanceState(android.os.Bundle)">documentation</a> for an example on how to retain the state of a fragment.<br /><br /><span style="font-size: large;"><b>Adapter using views</b></span><br /><br />You can also use the ViewPager with only View objects as pages if using fragments isn't an option for you. It's a bit more tricky to implement the adapter as you'll have to directly extend the <a href="http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html">PagerAdapter</a> class.<br /><br />The following example creates an adapter that shows a number of different colored pages like the FragmentPagerAdapter implementation above.<br /><br /><div class="separator" style="clear: both; text-align: center;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-mxaO0n1cYhQ/UIQNo43ZszI/AAAAAAAAFjw/FQ5bbhdhrMU/s1600/SamplePagerAdapter.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-mxaO0n1cYhQ/UIQNo43ZszI/AAAAAAAAFjw/FQ5bbhdhrMU/s1600/SamplePagerAdapter.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/ViewPager/01/SamplePagerAdapter.java">SamplePagerAdaper.java</a></td></tr></tbody></table><br />As you can see you need to write some boilerplate code to add and remove the views to the pager. I published a simple&nbsp;<a href="https://gist.github.com/3927202">ViewPagerAdapter</a> class on GitHub that does all this for you so that you don't need to write much more code than when using fragments:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-RG1Pybe0Qko/UIQNEZ0emlI/AAAAAAAAFjo/wiW86E8k3lY/s1600/SamplePagerAdapter2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-RG1Pybe0Qko/UIQNEZ0emlI/AAAAAAAAFjo/wiW86E8k3lY/s1600/SamplePagerAdapter2.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/ViewPager/01/SamplePagerAdapter2.java">SamplePagerAdapter2.java</a></td></tr></tbody></table><br /><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/Xd7oNSltXJE" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com4http://www.androidzeitgeist.com/2012/10/examining-viewpager-14.htmltag:blogger.com,1999:blog-1531829516427013333.post-29351607224188807622012-10-18T19:28:00.003+02:002012-10-30T23:19:15.647+01:00Displaying the camera preview - Instant Mustache #5<span style="font-size: x-small;">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.&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">Click here</a><span style="font-size: x-small;">&nbsp;to get a chronological list of all published&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">articles about Instant Mustache</a><span style="font-size: x-small;">.</span><br /><br />From <a href="http://www.androidzeitgeist.com/2012/10/using-fragment-for-camera-preview.html">the last article</a> we already have three components: A <a href="https://github.com/pocmo/Instant-Mustache/blob/article-04/src/com/androidzeitgeist/mustache/activity/CameraActivity.java">CameraActivity</a> with not much code, an empty <a href="https://github.com/pocmo/Instant-Mustache/blob/article-04/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java">CameraFragment</a> and a <a href="https://github.com/pocmo/Instant-Mustache/blob/article-04/src/com/androidzeitgeist/mustache/listener/CameraFragmentListener.java">CameraFragmentListener</a> interface for the communication between fragment and activity. Now we need to write the actual CameraFragment code to display the camera preview.<br /><br /><span style="font-size: large;"><b>The CameraPreview component</b></span><br /><br />For displaying the preview we need an instance of <a href="http://developer.android.com/reference/android/view/SurfaceView.html">SurfaceView</a> to draw the actual camera picture on. We'll extend the SurfaceView to create our own view component called <a href="https://github.com/pocmo/Instant-Mustache/blob/article-05/src/com/androidzeitgeist/mustache/view/CameraPreview.java">CameraPreview</a>.<br /><div><br /></div><div><a href="http://www.androidzeitgeist.com/2012/08/lets-start-coding-not-instant-mustache-3.html">In the first articles</a> we decided to use a square ratio for the preview. After some testing around it seems the emulator is the only "device" that supports a square sized camera preview and picture size out of the box. To work around this issue we would need to use a widely supported ratio (4:3) and crop the preview picture as well as the taken photo ourselves. To keep the code and the first version of the app small (and to follow the <a href="http://www.androidzeitgeist.com/2012/07/minimal-marketable-app-instant-mustache.html">Minimal Marketable App</a> principle) I decided to change this requirement. We will use the commonly supported ratio of 4:3.<br /><br />We implement onMeasure() to set the dimension of the view to a 4:3 ratio:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-NJ3UtjTFli4/UIA4oX9FTuI/AAAAAAAAFhg/AMivOOGn94Y/s1600/onmeasure.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-NJ3UtjTFli4/UIA4oX9FTuI/AAAAAAAAFhg/AMivOOGn94Y/s1600/onmeasure.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-05/src/com/androidzeitgeist/mustache/view/CameraPreview.java#L32">onMeasure() - CameraPreview.java</a></td></tr></tbody></table><br /><b><span style="font-size: large;">Setting up the camera</span></b><br /><br />Even though we know our desired ratio we can't just set a size via <a href="http://developer.android.com/reference/android/hardware/Camera.html#setParameters(android.hardware.Camera.Parameters)">camera.setParameters(Camera.Parameters)</a>. Instead we have to query the <a href="http://developer.android.com/reference/android/hardware/Camera.Parameters.html">Camera.Parameters</a> object we get from getParameters() to retrieve a list of supported preview and picture sizes. Then we have to scan this list for a size that has our desired ratio. This is what determineBestSize() does:</div><div><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-9EpZpQQQk_g/UH2o5PQn4kI/AAAAAAAAFgk/Ip5ecGrvDSU/s1600/determine_size.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-9EpZpQQQk_g/UH2o5PQn4kI/AAAAAAAAFgk/Ip5ecGrvDSU/s1600/determine_size.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-05/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L190">determineBestSize() - CameraFragment.java</a></td></tr></tbody></table>We are using a threshold for the preview and picture size to save some heap space for the bitmap transformations we'll do later. For now these limits are 640x480 for the preview size and 1280x960 for the &nbsp;picture size.</div><div><br />Finally the determined values are used to setup the camera object:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-aeWvWYDnT2s/UH2uJVZkAFI/AAAAAAAAFg4/puouCbK7VOQ/s1600/setup_camera.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-aeWvWYDnT2s/UH2uJVZkAFI/AAAAAAAAFg4/puouCbK7VOQ/s1600/setup_camera.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-05/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L166">setupCamera() - CameraFragment.java</a></td></tr></tbody></table><br />The CameraPreview component will be used as view of our CameraFragment by returning it in <a href="http://developer.android.com/reference/android/app/Fragment.html#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)">onCreateView()</a>. In addition to that our CameraFragment needs to implement <a href="http://developer.android.com/reference/android/view/SurfaceHolder.Callback.html">SurfaceHolder.Callback</a> in order to get notified when the surface is created or destroyed.</div><div><br /></div><div><b><span style="font-size: large;">Accessing the camera</span></b></div><div><br /></div><div>To access the camera of the device we need to call <a href="http://developer.android.com/reference/android/hardware/Camera.html#open(int)">Camera.open(int cameraId)</a> to obtain a <a href="http://developer.android.com/reference/android/hardware/Camera.html">Camera</a> object. The camera ids are numbered starting with 0. As we currently don't want to support multiple cameras we will just call Camera.open(0).</div><div><br /></div><div>Unfortunately using the camera can cause undefined exceptions to be thrown. Therefore we need to wrap some code accessing the camera object into try-catch blocks and catch the generic Exception object. This is usually <a href="http://source.android.com/source/code-style.html#dont-catch-generic-exception">considered bad practice</a> but in this case <a href="http://developer.android.com/guide/topics/media/camera.html#access-camera">encouraged to do by the documentation</a>.</div><div><br /><span style="font-size: large;"><b>Sharing the camera resource</b></span><br /><br />The camera can only be used by one application at a time so we need to release the camera every time the activity gets paused. Otherwise the user would not be able to use other camera applications while our activity is in the background. In some cases not releasing the camera can lead to the CameraService crashing. If this happens no camera application can be used until the user reboots his phone. We never want this to happen.<br /><br />To be safe we will open the camera by calling Camera.open() in the <a href="https://github.com/pocmo/Instant-Mustache/blob/article-05/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L70">onResume()</a> method of the CameraFragment and releasing it again in <a href="https://github.com/pocmo/Instant-Mustache/blob/article-05/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L87">onPause()</a>.<br /><br />Once we have a Camera object and our surface is created we can assign the surface to the camera object and start the preview. We wrap the call to startPreview() in our own method called <a href="https://github.com/pocmo/Instant-Mustache/blob/article-05/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L97">startCameraPreview()</a> inside our fragment. This way we can setup the camera object before actually starting the preview.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-KFpfQOjQJY4/UIA567dmV6I/AAAAAAAAFho/fv7wu7wsUYg/s1600/startpreview.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-KFpfQOjQJY4/UIA567dmV6I/AAAAAAAAFho/fv7wu7wsUYg/s1600/startpreview.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-05/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L97">startCameraPreview() - CameraFragment.java</a></td></tr></tbody></table><br /><br /><span style="font-size: large;"><b>Determining the display orientation</b></span><br /><br />The screen can be rotated in four different angles (0°, 90°, 180°, 270°). In addition to that the camera can also be built into the device in four different angles by the manufacturer. Finally the camera can be on the front or on the back of the device. We will need to get all these angles and tell the camera object the display orientation so that the preview will be drawn on the surface using the right rotation.<br /><br />This sounds tricky but fortunately there's a <a href="http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)">code snippet for that in the Android documentation</a>. We'll use <a href="http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)">this code snippet</a> to implement <a href="https://github.com/pocmo/Instant-Mustache/blob/article-05/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java#L126">determineDisplayOrientation()</a>.<br /><br /><b><span style="font-size: large;">Screenshot</span></b><br /><br />And that's how our app looks like so far:<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-YuVQvTJ3OU0/UIA00sECLRI/AAAAAAAAFhM/R8mHYZhRnIE/s1600/mustache_screen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-YuVQvTJ3OU0/UIA00sECLRI/AAAAAAAAFhM/R8mHYZhRnIE/s1600/mustache_screen.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">As always the&nbsp;<a href="https://github.com/pocmo/Instant-Mustache/tree/article-05">source code&nbsp;of the current version</a> of the app is available at Github.</div></div><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/C01oUsX3kSI" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com1http://www.androidzeitgeist.com/2012/10/displaying-camera-preview-instant.htmltag:blogger.com,1999:blog-1531829516427013333.post-77570194732928184942012-10-15T19:28:00.002+02:002012-10-30T23:19:35.663+01:00Using a fragment for the camera preview - Instant Mustache #4<span style="font-size: x-small;">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.&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">Click here</a><span style="font-size: x-small;">&nbsp;to get a chronological list of all published&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">articles about Instant Mustache</a><span style="font-size: x-small;">.</span><br /><br />This article will be the first one about writing the CameraActivity for <a href="http://www.androidzeitgeist.com/p/instant-mustache.html">Instant Mustache</a>. From <a href="http://www.androidzeitgeist.com/2012/08/lets-start-coding-not-instant-mustache-3.html">the last article</a> we already know how the layout of the activity should look like. Now it's time to start the coding part.<br /><br />I won't cover all the lines of code in this and the following blog articles but you can always get the complete <a href="https://github.com/pocmo/Instant-Mustache">source code of Instant Mustache at GitHub</a>.<br /><br /><b>Using a fragment</b><br />We'll move the actual code (and layout) that handles the camera preview to a <a href="http://developer.android.com/guide/components/fragments.html">fragment</a>. Using fragments has several advantages:<br /><br /><ul><li>Fragments can be reused in different activities and layouts. Therefore they are perfect building blocks for composing UIs for different screen sizes.</li><li>They encapsulate the code for a specific component including the layout of the component. This makes the activity code and layout much more simpler and cleaner. By using a bunch of fragments the activity basically only has to handle events and delegate them to the appropriate fragments. This leads to the activity becoming some kind of light meta controller instead of an unreadable dumping ground for handling everything on the current screen.</li><li>Fragments can be dynamically added, removed, replaced, added to the back stack, animated and other cool things that can be painful to do yourself.</li></ul><div><b><br />Activity layout</b></div><div><div style="margin-bottom: 0in;">As said before we are moving the camera preview code and layout to a fragment. Therefore our activity's layout is really quite simple:<br /><br /></div><div style="margin-bottom: 0in;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><span style="margin-left: auto; margin-right: auto;"><a href="http://www.blogger.com/goog_154278281"><img border="0" src="http://1.bp.blogspot.com/-9A5F03FauP0/UHxDx3aMDcI/AAAAAAAAFfE/x245XxDRKJk/s1600/camera_activity_layout_2.png" /></a></span></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-04/res/layout/activity_camera.xml">activity_camera.xml</a></td></tr></tbody></table><br /></div><div style="margin-bottom: 0in;"><b>Communication between activity and fragment</b></div><div style="margin-bottom: 0in;"></div><div style="margin-bottom: 0in;">There are situations where the fragment needs to notify the activity about events. In our case the fragment needs to tell the activity when it's unable to instantiate a camera preview so that the activity can handle this error case. Of course we could just access the activity inside the fragment using <a href="http://developer.android.com/reference/android/app/Fragment.html#getActivity()">getActivity()</a> and cast it to a CameraActivity object to get access to a method like onCameraError() but then our fragment would only be usable with this particular activity:</div><div style="margin-bottom: 0in;"><br /></div><div style="margin-bottom: 0in;"></div><div style="margin-bottom: 0in;"><span style="color: #444444; font-family: Courier New, Courier, monospace;">CameraActivity activity = (CameraActivity) getActivity();</span></div><div style="margin-bottom: 0in;"><span style="color: #444444; font-family: Courier New, Courier, monospace;">activity.onCameraError();</span></div><div style="margin-bottom: 0in;"><br /></div><div style="margin-bottom: 0in;"></div><div style="margin-bottom: 0in;">We work around this issue by defining an interface that has to be implemented by the CameraActivity and every other component that wants to use the CameraFragment:</div><div style="margin-bottom: 0in;"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/--DMu6Y45ciU/UHxBNXetSII/AAAAAAAAFe0/YrGtXiPkUXE/s1600/fragment_listener.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="color: black;"><img border="0" src="http://2.bp.blogspot.com/--DMu6Y45ciU/UHxBNXetSII/AAAAAAAAFe0/YrGtXiPkUXE/s1600/fragment_listener.png" /></span></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-04/src/com/androidzeitgeist/mustache/listener/CameraFragmentListener.java"><span style="color: black; font-size: small;">CameraFragmentListener.java</span></a></td></tr></tbody></table>Every time our fragment gets attached to an activity <a href="http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)">onAttach()</a> will be called with the activity as parameter. We use this method to enforce that the activity implements our defined interface:<br /><div style="margin-bottom: 0in;"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><span style="margin-left: auto; margin-right: auto;"><a href="http://www.blogger.com/goog_154278245"><img border="0" src="http://3.bp.blogspot.com/-hGixG23tyHc/UHxBneeyqOI/AAAAAAAAFe8/VF7FmuzO8Jk/s1600/fragment_on_attach.png" /></a></span></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="https://github.com/pocmo/Instant-Mustache/blob/article-04/src/com/androidzeitgeist/mustache/fragment/CameraFragment.java">CameraFragment.java</a></td></tr></tbody></table><div style="margin-bottom: 0in;"></div><div style="margin-bottom: 0in;">Finally take a look at the <a href="https://github.com/pocmo/Instant-Mustache/blob/article-04/src/com/androidzeitgeist/mustache/activity/CameraActivity.java">CameraActivity source code on GitHub</a>. It just sets the layout in onCreate() and shows a <a href="http://developer.android.com/guide/topics/ui/notifiers/toasts.html">toast</a> in case of the fragment calling onCameraError(). As this method will be called in case of non-recoverable errors we'll also finish the activity.</div></div><br /><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/BCc8E5OcNig" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com2http://www.androidzeitgeist.com/2012/10/using-fragment-for-camera-preview.htmltag:blogger.com,1999:blog-1531829516427013333.post-89785809975449839842012-08-20T19:02:00.001+02:002012-10-30T23:19:51.789+01:00Know your resources: Integers and BooleansAn Android application isn't only code but comes with a bunch of <a href="http://developer.android.com/guide/topics/resources/index.html">resources</a>. Probably the most important resources (besides layouts and drawables) are the string resources. In conjunction with the <a href="http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources">resource qualifiers</a> they are not only perfect to externalize strings for localization but also for defining different internal configurations of your app.<br /><br />However sometimes string resources are overused. Especially when they are used to define boolean or integer values. Let's look at the following example. Code lines like the next ones can be found in several Android projects. For example you can find <a href="https://github.com/pocmo/Yaaic/blob/master/application/src/org/yaaic/model/Settings.java#L74">some of these in Yaaic</a>.<br /><br /><div class="separator" style="clear: both; text-align: center;"></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-XLbaLF9yRos/UDJrT8HzfaI/AAAAAAAAFMk/6_ctbD-1rQ8/s1600/resources_example01.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-XLbaLF9yRos/UDJrT8HzfaI/AAAAAAAAFMk/6_ctbD-1rQ8/s1600/resources_example01.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Loading string resources and parsing as boolean or integer</td></tr></tbody></table><br />Or even worse in a condition without casting, doing a string comparison:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-IfOUvr3e9og/UDJrri2Gu3I/AAAAAAAAFMs/qB5wMlRTBzI/s1600/resources_example02.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-IfOUvr3e9og/UDJrri2Gu3I/AAAAAAAAFMs/qB5wMlRTBzI/s1600/resources_example02.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Loading boolean as string resource and doing string comparison</td></tr></tbody></table><br />The code above is not wrong but instead of using string resources and parsing or comparing them it would be easier to use resources of the appropriate type. Fortunately Android allows to&nbsp;define <a href="http://developer.android.com/guide/topics/resources/more-resources.html#Bool">boolean</a> and <a href="http://developer.android.com/guide/topics/resources/more-resources.html#Integer">integer</a> resources too (since API level 1). It's basically the same process as defining a string resource in XML just use the bool and integer tags:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-BIkUDBXNXFU/UDJr4uQMhTI/AAAAAAAAFM0/39d0Vj3jIKo/s1600/xml_resources.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-BIkUDBXNXFU/UDJr4uQMhTI/AAAAAAAAFM0/39d0Vj3jIKo/s1600/xml_resources.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Defining boolean and integer resources in XML</td></tr></tbody></table><br />After that you can access these resources in code. However there are no <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/content/Context.java#304">alias methods</a> on the <a href="http://developer.android.com/reference/android/content/Context.html">Context</a> object like for string resources. Therefore you need to get a <a href="http://developer.android.com/reference/android/content/res/Resources.html">Resources</a> instance first and then ask it for the defined resources.<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-bLP5ceuA0xY/UDOhAxoQunI/AAAAAAAAFNM/ONwuHscnEDw/s1600/accessing_resources.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-bLP5ceuA0xY/UDOhAxoQunI/AAAAAAAAFNM/ONwuHscnEDw/s1600/accessing_resources.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Accessing integer and boolean resources from code</td></tr></tbody></table><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/SH5Bn82Meao" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com1http://www.androidzeitgeist.com/2012/08/know-your-resources-integers-and.htmltag:blogger.com,1999:blog-1531829516427013333.post-75676943030018017872012-08-01T21:01:00.002+02:002012-10-30T23:20:51.695+01:00Let's start coding ... NOT! - Instant Mustache #3<span style="font-size: x-small;">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.&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">Click here</a><span style="font-size: x-small;">&nbsp;to get a chronological list of all published&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">articles about Instant Mustache</a><span style="font-size: x-small;">.</span><br /><br />In the <a href="http://www.androidzeitgeist.com/p/instant-mustache.html">last two articles</a> about Instant Mustache we've talked a lot and it's about time to start coding! Yet I’ve to disappoint you for now. There still will be no code in this article. Before we’ll start coding we’ve to define what we actually need or want. From the last article we already know there will be two screens. But what we don’t know yet is how they should look like. So how will we figure out how the (very simple) UI will look like? Of course we can fire up our code editor or visual editor but there’s a much faster way to get results: Good old paper.<br /><br />I won’t talk about paper prototypes or UI development and user testing here. But there’s a better resource for that. The <a href="http://www.androiduipatterns.com/">Android UI Patterns blog</a> has a lot of articles published covering a variety of Android UI topics. Here we are going to just sketch some screens, play around and hopefully come up with an idea of how the app will feel like when it’s finally developed. As said before I'll use paper for that. Other people may like to use software for creating mockups (<a href="http://www.fluidui.com/">Fluid UI</a> seems to be a nice online tool for that). It's up to you what works the best for you. For me paper is the easiest way to try different ideas without the limitations of a software tool. But the important thing is: We won't write a lot of code that we need to throw away again after we realize that our initial idea may not be as good as we thought.<br /><br />The following photo shows a bunch of discarded drawings I came up with during brainstorming the UI:<br /><br /><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-vLyg3OA870M/UBlqUI8HXhI/AAAAAAAAEeA/E3oDqTCMi9o/s1600/screens.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="176" src="http://2.bp.blogspot.com/-vLyg3OA870M/UBlqUI8HXhI/AAAAAAAAEeA/E3oDqTCMi9o/s400/screens.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"></div><b>The camera screen (CameraActivity)</b><br />The first screen the user will see after launching the app will be the camera screen. There are two main components we need: A camera preview and a button to take a photo. In addition to that we may want to reserve some space for future features like switching between cameras and mustaches.<br /><br />Let's take a look at the the final version of the camera screen as I've drawn it:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-ACfaRYTWXiQ/UBlrUkZlmPI/AAAAAAAAEe4/c5nw7R2I3EM/s1600/final_camera_activity.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-ACfaRYTWXiQ/UBlrUkZlmPI/AAAAAAAAEe4/c5nw7R2I3EM/s1600/final_camera_activity.jpg" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">CameraActivity</td></tr></tbody></table>The following decisions I've made during drawing and experimenting:<br /><br /><ul><li>I want the app to have an <a href="http://developer.android.com/design/patterns/actionbar.html">ActionBar</a>. To be consistent I also want the ActionBar in the camera screen. I don't know yet if it's useful to show the app title "Instant Mustache" in the ActionBar as I've drawn it. It's quite long and may take up too much space. Furthermore the overflow menu drawn here may not be visible as we have no menu entries defined yet.</li><li>The camera picture will be a square. We'll have to define a picture ratio and a square may fit quite nice into the screen. The darker areas on the screen will grow or shrink depending on the screen size of the device and always center the camera preview.</li><li>There will be a bar at the bottom of the screen which will house the button for taking a picture. Here we will have enough space for adding more functionality in later releases.</li><li>Almost all activities showing a camera preview are fixed in their orientation. While moving the phone around to take a photo you don't want the phone to destroy the current activity, do the rotation and re-create the activity. Especially because the camera picture will be oriented correctly anyways because you are rotating the camera inside the phone as well. To satisfy the previous points we will fixate the orientation to portrait mode.</li></ul><br /><b>The photo screen (PhotoActivity)</b><br />After taking a photo you will see another screen showing the photo you've taken and you'll have the option to share the photo with other apps on your phone.<br /><br />That's how my final version of the photo screen looks like:<br /><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody><tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-ZkXDFTYoBTs/UBlv6nT4mRI/AAAAAAAAEfI/RGFW1HHcngE/s1600/final_share_activity.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-ZkXDFTYoBTs/UBlv6nT4mRI/AAAAAAAAEfI/RGFW1HHcngE/s1600/final_share_activity.jpg" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">PhotoActivtiy</td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The first version of this screen will be really simple:</div><div class="separator" style="clear: both; text-align: left;"></div><ul><li>At the top we'll have the ActionBar again. To share the photo we'll add a <a href="http://developer.android.com/guide/topics/ui/actionbar.html#ActionProvider">ShareActionProvider</a> that shows a list of available share targets as well as the most used app as an icon right beside it.</li><li>Below the ActionBar we'll show the taken photo and that's everything for now.</li></ul><div><b>What's next?</b></div><div>Now we've defined how our two screens will look like. In the next article we are going to launch our IDE and start writing the CameraActivity. I am curious to know what you think about the UI as described here and if you have other ideas. Let me know in the comments or on Google+.</div><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/xRHDRu4wROY" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com2http://www.androidzeitgeist.com/2012/08/lets-start-coding-not-instant-mustache-3.htmltag:blogger.com,1999:blog-1531829516427013333.post-26310402606800882312012-07-03T20:08:00.003+02:002012-10-30T23:21:12.300+01:00Minimal marketable app - Instant Mustache #2<span style="font-size: x-small;">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.&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">Click here</a><span style="font-size: x-small;">&nbsp;to get a chronological list of all published&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-size: small;">articles about Instant Mustache</a><span style="font-size: x-small;">.</span><br /><b><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span></b><b id="internal-source-marker_0.039026354206725955"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">In <a href="http://www.androidzeitgeist.com/2012/06/instant-mustache-1-idea.html">the last article</a> I described roughly the idea behind Instant Mustache. Now it's time to get more concrete. While doing so we try to follow the principle of the minimal marketable feature (MMF).</span></b><br /><br /><b><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">Minimal marketable feature</span></b><br /><span id="internal-source-marker_0.039026354206725955"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">The idea behind the minimal marketable feature is to strip all aspects of a feature that are not necessarily needed to end up with a useful (or marketable) feature. After that you'll end up with smaller features that are easier and faster to develop. This is only a very rough explanation of MMF. You can find a way better <a href="http://www.upstarthq.com/2010/04/introduction-to-minimum-marketable-features-mmf/">Introduction to Minimum marketable feature</a> at the upstart blog.</span></span><br /><br /><b style="background-color: white; font-family: Arial; font-size: 15px; white-space: pre-wrap;">Minimal marketable app</b><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">In this case we are slightly modifying this idea to end up with something like a </span><span style="font-family: Arial; font-size: 15px; font-weight: bold; vertical-align: baseline; white-space: pre-wrap;">minimal marketable app</span><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">. This means we are going one level higher and strip all features that are not essentially required for a first releasable app. This does not mean that we won't develop these features at all but our first version will be without them enabling us to ship the app as early as possible. </span><br /><span style="background-color: white; font-family: Arial; font-size: 15px; white-space: pre-wrap;">Let's continue with an example. When you think about the app (<a href="http://www.androidzeitgeist.com/2012/06/instant-mustache-1-idea.html">see first article</a>) you will very fast come up with an idea like: The user should be able to select between a bunch of mustaches - more mustaches = more fun. But do we really need this feature when we launch the app? No, we don't. Our fun app will still be fun with one single mustache. Of course this is one of the first features we should have in the next release after the initial one.</span><br /><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Another feature that's easy to come up with is switching between the cameras of a device. In most cases this means switching between back and front camera. Again for our basic app it's totally acceptable to strip this feature in the first release. The users of our app won't be able to take pictures of themselves easily and therefore they won't have much fun when there's no mustache-less friend to photograph around. Therefore we should rank this feature high as well to implement it right after the first release.</span><br /><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">In our case the minimal marketable feature set is an app that has two screens: A camera screen - with a camera preview and a button to take a picture - and a second screen that shows the picture you took and has an option to share this picture. The camera preview will show a live preview. Every detected face on the live preview will automatically have a mustache overlayed. If you take a picture the second screen will show the composed picture including the added mustaches. The user will be able to share the picture via the <a href="http://android-developers.blogspot.co.uk/2012/02/share-with-intents.html">Android intent system</a>.</span><br /><br /><b><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">Roadmap and Milestones</span></b><br /><b id="internal-source-marker_0.039026354206725955"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Nothing is more motivating than playing around with what you have created. In contrast nothing is more killing motivation than programming for ages without coming up with something that runs at all.</span><span style="vertical-align: baseline;"> </span><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">This leads us to the goal to always have a running prototype that step by step gets features added until it's ready to ship. So let's break the functionality into parts that can be implemented in order and always leave us with a working prototype that we can deploy on our phone and use at the next party.</span></b><br /><ul><li><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><b>Milestone #1</b>: </span></span><b id="internal-source-marker_0.039026354206725955"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">A camera screen that shows the camera preview and has a button to take a photo. The photos will be saved on the SD card. After that we will end up with a basic camera app that we can use to take photos on the go. Goodbye native camera app!</span></b></li><li><b><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">Milestone #2</span><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">: </span></b><b id="internal-source-marker_0.039026354206725955"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">We add the second screen. After taking a photo we’ll switch to this second screen which shows the taken photo and has an option to share the photo.</span></b></li><li><b><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">Milestone #3</span><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">: </span></b><b id="internal-source-marker_0.039026354206725955"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">It’s time to add the face tracking. We’ll use the face tracking and display mustaches for every face on top of the camera preview.</span></b></li><li><b><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">Milestone #4</span><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">: </span></b><b id="internal-source-marker_0.039026354206725955"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">When taking a photo we’ll compose the final photo using the information of the face tracking. The composed photo will contain mustaches like the preview screen before. The composed photo will be saved on the SD card and shown in the second screen. We are ready to release!</span></b></li></ul><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/D7VVl5yQcUE" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com2http://www.androidzeitgeist.com/2012/07/minimal-marketable-app-instant-mustache.htmltag:blogger.com,1999:blog-1531829516427013333.post-25713927705088424512012-06-08T12:14:00.002+02:002012-10-30T23:21:30.173+01:00Creating a MarqueeLayout with the Android Animation SystemWhen I posted the article about <a href="http://www.androidzeitgeist.com/2012/05/curious-blinklayout.html">the hidden BlinkLayout</a> inside Android’s LayoutInflater <a href="https://plus.google.com/105344175486242358933/posts">Thierry-Dimitri Roy</a> wrote on Google+:<br /><br /><a href="http://2.bp.blogspot.com/-kpn1WJrtNRw/T9CZQxI269I/AAAAAAAADnM/Pxbd7NWhWkM/s1600/gpluscomment.png" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://2.bp.blogspot.com/-kpn1WJrtNRw/T9CZQxI269I/AAAAAAAADnM/Pxbd7NWhWkM/s1600/gpluscomment.png" /></a><br /><br />That’s a challenge I accept! Back in the days when I worked at Jimdo we developed “<a href="http://www.youtube.com/watch?v=RDNhra-6dQo">a lifeboat for GeoCities</a>” which allowed users to migrate their GeoCities website to Jimdo before GeoCities finally shut down in 2009. So I’ve some kind of heart for&nbsp;GeoCities&nbsp;users.<br /><br />The <a href="http://developer.android.com/guide/topics/graphics/view-animation.html">view animation system</a> of Android makes it quite easy to develop something like a MarqueeLayout. All we need to do is to extend an existing ViewGroup and attach a marquee-like animation to every instance.<br /><br />The <a href="http://developer.android.com/reference/android/view/animation/TranslateAnimation.html">TranslateAnimation</a> does already exactly what we need: It moves a view from a starting position to an ending position. The two positions can either be declared absolute (position on the screen) or relative to the view or its parent. After that we only need to define the duration of the animation and Android will do the rest for us.<br /><br />We'll define the start and end positions of the animation by using coordinates relative to the view. When using relative coordinates you can think of our view having a size of 1x1 and it's top left corner placed at (0,0).<br /><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-002il9hIkbs/T9CiYixSw3I/AAAAAAAADnY/-04SitxZqVg/s1600/view.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-002il9hIkbs/T9CiYixSw3I/AAAAAAAADnY/-04SitxZqVg/s1600/view.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">View of size 1x1 at position (0,0)</td></tr></tbody></table><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">If our view's width stretches to the whole width of the screen we can consider the screen's size also 1 (relative to the view's width).</div><div class="separator" style="clear: both; text-align: left;"><br /></div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-a8TAfL-nbQs/T9ClZz9tuxI/AAAAAAAADnk/qwDhuGD7DRU/s1600/view_on_screen.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-a8TAfL-nbQs/T9ClZz9tuxI/AAAAAAAADnk/qwDhuGD7DRU/s1600/view_on_screen.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">View (green) filling full width of screen (grey)</td></tr></tbody></table><div class="separator" style="clear: both; text-align: left;"><br /></div><div style="text-align: left;">Assuming a screen size of 1 and a starting position on the right side outside of the screen gives us a relative starting position of (1,0). This will place our view exactly outside the screen (or it's parent).</div><div style="text-align: left;"><br /></div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-wuSTX1TEt88/T9Cl3Z4JaxI/AAAAAAAADns/orVD4QJqXMg/s1600/start_at_1_0.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="231" src="http://2.bp.blogspot.com/-wuSTX1TEt88/T9Cl3Z4JaxI/AAAAAAAADns/orVD4QJqXMg/s320/start_at_1_0.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">View animation starting at (1,0)</td></tr></tbody></table><div class="separator" style="clear: both; text-align: left;"><br /></div><div style="text-align: left;">The view should move from the starting position to the left until it's outside of the screen at (-1, 0).</div><div style="text-align: left;"><br /></div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-NOoImxh16nw/T9CmIXQJRqI/AAAAAAAADn0/OF_7nGDw9Pw/s1600/end_-1_0_1.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="231" src="http://4.bp.blogspot.com/-NOoImxh16nw/T9CmIXQJRqI/AAAAAAAADn0/OF_7nGDw9Pw/s320/end_-1_0_1.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">View animation stopping at (-1,0)</td></tr></tbody></table><div class="separator" style="clear: both; text-align: left;"><br /></div><div style="text-align: left;">The animation system will generate all transient positions needed for the animation. Given all the information above we can write a simple MarqueeLayout in a few lines:</div><div style="text-align: left;"><br /></div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><span style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><a href="http://www.blogger.com/goog_1690609543"><img border="0" src="http://2.bp.blogspot.com/-OaTBb9uyHZ4/T9Cox5IvbMI/AAAAAAAADoA/bD9eXxmXu-Y/s1600/marquee_layout_code.png" /></a></span></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://source.androidzeitgeist.com/raw/marquee_layout_code.txt">MarqueeLayout.java</a></td></tr></tbody></table><div class="separator" style="clear: both; text-align: left;"><br /></div><div style="text-align: left;">That's what our MarqueeLayout looks like when adding a TextView to it:</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;"><iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/r1LT2EpIB0s" width="480"></iframe></div><br /></div><div style="text-align: left;">The following code was used to set up an activity using our MarqueeLayout:</div><div style="text-align: left;"><br /></div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><span style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><a href="http://www.blogger.com/goog_1690609550"><img border="0" src="http://3.bp.blogspot.com/-VzGJqlPup70/T9CpDx20HUI/AAAAAAAADoI/iw61M_02ZuQ/s1600/marquee_activity.png" /></a></span></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://source.androidzeitgeist.com/raw/marquee_layout_activity.txt">MarqueeLayoutActivity.java</a></td></tr></tbody></table><div style="text-align: left;"><br /></div><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/5ogW6KCmbFc" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com1http://www.androidzeitgeist.com/2012/06/creating-marqueelayout-with-android.htmltag:blogger.com,1999:blog-1531829516427013333.post-89855408824596290062012-06-05T20:33:00.000+02:002012-10-30T23:21:46.957+01:00Instant Mustache #1 - The idea<span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"><span style="font-family: 'Times New Roman'; font-size: x-small; white-space: normal;">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.&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">Click here</a><span style="font-family: 'Times New Roman'; font-size: x-small; white-space: normal;">&nbsp;to get a chronological list of all published&nbsp;</span><a href="http://www.androidzeitgeist.com/p/instant-mustache.html" style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">articles about Instant Mustache</a><span style="font-family: 'Times New Roman'; font-size: x-small; white-space: normal;">.</span></span><br /><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"><b><br /></b></span><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"><b>The idea</b></span><br /><span id="internal-source-marker_0.8338896373752505"><span style="font-family: Arial; vertical-align: baseline;"><span style="font-size: 15px; white-space: pre-wrap;">Instant Mustache is the name of an app that I'm going to develop. The idea is to show the development process of an app starting from a rough idea to a full featured app in the Android market. I'll demonstrate the process of developing the app as well as the thoughts and decisions involved. The source code will be publicly available on </span><a href="http://github.com/pocmo/Instant-Mustache" style="font-size: 15px; font-weight: normal; white-space: pre-wrap;">GitHub</a><span style="font-size: 15px; white-space: pre-wrap;">.</span></span></span><br /><span id="internal-source-marker_0.8338896373752505"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Later versions of this series of articles are expected to be about topics like <i>refactoring</i>, adding <i>features</i>, <i>testing</i> and maybe <i>monetization</i>. I'll try to make everything as transparent as possible including user statistics of the Android market.</span></span><br /><br /><b><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">The app</span></b><br /><b id="internal-source-marker_0.8338896373752505"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Instant Mustache is a fun app that utilizes the face tracking feature of Android 4.x to add mustaches to all detected faces currently visible to the camera. Users will be able to take funny pictures and share them with their friends on social networks or other apps on their mobile phone. The user shouldn't need to place the mustaches herself. Instead the placing should be done automatically for every detected face.</span></b><br /><div><span id="internal-source-marker_0.8338896373752505"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">This is a rough idea every developer can come up with. In the following postings we will refine the idea, plan the app with some basic wireframes and develop an elementary version that can be extended to match our planned full-featured app later.</span></span></div><div><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span></div><div><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><b>Do not forget the napkin</b></span></span></div><div><b id="internal-source-marker_0.8338896373752505"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">Of course as a good entrepreneur we had this world-changing idea on the go and had only time to sketch it on a napkin to not forget it later.</span><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">So here is the napkin we came up with:</span></b></div><div class="separator" style="clear: both; text-align: left;"></div><div><div class="separator" style="clear: both; text-align: left;"><a href="http://1.bp.blogspot.com/-fF1hf6-tlzA/T8ZmAQiTOzI/AAAAAAAADb4/ADS5E1ODlLU/s1600/instant_mustache_tissue.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="200" src="http://1.bp.blogspot.com/-fF1hf6-tlzA/T8ZmAQiTOzI/AAAAAAAADb4/ADS5E1ODlLU/s200/instant_mustache_tissue.jpg" width="200" /></a></div><b><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span></b></div><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/QNOIAC4E51A" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com2http://www.androidzeitgeist.com/2012/06/instant-mustache-1-idea.htmltag:blogger.com,1999:blog-1531829516427013333.post-90650865724884677382012-05-27T19:01:00.002+02:002012-10-30T23:22:02.346+01:00The curious BlinkLayout<span id="internal-source-marker_0.19517051335424185"><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">While reading the source code of Android's <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.3_r1/android/view/LayoutInflater.java#LayoutInflater">LayoutInflater class</a> I found a hidden gem that seems to be quite unnoticed yet. Ladies and gentlemen I present you the mighty <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.3_r1/android/view/LayoutInflater.java#LayoutInflater.BlinkLayout">BlinkLayout</a>. Views that are placed inside this ViewGroup blink at a rate of 500ms.</span><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">The BlinkLayout is an inner class of the LayoutInflater and can therefore only be used in a XML layout that will be parsed by a LayoutInflater instance. Due to the implementation it's only possible to use it as root node using the &lt;blink&gt; tag inside a XML layout.</span><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">It seems like the BlinkLayout is available since Android 4.0 and isn't used in the Android source code I observed. Maybe it was added for debugging reasons and was forgotten later.</span></span><br /><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span><div class="separator" style="clear: both; text-align: center;"><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/o_EX1mH5ZvM/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/o_EX1mH5ZvM?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /> <param name="bgcolor" value="#FFFFFF" /> <embed width="320" height="266" src="http://www.youtube.com/v/o_EX1mH5ZvM?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash"></embed></object></div><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;">T</span><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">he video above shows the BlinkLayout in action using the following layout XML:</span><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-2PBd4Bdwjr8/T8I-BukTXnI/AAAAAAAADZs/_z-Ajl7hOlo/s1600/blinklayout_xml.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img alt="" border="0" src="http://1.bp.blogspot.com/-2PBd4Bdwjr8/T8I-BukTXnI/AAAAAAAADZs/_z-Ajl7hOlo/s1600/blinklayout_xml.png" title="" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://source.androidzeitgeist.com/raw/blinklayout_view_xml.txt">main_layout.xml</a></td></tr></tbody></table><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"></span><br /><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"></span><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">T</span><span style="font-family: Arial; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;">he following snippet was used to inflate the layout and pass it to the activity:</span><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody><tr><td style="text-align: center;"><span style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><a href="http://www.blogger.com/goog_899898043"><img border="0" src="http://1.bp.blogspot.com/-zgP0dePQwzw/T8JIp8nqZ0I/AAAAAAAADaY/ZP7LER-JmM0/s1600/blinklayout_code.png" /></a></span></td></tr><tr><td class="tr-caption" style="text-align: center;"><a href="http://source.androidzeitgeist.com/raw/blinklayout_activity_code.txt">BlinkLayoutActivity.java</a><br /><br /></td></tr></tbody></table><div style="text-align: left;"><br /></div><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span><br /><span style="font-family: Arial;"><span style="font-size: 15px; white-space: pre-wrap;"><br /></span></span><span style="font-family: Arial; font-size: 15px; font-weight: normal; vertical-align: baseline; white-space: pre-wrap;"><br /></span><img src="http://feeds.feedburner.com/~r/AndroidZeitgeist/~4/1MleyCfQAow" height="1" width="1" alt=""/>Sebastian Kasparihttps://plus.google.com/112283223674539938062noreply@blogger.com2http://www.androidzeitgeist.com/2012/05/curious-blinklayout.html