Monday, August 04, 2014

USW-SVN+Git-ABS+ABC-ADT+Gradle = The Ultimate Refresh

The Ultimate Stopwatch is a pretty long running project in the Android scheme of things and when I kicked it off I had the source code in a CVS repo on a private server with regular(ish) backups.

In 2011 I relinquished the server and migrated to an SVN repo hosted on Google Code. But, setting up Subversion with any sort of IDE support is somewhat unpleasant, so I've decided to migrate again, this time to Git on GitHub. But SVN->Git isn't enough, so at the same time I'm throwing in a migration from ActionBarSherlock (thank you Jake Wharton) to ActionBarCompat and finally migrating the build from the Ant based Android Dev Tools to Gradle for good measure. Sometimes you just need to spring clean a project and sweep away the barriers holding back progress into new features like Custom Notifications, Cast and Android Wear.


There is more testing to do, but the new repo/build/actionbar are all in at https://github.com/geekyouup/ultimatestopwatch and the new features will be coming soon.

By far the easiest step of the migration was getting from SVN to Git, although it should have been even easier. The 'git svn ...' command does a great job of reading every commit from the SVN repo and playing it back into the new Git repo, so you keep all the history. However git-svn didn't work on my Mac, all I achieved was the error "Can't locate SVN/Core.pm in @INC". Fortunately I wasn't the first and Victor Quinn's guide to fix the fact that git-svn is broken on Mavericks and Mountain Lion was most useful. After that it was plain sailing, creating the GitHub project and syncing the repo.

ActionBarSherlock to the Android Support Library AppCompat Action Bar was a little more painful and the only reason I voluntarily dived in was to gain access to MediaRouteProvider to later add Cast Support. A solid first step in using ActionBarCompat is to check out the G+ post and video by Chris Banes to pick up the basics.

Then I plugged through the following steps:
  1. Add a project dependancy on support-v7-appcompat, official guide
  2. Switch Activity/Fragment/Manager classes from:
    1. SherlockActivity -> android.support.v7.app.ActionBarActivity
    2. SherlockFragment -> android.support.v4.app.Fragment
    3. SherlockFragmentActivity -> android.support.v7.app.ActionBarActivity
    4. SherlockListFragment -> android.support.v4.app.ListFragment
    5. SherlockFragmentManager  -> android.support.v4.app.FragmentManager
  3. Switch the packages of a number of imports throughout the project:
    1. import com.actionbarsherlock.view.Menu -> android.view.Menu
    2. import com.actionbarsherlock.view.MenuItem -> android.view.MenuItem
    3. import com.actionbarsherlock.view.MenuInflater -> android.view.MenuInflater
    4. import com.actionbarsherlock.view.ActionMode -> android.view.ActionMode
    5. import com.actionbarsherlock.app.ActionBar -> android.support.v7.app.ActionBar
  4. Replace some Sherlock specific function calls:
    1. getSupportMenuInflater() -> getMenuInflater()
    2. getSherlockActionBar() -> getSupportActionBar()
    3. getSherlockActivity() -> getActivity()
    4. invalidateOptionsMenu -> supportInvalidateOptionsMenu()
  5. Update the menu.xml files to use the AppCompat implementation of app:showAsAction="" instead of android:showAsAction=""
  6. Fortunately I'd generated my ActionBarSherlock theme using Jeff Gilfelt's ActionBarStyleGenerator, so it was relatively simple to go back and generate the style for AppCompat and replace the theme in my project. I also has a couple of custom themes that were based on Sherlock styles e.g. '@style/Sherlock.Widget.ActionBar.Title', which needed switching to the AppCompat equivalent '@style/Widget.AppCompat.ActionBar.Title'
I was also using NineOldAndroids for the ValueAnimator backwards compatibility, but unfortunately there isn't a Support library version, so for project cleanliness I decided to deprecate the animations support for pre-ICS, add a little API-gating around those methods and remove the option from settings.

Part 2 will cover the migration from ADT to Gradle.

Monday, July 01, 2013

Ultimate Stopwatch version 6.0.4


Fresh from being used to demo Android Studio at Google I/O, the Ultimate Stopwatch version 6.0.4 is rolling out with a couple more tweaks and bug fixes. This has also given me the opportunity to try the Google Play staged rollout feature.

First up, overdraw has been reduced again, this time by removing the background drawable for the window. 

    UltimateStopwatchActivity.onCreate(){
        getWindow().setBackgroundDrawable(null);
    }

Resulting in an almost totally green/blue layout, the only remaining red section is in the overlap of the minute and second hands. The screen was already drawing in a fraction of the time needed to maintain 60fps, so at this point it is really just optimizing for the sake of optimizing. Removing the window background had the side effect of a lap time screen without a background at all, leaving a hole in the UI and some freaky results. That was quickly resolved by setting a background color on that view.


I've gone one step further in an experimental version, which results in 0 overdraw for most of the app, but there are still a few margins left with no background color, so it needs a little more work and testing.

Another minor change in 6.0.4 was in the animation timing code. The animation runnable was already using postInvalidateOnAnimation() for JB+ devices, an optimization to cause redraws to happen on the next display frame:

    if(JELLYBEAN_OR_ABOVE) postInvalidateOnAnimation();
    else invalidate();


However, +ChrisBanes pointed out that there is a version of this in ViewCompat. ViewCompat is a backwards compatible helper provided in the Support Library for accessing View features introduced after API level 4. So, the custom view animation runnable now looks like this:


    //Stopwatch animation runnable
    private final Runnable animator = new Runnable() {
        @Override
        public void run() {
            updateWatchState(false);

            if(mIsRunning)
            {
                invalidate();
                removeCallbacks(this);
                ViewCompat.postOnAnimation(StopwatchCustomView.this, this);
            }
        }
    };

Which is more pleasing to the eye, easier to understand for devs reading the code and would benefit from any future advances in ViewCompat.

There are also a couple of other bug fixes in this release:

Issue #42: Fixed - Hands don't reset on Stopwatch when hand animations turned off.
Issue #47: Fixed - Ticking sounds go wrong during onPause onResume cycle.

As always the full source code to Ultimate Stopwatch is available at https://code.google.com/p/android-ultimatestopwatch

Saturday, February 23, 2013

Custom Paint Job: Accelerating the Ultimate Stopwatch

(Edit - 1st July 2013: Animator runnable updated again and one more layer of overdraw removed, details in new blog post)

Creating a custom Android view in 2013 isn't exactly breaking new ground, but the time was long overdue to relegate the old SurfaceView to history and move on; a task I was looking forward to but had kept putting off. Anyway, +Marie Schweiz's great new Ultimate Stopwatch designs deserved a hardware accelerated home.

The Android Developers training class breaks down the job of developing a Custom view into a few simple steps:
  1. Subclass a View
  2. Implement custom drawing
  3. Make the view interactive
In practice, when it came to the animations I also grabbed a couple of hints from Anders Ericsson's post at JayWay, as the mental model is also slightly different and I'd seen his related talk at Droidcon London 2012. In the SurfaceView I had a thread constantly looping to call the update/draw methods, however in a custom view it will redraw each time you call invalidate(). So, instead of a looping thread I can just call invalidate() after each cycle completes to start the next paint. This is done by posting a runnable to the View's message queue, I'm aiming for 15ms update cycles for just over 60fps.

    private Runnable animator = new Runnable() {
        @Override
        public void run() {
            updateWatchTime();
            invalidate();

            if (mIsRunning) postDelayed(this, 15);
        }
    };
The onDraw() methods from my SurfaceView and Custom view are identical, so just a copy and paste job there. The onTouch() and Activity communications handler also remained untouched. So in the end the Custom view is considerably simpler as there is no surface creation/destruction to worry about or separate thread to maintain.

A really nice feature of a Custom view is the ability to configure your own XML attributes. Previously I had to instantiate the SurfaceView and pass in details on whether it should represent the Stopwatch or Countdown view, with a Custom view I can add the following into attrs.xml

    <resources>
        <declare-styleable name="StopwatchCustomView">
            <attr name="watchType" format="enum">
                <enum name="stopwatch" value="1"/>
                <enum name="countdown" value="0"/>
            </attr>
        </declare-styleable>
    </resources>


Then by adding the name space xmlns:custom="http://schemas.android.com/apk/res-auto" in my layout, I can use the attribute custom:watchType="stopwatch" in the layout file and retrieve it in the code during instantiation. Being able to configure the custom view in XML can really simplify code and make the source easier to understand. Here is the code to retrieve the value:


    TypedArray a = context.getTheme().obtainStyledAttributes(
         attrs,
         R.styleable.StopwatchCustomView,
         0, 0);
    try{
        isStopwatch = a.getBoolean(R.styleable.StopwatchCustomView_watchType,true);
    }finally{
        a.recycle();
    }

That's it. The view is hardware accelerated where available and due to some optimizations the refresh rate has also increased when h/w acceleration is absent.

Some small improvements were gained by refactoring, but the main increases were delivered using the 'Show GPU overdraw' feature of Android's Developer Options, which is now available as I'm using the GPU to draw. It's immediately clear that before optimization there's excessive overdraw, highlighted by the red areas. This was almost entirely down to background colours being set on layers that weren't visible; the ViewPager, the layout of the custom view and also in the onDraw() of the Custom View. Eliminating these unseen, but still drawn, background colours reduced the graphics load and improved performance.


Now we have a wondrous, hardware accelerated Custom view we're in a much better position to add some final flourishes to improve the application. Later I'll write about some custom animations I've added, as suggested by +Nick Butcher to not only improve the perceived quality but also the usability.

The Ultimate Stopwatch and Timer is open source at android-ultimatestopwatch.googlecode.com, designed by +Marie Schweiz and makes use of the awesome ActionBarSherlock by Jake Wharton.

Friday, January 06, 2012

Ultimate Stopwatch & Timer finally updated!

Finally after almost a year of good intent, the updated version of Ultimate Stopwatch & Timer for Android has just gone live, v5.0.0. It'll be making its way through the interweb caches to an Android Market near you soon.

Thank you so much +Marie Schweiz (http://marie-schweiz.de/) for your awesome designs!

Updates:
- New clean design
- Action Bar on all versions (using ActionBarCompat)
- Touch feedback on all elements
- Light face for stopwatch, Dark face for countdown
- Lap Times separated out
- Built for Android 4.0 with support for versions 2.0+
- Portrait / Landscape layouts for all sceen sizes including QVGA and Tablets

Big fixes:
- Sporadic countdown alarm notifications fixed
- Galaxy Tab time appearance fixed
- QVGA layouts fixed
- Other minor issues resolved

Source available at http://android-ultimatestopwatch.googlecode.com
Comments and questions to the post on Google+ please so I can respond https://plus.google.com/u/0/115995639636688350464/posts/KhqUhC1ezB4

Monday, December 19, 2011

Android 4.0 ADB Backup / Restore

Android 4.0 ADB Backup/Restore
I switch phones quite a bit and the cloud is immensely useful for that, almost all of my #Android apps restore and most of the data is up there, even my wifi settings and wallpaper get reinstalled. But games are still lagging behind on this, they rarely keep their settings in the cloud or give a backup/restore option.

I really didn't want to lose my 'World of Goo' progress so I've just used adb backup/restore to pull it off one ICS device and push it onto another. Worked a treat.

The command lines I used:
> adb backup -f worldofgoo.bak com.twodboy.worldofgoofull
and a little dialog appears on the phone screen to confirm the backup and offer the chance to enter a password to protect.

> adb restore worldofgoo.bak
and another little dialog appears to confirm the restore

et voila, done. Other options on adb backup allow for full user data backups (-all), just type adb to see all options.


adb backup [-f ] [-apk|-noapk] [-shared|-noshared] [-all] [-system|-nosystem] []
- write an archive of the device's data to .
If no -f option is supplied then the data is written
to "backup.ab" in the current directory.
(-apk|-noapk enable/disable backup of the .apks themselves
in the archive; the default is noapk.)
(-shared|-noshared enable/disable backup of the device's
shared storage / SD card contents; the default is noshared.)
(-all means to back up all installed applications)
(-system|-nosystem toggles whether -all automatically includes
system applications; the default is to include system apps)
( is the list of applications to be backed up. If
the -all or -shared flags are passed, then the package
list is optional. Applications explicitly given on the
command line will be included even if -nosystem would
ordinarily cause them to be omitted.)

adb restore - restore device contents from the backup archive