TextUtilsCompat.java revision c5847d13e40f5d52459f5c0dab32dc08f1a9a683
177f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio/* 277f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * Copyright (C) 2013 The Android Open Source Project 377f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * 477f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * Licensed under the Apache License, Version 2.0 (the "License"); 577f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * you may not use this file except in compliance with the License. 677f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * You may obtain a copy of the License at 777f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * 877f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * http://www.apache.org/licenses/LICENSE-2.0 977f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * 1077f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * Unless required by applicable law or agreed to in writing, software 1177f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * distributed under the License is distributed on an "AS IS" BASIS, 1277f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1377f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * See the License for the specific language governing permissions and 1477f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * limitations under the License. 1577f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio */ 1677f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio 1777f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Megliopackage android.support.v4.text; 1877f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio 199c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagiimport android.os.Build; 20a3ff3273e976adf19770651dcf473fa67b38eb22Tor Norbyeimport android.support.annotation.NonNull; 21a3ff3273e976adf19770651dcf473fa67b38eb22Tor Norbyeimport android.support.annotation.Nullable; 2277f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglioimport android.support.v4.view.ViewCompat; 2377f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio 2477f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglioimport java.util.Locale; 2577f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio 26c5847d13e40f5d52459f5c0dab32dc08f1a9a683Chris Banespublic final class TextUtilsCompat { 279c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi private static class TextUtilsCompatImpl { 289c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi @NonNull 299c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi public String htmlEncode(@NonNull String s) { 309c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi StringBuilder sb = new StringBuilder(); 319c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi char c; 329c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi for (int i = 0; i < s.length(); i++) { 339c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi c = s.charAt(i); 349c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi switch (c) { 359c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi case '<': 369c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi sb.append("<"); //$NON-NLS-1$ 379c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi break; 389c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi case '>': 399c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi sb.append(">"); //$NON-NLS-1$ 409c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi break; 419c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi case '&': 429c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi sb.append("&"); //$NON-NLS-1$ 439c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi break; 449c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi case '\'': 459c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi //http://www.w3.org/TR/xhtml1 469c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi // The named character reference ' (the apostrophe, U+0027) was 479c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi // introduced in XML 1.0 but does not appear in HTML. Authors should 489c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi // therefore use ' instead of ' to work as expected in HTML 4 499c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi // user agents. 509c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi sb.append("'"); //$NON-NLS-1$ 519c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi break; 529c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi case '"': 539c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi sb.append("""); //$NON-NLS-1$ 549c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi break; 559c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi default: 569c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi sb.append(c); 579c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 589c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 599c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi return sb.toString(); 609c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 619c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi 629c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi public int getLayoutDirectionFromLocale(@Nullable Locale locale) { 639c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi if (locale != null && !locale.equals(ROOT)) { 649c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi final String scriptSubtag = ICUCompat.maximizeAndGetScript(locale); 659c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale); 669c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi 679c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) || 689c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) { 699c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi return ViewCompat.LAYOUT_DIRECTION_RTL; 709c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 719c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 729c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi return ViewCompat.LAYOUT_DIRECTION_LTR; 739c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 749c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi 759c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi /** 769c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * Fallback algorithm to detect the locale direction. Rely on the first char of the 779c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * localized locale name. This will not work if the localized locale name is in English 789c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * (this is the case for ICU 4.4 and "Urdu" script) 799c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * 809c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * @param locale 819c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * @return the layout direction. This may be one of: 829c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or 839c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * {@link ViewCompat#LAYOUT_DIRECTION_RTL}. 849c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * 859c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi * Be careful: this code will need to be updated when vertical scripts will be supported 869c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi */ 879c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi private static int getLayoutDirectionFromFirstChar(@NonNull Locale locale) { 889c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) { 899c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi case Character.DIRECTIONALITY_RIGHT_TO_LEFT: 909c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: 919c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi return ViewCompat.LAYOUT_DIRECTION_RTL; 929c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi 939c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi case Character.DIRECTIONALITY_LEFT_TO_RIGHT: 949c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi default: 959c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi return ViewCompat.LAYOUT_DIRECTION_LTR; 969c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 979c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 989c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 999c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi 1009c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi private static class TextUtilsCompatJellybeanMr1Impl extends TextUtilsCompatImpl { 1019c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi @NonNull 1029c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi public String htmlEncode(@NonNull String s) { 1039c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi return TextUtilsCompatJellybeanMr1.htmlEncode(s); 1049c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 1059c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi 1069c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi @Override 1079c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi public int getLayoutDirectionFromLocale(@Nullable Locale locale) { 1089c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi return TextUtilsCompatJellybeanMr1.getLayoutDirectionFromLocale(locale); 1099c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 1109c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 1119c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi 1129c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi private static final TextUtilsCompatImpl IMPL; 1139c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi static { 1149c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi final int version = Build.VERSION.SDK_INT; 1159c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi if (version >= 17) { // JellyBean MR1 1169c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi IMPL = new TextUtilsCompatJellybeanMr1Impl(); 1179c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } else { 1189c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi IMPL = new TextUtilsCompatImpl(); 1199c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 1209c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi } 12177f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio 12277f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio /** 12377f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * Html-encode the string. 12477f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * @param s the string to be encoded 12577f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * @return the encoded string 12677f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio */ 127a3ff3273e976adf19770651dcf473fa67b38eb22Tor Norbye @NonNull 128a3ff3273e976adf19770651dcf473fa67b38eb22Tor Norbye public static String htmlEncode(@NonNull String s) { 1299c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi return IMPL.htmlEncode(s); 13077f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio } 13177f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio 13277f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio /** 13377f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * Return the layout direction for a given Locale 13477f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * 13577f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * @param locale the Locale for which we want the layout direction. Can be null. 13677f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * @return the layout direction. This may be one of: 13777f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or 13877f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * {@link ViewCompat#LAYOUT_DIRECTION_RTL}. 13977f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * 14077f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio * Be careful: this code will need to be updated when vertical scripts will be supported 14177f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio */ 142a3ff3273e976adf19770651dcf473fa67b38eb22Tor Norbye public static int getLayoutDirectionFromLocale(@Nullable Locale locale) { 1439c1039484be273dcc63d9ee475655105361b65a6Keisuke Kuroyanagi return IMPL.getLayoutDirectionFromLocale(locale); 14477f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio } 14577f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio 14677f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio public static final Locale ROOT = new Locale("", ""); 14777f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio 14877f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio private static String ARAB_SCRIPT_SUBTAG = "Arab"; 14977f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio private static String HEBR_SCRIPT_SUBTAG = "Hebr"; 150c5847d13e40f5d52459f5c0dab32dc08f1a9a683Chris Banes 151c5847d13e40f5d52459f5c0dab32dc08f1a9a683Chris Banes private TextUtilsCompat() {} 15277f6bada6f88acea9025afce3eb0127d45411798Fabrice Di Meglio} 153