• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package com.android.settings.slices;
18 
19 import static com.android.settings.slices.SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX;
20 
21 import android.content.Context;
22 import android.database.Cursor;
23 import android.database.sqlite.SQLiteDatabase;
24 import android.net.Uri;
25 import android.os.Binder;
26 import android.util.Pair;
27 
28 import androidx.slice.Slice;
29 
30 import com.android.settings.overlay.FeatureFactory;
31 import com.android.settings.slices.SlicesDatabaseHelper.IndexColumns;
32 
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /**
37  * Class used to map a {@link Uri} from {@link SettingsSliceProvider} to a Slice.
38  */
39 public class SlicesDatabaseAccessor {
40 
41     public static final String[] SELECT_COLUMNS_ALL = {
42             IndexColumns.KEY,
43             IndexColumns.TITLE,
44             IndexColumns.SUMMARY,
45             IndexColumns.SCREENTITLE,
46             IndexColumns.KEYWORDS,
47             IndexColumns.ICON_RESOURCE,
48             IndexColumns.FRAGMENT,
49             IndexColumns.CONTROLLER,
50             IndexColumns.PLATFORM_SLICE,
51             IndexColumns.SLICE_TYPE,
52             IndexColumns.UNAVAILABLE_SLICE_SUBTITLE,
53     };
54 
55     // Cursor value for boolean true
56     private final int TRUE = 1;
57 
58     private final Context mContext;
59     private final SlicesDatabaseHelper mHelper;
60 
SlicesDatabaseAccessor(Context context)61     public SlicesDatabaseAccessor(Context context) {
62         mContext = context;
63         mHelper = SlicesDatabaseHelper.getInstance(mContext);
64     }
65 
66     /**
67      * Query the slices database and return a {@link SliceData} object corresponding to the row
68      * matching the key provided by the {@param uri}. Additionally adds the {@param uri} to the
69      * {@link SliceData} object so the {@link Slice} can bind to the {@link Uri}.
70      * Used when building a {@link Slice}.
71      */
getSliceDataFromUri(Uri uri)72     public SliceData getSliceDataFromUri(Uri uri) {
73         Pair<Boolean, String> pathData = SliceBuilderUtils.getPathData(uri);
74         if (pathData == null) {
75             throw new IllegalStateException("Invalid Slices uri: " + uri);
76         }
77         try (Cursor cursor = getIndexedSliceData(pathData.second /* key */)) {
78             return buildSliceData(cursor, uri, pathData.first /* isIntentOnly */);
79         }
80     }
81 
82     /**
83      * Query the slices database and return a {@link SliceData} object corresponding to the row
84      * matching the {@param key}.
85      * Used when handling the action of the {@link Slice}.
86      */
getSliceDataFromKey(String key)87     public SliceData getSliceDataFromKey(String key) {
88         try (Cursor cursor = getIndexedSliceData(key)) {
89             return buildSliceData(cursor, null /* uri */, false /* isIntentOnly */);
90         }
91     }
92 
93     /**
94      * @return a list of keys in the Slices database matching on {@param isPlatformSlice}.
95      */
getSliceKeys(boolean isPlatformSlice)96     public List<String> getSliceKeys(boolean isPlatformSlice) {
97         verifyIndexing();
98         final String whereClause;
99 
100         if (isPlatformSlice) {
101             whereClause = IndexColumns.PLATFORM_SLICE + " = 1";
102         } else {
103             whereClause = IndexColumns.PLATFORM_SLICE + " = 0";
104         }
105 
106         final SQLiteDatabase database = mHelper.getReadableDatabase();
107         final String[] columns = new String[]{IndexColumns.KEY};
108         final List<String> keys = new ArrayList<>();
109 
110         try (final Cursor resultCursor = database.query(TABLE_SLICES_INDEX, columns, whereClause,
111                 null /* selection */, null /* groupBy */, null /* having */, null /* orderBy */)) {
112             if (!resultCursor.moveToFirst()) {
113                 return keys;
114             }
115 
116             do {
117                 keys.add(resultCursor.getString(0 /* key index */));
118             } while (resultCursor.moveToNext());
119         }
120 
121         return keys;
122     }
123 
getIndexedSliceData(String path)124     private Cursor getIndexedSliceData(String path) {
125         verifyIndexing();
126 
127         final String whereClause = buildKeyMatchWhereClause();
128         final SQLiteDatabase database = mHelper.getReadableDatabase();
129         final String[] selection = new String[]{path};
130         final Cursor resultCursor = database.query(TABLE_SLICES_INDEX, SELECT_COLUMNS_ALL,
131                 whereClause, selection, null /* groupBy */, null /* having */, null /* orderBy */);
132 
133         int numResults = resultCursor.getCount();
134 
135         if (numResults == 0) {
136             throw new IllegalStateException("Invalid Slices key from path: " + path);
137         }
138 
139         if (numResults > 1) {
140             throw new IllegalStateException(
141                     "Should not match more than 1 slice with path: " + path);
142         }
143 
144         resultCursor.moveToFirst();
145         return resultCursor;
146     }
147 
buildKeyMatchWhereClause()148     private String buildKeyMatchWhereClause() {
149         return new StringBuilder(IndexColumns.KEY)
150                 .append(" = ?")
151                 .toString();
152     }
153 
buildSliceData(Cursor cursor, Uri uri, boolean isIntentOnly)154     private SliceData buildSliceData(Cursor cursor, Uri uri, boolean isIntentOnly) {
155         final String key = cursor.getString(cursor.getColumnIndex(IndexColumns.KEY));
156         final String title = cursor.getString(cursor.getColumnIndex(IndexColumns.TITLE));
157         final String summary = cursor.getString(cursor.getColumnIndex(IndexColumns.SUMMARY));
158         final String screenTitle = cursor.getString(
159                 cursor.getColumnIndex(IndexColumns.SCREENTITLE));
160         final String keywords = cursor.getString(cursor.getColumnIndex(IndexColumns.KEYWORDS));
161         final int iconResource = cursor.getInt(cursor.getColumnIndex(IndexColumns.ICON_RESOURCE));
162         final String fragmentClassName = cursor.getString(
163                 cursor.getColumnIndex(IndexColumns.FRAGMENT));
164         final String controllerClassName = cursor.getString(
165                 cursor.getColumnIndex(IndexColumns.CONTROLLER));
166         final boolean isPlatformDefined = cursor.getInt(
167                 cursor.getColumnIndex(IndexColumns.PLATFORM_SLICE)) == TRUE;
168         int sliceType = cursor.getInt(
169                 cursor.getColumnIndex(IndexColumns.SLICE_TYPE));
170         final String unavailableSliceSubtitle = cursor.getString(
171                 cursor.getColumnIndex(IndexColumns.UNAVAILABLE_SLICE_SUBTITLE));
172 
173         if (isIntentOnly) {
174             sliceType = SliceData.SliceType.INTENT;
175         }
176 
177         return new SliceData.Builder()
178                 .setKey(key)
179                 .setTitle(title)
180                 .setSummary(summary)
181                 .setScreenTitle(screenTitle)
182                 .setKeywords(keywords)
183                 .setIcon(iconResource)
184                 .setFragmentName(fragmentClassName)
185                 .setPreferenceControllerClassName(controllerClassName)
186                 .setUri(uri)
187                 .setPlatformDefined(isPlatformDefined)
188                 .setSliceType(sliceType)
189                 .setUnavailableSliceSubtitle(unavailableSliceSubtitle)
190                 .build();
191     }
192 
verifyIndexing()193     private void verifyIndexing() {
194         final long uidToken = Binder.clearCallingIdentity();
195         try {
196             FeatureFactory.getFactory(
197                     mContext).getSlicesFeatureProvider().indexSliceData(mContext);
198         } finally {
199             Binder.restoreCallingIdentity(uidToken);
200         }
201     }
202 }