118fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze/* 22078bc2358031ef3a191900d9036daf4251911c1Matthew Fritze * Copyright (C) 2017 The Android Open Source Project 318fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * 418fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * Licensed under the Apache License, Version 2.0 (the "License"); 518fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * you may not use this file except in compliance with the License. 618fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * You may obtain a copy of the License at 718fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * 818fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * http://www.apache.org/licenses/LICENSE-2.0 918fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * 1018fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * Unless required by applicable law or agreed to in writing, software 1118fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * distributed under the License is distributed on an "AS IS" BASIS, 1218fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1318fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * See the License for the specific language governing permissions and 1418fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * limitations under the License. 1518fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * 1618fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze */ 1718fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 182078bc2358031ef3a191900d9036daf4251911c1Matthew Fritzepackage com.android.settings.search; 1918fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 2018fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritzeimport android.content.Context; 213746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritzeimport android.content.Intent; 221dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritzeimport android.net.Uri; 233746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritzeimport android.os.Bundle; 241dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritzeimport android.util.ArrayMap; 2518fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritzeimport android.util.Log; 2618fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 273746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritzeimport com.android.internal.logging.nano.MetricsProto; 283746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritzeimport com.android.settings.SettingsActivity; 293a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritzeimport com.android.settings.core.BasePreferenceController; 301d583e125faf3ae4c9cd82636d8f3ecf1cdec3aaTony Mantlerimport com.android.settings.core.PreferenceControllerMixin; 311f6d24a991a688110fdd75ac852c51bd882c81e9Fan Zhangimport com.android.settings.core.SubSettingLauncher; 321d583e125faf3ae4c9cd82636d8f3ecf1cdec3aaTony Mantlerimport com.android.settingslib.core.AbstractPreferenceController; 3318fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 3418fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritzeimport java.lang.reflect.Field; 351dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritzeimport java.util.List; 361dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritzeimport java.util.Map; 3718fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 3818fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze/** 3918fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * Utility class for {@like DatabaseIndexingManager} to handle the mapping between Payloads 4018fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze * and Preference controllers, and managing indexable classes. 4118fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze */ 4218fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritzepublic class DatabaseIndexingUtils { 4318fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 441dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze private static final String TAG = "IndexingUtil"; 4518fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 467fddfebf6ceb7bdf2fd0c63e878594f9b98122aeMatthew Fritze public static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER = 4718fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze "SEARCH_INDEX_DATA_PROVIDER"; 4818fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 491dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze /** 5025f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang * Builds intent that launches the search destination as a sub-setting. 513746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritze */ 5225f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang public static Intent buildSearchResultPageIntent(Context context, String className, String key, 533746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritze String screenTitle) { 5425f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang return buildSearchResultPageIntent(context, className, key, screenTitle, 5525f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang MetricsProto.MetricsEvent.DASHBOARD_SEARCH_RESULTS); 5625f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang } 5725f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang 5825f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang public static Intent buildSearchResultPageIntent(Context context, String className, String key, 5925f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang String screenTitle, int sourceMetricsCategory) { 603746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritze final Bundle args = new Bundle(); 613746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritze args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key); 621f6d24a991a688110fdd75ac852c51bd882c81e9Fan Zhang final Intent searchDestination = new SubSettingLauncher(context) 631f6d24a991a688110fdd75ac852c51bd882c81e9Fan Zhang .setDestination(className) 641f6d24a991a688110fdd75ac852c51bd882c81e9Fan Zhang .setArguments(args) 651f6d24a991a688110fdd75ac852c51bd882c81e9Fan Zhang .setTitle(screenTitle) 661f6d24a991a688110fdd75ac852c51bd882c81e9Fan Zhang .setSourceMetricsCategory(sourceMetricsCategory) 671f6d24a991a688110fdd75ac852c51bd882c81e9Fan Zhang .toIntent(); 688068fda0599945b9f8f3029d420f813da900435dFan Zhang searchDestination.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key) 698068fda0599945b9f8f3029d420f813da900435dFan Zhang .setAction("com.android.settings.SEARCH_RESULT_TRAMPOLINE") 708068fda0599945b9f8f3029d420f813da900435dFan Zhang .setComponent(null); 7125f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang return searchDestination; 723746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritze } 733746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritze 743746d9e25f2805b16b69eb7420a629fc66f6a788Matthew Fritze /** 751dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze * @param className which wil provide the map between from {@link Uri}s to 7625f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang * {@link PreferenceControllerMixin} 771d583e125faf3ae4c9cd82636d8f3ecf1cdec3aaTony Mantler * @return A map between {@link Uri}s and {@link PreferenceControllerMixin}s to get the payload 781dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze * types for Settings. 791dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze */ 80917f101899e0584edde44567f7939a563cc2dc05Fan Zhang public static Map<String, ResultPayload> getPayloadKeyMap(String className, Context context) { 813a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze ArrayMap<String, ResultPayload> map = new ArrayMap<>(); 8265fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze if (context == null) { 833a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze return map; 8465fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze } 8565fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze 861dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze final Class<?> clazz = getIndexableClass(className); 871dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze 881dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze if (clazz == null) { 891dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze Log.d(TAG, "SearchIndexableResource '" + className + 901dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze "' should implement the " + Indexable.class.getName() + " interface!"); 913a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze return map; 921dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze } 931dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze 941dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze // Will be non null only for a Local provider implementing a 951dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze // SEARCH_INDEX_DATA_PROVIDER field 961dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze final Indexable.SearchIndexProvider provider = getSearchIndexProvider(clazz); 9765fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze 98917f101899e0584edde44567f7939a563cc2dc05Fan Zhang final List<AbstractPreferenceController> controllers = 99aed8994f6f50a6bc0adfaa92b499ebde3a8f2a76Fan Zhang provider.getPreferenceControllers(context); 1001dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze 10125f29bf126b71b94ac180b2c6664b22682886a12Fan Zhang if (controllers == null) { 1023a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze return map; 1031dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze } 1041dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze 1051d583e125faf3ae4c9cd82636d8f3ecf1cdec3aaTony Mantler for (AbstractPreferenceController controller : controllers) { 1063a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze ResultPayload payload; 1071d583e125faf3ae4c9cd82636d8f3ecf1cdec3aaTony Mantler if (controller instanceof PreferenceControllerMixin) { 1083a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze payload = ((PreferenceControllerMixin) controller).getResultPayload(); 1093a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze 1103a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze } else if (controller instanceof BasePreferenceController) { 1113a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze payload = ((BasePreferenceController) controller).getResultPayload(); 1121d583e125faf3ae4c9cd82636d8f3ecf1cdec3aaTony Mantler } else { 1131d583e125faf3ae4c9cd82636d8f3ecf1cdec3aaTony Mantler throw new IllegalStateException(controller.getClass().getName() 1141d583e125faf3ae4c9cd82636d8f3ecf1cdec3aaTony Mantler + " must implement " + PreferenceControllerMixin.class.getName()); 1151d583e125faf3ae4c9cd82636d8f3ecf1cdec3aaTony Mantler } 1163a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze if (payload != null) { 1173a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze map.put(controller.getPreferenceKey(), payload); 1183a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze } 1191dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze } 1201dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze 1211dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze return map; 1221dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze } 1231dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze 12418fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze public static Class<?> getIndexableClass(String className) { 12518fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze final Class<?> clazz; 12618fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze try { 12718fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze clazz = Class.forName(className); 12818fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } catch (ClassNotFoundException e) { 1291dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze Log.d(TAG, "Cannot find class: " + className); 13018fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze return null; 13118fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } 13218fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze return isIndexableClass(clazz) ? clazz : null; 13318fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } 13418fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 13518fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze public static boolean isIndexableClass(final Class<?> clazz) { 13618fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze return (clazz != null) && Indexable.class.isAssignableFrom(clazz); 13718fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } 13818fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze 13918fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze public static Indexable.SearchIndexProvider getSearchIndexProvider(final Class<?> clazz) { 14018fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze try { 14118fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze final Field f = clazz.getField(FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER); 14218fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze return (Indexable.SearchIndexProvider) f.get(null); 14318fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } catch (NoSuchFieldException e) { 1441dec073528352fcf036c5ba52d5d535ba32c472cMatthew Fritze Log.d(TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'"); 14518fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } catch (SecurityException se) { 14665fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze Log.d(TAG, "Security exception for field '" + 14765fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'"); 14818fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } catch (IllegalAccessException e) { 14965fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze Log.d(TAG, "Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'"); 15018fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } catch (IllegalArgumentException e) { 15165fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze Log.d(TAG, "Illegal argument when accessing field '" + 15265fb01b520a8eace6c5670c06e6ba5f81ab609afMatthew Fritze FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'"); 15318fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } 15418fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze return null; 15518fcb08308b50e73bbb380d3c9fc3261a4df7c83Matthew Fritze } 1563a4168360b94ac3fdefeafa2f4a39169b6905c1bMatthew Fritze}