{"id":1249,"date":"2010-02-25T21:50:50","date_gmt":"2010-02-25T21:50:50","guid":{"rendered":"http:\/\/dalelane.co.uk\/blog\/?p=1249"},"modified":"2010-02-25T22:21:57","modified_gmt":"2010-02-25T22:21:57","slug":"why-i-havent-written-twittoday-for-android","status":"publish","type":"post","link":"https:\/\/dalelane.co.uk\/blog\/?p=1249","title":{"rendered":"Why I haven&#8217;t written TwitToday for Android"},"content":{"rendered":"<h3 style=\"margin-top: 0.9em;\">(or &#8220;looking at the limitations of the Android home screen widgets framework&#8221;)<\/h3>\n<p><a href=\"http:\/\/www.flickr.com\/photos\/dalelane\/2387728775\/\" title=\"Twitter client for Windows Mobile by dalelane, on Flickr\"><img loading=\"lazy\" decoding=\"async\" hspace=10 vspace=10 align=\"left\" src=\"http:\/\/farm3.static.flickr.com\/2088\/2387728775_9ab57ee5fa.jpg\" border=\"0\" width=\"162\" height=\"250\" alt=\"Twitter client for Windows Mobile\" \/><\/a>A couple of years ago, I wrote a Windows Mobile app called <a href=\"http:\/\/dalelane.co.uk\/blog\/?p=244\">TwitToday<\/a> : a Twitter widget for the &#8220;Today screen&#8221; (the default screen when the phone is on, with all apps closed or minimised).  <\/p>\n<p>The rationale was that it was useful to have a way to post a tweet quickly when you don&#8217;t want to have to wait for a full-blown client to start.<\/p>\n<p>Or if you don&#8217;t want to incur the battery or memory cost of leaving a client running all the time.<br \/>\n<br clear=\"all\"\/><br \/>\n<a href=\"http:\/\/www.flickr.com\/photos\/dalelane\/4387620153\/\" title=\"A widget that doesn't work on Android by dalelane, on Flickr\"><img loading=\"lazy\" decoding=\"async\" align=\"right\" hspace=10 vspace=10 style=\"border: thin solid black\" src=\"http:\/\/farm5.static.flickr.com\/4013\/4387620153_b820eb1c4d_o.png\" width=\"224\" height=\"336\" alt=\"A widget that doesn't work on Android\" \/><\/a>Android has a &#8220;Home screen&#8221;, which is similar to the Today screen on Windows Mobile. And it has <a href=\"http:\/\/developer.android.com\/guide\/topics\/appwidgets\/index.html\" target=\"_blank\">support for widgets<\/a>. This led several people to ask me why I never ported TwitToday to Android.  <\/p>\n<p>The short answer is that I did try, but the current state of the widgets framework made it impossible.  <\/p>\n<p>I tried to write a TwitToday widget back when the Android widgets framework was first <a href=\"http:\/\/android-developers.blogspot.com\/2009\/04\/introducing-home-screen-widgets-and.html\" target=\"_blank\">introduced in Android 1.5<\/a>.  <\/p>\n<p>It all started quite well: <\/p>\n<ol>\n<li>I wrote the <a href=\"http:\/\/developer.android.com\/reference\/android\/app\/Service.html\" target=\"_blank\">Android Service<\/a> that would carry out the background task of posting a tweet. No problems there &#8211; it worked fine.\n<\/li>\n<li>I wrote the widget XML to define the GUI. This is what you can see running in the screenshot. And it works&#8230; sort of. It has a text box that you can type up to 140 characters into. And if you press the button, it causes a chunk of code to get invoked.  <\/li>\n<\/ol>\n<p>The problem came when I tried to link the two together: to get the Service that posts tweets to obtain the String from the text box in my widget. <\/p>\n<p>This isn&#8217;t possible.  Android&#8217;s widget framework as it currently stands is geared around displaying information to the user, not collecting user input. <\/p>\n<p><!--more-->The API access to widgets at runtime is to go through an intermediate layer called <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/RemoteViews.html\" target=\"_blank\">RemoteViews<\/a> &#8211; an Android mechanism that lets you implement a View within another process (in this case, the Home screen&#8217;s process).  <\/p>\n<p>Look at the <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/RemoteViews.html\" target=\"_blank\">Reference page for RemoteViews<\/a>. RemoteViews only provide &#8220;setter&#8221; methods. No &#8220;getter&#8221; methods. My code can set the text in the text box (so I could display tweets that I download from twitter.com). But it can&#8217;t get it.  <\/p>\n<p>So after the user types in their tweet, and presses the button, my code has no way to get the String that they typed in.  <\/p>\n<p>Dammit.  <\/p>\n<p>I haven&#8217;t seen this explained in the Android developer docs, but I should&#8217;ve guessed from the <a href=\"http:\/\/developer.android.com\/guide\/topics\/appwidgets\/index.html#CreatingLayout\" target=\"_blank\">guide to creating App Widgets<\/a>: <\/p>\n<blockquote><p>&#8230; you must be aware that App Widget layouts are based on RemoteViews, which do not support every kind of layout or view widget. <\/p>\n<p>A RemoteViews object (and, consequently, an App Widget) can support &#8230; the following widget classes: <\/p>\n<ul>\n<li>AnalogClock\n<\/li>\n<li>Button\n<\/li>\n<li>Chronometer\n<\/li>\n<li>ImageButton\n<\/li>\n<li>ImageView\n<\/li>\n<li>ProgressBar\n<\/li>\n<li>TextView <\/li>\n<\/ul>\n<p>Descendants of these classes are not supported. <\/p><\/blockquote>\n<p>You normally create editable text boxes using <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/EditText.html\" target=\"_blank\">EditText<\/a> (a subclass of TextView). As EditText wasn&#8217;t available for App Widgets, I made my own by using a TextView (which is supported) and setting the <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/TextView.html#attr_android:editable\" target=\"_blank\">editable<\/a> and <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/TextView.html#attr_android:cursorVisible\" target=\"_blank\">cursorVisible<\/a> properties to <code>true<\/code>. <\/p>\n<p>That gave me an editable text box where a user can type tweets into. But it doesn&#8217;t change the fact that the intermediate RemoteViews API layer that I have to go through to access it, still doesn&#8217;t have a <code>getTextViewText<\/code> method, only a <a href=\"http:\/\/developer.android.com\/reference\/android\/widget\/RemoteViews.html#setTextViewText(int, java.lang.CharSequence)\" target=\"_blank\"><code>setTextViewText<\/code><\/a> method.  <\/p>\n<p>In hindsight, what the supported classes have in common are that they are all ways of displaying information, not collecting it. (<em>With the exception of buttons which can accept button presses&#8230;. which is sort of input<\/em>). <\/p>\n<p>I guess EditText was left out for a reason.  \ud83d\ude42<\/p>\n<p>It is more than a little frustrating. The guys behind Android obviously see the benefit of being able to enter text into widgets in the Home screen, as Android has a Google Search widget that does exactly that. <\/p>\n<p><img decoding=\"async\" src=\"http:\/\/dalelane.co.uk\/blog\/post-images\/100217-android-2.jpg\"\/> <\/p>\n<p>But, looking at the source, they cheated. They don&#8217;t use the widget framework that we lowly app developers have to use. The Search widget is part of the Home screen process itself. It&#8217;s built in, rather than having to use the external plugin API. This means it isn&#8217;t subject to the same restrictions. Spoilsports \ud83d\ude09 <\/p>\n<p>So, for the moment, short of copying the Google Search Widget approach and coming up with my own customised version of Android OS that includes a Twitter widget, I&#8217;m not porting TwitToday to Android.  <\/p>\n<p>Sorry. \ud83d\ude41<\/p>\n","protected":false},"excerpt":{"rendered":"<p>(or &#8220;looking at the limitations of the Android home screen widgets framework&#8221;) A couple of years ago, I wrote a Windows Mobile app called TwitToday : a Twitter widget for the &#8220;Today screen&#8221; (the default screen when the phone is on, with all apps closed or minimised). The rationale was that it was useful to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[291,151,243,448,425],"class_list":["post-1249","post","type-post","status-publish","format-standard","hentry","category-code","tag-android","tag-twitter","tag-twittoday","tag-widget","tag-widgets"],"_links":{"self":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1249","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1249"}],"version-history":[{"count":0,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1249\/revisions"}],"wp:attachment":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1249"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1249"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1249"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}