1 /* 2 * Copyright (C) 2012 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.googlecode.eyesfree.braille.selfbraille; 18 19 import android.os.Bundle; 20 import android.os.Parcel; 21 import android.os.Parcelable; 22 import android.view.View; 23 import android.view.accessibility.AccessibilityNodeInfo; 24 25 /** 26 * Represents what should be shown on the braille display for a 27 * part of the accessibility node tree. 28 */ 29 public class WriteData implements Parcelable { 30 31 private static final String PROP_SELECTION_START = "selectionStart"; 32 private static final String PROP_SELECTION_END = "selectionEnd"; 33 34 private AccessibilityNodeInfo mAccessibilityNodeInfo; 35 private CharSequence mText; 36 private Bundle mProperties = Bundle.EMPTY; 37 38 /** 39 * Returns a new {@link WriteData} instance for the given {@code view}. 40 */ forView(View view)41 public static WriteData forView(View view) { 42 AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(view); 43 WriteData writeData = new WriteData(); 44 writeData.mAccessibilityNodeInfo = node; 45 return writeData; 46 } 47 getAccessibilityNodeInfo()48 public AccessibilityNodeInfo getAccessibilityNodeInfo() { 49 return mAccessibilityNodeInfo; 50 } 51 52 /** 53 * Sets the text to be displayed when the accessibility node associated 54 * with this instance has focus. If this method is not called (or 55 * {@code text} is {@code null}), this client relinquishes control over 56 * this node. 57 */ setText(CharSequence text)58 public WriteData setText(CharSequence text) { 59 mText = text; 60 return this; 61 } 62 getText()63 public CharSequence getText() { 64 return mText; 65 } 66 67 /** 68 * Sets the start position in the text of a text selection or cursor that 69 * should be marked on the display. A negative value (the default) means 70 * no selection will be added. 71 */ setSelectionStart(int v)72 public WriteData setSelectionStart(int v) { 73 writableProperties().putInt(PROP_SELECTION_START, v); 74 return this; 75 } 76 77 /** 78 * @see {@link #setSelectionStart}. 79 */ getSelectionStart()80 public int getSelectionStart() { 81 return mProperties.getInt(PROP_SELECTION_START, -1); 82 } 83 84 /** 85 * Sets the end of the text selection to be marked on the display. This 86 * value should only be non-negative if the selection start is 87 * non-negative. If this value is <= the selection start, the selection 88 * is a cursor. Otherwise, the selection covers the range from 89 * start(inclusive) to end (exclusive). 90 * 91 * @see {@link android.text.Selection}. 92 */ setSelectionEnd(int v)93 public WriteData setSelectionEnd(int v) { 94 writableProperties().putInt(PROP_SELECTION_END, v); 95 return this; 96 } 97 98 /** 99 * @see {@link #setSelectionEnd}. 100 */ getSelectionEnd()101 public int getSelectionEnd() { 102 return mProperties.getInt(PROP_SELECTION_END, -1); 103 } 104 writableProperties()105 private Bundle writableProperties() { 106 if (mProperties == Bundle.EMPTY) { 107 mProperties = new Bundle(); 108 } 109 return mProperties; 110 } 111 112 /** 113 * Checks constraints on the fields that must be satisfied before sending 114 * this instance to the self braille service. 115 * @throws IllegalStateException 116 */ validate()117 public void validate() throws IllegalStateException { 118 if (mAccessibilityNodeInfo == null) { 119 throw new IllegalStateException( 120 "Accessibility node info can't be null"); 121 } 122 int selectionStart = getSelectionStart(); 123 int selectionEnd = getSelectionEnd(); 124 if (mText == null) { 125 if (selectionStart > 0 || selectionEnd > 0) { 126 throw new IllegalStateException( 127 "Selection can't be set without text"); 128 } 129 } else { 130 if (selectionStart < 0 && selectionEnd >= 0) { 131 throw new IllegalStateException( 132 "Selection end without start"); 133 } 134 int textLength = mText.length(); 135 if (selectionStart > textLength || selectionEnd > textLength) { 136 throw new IllegalStateException("Selection out of bounds"); 137 } 138 } 139 } 140 141 // For Parcelable support. 142 143 public static final Parcelable.Creator<WriteData> CREATOR = 144 new Parcelable.Creator<WriteData>() { 145 @Override 146 public WriteData createFromParcel(Parcel in) { 147 return new WriteData(in); 148 } 149 150 @Override 151 public WriteData[] newArray(int size) { 152 return new WriteData[size]; 153 } 154 }; 155 156 @Override describeContents()157 public int describeContents() { 158 return 0; 159 } 160 161 /** 162 * {@inheritDoc} 163 * <strong>Note:</strong> The {@link AccessibilityNodeInfo} will be 164 * recycled by this method, don't try to use this more than once. 165 */ 166 @Override writeToParcel(Parcel out, int flags)167 public void writeToParcel(Parcel out, int flags) { 168 mAccessibilityNodeInfo.writeToParcel(out, flags); 169 // The above call recycles the node, so make sure we don't use it 170 // anymore. 171 mAccessibilityNodeInfo = null; 172 out.writeString(mText.toString()); 173 out.writeBundle(mProperties); 174 } 175 WriteData()176 private WriteData() { 177 } 178 WriteData(Parcel in)179 private WriteData(Parcel in) { 180 mAccessibilityNodeInfo = 181 AccessibilityNodeInfo.CREATOR.createFromParcel(in); 182 mText = in.readString(); 183 mProperties = in.readBundle(); 184 } 185 } 186