1f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira/*
2f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * Copyright (C) 2012 Google Inc.
3f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * Licensed to The Android Open Source Project.
4e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal *
5f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
6f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * you may not use this file except in compliance with the License.
7f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * You may obtain a copy of the License at
8e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal *
9f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira *      http://www.apache.org/licenses/LICENSE-2.0
10e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal *
11f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * Unless required by applicable law or agreed to in writing, software
12f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
13f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * See the License for the specific language governing permissions and
15f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira * limitations under the License.
16f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira */
17e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
18e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalpackage com.android.mail.providers;
19e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
20e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.app.SearchManager;
21e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.content.ContentProvider;
22e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.content.ContentResolver;
23e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.content.ContentValues;
24e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.content.Context;
25e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.content.UriMatcher;
26e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.database.Cursor;
27e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.database.sqlite.SQLiteDatabase;
28e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.database.sqlite.SQLiteOpenHelper;
29e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.net.Uri;
30e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.text.TextUtils;
31e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
32e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport com.android.mail.R;
33e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
34e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport java.util.ArrayList;
35e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
36f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereirapublic class SearchRecentSuggestionsProvider extends ContentProvider {
37f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    /*
38f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * String used to delimit different parts of a query.
39f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     */
40f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    public static final String QUERY_TOKEN_SEPARATOR = " ";
41e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
42f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    // client-provided configuration values
43e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private String mAuthority;
44e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private int mMode;
45f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira
46f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    // general database configuration and tables
47e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private SQLiteOpenHelper mOpenHelper;
48e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private static final String sDatabaseName = "suggestions.db";
49e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private static final String sSuggestions = "suggestions";
50e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private static final String ORDER_BY = "date DESC";
51e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private static final String NULL_COLUMN = "query";
52e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
53e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    // Table of database versions.  Don't forget to update!
54e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    // NOTE:  These version values are shifted left 8 bits (x 256) in order to create space for
55e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    // a small set of mode bitflags in the version int.
56e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    //
57e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    // 1      original implementation with queries, and 1 or 2 display columns
58e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    // 1->2   added UNIQUE constraint to display1 column
59f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    private static final int DATABASE_VERSION = 2 * 256;
60e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
61e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    /**
62e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * This mode bit configures the database to record recent queries.  <i>required</i>
63e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     *
64e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * @see #setupSuggestions(String, int)
65e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     */
66f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    public static final int DATABASE_MODE_QUERIES = 1;
67f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira
68f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    // Uri and query support
69e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private static final int URI_MATCH_SUGGEST = 1;
70e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
71e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private Uri mSuggestionsUri;
72e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private UriMatcher mUriMatcher;
73f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira
74e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private String mSuggestSuggestionClause;
75e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private String[] mSuggestionProjection;
76e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
77e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    /**
78e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * Builds the database.  This version has extra support for using the version field
79e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * as a mode flags field, and configures the database columns depending on the mode bits
80e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * (features) requested by the extending class.
81e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     *
82e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * @hide
83e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     */
84e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private static class DatabaseHelper extends SQLiteOpenHelper {
85e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        public DatabaseHelper(Context context, int newVersion) {
86e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            super(context, sDatabaseName, null, newVersion);
87e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
88e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
89e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        @Override
90e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        public void onCreate(SQLiteDatabase db) {
91e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            StringBuilder builder = new StringBuilder();
92e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            builder.append("CREATE TABLE suggestions (" +
93e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                    "_id INTEGER PRIMARY KEY" +
94e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                    ",display1 TEXT UNIQUE ON CONFLICT REPLACE" +
95e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                    ",query TEXT" +
96e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                    ",date LONG" +
97e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                    ");");
98e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            db.execSQL(builder.toString());
99e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
100e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
101e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        @Override
102e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
103e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            db.execSQL("DROP TABLE IF EXISTS suggestions");
104e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            onCreate(db);
105e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
106e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
107e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
108e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    /**
109e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * In order to use this class, you must extend it, and call this setup function from your
110e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * constructor.  In your application or activities, you must provide the same values when
111e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * you create the {@link android.provider.SearchRecentSuggestions} helper.
112e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     *
113e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * @param authority This must match the authority that you've declared in your manifest.
114e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * @param mode You can use mode flags here to determine certain functional aspects of your
115e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * database.  Note, this value should not change from run to run, because when it does change,
116e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * your suggestions database may be wiped.
117e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     *
118e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * @see #DATABASE_MODE_QUERIES
119e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     */
120e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    protected void setupSuggestions(String authority, int mode) {
121e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (TextUtils.isEmpty(authority) ||
122e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                ((mode & DATABASE_MODE_QUERIES) == 0)) {
123e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            throw new IllegalArgumentException();
124e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
125e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
126f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira        // saved values
127e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        mAuthority = new String(authority);
128e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        mMode = mode;
129e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
130f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira        // derived values
131e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        mSuggestionsUri = Uri.parse("content://" + mAuthority + "/suggestions");
132e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
133e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        mUriMatcher.addURI(mAuthority, SearchManager.SUGGEST_URI_PATH_QUERY, URI_MATCH_SUGGEST);
134e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
135e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        // The URI of the icon that we will include on every suggestion here.
136e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        final String historicalIcon = ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
137e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                + getContext().getPackageName() + "/" + R.drawable.ic_history_holo_light;
138e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
139e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        mSuggestSuggestionClause = "display1 LIKE ?";
140f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira        mSuggestionProjection = new String [] {
141e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                "_id",
142f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira                "display1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
143f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira                "query AS " + SearchManager.SUGGEST_COLUMN_QUERY,
144f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira                "'" + historicalIcon + "' AS " + SearchManager.SUGGEST_COLUMN_ICON_1
145e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        };
146e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
147e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
148f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    /**
149f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * This method is provided for use by the ContentResolver.  Do not override, or directly
150f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * call from your own code.
151f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     */
152e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    @Override
153e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    public int delete(Uri uri, String selection, String[] selectionArgs) {
154e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
155e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
156e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        final int length = uri.getPathSegments().size();
157e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (length != 1) {
158e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            throw new IllegalArgumentException("Unknown Uri");
159e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
160e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
161e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        final String base = uri.getPathSegments().get(0);
162e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        int count = 0;
163e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (base.equals(sSuggestions)) {
164e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            count = db.delete(sSuggestions, selection, selectionArgs);
165e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        } else {
166e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            throw new IllegalArgumentException("Unknown Uri");
167e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
168e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        getContext().getContentResolver().notifyChange(uri, null);
169e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        return count;
170e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
171e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
172f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    /**
173f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * This method is provided for use by the ContentResolver.  Do not override, or directly
174f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * call from your own code.
175f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     */
176e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    @Override
177e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    public String getType(Uri uri) {
178e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (mUriMatcher.match(uri) == URI_MATCH_SUGGEST) {
179e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            return SearchManager.SUGGEST_MIME_TYPE;
180e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
181e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        int length = uri.getPathSegments().size();
182e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (length >= 1) {
183e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            String base = uri.getPathSegments().get(0);
184e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            if (base.equals(sSuggestions)) {
185e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                if (length == 1) {
186e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                    return "vnd.android.cursor.dir/suggestion";
187e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                } else if (length == 2) {
188e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                    return "vnd.android.cursor.item/suggestion";
189e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                }
190e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            }
191e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
192e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        throw new IllegalArgumentException("Unknown Uri");
193e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
194e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
195f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    /**
196f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * This method is provided for use by the ContentResolver.  Do not override, or directly
197f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * call from your own code.
198f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     */
199e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    @Override
200e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    public Uri insert(Uri uri, ContentValues values) {
201e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
202f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira
203f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira        int length = uri.getPathSegments().size();
204e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (length < 1) {
205e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            throw new IllegalArgumentException("Unknown Uri");
206e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
207e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        // Note:  This table has on-conflict-replace semantics, so insert() may actually replace()
208e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        long rowID = -1;
209f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira        String base = uri.getPathSegments().get(0);
210e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        Uri newUri = null;
211e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (base.equals(sSuggestions)) {
212e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            if (length == 1) {
213e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                rowID = db.insert(sSuggestions, NULL_COLUMN, values);
214e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                if (rowID > 0) {
215e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                    newUri = Uri.withAppendedPath(mSuggestionsUri, String.valueOf(rowID));
216e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                }
217e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            }
218e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
219e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (rowID < 0) {
220e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            throw new IllegalArgumentException("Unknown Uri");
221e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
222e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        getContext().getContentResolver().notifyChange(newUri, null);
223e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        return newUri;
224e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
225e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
226f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    /**
227f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * This method is provided for use by the ContentResolver.  Do not override, or directly
228f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * call from your own code.
229f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     */
230e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    @Override
231e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    public boolean onCreate() {
232e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (mAuthority == null || mMode == 0) {
233e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            throw new IllegalArgumentException("Provider not configured");
234e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
235e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        int mWorkingDbVersion = DATABASE_VERSION + mMode;
236e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        mOpenHelper = new DatabaseHelper(getContext(), mWorkingDbVersion);
237e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
238e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        return true;
239e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
240e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
241e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private ArrayList<String> mFullQueryTerms;
242e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
243e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    /**
244f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     *  Copy the projection, and change the query field alone.
245e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * @param selectionArgs
246e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * @return projection
247e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     */
248e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    private String[] createProjection(String[] selectionArgs) {
249e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        String[] newProjection = new String[mSuggestionProjection.length];
250e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        String queryAs;
251e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        int fullSize = (mFullQueryTerms != null) ? mFullQueryTerms.size() : 0;
252e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (fullSize > 0) {
253e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            String realQuery = "'";
254e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            for (int i = 0; i < fullSize; i++) {
255f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira                realQuery+= mFullQueryTerms.get(i);
256f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira                if (i < fullSize -1) {
257f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira                    realQuery += QUERY_TOKEN_SEPARATOR;
258e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                }
259e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            }
260e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            queryAs = realQuery + " ' || query AS " + SearchManager.SUGGEST_COLUMN_QUERY;
261e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        } else {
262e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            queryAs = "query AS " + SearchManager.SUGGEST_COLUMN_QUERY;
263e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
264e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        for (int i = 0; i < mSuggestionProjection.length; i++) {
265e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            newProjection[i] = mSuggestionProjection[i];
266e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
267e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        // Assumes that newProjection[length-2] is the query field.
268e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        newProjection[mSuggestionProjection.length - 2] = queryAs;
269e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        return newProjection;
270e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
271e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
272e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    /**
273e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * Set the other query terms to be included in the user's query.
274e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * These are in addition to what is being looked up for suggestions.
275e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * @param terms
276e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     */
277e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    public void setFullQueryTerms(ArrayList<String> terms) {
278e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        mFullQueryTerms = terms;
279e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
280e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
281f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira    /**
282f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * This method is provided for use by the ContentResolver. Do not override,
283f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     * or directly call from your own code.
284f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira     */
285e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    // TODO: Confirm no injection attacks here, or rewrite.
286e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    @Override
287e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
288e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            String sortOrder) {
289e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
290e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
291e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        // special case for actual suggestions (from search manager)
292e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        String suggestSelection;
293e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        String[] myArgs;
294e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        if (TextUtils.isEmpty(selectionArgs[0])) {
295e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            suggestSelection = null;
296e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            myArgs = null;
297e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        } else {
298e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            String like = "%" + selectionArgs[0] + "%";
299f8fcb800aa4562734c6a4f165d92d09e78a8c484Mindy Pereira            myArgs = new String[] { like };
300e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal            suggestSelection = mSuggestSuggestionClause;
301e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        }
302e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        // Suggestions are always performed with the default sort order
303e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        // Add this to the query:
304e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        // "select 'real_query' as SearchManager.SUGGEST_COLUMN_QUERY.
305e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        // rest of query
306e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        // real query will then show up in the suggestion
307e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        Cursor c = db.query(sSuggestions, createProjection(selectionArgs), suggestSelection, myArgs,
308e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal                null, null, ORDER_BY, null);
309e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        c.setNotificationUri(getContext().getContentResolver(), uri);
310e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        return c;
311e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
312e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal
313e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    /**
314e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * This method is provided for use by the ContentResolver.  Do not override, or directly
315e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     * call from your own code.
316e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal     */
317e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    @Override
318e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
319e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal        throw new UnsupportedOperationException("Not implemented");
320e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal    }
321e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwal}