• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
18 package com.android.settings.search;
19 
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.Bundle;
24 import android.util.ArrayMap;
25 import android.util.Log;
26 
27 import com.android.internal.logging.nano.MetricsProto;
28 import com.android.settings.SettingsActivity;
29 import com.android.settings.core.BasePreferenceController;
30 import com.android.settings.core.PreferenceControllerMixin;
31 import com.android.settings.core.SubSettingLauncher;
32 import com.android.settingslib.core.AbstractPreferenceController;
33 
34 import java.lang.reflect.Field;
35 import java.util.List;
36 import java.util.Map;
37 
38 /**
39  * Utility class for {@like DatabaseIndexingManager} to handle the mapping between Payloads
40  * and Preference controllers, and managing indexable classes.
41  */
42 public class DatabaseIndexingUtils {
43 
44     private static final String TAG = "IndexingUtil";
45 
46     public static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER =
47             "SEARCH_INDEX_DATA_PROVIDER";
48 
49     /**
50      * Builds intent that launches the search destination as a sub-setting.
51      */
buildSearchResultPageIntent(Context context, String className, String key, String screenTitle)52     public static Intent buildSearchResultPageIntent(Context context, String className, String key,
53             String screenTitle) {
54         return buildSearchResultPageIntent(context, className, key, screenTitle,
55                 MetricsProto.MetricsEvent.DASHBOARD_SEARCH_RESULTS);
56     }
57 
buildSearchResultPageIntent(Context context, String className, String key, String screenTitle, int sourceMetricsCategory)58     public static Intent  buildSearchResultPageIntent(Context context, String className, String key,
59             String screenTitle, int sourceMetricsCategory) {
60         final Bundle args = new Bundle();
61         args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
62         final Intent searchDestination = new SubSettingLauncher(context)
63                 .setDestination(className)
64                 .setArguments(args)
65                 .setTitle(screenTitle)
66                 .setSourceMetricsCategory(sourceMetricsCategory)
67                 .toIntent();
68         searchDestination.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key)
69                 .setAction("com.android.settings.SEARCH_RESULT_TRAMPOLINE")
70                 .setComponent(null);
71         return searchDestination;
72     }
73 
74     /**
75      * @param className which wil provide the map between from {@link Uri}s to
76      *                  {@link PreferenceControllerMixin}
77      * @return A map between {@link Uri}s and {@link PreferenceControllerMixin}s to get the payload
78      * types for Settings.
79      */
getPayloadKeyMap(String className, Context context)80     public static Map<String, ResultPayload> getPayloadKeyMap(String className, Context context) {
81         ArrayMap<String, ResultPayload> map = new ArrayMap<>();
82         if (context == null) {
83             return map;
84         }
85 
86         final Class<?> clazz = getIndexableClass(className);
87 
88         if (clazz == null) {
89             Log.d(TAG, "SearchIndexableResource '" + className +
90                     "' should implement the " + Indexable.class.getName() + " interface!");
91             return map;
92         }
93 
94         // Will be non null only for a Local provider implementing a
95         // SEARCH_INDEX_DATA_PROVIDER field
96         final Indexable.SearchIndexProvider provider = getSearchIndexProvider(clazz);
97 
98         final List<AbstractPreferenceController> controllers =
99                 provider.getPreferenceControllers(context);
100 
101         if (controllers == null) {
102             return map;
103         }
104 
105         for (AbstractPreferenceController controller : controllers) {
106             ResultPayload payload;
107             if (controller instanceof PreferenceControllerMixin) {
108                 payload = ((PreferenceControllerMixin) controller).getResultPayload();
109 
110             } else if (controller instanceof BasePreferenceController) {
111                 payload = ((BasePreferenceController) controller).getResultPayload();
112             } else {
113                 throw new IllegalStateException(controller.getClass().getName()
114                         + " must implement " + PreferenceControllerMixin.class.getName());
115             }
116             if (payload != null) {
117                 map.put(controller.getPreferenceKey(), payload);
118             }
119         }
120 
121         return map;
122     }
123 
getIndexableClass(String className)124     public static Class<?> getIndexableClass(String className) {
125         final Class<?> clazz;
126         try {
127             clazz = Class.forName(className);
128         } catch (ClassNotFoundException e) {
129             Log.d(TAG, "Cannot find class: " + className);
130             return null;
131         }
132         return isIndexableClass(clazz) ? clazz : null;
133     }
134 
isIndexableClass(final Class<?> clazz)135     public static boolean isIndexableClass(final Class<?> clazz) {
136         return (clazz != null) && Indexable.class.isAssignableFrom(clazz);
137     }
138 
getSearchIndexProvider(final Class<?> clazz)139     public static Indexable.SearchIndexProvider getSearchIndexProvider(final Class<?> clazz) {
140         try {
141             final Field f = clazz.getField(FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER);
142             return (Indexable.SearchIndexProvider) f.get(null);
143         } catch (NoSuchFieldException e) {
144             Log.d(TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
145         } catch (SecurityException se) {
146             Log.d(TAG, "Security exception for field '" +
147                     FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
148         } catch (IllegalAccessException e) {
149             Log.d(TAG, "Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
150         } catch (IllegalArgumentException e) {
151             Log.d(TAG, "Illegal argument when accessing field '" +
152                     FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
153         }
154         return null;
155     }
156 }