OSDN Git Service

Import translations. DO NOT MERGE
[android-x86/packages-apps-Settings.git] / src / com / android / settings / dashboard / SearchResultsSummary.java
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.dashboard;
18
19 import android.app.Fragment;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.PackageManager;
24 import android.content.res.Resources;
25 import android.database.Cursor;
26 import android.graphics.drawable.Drawable;
27 import android.os.AsyncTask;
28 import android.os.Bundle;
29 import android.text.TextUtils;
30 import android.util.Log;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.AdapterView;
35 import android.widget.BaseAdapter;
36 import android.widget.ImageView;
37 import android.widget.ListView;
38 import android.widget.SearchView;
39 import android.widget.TextView;
40 import com.android.settings.R;
41 import com.android.settings.SettingsActivity;
42 import com.android.settings.Utils;
43 import com.android.settings.search.Index;
44
45 import java.util.HashMap;
46
47 public class SearchResultsSummary extends Fragment {
48
49     private static final String LOG_TAG = "SearchResultsSummary";
50
51     private static final String EMPTY_QUERY = "";
52     private static char ELLIPSIS = '\u2026';
53
54     private static final String SAVE_KEY_SHOW_RESULTS = ":settings:show_results";
55
56     private SearchView mSearchView;
57
58     private ListView mResultsListView;
59     private SearchResultsAdapter mResultsAdapter;
60     private UpdateSearchResultsTask mUpdateSearchResultsTask;
61
62     private ListView mSuggestionsListView;
63     private SuggestionsAdapter mSuggestionsAdapter;
64     private UpdateSuggestionsTask mUpdateSuggestionsTask;
65
66     private ViewGroup mLayoutSuggestions;
67     private ViewGroup mLayoutResults;
68
69     private String mQuery;
70
71     private boolean mShowResults;
72
73     /**
74      * A basic AsyncTask for updating the query results cursor
75      */
76     private class UpdateSearchResultsTask extends AsyncTask<String, Void, Cursor> {
77         @Override
78         protected Cursor doInBackground(String... params) {
79             return Index.getInstance(getActivity()).search(params[0]);
80         }
81
82         @Override
83         protected void onPostExecute(Cursor cursor) {
84             if (!isCancelled()) {
85                 setResultsCursor(cursor);
86                 setResultsVisibility(cursor.getCount() > 0);
87             } else if (cursor != null) {
88                 cursor.close();
89             }
90         }
91     }
92
93     /**
94      * A basic AsyncTask for updating the suggestions cursor
95      */
96     private class UpdateSuggestionsTask extends AsyncTask<String, Void, Cursor> {
97         @Override
98         protected Cursor doInBackground(String... params) {
99             return Index.getInstance(getActivity()).getSuggestions(params[0]);
100         }
101
102         @Override
103         protected void onPostExecute(Cursor cursor) {
104             if (!isCancelled()) {
105                 setSuggestionsCursor(cursor);
106                 setSuggestionsVisibility(cursor.getCount() > 0);
107             } else if (cursor != null) {
108                 cursor.close();
109             }
110         }
111     }
112
113     @Override
114     public void onCreate(Bundle savedInstanceState) {
115         super.onCreate(savedInstanceState);
116
117         mResultsAdapter = new SearchResultsAdapter(getActivity());
118         mSuggestionsAdapter = new SuggestionsAdapter(getActivity());
119
120         if (savedInstanceState != null) {
121             mShowResults = savedInstanceState.getBoolean(SAVE_KEY_SHOW_RESULTS);
122         }
123     }
124
125     @Override
126     public void onSaveInstanceState(Bundle outState) {
127         super.onSaveInstanceState(outState);
128
129         outState.putBoolean(SAVE_KEY_SHOW_RESULTS, mShowResults);
130     }
131
132     @Override
133     public void onStop() {
134         super.onStop();
135
136         clearSuggestions();
137         clearResults();
138     }
139
140     @Override
141     public void onDestroy() {
142         mResultsListView = null;
143         mResultsAdapter = null;
144         mUpdateSearchResultsTask = null;
145
146         mSuggestionsListView = null;
147         mSuggestionsAdapter = null;
148         mUpdateSuggestionsTask = null;
149
150         mSearchView = null;
151
152         super.onDestroy();
153     }
154
155     @Override
156     public View onCreateView(LayoutInflater inflater, ViewGroup container,
157                              Bundle savedInstanceState) {
158
159         final View view = inflater.inflate(R.layout.search_panel, container, false);
160
161         mLayoutSuggestions = (ViewGroup) view.findViewById(R.id.layout_suggestions);
162         mLayoutResults = (ViewGroup) view.findViewById(R.id.layout_results);
163
164         mResultsListView = (ListView) view.findViewById(R.id.list_results);
165         mResultsListView.setAdapter(mResultsAdapter);
166         mResultsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
167             @Override
168             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
169                 // We have a header, so we need to decrement the position by one
170                 position--;
171
172                 // Some Monkeys could create a case where they were probably clicking on the
173                 // List Header and thus the position passed was "0" and then by decrement was "-1"
174                 if (position < 0) {
175                     return;
176                 }
177
178                 final Cursor cursor = mResultsAdapter.mCursor;
179                 cursor.moveToPosition(position);
180
181                 final String className = cursor.getString(Index.COLUMN_INDEX_CLASS_NAME);
182                 final String screenTitle = cursor.getString(Index.COLUMN_INDEX_SCREEN_TITLE);
183                 final String action = cursor.getString(Index.COLUMN_INDEX_INTENT_ACTION);
184                 final String key = cursor.getString(Index.COLUMN_INDEX_KEY);
185
186                 final SettingsActivity sa = (SettingsActivity) getActivity();
187                 sa.needToRevertToInitialFragment();
188
189                 if (TextUtils.isEmpty(action)) {
190                     Bundle args = new Bundle();
191                     args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
192
193                     Utils.startWithFragment(sa, className, args, null, 0, -1, screenTitle);
194                 } else {
195                     final Intent intent = new Intent(action);
196
197                     final String targetPackage = cursor.getString(
198                             Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
199                     final String targetClass = cursor.getString(
200                             Index.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS);
201                     if (!TextUtils.isEmpty(targetPackage) && !TextUtils.isEmpty(targetClass)) {
202                         final ComponentName component =
203                                 new ComponentName(targetPackage, targetClass);
204                         intent.setComponent(component);
205                     }
206                     intent.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
207
208                     sa.startActivity(intent);
209                 }
210
211                 saveQueryToDatabase();
212             }
213         });
214         mResultsListView.addHeaderView(
215                 LayoutInflater.from(getActivity()).inflate(
216                         R.layout.search_panel_results_header, mResultsListView, false),
217                 null, false);
218
219         mSuggestionsListView = (ListView) view.findViewById(R.id.list_suggestions);
220         mSuggestionsListView.setAdapter(mSuggestionsAdapter);
221         mSuggestionsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
222             @Override
223             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
224                 // We have a header, so we need to decrement the position by one
225                 position--;
226                 // Some Monkeys could create a case where they were probably clicking on the
227                 // List Header and thus the position passed was "0" and then by decrement was "-1"
228                 if (position < 0) {
229                     return;
230                 }
231                 final Cursor cursor = mSuggestionsAdapter.mCursor;
232                 cursor.moveToPosition(position);
233
234                 mShowResults = true;
235                 mQuery = cursor.getString(0);
236                 mSearchView.setQuery(mQuery, false);
237             }
238         });
239         mSuggestionsListView.addHeaderView(
240                 LayoutInflater.from(getActivity()).inflate(
241                         R.layout.search_panel_suggestions_header, mSuggestionsListView, false),
242                 null, false);
243
244         return view;
245     }
246
247     @Override
248     public void onResume() {
249         super.onResume();
250
251         if (!mShowResults) {
252             showSomeSuggestions();
253         }
254     }
255
256     public void setSearchView(SearchView searchView) {
257         mSearchView = searchView;
258     }
259
260     private void setSuggestionsVisibility(boolean visible) {
261         if (mLayoutSuggestions != null) {
262             mLayoutSuggestions.setVisibility(visible ? View.VISIBLE : View.GONE);
263         }
264     }
265
266     private void setResultsVisibility(boolean visible) {
267         if (mLayoutResults != null) {
268             mLayoutResults.setVisibility(visible ? View.VISIBLE : View.GONE);
269         }
270     }
271
272     private void saveQueryToDatabase() {
273         Index.getInstance(getActivity()).addSavedQuery(mQuery);
274     }
275
276     public boolean onQueryTextSubmit(String query) {
277         mQuery = getFilteredQueryString(query);
278         mShowResults = true;
279         setSuggestionsVisibility(false);
280         updateSearchResults();
281         saveQueryToDatabase();
282         return true;
283     }
284
285     public boolean onQueryTextChange(String query) {
286         final String newQuery = getFilteredQueryString(query);
287
288         mQuery = newQuery;
289
290         if (TextUtils.isEmpty(mQuery)) {
291             mShowResults = false;
292             setResultsVisibility(false);
293             updateSuggestions();
294         } else {
295             mShowResults = true;
296             setSuggestionsVisibility(false);
297             updateSearchResults();
298         }
299
300         return true;
301     }
302
303     public void showSomeSuggestions() {
304         setResultsVisibility(false);
305         mQuery = EMPTY_QUERY;
306         updateSuggestions();
307     }
308
309     private void clearSuggestions() {
310         if (mUpdateSuggestionsTask != null) {
311             mUpdateSuggestionsTask.cancel(false);
312             mUpdateSuggestionsTask = null;
313         }
314         setSuggestionsCursor(null);
315     }
316
317     private void setSuggestionsCursor(Cursor cursor) {
318         if (mSuggestionsAdapter == null) {
319             return;
320         }
321         Cursor oldCursor = mSuggestionsAdapter.swapCursor(cursor);
322         if (oldCursor != null) {
323             oldCursor.close();
324         }
325     }
326
327     private void clearResults() {
328         if (mUpdateSearchResultsTask != null) {
329             mUpdateSearchResultsTask.cancel(false);
330             mUpdateSearchResultsTask = null;
331         }
332         setResultsCursor(null);
333     }
334
335     private void setResultsCursor(Cursor cursor) {
336         if (mResultsAdapter == null) {
337             return;
338         }
339         Cursor oldCursor = mResultsAdapter.swapCursor(cursor);
340         if (oldCursor != null) {
341             oldCursor.close();
342         }
343     }
344
345     private String getFilteredQueryString(CharSequence query) {
346         if (query == null) {
347             return null;
348         }
349         final StringBuilder filtered = new StringBuilder();
350         for (int n = 0; n < query.length(); n++) {
351             char c = query.charAt(n);
352             if (!Character.isLetterOrDigit(c) && !Character.isSpaceChar(c)) {
353                 continue;
354             }
355             filtered.append(c);
356         }
357         return filtered.toString();
358     }
359
360     private void clearAllTasks() {
361         if (mUpdateSearchResultsTask != null) {
362             mUpdateSearchResultsTask.cancel(false);
363             mUpdateSearchResultsTask = null;
364         }
365         if (mUpdateSuggestionsTask != null) {
366             mUpdateSuggestionsTask.cancel(false);
367             mUpdateSuggestionsTask = null;
368         }
369     }
370
371     private void updateSuggestions() {
372         clearAllTasks();
373         if (mQuery == null) {
374             setSuggestionsCursor(null);
375         } else {
376             mUpdateSuggestionsTask = new UpdateSuggestionsTask();
377             mUpdateSuggestionsTask.execute(mQuery);
378         }
379     }
380
381     private void updateSearchResults() {
382         clearAllTasks();
383         if (TextUtils.isEmpty(mQuery)) {
384             setResultsVisibility(false);
385             setResultsCursor(null);
386         } else {
387             mUpdateSearchResultsTask = new UpdateSearchResultsTask();
388             mUpdateSearchResultsTask.execute(mQuery);
389         }
390     }
391
392     private static class SuggestionItem {
393         public String query;
394
395         public SuggestionItem(String query) {
396             this.query = query;
397         }
398     }
399
400     private static class SuggestionsAdapter extends BaseAdapter {
401
402         private static final int COLUMN_SUGGESTION_QUERY = 0;
403         private static final int COLUMN_SUGGESTION_TIMESTAMP = 1;
404
405         private Context mContext;
406         private Cursor mCursor;
407         private LayoutInflater mInflater;
408         private boolean mDataValid = false;
409
410         public SuggestionsAdapter(Context context) {
411             mContext = context;
412             mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
413             mDataValid = false;
414         }
415
416         public Cursor swapCursor(Cursor newCursor) {
417             if (newCursor == mCursor) {
418                 return null;
419             }
420             Cursor oldCursor = mCursor;
421             mCursor = newCursor;
422             if (newCursor != null) {
423                 mDataValid = true;
424                 notifyDataSetChanged();
425             } else {
426                 mDataValid = false;
427                 notifyDataSetInvalidated();
428             }
429             return oldCursor;
430         }
431
432         @Override
433         public int getCount() {
434             if (!mDataValid || mCursor == null || mCursor.isClosed()) return 0;
435             return mCursor.getCount();
436         }
437
438         @Override
439         public Object getItem(int position) {
440             if (mDataValid && mCursor.moveToPosition(position)) {
441                 final String query = mCursor.getString(COLUMN_SUGGESTION_QUERY);
442
443                 return new SuggestionItem(query);
444             }
445             return null;
446         }
447
448         @Override
449         public long getItemId(int position) {
450             return 0;
451         }
452
453         @Override
454         public View getView(int position, View convertView, ViewGroup parent) {
455             if (!mDataValid && convertView == null) {
456                 throw new IllegalStateException(
457                         "this should only be called when the cursor is valid");
458             }
459             if (!mCursor.moveToPosition(position)) {
460                 throw new IllegalStateException("couldn't move cursor to position " + position);
461             }
462
463             View view;
464
465             if (convertView == null) {
466                 view = mInflater.inflate(R.layout.search_suggestion_item, parent, false);
467             } else {
468                 view = convertView;
469             }
470
471             TextView query = (TextView) view.findViewById(R.id.title);
472
473             SuggestionItem item = (SuggestionItem) getItem(position);
474             query.setText(item.query);
475
476             return view;
477         }
478     }
479
480     private static class SearchResult {
481         public Context context;
482         public String title;
483         public String summaryOn;
484         public String summaryOff;
485         public String entries;
486         public int iconResId;
487         public String key;
488
489         public SearchResult(Context context, String title, String summaryOn, String summaryOff,
490                             String entries, int iconResId, String key) {
491             this.context = context;
492             this.title = title;
493             this.summaryOn = summaryOn;
494             this.summaryOff = summaryOff;
495             this.entries = entries;
496             this.iconResId = iconResId;
497             this.key = key;
498         }
499     }
500
501     private static class SearchResultsAdapter extends BaseAdapter {
502
503         private Context mContext;
504         private Cursor mCursor;
505         private LayoutInflater mInflater;
506         private boolean mDataValid;
507         private HashMap<String, Context> mContextMap = new HashMap<String, Context>();
508
509         private static final String PERCENT_RECLACE = "%s";
510         private static final String DOLLAR_REPLACE = "$s";
511
512         public SearchResultsAdapter(Context context) {
513             mContext = context;
514             mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
515             mDataValid = false;
516         }
517
518         public Cursor swapCursor(Cursor newCursor) {
519             if (newCursor == mCursor) {
520                 return null;
521             }
522             Cursor oldCursor = mCursor;
523             mCursor = newCursor;
524             if (newCursor != null) {
525                 mDataValid = true;
526                 notifyDataSetChanged();
527             } else {
528                 mDataValid = false;
529                 notifyDataSetInvalidated();
530             }
531             return oldCursor;
532         }
533
534         @Override
535         public int getCount() {
536             if (!mDataValid || mCursor == null || mCursor.isClosed()) return 0;
537             return mCursor.getCount();
538         }
539
540         @Override
541         public Object getItem(int position) {
542             if (mDataValid && mCursor.moveToPosition(position)) {
543                 final String title = mCursor.getString(Index.COLUMN_INDEX_TITLE);
544                 final String summaryOn = mCursor.getString(Index.COLUMN_INDEX_SUMMARY_ON);
545                 final String summaryOff = mCursor.getString(Index.COLUMN_INDEX_SUMMARY_OFF);
546                 final String entries = mCursor.getString(Index.COLUMN_INDEX_ENTRIES);
547                 final String iconResStr = mCursor.getString(Index.COLUMN_INDEX_ICON);
548                 final String className = mCursor.getString(
549                         Index.COLUMN_INDEX_CLASS_NAME);
550                 final String packageName = mCursor.getString(
551                         Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
552                 final String key = mCursor.getString(
553                         Index.COLUMN_INDEX_KEY);
554
555                 Context packageContext;
556                 if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(packageName)) {
557                     packageContext = mContextMap.get(packageName);
558                     if (packageContext == null) {
559                         try {
560                             packageContext = mContext.createPackageContext(packageName, 0);
561                         } catch (PackageManager.NameNotFoundException e) {
562                             Log.e(LOG_TAG, "Cannot create Context for package: " + packageName);
563                             return null;
564                         }
565                         mContextMap.put(packageName, packageContext);
566                     }
567                 } else {
568                     packageContext = mContext;
569                 }
570
571                 final int iconResId = TextUtils.isEmpty(iconResStr) ?
572                         R.drawable.empty_icon : Integer.parseInt(iconResStr);
573
574                 return new SearchResult(packageContext, title, summaryOn, summaryOff,
575                         entries, iconResId, key);
576             }
577             return null;
578         }
579
580         @Override
581         public long getItemId(int position) {
582             return 0;
583         }
584
585         @Override
586         public View getView(int position, View convertView, ViewGroup parent) {
587             if (!mDataValid && convertView == null) {
588                 throw new IllegalStateException(
589                         "this should only be called when the cursor is valid");
590             }
591             if (!mCursor.moveToPosition(position)) {
592                 throw new IllegalStateException("couldn't move cursor to position " + position);
593             }
594
595             View view;
596             TextView textTitle;
597             ImageView imageView;
598
599             if (convertView == null) {
600                 view = mInflater.inflate(R.layout.search_result_item, parent, false);
601             } else {
602                 view = convertView;
603             }
604
605             textTitle = (TextView) view.findViewById(R.id.title);
606             imageView = (ImageView) view.findViewById(R.id.icon);
607
608             final SearchResult result = (SearchResult) getItem(position);
609             textTitle.setText(result.title);
610
611             if (result.iconResId != R.drawable.empty_icon) {
612                 final Context packageContext = result.context;
613                 final Drawable drawable;
614                 try {
615                     drawable = packageContext.getDrawable(result.iconResId);
616                     imageView.setImageDrawable(drawable);
617                 } catch (Resources.NotFoundException nfe) {
618                     // Not much we can do except logging
619                     Log.e(LOG_TAG, "Cannot load Drawable for " + result.title);
620                 }
621             } else {
622                 imageView.setImageDrawable(null);
623                 imageView.setBackgroundResource(R.drawable.empty_icon);
624             }
625
626             return view;
627         }
628     }
629 }