<![CDATA[Ninth Avenue Software]]> http://www.ninthavenue.com.au http://www.rssboard.org/rss-specification <![CDATA[How To Check If The Software Keyboard Is Shown In Android]]> Here is a method to detect if the soft keyboard is visible on the screen in Android. All the other methods I have seen test the height of screen elements to guess whether it is displayed. This doesn't work for keyboards like WifiKeyboard which is an invisible keyboard.

This method uses the callback result of InputMethodManager.showSoftInput() to determine if the keyboard status changed. This is suitable for me because I need to call showSoftInput() anyway if the keyboard is not shown. The result from the operation needs to be polled because it is asynchronous. This example only polls 500 milliseconds and assumes that anything longer than that is caused by the keyboard being loaded and rendered.

Here is how the code is used:

// try to show the keyboard and capture the result
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
IMMResult result = new IMMResult();
imm.showSoftInput(editText, 0, result);

// if keyboard doesn't change, handle the keypress
int res = result.getResult();
if (res == InputMethodManager.RESULT_UNCHANGED_SHOWN ||
        res == InputMethodManager.RESULT_UNCHANGED_HIDDEN) {
    showTags();
}

Read more...

]]>
http://www.ninthavenue.com.au/how-to-check-if-the-software-keyboard-is-shown-in-android http://www.ninthavenue.com.au/how-to-check-if-the-software-keyboard-is-shown-in-android Tue, 21 Feb 2012 10:11:44 +1000
<![CDATA[How To Move A Node In Nested Sets With SQL]]> Moving nodes in nested sets is a complex operation. There were a few solutions on Stack Overflow for moving a node under a given parent, however this doesn't let you select the position of the node amongst it's siblings.

This solution lets you move a node to any position in the tree, with just a single input parameter - the new left position (newpos) of the node.

Fundamentally there are three steps:

  1. Create new space for the subtree.
  2. Move the subtree into this space.
  3. Remove the old space vacated by the subtree.

In psuedo-sql, it looks like this:

    /**
     *  -- create new space for subtree
     *  UPDATE tags SET lpos = lpos + :width WHERE lpos >= :newpos
     *  UPDATE tags SET rpos = rpos + :width WHERE rpos >= :newpos
     * 
     *  -- move subtree into new space
     *  UPDATE tags SET lpos = lpos + :distance, rpos = rpos + :distance
     *           WHERE lpos >= :tmppos AND rpos < :tmppos + :width
     * 
     *  -- remove old space vacated by subtree
     *  UPDATE tags SET lpos = lpos - :width WHERE lpos > :oldrpos
     *  UPDATE tags SET rpos = rpos - :width WHERE rpos > :oldrpos
     */

Read more...

]]>
http://www.ninthavenue.com.au/how-to-move-a-node-in-nested-sets-with-sql http://www.ninthavenue.com.au/how-to-move-a-node-in-nested-sets-with-sql Sun, 8 Jan 2012 15:35:39 +1000
<![CDATA[Android ORM / JPA]]> Coming from the server-side, I find it hard to live without ORM (Object-Relational Mapping) such as JPA. Now that I've started writing Android Apps, I set about finding a JPA ORM tool for Android.

Here is a comparison of the tools I found that do ORM and are lightweight enough for use with Android.

Annotations DB Ops Size License Docs Age
OrmLite JPA or custom DAO or Entity 200kb Open docs 1yr
ORMAN JPA-like Entity 170kb Apache2 wiki 2yrs
greenDAO N/A Codegen DAO or Entity <100kb Apache2 docs
ActiveAndroid JPA-like Entity $20 wiki
AndrORM None docs 1yr

"DB Ops" refers to how database operations such as persist() and update() are handled. Normally either by a DAO (data access object) or by the entity themselves.

Handling Large Data Sets

Performance counts - especially since I need to handle large data sets. I don't want to load 3000 records into memory at once when the device only has room to display 10 at a time. Native Android supplies a Cursor to interate large data sets, and some ORM tools provide their own method too.

greenDAO has Query.lazyList() and OrmLite has DAO.iterator(Query) to do this, however Android adapters do not bind to an iterator so a custom adapter would need to be written in any case. There is a thread online that discusses this, however I think I can rig up a something similar to my LazyList for JPA to make this happen.

Verdict

I've started my project with OrmLite because it is the most mature. Lazy loading query results will need some custom code, but in the long run I think it will be better than polluting my code with column names and SQL hacks.

greenDAO was a good candidate from the performance perspective, but really I want to write my Entities myself so I can add the methods that I need.

Read more...

]]>
http://www.ninthavenue.com.au/android-orm-jpa http://www.ninthavenue.com.au/android-orm-jpa Mon, 19 Dec 2011 06:01:48 +1000
<![CDATA[Alt-Tab Shortcuts Broken In Ubuntu Lucid With XModMap]]> I use xmodmap to turn caps-lock into an extra modifier for special keys and shortcuts. Since upgrading finally to Ubuntu Lucid 10.04 LTS, I've had a problem with the Caps-Lock modifier key (Mode_switch) getting stuck when I accidentally press the Shift key. When I get it unstuck by pressing the same combination of Caps, Shift and one of my special keys, all my X-Windows shortcuts like Alt-Tab and Function keys stop working.

It's freaking annoying, however I finally found a workaround in Red Hat Bug 513815 and Bug 508434...

$ setxkbmap -layout jp

This resets your keyboard to it's original state. In my case the layout is japanese. You will probably want 'us' for US keyboards. Now you can reapply your xmodmap settings:

$ xmodmap ~/.xmodmaprc

It's still flaky, but it gets me back on the road.

Read more...

]]>
http://www.ninthavenue.com.au/alt-tab-shortcuts-broken-in-ubuntu-lucid-with-xmodmap http://www.ninthavenue.com.au/alt-tab-shortcuts-broken-in-ubuntu-lucid-with-xmodmap Thu, 15 Dec 2011 06:52:35 +1000
<![CDATA[Using AddThis fb:like And g:plusone Attributes With Facelets]]> Facelets won't parse code like this because of the semicolons:

<a class="addthis_button_google_plusone" g:plusone:size="medium"></a>

The following exception is raised.

javax.faces.view.facelets.FaceletException: Error Parsing /apps/cms/tags/share.xhtml: Error Traced[line: 15] Attribute name "g:plusone" associated with an element type "a" must be followed by the ' = ' character.

CDATA escaping and f:verbatim doesn't fix the problem. The only way around this I could find is using h:output text without escaping the output.

     <!-- escaped because facelets won't parse the g:plusone:size attribute --> 
     <h:outputText escape="false" value="
       &lt;a class=&quot;addthis_button_google_plusone&quot; g:plusone:size=&quot;medium&quot;&gt;&lt;/a&gt;"/>

Notice the input needs to be escaped because facelet files are XML. Ugly. Something like Tomahawk's t:htmlTag would do a better job, but it doesn't support adding arbitrary attributes.

Read more...

]]>
http://www.ninthavenue.com.au/using-addthis-fb-like-and-g-plusone-attributes-with-facelets http://www.ninthavenue.com.au/using-addthis-fb-like-and-g-plusone-attributes-with-facelets Thu, 1 Dec 2011 08:32:20 +1000
<![CDATA[MRTG Email Notifications And Alerts]]> While searching the MRTG docs for some settings, I stumbled across the following option:

ThreshMinI (PER TARGET)

This is the minimum acceptable value for the Input (first) parameter. If the parameter falls below this value, the program specified in ThreshProgI will be run and a mail will be sent to the ThreshMailAddress if specified. If the value ends in '%' then the threshold is defined relative to MaxBytes.

Although my version of MRTG didn't support ThreshMailAddress, it was pretty simple to rig up a shell script to send email notifications when threshold values are reached.

Here is the MRTG config I added to notify me if the cpu usage exceeds 75% or the disks reach 95% capacity.

Read more...

]]>
http://www.ninthavenue.com.au/mrtg-email-notifications-and-alerts http://www.ninthavenue.com.au/mrtg-email-notifications-and-alerts Tue, 30 Aug 2011 13:22:41 +1000
<![CDATA[VIM Key Bindings In Ubuntu Using Xmodmap]]> I really did deserve to be slapped for cleaning my laptop keyboard using water. My punishment was a bunch of dead keys, including the left arrow key. This is really annoying, and as it happens they simply don't stock replacement keyboards for my laptop in Lima. I searched everywhere.

With no other real option than to remap my broken keys I stumbled across something really cool in Ubuntu / Linux. Xmodmap is a utility I had used before for remapping dead keys, but I wasn't aware of the Mode_switch modifier which basically acts as an additional Control / Shift button.

The magic happens when you map Mode_switch to the otherwise useless Caps Lock key. Voila! Instantly you have a whole new set of shortcuts available using Caps Lock that you can use for ALL your applications. I've set up VIM style navigation keys, and shortcuts for hard to reach common keys like escape, backspace, delete, page up and page down.

Read more...

]]>
http://www.ninthavenue.com.au/vim-key-bindings-in-ubuntu-using-xmodmap http://www.ninthavenue.com.au/vim-key-bindings-in-ubuntu-using-xmodmap Wed, 27 Jul 2011 14:31:51 +1000
<![CDATA[Outlook 2010 Ignores CSS Padding]]> Wow, Microsoft write some pretty spaced out stuff. Or not-so-spaced-out, as the case may be. I just learnt that Outlook 2010 ignores CSS padding styles on all elements except <td>.

Why?

I have no idea.

Check out this blog for more information.

Read more...

]]>
http://www.ninthavenue.com.au/outlook-2010-ignores-css-padding http://www.ninthavenue.com.au/outlook-2010-ignores-css-padding Thu, 23 Jun 2011 13:44:00 +1000
<![CDATA[f:viewParam Validation Doesn't Fit With f:event]]> When I first head the JSF Expert Group talking about page parameters, I thought "great!". Fast forward a year and a half and after trying them out I got a bit of a surprise. My events were running even when validation failed which is quite different to a normal JSF postback where events are only run if the model is successfully validated and updated.

  <f:metadata>
    <f:viewParam label="Name" name="name" value="${subscribersBean.new.name}"/>
    <f:viewParam label="Email" name="email" value="${subscribersBean.new.email}" required="true">
      <web:validateEmail/>
    </f:viewParam>
    <f:event type="preRenderView" listener="${subscriptionsBean.subscribe}"/>
   </f:metadata>

Read more...

]]>
http://www.ninthavenue.com.au/f-viewparam-validation-doesn-t-fit-with-f-event http://www.ninthavenue.com.au/f-viewparam-validation-doesn-t-fit-with-f-event Tue, 21 Jun 2011 15:27:02 +1000
<![CDATA[JSF / Facelets Session Leak]]> Here is a bug that is fixed in JSF 2.1.0, but not the 2.0.x series. Bad Facelets code forces session creation for every request. One workaround is to remove the facelets.Encoding attribute from the FacesContext before rendering using a custom ViewHandler:

    /**
     * The default FaceletViewHandler uses session attributes the track
     * the response encoding to use. We can avoid session creation by 
     * removing the facelets.Encoding attribute from the FacesContext.
     */
    @Override
    public void renderView(FacesContext faces, UIViewRoot root)
            throws IOException {
        faces.getAttributes().remove("facelets.Encoding");
        super.renderView(faces, root);
    }

The problem with this is that the template encoding then defaults to ISO-8859-1 while most of will be using UTF-8. So we need another solution.

Read more...

]]>
http://www.ninthavenue.com.au/jsf-facelets-session-leak http://www.ninthavenue.com.au/jsf-facelets-session-leak Tue, 21 Jun 2011 15:27:33 +1000
<![CDATA[Unwrapping JSF java.lang.IllegalArgumentException: null source]]> Here's a great mysterious JSF error using Mojarra 2.0.2:

java.lang.IllegalArgumentException: null source
	at java.util.EventObject.<init>(EventObject.java:38)
	at javax.faces.event.SystemEvent.<init>(SystemEvent.java:71)
	at javax.faces.event.ComponentSystemEvent.<init>(ComponentSystemEvent.java:73)
	at javax.faces.event.PostRestoreStateEvent.<init>(PostRestoreStateEvent.java:73)
	at com.sun.faces.lifecycle.RestoreViewPhase.deliverPostRestoreStateEvent(RestoreViewPhase.java:265)
	at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:251)
	at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
	at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:111)
	at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at au.com.ninthavenue.webcore.webapp.WebcoreFilter.doFilter(WebcoreFilter.java:452);

Read more...

]]>
http://www.ninthavenue.com.au/unwrapping-jsf-java-lang-illegalargumentexception-null-source http://www.ninthavenue.com.au/unwrapping-jsf-java-lang-illegalargumentexception-null-source Tue, 21 Jun 2011 15:15:00 +1000
<![CDATA[JSF java.io.FileNotFoundException: Not Found in ExternalContext as a Resource]]> Really annoying. JSF 2/Facelets throws an exception for every request to resource which doesn't exist. You would have thought a 404 response would have made more sense. Here is the stack trace:

GRAVE: El Servlet.service() para servlet Faces Servlet lanzó una excepción
java.io.FileNotFoundException: /apps/foo.xhtml Not Found in ExternalContext as a Resource
	at com.sun.faces.facelets.impl.DefaultFaceletFactory.resolveURL(DefaultFaceletFactory.java:224)
	at com.sun.faces.facelets.impl.DefaultFaceletFactory.resolveURL(DefaultFaceletFactory.java:265)
	at com.sun.faces.facelets.impl.DefaultFaceletFactory.getMetadataFacelet(DefaultFaceletFactory.java:201)
	at com.sun.faces.application.view.ViewMetadataImpl.createMetadataView(ViewMetadataImpl.java:114)
	at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:227)
	at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
	at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:111)
	at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)

Read more...

]]>
http://www.ninthavenue.com.au/jsf-java-io-filenotfoundexception-not-found-in-externalcontext-as-a-resource http://www.ninthavenue.com.au/jsf-java-io-filenotfoundexception-not-found-in-externalcontext-as-a-resource Tue, 7 Jun 2011 04:24:35 +1000