fiore@0: /* fiore@0: CCmI Diagram Editor for Android fiore@0: fiore@0: Copyright (C) 2012 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) fiore@0: fiore@0: This program is free software: you can redistribute it and/or modify fiore@0: it under the terms of the GNU General Public License as published by fiore@0: the Free Software Foundation, either version 3 of the License, or fiore@0: (at your option) any later version. fiore@0: fiore@0: This program is distributed in the hope that it will be useful, fiore@0: but WITHOUT ANY WARRANTY; without even the implied warranty of fiore@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fiore@0: GNU General Public License for more details. fiore@0: fiore@0: You should have received a copy of the GNU General Public License fiore@0: along with this program. If not, see . fiore@0: */ fiore@0: package uk.ac.qmul.eecs.ccmi.accessibility; fiore@0: fiore@0: import uk.ac.qmul.eecs.ccmi.activities.R; fiore@1: import uk.ac.qmul.eecs.ccmi.utilities.ILogger; fiore@0: import android.content.Context; fiore@0: import android.util.AttributeSet; fiore@0: import android.view.MotionEvent; fiore@0: import android.view.View; fiore@0: import android.view.accessibility.AccessibilityEvent; fiore@0: import android.widget.ArrayAdapter; fiore@0: import android.widget.LinearLayout; fiore@0: import android.widget.Spinner; fiore@0: fiore@0: /** fiore@0: * fiore@0: * A check box list that can be operated without looking at the display. fiore@0: * fiore@0: */ fiore@0: public class AccessibleCheckbox extends LinearLayout{ fiore@0: private NoClickSpinner spinner; fiore@0: private AccessibilityService service; fiore@0: private boolean[] checks; fiore@0: fiore@0: public AccessibleCheckbox(Context context, AttributeSet attrs) { fiore@0: super(context, attrs); fiore@0: init(context); fiore@0: } fiore@0: fiore@0: public AccessibleCheckbox(Context context) { fiore@0: super(context); fiore@0: init(context); fiore@0: } fiore@0: fiore@0: private void init(Context context){ fiore@0: spinner = new NoClickSpinner(context); fiore@0: spinner.setContentDescription("selection box: "); fiore@0: spinner.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); fiore@0: setTag("CHECKBOX"); fiore@0: addView(spinner); fiore@0: } fiore@0: fiore@0: /** fiore@0: * Sets the {@code accessibility} service for this class. fiore@0: * fiore@0: * @param service the {@code AccessibilityService} for this class fiore@0: */ fiore@0: public void setAccessibilityService(AccessibilityService service){ fiore@0: this.service = service; fiore@0: } fiore@0: fiore@0: /** fiore@0: * Returns a reference to the {@code accessibility} service for this class. fiore@0: * fiore@0: * @return the {@code AccessibilityService} for this class fiore@0: */ fiore@0: public AccessibilityService getAccessibilityService(){ fiore@0: return service; fiore@0: } fiore@0: fiore@0: /** fiore@0: * Sets the values to display to the user. fiore@0: * fiore@0: * @param values the values to display fiore@0: * @param valueChecks The initial checks. If {@code valueChecks[i]} is {@code true} then {@code values[i]} will be fiore@0: * checked when displayed to the user fiore@0: */ fiore@0: public void setValues(String[] values, boolean[] valueChecks){ fiore@0: /* initialize the string array with the labels of the spinner, which are going to be between fiore@0: * "<" and ">" if they are checked (according to valueChecks). fiore@0: * checks is also initialized with valueChecks values, this will modified according fiore@0: * to the user's selections and then returned by getValues() when the dialog is closed. fiore@0: */ fiore@0: final String[] markedValues = new String[values.length]; fiore@0: checks = new boolean[valueChecks.length]; fiore@0: for(int i=0; i" : values[i] ); fiore@0: } fiore@0: fiore@0: spinner.setAdapter(new ArrayAdapter(getContext(),R.layout.list_item_1,markedValues)); fiore@0: fiore@0: setOnClickListener(new View.OnClickListener(){ fiore@0: @Override fiore@0: public void onClick(View view) { fiore@0: spinner.setSelection((spinner.getSelectedItemPosition()+1) % spinner.getCount()); fiore@1: ILogger.logTap("checkbox (new value="+spinner.getSelectedItem().toString()+')'); fiore@0: if(service != null){ fiore@0: service.speak(spinner.getSelectedItem().toString()+(checks[spinner.getSelectedItemPosition()] ? " " : " un")+"checked"); fiore@0: } fiore@0: } fiore@0: }); fiore@0: fiore@0: setOnLongClickListener(new View.OnLongClickListener(){ fiore@0: @Override fiore@0: public boolean onLongClick(View view) { fiore@0: int itemPosition = spinner.getSelectedItemPosition(); fiore@0: checks[itemPosition] = !checks[itemPosition]; fiore@0: if(checks[itemPosition]){ fiore@0: markedValues[itemPosition] = "<"+markedValues[itemPosition]+">"; fiore@0: }else{ fiore@0: markedValues[itemPosition] = markedValues[itemPosition].replaceAll("(^<)|(>$)", ""); fiore@0: } fiore@0: if(service != null) fiore@0: service.speak(spinner.getSelectedItem().toString()+(checks[spinner.getSelectedItemPosition()] ? "" : "un")+"checked"); fiore@1: ILogger.log("user long tap: spinner"); fiore@1: ILogger.log(spinner.getSelectedItem().toString()+' '+(checks[spinner.getSelectedItemPosition()] ? "" : "un")+"checked"); fiore@0: spinner.setAdapter(new ArrayAdapter(getContext(),R.layout.list_item_1,markedValues)); fiore@0: spinner.setSelection(itemPosition); fiore@0: return true; fiore@0: } fiore@0: }); fiore@0: } fiore@0: fiore@0: /** fiore@0: * Returns the checks of the current values. To be called after this checkbox has been displayed fiore@0: * to the user to collect their checks. fiore@0: * fiore@0: * @return the current checks fiore@0: */ fiore@0: public boolean[] getChecks(){ fiore@0: return checks; fiore@0: } fiore@0: fiore@0: /** fiore@0: * Returns the currently selected value of the list. fiore@0: * fiore@0: * @return the currently selected value of the list fiore@0: */ fiore@0: public String getSelectedValue(){ fiore@0: return spinner.getSelectedItem().toString(); fiore@0: } fiore@0: fiore@0: /** fiore@0: * Returns the position in the list of the currently selected value. fiore@0: * fiore@0: * @return the position in the list of the currently selected value fiore@0: */ fiore@0: public int getSelectedValuePosition(){ fiore@0: return spinner.getSelectedItemPosition(); fiore@0: } fiore@0: fiore@0: /** fiore@0: * This method is empty to prevent the screen reader to speak when clicking on this text view. fiore@0: */ fiore@0: @Override fiore@0: public void sendAccessibilityEvent(int eventType){ fiore@0: fiore@0: } fiore@0: fiore@0: /** fiore@0: * This method is empty to prevent the screen reader to speak when clicking on this text view. fiore@0: */ fiore@0: @Override fiore@0: public void sendAccessibilityEventUnchecked(AccessibilityEvent event){ fiore@0: fiore@0: } fiore@0: fiore@0: private static class NoClickSpinner extends Spinner { fiore@0: fiore@0: public NoClickSpinner(Context context) { fiore@0: super(context); fiore@0: } fiore@0: fiore@0: public NoClickSpinner(Context context, AttributeSet attrs, int defStyle) { fiore@0: super(context, attrs, defStyle); fiore@0: } fiore@0: fiore@0: public NoClickSpinner(Context context, AttributeSet attrs) { fiore@0: super(context, attrs); fiore@0: } fiore@0: fiore@0: @Override fiore@0: public boolean dispatchTouchEvent(MotionEvent evt){ fiore@0: return false; fiore@0: } fiore@0: } fiore@0: fiore@0: }