1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.search; 18 19 import android.annotation.XmlRes; 20 import android.content.Context; 21 import android.content.res.XmlResourceParser; 22 import android.provider.SearchIndexableResource; 23 import android.support.annotation.CallSuper; 24 import android.support.annotation.VisibleForTesting; 25 import android.text.TextUtils; 26 import android.util.AttributeSet; 27 import android.util.Log; 28 import android.util.Xml; 29 30 import com.android.settings.core.BasePreferenceController; 31 import com.android.settings.core.PreferenceControllerListHelper; 32 import com.android.settings.core.PreferenceControllerMixin; 33 import com.android.settings.core.PreferenceXmlParserUtils; 34 import com.android.settingslib.core.AbstractPreferenceController; 35 36 import org.xmlpull.v1.XmlPullParser; 37 import org.xmlpull.v1.XmlPullParserException; 38 39 import java.io.IOException; 40 import java.util.ArrayList; 41 import java.util.List; 42 43 /** 44 * A basic SearchIndexProvider that returns no data to index. 45 */ 46 public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider { 47 48 private static final String TAG = "BaseSearchIndex"; 49 BaseSearchIndexProvider()50 public BaseSearchIndexProvider() { 51 } 52 53 @Override getXmlResourcesToIndex(Context context, boolean enabled)54 public List<SearchIndexableResource> getXmlResourcesToIndex(Context context, boolean enabled) { 55 return null; 56 } 57 58 @Override getRawDataToIndex(Context context, boolean enabled)59 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { 60 return null; 61 } 62 63 @Override 64 @CallSuper getNonIndexableKeys(Context context)65 public List<String> getNonIndexableKeys(Context context) { 66 if (!isPageSearchEnabled(context)) { 67 // Entire page should be suppressed, mark all keys from this page as non-indexable. 68 return getNonIndexableKeysFromXml(context); 69 } 70 final List<AbstractPreferenceController> controllers = getPreferenceControllers(context); 71 if (controllers != null && !controllers.isEmpty()) { 72 final List<String> nonIndexableKeys = new ArrayList<>(); 73 for (AbstractPreferenceController controller : controllers) { 74 if (controller instanceof PreferenceControllerMixin) { 75 ((PreferenceControllerMixin) controller) 76 .updateNonIndexableKeys(nonIndexableKeys); 77 } else if (controller instanceof BasePreferenceController) { 78 ((BasePreferenceController) controller).updateNonIndexableKeys( 79 nonIndexableKeys); 80 } else { 81 Log.e(TAG, controller.getClass().getName() 82 + " must implement " + PreferenceControllerMixin.class.getName() 83 + " treating the key non-indexable"); 84 nonIndexableKeys.add(controller.getPreferenceKey()); 85 } 86 } 87 return nonIndexableKeys; 88 } else { 89 return new ArrayList<>(); 90 } 91 } 92 93 @Override getPreferenceControllers(Context context)94 public List<AbstractPreferenceController> getPreferenceControllers(Context context) { 95 final List<AbstractPreferenceController> controllersFromCode = 96 createPreferenceControllers(context); 97 final List<SearchIndexableResource> res = getXmlResourcesToIndex(context, true); 98 if (res == null || res.isEmpty()) { 99 return controllersFromCode; 100 } 101 List<BasePreferenceController> controllersFromXml = new ArrayList<>(); 102 for (SearchIndexableResource sir : res) { 103 controllersFromXml.addAll(PreferenceControllerListHelper 104 .getPreferenceControllersFromXml(context, sir.xmlResId)); 105 } 106 controllersFromXml = PreferenceControllerListHelper.filterControllers(controllersFromXml, 107 controllersFromCode); 108 final List<AbstractPreferenceController> allControllers = new ArrayList<>(); 109 if (controllersFromCode != null) { 110 allControllers.addAll(controllersFromCode); 111 } 112 allControllers.addAll(controllersFromXml); 113 return allControllers; 114 } 115 116 /** 117 * Creates a list of {@link AbstractPreferenceController} programatically. 118 * <p/> 119 * This list should create controllers that are not defined in xml as a Slice controller. 120 */ createPreferenceControllers(Context context)121 public List<AbstractPreferenceController> createPreferenceControllers(Context context) { 122 return null; 123 } 124 125 /** 126 * Returns true if the page should be considered in search query. If return false, entire page 127 * will be suppressed during search query. 128 */ isPageSearchEnabled(Context context)129 protected boolean isPageSearchEnabled(Context context) { 130 return true; 131 } 132 getNonIndexableKeysFromXml(Context context)133 private List<String> getNonIndexableKeysFromXml(Context context) { 134 final List<SearchIndexableResource> resources = getXmlResourcesToIndex( 135 context, true /* not used*/); 136 if (resources == null || resources.isEmpty()) { 137 return new ArrayList<>(); 138 } 139 final List<String> nonIndexableKeys = new ArrayList<>(); 140 for (SearchIndexableResource res : resources) { 141 nonIndexableKeys.addAll(getNonIndexableKeysFromXml(context, res.xmlResId)); 142 } 143 return nonIndexableKeys; 144 } 145 146 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) getNonIndexableKeysFromXml(Context context, @XmlRes int xmlResId)147 public List<String> getNonIndexableKeysFromXml(Context context, @XmlRes int xmlResId) { 148 final List<String> nonIndexableKeys = new ArrayList<>(); 149 final XmlResourceParser parser = context.getResources().getXml(xmlResId); 150 final AttributeSet attrs = Xml.asAttributeSet(parser); 151 try { 152 while (parser.next() != XmlPullParser.END_DOCUMENT) { 153 final String key = PreferenceXmlParserUtils.getDataKey(context, attrs); 154 if (!TextUtils.isEmpty(key)) { 155 nonIndexableKeys.add(key); 156 } 157 } 158 } catch (IOException | XmlPullParserException e) { 159 Log.w(TAG, "Error parsing non-indexable from xml " + xmlResId); 160 } 161 return nonIndexableKeys; 162 } 163 164 } 165