Build a sitemap manager to keep track of breadcrumbs

- Have a new db to track parent-child page relation at index time.
- Make a registry class to track (in IA) which page host what type of
  sub pages.
- Make a manager class that queries the db as well as IA to compute
  breadcrumbs

Fix: 32936784
Test: RunSettingsRoboTest

Change-Id: I5f1583fae772c3d477d2ad186e111b79cc3e41aa
This commit is contained in:
Fan Zhang
2017-01-21 14:53:01 -08:00
parent 03cd212f17
commit a96b11f65d
17 changed files with 692 additions and 63 deletions

View File

@@ -0,0 +1,93 @@
/*
* 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.dashboard;
import android.util.ArrayMap;
import com.android.settings.DevelopmentSettings;
import com.android.settings.DisplaySettings;
import com.android.settings.SecuritySettings;
import com.android.settings.accounts.UserAndAccountDashboardFragment;
import com.android.settings.applications.AdvancedAppSettings;
import com.android.settings.applications.AppAndNotificationDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.inputmethod.InputAndGestureSettings;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.notification.SoundSettings;
import com.android.settings.system.SystemDashboardFragment;
import com.android.settingslib.drawer.CategoryKey;
import java.util.Map;
/**
* A registry to keep track of which page hosts which category.
* TODO: Remove DashboardFragment#getCategoryKey() and just use this registry instead.
*/
public class DashboardFragmentRegistry {
/**
* Map from parent fragment to category key. The parent fragment hosts child with
* category_key.
*/
public static final Map<String, String> PARENT_TO_CATEGORY_KEY_MAP;
/**
* Map from category_key to parent. This is a helper to look up which fragment hosts the
* category_key.
*/
public static final Map<String, String> CATEGORY_KEY_TO_PARENT_MAP;
static {
PARENT_TO_CATEGORY_KEY_MAP = new ArrayMap<>();
PARENT_TO_CATEGORY_KEY_MAP.put(
NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);
PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragment.class.getName(),
CategoryKey.CATEGORY_DEVICE);
PARENT_TO_CATEGORY_KEY_MAP.put(AppAndNotificationDashboardFragment.class.getName(),
CategoryKey.CATEGORY_APPS);
PARENT_TO_CATEGORY_KEY_MAP.put(AdvancedAppSettings.class.getName(),
CategoryKey.CATEGORY_APPS_DEFAULT);
PARENT_TO_CATEGORY_KEY_MAP.put(DisplaySettings.class.getName(),
CategoryKey.CATEGORY_DISPLAY);
PARENT_TO_CATEGORY_KEY_MAP.put(SoundSettings.class.getName(),
CategoryKey.CATEGORY_SOUND);
PARENT_TO_CATEGORY_KEY_MAP.put(StorageDashboardFragment.class.getName(),
CategoryKey.CATEGORY_STORAGE);
PARENT_TO_CATEGORY_KEY_MAP.put(SecuritySettings.class.getName(),
CategoryKey.CATEGORY_SECURITY);
PARENT_TO_CATEGORY_KEY_MAP.put(UserAndAccountDashboardFragment.class.getName(),
CategoryKey.CATEGORY_ACCOUNT);
PARENT_TO_CATEGORY_KEY_MAP.put(UserAndAccountDashboardFragment.class.getName(),
CategoryKey.CATEGORY_ACCOUNT);
PARENT_TO_CATEGORY_KEY_MAP.put(
SystemDashboardFragment.class.getName(), CategoryKey.CATEGORY_SYSTEM);
PARENT_TO_CATEGORY_KEY_MAP.put(
InputAndGestureSettings.class.getName(), CategoryKey.CATEGORY_SYSTEM_INPUT);
PARENT_TO_CATEGORY_KEY_MAP.put(InputMethodAndLanguageSettings.class.getName(),
CategoryKey.CATEGORY_SYSTEM_LANGUAGE);
PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettings.class.getName(),
CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
CATEGORY_KEY_TO_PARENT_MAP = new ArrayMap<>(PARENT_TO_CATEGORY_KEY_MAP.size());
for (Map.Entry<String, String> parentToKey : PARENT_TO_CATEGORY_KEY_MAP.entrySet()) {
CATEGORY_KEY_TO_PARENT_MAP.put(parentToKey.getValue(), parentToKey.getKey());
}
}
}

View File

@@ -0,0 +1,212 @@
/*
* 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.dashboard;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Log;
import com.android.settings.SettingsActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.search.IndexDatabaseHelper.IndexColumns;
import com.android.settings.search.IndexDatabaseHelper.SiteMapColumns;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.android.settings.dashboard.DashboardFragmentRegistry.CATEGORY_KEY_TO_PARENT_MAP;
/**
* A manager class that maintains a "site map" and look up breadcrumb for a certain page on demand.
* <p/>
* The methods on this class can only be called on a background thread.
*/
public class SiteMapManager {
private static final String TAG = "SiteMapManager";
private static final boolean DEBUG_TIMING = false;
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public static final String[] SITE_MAP_COLUMNS = {
SiteMapColumns.PARENT_CLASS,
SiteMapColumns.PARENT_TITLE,
SiteMapColumns.CHILD_CLASS,
SiteMapColumns.CHILD_TITLE
};
private static final String[] CLASS_TO_SCREEN_TITLE_COLUMNS = {
IndexColumns.CLASS_NAME,
IndexColumns.SCREEN_TITLE,
};
private final List<SiteMapPair> mPairs = new ArrayList<>();
private boolean mInitialized;
/**
* Given a fragment class name and its screen title, build a breadcrumb from Settings root to
* this screen.
* <p/>
* Not all screens have a full breadcrumb path leading up to root, it's because either some
* page in the breadcrumb path is not indexed, or it's only reachable via search.
*/
@WorkerThread
public synchronized List<String> buildBreadCrumb(Context context, String clazz,
String screenTitle) {
init(context);
final long startTime = System.currentTimeMillis();
final List<String> breadcrumbs = new ArrayList<>();
if (!mInitialized) {
Log.w(TAG, "SiteMap is not initialized yet, skipping");
return breadcrumbs;
}
breadcrumbs.add(screenTitle);
String currentClass = clazz;
String currentTitle = screenTitle;
// Look up current page's parent, if found add it to breadcrumb string list, and repeat.
while (true) {
final SiteMapPair pair = lookUpParent(currentClass, currentTitle);
if (pair == null) {
if (DEBUG_TIMING) {
Log.d(TAG, "BreadCrumb timing: " + (System.currentTimeMillis() - startTime));
}
return breadcrumbs;
}
breadcrumbs.add(0, pair.parentTitle);
currentClass = pair.parentClass;
currentTitle = pair.parentTitle;
}
}
/**
* Initialize a list of {@link SiteMapPair}s. Each pair knows about a single parent-child
* page relationship.
*
* We get the knowledge of such mPairs from 2 sources:
* 1. Static indexing time: we know which page(s) a parent can open by parsing its pref xml.
* 2. IA: We know from {@link DashboardFeatureProvider} which page can be dynamically
* injected to where.
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@WorkerThread
synchronized void init(Context context) {
if (mInitialized) {
// Make sure only init once.
return;
}
final long startTime = System.currentTimeMillis();
// First load site map from static index table.
final Context appContext = context.getApplicationContext();
final SQLiteDatabase db = IndexDatabaseHelper.getInstance(appContext).getReadableDatabase();
Cursor sitemap = db.query(IndexDatabaseHelper.Tables.TABLE_SITE_MAP, SITE_MAP_COLUMNS, null,
null, null, null, null);
while (sitemap.moveToNext()) {
final SiteMapPair pair = new SiteMapPair(
sitemap.getString(sitemap.getColumnIndex(SiteMapColumns.PARENT_CLASS)),
sitemap.getString(sitemap.getColumnIndex(SiteMapColumns.PARENT_TITLE)),
sitemap.getString(sitemap.getColumnIndex(SiteMapColumns.CHILD_CLASS)),
sitemap.getString(sitemap.getColumnIndex(SiteMapColumns.CHILD_TITLE)));
mPairs.add(pair);
}
sitemap.close();
// Then prepare a local map that contains class name -> screen title mapping. This is needed
// to figure out the display name for any fragment if it's injected dynamically through IA.
final Map<String, String> classToTitleMap = new HashMap<>();
final Cursor titleQuery = db.query(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX,
CLASS_TO_SCREEN_TITLE_COLUMNS, null, null, null, null, null);
while (titleQuery.moveToNext()) {
classToTitleMap.put(
titleQuery.getString(titleQuery.getColumnIndex(IndexColumns.CLASS_NAME)),
titleQuery.getString(titleQuery.getColumnIndex(IndexColumns.SCREEN_TITLE)));
}
titleQuery.close();
// Loop through all IA categories and pages and build additional SiteMapPairs
List<DashboardCategory> categories = FeatureFactory.getFactory(context)
.getDashboardFeatureProvider(context).getAllCategories();
for (DashboardCategory category : categories) {
// Find the category key first.
final String parentClass = CATEGORY_KEY_TO_PARENT_MAP.get(category.key);
if (parentClass == null) {
continue;
}
// Use the key to look up parent (which page hosts this key)
final String parentName = classToTitleMap.get(parentClass);
if (parentName == null) {
continue;
}
// Build parent-child mPairs for all children listed under this key.
for (Tile tile : category.tiles) {
final String childTitle = tile.title.toString();
String childClass = null;
if (tile.metaData != null) {
childClass = tile.metaData.getString(
SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
}
if (childClass == null) {
continue;
}
mPairs.add(new SiteMapPair(parentClass, parentName, childClass, childTitle));
}
}
// Done.
mInitialized = true;
if (DEBUG_TIMING) {
Log.d(TAG, "Init timing: " + (System.currentTimeMillis() - startTime));
}
}
@WorkerThread
private SiteMapPair lookUpParent(String clazz, String title) {
for (SiteMapPair pair : mPairs) {
if (TextUtils.equals(pair.childClass, clazz)
&& TextUtils.equals(title, pair.childTitle)) {
return pair;
}
}
return null;
}
/**
* Data model for a parent-child page pair.
*/
private static class SiteMapPair {
public final String parentClass;
public final String parentTitle;
public final String childClass;
public final String childTitle;
public SiteMapPair(String parentClass, String parentTitle, String childClass,
String childTitle) {
this.parentClass = parentClass;
this.parentTitle = parentTitle;
this.childClass = childClass;
this.childTitle = childTitle;
}
}
}

View File

@@ -28,12 +28,13 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "IndexDatabaseHelper";
private static final String DATABASE_NAME = "search_index.db";
private static final int DATABASE_VERSION = 116;
private static final int DATABASE_VERSION = 117;
private static final String INDEX = "index";
public interface Tables {
String TABLE_PREFS_INDEX = "prefs_index";
String TABLE_SITE_MAP = "site_map";
String TABLE_META_INDEX = "meta_index";
String TABLE_SAVED_QUERIES = "saved_queries";
}
@@ -72,6 +73,14 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
String TIME_STAMP = "timestamp";
}
public interface SiteMapColumns {
String DOCID = "docid";
String PARENT_CLASS = "parent_class";
String CHILD_CLASS = "child_class";
String PARENT_TITLE = "parent_title";
String CHILD_TITLE = "child_title";
}
private static final String CREATE_INDEX_TABLE =
"CREATE VIRTUAL TABLE " + Tables.TABLE_PREFS_INDEX + " USING fts4" +
"(" +
@@ -132,6 +141,17 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
SavedQueriesColumns.TIME_STAMP + " INTEGER" +
")";
private static final String CREATE_SITE_MAP_TABLE =
"CREATE VIRTUAL TABLE " + Tables.TABLE_SITE_MAP + " USING fts4" +
"(" +
SiteMapColumns.PARENT_CLASS +
", " +
SiteMapColumns.CHILD_CLASS +
", " +
SiteMapColumns.PARENT_TITLE +
", " +
SiteMapColumns.CHILD_TITLE +
")";
private static final String INSERT_BUILD_VERSION =
"INSERT INTO " + Tables.TABLE_META_INDEX +
" VALUES ('" + Build.VERSION.INCREMENTAL + "');";
@@ -164,6 +184,7 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
db.execSQL(CREATE_INDEX_TABLE);
db.execSQL(CREATE_META_TABLE);
db.execSQL(CREATE_SAVED_QUERIES_TABLE);
db.execSQL(CREATE_SITE_MAP_TABLE);
db.execSQL(INSERT_BUILD_VERSION);
Log.i(TAG, "Bootstrapped database");
}
@@ -241,5 +262,6 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_META_INDEX);
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_PREFS_INDEX);
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SAVED_QUERIES);
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SITE_MAP);
}
}

View File

@@ -28,8 +28,10 @@ import android.os.BadParcelableException;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.dashboard.SiteMapManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -39,18 +41,20 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_ID;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_SCREEN_TITLE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_TITLE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_SUMMARY_ON;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_CLASS_NAME;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_ICON;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_ID;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
import static com.android.settings.search2.DatabaseResultLoader
.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS;
import static com.android.settings.search2.DatabaseResultLoader
.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_KEY;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD_TYPE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD_TYPE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_SCREEN_TITLE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_SUMMARY_ON;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_TITLE;
/**
* Controller to Build search results from {@link Cursor} Objects.
@@ -78,7 +82,8 @@ class CursorToSearchResultConverter {
mQueryText = queryText;
}
public List<SearchResult> convertCursor(Cursor cursorResults, int baseRank) {
public List<SearchResult> convertCursor(SiteMapManager sitemapManager,
Cursor cursorResults, int baseRank) {
if (cursorResults == null) {
return null;
}
@@ -86,8 +91,8 @@ class CursorToSearchResultConverter {
final List<SearchResult> results = new ArrayList<>();
while (cursorResults.moveToNext()) {
SearchResult result = buildSingleSearchResultFromCursor(contextMap, cursorResults,
baseRank);
SearchResult result = buildSingleSearchResultFromCursor(sitemapManager,
contextMap, cursorResults, baseRank);
if (result != null) {
results.add(result);
}
@@ -96,8 +101,8 @@ class CursorToSearchResultConverter {
return results;
}
private SearchResult buildSingleSearchResultFromCursor(Map<String, Context> contextMap,
Cursor cursor, int baseRank) {
private SearchResult buildSingleSearchResultFromCursor(SiteMapManager sitemapManager,
Map<String, Context> contextMap, Cursor cursor, int baseRank) {
final String docId = cursor.getString(COLUMN_INDEX_ID);
/* Make sure that this result has not yet been added as a result. Checking the docID
covers the case of multiple queries matching the same row, but we need to also to check
@@ -128,7 +133,7 @@ class CursorToSearchResultConverter {
return null;
}
final List<String> breadcrumbs = getBreadcrumbs(cursor);
final List<String> breadcrumbs = getBreadcrumbs(sitemapManager, cursor);
final int rank = getRank(breadcrumbs, baseRank);
final SearchResult.Builder builder = new SearchResult.Builder();
@@ -210,12 +215,11 @@ class CursorToSearchResultConverter {
return null;
}
private List<String> getBreadcrumbs(Cursor cursor) {
final List<String> breadcrumbs = new ArrayList<>();
private List<String> getBreadcrumbs(SiteMapManager siteMapManager, Cursor cursor) {
final String screenTitle = cursor.getString(COLUMN_INDEX_SCREEN_TITLE);
if (!TextUtils.isEmpty(screenTitle)) {
breadcrumbs.add(screenTitle);
}
final String screenClass = cursor.getString(COLUMN_INDEX_CLASS_NAME);
final List<String> breadcrumbs = siteMapManager.buildBreadCrumb(mContext, screenClass,
screenTitle);
return breadcrumbs;
}

View File

@@ -34,7 +34,6 @@ import android.provider.SearchIndexableData;
import android.provider.SearchIndexableResource;
import android.provider.SearchIndexablesContract;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -582,12 +581,13 @@ public class DatabaseIndexingManager {
String title;
String summary;
String keywords;
String childFragment;
ResultPayload payload;
ArrayMap<String, PreferenceController> controllerUriMap = null;
Map<String, PreferenceController> controllerUriMap = null;
if (fragmentName != null) {
controllerUriMap = (ArrayMap) DatabaseIndexingUtils
controllerUriMap = DatabaseIndexingUtils
.getPreferenceControllerUriMap(fragmentName, context);
}
@@ -655,8 +655,10 @@ public class DatabaseIndexingManager {
}
payload = DatabaseIndexingUtils.getPayloadFromUriMap(controllerUriMap, key);
childFragment = XmlParserUtils.getDataChildFragment(context, attrs);
builder.setEntries(entries)
.setChildClassName(childFragment)
.setPayload(payload);
// Insert rows for the child nodes of PreferenceScreen
@@ -811,6 +813,18 @@ public class DatabaseIndexingManager {
values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD, row.payload);
database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
if (!TextUtils.isEmpty(row.className) && !TextUtils.isEmpty(row.childClassName)) {
ContentValues siteMapPair = new ContentValues();
final int pairDocId = Objects.hash(row.className, row.childClassName);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.DOCID, pairDocId);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.PARENT_CLASS, row.className);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.PARENT_TITLE, row.screenTitle);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.CHILD_CLASS, row.childClassName);
siteMapPair.put(IndexDatabaseHelper.SiteMapColumns.CHILD_TITLE, row.updatedTitle);
database.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_SITE_MAP, null, siteMapPair);
}
}
/**
@@ -950,6 +964,7 @@ public class DatabaseIndexingManager {
public final String normalizedSummaryOff;
public final String entries;
public final String className;
public final String childClassName;
public final String screenTitle;
public final int iconResId;
public final int rank;
@@ -973,6 +988,7 @@ public class DatabaseIndexingManager {
normalizedSummaryOff = builder.mNormalizedSummaryOff;
entries = builder.mEntries;
className = builder.mClassName;
childClassName = builder.mChildClassName;
screenTitle = builder.mScreenTitle;
iconResId = builder.mIconResId;
rank = builder.mRank;
@@ -1008,6 +1024,7 @@ public class DatabaseIndexingManager {
private String mNormalizedSummaryOff;
private String mEntries;
private String mClassName;
private String mChildClassName;
private String mScreenTitle;
private int mIconResId;
private int mRank;
@@ -1067,6 +1084,11 @@ public class DatabaseIndexingManager {
return this;
}
public Builder setChildClassName(String childClassName) {
mChildClassName = childClassName;
return this;
}
public Builder setScreenTitle(String screenTitle) {
mScreenTitle = screenTitle;
return this;

View File

@@ -105,7 +105,7 @@ public class DatabaseIndexingUtils {
* @return The Payload from the {@link PreferenceController} specified by the key, if it exists.
* Otherwise null.
*/
public static ResultPayload getPayloadFromUriMap(ArrayMap<String, PreferenceController> uriMap,
public static ResultPayload getPayloadFromUriMap(Map<String, PreferenceController> uriMap,
String key) {
if (uriMap == null) {
return null;

View File

@@ -20,6 +20,8 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.android.settings.dashboard.SiteMapManager;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.IndexDatabaseHelper;
import com.android.settings.utils.AsyncLoader;
@@ -35,11 +37,6 @@ import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS
*/
public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
private static final String LOG = "DatabaseResultLoader";
private final String mQueryText;
protected final SQLiteDatabase mDatabase;
private final CursorToSearchResultConverter mConverter;
/* These indices are used to match the columns of the this loader's SELECT statement.
These are not necessarily the same order nor similar coverage as the schema defined in
@@ -99,8 +96,15 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
*/
private static final int[] BASE_RANKS = {1, 4, 7};
private final String mQueryText;
private final SQLiteDatabase mDatabase;
private final CursorToSearchResultConverter mConverter;
private final SiteMapManager mSiteMapManager;
public DatabaseResultLoader(Context context, String queryText) {
super(context);
mSiteMapManager = FeatureFactory.getFactory(context)
.getSearchFeatureProvider().getSiteMapManager();
mDatabase = IndexDatabaseHelper.getInstance(context).getReadableDatabase();
mQueryText = cleanQuery(queryText);
mConverter = new CursorToSearchResultConverter(context, mQueryText);
@@ -144,7 +148,7 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
final Cursor resultCursor = mDatabase.query(TABLE_PREFS_INDEX, SELECT_COLUMNS, whereClause,
selection, null, null, null);
return mConverter.convertCursor(resultCursor, baseRank);
return mConverter.convertCursor(mSiteMapManager, resultCursor, baseRank);
}
@Override
@@ -155,6 +159,7 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
/**
* A generic method to make the query suitable for searching the database.
*
* @return the cleaned query string
*/
private static String cleanQuery(String query) {

View File

@@ -29,7 +29,10 @@ import android.provider.Settings;
import android.text.TextUtils;
import com.android.settings.R;
import com.android.settings.applications.ManageApplications;
import com.android.settings.applications.PackageManagerWrapper;
import com.android.settings.dashboard.SiteMapManager;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.AsyncLoader;
import java.util.ArrayList;
@@ -46,7 +49,8 @@ public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> {
private static final Intent LAUNCHER_PROBE = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER);
private final List<String> mBreadcrumb;
private List<String> mBreadcrumb;
private SiteMapManager mSiteMapManager;
private final String mQuery;
private final UserManager mUserManager;
private final PackageManagerWrapper mPackageManager;
@@ -55,9 +59,8 @@ public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> {
public InstalledAppResultLoader(Context context, PackageManagerWrapper pmWrapper,
String query) {
super(context);
mBreadcrumb = new ArrayList<>();
mBreadcrumb.add(context.getString(R.string.app_and_notification_dashboard_title));
mBreadcrumb.add(context.getString(R.string.applications_settings));
mSiteMapManager = FeatureFactory.getFactory(context)
.getSearchFeatureProvider().getSiteMapManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mPackageManager = pmWrapper;
mQuery = query;
@@ -92,7 +95,7 @@ public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> {
builder.addIcon(info.loadIcon(pm))
.addTitle(info.loadLabel(pm))
.addRank(wordDiff)
.addBreadcrumbs(mBreadcrumb)
.addBreadcrumbs(getBreadCrumb())
.addPayload(new IntentPayload(intent));
results.add(builder.build());
}
@@ -162,4 +165,14 @@ public class InstalledAppResultLoader extends AsyncLoader<List<SearchResult>> {
// to infinity.
return valueText.length - queryTokens.length;
}
private List<String> getBreadCrumb() {
if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
final Context context = getContext();
mBreadcrumb = mSiteMapManager.buildBreadCrumb(
context, ManageApplications.class.getName(),
context.getString(R.string.applications_settings));
}
return mBreadcrumb;
}
}

View File

@@ -19,6 +19,8 @@ import android.app.Activity;
import android.content.Context;
import android.view.Menu;
import com.android.settings.dashboard.SiteMapManager;
/**
* FeatureProvider for Settings Search
*/
@@ -57,6 +59,11 @@ public interface SearchFeatureProvider {
*/
DatabaseIndexingManager getIndexingManager(Context context);
/**
* Returns the manager for looking up breadcrumbs.
*/
SiteMapManager getSiteMapManager();
/**
* Updates the Settings indexes
*/

View File

@@ -25,6 +25,7 @@ import android.view.MenuItem;
import com.android.settings.R;
import com.android.settings.applications.PackageManagerWrapperImpl;
import com.android.settings.dashboard.SiteMapManager;
import com.android.settings.search.Index;
/**
@@ -35,6 +36,7 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
private static final String TAG = "SearchFeatureProvider";
private DatabaseIndexingManager mDatabaseIndexingManager;
private SiteMapManager mSiteMapManager;
@Override
public boolean isEnabled(Context context) {
@@ -86,6 +88,13 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
return mDatabaseIndexingManager;
}
public SiteMapManager getSiteMapManager() {
if (mSiteMapManager == null) {
mSiteMapManager = new SiteMapManager();
}
return mSiteMapManager;
}
@Override
public void updateIndex(Context context) {
long indexStartTime = System.currentTimeMillis();

View File

@@ -24,9 +24,6 @@ import android.util.TypedValue;
import com.android.settings.R;
import java.text.Normalizer;
import java.util.regex.Pattern;
/**
* Utility class to parse elements of XML preferences
*/
@@ -74,6 +71,14 @@ public class XmlParserUtils {
return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords);
}
/**
* Returns the fragment name if this preference launches a child fragment.
*/
public static String getDataChildFragment(Context context, AttributeSet attrs) {
return getData(context, attrs, R.styleable.Preference,
R.styleable.Preference_android_fragment);
}
private static String getData(Context context, AttributeSet set, int[] attrs, int resId) {
final TypedArray sa = context.obtainStyledAttributes(set, attrs);
final TypedValue tv = sa.peekValue(resId);