14b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio/* 24b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * Copyright (C) 2011 The Android Open Source Project 34b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * 44b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * Licensed under the Apache License, Version 2.0 (the "License"); 54b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * you may not use this file except in compliance with the License. 64b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * You may obtain a copy of the License at 74b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * 84b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * http://www.apache.org/licenses/LICENSE-2.0 94b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * 104b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * Unless required by applicable law or agreed to in writing, software 114b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * distributed under the License is distributed on an "AS IS" BASIS, 124b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * See the License for the specific language governing permissions and 144b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio * limitations under the License. 154b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio */ 16cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 17cb379120456d8065d742021fc5c66748fc8a11a8Doug Feltpackage android.text; 18cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 19cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 203fb824bae3322252a68c1cf8537280a5d2bd356dFabrice Di Meglioimport android.view.View; 217810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio 2257a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglioimport java.nio.CharBuffer; 2357a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio 24cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt/** 25e442662330c972aa96986e6f09305be836b6a7f4Scott Main * Some objects that implement {@link TextDirectionHeuristic}. Use these with 26e442662330c972aa96986e6f09305be836b6a7f4Scott Main * the {@link BidiFormatter#unicodeWrap unicodeWrap()} methods in {@link BidiFormatter}. 27e442662330c972aa96986e6f09305be836b6a7f4Scott Main * Also notice that these direction heuristics correspond to the same types of constants 28e442662330c972aa96986e6f09305be836b6a7f4Scott Main * provided in the {@link android.view.View} class for {@link android.view.View#setTextDirection 29e442662330c972aa96986e6f09305be836b6a7f4Scott Main * setTextDirection()}, such as {@link android.view.View#TEXT_DIRECTION_RTL}. 30e442662330c972aa96986e6f09305be836b6a7f4Scott Main * <p>To support versions lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, 31e442662330c972aa96986e6f09305be836b6a7f4Scott Main * you can use the support library's {@link android.support.v4.text.TextDirectionHeuristicsCompat} 32e442662330c972aa96986e6f09305be836b6a7f4Scott Main * class. 33cefb4bc011fe47ec83b2fd9ec4da28bdf806c0cdGilles Debunne * 34cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 35cb379120456d8065d742021fc5c66748fc8a11a8Doug Feltpublic class TextDirectionHeuristics { 36cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 3757a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio /** 3857a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * Always decides that the direction is left to right. 3957a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio */ 40cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public static final TextDirectionHeuristic LTR = 41cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt new TextDirectionHeuristicInternal(null /* no algorithm */, false); 42cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 4357a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio /** 4457a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * Always decides that the direction is right to left. 4557a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio */ 46cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public static final TextDirectionHeuristic RTL = 47cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt new TextDirectionHeuristicInternal(null /* no algorithm */, true); 48cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 49cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 5057a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * Determines the direction based on the first strong directional character, including bidi 5157a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * format chars, falling back to left to right if it finds none. This is the default behavior 5257a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * of the Unicode Bidirectional Algorithm. 53cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 54cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public static final TextDirectionHeuristic FIRSTSTRONG_LTR = 55cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false); 56cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 57cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 5857a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * Determines the direction based on the first strong directional character, including bidi 5957a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * format chars, falling back to right to left if it finds none. This is similar to the default 6057a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * behavior of the Unicode Bidirectional Algorithm, just with different fallback behavior. 61cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 62cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public static final TextDirectionHeuristic FIRSTSTRONG_RTL = 63cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true); 64cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 65cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 6657a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * If the text contains any strong right to left non-format character, determines that the 6757a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * direction is right to left, falling back to left to right if it finds none. 684b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio */ 694b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio public static final TextDirectionHeuristic ANYRTL_LTR = 704b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false); 714b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio 724b60c30838fbd635964f1e79c057de5048dcc66fFabrice Di Meglio /** 737810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio * Force the paragraph direction to the Locale direction. Falls back to left to right. 747810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio */ 757810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio public static final TextDirectionHeuristic LOCALE = TextDirectionHeuristicLocale.INSTANCE; 767810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio 7757a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio /** 7857a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * State constants for taking care about true / false / unknown 7957a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio */ 8057a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio private static final int STATE_TRUE = 0; 8157a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio private static final int STATE_FALSE = 1; 8257a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio private static final int STATE_UNKNOWN = 2; 8357a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio 84473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader /* Returns STATE_TRUE for strong RTL characters, STATE_FALSE for strong LTR characters, and 85473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader * STATE_UNKNOWN for everything else. 86473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader */ 87473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader private static int isRtlCodePoint(int codePoint) { 88473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader switch (Character.getDirectionality(codePoint)) { 8957a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio case Character.DIRECTIONALITY_LEFT_TO_RIGHT: 9057a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio return STATE_FALSE; 9157a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio case Character.DIRECTIONALITY_RIGHT_TO_LEFT: 9257a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: 9357a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio return STATE_TRUE; 94473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader case Character.DIRECTIONALITY_UNDEFINED: 95473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // Unassigned characters still have bidi direction, defined at: 96473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // http://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedBidiClass.txt 9757a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio 98473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader if ((0x0590 <= codePoint && codePoint <= 0x08FF) || 99473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0xFB1D <= codePoint && codePoint <= 0xFDCF) || 100473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0xFDF0 <= codePoint && codePoint <= 0xFDFF) || 101473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0xFE70 <= codePoint && codePoint <= 0xFEFF) || 102473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0x10800 <= codePoint && codePoint <= 0x10FFF) || 103473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0x1E800 <= codePoint && codePoint <= 0x1EFFF)) { 104473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // Unassigned RTL character 105473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader return STATE_TRUE; 106473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } else if ( 107473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // Potentially-unassigned Default_Ignorable. Ranges are from unassigned 108473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // characters that have Unicode property Other_Default_Ignorable_Code_Point 109473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // plus some enlargening to cover bidi isolates and simplify checks. 110473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0x2065 <= codePoint && codePoint <= 0x2069) || 111473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0xFFF0 <= codePoint && codePoint <= 0xFFF8) || 112473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0xE0000 <= codePoint && codePoint <= 0xE0FFF) || 113473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // Non-character 114473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0xFDD0 <= codePoint && codePoint <= 0xFDEF) || 115473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader ((codePoint & 0xFFFE) == 0xFFFE) || 116473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // Currency symbol 117473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0x20A0 <= codePoint && codePoint <= 0x20CF) || 118473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // Unpaired surrogate 119473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader (0xD800 <= codePoint && codePoint <= 0xDFFF)) { 120473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader return STATE_UNKNOWN; 121473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } else { 122473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // Unassigned LTR character 123473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader return STATE_FALSE; 124473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } 12557a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio default: 12657a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio return STATE_UNKNOWN; 12757a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio } 128cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 129cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 130cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 131cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Computes the text direction based on an algorithm. Subclasses implement 132cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the 133cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * direction from the text alone. 134cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 135e7beae3f4c9c170c7c6c42cf9b572f0ee1ec9c81Fabrice Di Meglio private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic { 136cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private final TextDirectionAlgorithm mAlgorithm; 137cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 138cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) { 139cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt mAlgorithm = algorithm; 140cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 141cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 142cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 143cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Return true if the default text direction is rtl. 144cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 145cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt abstract protected boolean defaultIsRtl(); 146cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 147cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt @Override 14857a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio public boolean isRtl(char[] array, int start, int count) { 14957a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio return isRtl(CharBuffer.wrap(array), start, count); 15057a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio } 15157a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio 15257a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio @Override 15357a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio public boolean isRtl(CharSequence cs, int start, int count) { 15457a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio if (cs == null || start < 0 || count < 0 || cs.length() - count < start) { 155cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt throw new IllegalArgumentException(); 156cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 157cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt if (mAlgorithm == null) { 158cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return defaultIsRtl(); 159cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 16057a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio return doCheck(cs, start, count); 161cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 162cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 16357a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio private boolean doCheck(CharSequence cs, int start, int count) { 16457a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio switch(mAlgorithm.checkRtl(cs, start, count)) { 16557a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio case STATE_TRUE: 166cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return true; 16757a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio case STATE_FALSE: 168cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return false; 169cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt default: 170cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return defaultIsRtl(); 171cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 172cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 173cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 174cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 175cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl { 176cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private final boolean mDefaultIsRtl; 177cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 178cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm, 179cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt boolean defaultIsRtl) { 180cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt super(algorithm); 181cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt mDefaultIsRtl = defaultIsRtl; 182cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 183cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 184cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt @Override 185cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt protected boolean defaultIsRtl() { 186cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return mDefaultIsRtl; 187cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 188cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 189cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 190cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 191cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Interface for an algorithm to guess the direction of a paragraph of text. 192cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 193e7beae3f4c9c170c7c6c42cf9b572f0ee1ec9c81Fabrice Di Meglio private static interface TextDirectionAlgorithm { 194cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 195cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt * Returns whether the range of text is RTL according to the algorithm. 196cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 19757a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio int checkRtl(CharSequence cs, int start, int count); 198cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 199cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 200cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 20157a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio * Algorithm that uses the first strong directional character to determine the paragraph 202473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader * direction. This is the standard Unicode Bidirectional Algorithm (steps P2 and P3), with the 203473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader * exception that if no strong character is found, UNKNOWN is returned. 204cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 205e7beae3f4c9c170c7c6c42cf9b572f0ee1ec9c81Fabrice Di Meglio private static class FirstStrong implements TextDirectionAlgorithm { 206cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt @Override 20757a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio public int checkRtl(CharSequence cs, int start, int count) { 20857a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio int result = STATE_UNKNOWN; 209473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader int openIsolateCount = 0; 210473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader for (int cp, i = start, end = start + count; 211473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader i < end && result == STATE_UNKNOWN; 212473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader i += Character.charCount(cp)) { 213473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader cp = Character.codePointAt(cs, i); 214473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader if (0x2066 <= cp && cp <= 0x2068) { // Opening isolates 215473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader openIsolateCount += 1; 216473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } else if (cp == 0x2069) { // POP DIRECTIONAL ISOLATE (PDI) 217473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader if (openIsolateCount > 0) openIsolateCount -= 1; 218473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } else if (openIsolateCount == 0) { 219473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // Only consider the characters outside isolate pairs 220473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader result = isRtlCodePoint(cp); 221473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } 222cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 223cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt return result; 224cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 225cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 226cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private FirstStrong() { 227cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 228cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 229cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public static final FirstStrong INSTANCE = new FirstStrong(); 230cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 231cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 232cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 233473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader * Algorithm that uses the presence of any strong directional character of the type indicated 234473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader * in the constructor parameter to determine the direction of text. 235473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader * 236473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader * Characters inside isolate pairs are skipped. 237cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt */ 238e7beae3f4c9c170c7c6c42cf9b572f0ee1ec9c81Fabrice Di Meglio private static class AnyStrong implements TextDirectionAlgorithm { 239cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private final boolean mLookForRtl; 240cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 241cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt @Override 24257a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio public int checkRtl(CharSequence cs, int start, int count) { 243cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt boolean haveUnlookedFor = false; 244473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader int openIsolateCount = 0; 245473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader for (int cp, i = start, end = start + count; i < end; i += Character.charCount(cp)) { 246473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader cp = Character.codePointAt(cs, i); 247473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader if (0x2066 <= cp && cp <= 0x2068) { // Opening isolates 248473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader openIsolateCount += 1; 249473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } else if (cp == 0x2069) { // POP DIRECTIONAL ISOLATE (PDI) 250473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader if (openIsolateCount > 0) openIsolateCount -= 1; 251473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } else if (openIsolateCount == 0) { 252473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader // Only consider the characters outside isolate pairs 253473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader switch (isRtlCodePoint(cp)) { 254473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader case STATE_TRUE: 255473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader if (mLookForRtl) { 256473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader return STATE_TRUE; 257473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } 258473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader haveUnlookedFor = true; 259473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader break; 260473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader case STATE_FALSE: 261473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader if (!mLookForRtl) { 262473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader return STATE_FALSE; 263473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } 264473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader haveUnlookedFor = true; 265473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader break; 266473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader default: 267473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader break; 268473601013923d6b5a30fecad5dbc4ff719f38fe4Roozbeh Pournader } 269cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 270cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 271cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt if (haveUnlookedFor) { 27257a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio return mLookForRtl ? STATE_FALSE : STATE_TRUE; 273cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 27457a85740d721caf8dcd94a545b2dd920e8e84e01Fabrice Di Meglio return STATE_UNKNOWN; 275cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 276cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 277cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt private AnyStrong(boolean lookForRtl) { 278cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt this.mLookForRtl = lookForRtl; 279cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 280cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 281cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public static final AnyStrong INSTANCE_RTL = new AnyStrong(true); 282cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt public static final AnyStrong INSTANCE_LTR = new AnyStrong(false); 283cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt } 284cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt 285cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt /** 2867810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio * Algorithm that uses the Locale direction to force the direction of a paragraph. 2877810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio */ 288e7beae3f4c9c170c7c6c42cf9b572f0ee1ec9c81Fabrice Di Meglio private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl { 2897810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio 2907810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio public TextDirectionHeuristicLocale() { 2917810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio super(null); 2927810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio } 2937810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio 2947810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio @Override 2957810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio protected boolean defaultIsRtl() { 296d3d9f3f1004dfee2649a26cfe8dba948cd364904Fabrice Di Meglio final int dir = TextUtils.getLayoutDirectionFromLocale(java.util.Locale.getDefault()); 2973fb824bae3322252a68c1cf8537280a5d2bd356dFabrice Di Meglio return (dir == View.LAYOUT_DIRECTION_RTL); 2987810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio } 2997810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio 3007810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio public static final TextDirectionHeuristicLocale INSTANCE = 3017810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio new TextDirectionHeuristicLocale(); 3027810b5f8cffb3c2c98fd0df579f4da5a9ac6cc73Fabrice Di Meglio } 303cb379120456d8065d742021fc5c66748fc8a11a8Doug Felt} 304