1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text; 18 19 20 import android.view.View; 21 22 /** 23 * Some objects that implement TextDirectionHeuristic. 24 * 25 * @hide 26 */ 27 public class TextDirectionHeuristics { 28 29 /** Always decides that the direction is left to right. */ 30 public static final TextDirectionHeuristic LTR = 31 new TextDirectionHeuristicInternal(null /* no algorithm */, false); 32 33 /** Always decides that the direction is right to left. */ 34 public static final TextDirectionHeuristic RTL = 35 new TextDirectionHeuristicInternal(null /* no algorithm */, true); 36 37 /** 38 * Determines the direction based on the first strong directional character, 39 * including bidi format chars, falling back to left to right if it 40 * finds none. This is the default behavior of the Unicode Bidirectional 41 * Algorithm. 42 */ 43 public static final TextDirectionHeuristic FIRSTSTRONG_LTR = 44 new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false); 45 46 /** 47 * Determines the direction based on the first strong directional character, 48 * including bidi format chars, falling back to right to left if it 49 * finds none. This is similar to the default behavior of the Unicode 50 * Bidirectional Algorithm, just with different fallback behavior. 51 */ 52 public static final TextDirectionHeuristic FIRSTSTRONG_RTL = 53 new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true); 54 55 /** 56 * If the text contains any strong right to left non-format character, determines 57 * that the direction is right to left, falling back to left to right if it 58 * finds none. 59 */ 60 public static final TextDirectionHeuristic ANYRTL_LTR = 61 new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false); 62 63 /** 64 * Force the paragraph direction to the Locale direction. Falls back to left to right. 65 */ 66 public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE; 67 68 private static enum TriState { 69 TRUE, FALSE, UNKNOWN; 70 } 71 72 /** 73 * Computes the text direction based on an algorithm. Subclasses implement 74 * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the 75 * direction from the text alone. 76 */ 77 private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic { 78 private final TextDirectionAlgorithm mAlgorithm; 79 TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm)80 public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) { 81 mAlgorithm = algorithm; 82 } 83 84 /** 85 * Return true if the default text direction is rtl. 86 */ defaultIsRtl()87 abstract protected boolean defaultIsRtl(); 88 89 @Override isRtl(char[] chars, int start, int count)90 public boolean isRtl(char[] chars, int start, int count) { 91 if (chars == null || start < 0 || count < 0 || chars.length - count < start) { 92 throw new IllegalArgumentException(); 93 } 94 if (mAlgorithm == null) { 95 return defaultIsRtl(); 96 } 97 return doCheck(chars, start, count); 98 } 99 doCheck(char[] chars, int start, int count)100 private boolean doCheck(char[] chars, int start, int count) { 101 switch(mAlgorithm.checkRtl(chars, start, count)) { 102 case TRUE: 103 return true; 104 case FALSE: 105 return false; 106 default: 107 return defaultIsRtl(); 108 } 109 } 110 } 111 112 private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl { 113 private final boolean mDefaultIsRtl; 114 TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm, boolean defaultIsRtl)115 private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm, 116 boolean defaultIsRtl) { 117 super(algorithm); 118 mDefaultIsRtl = defaultIsRtl; 119 } 120 121 @Override defaultIsRtl()122 protected boolean defaultIsRtl() { 123 return mDefaultIsRtl; 124 } 125 } 126 isRtlText(int directionality)127 private static TriState isRtlText(int directionality) { 128 switch (directionality) { 129 case Character.DIRECTIONALITY_LEFT_TO_RIGHT: 130 return TriState.FALSE; 131 case Character.DIRECTIONALITY_RIGHT_TO_LEFT: 132 case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: 133 return TriState.TRUE; 134 default: 135 return TriState.UNKNOWN; 136 } 137 } 138 isRtlTextOrFormat(int directionality)139 private static TriState isRtlTextOrFormat(int directionality) { 140 switch (directionality) { 141 case Character.DIRECTIONALITY_LEFT_TO_RIGHT: 142 case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: 143 case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: 144 return TriState.FALSE; 145 case Character.DIRECTIONALITY_RIGHT_TO_LEFT: 146 case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: 147 case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: 148 case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: 149 return TriState.TRUE; 150 default: 151 return TriState.UNKNOWN; 152 } 153 } 154 155 /** 156 * Interface for an algorithm to guess the direction of a paragraph of text. 157 * 158 */ 159 private static interface TextDirectionAlgorithm { 160 /** 161 * Returns whether the range of text is RTL according to the algorithm. 162 * 163 */ checkRtl(char[] text, int start, int count)164 TriState checkRtl(char[] text, int start, int count); 165 } 166 167 /** 168 * Algorithm that uses the first strong directional character to determine 169 * the paragraph direction. This is the standard Unicode Bidirectional 170 * algorithm. 171 * 172 */ 173 private static class FirstStrong implements TextDirectionAlgorithm { 174 @Override checkRtl(char[] text, int start, int count)175 public TriState checkRtl(char[] text, int start, int count) { 176 TriState result = TriState.UNKNOWN; 177 for (int i = start, e = start + count; i < e && result == TriState.UNKNOWN; ++i) { 178 result = isRtlTextOrFormat(Character.getDirectionality(text[i])); 179 } 180 return result; 181 } 182 FirstStrong()183 private FirstStrong() { 184 } 185 186 public static final FirstStrong INSTANCE = new FirstStrong(); 187 } 188 189 /** 190 * Algorithm that uses the presence of any strong directional non-format 191 * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the 192 * direction of text. 193 * 194 */ 195 private static class AnyStrong implements TextDirectionAlgorithm { 196 private final boolean mLookForRtl; 197 198 @Override checkRtl(char[] text, int start, int count)199 public TriState checkRtl(char[] text, int start, int count) { 200 boolean haveUnlookedFor = false; 201 for (int i = start, e = start + count; i < e; ++i) { 202 switch (isRtlText(Character.getDirectionality(text[i]))) { 203 case TRUE: 204 if (mLookForRtl) { 205 return TriState.TRUE; 206 } 207 haveUnlookedFor = true; 208 break; 209 case FALSE: 210 if (!mLookForRtl) { 211 return TriState.FALSE; 212 } 213 haveUnlookedFor = true; 214 break; 215 default: 216 break; 217 } 218 } 219 if (haveUnlookedFor) { 220 return mLookForRtl ? TriState.FALSE : TriState.TRUE; 221 } 222 return TriState.UNKNOWN; 223 } 224 AnyStrong(boolean lookForRtl)225 private AnyStrong(boolean lookForRtl) { 226 this.mLookForRtl = lookForRtl; 227 } 228 229 public static final AnyStrong INSTANCE_RTL = new AnyStrong(true); 230 public static final AnyStrong INSTANCE_LTR = new AnyStrong(false); 231 } 232 233 /** 234 * Algorithm that uses the Locale direction to force the direction of a paragraph. 235 */ 236 private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl { 237 TextDirectionHeuristicLocale()238 public TextDirectionHeuristicLocale() { 239 super(null); 240 } 241 242 @Override defaultIsRtl()243 protected boolean defaultIsRtl() { 244 final int dir = TextUtils.getLayoutDirectionFromLocale(java.util.Locale.getDefault()); 245 return (dir == View.LAYOUT_DIRECTION_RTL); 246 } 247 248 public static final TextDirectionHeuristicLocale INSTANCE = 249 new TextDirectionHeuristicLocale(); 250 } 251 } 252