• 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.intelligence.search.query;
18 
19 import static com.android.settings.intelligence.search.query.DatabaseResultTask.BASE_RANKS;
20 import static com.android.settings.intelligence.search.SearchResult.TOP_RANK;
21 
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.content.res.Resources;
25 import android.database.Cursor;
26 import android.graphics.drawable.Drawable;
27 import android.os.BadParcelableException;
28 import android.text.TextUtils;
29 import android.util.Log;
30 
31 import com.android.settings.intelligence.search.ResultPayload;
32 import com.android.settings.intelligence.search.ResultPayloadUtils;
33 import com.android.settings.intelligence.search.SearchResult;
34 import com.android.settings.intelligence.search.indexing.IndexDatabaseHelper;
35 import com.android.settings.intelligence.search.sitemap.SiteMapManager;
36 
37 import java.util.Arrays;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Set;
43 
44 /**
45  * Controller to Build search results from {@link Cursor} Objects.
46  *
47  * Each converted {@link Cursor} has the following fields:
48  * - String Title
49  * - String Summary
50  * - int rank
51  * - {@link Drawable} icon
52  * - {@link ResultPayload} payload
53  */
54 public class CursorToSearchResultConverter {
55 
56     private static final String TAG = "CursorConverter";
57 
58     private final Context mContext;
59 
60     private final int LONG_TITLE_LENGTH = 20;
61 
62     private static final String[] whiteList = {
63             "main_toggle_wifi",
64             "main_toggle_bluetooth",
65             "main_toggle_bluetooth_obsolete",
66             "toggle_airplane",
67             "tether_settings",
68             "battery_saver",
69             "toggle_nfc",
70             "restrict_background",
71             "data_usage_enable",
72             "button_roaming_key",
73     };
74     private static final Set<String> prioritySettings = new HashSet(Arrays.asList(whiteList));
75 
76 
CursorToSearchResultConverter(Context context)77     public CursorToSearchResultConverter(Context context) {
78         mContext = context;
79     }
80 
convertCursor(Cursor cursorResults, int baseRank, SiteMapManager siteMapManager)81     public Set<SearchResult> convertCursor(Cursor cursorResults, int baseRank,
82             SiteMapManager siteMapManager) {
83         if (cursorResults == null) {
84             return null;
85         }
86         final Map<String, Context> contextMap = new HashMap<>();
87         final Set<SearchResult> results = new HashSet<>();
88 
89         while (cursorResults.moveToNext()) {
90             SearchResult result = buildSingleSearchResultFromCursor(siteMapManager,
91                     contextMap, cursorResults, baseRank);
92             if (result != null) {
93                 results.add(result);
94             }
95         }
96         return results;
97     }
98 
getUnmarshalledPayload(byte[] marshalledPayload, int payloadType)99     public static ResultPayload getUnmarshalledPayload(byte[] marshalledPayload,
100             int payloadType) {
101         try {
102             switch (payloadType) {
103                 case ResultPayload.PayloadType.INTENT:
104                     return ResultPayloadUtils.unmarshall(marshalledPayload,
105                             ResultPayload.CREATOR);
106                 // TODO REFACTOR (b/62807132) Re-add inline
107 //                case ResultPayload.PayloadType.INLINE_SWITCH:
108 //                    return ResultPayloadUtils.unmarshall(marshalledPayload,
109 //                            InlineSwitchPayload.CREATOR);
110 //                case ResultPayload.PayloadType.INLINE_LIST:
111 //                    return ResultPayloadUtils.unmarshall(marshalledPayload,
112 //                            InlineListPayload.CREATOR);
113             }
114         } catch (BadParcelableException e) {
115             Log.w(TAG, "Error creating parcelable: " + e);
116         }
117         return null;
118     }
119 
buildSingleSearchResultFromCursor(SiteMapManager siteMapManager, Map<String, Context> contextMap, Cursor cursor, int baseRank)120     private SearchResult buildSingleSearchResultFromCursor(SiteMapManager siteMapManager,
121             Map<String, Context> contextMap, Cursor cursor, int baseRank) {
122         final String pkgName = cursor.getString(cursor.getColumnIndexOrThrow(
123                 IndexDatabaseHelper.IndexColumns.DATA_PACKAGE));
124         final String title = cursor.getString(cursor.getColumnIndexOrThrow(
125                 IndexDatabaseHelper.IndexColumns.DATA_TITLE));
126         final String summaryOn = cursor.getString(cursor.getColumnIndex(
127                 IndexDatabaseHelper.IndexColumns.DATA_SUMMARY_ON));
128         final String key = cursor.getString(cursor.getColumnIndexOrThrow(
129                 IndexDatabaseHelper.IndexColumns.DATA_KEY_REF));
130         final String iconResStr = cursor.getString(cursor.getColumnIndexOrThrow(
131                 IndexDatabaseHelper.IndexColumns.ICON));
132         final int payloadType = cursor.getInt(cursor.getColumnIndexOrThrow(
133                 IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE));
134         final byte[] marshalledPayload = cursor.getBlob(cursor.getColumnIndexOrThrow(
135                 IndexDatabaseHelper.IndexColumns.PAYLOAD));
136         final ResultPayload payload = getUnmarshalledPayload(marshalledPayload, payloadType);
137 
138         final List<String> breadcrumbs = getBreadcrumbs(siteMapManager, cursor);
139         final int rank = getRank(title, baseRank, key);
140         final Drawable icon = getIconForPackage(contextMap, pkgName, iconResStr);
141 
142         final SearchResult.Builder builder = new SearchResult.Builder()
143                 .setDataKey(key)
144                 .setTitle(title)
145                 .setSummary(summaryOn)
146                 .addBreadcrumbs(breadcrumbs)
147                 .setRank(rank)
148                 .setIcon(icon)
149                 .setPayload(payload);
150         return builder.build();
151     }
152 
getIconForPackage(Map<String, Context> contextMap, String pkgName, String iconResStr)153     private Drawable getIconForPackage(Map<String, Context> contextMap, String pkgName,
154             String iconResStr) {
155         if (TextUtils.isEmpty(pkgName)) {
156             return null;
157         }
158         final int iconId = TextUtils.isEmpty(iconResStr) ? 0 : Integer.parseInt(iconResStr);
159         if (iconId == 0) {
160             return null;
161         }
162         Context packageContext = contextMap.get(pkgName);
163         if (packageContext == null) {
164             try {
165                 packageContext = mContext.createPackageContext(pkgName, 0);
166                 contextMap.put(pkgName, packageContext);
167             } catch (PackageManager.NameNotFoundException e) {
168                 Log.e(TAG, "Cannot create Context for package: " + pkgName);
169                 return null;
170             }
171         }
172         try {
173             final Drawable drawable = packageContext.getDrawable(iconId);
174             Log.d(TAG, "Returning icon, id :" + iconId);
175             return drawable;
176         } catch (Resources.NotFoundException nfe) {
177             Log.w(TAG, "Cannot get icon, pkg/id :" + pkgName + "/" + iconId);
178             return null;
179         }
180     }
181 
getBreadcrumbs(SiteMapManager siteMapManager, Cursor cursor)182     private List<String> getBreadcrumbs(SiteMapManager siteMapManager, Cursor cursor) {
183         final String screenTitle = cursor.getString(cursor.getColumnIndexOrThrow(
184                 IndexDatabaseHelper.IndexColumns.SCREEN_TITLE));
185         final String screenClass = cursor.getString(cursor.getColumnIndexOrThrow(
186                 IndexDatabaseHelper.IndexColumns.CLASS_NAME));
187         return siteMapManager == null ? null : siteMapManager.buildBreadCrumb(mContext,
188                 screenClass, screenTitle);
189     }
190 
191     /**
192      * Uses the breadcrumbs to determine the offset to the base rank.
193      * There are three checks
194      * A) If the result is prioritized and the highest base level
195      * B) If the query matches the highest level menu title
196      * C) If the query is longer than 20
197      *
198      * If the query matches A, set it to TOP_RANK
199      * If the query matches B, the offset is 0.
200      * If the query matches C, the offset is 1
201      *
202      * @param title    of the result.
203      * @param baseRank of the result. Lower if it's a better result.
204      */
getRank(String title, int baseRank, String key)205     private int getRank(String title, int baseRank, String key) {
206         // The result can only be prioritized if it is a top ranked result.
207         if (prioritySettings.contains(key) && baseRank < BASE_RANKS[1]) {
208             return TOP_RANK;
209         }
210         if (title.length() > LONG_TITLE_LENGTH) {
211             return baseRank + 1;
212         }
213         return baseRank;
214     }
215 }