1/* 2 * Copyright (C) 2007 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 17package android.server.search; 18 19import com.android.internal.content.PackageMonitor; 20 21import android.app.ISearchManager; 22import android.app.SearchManager; 23import android.app.SearchableInfo; 24import android.content.BroadcastReceiver; 25import android.content.ComponentName; 26import android.content.ContentResolver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.content.pm.ResolveInfo; 31import android.database.ContentObserver; 32import android.os.Process; 33import android.provider.Settings; 34import android.util.Log; 35 36import java.util.List; 37 38/** 39 * The search manager service handles the search UI, and maintains a registry of searchable 40 * activities. 41 */ 42public class SearchManagerService extends ISearchManager.Stub { 43 44 // general debugging support 45 private static final String TAG = "SearchManagerService"; 46 47 // Context that the service is running in. 48 private final Context mContext; 49 50 // This field is initialized lazily in getSearchables(), and then never modified. 51 private Searchables mSearchables; 52 53 private ContentObserver mGlobalSearchObserver; 54 55 /** 56 * Initializes the Search Manager service in the provided system context. 57 * Only one instance of this object should be created! 58 * 59 * @param context to use for accessing DB, window manager, etc. 60 */ 61 public SearchManagerService(Context context) { 62 mContext = context; 63 mContext.registerReceiver(new BootCompletedReceiver(), 64 new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); 65 mGlobalSearchObserver = new GlobalSearchProviderObserver( 66 mContext.getContentResolver()); 67 } 68 69 private synchronized Searchables getSearchables() { 70 if (mSearchables == null) { 71 Log.i(TAG, "Building list of searchable activities"); 72 new MyPackageMonitor().register(mContext, null, true); 73 mSearchables = new Searchables(mContext); 74 mSearchables.buildSearchableList(); 75 } 76 return mSearchables; 77 } 78 79 /** 80 * Creates the initial searchables list after boot. 81 */ 82 private final class BootCompletedReceiver extends BroadcastReceiver { 83 @Override 84 public void onReceive(Context context, Intent intent) { 85 new Thread() { 86 @Override 87 public void run() { 88 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 89 mContext.unregisterReceiver(BootCompletedReceiver.this); 90 getSearchables(); 91 } 92 }.start(); 93 } 94 } 95 96 /** 97 * Refreshes the "searchables" list when packages are added/removed. 98 */ 99 class MyPackageMonitor extends PackageMonitor { 100 101 @Override 102 public void onSomePackagesChanged() { 103 updateSearchables(); 104 } 105 106 @Override 107 public void onPackageModified(String pkg) { 108 updateSearchables(); 109 } 110 111 private void updateSearchables() { 112 // Update list of searchable activities 113 getSearchables().buildSearchableList(); 114 // Inform all listeners that the list of searchables has been updated. 115 Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); 116 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 117 mContext.sendBroadcast(intent); 118 } 119 } 120 121 class GlobalSearchProviderObserver extends ContentObserver { 122 private final ContentResolver mResolver; 123 124 public GlobalSearchProviderObserver(ContentResolver resolver) { 125 super(null); 126 mResolver = resolver; 127 mResolver.registerContentObserver( 128 Settings.Secure.getUriFor(Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY), 129 false /* notifyDescendants */, 130 this); 131 } 132 133 @Override 134 public void onChange(boolean selfChange) { 135 getSearchables().buildSearchableList(); 136 Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); 137 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 138 mContext.sendBroadcast(intent); 139 } 140 141 } 142 143 // 144 // Searchable activities API 145 // 146 147 /** 148 * Returns the SearchableInfo for a given activity. 149 * 150 * @param launchActivity The activity from which we're launching this search. 151 * @return Returns a SearchableInfo record describing the parameters of the search, 152 * or null if no searchable metadata was available. 153 */ 154 public SearchableInfo getSearchableInfo(final ComponentName launchActivity) { 155 if (launchActivity == null) { 156 Log.e(TAG, "getSearchableInfo(), activity == null"); 157 return null; 158 } 159 return getSearchables().getSearchableInfo(launchActivity); 160 } 161 162 /** 163 * Returns a list of the searchable activities that can be included in global search. 164 */ 165 public List<SearchableInfo> getSearchablesInGlobalSearch() { 166 return getSearchables().getSearchablesInGlobalSearchList(); 167 } 168 169 public List<ResolveInfo> getGlobalSearchActivities() { 170 return getSearchables().getGlobalSearchActivities(); 171 } 172 173 /** 174 * Gets the name of the global search activity. 175 */ 176 public ComponentName getGlobalSearchActivity() { 177 return getSearchables().getGlobalSearchActivity(); 178 } 179 180 /** 181 * Gets the name of the web search activity. 182 */ 183 public ComponentName getWebSearchActivity() { 184 return getSearchables().getWebSearchActivity(); 185 } 186 187} 188