• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY;
20 import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SEARCHABLE;
21 import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_INCLUDE_PREF_SCREEN;
22 import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_NEED_KEY;
23 import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_NEED_SEARCHABLE;
24 
25 import android.annotation.XmlRes;
26 import android.content.Context;
27 import android.os.Bundle;
28 import android.provider.SearchIndexableResource;
29 import android.util.Log;
30 
31 import androidx.annotation.CallSuper;
32 import androidx.annotation.VisibleForTesting;
33 
34 import com.android.settings.core.BasePreferenceController;
35 import com.android.settings.core.PreferenceControllerListHelper;
36 import com.android.settings.core.PreferenceControllerMixin;
37 import com.android.settings.core.PreferenceXmlParserUtils;
38 import com.android.settingslib.core.AbstractPreferenceController;
39 
40 import org.xmlpull.v1.XmlPullParserException;
41 
42 import java.io.IOException;
43 import java.util.ArrayList;
44 import java.util.List;
45 
46 /**
47  * A basic SearchIndexProvider that returns no data to index.
48  */
49 public class BaseSearchIndexProvider implements Indexable.SearchIndexProvider {
50 
51     private static final String TAG = "BaseSearchIndex";
52 
BaseSearchIndexProvider()53     public BaseSearchIndexProvider() {
54     }
55 
56     @Override
getXmlResourcesToIndex(Context context, boolean enabled)57     public List<SearchIndexableResource> getXmlResourcesToIndex(Context context, boolean enabled) {
58         return null;
59     }
60 
61     @Override
getRawDataToIndex(Context context, boolean enabled)62     public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
63         return null;
64     }
65 
66     @Override
67     @CallSuper
getNonIndexableKeys(Context context)68     public List<String> getNonIndexableKeys(Context context) {
69         if (!isPageSearchEnabled(context)) {
70             // Entire page should be suppressed, mark all keys from this page as non-indexable.
71             return getNonIndexableKeysFromXml(context, true /* suppressAllPage */);
72         }
73         final List<String> nonIndexableKeys = new ArrayList<>();
74         nonIndexableKeys.addAll(getNonIndexableKeysFromXml(context, false /* suppressAllPage */));
75         final List<AbstractPreferenceController> controllers = getPreferenceControllers(context);
76         if (controllers != null && !controllers.isEmpty()) {
77             for (AbstractPreferenceController controller : controllers) {
78                 if (controller instanceof PreferenceControllerMixin) {
79                     ((PreferenceControllerMixin) controller)
80                             .updateNonIndexableKeys(nonIndexableKeys);
81                 } else if (controller instanceof BasePreferenceController) {
82                     ((BasePreferenceController) controller).updateNonIndexableKeys(
83                             nonIndexableKeys);
84                 } else {
85                     Log.e(TAG, controller.getClass().getName()
86                             + " must implement " + PreferenceControllerMixin.class.getName()
87                             + " treating the key non-indexable");
88                     nonIndexableKeys.add(controller.getPreferenceKey());
89                 }
90             }
91         }
92         return nonIndexableKeys;
93     }
94 
95     @Override
getPreferenceControllers(Context context)96     public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
97         final List<AbstractPreferenceController> controllersFromCode =
98                 createPreferenceControllers(context);
99         final List<SearchIndexableResource> res = getXmlResourcesToIndex(context, true);
100         if (res == null || res.isEmpty()) {
101             return controllersFromCode;
102         }
103         List<BasePreferenceController> controllersFromXml = new ArrayList<>();
104         for (SearchIndexableResource sir : res) {
105             controllersFromXml.addAll(PreferenceControllerListHelper
106                     .getPreferenceControllersFromXml(context, sir.xmlResId));
107         }
108         controllersFromXml = PreferenceControllerListHelper.filterControllers(controllersFromXml,
109                 controllersFromCode);
110         final List<AbstractPreferenceController> allControllers = new ArrayList<>();
111         if (controllersFromCode != null) {
112             allControllers.addAll(controllersFromCode);
113         }
114         allControllers.addAll(controllersFromXml);
115         return allControllers;
116     }
117 
118     /**
119      * Creates a list of {@link AbstractPreferenceController} programatically.
120      * <p/>
121      * This list should create controllers that are not defined in xml as a Slice controller.
122      */
createPreferenceControllers(Context context)123     public List<AbstractPreferenceController> createPreferenceControllers(Context context) {
124         return null;
125     }
126 
127     /**
128      * Returns true if the page should be considered in search query. If return false, entire page
129      * will be suppressed during search query.
130      */
isPageSearchEnabled(Context context)131     protected boolean isPageSearchEnabled(Context context) {
132         return true;
133     }
134 
135     /**
136      * Get all non-indexable keys from xml. If {@param suppressAllPage} is set, all keys are
137      * considered non-indexable. Otherwise, only keys with searchable="false" are included.
138      */
getNonIndexableKeysFromXml(Context context, boolean suppressAllPage)139     private List<String> getNonIndexableKeysFromXml(Context context, boolean suppressAllPage) {
140         final List<SearchIndexableResource> resources = getXmlResourcesToIndex(
141                 context, true /* not used*/);
142         if (resources == null || resources.isEmpty()) {
143             return new ArrayList<>();
144         }
145         final List<String> nonIndexableKeys = new ArrayList<>();
146         for (SearchIndexableResource res : resources) {
147             nonIndexableKeys.addAll(
148                     getNonIndexableKeysFromXml(context, res.xmlResId, suppressAllPage));
149         }
150         return nonIndexableKeys;
151     }
152 
153     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
getNonIndexableKeysFromXml(Context context, @XmlRes int xmlResId, boolean suppressAllPage)154     public List<String> getNonIndexableKeysFromXml(Context context, @XmlRes int xmlResId,
155             boolean suppressAllPage) {
156         return getKeysFromXml(context, xmlResId, suppressAllPage);
157     }
158 
getKeysFromXml(Context context, @XmlRes int xmlResId, boolean suppressAllPage)159     private List<String> getKeysFromXml(Context context, @XmlRes int xmlResId,
160             boolean suppressAllPage) {
161         final List<String> keys = new ArrayList<>();
162         try {
163             final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(context,
164                     xmlResId, FLAG_NEED_KEY | FLAG_INCLUDE_PREF_SCREEN | FLAG_NEED_SEARCHABLE);
165             for (Bundle bundle : metadata) {
166                 if (suppressAllPage || !bundle.getBoolean(METADATA_SEARCHABLE, true)) {
167                     keys.add(bundle.getString(METADATA_KEY));
168                 }
169             }
170         } catch (IOException | XmlPullParserException e) {
171             Log.w(TAG, "Error parsing non-indexable from xml " + xmlResId);
172         }
173         return keys;
174     }
175 }
176