blob: ce64de957e3e0814cd97641e10d4fda665861424 [file] [log] [blame]
/*
* 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.search;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.BadParcelableException;
import android.text.TextUtils;
import android.util.Log;
import com.android.settings.dashboard.SiteMapManager;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.android.settings.search.DatabaseResultLoader.BASE_RANKS;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_CLASS_NAME;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ICON;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ID;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_KEY;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD_TYPE;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_SCREEN_TITLE;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_SUMMARY_ON;
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_TITLE;
import static com.android.settings.search.SearchResult.TOP_RANK;
/**
* Controller to Build search results from {@link Cursor} Objects.
*
* Each converted {@link Cursor} has the following fields:
* - String Title
* - String Summary
* - int rank
* - {@link Drawable} icon
* - {@link ResultPayload} payload
*/
public class CursorToSearchResultConverter {
private static final String TAG = "CursorConverter";
private final Context mContext;
private final int LONG_TITLE_LENGTH = 20;
private static final String[] whiteList = {
"main_toggle_wifi",
"main_toggle_bluetooth",
"main_toggle_bluetooth_obsolete",
"toggle_airplane",
"tether_settings",
"battery_saver",
"toggle_nfc",
"restrict_background",
"data_usage_enable",
"button_roaming_key",
};
private static final Set<String> prioritySettings = new HashSet(Arrays.asList(whiteList));
public CursorToSearchResultConverter(Context context) {
mContext = context;
}
public Set<SearchResult> convertCursor(SiteMapManager sitemapManager,
Cursor cursorResults, int baseRank) {
if (cursorResults == null) {
return null;
}
final Map<String, Context> contextMap = new HashMap<>();
final Set<SearchResult> results = new HashSet<>();
while (cursorResults.moveToNext()) {
SearchResult result = buildSingleSearchResultFromCursor(sitemapManager,
contextMap, cursorResults, baseRank);
if (result != null) {
results.add(result);
}
}
return results;
}
public static ResultPayload getUnmarshalledPayload(byte[] marshalledPayload,
int payloadType) {
try {
switch (payloadType) {
case ResultPayload.PayloadType.INTENT:
return ResultPayloadUtils.unmarshall(marshalledPayload,
ResultPayload.CREATOR);
case ResultPayload.PayloadType.INLINE_SWITCH:
return ResultPayloadUtils.unmarshall(marshalledPayload,
InlineSwitchPayload.CREATOR);
case ResultPayload.PayloadType.INLINE_LIST:
return ResultPayloadUtils.unmarshall(marshalledPayload,
InlineListPayload.CREATOR);
}
} catch (BadParcelableException e) {
Log.w(TAG, "Error creating parcelable: " + e);
}
return null;
}
private SearchResult buildSingleSearchResultFromCursor(SiteMapManager sitemapManager,
Map<String, Context> contextMap, Cursor cursor, int baseRank) {
final int docId = cursor.getInt(COLUMN_INDEX_ID);
final String pkgName = cursor.getString(COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
final String title = cursor.getString(COLUMN_INDEX_TITLE);
final String summaryOn = cursor.getString(COLUMN_INDEX_SUMMARY_ON);
final String className = cursor.getString(COLUMN_INDEX_CLASS_NAME);
final String key = cursor.getString(COLUMN_INDEX_KEY);
final String iconResStr = cursor.getString(COLUMN_INDEX_ICON);
final int payloadType = cursor.getInt(COLUMN_INDEX_PAYLOAD_TYPE);
final byte[] marshalledPayload = cursor.getBlob(COLUMN_INDEX_PAYLOAD);
final ResultPayload payload = getUnmarshalledPayload(marshalledPayload, payloadType);
final List<String> breadcrumbs = getBreadcrumbs(sitemapManager, cursor);
final int rank = getRank(title, baseRank, key);
final SearchResult.Builder builder = new SearchResult.Builder()
.setStableId(docId)
.setTitle(title)
.setSummary(summaryOn)
.addBreadcrumbs(breadcrumbs)
.setRank(rank)
.setIcon(getIconForPackage(contextMap, pkgName, className, iconResStr))
.setPayload(payload);
return builder.build();
}
private Drawable getIconForPackage(Map<String, Context> contextMap, String pkgName,
String className, String iconResStr) {
final int iconId = TextUtils.isEmpty(iconResStr)
? 0 : Integer.parseInt(iconResStr);
Drawable icon;
Context packageContext;
if (iconId == 0) {
icon = null;
} else {
if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(pkgName)) {
packageContext = contextMap.get(pkgName);
if (packageContext == null) {
try {
packageContext = mContext.createPackageContext(pkgName, 0);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Cannot create Context for package: " + pkgName);
return null;
}
contextMap.put(pkgName, packageContext);
}
} else {
packageContext = mContext;
}
try {
icon = packageContext.getDrawable(iconId);
} catch (Resources.NotFoundException nfe) {
icon = null;
}
}
return icon;
}
private List<String> getBreadcrumbs(SiteMapManager siteMapManager, Cursor cursor) {
final String screenTitle = cursor.getString(COLUMN_INDEX_SCREEN_TITLE);
final String screenClass = cursor.getString(COLUMN_INDEX_CLASS_NAME);
return siteMapManager == null ? null : siteMapManager.buildBreadCrumb(mContext, screenClass,
screenTitle);
}
/** Uses the breadcrumbs to determine the offset to the base rank.
* There are three checks
* A) If the result is prioritized and the highest base level
* B) If the query matches the highest level menu title
* C) If the query is longer than 20
*
* If the query matches A, set it to TOP_RANK
* If the query matches B, the offset is 0.
* If the query matches C, the offset is 1
* @param title of the result.
* @param baseRank of the result. Lower if it's a better result.
* @return
*/
private int getRank(String title, int baseRank, String key) {
// The result can only be prioritized if it is a top ranked result.
if (prioritySettings.contains(key) && baseRank < BASE_RANKS[1]) {
return TOP_RANK;
}
if (title.length() > LONG_TITLE_LENGTH) {
return baseRank + 1;
}
return baseRank;
}
}