view src/uk/ac/qmul/eecs/ccmi/accessibility/AccessibleCheckbox.java @ 0:e0ee6ac3a45f

first import
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Thu, 13 Dec 2012 20:00:21 +0000
parents
children 66b3a838feca
line wrap: on
line source
/*  
 CCmI Diagram Editor for Android 
  
 Copyright (C) 2012  Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package uk.ac.qmul.eecs.ccmi.accessibility;

import uk.ac.qmul.eecs.ccmi.activities.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;

/**
 * 
 * A check box list that can be operated without looking at the display. 
 *
 */
public class AccessibleCheckbox extends LinearLayout{
	private NoClickSpinner spinner;
	private AccessibilityService service;
	private boolean[] checks; 
	
	public AccessibleCheckbox(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public AccessibleCheckbox(Context context) {
		super(context);
		init(context);
	}
	
	private void init(Context context){
		spinner = new NoClickSpinner(context);
		spinner.setContentDescription("selection box: ");
		spinner.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
		setTag("CHECKBOX");
		addView(spinner);
	}
	
	/**
	 * Sets the {@code accessibility} service for this class.
	 * 
	 * @param service the {@code AccessibilityService} for this class
	 */
	public void setAccessibilityService(AccessibilityService service){
		this.service = service;
	}
	
	/**
	 * Returns a reference to the {@code accessibility} service for this class.
	 * 
	 * @return the {@code AccessibilityService} for this class
	 */
	public AccessibilityService getAccessibilityService(){
		return service;
	}
	
	/**
	 * Sets the values to display to the user.
	 * 
	 * @param values the values to display 
	 * @param valueChecks The initial checks. If {@code valueChecks[i]} is {@code true} then {@code values[i]} will be 
	 * checked when displayed to the user
	 */
	public void setValues(String[] values, boolean[] valueChecks){
		/* initialize the string array with the labels of the spinner, which are going to be between 
		 * "<" and ">" if they are checked (according to valueChecks).
		 * checks is also initialized with valueChecks values, this will modified according 
		 * to the user's selections and then returned by getValues() when the dialog is closed.
		 */
		final String[] markedValues = new String[values.length];
		checks = new boolean[valueChecks.length];
		for(int i=0; i<values.length; i++){
			checks[i] = valueChecks[i];
			markedValues[i] = (checks[i] ? "<"+values[i]+">" : values[i] );
		}
		
		spinner.setAdapter(new ArrayAdapter<String>(getContext(),R.layout.list_item_1,markedValues));
		
		setOnClickListener(new View.OnClickListener(){
			@Override
			public void onClick(View view) {
				spinner.setSelection((spinner.getSelectedItemPosition()+1) % spinner.getCount());
				if(service != null){
					service.speak(spinner.getSelectedItem().toString()+(checks[spinner.getSelectedItemPosition()] ? " " : " un")+"checked");
				}
			}
		});
		
		setOnLongClickListener(new View.OnLongClickListener(){
			@Override
			public boolean onLongClick(View view) {
				int itemPosition = spinner.getSelectedItemPosition();
				checks[itemPosition] = !checks[itemPosition];  
				if(checks[itemPosition]){
					markedValues[itemPosition] = "<"+markedValues[itemPosition]+">"; 
				}else{
					markedValues[itemPosition] = markedValues[itemPosition].replaceAll("(^<)|(>$)", "");
				}
				if(service != null)
					service.speak(spinner.getSelectedItem().toString()+(checks[spinner.getSelectedItemPosition()] ? "" : "un")+"checked");
				
				spinner.setAdapter(new ArrayAdapter<String>(getContext(),R.layout.list_item_1,markedValues));
				spinner.setSelection(itemPosition);
				return true;
			}
		});
	}
	
	/**
	 * Returns the checks of the current values. To be called after this checkbox has been displayed 
	 * to the user to collect their checks. 
	 * 
	 * @return the current checks 
	 */
	public boolean[] getChecks(){
		return checks;
	}
	
	/**
	 * Returns the currently selected value of the list.
	 *  
	 * @return the currently selected value of the list
	 */
	public String getSelectedValue(){
		return spinner.getSelectedItem().toString();
	}
	
	/**
	 * Returns the position in the list of the currently selected value.
	 * 
	 * @return the position in the list of the currently selected value
	 */
	public int getSelectedValuePosition(){
		return spinner.getSelectedItemPosition();
	}
	
	/**
	 * This method is empty to prevent the screen reader to speak when clicking on this text view. 
	 */
	@Override 
	public void sendAccessibilityEvent(int eventType){
		
	}
	
	/**
	 * This method is empty to prevent the screen reader to speak when clicking on this text view.
	 */
	@Override
	public void sendAccessibilityEventUnchecked(AccessibilityEvent event){
		
	}
	
	private static class NoClickSpinner extends Spinner {
		
		public NoClickSpinner(Context context) {
			super(context);
		}
		
		public NoClickSpinner(Context context, AttributeSet attrs, int defStyle) {
			super(context, attrs, defStyle);
		}
		
		public NoClickSpinner(Context context, AttributeSet attrs) {
			super(context, attrs);
		}
		
		@Override
		public boolean dispatchTouchEvent(MotionEvent evt){
			return false;
		}
	}

}