13e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/*
23e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Copyright (C) 2009 The Android Open Source Project
33e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
43e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
53e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * you may not use this file except in compliance with the License.
63e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * You may obtain a copy of the License at
73e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
83e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
93e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * See the License for the specific language governing permissions and
143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * limitations under the License.
153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpackage com.android.quicksearchbox;
183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
19120040ef31d79c1d69138b13ca7f256841f3298eMathew Inwoodimport com.google.common.annotations.VisibleForTesting;
20120040ef31d79c1d69138b13ca7f256841f3298eMathew Inwood
21fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.database.DataSetObservable;
22fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.database.DataSetObserver;
233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport java.util.ArrayList;
255229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwoodimport java.util.Collection;
265229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwoodimport java.util.HashSet;
273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
2993bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert * A SuggestionCursor that is backed by a list of Suggestions.
303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
3104a180b52fb4100a2f3747e795fb5d26e3207a4aBjorn Bringertpublic class ListSuggestionCursor extends AbstractSuggestionCursorWrapper {
323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3308ff0a7cb6b99db79508fa3124730eb81411bc56Bjorn Bringert    private static final int DEFAULT_CAPACITY = 16;
3408ff0a7cb6b99db79508fa3124730eb81411bc56Bjorn Bringert
35fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private final DataSetObservable mDataSetObservable = new DataSetObservable();
36fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
375229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood    private final ArrayList<Entry> mSuggestions;
385229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood
395229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood    private HashSet<String> mExtraColumns;
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4193bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert    private int mPos = 0;
423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public ListSuggestionCursor(String userQuery) {
4408ff0a7cb6b99db79508fa3124730eb81411bc56Bjorn Bringert        this(userQuery, DEFAULT_CAPACITY);
4593bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert    }
4693bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert
47120040ef31d79c1d69138b13ca7f256841f3298eMathew Inwood    @VisibleForTesting
4893bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert    public ListSuggestionCursor(String userQuery, Suggestion...suggestions) {
4908ff0a7cb6b99db79508fa3124730eb81411bc56Bjorn Bringert        this(userQuery, suggestions.length);
5093bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        for (Suggestion suggestion : suggestions) {
515229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood            add(suggestion);
5293bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        }
533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5508ff0a7cb6b99db79508fa3124730eb81411bc56Bjorn Bringert    public ListSuggestionCursor(String userQuery, int capacity) {
5608ff0a7cb6b99db79508fa3124730eb81411bc56Bjorn Bringert        super(userQuery);
575229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        mSuggestions = new ArrayList<Entry>(capacity);
5808ff0a7cb6b99db79508fa3124730eb81411bc56Bjorn Bringert    }
5908ff0a7cb6b99db79508fa3124730eb81411bc56Bjorn Bringert
603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Adds a suggestion from another suggestion cursor.
623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @return {@code true} if the suggestion was added.
643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
6593bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert    public boolean add(Suggestion suggestion) {
665229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        mSuggestions.add(new Entry(suggestion));
673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return true;
683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void close() {
713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSuggestions.clear();
723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public int getPosition() {
753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mPos;
763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void moveTo(int pos) {
793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mPos = pos;
803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
8287e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert    public boolean moveToNext() {
8387e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert        int size = mSuggestions.size();
8487e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert        if (mPos >= size) {
8587e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert            // Already past the end
8687e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert            return false;
8787e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert        }
8887e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert        mPos++;
8987e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert        return mPos < size;
9087e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert    }
9187e947cbd9f279a83337900ff8bbd5ab0a8dc455Bjorn Bringert
9294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    public void removeRow() {
9394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        mSuggestions.remove(mPos);
9494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    }
9594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
9693bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert    public void replaceRow(Suggestion suggestion) {
975229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        mSuggestions.set(mPos, new Entry(suggestion));
9894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    }
9994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
1003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public int getCount() {
1013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mSuggestions.size();
1023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
10404a180b52fb4100a2f3747e795fb5d26e3207a4aBjorn Bringert    @Override
10593bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert    protected Suggestion current() {
1065229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        return mSuggestions.get(mPos).get();
1073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
109bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert    @Override
110bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert    public String toString() {
1119038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        return getClass().getSimpleName() + "{[" + getUserQuery() + "] " + mSuggestions + "}";
112bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert    }
113bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert
114fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    /**
115fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     * Register an observer that is called when changes happen to this data set.
116fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     *
117fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     * @param observer gets notified when the data set changes.
118fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     */
119fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public void registerDataSetObserver(DataSetObserver observer) {
120fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mDataSetObservable.registerObserver(observer);
121ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert    }
122ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert
123fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    /**
124fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     * Unregister an observer that has previously been registered with
125fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     * {@link #registerDataSetObserver(DataSetObserver)}
126fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     *
127fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     * @param observer the observer to unregister.
128fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     */
129fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public void unregisterDataSetObserver(DataSetObserver observer) {
130fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mDataSetObservable.unregisterObserver(observer);
1313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
133fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected void notifyDataSetChanged() {
134fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        mDataSetObservable.notifyChanged();
1353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1365229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood
1375229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood    @Override
1385229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood    public SuggestionExtras getExtras() {
1395229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        // override with caching to avoid re-parsing the extras
1405229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        return mSuggestions.get(mPos).getExtras();
1415229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood    }
1425229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood
1435229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood   public Collection<String> getExtraColumns() {
1445229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        if (mExtraColumns == null) {
1455229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood            mExtraColumns = new HashSet<String>();
1465229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood            for (Entry e : mSuggestions) {
1475229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood                SuggestionExtras extras = e.getExtras();
1485229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood                Collection<String> extraColumns = extras == null ? null
1495229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood                        : extras.getExtraColumnNames();
1505229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood                if (extraColumns != null) {
1515229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood                    for (String column : extras.getExtraColumnNames()) {
1525229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood                        mExtraColumns.add(column);
1535229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood                    }
1545229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood                }
1555229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood            }
1565229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        }
1575229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        return mExtraColumns.isEmpty() ? null : mExtraColumns;
1585229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood    }
1595229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood
1605229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood    /**
1615229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood     * This class exists purely to cache the suggestion extras.
1625229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood     */
1635229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood    private static class Entry {
1645229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        private final Suggestion mSuggestion;
1655229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        private SuggestionExtras mExtras;
1665229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        public Entry(Suggestion s) {
1675229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood            mSuggestion = s;
1685229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        }
1695229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        public Suggestion get() {
1705229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood            return mSuggestion;
1715229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        }
1725229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        public SuggestionExtras getExtras() {
1735229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood            if (mExtras == null) {
1745229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood                mExtras = mSuggestion.getExtras();
1755229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood            }
1765229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood            return mExtras;
1775229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood        }
1785229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood    }
1795229b06f00d20aac76cd8519b37f2a579d61c54fMathew Inwood
1803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
181