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