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 androidx.core.content;
18
19import static android.os.Build.VERSION.SDK_INT;
20
21import android.content.ContentResolver;
22import android.database.Cursor;
23import android.net.Uri;
24
25import androidx.core.os.CancellationSignal;
26import androidx.core.os.OperationCanceledException;
27
28/**
29 * Helper for accessing features in {@link android.content.ContentResolver} in a backwards
30 * compatible fashion.
31 */
32public final class ContentResolverCompat {
33    private ContentResolverCompat() {
34        /* Hide constructor */
35    }
36
37    /**
38     * Query the given URI, returning a {@link Cursor} over the result set
39     * with optional support for cancellation.
40     * <p>
41     * For best performance, the caller should follow these guidelines:
42     * <ul>
43     * <li>Provide an explicit projection, to prevent
44     * reading data from storage that aren't going to be used.</li>
45     * <li>Use question mark parameter markers such as 'phone=?' instead of
46     * explicit values in the {@code selection} parameter, so that queries
47     * that differ only by those values will be recognized as the same
48     * for caching purposes.</li>
49     * </ul>
50     * </p>
51     *
52     * @param uri The URI, using the content:// scheme, for the content to
53     *         retrieve.
54     * @param projection A list of which columns to return. Passing null will
55     *         return all columns, which is inefficient.
56     * @param selection A filter declaring which rows to return, formatted as an
57     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
58     *         return all rows for the given URI.
59     * @param selectionArgs You may include ?s in selection, which will be
60     *         replaced by the values from selectionArgs, in the order that they
61     *         appear in the selection. The values will be bound as Strings.
62     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
63     *         clause (excluding the ORDER BY itself). Passing null will use the
64     *         default sort order, which may be unordered.
65     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
66     * If the operation is canceled, then {@link OperationCanceledException} will be thrown
67     * when the query is executed.
68     * @return A Cursor object, which is positioned before the first entry, or null
69     * @see Cursor
70     */
71    public static Cursor query(ContentResolver resolver,
72            Uri uri, String[] projection, String selection, String[] selectionArgs,
73            String sortOrder, CancellationSignal cancellationSignal) {
74        if (SDK_INT >= 16) {
75            try {
76                final android.os.CancellationSignal cancellationSignalObj =
77                        (android.os.CancellationSignal)
78                                (cancellationSignal != null
79                                        ? cancellationSignal.getCancellationSignalObject()
80                                        : null);
81                return resolver.query(uri, projection, selection, selectionArgs, sortOrder,
82                        cancellationSignalObj);
83            } catch (Exception e) {
84                if (e instanceof android.os.OperationCanceledException) {
85                    // query() can throw a framework OperationCanceledException if it has been
86                    // canceled. We catch that and throw the support version instead.
87                    throw new OperationCanceledException();
88                } else {
89                    // If it's not a framework OperationCanceledException, re-throw the exception
90                    throw e;
91                }
92            }
93        } else {
94            // Note that the cancellation signal cannot cancel the query in progress
95            // prior to Jellybean so we cancel it preemptively here if needed.
96            if (cancellationSignal != null) {
97                cancellationSignal.throwIfCanceled();
98            }
99            return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
100        }
101    }
102}
103