/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.settings.slices; import static com.android.settings.slices.SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.content.Context; import android.os.Binder; import android.util.Pair; import com.android.settings.overlay.FeatureFactory; import com.android.settings.slices.SlicesDatabaseHelper.IndexColumns; import java.util.ArrayList; import java.util.List; import androidx.slice.Slice; /** * Class used to map a {@link Uri} from {@link SettingsSliceProvider} to a Slice. */ public class SlicesDatabaseAccessor { public static final String[] SELECT_COLUMNS_ALL = { IndexColumns.KEY, IndexColumns.TITLE, IndexColumns.SUMMARY, IndexColumns.SCREENTITLE, IndexColumns.KEYWORDS, IndexColumns.ICON_RESOURCE, IndexColumns.FRAGMENT, IndexColumns.CONTROLLER, IndexColumns.PLATFORM_SLICE, IndexColumns.SLICE_TYPE, }; // Cursor value for boolean true private final int TRUE = 1; private final Context mContext; private final SlicesDatabaseHelper mHelper; public SlicesDatabaseAccessor(Context context) { mContext = context; mHelper = SlicesDatabaseHelper.getInstance(mContext); } /** * Query the slices database and return a {@link SliceData} object corresponding to the row * matching the key provided by the {@param uri}. Additionally adds the {@param uri} to the * {@link SliceData} object so the {@link Slice} can bind to the {@link Uri}. * Used when building a {@link Slice}. */ public SliceData getSliceDataFromUri(Uri uri) { Pair pathData = SliceBuilderUtils.getPathData(uri); Cursor cursor = getIndexedSliceData(pathData.second /* key */); return buildSliceData(cursor, uri, pathData.first /* isIntentOnly */); } /** * Query the slices database and return a {@link SliceData} object corresponding to the row * matching the {@param key}. * Used when handling the action of the {@link Slice}. */ public SliceData getSliceDataFromKey(String key) { Cursor cursor = getIndexedSliceData(key); return buildSliceData(cursor, null /* uri */, false /* isIntentOnly */); } /** * @return a list of keys in the Slices database matching on {@param isPlatformSlice}. */ public List getSliceKeys(boolean isPlatformSlice) { verifyIndexing(); final String whereClause; if (isPlatformSlice) { whereClause = IndexColumns.PLATFORM_SLICE + " = 1"; } else { whereClause = IndexColumns.PLATFORM_SLICE + " = 0"; } final SQLiteDatabase database = mHelper.getReadableDatabase(); final String[] columns = new String[]{IndexColumns.KEY}; final List keys = new ArrayList<>(); try (final Cursor resultCursor = database.query(TABLE_SLICES_INDEX, columns, whereClause, null /* selection */, null /* groupBy */, null /* having */, null /* orderBy */)) { if (!resultCursor.moveToFirst()) { return keys; } do { keys.add(resultCursor.getString(0 /* key index */)); } while (resultCursor.moveToNext()); } return keys; } private Cursor getIndexedSliceData(String path) { verifyIndexing(); final String whereClause = buildKeyMatchWhereClause(); final SQLiteDatabase database = mHelper.getReadableDatabase(); final String[] selection = new String[]{path}; final Cursor resultCursor = database.query(TABLE_SLICES_INDEX, SELECT_COLUMNS_ALL, whereClause, selection, null /* groupBy */, null /* having */, null /* orderBy */); int numResults = resultCursor.getCount(); if (numResults == 0) { throw new IllegalStateException("Invalid Slices key from path: " + path); } if (numResults > 1) { throw new IllegalStateException( "Should not match more than 1 slice with path: " + path); } resultCursor.moveToFirst(); return resultCursor; } private String buildKeyMatchWhereClause() { return new StringBuilder(IndexColumns.KEY) .append(" = ?") .toString(); } private SliceData buildSliceData(Cursor cursor, Uri uri, boolean isIntentOnly) { final String key = cursor.getString(cursor.getColumnIndex(IndexColumns.KEY)); final String title = cursor.getString(cursor.getColumnIndex(IndexColumns.TITLE)); final String summary = cursor.getString(cursor.getColumnIndex(IndexColumns.SUMMARY)); final String screenTitle = cursor.getString( cursor.getColumnIndex(IndexColumns.SCREENTITLE)); final String keywords = cursor.getString(cursor.getColumnIndex(IndexColumns.KEYWORDS)); final int iconResource = cursor.getInt(cursor.getColumnIndex(IndexColumns.ICON_RESOURCE)); final String fragmentClassName = cursor.getString( cursor.getColumnIndex(IndexColumns.FRAGMENT)); final String controllerClassName = cursor.getString( cursor.getColumnIndex(IndexColumns.CONTROLLER)); final boolean isPlatformDefined = cursor.getInt( cursor.getColumnIndex(IndexColumns.PLATFORM_SLICE)) == TRUE; int sliceType = cursor.getInt( cursor.getColumnIndex(IndexColumns.SLICE_TYPE)); if (isIntentOnly) { sliceType = SliceData.SliceType.INTENT; } return new SliceData.Builder() .setKey(key) .setTitle(title) .setSummary(summary) .setScreenTitle(screenTitle) .setKeywords(keywords) .setIcon(iconResource) .setFragmentName(fragmentClassName) .setPreferenceControllerClassName(controllerClassName) .setUri(uri) .setPlatformDefined(isPlatformDefined) .setSliceType(sliceType) .build(); } private void verifyIndexing() { final long uidToken = Binder.clearCallingIdentity(); try { FeatureFactory.getFactory( mContext).getSlicesFeatureProvider().indexSliceData(mContext); } finally { Binder.restoreCallingIdentity(uidToken); } } }