Index Data to build Slices in Settings
The indexing is done by taking the indexable fragments from search, grabbing their XML via SearchIndexableResources, and then looking for controllers defined in preferences. For each controller found, we take the combination of the fragment providing the XML and the Preference info to create an indexable row. Buiding a Slice will be handled in a subsquent CL, but a prototype can be found here: ag/3324435 Test: robotests Bug: 67996923 Change-Id: I48668618079bcc3da55ab77b7323ee8e467073af
This commit is contained in:
@@ -25,8 +25,7 @@
|
||||
<Preference
|
||||
android:key="brightness"
|
||||
android:title="@string/brightness"
|
||||
settings:keywords="@string/keywords_display_brightness_level"
|
||||
settings:controller="com.android.settings.display.AutoBrightnessPreferenceController">
|
||||
settings:keywords="@string/keywords_display_brightness_level">
|
||||
<intent android:action="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
|
||||
</Preference>
|
||||
|
||||
@@ -43,7 +42,8 @@
|
||||
android:key="auto_brightness"
|
||||
android:title="@string/auto_brightness_title"
|
||||
settings:keywords="@string/keywords_display_auto_brightness"
|
||||
android:summary="@string/auto_brightness_summary" />
|
||||
android:summary="@string/auto_brightness_summary"
|
||||
settings:controller="com.android.settings.display.AutoBrightnessPreferenceController" />
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
android:key="wallpaper"
|
||||
|
@@ -34,6 +34,7 @@ import com.android.settings.gestures.AssistGestureFeatureProvider;
|
||||
import com.android.settings.localepicker.LocaleFeatureProvider;
|
||||
import com.android.settings.security.SecurityFeatureProvider;
|
||||
import com.android.settings.search.SearchFeatureProvider;
|
||||
import com.android.settings.slices.SlicesFeatureProvider;
|
||||
import com.android.settings.users.UserFeatureProvider;
|
||||
|
||||
/**
|
||||
@@ -106,6 +107,8 @@ public abstract class FeatureFactory {
|
||||
|
||||
public abstract SmsMirroringFeatureProvider getSmsMirroringFeatureProvider();
|
||||
|
||||
public abstract SlicesFeatureProvider getSlicesFeatureProvider();
|
||||
|
||||
public static final class FactoryNotFoundException extends RuntimeException {
|
||||
public FactoryNotFoundException(Throwable throwable) {
|
||||
super("Unable to create factory. Did you misconfigure Proguard?", throwable);
|
||||
|
@@ -48,6 +48,8 @@ import com.android.settings.search.SearchFeatureProvider;
|
||||
import com.android.settings.search.SearchFeatureProviderImpl;
|
||||
import com.android.settings.security.SecurityFeatureProvider;
|
||||
import com.android.settings.security.SecurityFeatureProviderImpl;
|
||||
import com.android.settings.slices.SlicesFeatureProvider;
|
||||
import com.android.settings.slices.SlicesFeatureProviderImpl;
|
||||
import com.android.settings.users.UserFeatureProvider;
|
||||
import com.android.settings.users.UserFeatureProviderImpl;
|
||||
import com.android.settings.wrapper.ConnectivityManagerWrapper;
|
||||
@@ -75,6 +77,7 @@ public class FeatureFactoryImpl extends FeatureFactory {
|
||||
private BluetoothFeatureProvider mBluetoothFeatureProvider;
|
||||
private DataPlanFeatureProvider mDataPlanFeatureProvider;
|
||||
private SmsMirroringFeatureProvider mSmsMirroringFeatureProvider;
|
||||
private SlicesFeatureProvider mSlicesFeatureProvider;
|
||||
|
||||
@Override
|
||||
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
|
||||
@@ -208,4 +211,12 @@ public class FeatureFactoryImpl extends FeatureFactory {
|
||||
}
|
||||
return mSmsMirroringFeatureProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SlicesFeatureProvider getSlicesFeatureProvider() {
|
||||
if (mSlicesFeatureProvider == null) {
|
||||
mSlicesFeatureProvider = new SlicesFeatureProviderImpl();
|
||||
}
|
||||
return mSlicesFeatureProvider;
|
||||
}
|
||||
}
|
||||
|
@@ -20,8 +20,7 @@ import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* TODO (b/67996923) Add SlicesIndexingManager
|
||||
* Data class representing a slice stored by {@link SlicesIndexingManager}.
|
||||
* Data class representing a slice stored by {@link SlicesIndexer}.
|
||||
* Note that {@link #key} is treated as a primary key for this class and determines equality.
|
||||
*/
|
||||
public class SliceData {
|
||||
@@ -173,10 +172,6 @@ public class SliceData {
|
||||
throw new IllegalStateException("Preference Controller cannot be empty");
|
||||
}
|
||||
|
||||
if (mUri == null) {
|
||||
throw new IllegalStateException("Uri cannot be null");
|
||||
}
|
||||
|
||||
return new SliceData(this);
|
||||
}
|
||||
|
||||
|
202
src/com/android/settings/slices/SliceDataConverter.java
Normal file
202
src/com/android/settings/slices/SliceDataConverter.java
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.DatabaseIndexingUtils;
|
||||
import com.android.settings.search.Indexable.SearchIndexProvider;
|
||||
import com.android.settings.search.SearchIndexableResources;
|
||||
import com.android.settings.search.XmlParserUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Converts {@link DashboardFragment} to {@link SliceData}.
|
||||
*/
|
||||
class SliceDataConverter {
|
||||
|
||||
private static final String TAG = "SliceDataConverter";
|
||||
|
||||
private static final String NODE_NAME_PREFERENCE_SCREEN = "PreferenceScreen";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private List<SliceData> mSliceData;
|
||||
|
||||
public SliceDataConverter(Context context) {
|
||||
mContext = context;
|
||||
mSliceData = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of {@link SliceData} to be indexed and later referenced as a Slice.
|
||||
*
|
||||
* The collection works as follows:
|
||||
* - Collects a list of Fragments from {@link SearchIndexableResources}.
|
||||
* - From each fragment, grab a {@link SearchIndexProvider}.
|
||||
* - For each provider, collect XML resource layout and a list of
|
||||
* {@link com.android.settings.core.BasePreferenceController}.
|
||||
*/
|
||||
public List<SliceData> getSliceData() {
|
||||
if (!mSliceData.isEmpty()) {
|
||||
return mSliceData;
|
||||
}
|
||||
|
||||
final Collection<Class> indexableClasses = SearchIndexableResources.providerValues();
|
||||
|
||||
for (Class clazz : indexableClasses) {
|
||||
final String fragmentName = clazz.getName();
|
||||
|
||||
final SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
|
||||
clazz);
|
||||
|
||||
// CodeInspection test guards against the null check. Keep check in case of bad actors.
|
||||
if (provider == null) {
|
||||
Log.e(TAG, fragmentName + " dose not implement Search Index Provider");
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<SliceData> providerSliceData = getSliceDataFromProvider(provider,
|
||||
fragmentName);
|
||||
mSliceData.addAll(providerSliceData);
|
||||
}
|
||||
|
||||
return mSliceData;
|
||||
}
|
||||
|
||||
private List<SliceData> getSliceDataFromProvider(SearchIndexProvider provider,
|
||||
String fragmentName) {
|
||||
final List<SliceData> sliceData = new ArrayList<>();
|
||||
|
||||
final List<SearchIndexableResource> resList =
|
||||
provider.getXmlResourcesToIndex(mContext, true /* enabled */);
|
||||
|
||||
if (resList == null) {
|
||||
return sliceData;
|
||||
}
|
||||
|
||||
// TODO (b/67996923) get a list of permanent NIKs and skip the invalid keys.
|
||||
|
||||
for (SearchIndexableResource resource : resList) {
|
||||
int xmlResId = resource.xmlResId;
|
||||
if (xmlResId == 0) {
|
||||
Log.e(TAG, fragmentName + " provides invalid XML (0) in search provider.");
|
||||
continue;
|
||||
}
|
||||
|
||||
List<SliceData> xmlSliceData = getSliceDataFromXML(xmlResId, fragmentName);
|
||||
sliceData.addAll(xmlSliceData);
|
||||
}
|
||||
|
||||
return sliceData;
|
||||
}
|
||||
|
||||
private List<SliceData> getSliceDataFromXML(int xmlResId, String fragmentName) {
|
||||
XmlResourceParser parser = null;
|
||||
|
||||
final List<SliceData> xmlSliceData = new ArrayList<>();
|
||||
String key;
|
||||
String title;
|
||||
String summary;
|
||||
@DrawableRes int iconResId;
|
||||
String controllerClassName;
|
||||
|
||||
try {
|
||||
parser = mContext.getResources().getXml(xmlResId);
|
||||
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& type != XmlPullParser.START_TAG) {
|
||||
// Parse next until start tag is found
|
||||
}
|
||||
|
||||
String nodeName = parser.getName();
|
||||
if (!NODE_NAME_PREFERENCE_SCREEN.equals(nodeName)) {
|
||||
throw new RuntimeException(
|
||||
"XML document must start with <PreferenceScreen> tag; found"
|
||||
+ nodeName + " at " + parser.getPositionDescription());
|
||||
}
|
||||
|
||||
final int outerDepth = parser.getDepth();
|
||||
final AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
final String screenTitle = XmlParserUtils.getDataTitle(mContext, attrs);
|
||||
|
||||
// TODO (b/67996923) Investigate if we need headers for Slices, since they never
|
||||
// correspond to an actual setting.
|
||||
SliceData xmlSlice;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// TODO (b/67996923) Non-controller Slices should become intent-only slices.
|
||||
// Note that without a controller, dynamic summaries are impossible.
|
||||
// TODO (b/67996923) This will not work if preferences have nested intens:
|
||||
// <pref ....>
|
||||
// <intent action="blab"/> </pref>
|
||||
controllerClassName = XmlParserUtils.getController(mContext, attrs);
|
||||
if (TextUtils.isEmpty(controllerClassName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
title = XmlParserUtils.getDataTitle(mContext, attrs);
|
||||
key = XmlParserUtils.getDataKey(mContext, attrs);
|
||||
iconResId = XmlParserUtils.getDataIcon(mContext, attrs);
|
||||
summary = XmlParserUtils.getDataSummary(mContext, attrs);
|
||||
|
||||
xmlSlice = new SliceData.Builder()
|
||||
.setKey(key)
|
||||
.setTitle(title)
|
||||
.setSummary(summary)
|
||||
.setIcon(iconResId)
|
||||
.setScreenTitle(screenTitle)
|
||||
.setPreferenceControllerClassName(controllerClassName)
|
||||
.setFragmentName(fragmentName)
|
||||
.build();
|
||||
|
||||
xmlSliceData.add(xmlSlice);
|
||||
}
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.w(TAG, "XML Error parsing PreferenceScreen: ", e);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "IO Error parsing PreferenceScreen: ", e);
|
||||
} catch (Resources.NotFoundException e) {
|
||||
Log.w(TAG, "Resoucre not found error parsing PreferenceScreen: ", e);
|
||||
} finally {
|
||||
if (parser != null) parser.close();
|
||||
}
|
||||
return xmlSliceData;
|
||||
}
|
||||
}
|
@@ -1,12 +1,30 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Defines the schema for the Slices database.
|
||||
@@ -38,7 +56,7 @@ public class SlicesDatabaseHelper extends SQLiteOpenHelper {
|
||||
/**
|
||||
* Summary / Subtitle for the setting.
|
||||
*/
|
||||
String SUBTITLE = "subtitle";
|
||||
String SUMMARY = "summary";
|
||||
|
||||
/**
|
||||
* Title of the Setting screen on which the Setting lives.
|
||||
@@ -69,7 +87,7 @@ public class SlicesDatabaseHelper extends SQLiteOpenHelper {
|
||||
", " +
|
||||
IndexColumns.TITLE +
|
||||
", " +
|
||||
IndexColumns.SUBTITLE +
|
||||
IndexColumns.SUMMARY +
|
||||
", " +
|
||||
IndexColumns.SCREENTITLE +
|
||||
", " +
|
||||
@@ -82,7 +100,16 @@ public class SlicesDatabaseHelper extends SQLiteOpenHelper {
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public SlicesDatabaseHelper(Context context) {
|
||||
private static SlicesDatabaseHelper sSingleton;
|
||||
|
||||
public static synchronized SlicesDatabaseHelper getInstance(Context context) {
|
||||
if (sSingleton == null) {
|
||||
sSingleton = new SlicesDatabaseHelper(context);
|
||||
}
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
private SlicesDatabaseHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null /* CursorFactor */, DATABASE_VERSION);
|
||||
mContext = context;
|
||||
}
|
||||
@@ -100,7 +127,11 @@ public class SlicesDatabaseHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/**
|
||||
* Drops the currently stored databases rebuilds them.
|
||||
* Also un-marks the state of the data such that any subsequent call to
|
||||
* {@link#isNewIndexingState(Context)} will return {@code true}.
|
||||
*/
|
||||
void reconstruct(SQLiteDatabase db) {
|
||||
mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
@@ -110,13 +141,61 @@ public class SlicesDatabaseHelper extends SQLiteOpenHelper {
|
||||
createDatabases(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the current state of the device for the validity of the data. Should be called after
|
||||
* a full index of the TABLE_SLICES_INDEX.
|
||||
*/
|
||||
public void setIndexedState() {
|
||||
setBuildIndexed();
|
||||
setLocaleIndexed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the indexed slice data reflects the current state of the phone.
|
||||
*
|
||||
* @return {@code true} if database should be rebuilt, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isSliceDataIndexed() {
|
||||
return isBuildIndexed() && isLocaleIndexed();
|
||||
}
|
||||
|
||||
private void createDatabases(SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_SLICES_TABLE);
|
||||
Log.d(TAG, "Created databases");
|
||||
}
|
||||
|
||||
|
||||
private void dropTables(SQLiteDatabase db) {
|
||||
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SLICES_INDEX);
|
||||
}
|
||||
|
||||
private void setBuildIndexed() {
|
||||
mContext.getSharedPreferences(SHARED_PREFS_TAG, 0 /* mode */)
|
||||
.edit()
|
||||
.putBoolean(getBuildTag(), true /* value */)
|
||||
.apply();
|
||||
}
|
||||
|
||||
private void setLocaleIndexed() {
|
||||
mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putBoolean(Locale.getDefault().toString(), true /* value */)
|
||||
.apply();
|
||||
}
|
||||
|
||||
private boolean isBuildIndexed() {
|
||||
return mContext.getSharedPreferences(SHARED_PREFS_TAG,
|
||||
Context.MODE_PRIVATE)
|
||||
.getBoolean(getBuildTag(), false /* default */);
|
||||
}
|
||||
|
||||
private boolean isLocaleIndexed() {
|
||||
return mContext.getSharedPreferences(SHARED_PREFS_TAG,
|
||||
Context.MODE_PRIVATE)
|
||||
.getBoolean(Locale.getDefault().toString(), false /* default */);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String getBuildTag() {
|
||||
return Build.FINGERPRINT;
|
||||
}
|
||||
}
|
17
src/com/android/settings/slices/SlicesFeatureProvider.java
Normal file
17
src/com/android/settings/slices/SlicesFeatureProvider.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.android.settings.slices;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Manages Slices in Settings.
|
||||
*/
|
||||
public interface SlicesFeatureProvider {
|
||||
|
||||
boolean DEBUG = false;
|
||||
|
||||
SlicesIndexer getSliceIndexer(Context context);
|
||||
|
||||
SliceDataConverter getSliceDataConverter(Context context);
|
||||
|
||||
void indexSliceData(Context context);
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package com.android.settings.slices;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
/**
|
||||
* Manages Slices in Settings.
|
||||
*/
|
||||
public class SlicesFeatureProviderImpl implements SlicesFeatureProvider {
|
||||
|
||||
private SlicesIndexer mSlicesIndexer;
|
||||
private SliceDataConverter mSliceDataConverter;
|
||||
|
||||
@Override
|
||||
public SlicesIndexer getSliceIndexer(Context context) {
|
||||
if (mSlicesIndexer == null) {
|
||||
mSlicesIndexer = new SlicesIndexer(context.getApplicationContext());
|
||||
}
|
||||
return mSlicesIndexer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SliceDataConverter getSliceDataConverter(Context context) {
|
||||
if(mSliceDataConverter == null) {
|
||||
mSliceDataConverter = new SliceDataConverter(context.getApplicationContext());
|
||||
}
|
||||
return mSliceDataConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void indexSliceData(Context context) {
|
||||
// TODO (b/67996923) add indexing time log
|
||||
SlicesIndexer indexer = getSliceIndexer(context);
|
||||
ThreadUtils.postOnBackgroundThread(indexer);
|
||||
}
|
||||
}
|
102
src/com/android/settings/slices/SlicesIndexer.java
Normal file
102
src/com/android/settings/slices/SlicesIndexer.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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 android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.slices.SlicesDatabaseHelper.IndexColumns;
|
||||
import com.android.settings.slices.SlicesDatabaseHelper.Tables;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manages the conversion of {@link DashboardFragment} and {@link BasePreferenceController} to
|
||||
* indexable data {@link SliceData} to be stored for Slices.
|
||||
*/
|
||||
class SlicesIndexer implements Runnable {
|
||||
|
||||
private static final String TAG = "SlicesIndexingManager";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private SlicesDatabaseHelper mHelper;
|
||||
|
||||
public SlicesIndexer(Context context) {
|
||||
mContext = context;
|
||||
mHelper = SlicesDatabaseHelper.getInstance(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously takes data obtained from {@link SliceDataConverter} and indexes it into a
|
||||
* SQLite database.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
if (mHelper.isSliceDataIndexed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SQLiteDatabase database = mHelper.getWritableDatabase();
|
||||
|
||||
try {
|
||||
database.beginTransaction();
|
||||
|
||||
mHelper.reconstruct(mHelper.getWritableDatabase());
|
||||
List<SliceData> indexData = getSliceData();
|
||||
insertSliceData(database, indexData);
|
||||
|
||||
mHelper.setIndexedState();
|
||||
database.setTransactionSuccessful();
|
||||
} finally {
|
||||
database.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<SliceData> getSliceData() {
|
||||
return FeatureFactory.getFactory(mContext)
|
||||
.getSlicesFeatureProvider()
|
||||
.getSliceDataConverter(mContext)
|
||||
.getSliceData();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void insertSliceData(SQLiteDatabase database, List<SliceData> indexData) {
|
||||
ContentValues values;
|
||||
|
||||
for (SliceData dataRow : indexData) {
|
||||
values = new ContentValues();
|
||||
values.put(IndexColumns.KEY, dataRow.getKey());
|
||||
values.put(IndexColumns.TITLE, dataRow.getTitle());
|
||||
values.put(IndexColumns.SUMMARY, dataRow.getSummary());
|
||||
values.put(IndexColumns.SCREENTITLE, dataRow.getScreenTitle());
|
||||
values.put(IndexColumns.ICON_RESOURCE, dataRow.getIconResource());
|
||||
values.put(IndexColumns.FRAGMENT, dataRow.getFragmentClassName());
|
||||
values.put(IndexColumns.CONTROLLER, dataRow.getPreferenceController());
|
||||
|
||||
database.replaceOrThrow(Tables.TABLE_SLICES_INDEX, null /* nullColumnHack */,
|
||||
values);
|
||||
}
|
||||
}
|
||||
}
|
29
tests/robotests/res/xml-mcc999/location_settings.xml
Normal file
29
tests/robotests/res/xml-mcc999/location_settings.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2011 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.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="fake_title_key"
|
||||
android:title="screen_title">
|
||||
|
||||
<Preference
|
||||
android:key="key"
|
||||
android:title="title"
|
||||
android:icon="@drawable/ic_android"
|
||||
android:summary="summary"
|
||||
settings:controller="com.android.settings.slices.FakePreferenceController"/>
|
||||
|
||||
</PreferenceScreen>
|
@@ -20,8 +20,10 @@ package com.android.settings.search;
|
||||
import android.content.Context;
|
||||
import android.provider.SearchIndexableResource;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FakeIndexProvider implements Indexable {
|
||||
@@ -33,7 +35,11 @@ public class FakeIndexProvider implements Indexable {
|
||||
@Override
|
||||
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
|
||||
boolean enabled) {
|
||||
return null;
|
||||
List<SearchIndexableResource> resources = new ArrayList<>();
|
||||
SearchIndexableResource res = new SearchIndexableResource(context);
|
||||
res.xmlResId = R.xml.location_settings;
|
||||
resources.add(res);
|
||||
return resources;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +50,8 @@ public class FakeIndexProvider implements Indexable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
|
||||
public List<AbstractPreferenceController> getPreferenceControllers(
|
||||
Context context) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
public class FakePreferenceController extends BasePreferenceController {
|
||||
|
||||
public FakePreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.search.FakeIndexProvider;
|
||||
import com.android.settings.search.SearchIndexableResources;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class SliceDataConverterTest {
|
||||
|
||||
private final String fakeKey = "key";
|
||||
private final String fakeTitle = "title";
|
||||
private final String fakeSummary = "summary";
|
||||
private final String fakeScreenTitle = "screen_title";
|
||||
private final String fakeFragmentClassName = FakeIndexProvider.class.getName();
|
||||
private final String fakeControllerName = FakePreferenceController.class.getName();
|
||||
|
||||
Context mContext;
|
||||
|
||||
private Set<Class> mProviderClassesCopy;
|
||||
|
||||
SliceDataConverter mSliceDataConverter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mProviderClassesCopy = new HashSet<>(SearchIndexableResources.providerValues());
|
||||
mSliceDataConverter = new SliceDataConverter(mContext);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
SearchIndexableResources.providerValues().clear();
|
||||
SearchIndexableResources.providerValues().addAll(mProviderClassesCopy);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void testFakeProvider_convertsFakeData() {
|
||||
SearchIndexableResources.providerValues().clear();
|
||||
SearchIndexableResources.providerValues().add(FakeIndexProvider.class);
|
||||
|
||||
List<SliceData> sliceDataList = mSliceDataConverter.getSliceData();
|
||||
|
||||
assertThat(sliceDataList).hasSize(1);
|
||||
SliceData fakeSlice = sliceDataList.get(0);
|
||||
|
||||
assertThat(fakeSlice.getKey()).isEqualTo(fakeKey);
|
||||
assertThat(fakeSlice.getTitle()).isEqualTo(fakeTitle);
|
||||
assertThat(fakeSlice.getSummary()).isEqualTo(fakeSummary);
|
||||
assertThat(fakeSlice.getScreenTitle()).isEqualTo(fakeScreenTitle);
|
||||
assertThat(fakeSlice.getIconResource()).isNotNull();
|
||||
assertThat(fakeSlice.getUri()).isNull();
|
||||
assertThat(fakeSlice.getFragmentClassName()).isEqualTo(fakeFragmentClassName);
|
||||
assertThat(fakeSlice.getPreferenceController()).isEqualTo(fakeControllerName);
|
||||
}
|
||||
}
|
@@ -104,19 +104,6 @@ public class SliceDataTest {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testBuilder_noUri_throwsIllegalStateException() {
|
||||
new SliceData.Builder()
|
||||
.setKey(KEY)
|
||||
.setTitle(TITLE)
|
||||
.setSummary(SUMMARY)
|
||||
.setScreenTitle(SCREEN_TITLE)
|
||||
.setIcon(ICON)
|
||||
.setFragmentName(FRAGMENT_NAME)
|
||||
.setPreferenceControllerClassName(PREF_CONTROLLER)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testBuilder_noPrefController_throwsIllegalStateException() {
|
||||
new SliceData.Builder()
|
||||
@@ -199,6 +186,30 @@ public class SliceDataTest {
|
||||
assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilder_noUri_buildsMatchingObject() {
|
||||
SliceData.Builder builder = new SliceData.Builder()
|
||||
.setKey(KEY)
|
||||
.setTitle(TITLE)
|
||||
.setSummary(SUMMARY)
|
||||
.setScreenTitle(SCREEN_TITLE)
|
||||
.setIcon(ICON)
|
||||
.setFragmentName(FRAGMENT_NAME)
|
||||
.setUri(null)
|
||||
.setPreferenceControllerClassName(PREF_CONTROLLER);
|
||||
|
||||
SliceData data = builder.build();
|
||||
|
||||
assertThat(data.getKey()).isEqualTo(KEY);
|
||||
assertThat(data.getTitle()).isEqualTo(TITLE);
|
||||
assertThat(data.getSummary()).isEqualTo(SUMMARY);
|
||||
assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE);
|
||||
assertThat(data.getIconResource()).isEqualTo(ICON);
|
||||
assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME);
|
||||
assertThat(data.getUri()).isNull();
|
||||
assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquality_identicalObjects() {
|
||||
SliceData.Builder builder = new SliceData.Builder()
|
||||
|
@@ -1,7 +1,26 @@
|
||||
/*
|
||||
* 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.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
@@ -19,6 +38,8 @@ import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class SlicesDatabaseHelperTest {
|
||||
@@ -30,7 +51,7 @@ public class SlicesDatabaseHelperTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mSlicesDatabaseHelper = new SlicesDatabaseHelper(mContext);
|
||||
mSlicesDatabaseHelper = spy(SlicesDatabaseHelper.getInstance(mContext));
|
||||
mDatabase = mSlicesDatabaseHelper.getWritableDatabase();
|
||||
}
|
||||
|
||||
@@ -47,7 +68,7 @@ public class SlicesDatabaseHelperTest {
|
||||
String[] expectedNames = new String[]{
|
||||
IndexColumns.KEY,
|
||||
IndexColumns.TITLE,
|
||||
IndexColumns.SUBTITLE,
|
||||
IndexColumns.SUMMARY,
|
||||
IndexColumns.SCREENTITLE,
|
||||
IndexColumns.ICON_RESOURCE,
|
||||
IndexColumns.FRAGMENT,
|
||||
@@ -71,17 +92,48 @@ public class SlicesDatabaseHelperTest {
|
||||
assertThat(newCursor.getCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexState_buildAndLocaleSet() {
|
||||
mSlicesDatabaseHelper.reconstruct(mDatabase);
|
||||
|
||||
boolean baseState = mSlicesDatabaseHelper.isSliceDataIndexed();
|
||||
assertThat(baseState).isFalse();
|
||||
|
||||
mSlicesDatabaseHelper.setIndexedState();
|
||||
boolean indexedState = mSlicesDatabaseHelper.isSliceDataIndexed();
|
||||
assertThat(indexedState).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocaleChanges_newIndexingState() {
|
||||
mSlicesDatabaseHelper.reconstruct(mDatabase);
|
||||
mSlicesDatabaseHelper.setIndexedState();
|
||||
|
||||
Locale.setDefault(new Locale("ca"));
|
||||
|
||||
assertThat(mSlicesDatabaseHelper.isSliceDataIndexed()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildFingerprintChanges_newIndexingState() {
|
||||
mSlicesDatabaseHelper.reconstruct(mDatabase);
|
||||
mSlicesDatabaseHelper.setIndexedState();
|
||||
doReturn("newBuild").when(mSlicesDatabaseHelper).getBuildTag();
|
||||
|
||||
assertThat(mSlicesDatabaseHelper.isSliceDataIndexed()).isFalse();
|
||||
}
|
||||
|
||||
private ContentValues getDummyRow() {
|
||||
ContentValues values;
|
||||
|
||||
values = new ContentValues();
|
||||
values.put(IndexColumns.KEY, "key");
|
||||
values.put(IndexColumns.TITLE, "title");
|
||||
values.put(IndexColumns.SUBTITLE, "subtitle");
|
||||
values.put(IndexColumns.SUMMARY, "summary");
|
||||
values.put(IndexColumns.ICON_RESOURCE, 99);
|
||||
values.put(IndexColumns.FRAGMENT, "fragmentClassName");
|
||||
values.put(IndexColumns.CONTROLLER, "preferenceController");
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.slices.SlicesDatabaseHelper.IndexColumns;
|
||||
import com.android.settings.testutils.DatabaseTestUtils;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class SlicesIndexerTest {
|
||||
|
||||
private final String[] KEYS = new String[]{"key1", "key2", "key3"};
|
||||
private final String[] TITLES = new String[]{"title1", "title2", "title3"};
|
||||
private final String SUMMARY = "subtitle";
|
||||
private final String SCREEN_TITLE = "screen title";
|
||||
private final String FRAGMENT_NAME = "fragment name";
|
||||
private final int ICON = 1234; // I declare a thumb war
|
||||
private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
|
||||
private final String PREF_CONTROLLER = "com.android.settings.slices.tester";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private SlicesIndexer mManager;
|
||||
|
||||
private SQLiteDatabase mDb;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mManager = spy(new SlicesIndexer(mContext));
|
||||
mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
DatabaseTestUtils.clearDb(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlreadyIndexed_doesNotIndexAgain() {
|
||||
String newKey = "newKey";
|
||||
String newTitle = "newTitle";
|
||||
SlicesDatabaseHelper.getInstance(mContext).setIndexedState();
|
||||
Locale.setDefault(new Locale("ca"));
|
||||
insertSpecialCase(newKey, newTitle);
|
||||
|
||||
// Attempt indexing - should not do anything.
|
||||
mManager.run();
|
||||
|
||||
Cursor cursor = mDb.rawQuery("SELECT * FROM slices_index", null);
|
||||
cursor.moveToFirst();
|
||||
assertThat(cursor.getCount()).isEqualTo(1);
|
||||
assertThat(cursor.getString(cursor.getColumnIndex(IndexColumns.KEY))).isEqualTo(newKey);
|
||||
assertThat(cursor.getString(cursor.getColumnIndex(IndexColumns.TITLE))).isEqualTo(newTitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertSliceData_indexedStateSet() {
|
||||
SlicesDatabaseHelper helper = SlicesDatabaseHelper.getInstance(mContext);
|
||||
helper.setIndexedState();
|
||||
doReturn(new ArrayList<SliceData>()).when(mManager).getSliceData();
|
||||
|
||||
mManager.run();
|
||||
|
||||
assertThat(helper.isSliceDataIndexed()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertSliceData_mockDataInserted() {
|
||||
List<SliceData> sliceData = getDummyIndexableData();
|
||||
doReturn(sliceData).when(mManager).getSliceData();
|
||||
|
||||
mManager.run();
|
||||
|
||||
Cursor cursor = mDb.rawQuery("SELECT * FROM slices_index", null);
|
||||
assertThat(cursor.getCount()).isEqualTo(sliceData.size());
|
||||
|
||||
cursor.moveToFirst();
|
||||
for (int i = 0; i < sliceData.size(); i++) {
|
||||
assertThat(cursor.getString(cursor.getColumnIndex(IndexColumns.KEY))).isEqualTo(
|
||||
KEYS[i]);
|
||||
assertThat(cursor.getString(cursor.getColumnIndex(IndexColumns.TITLE))).isEqualTo(
|
||||
TITLES[i]);
|
||||
cursor.moveToNext();
|
||||
}
|
||||
}
|
||||
|
||||
private void insertSpecialCase(String key, String title) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(IndexColumns.KEY, key);
|
||||
values.put(IndexColumns.TITLE, title);
|
||||
|
||||
mDb.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, values);
|
||||
}
|
||||
|
||||
private List<SliceData> getDummyIndexableData() {
|
||||
List<SliceData> sliceData = new ArrayList<>();
|
||||
SliceData.Builder builder = new SliceData.Builder();
|
||||
builder.setSummary(SUMMARY)
|
||||
.setScreenTitle(SCREEN_TITLE)
|
||||
.setFragmentName(FRAGMENT_NAME)
|
||||
.setIcon(ICON)
|
||||
.setUri(URI)
|
||||
.setPreferenceControllerClassName(PREF_CONTROLLER);
|
||||
|
||||
for (int i = 0; i < KEYS.length; i++) {
|
||||
builder.setKey(KEYS[i])
|
||||
.setTitle(TITLES[i]);
|
||||
sliceData.add(builder.build());
|
||||
}
|
||||
|
||||
return sliceData;
|
||||
}
|
||||
}
|
@@ -19,12 +19,33 @@ package com.android.settings.testutils;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.search.IndexDatabaseHelper;
|
||||
import com.android.settings.slices.SlicesDatabaseHelper;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class DatabaseTestUtils {
|
||||
|
||||
public static void clearDb(Context context) {
|
||||
clearSearchDb(context);
|
||||
clearSlicesDb(context);
|
||||
}
|
||||
|
||||
private static void clearSlicesDb(Context context) {
|
||||
SlicesDatabaseHelper helper = SlicesDatabaseHelper.getInstance(context);
|
||||
helper.close();
|
||||
|
||||
Field instance;
|
||||
Class clazz = SlicesDatabaseHelper.class;
|
||||
try {
|
||||
instance = clazz.getDeclaredField("sSingleton");
|
||||
instance.setAccessible(true);
|
||||
instance.set(null, null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void clearSearchDb(Context context) {
|
||||
IndexDatabaseHelper helper = IndexDatabaseHelper.getInstance(context);
|
||||
helper.close();
|
||||
|
||||
|
@@ -37,6 +37,7 @@ import com.android.settings.overlay.SupportFeatureProvider;
|
||||
import com.android.settings.overlay.SurveyFeatureProvider;
|
||||
import com.android.settings.search.SearchFeatureProvider;
|
||||
import com.android.settings.security.SecurityFeatureProvider;
|
||||
import com.android.settings.slices.SlicesFeatureProvider;
|
||||
import com.android.settings.users.UserFeatureProvider;
|
||||
|
||||
import org.mockito.Answers;
|
||||
@@ -63,6 +64,7 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public final BluetoothFeatureProvider bluetoothFeatureProvider;
|
||||
public final DataPlanFeatureProvider dataPlanFeatureProvider;
|
||||
public final SmsMirroringFeatureProvider smsMirroringFeatureProvider;
|
||||
public final SlicesFeatureProvider slicesFeatureProvider;
|
||||
|
||||
/**
|
||||
* Call this in {@code @Before} method of the test class to use fake factory.
|
||||
@@ -101,6 +103,7 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
bluetoothFeatureProvider = mock(BluetoothFeatureProvider.class);
|
||||
dataPlanFeatureProvider = mock(DataPlanFeatureProvider.class);
|
||||
smsMirroringFeatureProvider = mock(SmsMirroringFeatureProvider.class);
|
||||
slicesFeatureProvider = mock(SlicesFeatureProvider.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,4 +185,9 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public SmsMirroringFeatureProvider getSmsMirroringFeatureProvider() {
|
||||
return smsMirroringFeatureProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SlicesFeatureProvider getSlicesFeatureProvider() {
|
||||
return slicesFeatureProvider;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user