{"id":1700,"date":"2013-09-22T15:15:59","date_gmt":"2013-09-22T03:15:59","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=1700"},"modified":"2013-09-22T15:15:59","modified_gmt":"2013-09-22T03:15:59","slug":"honourable-intentions","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/1700\/","title":{"rendered":"Honourable Intentions"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">10<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>This is it.  The home straight.  The final part of my series on writing a camera app using Oxygene for Android.  In this concluding part I shall look at making my application a well behaved Android citizen.  As well as pointing out (and fixing) some mistakes I have made along the way, we will add a new capability.<\/p>\n<p><!--more--><\/p>\n<h3>Models of Re-Use<\/h3>\n<p>A lot of the code written so far could easily be put into a library to be called much more conveniently in the future, and that might be a good idea if we intended writing lots of camera apps.<\/p>\n<p>But why would we want to do that ?<\/p>\n<p>I suppose we might have another app in the future where we also needed to be able to capture pictures from the camera.  But what if the user of our camera app really, really likes using our app to capture their photos ?  We wouldn&#8217;t want to duplicate the entire app every time.<\/p>\n<p>Android has a feature designed specifically to allow an application to provide services directly to other applications that wish to take advantage.  I am sure you can think of any number of applications that include the ability to take a photo from the device camera.  The <strong>Google+<\/strong> app is one.<\/p>\n<p><em><strong>NOTE:<\/strong> The <strong>FaceBook<\/strong> Android app does <strong>not<\/strong> appear to use intents.  When you post a photo from the <strong>FaceBook<\/strong> app it uses an internal camera function, ignoring <strong>any<\/strong> camera apps on your device.<\/em><\/p>\n<p>In the <strong>Google+<\/strong> app a user can directly share a new photo by selecting &#8220;<strong>Photo<\/strong>&#8221; from the main app screen:<\/p>\n<figure id=\"attachment_1731\" aria-describedby=\"caption-attachment-1731\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Main.jpg?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Main.jpg?resize=500%2C312&#038;ssl=1\" alt=\"Post a photo from Google+\" width=\"500\" height=\"312\" class=\"size-large wp-image-1731\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Main.jpg?resize=1024%2C640&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Main.jpg?resize=300%2C187&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Main.jpg?resize=480%2C300&amp;ssl=1 480w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Main.jpg?w=1280&amp;ssl=1 1280w\" sizes=\"(max-width: 500px) 100vw, 500px\" data-recalc-dims=\"1\" \/><\/a><figcaption id=\"caption-attachment-1731\" class=\"wp-caption-text\">Post a photo from Google+<\/figcaption><\/figure>\n<p>This then presents the <strong>Google+<\/strong> image gallery, and in the top right is an action bar button to capture a new image from the camera:<\/p>\n<figure id=\"attachment_1732\" aria-describedby=\"caption-attachment-1732\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Post-Photo.jpg?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Post-Photo.jpg?resize=500%2C312&#038;ssl=1\" alt=\"Capture new photo with camera...\" width=\"500\" height=\"312\" class=\"size-large wp-image-1732\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Post-Photo.jpg?resize=1024%2C640&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Post-Photo.jpg?resize=300%2C187&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Post-Photo.jpg?resize=480%2C300&amp;ssl=1 480w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Google+-Post-Photo.jpg?w=1280&amp;ssl=1 1280w\" sizes=\"(max-width: 500px) 100vw, 500px\" data-recalc-dims=\"1\" \/><\/a><figcaption id=\"caption-attachment-1732\" class=\"wp-caption-text\">Capture new photo with camera&#8230;<\/figcaption><\/figure>\n<p>Selecting this takes us to the camera app on the device:<\/p>\n<figure id=\"attachment_1733\" aria-describedby=\"caption-attachment-1733\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Default-Camera-App.jpg?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Default-Camera-App.jpg?resize=500%2C312&#038;ssl=1\" alt=\"The default camera app...  pfft.  We can do better than that!\" width=\"500\" height=\"312\" class=\"size-large wp-image-1733\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Default-Camera-App.jpg?resize=1024%2C640&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Default-Camera-App.jpg?resize=300%2C187&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Default-Camera-App.jpg?resize=480%2C300&amp;ssl=1 480w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Default-Camera-App.jpg?w=1280&amp;ssl=1 1280w\" sizes=\"(max-width: 500px) 100vw, 500px\" data-recalc-dims=\"1\" \/><\/a><figcaption id=\"caption-attachment-1733\" class=\"wp-caption-text\">The default camera app&#8230;  pfft.  We can do better than that!<\/figcaption><\/figure>\n<p>But I really, <em>really<\/em> like <strong>my<\/strong> app.  It&#8217;s <em>soooo<\/em> much better than the default camera app.  <em>*cough*<\/em>.   I&#8217;d like to be able to use <strong>my<\/strong> app to take the picture, even when using the <strong>Google+<\/strong> app (which of course knows nothing about my app).<\/p>\n<p>Fortunately <strong>Android<\/strong> supports this through a system called <strong>Intents<\/strong>.<\/p>\n<h3>State Your Intentions<\/h3>\n<p>An application can invoke a service by stating that it requires a particular intent to be satisfied.  Other applications can indicate the intents that they are capable of satisfying in their manifest.<\/p>\n<p>When an application makes an intent request, the system checks to see what applications are available.  If there is <strong>only<\/strong> one then this will be called into action.  If there is <strong>more<\/strong> than one, then the user is asked which they would prefer to use.<\/p>\n<p>If there are <strong>none<\/strong>, then the application making the request has to have some fall-back position and either tell the user they need to acquire a suitable app or provide at least a basic facility to meet the need itself.<\/p>\n<p>I won&#8217;t be asking for intents in my camera app, but I will be <strong>satisfying<\/strong> an intent.<\/p>\n<p>Intents have names, and in this case the intent is <strong>android.media.action.IMAGE_CAPTURE<\/strong>.<\/p>\n<p>The first step is to add an entry to the application manifest so that the system knows our application can satisfy this intent.  This is called an <strong>Intent Filter<\/strong>.  We add the intent-filter entry to the Activity that we intend to be invoked to satisfy the intent upon request.  In our simple app, this is the <strong>MainActivity<\/strong>:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n  &lt;activity android:label=&quot;@string\/app_name&quot; android:name=&quot;nz.co.deltics.demo.camera.MainActivity&quot;&gt;\r\n   &lt;intent-filter&gt;\r\n    &lt;action android:name=&quot;android.intent.action.MAIN&quot; \/&gt;\r\n    &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; \/&gt;\r\n   &lt;\/intent-filter&gt;\r\n   &lt;intent-filter&gt;\r\n    &lt;action android:name=&quot;android.media.action.IMAGE_CAPTURE&quot; \/&gt;\r\n    &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; \/&gt;\r\n   &lt;\/intent-filter&gt;\r\n  &lt;\/activity&gt;\r\n<\/pre>\n<p>Suddenly the existing intent-filter entry that we perhaps took for granted makes a bit more sense.  <strong>android.intent.action.MAIN<\/strong> identifies <strong>MainActivity<\/strong> as the activity in our application that is our application&#8217;s <strong>MAIN<\/strong>, or Home screen, to be used when launching the app.<\/p>\n<p>Now we have identified that this same Activity is to be used when we respond to a request to capture an image.<\/p>\n<p>If our application was more complex, we might have a different <strong>Activity<\/strong> for image capture, in addition to and separate from our <strong>MainActivity<\/strong>.  The system will know that when it launches our application to take a picture it should launch directly into the image capture activity instead of our app &#8220;<em>Home<\/em>&#8221; screen.<\/p>\n<p>With this declaration in our manifest (and my application installed on the device, obviously) when I select the &#8220;Take a Photo&#8221; action in Google+, my app is now presented as an option:<\/p>\n<figure id=\"attachment_1734\" aria-describedby=\"caption-attachment-1734\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Choose-Your-App.jpg?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Choose-Your-App.jpg?resize=500%2C312&#038;ssl=1\" alt=\"Choose your weapon app !\" width=\"500\" height=\"312\" class=\"size-large wp-image-1734\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Choose-Your-App.jpg?resize=1024%2C640&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Choose-Your-App.jpg?resize=300%2C187&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Choose-Your-App.jpg?resize=480%2C300&amp;ssl=1 480w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Choose-Your-App.jpg?w=1280&amp;ssl=1 1280w\" sizes=\"(max-width: 500px) 100vw, 500px\" data-recalc-dims=\"1\" \/><\/a><figcaption id=\"caption-attachment-1734\" class=\"wp-caption-text\">Choose your <del>weapon<\/del> app !<\/figcaption><\/figure>\n<p>At this point if I select my app it will launch as normal and I can use it to take a photo, but <strong>Google+<\/strong> won&#8217;t know anything about it when I do.<\/p>\n<p>I need to make some changes to my app to make it co-operate with the intents system.<\/p>\n<h3>Just What <strong>Is<\/strong> Your Intention ?<\/h3>\n<p>The first step is identifying the intent with which our app was launched.  If you have no specific filters other than the ability to be launched, you can probably just assume this is the intent.  But my app now has two possible reasons to be launched.  Two intents.<\/p>\n<p>Most of the functionality of my app is the same, regardless of the intent.  But there we will need to adapt some of the behaviour if launched with the <strong>IMAGE_CAPTURE<\/strong> intent, so we need to be able to identify that.<\/p>\n<p>To test for a specific intent action we can simply write:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  if Intent.Action = MediaStore.ACTION_IMAGE_CAPTURE then\r\n<\/pre>\n<p>This is another example of <strong>Oxygene<\/strong> presenting a &#8220;<em>getter<\/em>&#8221; method as a property, in this case &#8220;<strong>Intent<\/strong>&#8221; which actually corresponds to the <strong>getIntent()<\/strong> method that our <strong>MainActivity<\/strong> inherits from the <strong>Activity<\/strong> class.<\/p>\n<p><em><strong>NOTE:<\/strong> The <strong>MediaStore<\/strong> class is in the android.provider namespace so this was added to the uses list of the unit.  I think I may have forgotten to mention some of the required namespaces that I have introduced along the way.  Apologies for that if you&#8217;ve been trying to follow along.<\/em><\/p>\n<p>The <strong>MediaStore.ACTION_IMAGE_CAPTURE<\/strong> constant took a bit of tracking down.  Many action identifiers are <a href=\"http:\/\/developer.android.com\/reference\/android\/content\/Intent.html#ACTION_MAIN\">constants of the <strong>Intent<\/strong> class<\/a>, but since <strong>Intent<\/strong> actions can be introduced and defined as needed, these are not an exhaustive list.<\/p>\n<p>This does mean however that if we devised some entirely new application service that other applications might find useful that is not already defined by the <strong>Android<\/strong> system, we can filter for our own action and simply document the action name that other applications developers should use if they wish to use that intent.<\/p>\n<h3>Satisfying An Intent<\/h3>\n<p>When responding to a particular intent it isn&#8217;t very useful if you are not doing what the intent, um, intends.  Sometimes this is implicit in the action itself, but in other cases an action may be accompanied by additional data that an application is expected to take into account when responding.  When provided, this additional data is supplied in an <strong>Extras<\/strong> object, as part of the <strong>Intent<\/strong>.<\/p>\n<p>This should be described as part of the documentation for the intent action itself.  In the case of the <a href=\"http:\/\/developer.android.com\/reference\/android\/provider\/MediaStore.html#ACTION_IMAGE_CAPTURE\"><strong>MediaStore.ACTION_IMAGE_CAPTURE<\/strong><\/a> action there is an optional <strong>EXTRA_OUTPUT<\/strong> item that may or may not be supplied.  This means we have to allow for two possibilities:<\/p>\n<ul>\n<li>No <strong>EXTRA_OUTPUT<\/strong> &#8211; return a thumbnail<\/li>\n<li><strong>EXTRA_OUTPUT<\/strong> supplied &#8211; place the capture image in the specified location<\/li>\n<\/ul>\n<p>I&#8217;m going to cheat a little here.<\/p>\n<p>I know, from inspecting the <strong>Intent<\/strong>.<strong>Extras<\/strong> in the debugger, that the <strong>Google+<\/strong> app is one that passes an <strong>EXTRA_OUTPUT<\/strong> item, so I shall implement only this response and leave the alternate response as a later exercise (perhaps for a future article).<\/p>\n<p>So when responding to this intent, the two changes I need to make are:<\/p>\n<ul>\n<li>Store the image in the file specified by the <strong>Uri<\/strong> in <strong>EXTRA_OUTPUT<\/strong><\/li>\n<li>Notify the calling activity that the intent response is available and return to that calling activity<\/li>\n<\/ul>\n<p>These are both in the <strong>captureClick()<\/strong> method implementation and both are relatively simple, though the first is a bit messy (but the sort of mess that is easily packaged up in a re-usable library function for future, easier use).  For reasons that will become apparent very soon, we&#8217;ll use a new local boolean variable to track the success or failure of our image capture which we will initialise (I haven&#8217;t yet looked at Oxygene initialisation rules for local variables, so this may not be necessary &#8211; but, just in case&#8230;).<\/p>\n<p>First, creating the file in the required location:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    successful := FALSE;\r\n\r\n    if Intent.Action = MediaStore.ACTION_IMAGE_CAPTURE then\r\n    begin\r\n      if assigned(Intent.Extras) and Intent.Extras.containsKey(MediaStore.EXTRA_OUTPUT) then\r\n      begin\r\n        outputUri := Uri(Intent.Extras.get(MediaStore.EXTRA_OUTPUT));\r\n\r\n        if assigned(outputUri) then\r\n        begin\r\n          if outputUri.Scheme = 'file' then\r\n            jpegFile := new File(outputUri.Path)\r\n          else\r\n            Log.e('Camera Demo', &quot;Only file: Uri's are supported, sorry&quot;);\r\n        end\r\n        else\r\n          Log.e('Camera Demo', 'EXTRA_OUTPUT was unexpectedly null');\r\n      end\r\n      else\r\n        Log.i('Camera Demo', 'Thumbnail results not supported, sorry');\r\n    end\r\n    else\r\n    begin\r\n      \/\/ Create default file in Pictures folder\r\n\r\n      timestamp := new SimpleDateFormat('yyyyMMdd_HHmmss').format(new Date());\r\n\r\n      dir      := Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);\r\n      jpegFile := new File(dir.Path + File.separator + 'IMG_' + timestamp + '.jpg');\r\n    end;\r\n<\/pre>\n<p>The <strong>Intent.Extras<\/strong> object is a <a href=\"http:\/\/developer.android.com\/reference\/android\/os\/Bundle.html\"><strong>Bundle<\/strong><\/a> containing any named data that was supplied with the <strong>Intent<\/strong> by the caller.  We are looking for one called <strong>MediaStore.EXTRA_OUTPUT<\/strong>.  If found, the named item is a <strong>Uri<\/strong> object from which we obtain the full path.<\/p>\n<p>Again a little bit of cheating here, since I am only supporting <strong>file:<\/strong> scheme Uri&#8217;s (which I determined is what <strong>Google+<\/strong> provides).  As a result almost half the added code is just emitting excuses to the Android Log (which will appear in our debugger output window, for example).  <strong>Log.i()<\/strong> emits information.  <strong>Log.e()<\/strong> emits an error.<\/p>\n<p>The second and final change is notifying the calling action when we have completed the request and the image is ready.  First we set the successful variable indicating when we have successfully saved the file.  When responding to an intent, we then use this indicator to ensure we set the correct return code.<\/p>\n<p>All this is achieved with a little bit of additional behaviour after we have saved the JPEG to the output stream:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    if assigned(jpegFile) then\r\n    begin\r\n      stream := new FileOutputStream(jpegFile);\r\n      stream.write(aPicture);\r\n      stream.close;\r\n\r\n      successful := FALSE;\r\n    end;\r\n\r\n    if Intent.Action = MediaStore.ACTION_IMAGE_CAPTURE then\r\n    begin\r\n      \/\/ If satisfying an intent, indicate a the appropriate result and\r\n      \/\/  return to the calling app (our activity is finished)\r\n      setResult(if successful then RESULT_OK else RESULT_CANCELED);\r\n      finish;\r\n      end;\r\n    end;\r\n<\/pre>\n<p>I could have used the ability to call &#8220;<strong>setResult<\/strong>&#8221; as a &#8220;property&#8221; setter, but would have had to quality this with <strong>self<\/strong> to ensure that the compiler did not mistakenly think I was trying to set the <em>method<\/em> <strong>result<\/strong> value:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n   self.Result := if successful then RESULT_OK else RESULT_CANCELED;\r\n<\/pre>\n<p>At this point we see another <strong>Oxygene<\/strong> language feature demonstrated that deserves special mention:  <strong>if expressions<\/strong>.<\/p>\n<p>The value of the parameter passed to <strong>setResult<\/strong> is dependent upon the state of the <strong>successful<\/strong> <strong>Boolean<\/strong>.  A common pattern in Delphi code on such occasions involves using a <strong>const array<\/strong> declaration, indexed by the selector value:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  const\r\n    RETURN_CODE : array[FALSE..TRUE] of Integer = (RESULT_OK, RESULT_CANCELED);\r\n\r\n  \/\/ and then ...\r\n\r\n  setResult(RETURN_CODE[successful]);\r\n<\/pre>\n<p>I for one much prefer <strong>Oxygene<\/strong>&#8216;s <strong>if expression<\/strong> solution. \ud83d\ude42<\/p>\n<p>But, back to the new intent related code.<\/p>\n<p>This extra behaviour only applies when responding to the <strong>ACTION_IMAGE_CAPTURE<\/strong> intent.  We don&#8217;t have to actually make any calls to our caller though.  The system knows that it launched our activity with an intent from another app, and simply by indicating that the result is OK and we have finished, the system can then return to that other app.<\/p>\n<p>If we were returning information more directly (such as providing that thumbnail response when no <strong>EXTRA_OUTPUT<\/strong> is supplied by the caller) I believe the mechanism that allows us to do that involve passing a new <strong>Intent<\/strong> object back to the caller with an overloaded version of <strong>setResult()<\/strong>.<\/p>\n<p>But since I am not supporting that mechanism at this time, only the result code is being set.<\/p>\n<p>And there we have it.  Not only a camera app, but a camera app that will happily respond to requests from another application &#8211; should the user decide &#8211; to take a photo on their behalf.<\/p>\n<p>Just one final thing to mention.<\/p>\n<h3>Debugging Intents<\/h3>\n<p>On a couple of occasions I referred to having figured out how the <strong>Google+<\/strong> app was implementing it&#8217;s intent request by inspecting aspects of the supplied <strong>Intent<\/strong> in the debugger.  This might have raised the question as to just how you go about debugging intents when an application is launched not by the debugger but by some other <strong>Android<\/strong> app that the debugger doesn&#8217;t even (necessarily) know about.<\/p>\n<p>It is actually very straightforward.  I didn&#8217;t even need to research this, I just tried what I thought would work and it did. \ud83d\ude42<\/p>\n<p>When I want to debug some aspect of my app when responding to an intent I simply launch the app on my device for debugging as normal with any breakpoint(s) set as appropriate (typically in a branch of the code reachable only when responding to the intent of interest).<\/p>\n<p>Once the app has deployed and launched I simply navigate to the home screen of my device in order to launch the app that I am going to use to issue the intent (or re-activate it via the app list, if already running).  In this case <strong>Google+<\/strong>.  My app is still running, just in the background and not visible at this stage.<\/p>\n<p>I perform whatever operations in the app that are required to trigger the intent request, at which point of course my app comes to the foreground once again and when the breakpoint is reached in <strong>my<\/strong> app, the <strong>Visual Studio<\/strong>\/<strong>Oxygene<\/strong> debugger interrupts execution just as you would expect when a breakpoint is reached.<\/p>\n<p>Simple.<\/p>\n<h3>Use The Source<\/h3>\n<p>As promised I am providing the full source for the app for download, so people can fully mock my efforts.  \ud83d\ude42<\/p>\n<div style=\"width: 80%; border: 1px solid black; background-color: rgb(250,250,255); padding: 5px; margin: 10px auto\">\r\n<p><img src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/plugins\/wp-downloadmanager\/images\/ext\/zip.gif?w=640&#038;ssl=1\" alt=\"\" title=\"\" style=\"vertical-align: middle;\" data-recalc-dims=\"1\" \/>&nbsp;&nbsp;<strong><a href=\"https:\/\/www.deltics.co.nz\/blog\/?dl_name=nz.co.deltics.demo.camera.1.0.zip\">Demo Camera App for Android<\/a><\/strong> (48.7 KiB, 501 hits)<\/p>\r\n<div style=\"font: 9pt serif\">Full source for the demo camera app for Android.  Supports and demonstrates: ActionBar UI, live camera viewfinder, touch to autofocus, taking and saving camera pictures, (limited) support for IMAGE_CAPTURE intent.\r\n\r\nRequires Android 3.0 (due to use of ActionBar)<\/div>\r\n<\/div>\n<p><em><strong>NOTE: <\/strong>This is the first time I&#8217;ve ever tried distributing a VisualStudio project source, let along Oxygene for Android.  If I have missed anything please let me know a.a.s.p.<br \/>\n<\/em><\/p>\n<p>In this source you will notice some differences in a few areas, compared to the code previously posted over the course of this series.  This is the result of a general tidy up and my having fixed a couple of mistakes that I realised I had made since I started this series:<\/p>\n<ul>\n<li><strong>Camera<\/strong> should be <strong>releas()<\/strong>ed when app activity is paused (override <strong>MainActivity.onPause()<\/strong> method) and (re-)<strong>open()<\/strong>ed when resumed (override <strong>MainActivity.onResume()<\/strong> method &#8211; also called during initial startup)<\/li>\n<li>Do not explicitly stop preview before calling <strong>takePicture()<\/strong>.  Camera will stop preview  which will stop the preview itself.  Also, do not re-start preview in <strong>onPictureTaken()<\/strong>, but immediately after calling <strong>takePicture()<\/strong><\/li>\n<li>Added the code to correctly stop\/restart preview when changing camera orientation on older Android versions<\/li>\n<li>Enable default camera shutter sound on capable Android versions<\/li>\n<li>Pretty icons  \ud83d\ude42<\/li>\n<\/ul>\n<p>If there are any Android experts that read my code and spot anything else that I may have inadvertently cocked-up, please let me know (how and why).<\/p>\n<p>Apart from trivial &#8220;hello world&#8221; exercises in the past, this is the first time I have ever even tried sitting down to write an actual Android app that actually did something.  I have been surprised and delighted at both how straightforward it has proven using <strong>Oxygene<\/strong> not to mention how much <strong>fun<\/strong> it has been.<\/p>\n<h3>Caveat Developor<\/h3>\n<p>Things to be aware of if you are intending to try this app on your device:<\/p>\n<ul>\n<li>The use of <strong>ActionBar<\/strong> (as implemented) requires a Honeycomb Android device or later (3.0).  Older versions of Android can be used but would require a bit of extra work (<a href=\"http:\/\/developer.android.com\/guide\/topics\/ui\/actionbar.html\">documented in the Android SDK<\/a> if you are interested).<\/li>\n<li>The app assumes the use of camera #0.  On my device this is the rear-facing camera.  I don&#8217;t think that there is any guarantee that this will be the most appropriate camera on your device, and there is no facility for changing cameras.<\/li>\n<li>The intent support is incomplete.  It works with the <strong>Google+<\/strong> app but may not work with other apps.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">10<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> This is it. The home straight. The final part of my series on writing a camera app using Oxygene for Android. In this concluding part I shall look at making my application a well behaved Android citizen. As well as pointing out (and fixing) some mistakes I have made along the way, we will add [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[212,205,4,180],"tags":[153,217,220,221,181],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-rq","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":1605,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1605\/","url_meta":{"origin":1700,"position":0},"title":"Anatomy of a Camera App","date":"15 Sep 2013","format":false,"excerpt":"Part 1 in an as yet unknown number of articles using a (very) simple camera application to demonstrate building first class Android applications using \"Pascal for Java\" - i.e. Oxygene Cooper. In this first instalment we will look at the basics - the components we are going to need and\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-09-15-at-10.12.32-.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":1624,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1624\/","url_meta":{"origin":1700,"position":1},"title":"Exploring Listeners With Oxygene","date":"16 Sep 2013","format":false,"excerpt":"Part 2 in a short series demonstrating the development of a simple camera app for Android using Oxygene. In the previous instalment we looked at the basic framework of our app. For this instalment I was going to show how to implement the camera preview or viewfinder for this instalment,\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1658,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1658\/","url_meta":{"origin":1700,"position":2},"title":"Crash Bang Wallop, What a Picture!","date":"19 Sep 2013","format":false,"excerpt":"The fourth and final part in the not-as-short-as-I-thought-it-would be series on building a camera app for Android using Oxygene. In this penultimate instalment we will add the capability to actually take a picture. But that won't take very long, so then we will spend a bit of time tidying up\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"Very Ugly Duckling","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screenshot_2013-09-19-19-38-56-1024x640.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":1634,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1634\/","url_meta":{"origin":1700,"position":3},"title":"An App With View","date":"17 Sep 2013","format":false,"excerpt":"Not a Merchant Ivory production, but Part 3 in the Oxygene for Java camera app for Android series. So far we have seen that we can work directly with the Android platform manifest and layout files and how the Oxygene language is a first class citizen in the Java platform\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1791,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1791\/","url_meta":{"origin":1700,"position":4},"title":"Developing and Debugging an AppWidget &#8211; Part 2","date":"30 Sep 2013","format":false,"excerpt":"In the first instalment of this series, I implemented the basic framework of a new appwidget and established a means by which I could debug the widget code. Now it's time to add some code worth debugging. You may recall that the aim of my widget is to display the\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screenshot_2013-09-30-20-03-27.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2280,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2280\/","url_meta":{"origin":1700,"position":5},"title":"Oxygene Constructor Magic on Cocoa","date":"30 Aug 2014","format":false,"excerpt":"Earlier this week I mentioned that I had published my TXT-2-PARK app for Android in the Google Play Store. Today I published the iOS version to the Apple App Store (still awaiting approval at this stage). As with the Android version, I implemented the iOS version using Oxygene, and things\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1700"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=1700"}],"version-history":[{"count":16,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1700\/revisions"}],"predecessor-version":[{"id":1743,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1700\/revisions\/1743"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=1700"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=1700"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=1700"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}