{"id":1817,"date":"2013-10-01T20:24:26","date_gmt":"2013-10-01T08:24:26","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=1817"},"modified":"2013-10-03T09:09:24","modified_gmt":"2013-10-02T21:09:24","slug":"getting-the-battery-level-on-android-with-delphi","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/1817\/","title":{"rendered":"Getting the Battery Level on Android With Delphi"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>Over the past few days I posted a two part series showing how to obtain the current battery level as part of the implementation of an Android <strong>AppWidget<\/strong> using <strong>Oxygene<\/strong>.  As far as I can tell <strong>AppWidgets<\/strong> simply aren&#8217;t possible using Delphi but reading the battery is quite straightforward <strong>Android SDK<\/strong> work, and I thought a comparison of the Oxygene and Delphi equivalents might be interesting.<\/p>\n<p><!--more--><\/p>\n<p>With Oxygene you are working directly with the Java Android SDK.  The code I originally found for reading the battery level was written in Java.  A quick dusting of Oxygene syntax and I was done.  Things proved a little less straightforward with Delphi.<\/p>\n<p>For the purposes of this comparison I&#8217;ll present the relevant code in the form of a simple function &#8211; not intended to be compilable as-is in any of the languages, just to encapsulate and demonstrate the key elements.<\/p>\n<h3>JAVA<\/h3>\n<p>First, the Java &#8216;original&#8217;:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nusing\r\n  android.content,\r\n  android.os;\r\n\r\nint BatteryPercent(Context context);\r\n{\r\n  IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);\r\n  Intent battery = context.registerReceiver(null, filter);\r\n \r\n  int level := battery.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);\r\n  int scale := battery.getIntExtra(BatteryManager.EXTRA_SCALE, -1);\r\n \r\n  return ((100 * level) \/ scale);\r\n}\r\n<\/pre>\n<h3>OXYGENE<\/h3>\n<p>In the case of Oxygene you can also use inline variable declarations if you prefer with the added benefit of type inference, if that&#8217;s your thing.  It&#8217;s not really <em>my<\/em> thing (though it may grow on me), but for the benefit of those who do appreciate such things, here&#8217;s the thoroughly modern <strong>Oxygene<\/strong> version:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\nuses\r\n  android.content,\r\n  android.os;\r\n\r\nmethod BatteryPercent(const aContext: Context): Integer;\r\nbegin\r\n  var filter  := new IntentFilter(Intent.ACTION_BATTERY_CHANGED);\r\n  var battery := aContext.registerReceiver(NIL, filter);\r\n \r\n  var level := battery.IntExtra[BatteryManager.EXTRA_LEVEL, -1];\r\n  var scale := battery.IntExtra[BatteryManager.EXTRA_SCALE, -1];\r\n \r\n  result := (100 * level) div scale;\r\nend;\r\n<\/pre>\n<h3>DELPHI<\/h3>\n<p>When it comes to Delphi you are stuck with predeclared variables in many circumstances, whether you like it or not.  As far as I know, this is one of those circumstances:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nuses\r\n  AndroidAPI.JNI.GraphicsContentViewText,\r\n  AndroidAPI.JNI.JavaTypes,\r\n  AndroidAPI.JNI.OS;\r\n\r\nfunction BatteryPercent(const aContext: JContext): Integer;\r\nvar\r\n  filter: JIntentFilter;\r\n  battery: JIntent;\r\n  level, scale: Integer;\r\nbegin\r\n  filter := TJIntentFilter.Create;\r\n  filter.addAction(TJIntent.JavaClass.ACTION_BATTERY_CHANGED);\r\n\r\n  battery := aContext.registerReceiver(NIL, filter);\r\n  level := battery.getIntExtra(StringToJString('level'), -1);\r\n  scale := battery.getIntExtra(StringToJString('scale'), -1);\r\n\r\n  result := (100 * level) div scale;\r\nend;\r\n<\/pre>\n<p>A few things to mention here.<\/p>\n<h3>They Seek Him Here, They Seek Him There<\/h3>\n<p>First of all <strong>Embarcadero<\/strong> seem to have gone out of their way to make it hard to find some of the various bits of the JNI that they have provided.<\/p>\n<p>For example:  The Android SDK <strong>Intent<\/strong> and <strong>IntentFilter<\/strong> classes reside in the <strong>android.content<\/strong> namespace.  But there is no <strong>AndroidAPI.JNI.Content<\/strong> unit.<\/p>\n<p>The intent classes (and interfaces, due to the way the wrappers work) are instead in the &#8220;<strong>AndroidAPI.JNI.GraphicsContentViewText<\/strong>&#8221; unit, a fact that I had to discover using a good old-fashioned &#8220;<em>Search > <strong>Find in Files<\/strong><\/em>&#8221; rigmarole.<\/p>\n<p>Initially I wasn&#8217;t aware of the &#8220;<strong>J<\/strong>&#8221; prefix on the interface names and &#8220;<strong>TJ<\/strong>&#8221; prefix on classes, so it took an initial, fruitless search for <strong>TIntent<\/strong> before I stumbled across that bit of genius.<\/p>\n<p>At first I thought this mouthful of a unit was combination of <strong>android.graphics<\/strong>, <strong>android.content<\/strong>, <strong>android.view<\/strong> and <strong>android.text<\/strong>, and perhaps this is how it started out in life since it does seem to contain a lot of stuff from these namespaces.<\/p>\n<p>Then I noticed some members of the <strong>android.database.sqlite<\/strong> namespace in there as well.<\/p>\n<p>Whatever noble intentions this unit started out with, it seems now that it is probably just a dumping ground for all sorts of stuff where the Embarcadero devs can stick things when they are in a hurry.<\/p>\n<h3>Everything But The Kitchen Sink<\/h3>\n<p>The second thing to mention is that there are some pieces missing from the <a href=\"http:\/\/blogs.embarcadero.com\/davidi\/\">supposedly comprehensive JNI wrappers<\/a>.<\/p>\n<p>As you can see, the Delphi code uses the <strong>AndroidAPI.JNI.OS<\/strong> unit, because the <strong>BatteryManager<\/strong> class which &#8211; in the Android SDK &#8211; provides the constants for the <strong>EXTRA_LEVEL<\/strong> and <strong>EXTRA_SCALE<\/strong> extras names is located in that namespace.<\/p>\n<p>I needn&#8217;t have bothered.<\/p>\n<p>Not only is the <strong>BatteryManager<\/strong> class not in the <strong>AndroidAPI.JNI.OS<\/strong> unit where you might expect, it is entirely absent, as are the constants it provides.  Hence instead you will have to research any such missing constants to discover what the underlying values are and declare them yourself or use them as literals.<\/p>\n<p>Of course, in the case of string literals you first have to put them through the <strong>StringToJString()<\/strong> transmogrifier as here.<\/p>\n<h3>Pascal That Looks Like Java<\/h3>\n<p>The third thing to mention is that Delphi provides none of the syntactic sugar that makes working with the Java SDK more Pascal-like in Oxygene.<\/p>\n<p>To illustrate this, here is the same line of code presented in what I consider to be <strong>decreasing order of &#8220;<em>Pascal-ness<\/em>&#8220;<\/strong>:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  level := battery.IntExtra[ BatteryManager.EXTRA_LEVEL, -1 ];\r\n\r\n  level = battery.getIntExtra( BatteryManager.EXTRA_LEVEL, -1 );\r\n\r\n  level := battery.getIntExtra( StringToJString('level'), -1 );\r\n<\/pre>\n<p>For the avoidance of doubt that&#8217;s: <strong>Oxygene<\/strong> &gt; <strong>Java<\/strong> &gt; <strong>Delphi<\/strong><\/p>\n<p>The differences &#8211; setting aside inline declarations vs predeclared &#8211; are subtle in this case, to be sure, and it&#8217;s not about &#8220;brevity&#8221; &#8211; just one character here or there in this case (though taking variable declaration into account, Delphi is far and away the least &#8216;productive&#8217; language if you measure such things in this way).<\/p>\n<p>It seems that if you want to write Pascal that looks and feels more like Java than Java itself then Delphi is a very good choice these days.<\/p>\n<h3>Cumbersome Constructions<\/h3>\n<p>As I mentioned earlier, the <strong>JNI<\/strong> wrappers use a combination of classes and interfaces.  Since interfaces do not have constructors, this makes constructing instances of a class an operation which cannot simply be taken directly from any Java code you might find.<\/p>\n<p><del>You instead have to use a parameterless constructor and figure out which methods can then be called to achieve the same initialisation that the Java constructor would have provided.<\/del><\/p>\n<p><del datetime=\"2013-10-02T21:08:10+00:00\">Where the Java class authors have implemented one or more constructors which <strong>cannot<\/strong> be approximated in this way, the Embarcadero devs seem to provide <strong>init()<\/strong> methods (example: <strong>JComponentName<\/strong>) though I can&#8217;t be bothered figuring out how you are supposed to work with these.<br \/>\n<\/del><\/p>\n<p><del datetime=\"2013-10-02T21:08:10+00:00\">And this is not applied consistently.  If these <strong>init()<\/strong> methods are not absolutely <em>needed<\/em>, then they are not on offer so there isn&#8217;t one consistent approach you can take when creating instances of <strong>JNI<\/strong> wrapped classes.<\/del><\/p>\n<p><strong>Update:<\/strong> <em>With a more complete understanding of the import class mechanism, there is a consistent constructor pattern that can be followed as <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/1834\">explained in this separate post<\/a>.<\/em><\/p>\n<h3>Something To Investigate &#8230;<\/h3>\n<p>If you were following my widget example in Oxygene, you will remember that trying to call <strong>registerReceiver()<\/strong> in the context of the <strong>AppWidgetProvider<\/strong> resulted in an Android runtime exception.<\/p>\n<p>No such problem occurred with the Delphi application.<\/p>\n<p>This leaves me wondering if my theory that the declaration of an <strong>intent-filter<\/strong> was responsible for this and thus would also apply to <strong>Activity<\/strong>&#8216;s as well is in fact mistaken.<\/p>\n<p>If the restriction applies specifically to <strong>AppWidgetProvider<\/strong>s this would fit with my further speculation that <strong>IntentReceiver<\/strong> (as referenced in the exception message) and <strong>BroadcastReceiver<\/strong> (what the Android SDK yields when searching for <strong>IntentReceiver<\/strong>) are now &#8211; and perhaps always were &#8211; synonymous.<\/p>\n<p>An <strong>AppWidgetProvider<\/strong> is nothing more or less than a specialised <strong>BroadcastReceiver<\/strong> so this would definitely fit.<\/p>\n<p>This bears a little more investigation to pin down, but at least I learned <em>something<\/em> from this exercise.<\/p>\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\">5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> Over the past few days I posted a two part series showing how to obtain the current battery level as part of the implementation of an Android AppWidget using Oxygene. As far as I can tell AppWidgets simply aren&#8217;t possible using Delphi but reading the battery is quite straightforward Android SDK work, and I thought [&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,4],"tags":[153,226,292,225],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-tj","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":1713,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1713\/","url_meta":{"origin":1817,"position":0},"title":"How to Call Java Code from an Oxygene Android Application","date":"20 Sep 2013","format":false,"excerpt":"Lachlan just posted a link to a post on Google+ (also available as a PDF) demonstrating how to call Java from Delphi XE5. I was shocked at both the amount and the nature of the code involved. It is long, convoluted and ugly stuff (nb. that isn't a criticism of\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1833,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1833\/","url_meta":{"origin":1817,"position":1},"title":"Importing an Android Class For Use in Delphi","date":"03 Oct 2013","format":false,"excerpt":"In a previous post I noted the absence of the BatteryManager class in the AndroidAPI.JNI units. This class contains some constants useful when reading battery information. I showed how to use a suitably massaged literal in place of these missing constants, but in response to observations from Paul and Brian\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2296,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2296\/","url_meta":{"origin":1817,"position":2},"title":"Sign Here Please&#8230; (Using This Specific Pen)","date":"01 Sep 2014","format":false,"excerpt":"My original Android version of TXT-2-PARK has been in the Google Play Store for a little over a week now. But it was only on Saturday that I decided to install the release version onto my phone from the store itself, and found a problem. The application would download just\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1199,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1199\/","url_meta":{"origin":1817,"position":3},"title":"Oxygene &#8220;Nougat&#8221; is Mac and iOS !","date":"06 Sep 2012","format":false,"excerpt":"Marc Hoffman has confirmed that \"Nougat\" is to Mac\/iOS as \"Cooper\" was to Java. \u00a0Some have speculated that this will be based on Mono, but Oxygene has had Mono covered for some time already, so I strongly doubt that this is the case. Far more likely is that just as\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1503,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1503\/","url_meta":{"origin":1817,"position":4},"title":"Sharing Code Across Platforms in Oxygene","date":"22 Aug 2013","format":false,"excerpt":"There seems to be a perception among some people that Delphi is in the unique position of allowing developers to share and re-use code across the various platforms that it's compiler can now (and will soon) target. But this is not the case. Oxygene has had this capability right from\u2026","rel":"","context":"In &quot;Cooper&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1624,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1624\/","url_meta":{"origin":1817,"position":5},"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":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1817"}],"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=1817"}],"version-history":[{"count":11,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1817\/revisions"}],"predecessor-version":[{"id":1840,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1817\/revisions\/1840"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=1817"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=1817"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=1817"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}