{"id":1873,"date":"2013-10-13T09:10:46","date_gmt":"2013-10-12T21:10:46","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=1873"},"modified":"2013-10-14T08:48:16","modified_gmt":"2013-10-13T20:48:16","slug":"developing-an-appwidget-part-5","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/1873\/","title":{"rendered":"Developing an AppWidget &#8211; Part 5"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 7<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>In my <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/1869\">previous post<\/a> I explained how I believed I had solved a problem with my widget, only to discover that it created a different problem in the process.<\/p>\n<p>I had believed that IntentService based services were long-lived, but in fact this is not the case.  However, the change remains valid for solving the problem of my update alarm surviving device sleep, leaving only the question of how to refactor the behaviour that using an <strong>IntentService<\/strong> had broken.<\/p>\n<p><!--more--><\/p>\n<h2>Devices Playing Possum<\/h2>\n<p>When an Android device turns off the screen, this does not necessarily mean that the device is yet asleep.  Any application or widget that continues to update itself when the screen is off is pretty much wasting it&#8217;s time but more importantly is wasting precious battery as well.<\/p>\n<p>Fortunately there are notifications we can receive that will allow us to adjust our behaviour when the screen goes off (and comes back on) which means we can be even more efficient in our battery use.<\/p>\n<h3>Yet More Intents<\/h3>\n<p>As you may have come to expect by now, this involves <strong>Intent<\/strong>s.  Again.<\/p>\n<p>In this case we are interested in a pair of system defined intents:<\/p>\n<ul>\n<li>Intent.ACTION_SCREEN_OFF<\/li>\n<li>Intent.ACTION_SCREEN_ON<\/li>\n<\/ul>\n<p>Unlike the battery level information, these intents are not &#8220;sticky&#8221;.  They are dynamic intents, broadcast to interested parties as and when the screen is turned on or off, whether by the system in response to a time-out, or by the user specifically.<\/p>\n<p>These particular system intents are a bit unusual however.<\/p>\n<p>Normally we would add an <code class=\"\" data-line=\"\">intent-filter<\/code> to our manifest to register our interest in receiving intents and identifying the entity (or entities) in our application that will respond to them.  But these intents specifically <em>cannot<\/em> be declared in this way.   Or rather, you can declare them but they won&#8217;t be acknowledged by the system.  You won&#8217;t receive the intents.<\/p>\n<p>Instead we must create and register a receiver at runtime.<\/p>\n<p>Originally, I chose to create and register my receiver in the <code class=\"\" data-line=\"\">onHandleIntent<\/code> method of my <code class=\"\" data-line=\"\">UpdateService<\/code>.  This was the mistake I made.  I had believed that my UpdateService was &#8220;long-lived&#8221; when it wasn&#8217;t.  However, deriving from IntentService did address my issue with surviving sleep.<\/p>\n<p>So, for my screen state notifications all I needed to do was create an entirely new service that <strong><em>is<\/em><\/strong> long-lived.<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\ninterface\r\n\r\n  ScreenStateService = public class (Service)\r\n  private\r\n    class var cfScreenStateReceiver: ScreenStateReceiver;\r\n  public\r\n    method onDestroy; override;\r\n    method onBind(aIntent: Intent): IBinder; override;\r\n    method onStartCommand(aIntent: Intent; aFlags, aStartID: Integer): Integer; override;\r\nend;\r\n\r\n<\/pre>\n<p>As before my service class overrides <code class=\"\" data-line=\"\">onBind<\/code> to provide a default implementation returning <strong>NIL<\/strong>.<\/p>\n<p>My <code class=\"\" data-line=\"\">onStartCommand<\/code> override creates and registers the receiver of the <code class=\"\" data-line=\"\">SCREEN_ON<\/code>\/<code class=\"\" data-line=\"\">SCREEN_OFF<\/code> intents.<\/p>\n<p>My <code class=\"\" data-line=\"\">onDestroy<\/code> override unregisters the receiver.<\/p>\n<p>The <strong>cfScreenStateReceiver<\/strong> class var holds a reference to my registered receiver for as long as it remains registered.<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\nimplementation\r\n\r\n  method ScreenStateService.onDestroy;\r\n  begin\r\n    unregisterReceiver(fScreenStateReceiver);\r\n    fScreenStateReceiver := NIL;\r\n\r\n    inherited;\r\n  end;\r\n\r\n\r\n  method ScreenStateService.onStartCommand(aIntent: Intent; \r\n                                           aFlags: Integer; \r\n                                           aStartID: Integer): Integer;\r\n  begin\r\n    if NOT assigned(fScreenStateReceiver) then\r\n    begin\r\n      var screenState  := new IntentFilter;\r\n      \r\n      screenState.addAction(Intent.ACTION_SCREEN_ON);\r\n      screenState.addAction(Intent.ACTION_SCREEN_OFF);\r\n\r\n      fScreenStateReceiver := new ScreenStateReceiver;\r\n      registerReceiver(fScreenStateReceiver, screenState);\r\n    end;\r\n\r\n    result := START_STICKY;\r\n  end;\r\n<\/pre>\n<p>Creating the receiver is simple enough, as is registering it using the <code class=\"\" data-line=\"\">registerReceiver<\/code> method, though this time we pass a reference to the receiver to be received, rather than <strong>NIL<\/strong>, as we did with the battery information &#8220;sticky&#8221; intent.<\/p>\n<p>We also pass in the <strong>IntentFilter<\/strong> identifying the intents this receiver should receive.  This also is relatively straightforward.  We simply create the new filter and add the actions of interest to it.<\/p>\n<p>The key to making the service long-lived is to return <strong>Service.START_STICKY<\/strong> from this method.<\/p>\n<h3>Live Long, <del>and Prosper<\/del> If You Have Lots To Process<\/h3>\n<p>This code was originally implemented in my existing <strong>UpdateService<\/strong>, and (with the addition of calls to Android Log methods) I found that my <strong>UpdateService<\/strong> would register the <strong>ScreenStateReceiver<\/strong> but was then immediately destroyed and thus immediately <strong>UNregister<\/strong> that receiver.  This was how I learned that the notion that an <strong>IntentService<\/strong> is long-lived was wrong.<\/p>\n<p>With an <strong>IntentService<\/strong> you don&#8217;t get to decide what is returned from <code class=\"\" data-line=\"\">onStartCommand<\/code> since the extension point is <code class=\"\" data-line=\"\">onHandleIntent<\/code>, with no mechanism for indicating any preferred service lifetime.<\/p>\n<p>So where did I get the idea that IntentService was appropriate for long-lived services ?<\/p>\n<p>The key is in distinguishing between a stated service lifetime which may determine how the service is managed <em>after it completes processing<\/em>, and the amount of time required to <em>perform that processing itself<\/em>.<\/p>\n<p>The <em>real<\/em> point of <strong>IntentService<\/strong> is that <code class=\"\" data-line=\"\">onHandleIntent<\/code> is called by a worker thread, and thus will not block your application main thread.  Thus, if you have a service which will be doing a potentially significant amount of work when handling an intent, an <strong>IntentService<\/strong> is a convenient way of shunting that work into a worker thread.<\/p>\n<p>But once the work is done (<strong>onHandleIntent<\/strong> returns) the service will be quickly cleaned up.<\/p>\n<p>In other words, if you have a potentially long living service, an <strong>IntentService<\/strong> based implementation is <em>highly recommended<\/em>.  But using an <strong>IntentService<\/strong> does not itself make a service long lived.<\/p>\n<p>In any event, I now have a suitable service which will register a receiver for the intents of interest.  The question now is what to do when we receive these intents, and this is of course determined by how I implement the <strong>ScreenStateReceiver<\/strong> class itself.<\/p>\n<h3>Receiving Intents, Load and Clear<\/h3>\n<p>First of all, declaring the <strong>ScreenStateReceiver<\/strong> is trivial.  It is a sub-class of <strong>BroadcastReceiver<\/strong>, and I only need to override one method, <code class=\"\" data-line=\"\">onReceive<\/code>:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\ninterface\r\n\r\n  uses\r\n    android.app,\r\n    android.content;\r\n    \r\n\r\n  type\r\n    ScreenStateReceiver = public class(BroadcastReceiver)\r\n    public\r\n      method onReceive(aContext: Context; aIntent: Intent); override;\r\n    end;\r\n\r\n<\/pre>\n<p>The question is:  <em>What should I do when the screen comes on or goes off ?<\/em><\/p>\n<p>What I want to do is enable or disable my update alarm as appropriate.  If the screen goes off, I want to stop updating my widget, and when the screen comes on resume those updates.<\/p>\n<p>But the code for doing this is in my widget provider.  I could perhaps duplicate the scheduling and cancellation of the alarm and in this simple case such duplication might be acceptable.  I could perhaps refactor the code into class methods on the widget provider class which I can call directly from my <strong>ScreenStateReceiver<\/strong>.  But this feels wrong for some reason (I honestly do not know if it is)<\/p>\n<p>What I would prefer to do is ensure that the communication between my <strong>ScreenStateReceiver<\/strong> and the widget provider follows normal Android protocols and that means intents.  This time, <em>sending<\/em> an intent.<\/p>\n<p>I cannot send the system defined <strong>ACTION_SCREEN_ON<\/strong>\/<strong>OFF<\/strong> intents themselves.  Android system will not allow that with these particular intents.<\/p>\n<p>But I can send a custom intent and with an <code class=\"\" data-line=\"\">intent-filter<\/code> in my manifest I can ensure that my <strong>BatteryWidgetProvider<\/strong> will be signalled.<\/p>\n<p>So first, a couple of class constants for the intent names, to avoid silly typing mistakes:<\/p>\n<pre class=\"brush: oxygene; highlight: [3,4]; title: ; notranslate\" title=\"\">\r\n    ScreenStateReceiver = public class(BroadcastReceiver)\r\n    public\r\n      const SCREEN_ON  = 'nz.co.deltics.SCREEN_ON';\r\n      const SCREEN_OFF = 'nz.co.deltics.SCREEN_OFF';\r\n      ...\r\n    end;\r\n<\/pre>\n<p>And now to receive the system screen state intents and pass them on to the widget provider in the form of my corresponding custom intents:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  method ScreenStateReceiver.onReceive(aContext: Context; aIntent: Intent);\r\n  begin\r\n    var i := new Intent(aContext, typeOf(BatteryWidgetProvider));\r\n\r\n    case aIntent.Action of\r\n      Intent.ACTION_SCREEN_ON  : i.Action := SCREEN_ON;\r\n      Intent.ACTION_SCREEN_OFF : i.Action := SCREEN_OFF;\r\n    else\r\n      i := NIL;\r\n    end;\r\n\r\n    if assigned(i) then\r\n      aContext.sendBroadcast(i)\r\n    else\r\n      inherited;\r\n  end;\r\n<\/pre>\n<p>First I create the intent I will be sending.  Just to simplify the rest of the method I create this before determining whether it will be needed.  A bit wasteful perhaps but not a major concern I don&#8217;t think.<\/p>\n<p>I then use a <strong>case<\/strong> statement to set the appropriate custom intent constant (string) as the <strong>Action<\/strong> of the intent.  I simply <strong>NIL<\/strong> the intent reference &#8216;<code class=\"\" data-line=\"\">i<\/code>&#8216; if the intent I received turns out not to be one of the screen state intents.<\/p>\n<p>This is a little demonstration of the fact that <strong>Oxygene<\/strong> case statements are more flexible than we are used to in Delphi: they support strings!  This really isn&#8217;t your grand-daddy&#8217;s Pascal.  \ud83d\ude09<\/p>\n<p>If I end up with an intent, I broadcast it.  Otherwise I pass the buck, up to the inherited <code class=\"\" data-line=\"\">onReceive<\/code> implementation, just in case.<\/p>\n<p>Now is as good time as any to add my actions to the <code class=\"\" data-line=\"\">intent-filter<\/code> for the widget provider in the manifest:<\/p>\n<pre class=\"brush: xml; highlight: [5,6]; title: ; notranslate\" title=\"\">\r\n      &lt;intent-filter&gt;\r\n        &lt;action android:name=&quot;android.appwidget.action.APPWIDGET_UPDATE&quot; \/&gt;\r\n        &lt;action android:name=&quot;android.appwidget.action.APPWIDGET_ENABLED&quot; \/&gt;\r\n        &lt;action android:name=&quot;android.appwidget.action.APPWIDGET_DISABLED&quot; \/&gt;\r\n        &lt;action android:name=&quot;nz.co.deltics.SCREEN_OFF&quot; \/&gt;\r\n        &lt;action android:name=&quot;nz.co.deltics.SCREEN_ON&quot; \/&gt;\r\n      &lt;\/intent-filter&gt;\r\n<\/pre>\n<p>And finally implement an override of the onReceive method in the widget provider itself to respond to these actions:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n  method BatteryWidgetProvider.onReceive(aContext: Context; \r\n                                         aIntent: Intent);\r\n  begin\r\n    case aIntent.Action of\r\n      ScreenStateReceiver.SCREEN_ON  : onEnabled(aContext);\r\n      ScreenStateReceiver.SCREEN_OFF : onDisabled(aContext);\r\n    else\r\n      inherited;\r\n    end\r\n  end;\r\n<\/pre>\n<p>The code I needed was ready and waiting on the widget provider class in the form of the <code class=\"\" data-line=\"\">onEnable()<\/code>\/<code class=\"\" data-line=\"\">onDisable()<\/code> methods, so when I receive a <strong>SCREEN_ON<\/strong>\/<strong>SCREEN_OFF<\/strong> intent, I simply call the appropriate one of those methods.<\/p>\n<p>It is very important that I call the inherited implementation of <code class=\"\" data-line=\"\">onReceive()<\/code> in this case since I know for a <em>fact<\/em> that the superclass responds to a variety of other intents necessary to the functioning of an <strong>AppWidgetProvider<\/strong> (you may recall this is a specialisation of <strong>BroadcastReceiver<\/strong>).<\/p>\n<p>Indeed the <code class=\"\" data-line=\"\">onEnable()<\/code> and <code class=\"\" data-line=\"\">onDisable()<\/code> methods are introduced by this base class and will be called by the inherited <code class=\"\" data-line=\"\">onReceive()<\/code> implementation in response to other system generated intents.<\/p>\n<p>Just one last thing and it&#8217;s all done.<\/p>\n<h3>Getting the Service Started<\/h3>\n<p>To support these screen state intents I have introduced a new service, but the intents that service is designed to support are not intents that we can declare in the manifest, so as things stand currently, this new service will never get started and will never register the receiver that my widget relies on for the screen state notifications.<\/p>\n<p>Somewhere I need to start this service myself.<\/p>\n<p>I decided that the <code class=\"\" data-line=\"\">onEnabled()<\/code> handler of my <strong>BatteryWidgetProvider<\/strong> would suit.<\/p>\n<p>Starting the <strong>ScreenStateService<\/strong> here means that the service is certain to be started at some suitable point when I have an active instance of my widget.<\/p>\n<p>Further more, the <code class=\"\" data-line=\"\">onEnabled()<\/code> method is also called whenever the device screen comes on (thanks to my handling of that intent).  I am not entirely certain that it is necessary, but it surely can&#8217;t hurt that re-starting the service at this point will add further weight to the <strong>START_STICKY<\/strong> nature of the service, making it even less likely that the service will be cleaned up unless absolutely necessary.  I think.  Either way I don&#8217;t think it can hurt.<\/p>\n<p>So I add the following line to my <code class=\"\" data-line=\"\">BatteryWidgetProvider.onEnabled()<\/code> method:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    aContext.startService(new Intent(aContext, typeOf(ScreenStateService)));\r\n<\/pre>\n<h3>All Over Bar the Shouting<\/h3>\n<p>My widget now will suspend updates as soon as the screen is turned off &#8211; for whatever reason &#8211; even before the device has entered sleep, and will resume updates when the screen is turned on.  Since this will start a new alarm schedule, this means the widget will also perform an initial update so that I get an accurate battery level reading instantly.<\/p>\n<p>This latter point is worth highlighting.<\/p>\n<p>I am running a custom ROM on my Android phone, and this incorporates a battery level reading on the default lock screen.  Interestingly, this is <strong>not<\/strong> refreshed when the screen comes on.  As a result, the battery level initially presented on my lock screen is often a little, um, <em>optimistic<\/em>.<\/p>\n<p>Also worth mentioning is that I have been running my revised widget on my phone all day for the past 2 days and as far as I can tell, battery use has been no greater than usual but continues to update consistently, so it would appear that the time spent ensuring that my widget behaves itself has been well spent!<\/p>\n<p>The final leg in this journey is to get my widget up in the Google Play store.<\/p>\n<p>Not that I think it really has anything compelling to offer alongside the myriad other battery widgets that are already there, but it will be a useful exercise in going through the process of getting something approved for the store.<\/p>\n<p>An experience which &#8211; all being well &#8211; I will then also share.<\/p>\n<p>\ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 7<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span>In my previous post I explained how I believed I had solved a problem with my widget, only to discover that it created a different problem in the process. I had believed that IntentService based services were long-lived, but in fact this is not the case. However, the change remains valid for solving the problem [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[212,205,4,180],"tags":[153,235,181,234],"class_list":["post-1873","post","type-post","status-publish","format-standard","hentry","category-android-2","category-cooper","category-delphi","category-oxygene","tag-android","tag-custom-intents","tag-oxygene-2","tag-screen"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-ud","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1873","targetHints":{"allow":["GET"]}}],"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=1873"}],"version-history":[{"count":9,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1873\/revisions"}],"predecessor-version":[{"id":1899,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1873\/revisions\/1899"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=1873"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=1873"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=1873"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}