TextUtilsCompat.java revision 15375aa6fd54b036f97f99229aefab2822c8a1c9
1/*
2 * Copyright (C) 2013 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
17package android.support.v4.text;
18
19import android.os.Build;
20import android.support.annotation.NonNull;
21import android.support.annotation.Nullable;
22import android.support.v4.view.ViewCompat;
23
24import java.util.Locale;
25
26public final class TextUtilsCompat {
27    private static class TextUtilsCompatImpl {
28        @NonNull
29        public String htmlEncode(@NonNull String s) {
30            StringBuilder sb = new StringBuilder();
31            char c;
32            for (int i = 0; i < s.length(); i++) {
33                c = s.charAt(i);
34                switch (c) {
35                    case '<':
36                        sb.append("&lt;"); //$NON-NLS-1$
37                        break;
38                    case '>':
39                        sb.append("&gt;"); //$NON-NLS-1$
40                        break;
41                    case '&':
42                        sb.append("&amp;"); //$NON-NLS-1$
43                        break;
44                    case '\'':
45                        //http://www.w3.org/TR/xhtml1
46                        // The named character reference &apos; (the apostrophe, U+0027) was
47                        // introduced in XML 1.0 but does not appear in HTML. Authors should
48                        // therefore use &#39; instead of &apos; to work as expected in HTML 4
49                        // user agents.
50                        sb.append("&#39;"); //$NON-NLS-1$
51                        break;
52                    case '"':
53                        sb.append("&quot;"); //$NON-NLS-1$
54                        break;
55                    default:
56                        sb.append(c);
57                }
58            }
59            return sb.toString();
60        }
61
62        public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
63            if (locale != null && !locale.equals(ROOT)) {
64                final String scriptSubtag = ICUCompat.maximizeAndGetScript(locale);
65                if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale);
66
67                if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
68                        scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
69                    return ViewCompat.LAYOUT_DIRECTION_RTL;
70                }
71            }
72            return ViewCompat.LAYOUT_DIRECTION_LTR;
73        }
74
75        /**
76         * Fallback algorithm to detect the locale direction. Rely on the first char of the
77         * localized locale name. This will not work if the localized locale name is in English
78         * (this is the case for ICU 4.4 and "Urdu" script)
79         *
80         * @param locale
81         * @return the layout direction. This may be one of:
82         * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
83         * {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
84         *
85         * Be careful: this code will need to be updated when vertical scripts will be supported
86         */
87        private static int getLayoutDirectionFromFirstChar(@NonNull Locale locale) {
88            switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
89                case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
90                case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
91                    return ViewCompat.LAYOUT_DIRECTION_RTL;
92
93                case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
94                default:
95                    return ViewCompat.LAYOUT_DIRECTION_LTR;
96            }
97        }
98    }
99
100    private static class TextUtilsCompatJellybeanMr1Impl extends TextUtilsCompatImpl {
101        @Override
102        @NonNull
103        public String htmlEncode(@NonNull String s) {
104            return TextUtilsCompatJellybeanMr1.htmlEncode(s);
105        }
106
107        @Override
108        public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
109            return TextUtilsCompatJellybeanMr1.getLayoutDirectionFromLocale(locale);
110        }
111    }
112
113    private static final TextUtilsCompatImpl IMPL;
114    static {
115        final int version = Build.VERSION.SDK_INT;
116        if (version >= 17) { // JellyBean MR1
117            IMPL = new TextUtilsCompatJellybeanMr1Impl();
118        } else {
119            IMPL = new TextUtilsCompatImpl();
120        }
121    }
122
123    /**
124     * Html-encode the string.
125     * @param s the string to be encoded
126     * @return the encoded string
127     */
128    @NonNull
129    public static String htmlEncode(@NonNull String s) {
130        return IMPL.htmlEncode(s);
131    }
132
133    /**
134     * Return the layout direction for a given Locale
135     *
136     * @param locale the Locale for which we want the layout direction. Can be null.
137     * @return the layout direction. This may be one of:
138     * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
139     * {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
140     *
141     * Be careful: this code will need to be updated when vertical scripts will be supported
142     */
143    public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
144        return IMPL.getLayoutDirectionFromLocale(locale);
145    }
146
147    public static final Locale ROOT = new Locale("", "");
148
149    private static String ARAB_SCRIPT_SUBTAG = "Arab";
150    private static String HEBR_SCRIPT_SUBTAG = "Hebr";
151
152    private TextUtilsCompat() {}
153}
154