Sunday, December 15, 2013

ActionBarCompat: "Dialog" Activity

Trying to convert this project from ActionBarSherlock 4.2.0 to ActionBarCompat I hit this problem where a couple activities were using Theme.Sherlock.Dialog theme and there's no similar available  anymore.

So here is my attempt to port that over into an ActionBarCompat project, inspiring from ActionBarSherlock theme (longer term note, I should be looking at using DialogFragment(s) instead, but here it is now for transition).
   

We're going to define two separate styles, one for older and one for the newer APIs, with the latter one using the real dialog theme:
<style name="AppTheme.Dialog" parent="android:Theme.Light">
 <item name="android:windowNoTitle">true</item>
 <item name="android:windowFrame">@null</item>
 <item name="android:windowIsFloating">true</item>
 <item name="android:windowContentOverlay">@null</item>
 <item name="android:windowBackground">@drawable/dialog_full_holo_light</item>
 <item name="android:maxWidth">600dp</item>
 <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
<style name="AppTheme.Dialog" parent="android:Theme.Holo.Light.Dialog">
 <item name="android:windowNoTitle">true</item>
 <item name="android:windowCloseOnTouchOutside">true</item>
</style>

The background drawable is coming from the Android project resources or if you have downloaded them already using Android SDK Manager, copy from ANDROID_SDK/platforms/android-xx/data/res/. That makes for the nice rounded corners, shadow and the spacing on each side.

Now we're going to use the new theme with our "dialog" activity:
<activity
 android:name="DialogActivity"
 android:theme="@style/AppTheme.Dialog" />

Now to the actual DialogActivity, there's nothing special about it but you might notice that it doesn't extend ActionBarActivity, but instead a plain Activity, and that is for a few reasons. For one, it doesn't need it, it's not like the dialog will have an action bar (nor that it's possible I believe), but more importantly you will hit a few issues with that.
  1. ActionBarActivity requires a theme that extends from Theme.AppCompat, otherwise your app will crash with this error:

    java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

    You could build a dialog theme that's based on that, but you won't be able to use the real Holo dialog themes with later APIs, like we did above.

  2.  More importantly, even if you fix point 1, you will hit this error on older APIs, as ActionBarView checks for size and it will crash with:

    java.lang.IllegalStateException: ActionBarView can only be used with android:layout_width="MATCH_PARENT" (or fill_parent)
public class DialogActivity extends Activity {
  [...]
}
* * *

Source code (Google Code - SVN):
Prerequisites :
Resources:

Wednesday, November 6, 2013

XBMC4Xbox Remote v2.9.9.1 - Android



XBMC4Xbox Remote control application.



Download:

Support:

Changes:
v2.9.9.1 (Mon, 24 Oct 2016):
 * Fix: display issues with high resolution devices
 * Fix: occasional crashes in main screen

v2.9.9 (Sun, 21 Sep 2014):
 * New: Main > Programs (thanks AwesomeNurd):
   - Apps / Games / Emulators;
   - available to premium only;
 * Mod: Main > Touchpad:
   - available to all;
 * New: Analytics > Versions;

v2.9.8 (Sun, 13 Jul 2014):
 * Fix: Crashes on Android 2.1 & 2.2

v2.9.7 (Fri, 4 Jul 2014):
 * Fix: Cannot share / play "youtu.be" URLs (thanks sahib12);
 * Fix: Widget > Currently playing thumbnail (thanks AwesomeNurd);
 * Fix: Movies > Files > cannot play video files (thanks Fastlane);
 * Fix: Crashes on Android 3.2
 * New: Commands > Screenshots

v2.9.6 (Sun, 19 Jan 2014):
 * Main :
  - Remember last used XBMC4Xbox;
  - Allow to delete XBMC4Xbox entry (from "edit" dialog);
 * Main > Network Scan
  - Allow to cancel scan;
  - Update entry with same MAC address (instead of adding new one);

v2.9.5 (Sun, 12 Jan 2014):
 * Main:
  - better use of space on large screens / landscape mode;
 * Movies, Music, Addons, System Info, Analytics, Feeds:
  - horizontal swiping between tabs ("Library" tabs require edge swiping)
 * Translation :
  - French, Spanish (first draft) 
 * Technology changes (ActionBarCompat, Picasso, OkHttp);

v2.9.4 (Sun, 1 Dec 2013):
 * TV Episodes :
  - fill entire horizontal space (collapsible);
  - display fanart background;
 * Media Send :
  - YouTube filter too broad (listed for URLs that could not be handled);
  - allow to send other media URLs (HTTP, audio + video);

v2.9.3 (Fri, 29 Nov 2013) :
 * Main:
  - ability to add new XBMC4Xbox entries (spinner);
 * Movies > Library:
  - group movies by first letter (collapsible);
  - gallery filling horizontal space;
 * Analytics :
  - show country flags;

v2.9.2 (Sun, 3 Nov 2013):
 * Crashing at startup or missing premium features

v2.9.1 (Sat, 2 Nov 2013):
 * Widget - home / lock screen:
  - full functionality to premium only; 

v2.9.0 (Sat, 12 Oct 2013):
 * TouchPad:
  - full functionality available to premium only;
 * Feeds:
  - highlight new entries;
 * Wi-Fi Hotspot (in-car) scenario (thanks to sealeysonic);

v2.8.9 (Fri, 30 Aug 2013):
 * Addons:
  - available to all;
 * Main > Feeds ticker:
  - do not scroll on low refresh devices (e.g. Nook);

v2.8.8 (Sun, 25 Aug 2013):
 * Music > Playlist:
  - select playlist / party mode;

v2.8.7 (Sat, 17 Aug 2013):
 * Music > Library:
   - crashing with large library;
   - changed to expandable list + scrolling horizontal grid
 * DVD Remote > Volume :
   - crashing on Android 3.2
   - volume follows slider (requires Event Server enabled)

v2.8.6 (Thu, 15 Aug 2013):
 * Keyboard: add "<" and ">" navigation buttons (repeatable);

v2.8.5 (Sun, 28 July 2013):
 * Feeds - faster load, using FeedBurner
 * DVD Remote + Volume: volume control with hardware buttons
 * Analytics: today, 7 days, 30 days, all time
 * Preferences > Themes (dark, light, green) - premium only

v2.8 (Wed, 10 July 2013)
 * Add "Addons" (premium only, for now)
 * DVD Remote: repeat arrow keys on hold
 * TV Episodes: show "watched" icon
 * Main > Analytics (new), see:
  https://sites.google.com/site/xbmc4xboxremoteandroid/xbmc4xbox-remote/analytics
 * Misc fixes

v2.7.5 (Sat, 15 June 2013)
 * Add "Voice" search (where available);
 * Music > Playlist - show song numbers instead of icons;
 * Keyboard - close when "done";
 * Handle SetResponseFormat() change by Web Remote;
 * Image cache not working correctly;

v2.7.1 (Sat, 8 June 2013)
 * Add: "Help" options menu item - Main;
 * Fix: CPU / battery drain while in background (thanks to Kozz);

v2.7 (Sun, 2 Jun 2013):
 * Send YouTube videos to XBMC4Xbox (requires YouTube plugin);
   https://sites.google.com/site/xbmc4xboxremoteandroid/xbmc4xbox-remote/youtube-playback

      Saturday, August 3, 2013

      VMware Player 5.0.2 on Linux Mint 15 - VMware Tools installation error

      Trying to install VMware Tools for Windows on a Windows virtual machine running on Linux Mint 15 you might get the error below:

      There was a problem updating a software component. Try again later and if the problem persists, contact VMware Support or your system administrator.


      A process trace on vmplayer  reveals what might be the obvious for some (access denied)...
      $sudo strace -f -eopen -p `pidof vmplayer`
      [...]
      13783 open("/etc/vmware-installer/database", O_RDWR|O_CREAT, 0644) = -1 EACCES (Permission denied)
      [...]

      Run vmplayer elevated to be able to install VMware tools:
      gksudo vmplayer &

      A new strace shows success where it failed earlier running as a normal user:
      $sudo strace -f -eopen -p `pidof vmplayer`
      [...]
      16026 open("/etc/vmware-installer/database", O_RDWR|O_CREAT, 0644) = 4
      [...]
      


      Sunday, July 14, 2013

      Windows 8.1 Preview on ASUS R2H

      ASUS R2H UMPC running Windows 8.1 Preview!

      Installation:





      Sunday, June 23, 2013

      Eclipse CDT and Microsoft Visual C++ compiler

      Trying to work on this legacy project in Eclipse instead of Visual Studio 2003 I hit a few snags, so I started again by creating a new project to see if possible to use the VC++ compiler in the first place - so here it is sharing this with you. Although proficient with Eclipse for Java development I'm still a C++ newbie, so if any Visual Studio / C++ veterans reading this, feel free to correct me :-)

      • Eclipse 4.3 RC3 (CDT 8.2)
      • Visual Studio 2003 (VC7)

      Let's create the project - File > New > C++ Project



      The project will have a few compile errors, due to missing includes:


      Now to fix this, you can obviously search the VC++ directories for iostream in this case, or if you want to go all the includes you can run vcvars32.bat and show the INCLUDE environment variable, e.g.

      C:>"C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\vsvars32.bat"
      Setting environment for using Microsoft Visual Studio .NET 2003 tools.
      (If you have another version of Visual Studio or Visual C++ installed and wish
      to use its tools from the command line, run vcvars32.bat for that version.)
      
      C:>set INCLUDE
      INCLUDE=C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\ATLMFC\INCLUDE;...
      
      I'll just add the minimum necessary to fix the project - go to Project (right-click) > Properties > C/C++ Build > Settings > Tool Settings > C++ Compiler > Preprocessor > Add...


      Coming back to the Project Explorer you may notice the Includes section - if the errors still persist, right click the project and choose Index > Rebuild or choose Project (top menu) > C/C++ Index > Rebuild and that should clear them.


      This concludes the CDT compiler part, now what we want to do is build the project using VC++ compiler and linker. If you chose to Build the project (the "hammer" or right-click build), you will notice the errors in the console where it can't find CL.exe.
      01:15:55 **** Incremental Build of configuration Debug for project HelloWorld ****
      Info: Internal Builder is used for build
      cl /c /EHs /MD /Zi "/IC:\\Program Files\\Microsoft Visual Studio .NET 2003\\Vc7\\include" /nologo "/Fosrc\\HelloWorld.obj" "..\\src\\HelloWorld.cpp" 
      Cannot run program "cl": Launching failed
      
      Error: Program "cl" not found in PATH
      PATH=[...]
      
      01:15:55 Build Finished (took 46ms)
      

      We're going to fix this by adding VCx\bin location to the PATH environment variable: Project > Properties > C/C++ Build > Environment > PATH... We're going to add it to all configurations, not just debug.


      If you're going to build the project again, you will notice that CL.exe now runs correctly, but it ends abruptly and it's not followed by the linking.


      Running a ProcMon trace it shows that CL.exe could not find mspdb71.dll that it needs and it just ends there. To resolve this we'll need to add the correct location (..\Common7\IDE) to the PATH environment variable we defined at previous step.


      Rebuilding the project looks better, but now linking will fail to find msvcprt.lib.


      To fix this, we're going to define the LIB environment variable (VC7\lib), same as we did for PATH earlier - it also also fail for kernel32.lib so we're going to add Vc7\PlatformSDK\Lib as well:


      All is working well now and the .exe is generated:


      Now that we got an exe we get to run it - either manually create it in Run Configurations or right-click the project > Run As > Local C/C++ Application:


      Now if you change the source and click to "run" again, it will kick the build automatically and you'll see your changes in action - you may actually notice the CDT build output for a while, or you can configure to see both console views as the same time.


      Friday, May 24, 2013

      Android Emulator performance - Windows 8 vs Ubuntu 13.04

      I mentioned earlier I've been trying Ubuntu for Android development, and today I'll try to capture the difference in performance of the Android emulator on the two platforms. I haven't seen much difference in terms of Eclipse performance, but when it comes down to the Emulator it is quite obvious.

      I'm not quite sure what makes the difference so large, might be the OS or maybe the QEMU software implementation on each OS. I chose to test with Android 4.2.2 AVD images just to push it - older versions like Android 2.1 or 2.3 seem to work better.

      Also I'm running the tests on my home Dell laptop which is a few years old, making the difference even more obvious. I find Windows 8 on this machine faster than Windows 7 and generally a pleasant experience while probably on par with Ubuntu 13.04 in terms of performance.

      While I find Windows more familiar and easier to use, because of the poor Android emulator experience on Windows I find using Ubuntu a less annoying experience for this particular task.

      Environment


      Machine

      • Dell Vostro 1520
      • 15.4” Widescreen WXGA+ CCLF, anti-glare (1440x900);
      • Intel Core 2 Duo P8600 (2.4 GHz, 1066 Mhz, 3 MB cache);
      • Intel® 45 Express chipset
      • Intel Integrated GMA 4500MHD;
      • 4 GB RAM, 800 MHz DDR2 Dual Channel;
      • 320 GB Seagate ST9320423ASG 7200 rpm, NCQ enabled;

        Windows 8

        • Windows 8 (x64)
          Version: 6.2.9200
        • android-sdk\tools\emulator-x86.exe
          Version: n/a
          Build Time: Tue May 14 02:49:56 2013
        • Intel HAXM 1.0.6 
        • Intel 4 Series Express display driver
          WDDM 1.1, 8.15.10.2702

        Ubuntu 13.04

        • Ubuntu 13.04 (x64)
          Linux VOSTRO 3.8.0-21-generic #32-Ubuntu SMP Tue May 14 22:16:46 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
        • android-sdk/tools/emulator64-x86
          Android emulator version 22.0 (build_id OPENMASTER-675183)

        Eclipse / Android SDK

        • Eclipse 4.3 M7 x64, Kepler
          • 20130509-1105 (x64) - Windows
          • 20130225-0426 (x64) - Ubuntu
        • Oracle / Sun JDK 1.7.0_21 (x64)
        • Android SDK Tools 22
        • Android SDK Platform Tools 17
        • Android 4.2.2 (API 17) - Intel x86 Atom System Image rev. 1

        Android Virtual Devices


        Phone

        • 3.2" QVGA (320 x 480: mdpi)
        • Android 4.2.2 - API Level 17
        • Intel Atom (x86)
        • RAM: 512 MB
        • VM Heap: 16 MB
        • Internal Storage: 200 MB
        • SD Card: 200 MB
        • Use Host GPU

        Nexus 7

        • Nexus 7 (7.27", 800 x 1280: tvdpi)
        • Android 4.2.2 - API Level 17
        • Intel Atom (x86)
        • RAM: 512 MB
        • VM Heap: 32 MB
        • Internal Storage: 200 MB
        • SD Card: 200 MB
        • Use Host GPU

        Windows 8


        Performance on the Phone AVD is acceptable although slightly laggy, using Intel HAXM 1.0.6 and "Use Host GPU" option. My GPU chip / driver don't seem to support OpenGL 2.0 (1.1 only?), showing in the startup messages.
        Starting emulator for AVD 'Phone_4.2'
        Failed to create Context 0x3005
        could not get wglGetExtensionsStringARB
        emulator: WARNING: Could not initialize OpenglES emulation, using software renderer.
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        emulator: device fd:688
        HAX is working and emulator runs in fast virt mode
        AnTuTu Benchmark v3.3 scores:
        • Total: 13,837
        • CPU: 7,503
        • GPU: 141
        • RAM: 5,643
        • I/O: 550

        For Nexus 7 AVD, I had to drop down the RAM to 512 MB to get HAXM working with it, maybe because I configured HAXM earlier to only use 512 MB, trying to avoid the system running out of memory (only 4 GB of RAM). The experience is laggy (1-2 seconds wait between activities), barely usable - disabling animations helps a bit.
        Starting emulator for AVD 'Nexus_7'
        Failed to create Context 0x3005
        could not get wglGetExtensionsStringARB
        emulator: WARNING: Could not initialize OpenglES emulation, using software renderer.
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        could not get wglGetExtensionsStringARB
        emulator: device fd:692
        HAX is working and emulator runs in fast virt mode
        emulator: emulator window was out of view and was recentered
        AnTuTu Benchmark v3.3 scores:
        • Total: 11,572
        • CPU: 6,282
        • GPU: 42
        • RAM: 4,713
        • I/O: 535

        Ubuntu 13.04


        Emulators start in seconds (15-30) to desktop, compared to minutes (1-3) in Windows!

        Phone 4.2 AVD is quite snappy, and haven't done anything special for it like install KVM. No additional messages at startup. AnTuTu shows same OpenGL 1.1 on screen during testing, but the FPS is much higher (~60 fps)!

        AnTuTu Benchmark v3.3 scores:
        • Total: 15,440
        • CPU: 7,728
        • GPU: 1,234
        • RAM: 5,918
        • I/O: 560

        Nexus 7 AVD starts quickly and it's very snappy! Navigation through activities and scrolling is very responsive, a huge difference from Windows. Some error messages at image startup though:
        Starting emulator for AVD 'Nexus_7'
        Failed to load libGL.so
        error libGL.so: cannot open shared object file: No such file or directory
        Failed to load libGL.so
        error libGL.so: cannot open shared object file: No such file or directory
        emulator: emulator window was out of view and was recentered
        Reading this thread I created the link below to fix the error message - didn't see any change in the scores though.
        # sudo apt-get install libgl1-mesa-dev
        # sudo ln -sv /usr/lib/x86_64-linux-gnu/libGL.so /usr/lib/libGL.so
        ‘/usr/lib/libGL.so’ -> ‘/usr/lib/x86_64-linux-gnu/libGL.so’
        
        AnTuTu Benchmark v3.3 scores:
        • Total: 14,104
        • CPU: 7,365
        • GPU: 538
        • RAM: 5,662
        • I/O: 560

        Saturday, May 18, 2013

        Seagate GoFlex Home NAS - SVN server installation

        This comes with a story as well, out of some need, not just technical curiosity :-) I recently gave Ubuntu a shot to see how it compares with Windows for Android development (I might post something about it later), and for Windows I used a local VisualSVN Server - a no brainer SVN server package, very easy to install and maintain. When in Ubuntu I can't use it, well, not as it is. I can obviously set up an SVN server on Ubuntu and point to the same repositories (I've actually managed to access them through running svnserve and svn:// URLs as a start), but having a separate SVN server on GoFlex Home would make me feel better knowing that base copy is on another machine in case my laptop HDD crashes, I would only loose the working copy.


        Preparation


        See previous post where we installed ipkg (will use it to install the SVN package) and we setup a system account for HDD storage (will use the same again to store the SVN repositories).

        SVN software

        bash-3.2# ipkg install svn
        Installing svn (1.7.7-1) to root...
        Downloading http://ipkg.nslu2-linux.org/feeds/optware/cs08q1armel/cross/stable/svn_1.7.7-1_arm.ipk
        package apr-util suggests installing sqlite
        package apr-util suggests installing openldap-libs
        Installing neon (0.29.6-1) to root...
        [...]
        Successfully terminated.
        
        bash-3.2# svnadmin --version
        svnadmin, version 1.7.7 (r1393599)
           compiled Oct 10 2012, 00:19:36
        [...]

        SVN repositories


        We will prepare the SVN repositories location (/srv/svn linked to external storage), where you can create a new blank repository (I'll call it test):

        bash-3.2# mkdir -p /home/0loop0/srv/svn/
        bash-3.2# ln -sv /home/0loop0/srv/svn/ /srv/
        
        bash-3.2# cd /srv/svn
        bash-3.2# df -h .
        Filesystem            Size  Used Avail Use% Mounted on
        /dev/loop0            985M   50M  885M   6% /home/0loop0
        
        bash-3.2# mkdir repos
        bash-3.2# cd repos
        
        bash-3.2# svnadmin create test
        bash-3.2# svnadmin verify test
        * Verified revision 0.
        
        bash-3.2# svn mkdir file:///srv/svn/repos/test/trunk -m "Initial commit"
        Committed revision 1.
        
        bash-3.2# svn mkdir file:///srv/svn/repos/test/branches -m "Initial commit"
        Committed revision 2.
        
        bash-3.2# svn mkdir file:///srv/svn/repos/test/tags -m "Initial commit"
        Committed revision 3.
        
        bash-3.2# svn list -v file:///srv/svn/repos/test/
              3 root                  May 18 21:35 ./
              2 root                  May 18 21:35 branches/
              3 root                  May 18 21:35 tags/
              1 root                  May 18 21:34 trunk/

        OR, if you have a dump file from a previous repository, you can restore that instead:
        bash-3.2# cd /srv/svn/repos
        
        bash-3.2# svnadmin create android-apps
        bash-3.2# svnadmin load android-apps < android-apps.svn-dump

        SVN server (http://) - FAIL


        Unfortunately, although this is how I saw it setup in the first place (no additional processes, running as a module in Appache HTTP server), I couldn't get this working. After linking the additional config and changing it to correctly point to the modules (change LoadModule from libexec to /opt/libexec/...), the process will die with Segmentation fault.

        bash-3.2# ln -sv /opt/etc/apache2/conf.d/mod_dav_svn.conf /etc/httpd/conf.d/
        create symbolic link `/etc/httpd/conf.d/mod_dav_svn.conf' to `/opt/etc/apache2/conf.d/mod_dav_svn.conf'
        
        bash-3.2# vi /etc/httpd/conf.d/mod_dav_svn.conf
        
        bash-3.2# service httpd restart
        Stopping httpd:                                            [  OK  ]
        Starting httpd: /bin/bash: line 1:  8741 Segmentation fault      /usr/sbin/httpd
                                                                   [FAILED]

        Trying to debug this doesn't show more than maybe an incompatibility between the APR library used to compile Apache HTTP server and the modules...
        bash-3.2# apk install gdb
        [...]
        
        bash-3.2# gdb httpd
        GNU gdb 6.8
        [...]
        
        (gdb) run
        Starting program: /usr/sbin/httpd
        (no debugging symbols found)
        (no debugging symbols found)
        [Thread debugging using libthread_db enabled]
        [New Thread 0x404d1000 (LWP 7770)]
        
        Program received signal SIGSEGV, Segmentation fault.
        [Switching to Thread 0x404d1000 (LWP 7770)]
        0x40736034 in apr_pool_create_ex () from /opt/lib/libapr.so.0

        Fix it back by removing the link, otherwise the webserver will not start next time:

        bash-3.2# unlink /etc/httpd/conf.d/mod_dav_svn.conf
        
        bash-3.2# service httpd restart
        Stopping httpd:                                            [FAILED]
        Starting httpd:                                            [  OK  ]


        SVN server (svn://)

        Although not ideal, as it requires a separate process (svnserver) and it will make it harder to setup as we will need to start it with a script to run the external partition is mounted, here we go...

        bash-3.2# cd /etc/oe-initscripts/oe-bootfinish/start.d/
        
        bash-3.2# cat > 1_svnserve << EOL
        /opt/bin/svnserve -d -r /srv/svn/repos
        EOL
        
        bash-3.2# cat 1_svnserve
        /opt/bin/svnserve -d -M 8 -r /srv/svn/repos
        
        bash-3.2# chmod a+x 1_svnserve

        Test the script, see how the SVN server works:

        bash-3.2# ./1_svnserve
        
        bash-3.2# svn list -v svn://localhost/test
              3 root                  May 18 21:35 ./
              2 root                  May 18 21:35 branches/
              3 root                  May 18 21:35 tags/
              1 root                  May 18 21:34 trunk/
        Let's also create the stop script - remember in previous post we created the unmount script with a "9_" prefix so that we can create the svnserve script with a lower prefix to make it run before it, otherwise the partition will be in use and won't be able to unmount and flush, possibly leading to loss of data or corruption of the SVN repository...
        bash-3.2# cd /etc/oe-initscripts/oe-bootfinish/stop.d/
        
        bash-3.2# cat > 8_svnserve << EOL
        killall svnserve
        EOL
        
        bash-3.2# chmod a+x 8_svnserve
        Trying the stop script...
        bash-3.2# ./8_svnserve
        
        bash-3.2# svn list -v svn://localhost/test
        svn: E000111: Unable to connect to a repository at URL 'svn://localhost/test'
        svn: E000111: Can't connect to host 'localhost': Connection refused

        Finally reboot and try accessing the repository from another machine, using svn://goflex_home/test/ (on Windows) or svn://ip_address/test (on Linux - that's because of the character in the name, nslookup works with "goflex home", but not svn...)

        SVN backup


        As we've mounted the SVN repositories inside of the file partition, I know I would feel more comfortable to know there is a backup that I can easily access if that gets corrupted. So what we're going to do is to setup a script to run either every night or every two days and make a dump of the repository, and make that available in the \\goflex_home\GoFlex Home Backup directory.
        bash-3.2# cd /srv/svn
        bash-3.2# cat > svn-backup << EOL
        #!/bin/bash
        
        if [ -z "\$1" ]; then
          echo -e "Usage:\n   \$0 repository-name"
          exit 99
        fi
        
        OUTPUT_DIR=/home/system/GoFlex\ Home\ Backup/svn
        OUTPUT_FILE=\$1.svn-dump-\$(date +"%Y%m%d-%H%M%S")
        KEEP_DAYS=31
        
        /opt/bin/svnadmin -M 8 -q dump /srv/svn/repos/\$1 > "\$OUTPUT_DIR/\$OUTPUT_FILE"
        /usr/bin/zip -m -2 -j "\$OUTPUT_DIR/\$OUTPUT_FILE.zip" "\$OUTPUT_DIR/\$OUTPUT_FILE"
        
        echo -e "\nTrimming backups - older than \$KEEP_DAYS days..."
        find "\$OUTPUT_DIR" -name "\$1.*.zip" -mtime +\$KEEP_DAYS -exec ls {} \; -exec rm {} \;
        EOL
        
        bash-3.2# chmod a+x svn-backup
        
        bash-3.2# cd "/home/system/GoFlex Home Backup/"
        bash-3.2# mkdir svn
        bash-3.2# cd svn
        
        bash-3.2# /srv/svn/svn-backup test
        * Dumped revision 0.
        * Dumped revision 1.
        * Dumped revision 2.
        * Dumped revision 3.
           adding: test.svn-dump-20130518-234455 (deflated 71%)
        
        Trimming backups - older than 31 days...
        
        bash-3.2# ls -la *
        total 0
        -rwxrwxrwx 1 apache users 320 May 18 22:07 test.svn-dump-20130518-220731.zip

        Now that we tested the command line we're going to add it to the cron to run every night at 11pm.
        bash-3.2# cat > /etc/cron.d/svn-backup << EOL
        0 23 * * * root /srv/svn/svn-backup test
        EOL
        
        bash-3.2# cat /etc/cron.d/svn-backup
        0 23 * * * root /srv/svn/svn-backup test

        Seagate GoFlex Home NAS - Installing a package manager (again)

        This is a redo of the previous post, with which a had a few annoying problems.

        First, the symbolic links in /opt/lib on NTFS were broken on next boot (converted into plain files) generating the "file too short" errors when running ipkg command - a fix I guess would be to duplicate the .so libraries to replace the symlinks.

        Then later I ran into a very odd issue with Subversion, where the repository created on NTFS would be seen as corrupted. Creating a repository temporarily in /tmp would verify ok, moving it to NTFS would have the error again.
        bash-3.2# cd /home/system/GoFlex\ Home\ Personal/tmp/
        bash-3.2# svnadmin create test
        bash-3.2# svnadmin verify test
        svnadmin: E160004: Corrupt node-revision '0.0.r0/17'
        svnadmin: E160004: Missing cpath field in node-rev '0.0.r0/17'
        Although I don't feel too comfortable with the complexity of the new setup, we will follow the good work done on OpenStora Forums (here), where pippone is suggesting to use a Linux partition image on the HDD and mount it as a loop device. So here we go...

        * * *

        Create Linux partition image


        We will create a 1GB image (to hold /opt and the SVN files later on) - this will be visible through \\goflex_home\GoFlex Home Personal (system account), although when mounted it will be in use so you won't be able to delete it by accident. If you want to hide it, start the filename with a dot (.).
        bash-3.2# cd /home/system/GoFlex\ Home\ Personal/
        
        bash-3.2# dd if=/dev/zero of=ext3_linux_partition.img bs=4k count=256000
        256000+0 records in 256000+0 records out 1048576000 bytes (1.0 GB) copied, 15.6834 seconds, 66.9 MB/s
        
        bash-3.2# losetup /dev/loop0 "`pwd`/ext3_linux_partition.img"
        bash-3.2# losetup -a
        /dev/loop0: [0800]:15206 (/home/system/GoFlex Home Personal/ext3_linux_partition.img)
        
        bash-3.2# mkfs.ext3 /dev/loop0
        mke2fs 1.39 (29-May-2006)
        [...]
        
        bash-3.2# mkdir /home/0loop0
        bash-3.2# mount /dev/loop0 /home/0loop0/
        
        bash-3.2# df -h /home/0loop0
        Filesystem            Size  Used Avail Use% Mounted on
        /dev/loop0            985M   18M  917M   2% /home/0loop0
        
        bash-3.2# ls -la /home/0loop0
        total 20
        drwxr-xr-x  4 root root  4096 May 18 18:37 .
        drwxr-xr-x 14 root root  1024 May 18 17:39 ..
        drwx------  2 root root 16384 May 18 17:35 lost+found
        What we did so far was to create the partition image, setup it up as a loop device and mount it into /home/0loop0. This is not permanent, the mount will be lost with the next boot.

        Persisting the changes


        To make this permanent, the same thread is suggesting to change the /etc/init.d/eo-bootfinish script. But on a second look, the start and stop sections allow for additional external scripts, and that's where we're going to create them. I've named the script with a digit prefix so we can control the order in which they are running - later on we are going to add a command to start svnserve and we want to make sure the partition is mount first.
        bash-3.2# cd /etc/oe-initscripts/oe-bootfinish/start.d/
        
        bash-3.2# cat > 0_loop0 << EOL
        losetup /dev/loop0 "/home/system/GoFlex Home Personal/ext3_linux_partition.img"
        mount /dev/loop0 /home/0loop0/
        EOL
        
        bash-3.2# cat 0_loop0
        losetup /dev/loop0 "/home/system/GoFlex Home Personal/ext3_linux_partition.img"
        mount /dev/loop0 /home/0loop0/
        
        bash-3.2# chmod a+x 0_loop0
        Issue a reboot and check it once back on:
        bash-3.2# df -h /home/0loop0
        Filesystem            Size  Used Avail Use% Mounted on
        /dev/loop0            985M   18M  917M   2% /home/0loop0

        While here, we will also prepare a shutdown script to un-mount and disconnect the loop device.
        bash-3.2# cd /etc/oe-initscripts/oe-bootfinish/stop.d/
        
        bash-3.2# cat > 9_loop0 <<EOL
        umount /home/0loop0
        losetup -d /dev/loop0
        EOL
        
        bash-3.2# cat 9_loop0
        umount /home/0loop0
        losetup -d /dev/loop0
        
        bash-3.2# chmod a+x 9_loop0
        Try running it directly to see if it does the job (/home/0loop0 is now the original directory on NAND, the mount is gone, notice Filesystem value):
        bash-3.2# /etc/oe-initscripts/oe-bootfinish/stop.d/9_loop0
        
        bash-3.2# df -h /home/0loop0
        Filesystem            Size  Used Avail Use% Mounted on
        ubi0:rootfs           212M  161M   51M  77% /

        Relocate /opt to external partition image

        bash-3.2# df -h /opt
        Filesystem            Size  Used Avail Use% Mounted on
        ubi0:rootfs           212M  161M   51M  77% /
        
        bash-3.2# mv /opt/ /home/0loop0/
        
        bash-3.2# ln -sv /home/0loop0/opt/ /opt
        create symbolic link `/opt' to `/home/0loop0/opt/'
        
        bash-3.2# df -h /opt
        Filesystem            Size  Used Avail Use% Mounted on
        /dev/loop0            985M   18M  917M   2% /home/0loop0

        Installing the package manager (finally...)

        bash-3.2# cd /opt
        
        bash-3.2# wget http://ipkg.nslu2-linux.org/feeds/optware/cs08q1armel/cross/stable/ipkg-opt_0.99.163-10_arm.ipk
        [...]
        18:41:31 (80.4 KB/s) - `ipkg-opt_0.99.163-10_arm.ipk' saved [74474/74474]
        
        bash-3.2# tar xvf ipkg-opt_0.99.163-10_arm.ipk
        ./debian-binary
        ./data.tar.gz
        ./control.tar.gz
        
        bash-3.2# tar xvf data.tar.gz
        ./
        ./opt/
        [...]
        
        bash-3.2# rm control.tar.gz data.tar.gz debian-binary ipkg-opt_0.99.163-10_arm.ipk
        
        bash-3.2# mv opt/* .
        bash-3.2# rmdir opt
        
        bash-3.2# ls /opt
        bin  etc  lib  share
        
        bash-3.2# /opt/bin/ipkg -version
        ipkg version 0.99.163
        
        bash-3.2# echo src cs08q1armel http://ipkg.nslu2-linux.org/feeds/optware/cs08q1armel/cross/stable >> /opt/etc/ipkg.conf
        
        bash-3.2# /opt/bin/ipkg update
        Downloading http://ipkg.nslu2-linux.org/feeds/optware/cs08q1armel/cross/stable/Packages
        Updated list of available packages in /opt/lib/ipkg/lists/cs08q1armel
        Successfully terminated.

        Wednesday, May 15, 2013

        Seagate GoFlex Home NAS - Installing a package manager

        This comes with a longer story, so I'll try to keep it short. When researching which NAS to buy at the time I found the OpenStora Wiki going in detail on all the things that you can do with not only Netgear Stora, but also with Seagate GoFlex Home NAS, which made it very appealing to me for a small network server. So today, we're going to follow one of the tutorials and install a package  manager, in preparation for other things, like hopefully installing an SVN server :-)

        A word of warning before we go on - if you are not comfortable with the Linux commands and all that, it's probably best to read up more before you do anything, as you might break your NAS system. The good news is that you might be able to factory reset it, but still it's something that you don't want to come to.

        Update 2013-05-18


        Although this should still work with it's problems (see end of the post), you should follow the new post for an alternative solution.

        Preparation


        Aside from the usual personal accounts, I've created a system account as an Administrator through the web interface. The reason for that is we're going to use it's GoFlex Home Personal directory which resides on the HDD to store things outside of the Flash drive - this is to avoid wear as well as running into space issues (df / shows it only has 50 MB left). Just as well you can use your own personal account if you want to.

        SSH remote access


        The OpenStora Easy Root Access wiki page covers it very well. As the article recommends, Putty for Windows or Ubuntu is a very good choice as it allows you to create a session you can reuse later - you can customize things like the font (Window > Appearance) or more useful the auto-login username (Connection > Data), as this is quite long and not that easy to type (USERNAME_hipserv2_seagateplug_XXXX-XXXX-XXXX-XXXX). You can find the product key on the bottom of the unit, or remotely after you logon through the web interface (http://goflex_home) see the About GoFlex Home link in the bottom left corner.

        Installing the package manager


        This is pretty much following the steps in the OpenStora Installing a package manager wiki page, with just a change around the relocation of the /opt to the HDD.

        1. Switch to root access (ignore the audit error):
        -bash-3.2$ sudo -E -s
        Password:
        audit_log_user_command(): Connection refused
        bash-3.2# whoami
        root
        
        2. Move the /opt directory to the HDD and create a symbolic link to it:
        bash-3.2# df /opt
        Filesystem           1K-blocks      Used Available Use% Mounted on
        ubi0:rootfs             216180    164508     51672  77% /
        
        bash-3.2# mv /opt/ /home/system/GoFlex\ Home\ Personal/
        
        bash-3.2# ln -sv /home/system/GoFlex\ Home\ Personal/opt/ /opt
        create symbolic link `/opt' to `/home/system/GoFlex Home Personal/opt/'
        
        bash-3.2# df /opt
        Filesystem           1K-blocks      Used Available Use% Mounted on
        /dev/sda             2930255996 471090240 2459165756  17% /home/system/GoFlex Home Personal
        
        Additionally, we're going to allow everyone to read from the new location:
        bash-3.2# chmod a+rx /home/system/
        bash-3.2# chmod a+rx /home/system/GoFlex\ Home\ Personal/
        bash-3.2# chmod a+rx /home/system/GoFlex\ Home\ Personal/opt/
        
        3. Download and extract the ipkg package.
        bash-3.2# cd /home/system/GoFlex\ Home\ Personal/
        
        bash-3.2# wget http://ipkg.nslu2-linux.org/feeds/optware/cs08q1armel/cross/stable/ipkg-opt_0.99.163-10_arm.ipk
        [...]
        14:56:40 (80.4 KB/s) - `ipkg-opt_0.99.163-10_arm.ipk' saved [74474/74474]
        
        bash-3.2# tar -xvf ipkg-opt_0.99.163-10_arm.ipk
        ./debian-binary
        ./data.tar.gz
        ./control.tar.gz
        
        bash-3.2# tar -xvf data.tar.gz
        ./
        ./opt/
        ./opt/bin/
        [...]
        
        bash-3.2# rm control.tar.gz data.tar.gz debian-binary ipkg-opt_0.99.163-10_arm.ipk
        
        bash-3.2# /opt/bin/ipkg -version
        ipkg version 0.99.163
        
        bash-3.2# echo src cs08q1armel http://ipkg.nslu2-linux.org/feeds/optware/cs08q1armel/cross/stable >> /opt/etc/ipkg.conf
        
        bash-3.2# /opt/bin/ipkg update
        Downloading http://ipkg.nslu2-linux.org/feeds/optware/cs08q1armel/cross/stable/Packages
        Updated list of available packages in /opt/lib/ipkg/lists/cs08q1armel
        Successfully terminated
        

        4. PATH variable (optional)

        Finally, we're going to alter the PATH environment variable so you can run programs from /opt/bin and /opt/sbin, otherwise you'll have to always specify the full path - see the OpenStora wiki page.

        It is probably a good idea to keep the existing SSH session and open up a new one to test the new PATH. This way you have a working session to fix things back, otherwise you'll loose direct access to system commands and you'll have to specify full paths for everything (I know it happened to me :)


        Update 2013-05-17:


        If you find on next reboot that ipkg fails with the message below, it appears to be a problem with the symbolic links on NTFS, see this thread here - they seemed to be valid when first extracted... Since they were links to each other, I will just duplicate the file to replace the links.

        bash-3.2# ipkg
        ipkg: error while loading shared libraries: /opt/lib/libipkg.so.0: file too short
        
        bash-3.2# ls -la /opt/lib/
        total 152
        -rwxrwxrwx 1 apache users     82 May 17 23:45 1
        drwxrwxrwx 1 apache users   4096 May 15 15:00 ipkg
        -r-xr-xr-x 1 apache users     42 May 15 14:57 libipkg.so
        -r-xr-xr-x 1 apache users     42 May 15 14:57 libipkg.so.0
        -rwxrwxrwx 1 apache users 150260 Feb 15  2012 libipkg.so.0.0.0
        
        bash-3.2# cp libipkg.so.0.0.0 libipkg.so
        bash-3.2# cp libipkg.so.0.0.0 libipkg.so.0
        
        bash-3.2# ipkg -v
        ipkg version 0.99.163

        Sunday, April 28, 2013

        Ubuntu 13.04 on ASUS R2H

        With Ubuntu 13.04 "Raring Ringtail" just released the other day, thought I would upgrade the recent 12.04 installation. Instead of doing a fresh install, I've decided for an upgrade - never done one before and wanted to see how well it works.

        I wouldn't recommend it though, for two reasons: one cause I first had to upgrade to 12.10 (~700 MB download) and then again to 13.04 (another 700MB) - much larger than downloading the full 13.04 ISO - and second cause the wi-fi network was quite slow at around 50 KB/s and took ages to download and seemed like hogging the wi-fi channel for my laptop to use at the same time (seen similar with the original install when downloading updates). One advantage on the other hand is that it kept what I had, like apps and screen resolution / calibration.

        Some parts of the UI seem a little more responsive (like dragging the on-screen keyboard around), but the I guess visual effects (fading, zooming) make it slower. Other things like starting programs seem slower as well - the file manager starts after a few seconds after being clicked with a gray window for another couple of seconds, then you get your files. VNC connection seems slower as well.

        Cue Ubutu Software Center > Unity Tweak Tool.


        CompizConfig Settings Manager is still there - disabling animations and fading windows locks up the UI for a good few seconds, but it will come back eventually.


        One thing I didn't cover in previous post was the on-board keyboard, which comes very handy when you use the unit away from your desk. Search for OnBoard Settings:


        Another thing I didn't cover was how to enable hibernation. Press the Power hardware button and will be presented with the lock / standby / hibernate / shutdown dialog.
        http://askubuntu.com/questions/94754/how-to-enable-hibernation

        And this is all for now...


        Saturday, April 13, 2013

        Ubuntu 12.04.2 on ASUS R2H

        Today we're going to install Ubuntu 12.04.2 Desktop on the ASUS-R2H UMPC. Why not latest 12.10? I've already done that and somehow it seems slower - feel free to install that if you want, should be fairly close to the one below.
        * * *

        Screen resolution is quite low (640x480) and windows are not fitting entirely. If you have an external monitor connect it through the VGA port, otherwise you use ALT + left mouse to move a window around say to get to the buttons at the bottom (best to connect an external mouse and keyboard).

        You can increase the resolution to 800x480 by going into System Settings (left) > Displays, and in there uncheck Mirror displays box, select Laptop (left) and change the resolution from the drop-down (now 800x480 becomes available, filling up the screen).


        If you prefer to continue the installation remotely, once you connect to the local network (wi-fi or wired) enable Desktop Sharing and use a VNC client for remote control - I've used TightVNC from my Windows laptop.


        Start installation with the shortcut on the desktop. I won't cover all steps, they are quite easy to follow and you can find a lot of step-by-step tutorials on the net covering it all in detail. Checking the box to Download updates while installing makes it download a lot of things during installation and at times it will hog the wi-fi network...


        What I will cover though is the partition creation part - I already had the disk split into a Windows 8 partition (will replace with Ubuntu) and a personal data partition (that i'd like to keep). In my previous attempt, I chose the Install Ubuntu alongside Windows 8 which didn't work out very well for me, as it removed all partitions (again, remember to back up your disk! see note at the start) - maybe because it couldn't find any un-allocated disk space. So what I'll do is go with the manual option, remove the recovery and Windows 8 partition and manually create a swap and system partitions for Ubuntu (just as well it might work to use the manual step to remove the two and then come back and choose the first option, haven't tried it though).


        Remove first two partitions
        Create new partitions (swap + system)
        Click Install now to continue with the installation, that will take a while... Once done it will ask you to restart - remove the USB stick and restart.

        * * *

        Now installed, login and change resolution, maybe enable Desktop Sharing, just like you did when in the installer (see above). Finish installing updates (around 139) through the Update Manager (left) - that will take a while...

        UI performance:
        • install MyUnity and CompizConfig Settings Manager (Ubuntu Software Center)
        • disable window animations, fading and other effects...


        Touch screen works, but the cursor is off:
        • install xinput-calibrator (Ubuntu Software Center)
          https://apps.ubuntu.com/cat/applications/xinput-calibrator/
        • Run application from Dash Home (search for calibrate)
        • Copy the on-screen output and save to persist settings:
          copy the snippet below into '/etc/X11/xorg.conf.d/99-calibration.conf'...
        • Select text (existing terminal window) > right click > Copy
        • Right-click (existing terminal window) > Open Terminal
        • sudo mkdir /etc/X11/xorg.conf.d/
        • sudo gedit /etc/X11/xorg.conf.d/99-calibration.conf
        • Right click > Paste > Save

        Webcam works, but the image is upside down. Haven't tried this fix just yet:
        http://www.danbishop.org/2011/03/27/asus-upside-down-webcam-in-ubuntu/

        Bluetooth seems to work out of the box.

        Fingerprint reader:
        • install Fingerprint GUI
          https://launchpad.net/~fingerprint/+archive/fingerprint-gui
          sudo add-apt-repository ppa:fingerprint/fingerprint-gui
          sudo apt-get update
          sudo apt-get install libbsapi policykit-1-fingerprint-gui fingerprint-gui
        • logout and log back in
        • run Fingerprint GUI to enroll / verify your fingerprint(s)

        GPS

        Sunday, March 24, 2013

        ActionBarSherlock: Custom List Navigation

        In this tutorial I'm going to cover activity navigation with ActionBarSherlock and custom navigation view, similar to Google Maps or Gmail - see full source links at the bottom of the post.



        As you look at the source code, you will notice that most of the code is in this AbstractActivity class that extends SherlockActivity and provides consistent navigation across the three activities that extend it.

        onCreate() method sets up the activity so that it displays an "up" icon, hides the title and uses the activity logos defined in AndroidManifest.xml for each activity - this way, the activity logo can be touched to go back to the previous one.
        // Up Icon + Logo + Hide title...
        getSupportActionBar().setDisplayHomeAsUpEnabled (true);
        getSupportActionBar().setDisplayShowTitleEnabled(false);
        getSupportActionBar().setDisplayUseLogoEnabled  (true);
        The next thing it does it configures the ActionBar list navigation with a custom NavigationListAdapter and NavigationListListener, reading the resources (logo, title, subtitle) from typed-arrays as references. Just as well, that can be changed to say implement a custom NavigationListItem to store the three together in an object if you want.
        // Custom navigation list adapter...
        Context    context   = getSupportActionBar().getThemedContext();
        TypedArray logos     = getResources().obtainTypedArray(R.array.activity_logos);
        TypedArray titles    = getResources().obtainTypedArray(R.array.activity_titles);
        TypedArray subtitles = getResources().obtainTypedArray(R.array.activity_subtitles);
        NavigationListAdapter navigationListApdater = new NavigationListAdapter(context, logos, titles, subtitles);
                
        // Custom navigation list listener... 
        NavigationListListener navigationListListener = new NavigationListListener(this);
        
        // Set navigation mode...
        getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
        getSupportActionBar().setListNavigationCallbacks(navigationListApdater, navigationListListener);
        

        Then just to finish it off, it determines the position in the navigation list for current activity - this will be used to select current activity in the navigation list onResume() - for both initial run as well as when coming back from next activity - as well as used in the listener to avoid going into a loop. That is because when the activity starts it will select the first entry in the navigation list, causing an unintended activity run.
        /*
         * (non-Javadoc)
         * @see android.app.Activity#onCreate(android.os.Bundle)
         */
        protected void onCreate(Bundle savedInstanceState) {
          [...]
          m_currentNavigationItem = getCurrentNavigationItem(this, titles);
          [...]
        }
        
        /*
         * (non-Javadoc)
         * @see android.app.Activity#onResume()
         */
        @Override
        protected void onResume() {
          /*
           * Super...
           */
           super.onResume();
             
           /*
            * Select current title in navigation list (first start or on back)...
            */
           getSupportActionBar().setSelectedNavigationItem(m_currentNavigationItem);
        }
        
        /**
         * Get navigation list index for current activity.
         * 
         * @param p_activity
         * @param p_titles
         * @return
         */
        private int getCurrentNavigationItem(Activity p_activity, TypedArray p_titles) {
          String title    = p_activity.getTitle().toString();
          int    position = 0;
             
          for (int i = 0, n = p_titles.length(); i < n; i++) {
            if (p_titles.getString(i).equals(title)) {
              position = i;
              break;
            }
          }
          
          return position;
        }
        
        The NavigationListAdapter implements a SpinnerAdapter and provides custom layouts for the top ActionBar item and drop-down items.

        The top item doesn't have an icon (only title and subtitle), as explained above, the activity will provide it.
        /*
         * (non-Javadoc)
         * @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
         */
        @Override
        public View getView(int p_position, View p_convertView, ViewGroup p_parent) {
         /*
          * View...
          */
         View view = p_convertView;
         if (view == null) {
          view = m_layoutInflater.inflate(R.layout.navigation_list_item, p_parent, false);
         }
         
         /*
          * Display...
          */
         // Title...
         TextView tv_title = (TextView) view.findViewById(R.id.title);
         tv_title.setText(m_titles.getString(p_position));
         
         // Subtitle...
         TextView tv_subtitle = ((TextView) view.findViewById(R.id.subtitle));
         tv_subtitle.setText      (m_subtitles.getString(p_position));
         tv_subtitle.setVisibility("".equals(tv_subtitle.getText()) ? View.GONE : View.VISIBLE);
         
         /*
          * Return...
          */
         return view;
        }
        
        <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:gravity="center_vertical|left">
         
         <TextView
          android:id="@+id/title"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:paddingLeft="0dp"
          android:singleLine="true"
          android:ellipsize="end"
          style="?attr/spinnerItemStyle" />
         
         <TextView
          android:id="@+id/subtitle"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:paddingLeft="0dp"
          android:singleLine="true"
          android:ellipsize="end"
          android:textAppearance="?attr/textAppearanceSmall"
          style="?attr/spinnerItemStyle" />
         
        </LinearLayout>
        
        The drop-down item has a logo, title and subtitle. Both allow for subtitles to be optional, hiding the subtitle when blank, vertically centering the title to take the space.
        /*
         * (non-Javadoc)
         * @see android.widget.BaseAdapter#getDropDownView(int, android.view.View, android.view.ViewGroup)
         */
        @Override
        public View getDropDownView(int p_position, View p_convertView, ViewGroup p_parent) {
         /*
          * View...
          */
         View view = p_convertView;
         if (view == null) {
          view = m_layoutInflater.inflate(R.layout.navigation_list_dropdown_item, p_parent, false);
         }
         
         /*
          * Display...
          */
        
         // Icon...
         ImageView iv_logo = (ImageView) view.findViewById(R.id.logo);
         iv_logo.setImageDrawable(m_logos.getDrawable(p_position));
         
         // Title...
         TextView tv_title = (TextView) view.findViewById(R.id.title);
         tv_title.setText(m_titles.getString(p_position));
         
         // Subtitle...
         TextView tv_subtitle = ((TextView) view.findViewById(R.id.subtitle));
         tv_subtitle.setText      (m_subtitles.getString(p_position));
         tv_subtitle.setVisibility("".equals(tv_subtitle.getText()) ? View.GONE : View.VISIBLE);
         
         /*
          * Return...
          */
         return view;
        }
        
        <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="?attr/dropdownListPreferredItemHeight"
         android:orientation="horizontal"
         style="?attr/spinnerDropDownItemStyle">
         
         <ImageView
          android:id="@+id/logo"
          android:layout_width="wrap_content"
          android:layout_height="match_parent"
          android:adjustViewBounds="true"/>
         
         <LinearLayout
          android:layout_width="wrap_content"
          android:layout_height="match_parent"
          android:orientation="vertical"
          android:gravity="center_vertical|left">
          
          <TextView 
           android:id="@+id/title"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"  
           android:singleLine="true"
           android:ellipsize="end"
           style="?attr/spinnerDropDownItemStyle" />
          
          <TextView 
           android:id="@+id/subtitle"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:singleLine="true"
           android:ellipsize="end"
           android:textAppearance="?attr/textAppearanceSmall"
           style="?attr/spinnerDropDownItemStyle" />
          
         </LinearLayout>
        
        </LinearLayout>

        Finally, NavigationListListener implements OnNavigationListener - an interesting note, it uses the item id - provided by NavigationListAdapter.getItemId() - which is in fact the title string id, to decide which activity to start. This is an alternative to using the position, which might just as well change during development, but this way you won't need to change the code to match.
        /**
         * Custom navigation list listener.
         */
        private class NavigationListListener implements OnNavigationListener {
         /**
          * Members
          */
         private AbstractActivity m_activity;
         
         /**
          * 
          * @param p_activity
          */
         NavigationListListener(AbstractActivity p_activity) {
          m_activity = p_activity;
         }
         
         /*
          * (non-Javadoc)
          * @see com.actionbarsherlock.app.ActionBar.OnNavigationListener#onNavigationItemSelected(int, long)
          */
         @Override
         public boolean onNavigationItemSelected(int p_itemPosition, long p_itemId) {
          /*
           * Ignore if selecting current...
           */
          if (p_itemPosition == m_activity.m_currentNavigationItem) {
           return true;
          }
          
          /*
           * Start new activity...
           */
          Intent intent = null;
          if (p_itemId == R.string.title_activity_main) {
           intent = new Intent(m_activity, MainActivity.class);
          }
          else if (p_itemId == R.string.title_activity_one) {
           intent = new Intent(m_activity, FirstActivity.class);
          }
          else if (p_itemId == R.string.title_activity_two) {
           intent = new Intent(m_activity, SecondActivity.class);
          }
          
          if (intent != null) {
           intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
           startActivity(intent);
          }
          
          return true;
         }
        }
        
        And that's about it - the activities will just need to provide a layout id and the generic AbstractActivity logic will take care of the work consistently across.

        Source code (Google Code - SVN):
        Resources:

        Sunday, March 3, 2013

        Google Analytics EasyTracker: detailed stack-traces

        If you're using Google Analytics to capture crashes in your application you may have noticed that uncaught exceptions give you very little information (just the exception message):
        An error occured while executing doInBackground() 

        Google Analytics documentation does mention that you can use your own custom ExceptionReporter / ExceptionParserDecompiling the library though you can see how it is all implemented - I should note that this article is based on Google Analytics v2.0 b4 and may or may not work with other versions.

        Instead of creating a new ExceptionReporter and attaching it as a default handler, will assign a custom ExceptionParser in the default ExceptionReporter. That is mainly because from looking at the code the suggested way will chain handlers, possibly producing duplicate reports.

        The replacement will go the Application class, before anything else (you could do it if you want in any other activity, although once is enough). First EasyTracker.getInstance().setContext() call initializes the structures, calling EasyTracker.loadParameters() method - among other things it will also create an ExceptionParser and set it as a default exception handler - Thread.setDefaultUncaughtExceptionHandler()Should note that the uncaught exception handler is only set when you have set a ga_trackingId and ga_reportUncaughtExceptions = true in your analytics.xml config file.
        package com.your.package;
        
        import com.google.analytics.tracking.android.EasyTracker;
        import com.google.analytics.tracking.android.ExceptionReporter;
        
        public class Application extends android.app.Application {
          /*
           * (non-Javadoc)
           * @see android.app.Application#onCreate()
           */
          public void onCreate() {
            [...]
        
            /*
             * Google Analytics...
             */
            EasyTracker.getInstance().setContext(this);
        
            // Change uncaught exception parser...
            // Note: Checking uncaughtExceptionHandler type can be useful if clearing ga_trackingId during development to disable analytics - avoid NullPointerException.
            Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
            if (uncaughtExceptionHandler instanceof ExceptionReporter) {
              ExceptionReporter exceptionReporter = (ExceptionReporter) uncaughtExceptionHandler;
              exceptionReporter.setExceptionParser(new AnalyticsExceptionParser());
            }
          }
        }
        

        My custom AnalyticsExceptionParser uses Apache Commons Lang ExceptionUtils, but just as well you could use your own implementation.
        package com.your.package.util;
        
        import org.apache.commons.lang3.exception.ExceptionUtils;
        import com.google.analytics.tracking.android.ExceptionParser;
        
        public class AnalyticsExceptionParser implements ExceptionParser {
          /*
           * (non-Javadoc)
           * @see com.google.analytics.tracking.android.ExceptionParser#getDescription(java.lang.String, java.lang.Throwable)
           */
          public String getDescription(String p_thread, Throwable p_throwable) {
             return "Thread: " + p_thread + ", Exception: " + ExceptionUtils.getStackTrace(p_throwable);
          }
        }
        Now this will produce you a complete stack trace with class names and line numbers, that should be very helpful to figure out where the problem happened exactly.


        * * *
        You can apply similar to EasyTracker.getTracker() setting your custom ExceptionParser with EasyTracker.getTracker().setExceptionParser(...) for caught exceptions, although not necessary as probably in those cases you will want to send the exception plus some other contextual data to figure out the context, and for what you can use sendException(String description, boolean fatal) e.g.:

        String response = null;
        try {
          [...]
          response = doSomething();
        }
        catch (Throwable t) {
          // Analytics...
          EasyTracker.getTracker().sendException("response = " + response + ", " + ExceptionUtils.getStackTrace(t), false);
        
          // Logging...
          if (BuildConfig.DEBUG) {
            Log.e(TAG, ExceptionUtils.getStackTrace(t));
          }
        }