19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.Editable;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.SpannableString;
22a4a5758b5f51834f7555ec909e8188c432fc5dc4Gilles Debunneimport android.text.Spanned;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.TextUtils;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.QwertyKeyListener;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
268a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
278a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * An editable text view, extending {@link AutoCompleteTextView}, that
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * can show completion suggestions for the substring of the text where
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the user is typing instead of necessarily for the entire thing.
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>
34979154ffd3dc3a3a76ec11fbf3f50440c13b70b6Daisuke Miyakawa * You must provide a {@link Tokenizer} to distinguish the
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * various substrings.
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>The following code snippet shows how to create a text view which suggests
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * various countries names while the user is typing:</p>
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <pre class="prettyprint">
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * public class CountriesActivity extends Activity {
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *     protected void onCreate(Bundle savedInstanceState) {
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         super.onCreate(savedInstanceState);
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         setContentView(R.layout.autocomplete_7);
45979154ffd3dc3a3a76ec11fbf3f50440c13b70b6Daisuke Miyakawa *
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         ArrayAdapter&lt;String&gt; adapter = new ArrayAdapter&lt;String&gt;(this,
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *                 android.R.layout.simple_dropdown_item_1line, COUNTRIES);
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         MultiAutoCompleteTextView textView = (MultiAutoCompleteTextView) findViewById(R.id.edit);
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         textView.setAdapter(adapter);
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         textView.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *     }
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *     private static final String[] COUNTRIES = new String[] {
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *         "Belgium", "France", "Italy", "Germany", "Spain"
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *     };
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * }</pre>
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class MultiAutoCompleteTextView extends AutoCompleteTextView {
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Tokenizer mTokenizer;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public MultiAutoCompleteTextView(Context context) {
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, null);
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public MultiAutoCompleteTextView(Context context, AttributeSet attrs) {
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, attrs, com.android.internal.R.attr.autoCompleteTextViewStyle);
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public MultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super(context, attrs, defStyle);
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ void finishInit() { }
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Sets the Tokenizer that will be used to determine the relevant
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * range of the text where the user is typing.
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setTokenizer(Tokenizer t) {
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTokenizer = t;
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Instead of filtering on the entire contents of the edit box,
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * this subclass method filters on the range from
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link Tokenizer#findTokenStart} to {@link #getSelectionEnd}
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * if the length of that range meets or exceeds {@link #getThreshold}.
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void performFiltering(CharSequence text, int keyCode) {
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (enoughToFilter()) {
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int end = getSelectionEnd();
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start = mTokenizer.findTokenStart(text, end);
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            performFiltering(text, start, end, keyCode);
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dismissDropDown();
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Filter f = getFilter();
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f != null) {
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                f.filter(null);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Instead of filtering whenever the total length of the text
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * exceeds the threshhold, this subclass filters only when the
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * length of the range from
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link Tokenizer#findTokenStart} to {@link #getSelectionEnd}
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * meets or exceeds {@link #getThreshold}.
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean enoughToFilter() {
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Editable text = getText();
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end = getSelectionEnd();
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (end < 0 || mTokenizer == null) {
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = mTokenizer.findTokenStart(text, end);
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (end - start >= getThreshold()) {
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Instead of validating the entire text, this subclass method validates
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * each token of the text individually.  Empty tokens are removed.
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
136979154ffd3dc3a3a76ec11fbf3f50440c13b70b6Daisuke Miyakawa    @Override
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void performValidation() {
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Validator v = getValidator();
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (v == null || mTokenizer == null) {
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Editable e = getText();
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i = getText().length();
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (i > 0) {
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start = mTokenizer.findTokenStart(e, i);
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int end = mTokenizer.findTokenEnd(e, start);
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            CharSequence sub = e.subSequence(start, end);
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (TextUtils.isEmpty(sub)) {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                e.replace(start, i, "");
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (!v.isValid(sub)) {
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                e.replace(start, i,
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                          mTokenizer.terminateToken(v.fixText(sub)));
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            i = start;
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Starts filtering the content of the drop down list. The filtering
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * pattern is the specified range of text from the edit box. Subclasses may
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * override this method to filter with a different pattern, for
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * instance a smaller substring of <code>text</code>.</p>
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void performFiltering(CharSequence text, int start, int end,
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    int keyCode) {
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        getFilter().filter(text.subSequence(start, end), this);
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Performs the text completion by replacing the range from
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link Tokenizer#findTokenStart} to {@link #getSelectionEnd} by the
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the result of passing <code>text</code> through
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link Tokenizer#terminateToken}.
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * In addition, the replaced region will be marked as an AutoText
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * substition so that if the user immediately presses DEL, the
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * completion will be undone.
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Subclasses may override this method to do some different
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * insertion of the content into the edit box.</p>
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text the selected suggestion in the drop down list
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void replaceText(CharSequence text) {
188c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa        clearComposingText();
189c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end = getSelectionEnd();
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = mTokenizer.findTokenStart(getText(), end);
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Editable editable = getText();
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String original = TextUtils.substring(editable, start, end);
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        QwertyKeyListener.markAsReplaced(editable, start, end, original);
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        editable.replace(start, end, mTokenizer.terminateToken(text));
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2008a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
2018a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
2028a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityEvent(event);
2038a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        event.setClassName(MultiAutoCompleteTextView.class.getName());
2048a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
2058a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
2068a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
2078a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
2088a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityNodeInfo(info);
2098a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        info.setClassName(MultiAutoCompleteTextView.class.getName());
2108a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
2118a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static interface Tokenizer {
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the start of the token that ends at offset
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <code>cursor</code> within <code>text</code>.
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int findTokenStart(CharSequence text, int cursor);
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the end of the token (minus trailing punctuation)
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * that begins at offset <code>cursor</code> within <code>text</code>.
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int findTokenEnd(CharSequence text, int cursor);
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns <code>text</code>, modified, if necessary, to ensure that
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * it ends with a token terminator (for example a space or comma).
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence terminateToken(CharSequence text);
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This simple Tokenizer can be used for lists where the items are
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * separated by a comma and one or more spaces.
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class CommaTokenizer implements Tokenizer {
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int findTokenStart(CharSequence text, int cursor) {
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i = cursor;
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (i > 0 && text.charAt(i - 1) != ',') {
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i--;
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (i < cursor && text.charAt(i) == ' ') {
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i++;
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return i;
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int findTokenEnd(CharSequence text, int cursor) {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i = cursor;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int len = text.length();
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (i < len) {
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (text.charAt(i) == ',') {
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return i;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i++;
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return len;
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence terminateToken(CharSequence text) {
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i = text.length();
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (i > 0 && text.charAt(i - 1) == ' ') {
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                i--;
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (i > 0 && text.charAt(i - 1) == ',') {
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return text;
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (text instanceof Spanned) {
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    SpannableString sp = new SpannableString(text + ", ");
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            Object.class, sp, 0);
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return sp;
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return text + ", ";
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
287