SuggestionSpan.java revision 4dacef25662de37825fa7a67fd6bdb318b31d8d9
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
17package android.text.style;
18
19import android.content.Context;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.os.SystemClock;
23import android.text.ParcelableSpan;
24import android.text.TextUtils;
25import android.widget.TextView;
26
27import java.util.Arrays;
28import java.util.Locale;
29
30/**
31 * Holds suggestion candidates for the text enclosed in this span.
32 *
33 * When such a span is edited in an EditText, double tapping on the text enclosed in this span will
34 * display a popup dialog listing suggestion replacement for that text. The user can then replace
35 * the original text by one of the suggestions.
36 *
37 * These spans should typically be created by the input method to privide correction and alternates
38 * for the text.
39 *
40 * @see TextView#setSuggestionsEnabled(boolean)
41 */
42public class SuggestionSpan implements ParcelableSpan {
43    /**
44     * Flag for indicating that the input is verbatim. TextView refers to this flag to determine
45     * how it displays a word with SuggestionSpan.
46     */
47    public static final int FLAG_VERBATIM = 0x0001;
48
49    public static final String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED";
50    public static final String SUGGESTION_SPAN_PICKED_AFTER = "after";
51    public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before";
52    public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode";
53
54    public static final int SUGGESTIONS_MAX_SIZE = 5;
55
56    /*
57     * TODO: Needs to check the validity and add a feature that TextView will change
58     * the current IME to the other IME which is specified in SuggestionSpan.
59     * An IME needs to set the span by specifying the target IME and Subtype of SuggestionSpan.
60     * And the current IME might want to specify any IME as the target IME including other IMEs.
61     */
62
63    private final int mFlags;
64    private final String[] mSuggestions;
65    private final String mLocaleString;
66    private final String mNotificationTargetClassName;
67    private final int mHashCode;
68
69    /*
70     * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo
71     * and InputMethodSubtype.
72     */
73
74    /**
75     * @param context Context for the application
76     * @param suggestions Suggestions for the string under the span
77     * @param flags Additional flags indicating how this span is handled in TextView
78     */
79    public SuggestionSpan(Context context, String[] suggestions, int flags) {
80        this(context, null, suggestions, flags, null);
81    }
82
83    /**
84     * @param locale Locale of the suggestions
85     * @param suggestions Suggestions for the string under the span
86     * @param flags Additional flags indicating how this span is handled in TextView
87     */
88    public SuggestionSpan(Locale locale, String[] suggestions, int flags) {
89        this(null, locale, suggestions, flags, null);
90    }
91
92    /**
93     * @param context Context for the application
94     * @param locale locale Locale of the suggestions
95     * @param suggestions Suggestions for the string under the span
96     * @param flags Additional flags indicating how this span is handled in TextView
97     * @param notificationTargetClass if not null, this class will get notified when the user
98     * selects one of the suggestions.
99     */
100    public SuggestionSpan(Context context, Locale locale, String[] suggestions, int flags,
101            Class<?> notificationTargetClass) {
102        final int N = Math.min(SUGGESTIONS_MAX_SIZE, suggestions.length);
103        mSuggestions = Arrays.copyOf(suggestions, N);
104        mFlags = flags;
105        if (context != null && locale == null) {
106            mLocaleString = context.getResources().getConfiguration().locale.toString();
107        } else {
108            mLocaleString = locale.toString();
109        }
110        if (notificationTargetClass != null) {
111            mNotificationTargetClassName = notificationTargetClass.getCanonicalName();
112        } else {
113            mNotificationTargetClassName = "";
114        }
115        mHashCode = hashCodeInternal(
116                mFlags, mSuggestions, mLocaleString, mNotificationTargetClassName);
117    }
118
119    public SuggestionSpan(Parcel src) {
120        mSuggestions = src.readStringArray();
121        mFlags = src.readInt();
122        mLocaleString = src.readString();
123        mNotificationTargetClassName = src.readString();
124        mHashCode = src.readInt();
125    }
126
127    /**
128     * @return an array of suggestion texts for this span
129     */
130    public String[] getSuggestions() {
131        return mSuggestions;
132    }
133
134    /**
135     * @return the locale of the suggestions
136     */
137    public String getLocale() {
138        return mLocaleString;
139    }
140
141    /**
142     * @return The name of the class to notify. The class of the original IME package will receive
143     * a notification when the user selects one of the suggestions. The notification will include
144     * the original string, the suggested replacement string as well as the hashCode of this span.
145     * The class will get notified by an intent that has those information.
146     * This is an internal API because only the framework should know the class name.
147     *
148     * @hide
149     */
150    public String getNotificationTargetClassName() {
151        return mNotificationTargetClassName;
152    }
153
154    public int getFlags() {
155        return mFlags;
156    }
157
158    @Override
159    public int describeContents() {
160        return 0;
161    }
162
163    @Override
164    public void writeToParcel(Parcel dest, int flags) {
165        dest.writeStringArray(mSuggestions);
166        dest.writeInt(mFlags);
167        dest.writeString(mLocaleString);
168        dest.writeString(mNotificationTargetClassName);
169        dest.writeInt(mHashCode);
170    }
171
172    @Override
173    public int getSpanTypeId() {
174        return TextUtils.SUGGESTION_SPAN;
175    }
176
177    @Override
178    public boolean equals(Object o) {
179        if (o instanceof SuggestionSpan) {
180            return ((SuggestionSpan)o).hashCode() == mHashCode;
181        }
182        return false;
183    }
184
185    @Override
186    public int hashCode() {
187        return mHashCode;
188    }
189
190    private static int hashCodeInternal(int flags, String[] suggestions,String locale,
191            String notificationTargetClassName) {
192        return Arrays.hashCode(new Object[] {SystemClock.uptimeMillis(), flags, suggestions, locale,
193                notificationTargetClassName});
194    }
195
196    public static final Parcelable.Creator<SuggestionSpan> CREATOR =
197            new Parcelable.Creator<SuggestionSpan>() {
198        @Override
199        public SuggestionSpan createFromParcel(Parcel source) {
200            return new SuggestionSpan(source);
201        }
202
203        @Override
204        public SuggestionSpan[] newArray(int size) {
205            return new SuggestionSpan[size];
206        }
207    };
208}
209