Building clean RPN part one – Toggling the device and built in keypad

The Clean RPN calculator app allows users to either input formulas with a custom keyboard, or the devices soft keyboard.  This is toggled by pressing the keyboard button in the action bar.  Pressing the button once will hide the custom number input and show the device keyboard.  Pressing it again will hide the keyboard and show the custom number input.  This is a small feature, but it proved incredibly hard to implement.

device-2013-04-06-230322 device-2013-04-06-230407

 

 

 

 

 

 

 

 

Before delving to far into the how, it is worthwhile to dwell briefly on the why.  What is the advantage of allowing the user to toggle between the built in keyboard an the device one?  Why not just use the built in keyboard as it exposes all the functions the user needs?  Under Android users can install their own keyboards to supplement the device default keyboard.  There are a number of 3rd party keyboards which may be of particular intrest to people inputing mathematical functions, such at the math keyboard or the hackers keyboard available in the google play store.  A calculator app should ideally support any of the custom keyboards users with to install.

It turns out that toggling the visibility of the on screen keyboard, with absolute certainty is quite a difficult thing.  Witness the this question on stack overflow about hiding the onscreen keyboard, with 19 different answers.  After working through many of the suggested answers on a number of different devices it became apparent that android apps can reliably:

  1. Temporarily hide the keyboard.  The Keyboard will re-appear again when a user focuses a new text field.
  2. Show the keyboard when an activity starts and set a flag on the activity indicating that they keyboard should always be visible.  This flag can only be set when an activity is initialising.
  3. Mark an activity to never show or allow the use of the keyboard.  This flag can only be set when an activity is initialising.

Temporarily hiding the keyboard is not enough.  On some devices it will re-appear as soon as a new text field is focused.  As clean RPN uses multiple text fields for input, pressing ‘enter’ to move to a new line, will cause the hidden keyboard to pop back up again.

Unfortunately item 2 and 3 on the list only work reliability when an activity is being started.  Once the activity has become visible you cannot permanently hide or show the keyboard.

The trick is to actually restart your activity when the user presses the keyboard toggle button.  In clean RPN when the user presses on the toggle keyboard button, the following code runs:

    private void toggleKeyboard(){

    	if(keypadPager.getVisibility() == View.VISIBLE){
    		Intent i = new Intent(this, MainActivity.class);
    		i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
    		Bundle state = new Bundle();
    		onSaveInstanceState(state);
    		state.putBoolean(SHOW_KEYBOARD, true);
    		i.putExtras(state);

    		startActivity(i);
    	}
    	else{
    		Intent i = new Intent(this, MainActivity.class);
    		i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
    		Bundle state = new Bundle();
    		onSaveInstanceState(state);
    		state.putBoolean(SHOW_KEYBOARD, false);
    		i.putExtras(state);

    		startActivity(i);
    	}
    }

This causes the current activity to have its state saved into a Bundle, and then the activity is started, passing through an boolean which indicates if the keyboard should be shown or hidden.

Inside the onCreate method the following code is run

if(bundle.getBoolean(SHOW_KEYBOARD)){
	keypadPager.setVisibility(View.GONE);
	indicatorWrapper.setVisibility(View.GONE);
	((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(newEquationText,0);
	getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
else{
	getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
	        WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
}

If the soft keyboard should be shown, then the InputMethodManager is told to show the keyboard and the window is instructed to make the soft input always visible. If the soft keyboard should be hidden then the WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM is set – this flag is used by dialogs to ensure that the soft keyboard never appears when a dialog is shown.

This approach works reliably on all devices I have tested on – from a 4 year old HTC phone running android 2.2 up to a nexus 7 running 4.2.2

Two good Android design links

I just wanted to point out two good links relating to android design.

The first is a set of slide by Hervé Mischler on the subject of porting your iOS designs to android.  Unfortunately the slides are light on text, and there are no speaker notes or recorded audio.  Never less, they only take a few moments to look through and there is some good information in there:

https://speakerdeck.com/dstroii/quick-tips-for-porting-your-ios-designs-to-android

 

The second link is the recent android design in action episode relating to responsive design.  There are a bunch of good tips and examples in there if you are interested in scaling your app up from phones to tablets:

https://developers.google.com/live/shows/ahNzfmdvb2dsZS1kZXZlbG9wZXJzchkLEgVFdmVudBjT3pMEDAsSBUV2ZW50GAEM/

 

 

 

A minimal PagerAdapter for ViewPager

As part of the android support library google has introduced the ViewPager class. ViewPager allows you to swipe left and right between a number of views. ViewPager is particularly useful for photo gallery style applications. Unfortunately google have not provided an example of of how to to do a minimal implementation of the PagerAdaptor class which provides the contents displayed in the ViewPager.

The following is a minimal implementation of PagerAdaptor which displays a list of Bitmaps in ImageViews:

import java.util.List;

import android.graphics.Bitmap;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;

public class ImagePagerAdapter extends PagerAdapter {

    private List<Bitmap> bitmaps;

    public ImagePagerAdapter(List<Bitmap> newBitmaps) {
        bitmaps = newBitmaps;
    }

    @Override
    public int getCount() {
        return bitmaps.size();
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position){

    	ImageView imageView = new ImageView(container.getContext());
        imageView.setLayoutParams(new ViewGroup.LayoutParams(
        		LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

        Bitmap bitmap = bitmaps.get(position);
        imageView.setImageBitmap(bitmap);

        return imageView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object){
    	container.removeView((View)object);
    }

    @Override
    public boolean isViewFromObject(View view, Object object){
    	return view == object;
    }
}

Attaching sources to android dependencies in eclipse

The problem

Up until recently is has been impossible to attach source code to any of the jar files contained in the ‘android dependencies’ portion of an android project in eclipse. If you are trying to debug into a third party library or understand how something works then this is very annoying. Fortunately in version 20 of the android development tools, Google have provided a work around of sorts. Its all detailed in the following bug reports:

https://android-review.googlesource.com/#/c/35702/
http://code.google.com/p/android/issues/detail?id=28658

Continue reading

UI Anti-patterns in porting iOS apps to android

If you are considering porting an iOS app to Android one of the key issues that you need to address is how to adapt the ui to Android.  Fortunately Google provide a series of excellent resources around creating Android UIs .  I highly recommend reading:

However there is one particular anti-pattern I wanted to talk about:

Copying the iOS ui pixel for pixel

Unfortunately a number of apps attempt to copy the iOS version using the same ui controls and resources.  An example of this approach is the commonwealth bank app:

Android commonwealth bank app

iOS commonwealth bank app

 

 

 

 

 

 

 

 

 

Google specifically advises against this in their design guide:

As you plan your app for Android, keep in mind that different platforms play by different rules and conventions. Design decisions that make perfect sense on one platform will look and feel misplaced in the context of a different platform. While a “design once, ship anywhere” approach might save you time up-front, you run the very real risk of creating inconsistent apps that alienate users.

Copying the iOS ui of an app comes with a number of significant down sides:

The app will feel out of place

Its true that apps come in all shapes and sizes.  Companies often customise the look and feel of an app to implement their own branding.  However if you just copy the look and feel of your iOS app, users will know what you are up to right away.  Consider the following review of the commonwealth bank app, from the google play store:

 Since Kaching is still not marked as compatible with my phone, I’m forced to use this poorly ported iPhone app. The content doesn’t resize to fit the screen width, leaving an unused border down the right hand side of the screen, and nothing about the design looks like Android. Commonwealth could learn a lot from ANZ here, their GoMoney app is brilliant.

Lack of device independence

The fact that android must support different screen sizes and pixel densities means that the app must gracefully deal with being resized.  This is a constraint that iOS apps don’t have and often no thought as to how they should resize has been put into the design.

Images in the app are stretched

The bottom tab bar doesn’t take advantage of any extra space to the left or right

Fails to take advantage of native android features

Copying the iOS UI means the app cannot take advantage of Android features that are unavailable on iOS.  For example Android includes the excellent share intent, which allows you to share URLs, apps,images etc via any social apps you have installed.  The commonwealth bank app however copies the iOS version all too literally:

I should note that when I took this screenshot I didn’t even have a Twitter client installed!  I guess if I wanted to share over Google+ or app.net then I would be out of luck.

 

What should have been done instead

When considering porting an iOS app to Android, I would recommend the following:

  • Start by reviewing the android design guide.  The first step in building a great looking android app to to understand what the platform provides and how android apps should look and behave.
  • Redesign for android.  When porting an iOS app you need to consider how to provide a UI that feels ‘native’ to android yet still familiar to users of the iOS app.  This is always going to entail some level of redesign.
  • Ensure HTML5 cross platform content is truly platform independent.  The commonwealth bank app makes heavy use of platform independent html5 content.  When using such content you need to ensure the look and feel is appropriate both for iOS and Android.
  • Test, test and test again.  Any android app needs to support a wide number of devices running at different screen resolutions and display densities.  Testing on a number of different devices would have picked up several of the issues with the commonwealth bank app.