From 762b4969d9c5efaed9241d66abc4417419ddd0e5 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Wed, 30 Nov 2016 15:56:17 -0800 Subject: [PATCH] Add search providers for Connected Devices in new IA Also add code inspection tests to ensure search provider is added properly. Bug: 33252252 Test: make RunSettingsRoboTests Change-Id: I192e1d9fe0498b76013c4d43b5624d1ef2beb6f9 --- res/values/strings.xml | 2 +- res/xml/connected_devices.xml | 4 +- .../ConnectedDeviceDashboardFragment.java | 22 +++++ .../MetricsFeatureProviderImpl.java | 6 -- src/com/android/settings/search/Ranking.java | 2 + .../search/SearchIndexableResources.java | 25 +++--- ...ther_not_in_search_index_provider_registry | 3 + .../ConnectedDeviceDashboardFragmentTest.java | 46 ++++++++--- .../InstrumentableFragmentCodeInspector.java | 8 +- .../SharedPreferenceLoggerTest.java | 30 +++---- .../SearchIndexProviderCodeInspector.java | 81 +++++++++++++------ 11 files changed, 152 insertions(+), 77 deletions(-) create mode 100644 tests/robotests/assets/grandfather_not_in_search_index_provider_registry diff --git a/res/values/strings.xml b/res/values/strings.xml index 57ae326e771..10ea414da84 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4517,7 +4517,7 @@ Never - at %1$sbattery + at %1$s battery diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml index bf40f9d38ec..188fd86917c 100644 --- a/res/xml/connected_devices.xml +++ b/res/xml/connected_devices.xml @@ -14,7 +14,9 @@ limitations under the License. --> - + getXmlResourcesToIndex( + Context context, boolean enabled) { + if (!FeatureFactory.getFactory(context).getDashboardFeatureProvider(context) + .isEnabled()) { + return null; + } + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.connected_devices; + return Arrays.asList(sir); + } + }; } \ No newline at end of file diff --git a/src/com/android/settings/core/instrumentation/MetricsFeatureProviderImpl.java b/src/com/android/settings/core/instrumentation/MetricsFeatureProviderImpl.java index e4492befbe4..b74c8863085 100644 --- a/src/com/android/settings/core/instrumentation/MetricsFeatureProviderImpl.java +++ b/src/com/android/settings/core/instrumentation/MetricsFeatureProviderImpl.java @@ -16,7 +16,6 @@ package com.android.settings.core.instrumentation; import android.content.Context; -import android.support.annotation.VisibleForTesting; import java.util.ArrayList; import java.util.List; @@ -92,9 +91,4 @@ public class MetricsFeatureProviderImpl implements MetricsFeatureProvider { writer.histogram(context, name, bucket); } } - - @VisibleForTesting - public void addLogWriter(LogWriter logWriter) { - mLoggerWriters.add(logWriter); - } } diff --git a/src/com/android/settings/search/Ranking.java b/src/com/android/settings/search/Ranking.java index 10702008df9..28861ca2666 100644 --- a/src/com/android/settings/search/Ranking.java +++ b/src/com/android/settings/search/Ranking.java @@ -32,6 +32,7 @@ import com.android.settings.accounts.AccountSettings; import com.android.settings.applications.AdvancedAppSettings; import com.android.settings.applications.SpecialAccessSettings; import com.android.settings.bluetooth.BluetoothSettings; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; import com.android.settings.datausage.DataUsageMeteredSettings; import com.android.settings.datausage.DataUsageSummary; import com.android.settings.deviceinfo.StorageDashboardFragment; @@ -108,6 +109,7 @@ public final class Ranking { // BT sRankMap.put(BluetoothSettings.class.getName(), RANK_BT); + sRankMap.put(ConnectedDeviceDashboardFragment.class.getName(), RANK_BT); // SIM Cards sRankMap.put(SimSettings.class.getName(), RANK_SIM); diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java index 7acac9750b7..959f4444d42 100644 --- a/src/com/android/settings/search/SearchIndexableResources.java +++ b/src/com/android/settings/search/SearchIndexableResources.java @@ -20,6 +20,7 @@ import android.provider.SearchIndexableResource; import android.support.annotation.DrawableRes; import android.support.annotation.VisibleForTesting; import android.support.annotation.XmlRes; + import com.android.settings.DateTimeSettings; import com.android.settings.DevelopmentSettings; import com.android.settings.DeviceInfoSettings; @@ -36,6 +37,7 @@ import com.android.settings.accounts.AccountSettings; import com.android.settings.applications.AdvancedAppSettings; import com.android.settings.applications.SpecialAccessSettings; import com.android.settings.bluetooth.BluetoothSettings; +import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; import com.android.settings.datausage.DataUsageMeteredSettings; import com.android.settings.datausage.DataUsageSummary; import com.android.settings.deviceinfo.StorageDashboardFragment; @@ -83,9 +85,9 @@ public final class SearchIndexableResources { static { addIndex(WifiSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_wireless); addIndex(AdvancedWifiSettings.class, - R.xml.wifi_advanced_settings, R.drawable.ic_settings_wireless); + R.xml.wifi_advanced_settings, R.drawable.ic_settings_wireless); addIndex(SavedAccessPointsWifiSettings.class, - R.xml.wifi_display_saved_access_points, R.drawable.ic_settings_wireless); + R.xml.wifi_display_saved_access_points, R.drawable.ic_settings_wireless); addIndex(BluetoothSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_bluetooth); addIndex(SimSettings.class, NO_DATA_RES_ID, R.drawable.ic_sim_sd); addIndex(DataUsageSummary.class, NO_DATA_RES_ID, R.drawable.ic_settings_data_usage); @@ -95,21 +97,21 @@ public final class SearchIndexableResources { addIndex(DisplaySettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_display); addIndex(WallpaperTypeSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_display); addIndex(ConfigureNotificationSettings.class, - R.xml.configure_notification_settings, R.drawable.ic_settings_notifications); + R.xml.configure_notification_settings, R.drawable.ic_settings_notifications); addIndex(SoundSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_sound); addIndex(OtherSoundSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_sound); addIndex(ZenModeSettings.class, - R.xml.zen_mode_settings, R.drawable.ic_settings_notifications); + R.xml.zen_mode_settings, R.drawable.ic_settings_notifications); addIndex(ZenModePrioritySettings.class, - R.xml.zen_mode_priority_settings, R.drawable.ic_settings_notifications); + R.xml.zen_mode_priority_settings, R.drawable.ic_settings_notifications); addIndex(StorageSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_storage); addIndex(PowerUsageSummary.class, - R.xml.power_usage_summary, R.drawable.ic_settings_battery); + R.xml.power_usage_summary, R.drawable.ic_settings_battery); addIndex(BatterySaverSettings.class, - R.xml.battery_saver_settings, R.drawable.ic_settings_battery); + R.xml.battery_saver_settings, R.drawable.ic_settings_battery); addIndex(AdvancedAppSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_applications); addIndex(SpecialAccessSettings.class, - R.xml.special_access, R.drawable.ic_settings_applications); + R.xml.special_access, R.drawable.ic_settings_applications); addIndex(UserSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_multiuser); addIndex(GestureSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_gestures); addIndex(LocationSettings.class, R.xml.location_settings, R.drawable.ic_settings_location); @@ -118,7 +120,7 @@ public final class SearchIndexableResources { addIndex(ScreenPinningSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_security); addIndex(AccountSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_accounts); addIndex(InputMethodAndLanguageSettings.class, - NO_DATA_RES_ID, R.drawable.ic_settings_language); + NO_DATA_RES_ID, R.drawable.ic_settings_language); addIndex(PrivacySettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_backup); addIndex(DateTimeSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_date_time); addIndex(AccessibilitySettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_accessibility); @@ -127,10 +129,11 @@ public final class SearchIndexableResources { addIndex(DeviceInfoSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_about); addIndex(LegalSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_about); addIndex(ZenModeVisualInterruptionSettings.class, - R.xml.zen_mode_visual_interruptions_settings, - R.drawable.ic_settings_notifications); + R.xml.zen_mode_visual_interruptions_settings, + R.drawable.ic_settings_notifications); addIndex(SystemDashboardFragment.class, NO_DATA_RES_ID, R.drawable.ic_settings_about); addIndex(StorageDashboardFragment.class, NO_DATA_RES_ID, R.drawable.ic_settings_storage); + addIndex(ConnectedDeviceDashboardFragment.class, NO_DATA_RES_ID, R.drawable.ic_bt_laptop); addIndex(EnterprisePrivacySettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_about); } diff --git a/tests/robotests/assets/grandfather_not_in_search_index_provider_registry b/tests/robotests/assets/grandfather_not_in_search_index_provider_registry new file mode 100644 index 00000000000..29f3816ea76 --- /dev/null +++ b/tests/robotests/assets/grandfather_not_in_search_index_provider_registry @@ -0,0 +1,3 @@ +com.android.settings.display.ScreenZoomPreferenceFragmentForSetupWizard +com.android.settings.wifi.WifiSettingsForSetupWizard +com.android.settings.print.PrintServiceSettingsFragment diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java index d53a27b54b8..83bae5028a5 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragmentTest.java @@ -16,10 +16,13 @@ package com.android.settings.connecteddevice; import android.content.Context; +import android.provider.SearchIndexableResource; + import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settingslib.drawer.CategoryKey; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,28 +30,45 @@ import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; + +import java.util.List; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class ConnectedDeviceDashboardFragmentTest { - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Context mContext; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; - private ConnectedDeviceDashboardFragment mFragment; + private ConnectedDeviceDashboardFragment mFragment; - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - FakeFeatureFactory.setupForTest(mContext); - mFragment = new ConnectedDeviceDashboardFragment(); - } + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(mContext); + final FakeFeatureFactory factory = + (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + when(factory.dashboardFeatureProvider.isEnabled()).thenReturn(true); + mFragment = new ConnectedDeviceDashboardFragment(); + } - @Test - public void testCategory_isConnectedDevice() { - assertThat(mFragment.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_DEVICE); - } + @Test + public void testCategory_isConnectedDevice() { + assertThat(mFragment.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_DEVICE); + } + @Test + public void testSearchIndexProvider_shouldIndexResource() { + final List indexRes = + ConnectedDeviceDashboardFragment.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex( + ShadowApplication.getInstance().getApplicationContext(), + true /* enabled */); + + assertThat(indexRes).isNotNull(); + assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId()); + } } diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java index 109d2ee6987..ed4e50c7b72 100644 --- a/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java +++ b/tests/robotests/src/com/android/settings/core/instrumentation/InstrumentableFragmentCodeInspector.java @@ -32,12 +32,12 @@ import static com.google.common.truth.Truth.assertWithMessage; */ public class InstrumentableFragmentCodeInspector extends CodeInspector { - private final List grandfather_notImplmentingInstrumentable; + private final List grandfather_notImplementingInstrumentable; public InstrumentableFragmentCodeInspector(List> classes) { super(classes); - grandfather_notImplmentingInstrumentable = new ArrayList<>(); - initializeGrandfatherList(grandfather_notImplmentingInstrumentable, + grandfather_notImplementingInstrumentable = new ArrayList<>(); + initializeGrandfatherList(grandfather_notImplementingInstrumentable, "grandfather_not_implementing_instrumentable"); } @@ -53,7 +53,7 @@ public class InstrumentableFragmentCodeInspector extends CodeInspector { // If it's a fragment, it must also be instrumentable. if (Fragment.class.isAssignableFrom(clazz) && !Instrumentable.class.isAssignableFrom(clazz) - && !grandfather_notImplmentingInstrumentable.contains(className)) { + && !grandfather_notImplementingInstrumentable.contains(className)) { broken.add(className); } } diff --git a/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java b/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java index dad6fa901bd..eeaa1751864 100644 --- a/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java +++ b/tests/robotests/src/com/android/settings/core/instrumentation/SharedPreferenceLoggerTest.java @@ -17,16 +17,18 @@ package com.android.settings.core.instrumentation; import android.content.Context; import android.content.SharedPreferences; + import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; -import com.android.settings.overlay.FeatureFactory; +import com.android.settings.testutils.FakeFeatureFactory; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowApplication; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; @@ -41,23 +43,21 @@ public class SharedPreferenceLoggerTest { private static final String TEST_TAG = "tag"; private static final String TEST_KEY = "key"; - @Mock - private LogWriter mLogWriter; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + private FakeFeatureFactory mFactory; private MetricsFeatureProvider mMetricsFeature; - private ShadowApplication mApplication; private SharedPreferencesLogger mSharedPrefLogger; @Before public void init() { MockitoAnnotations.initMocks(this); - mApplication = ShadowApplication.getInstance(); - Context context = mApplication.getApplicationContext(); - mMetricsFeature = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); - ((MetricsFeatureProviderImpl) mMetricsFeature).addLogWriter(mLogWriter); + FakeFeatureFactory.setupForTest(mContext); + mFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + mMetricsFeature = mFactory.metricsFeatureProvider; - mSharedPrefLogger = new SharedPreferencesLogger( - mApplication.getApplicationContext(), TEST_TAG); + mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG); } @Test @@ -71,7 +71,7 @@ public class SharedPreferenceLoggerTest { editor.putInt(TEST_KEY, 2); editor.putInt(TEST_KEY, 2); - verify(mLogWriter, times(6)).count(any(Context.class), anyString(), anyInt()); + verify(mMetricsFeature, times(6)).count(any(Context.class), anyString(), anyInt()); } @Test @@ -83,7 +83,7 @@ public class SharedPreferenceLoggerTest { editor.putBoolean(TEST_KEY, false); editor.putBoolean(TEST_KEY, false); - verify(mLogWriter, times(4)).count(any(Context.class), anyString(), anyInt()); + verify(mMetricsFeature, times(4)).count(any(Context.class), anyString(), anyInt()); } @Test @@ -95,7 +95,7 @@ public class SharedPreferenceLoggerTest { editor.putLong(TEST_KEY, 1); editor.putLong(TEST_KEY, 2); - verify(mLogWriter, times(4)).count(any(Context.class), anyString(), anyInt()); + verify(mMetricsFeature, times(4)).count(any(Context.class), anyString(), anyInt()); } @Test @@ -107,7 +107,7 @@ public class SharedPreferenceLoggerTest { editor.putFloat(TEST_KEY, 1); editor.putFloat(TEST_KEY, 2); - verify(mLogWriter, times(4)).count(any(Context.class), anyString(), anyInt()); + verify(mMetricsFeature, times(4)).count(any(Context.class), anyString(), anyInt()); } } diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java index ee260901f06..899952b2fe4 100644 --- a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java +++ b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java @@ -35,23 +35,37 @@ import static com.google.common.truth.Truth.assertWithMessage; public class SearchIndexProviderCodeInspector extends CodeInspector { private static final String TAG = "SearchCodeInspector"; - private final List notImplementingIndexableWhitelist; - private final List notImplementingIndexProviderWhitelist; + private static final String NOT_IMPLEMENTING_INDEXABLE_ERROR = + "SettingsPreferenceFragment should implement Indexable, but these are not:\n"; + private static final String NOT_CONTAINING_PROVIDER_OBJECT_ERROR = + "Indexable should have public field " + Index.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + + " but these are not:\n"; + private static final String NOT_IN_INDEXABLE_PROVIDER_REGISTRY = + "Class containing " + Index.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + " must be added to " + + SearchIndexableResources.class.getName() + " but these are not: \n"; + + private final List notImplementingIndexableGrandfatherList; + private final List notImplementingIndexProviderGrandfatherList; + private final List notInSearchIndexableRegistryGrandfatherList; public SearchIndexProviderCodeInspector(List> classes) { super(classes); - notImplementingIndexableWhitelist = new ArrayList<>(); - notImplementingIndexProviderWhitelist = new ArrayList<>(); - initializeGrandfatherList(notImplementingIndexableWhitelist, + notImplementingIndexableGrandfatherList = new ArrayList<>(); + notImplementingIndexProviderGrandfatherList = new ArrayList<>(); + notInSearchIndexableRegistryGrandfatherList = new ArrayList<>(); + initializeGrandfatherList(notImplementingIndexableGrandfatherList, "grandfather_not_implementing_indexable"); - initializeGrandfatherList(notImplementingIndexProviderWhitelist, + initializeGrandfatherList(notImplementingIndexProviderGrandfatherList, "grandfather_not_implementing_index_provider"); + initializeGrandfatherList(notInSearchIndexableRegistryGrandfatherList, + "grandfather_not_in_search_index_provider_registry"); } @Override public void run() { final Set notImplementingIndexable = new ArraySet<>(); final Set notImplementingIndexProvider = new ArraySet<>(); + final Set notInSearchProviderRegistry = new ArraySet<>(); for (Class clazz : mClasses) { if (!isConcreteSettingsClass(clazz)) { @@ -64,44 +78,59 @@ public class SearchIndexProviderCodeInspector extends CodeInspector { } // If it's a SettingsPreferenceFragment, it must also be Indexable. final boolean implementsIndexable = Indexable.class.isAssignableFrom(clazz); - if (!implementsIndexable && !notImplementingIndexableWhitelist.contains(className)) { + if (!implementsIndexable + && !notImplementingIndexableGrandfatherList.contains(className)) { notImplementingIndexable.add(className); } + final boolean hasSearchIndexProvider = hasSearchIndexProvider(clazz); // If it implements Indexable, it must also implement the index provider field. - if (implementsIndexable && !hasSearchIndexProvider(clazz) - && !notImplementingIndexProviderWhitelist.contains(className)) { + if (implementsIndexable && !hasSearchIndexProvider + && !notImplementingIndexProviderGrandfatherList.contains(className)) { notImplementingIndexProvider.add(className); } + if (hasSearchIndexProvider + && SearchIndexableResources.getResourceByName(className) == null + && !notInSearchIndexableRegistryGrandfatherList.contains(className)) { + notInSearchProviderRegistry.add(className); + } } // Build error messages - final StringBuilder indexableError = new StringBuilder( - "SettingsPreferenceFragment should implement Indexable, but these are not:\n"); - for (String c : notImplementingIndexable) { - indexableError.append(c).append("\n"); - } - final StringBuilder indexProviderError = new StringBuilder( - "Indexable should have public field " + Index.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER - + " but these are not:\n"); - for (String c : notImplementingIndexProvider) { - indexProviderError.append(c).append("\n"); - } - - assertWithMessage(indexableError.toString()) - .that(notImplementingIndexable.isEmpty()) - .isTrue(); + final String indexableError = buildErrorMessage(NOT_IMPLEMENTING_INDEXABLE_ERROR, + notImplementingIndexable); + final String indexProviderError = buildErrorMessage(NOT_CONTAINING_PROVIDER_OBJECT_ERROR, + notImplementingIndexProvider); + final String notInProviderRegistryError = + buildErrorMessage(NOT_IN_INDEXABLE_PROVIDER_REGISTRY, notInSearchProviderRegistry); + assertWithMessage(indexableError) + .that(notImplementingIndexable) + .isEmpty(); assertWithMessage(indexProviderError.toString()) - .that(notImplementingIndexProvider.isEmpty()) - .isTrue(); + .that(notImplementingIndexProvider) + .isEmpty(); + assertWithMessage(notInProviderRegistryError.toString()) + .that(notInSearchProviderRegistry) + .isEmpty(); } private boolean hasSearchIndexProvider(Class clazz) { try { final Field f = clazz.getField(Index.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER); return f != null; + } catch (NoClassDefFoundError e) { + // Cannot find class def, ignore + return true; } catch (NoSuchFieldException e) { Log.e(TAG, "error fetching search provider from class " + clazz.getName()); return false; } } + + private String buildErrorMessage(String errorSummary, Set errorClasses) { + final StringBuilder error = new StringBuilder(errorSummary); + for (String c : errorClasses) { + error.append(c).append("\n"); + } + return error.toString(); + } }