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