1/*
2 * Copyright (C) 2011 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.support.v4.widget;
18
19import android.app.SearchManager;
20import android.content.ComponentName;
21import android.content.Context;
22import android.os.Build;
23import android.view.View;
24import android.widget.TextView;
25
26/**
27 * Helper for accessing features in {@link android.widget.SearchView}
28 * introduced after API level 4 in a backwards compatible fashion.
29 */
30public class SearchViewCompat {
31
32    interface SearchViewCompatImpl {
33        View newSearchView(Context context);
34        void setSearchableInfo(View searchView, ComponentName searchableComponent);
35        void setImeOptions(View searchView, int imeOptions);
36        void setInputType(View searchView, int inputType);
37        Object newOnQueryTextListener(OnQueryTextListenerCompat listener);
38        void setOnQueryTextListener(Object searchView, Object listener);
39        Object newOnCloseListener(OnCloseListenerCompat listener);
40        void setOnCloseListener(Object searchView, Object listener);
41        CharSequence getQuery(View searchView);
42        void setQuery(View searchView, CharSequence query, boolean submit);
43        void setQueryHint(View searchView, CharSequence hint);
44        void setIconified(View searchView, boolean iconify);
45        boolean isIconified(View searchView);
46        void setSubmitButtonEnabled(View searchView, boolean enabled);
47        boolean isSubmitButtonEnabled(View searchView);
48        void setQueryRefinementEnabled(View searchView, boolean enable);
49        boolean isQueryRefinementEnabled(View searchView);
50        void setMaxWidth(View searchView, int maxpixels);
51    }
52
53    static class SearchViewCompatStubImpl implements SearchViewCompatImpl {
54
55        @Override
56        public View newSearchView(Context context) {
57            return null;
58        }
59
60        @Override
61        public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
62        }
63
64        @Override
65        public void setImeOptions(View searchView, int imeOptions) {
66        }
67
68        @Override
69        public void setInputType(View searchView, int inputType) {
70        }
71
72        @Override
73        public Object newOnQueryTextListener(OnQueryTextListenerCompat listener) {
74            return null;
75        }
76
77        @Override
78        public void setOnQueryTextListener(Object searchView, Object listener) {
79        }
80
81        @Override
82        public Object newOnCloseListener(OnCloseListenerCompat listener) {
83            return null;
84        }
85
86        @Override
87        public void setOnCloseListener(Object searchView, Object listener) {
88        }
89
90        @Override
91        public CharSequence getQuery(View searchView) {
92            return null;
93        }
94
95        @Override
96        public void setQuery(View searchView, CharSequence query, boolean submit) {
97        }
98
99        @Override
100        public void setQueryHint(View searchView, CharSequence hint) {
101        }
102
103        @Override
104        public void setIconified(View searchView, boolean iconify) {
105        }
106
107        @Override
108        public boolean isIconified(View searchView) {
109            return true;
110        }
111
112        @Override
113        public void setSubmitButtonEnabled(View searchView, boolean enabled) {
114        }
115
116        @Override
117        public boolean isSubmitButtonEnabled(View searchView) {
118            return false;
119        }
120
121        @Override
122        public void setQueryRefinementEnabled(View searchView, boolean enable) {
123        }
124
125        @Override
126        public boolean isQueryRefinementEnabled(View searchView) {
127            return false;
128        }
129
130        @Override
131        public void setMaxWidth(View searchView, int maxpixels) {
132        }
133    }
134
135    static class SearchViewCompatHoneycombImpl extends SearchViewCompatStubImpl {
136
137        @Override
138        public View newSearchView(Context context) {
139            return SearchViewCompatHoneycomb.newSearchView(context);
140        }
141
142        @Override
143        public void setSearchableInfo(View searchView, ComponentName searchableComponent) {
144            SearchViewCompatHoneycomb.setSearchableInfo(searchView, searchableComponent);
145        }
146
147        @Override
148        public Object newOnQueryTextListener(final OnQueryTextListenerCompat listener) {
149            return SearchViewCompatHoneycomb.newOnQueryTextListener(
150                    new SearchViewCompatHoneycomb.OnQueryTextListenerCompatBridge() {
151                        @Override
152                        public boolean onQueryTextSubmit(String query) {
153                            return listener.onQueryTextSubmit(query);
154                        }
155                        @Override
156                        public boolean onQueryTextChange(String newText) {
157                            return listener.onQueryTextChange(newText);
158                        }
159                    });
160        }
161
162        @Override
163        public void setOnQueryTextListener(Object searchView, Object listener) {
164            SearchViewCompatHoneycomb.setOnQueryTextListener(searchView, listener);
165        }
166
167        @Override
168        public Object newOnCloseListener(final OnCloseListenerCompat listener) {
169            return SearchViewCompatHoneycomb.newOnCloseListener(
170                    new SearchViewCompatHoneycomb.OnCloseListenerCompatBridge() {
171                        @Override
172                        public boolean onClose() {
173                            return listener.onClose();
174                        }
175                    });
176        }
177
178        @Override
179        public void setOnCloseListener(Object searchView, Object listener) {
180            SearchViewCompatHoneycomb.setOnCloseListener(searchView, listener);
181        }
182
183        @Override
184        public CharSequence getQuery(View searchView) {
185            return SearchViewCompatHoneycomb.getQuery(searchView);
186        }
187
188        @Override
189        public void setQuery(View searchView, CharSequence query, boolean submit) {
190            SearchViewCompatHoneycomb.setQuery(searchView, query, submit);
191        }
192
193        @Override
194        public void setQueryHint(View searchView, CharSequence hint) {
195            SearchViewCompatHoneycomb.setQueryHint(searchView, hint);
196        }
197
198        @Override
199        public void setIconified(View searchView, boolean iconify) {
200            SearchViewCompatHoneycomb.setIconified(searchView, iconify);
201        }
202
203        @Override
204        public boolean isIconified(View searchView) {
205            return SearchViewCompatHoneycomb.isIconified(searchView);
206        }
207
208        @Override
209        public void setSubmitButtonEnabled(View searchView, boolean enabled) {
210            SearchViewCompatHoneycomb.setSubmitButtonEnabled(searchView, enabled);
211        }
212
213        @Override
214        public boolean isSubmitButtonEnabled(View searchView) {
215            return SearchViewCompatHoneycomb.isSubmitButtonEnabled(searchView);
216        }
217
218        @Override
219        public void setQueryRefinementEnabled(View searchView, boolean enable) {
220            SearchViewCompatHoneycomb.setQueryRefinementEnabled(searchView, enable);
221        }
222
223        @Override
224        public boolean isQueryRefinementEnabled(View searchView) {
225            return SearchViewCompatHoneycomb.isQueryRefinementEnabled(searchView);
226        }
227
228        @Override
229        public void setMaxWidth(View searchView, int maxpixels) {
230            SearchViewCompatHoneycomb.setMaxWidth(searchView, maxpixels);
231        }
232    }
233
234    static class SearchViewCompatIcsImpl extends SearchViewCompatHoneycombImpl {
235
236        @Override
237        public View newSearchView(Context context) {
238            return SearchViewCompatIcs.newSearchView(context);
239        }
240
241        @Override
242        public void setImeOptions(View searchView, int imeOptions) {
243            SearchViewCompatIcs.setImeOptions(searchView, imeOptions);
244        }
245
246        @Override
247        public void setInputType(View searchView, int inputType) {
248            SearchViewCompatIcs.setInputType(searchView, inputType);
249        }
250    }
251
252    private static final SearchViewCompatImpl IMPL;
253
254    static {
255        if (Build.VERSION.SDK_INT >= 14) { // ICS
256            IMPL = new SearchViewCompatIcsImpl();
257        } else if (Build.VERSION.SDK_INT >= 11) { // Honeycomb
258            IMPL = new SearchViewCompatHoneycombImpl();
259        } else {
260            IMPL = new SearchViewCompatStubImpl();
261        }
262    }
263
264    private SearchViewCompat(Context context) {
265        /* Hide constructor */
266    }
267
268    /**
269     * Creates a new SearchView.
270     *
271     * @param context The Context the view is running in.
272     * @return A SearchView instance if the class is present on the current
273     *         platform, null otherwise.
274     */
275    public static View newSearchView(Context context) {
276        return IMPL.newSearchView(context);
277    }
278
279    /**
280     * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used
281     * to display labels, hints, suggestions, create intents for launching search results screens
282     * and controlling other affordances such as a voice button.
283     *
284     * @param searchView The SearchView to operate on.
285     * @param searchableComponent The application component whose
286     * {@link android.app.SearchableInfo} should be loaded and applied to
287     * the SearchView.
288     */
289    public static void setSearchableInfo(View searchView, ComponentName searchableComponent) {
290        IMPL.setSearchableInfo(searchView, searchableComponent);
291    }
292
293    /**
294     * Sets the IME options on the query text field.  This is a no-op if
295     * called on pre-{@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}
296     * platforms.
297     *
298     * @see TextView#setImeOptions(int)
299     * @param searchView The SearchView to operate on.
300     * @param imeOptions the options to set on the query text field
301     */
302    public static void setImeOptions(View searchView, int imeOptions) {
303        IMPL.setImeOptions(searchView, imeOptions);
304    }
305
306    /**
307     * Sets the input type on the query text field.  This is a no-op if
308     * called on pre-{@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}
309     * platforms.
310     *
311     * @see TextView#setInputType(int)
312     * @param searchView The SearchView to operate on.
313     * @param inputType the input type to set on the query text field
314     */
315    public static void setInputType(View searchView, int inputType) {
316        IMPL.setInputType(searchView, inputType);
317    }
318
319    /**
320     * Sets a listener for user actions within the SearchView.
321     *
322     * @param searchView The SearchView in which to register the listener.
323     * @param listener the listener object that receives callbacks when the user performs
324     *     actions in the SearchView such as clicking on buttons or typing a query.
325     */
326    public static void setOnQueryTextListener(View searchView, OnQueryTextListenerCompat listener) {
327        IMPL.setOnQueryTextListener(searchView, listener.mListener);
328    }
329
330    /**
331     * Callbacks for changes to the query text.
332     */
333    public static abstract class OnQueryTextListenerCompat {
334        final Object mListener;
335
336        public OnQueryTextListenerCompat() {
337            mListener = IMPL.newOnQueryTextListener(this);
338        }
339
340        /**
341         * Called when the user submits the query. This could be due to a key press on the
342         * keyboard or due to pressing a submit button.
343         * The listener can override the standard behavior by returning true
344         * to indicate that it has handled the submit request. Otherwise return false to
345         * let the SearchView handle the submission by launching any associated intent.
346         *
347         * @param query the query text that is to be submitted
348         *
349         * @return true if the query has been handled by the listener, false to let the
350         * SearchView perform the default action.
351         */
352        public boolean onQueryTextSubmit(String query) {
353            return false;
354        }
355
356        /**
357         * Called when the query text is changed by the user.
358         *
359         * @param newText the new content of the query text field.
360         *
361         * @return false if the SearchView should perform the default action of showing any
362         * suggestions if available, true if the action was handled by the listener.
363         */
364        public boolean onQueryTextChange(String newText) {
365            return false;
366        }
367    }
368
369    /**
370     * Sets a listener to inform when the user closes the SearchView.
371     *
372     * @param searchView The SearchView in which to register the listener.
373     * @param listener the listener to call when the user closes the SearchView.
374     */
375    public static void setOnCloseListener(View searchView, OnCloseListenerCompat listener) {
376        IMPL.setOnCloseListener(searchView, listener.mListener);
377    }
378
379    /**
380     * Callback for closing the query UI.
381     */
382    public static abstract class OnCloseListenerCompat {
383        final Object mListener;
384
385        public OnCloseListenerCompat() {
386            mListener = IMPL.newOnCloseListener(this);
387        }
388
389        /**
390         * The user is attempting to close the SearchView.
391         *
392         * @return true if the listener wants to override the default behavior of clearing the
393         * text field and dismissing it, false otherwise.
394         */
395        public boolean onClose() {
396            return false;
397        }
398    }
399
400    /**
401     * Returns the query string currently in the text field.
402     *
403     * @param searchView The SearchView to operate on.
404     *
405     * @return the query string
406     */
407    public static CharSequence getQuery(View searchView) {
408        return IMPL.getQuery(searchView);
409    }
410
411    /**
412     * Sets a query string in the text field and optionally submits the query as well.
413     *
414     * @param searchView The SearchView to operate on.
415     * @param query the query string. This replaces any query text already present in the
416     * text field.
417     * @param submit whether to submit the query right now or only update the contents of
418     * text field.
419     */
420    public static void setQuery(View searchView, CharSequence query, boolean submit) {
421        IMPL.setQuery(searchView, query, submit);
422    }
423
424    /**
425     * Sets the hint text to display in the query text field. This overrides any hint specified
426     * in the SearchableInfo.
427     *
428     * @param searchView The SearchView to operate on.
429     * @param hint the hint text to display
430     */
431    public static void setQueryHint(View searchView, CharSequence hint) {
432        IMPL.setQueryHint(searchView, hint);
433    }
434
435    /**
436     * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
437     * a temporary state and does not override the default iconified state set by
438     * setIconifiedByDefault(boolean). If the default state is iconified, then
439     * a false here will only be valid until the user closes the field. And if the default
440     * state is expanded, then a true here will only clear the text field and not close it.
441     *
442     * @param searchView The SearchView to operate on.
443     * @param iconify a true value will collapse the SearchView to an icon, while a false will
444     * expand it.
445     */
446    public static void setIconified(View searchView, boolean iconify) {
447        IMPL.setIconified(searchView, iconify);
448    }
449
450    /**
451     * Returns the current iconified state of the SearchView.
452     *
453     * @param searchView The SearchView to operate on.
454     * @return true if the SearchView is currently iconified, false if the search field is
455     * fully visible.
456     */
457    public static boolean isIconified(View searchView) {
458        return IMPL.isIconified(searchView);
459    }
460
461    /**
462     * Enables showing a submit button when the query is non-empty. In cases where the SearchView
463     * is being used to filter the contents of the current activity and doesn't launch a separate
464     * results activity, then the submit button should be disabled.
465     *
466     * @param searchView The SearchView to operate on.
467     * @param enabled true to show a submit button for submitting queries, false if a submit
468     * button is not required.
469     */
470    public static void setSubmitButtonEnabled(View searchView, boolean enabled) {
471        IMPL.setSubmitButtonEnabled(searchView, enabled);
472    }
473
474    /**
475     * Returns whether the submit button is enabled when necessary or never displayed.
476     *
477     * @param searchView The SearchView to operate on.
478     * @return whether the submit button is enabled automatically when necessary
479     */
480    public static boolean isSubmitButtonEnabled(View searchView) {
481        return IMPL.isSubmitButtonEnabled(searchView);
482    }
483
484    /**
485     * Specifies if a query refinement button should be displayed alongside each suggestion
486     * or if it should depend on the flags set in the individual items retrieved from the
487     * suggestions provider. Clicking on the query refinement button will replace the text
488     * in the query text field with the text from the suggestion. This flag only takes effect
489     * if a SearchableInfo has been specified with {@link #setSearchableInfo(View, ComponentName)}
490     * and not when using a custom adapter.
491     *
492     * @param searchView The SearchView to operate on.
493     * @param enable true if all items should have a query refinement button, false if only
494     * those items that have a query refinement flag set should have the button.
495     *
496     * @see SearchManager#SUGGEST_COLUMN_FLAGS
497     * @see SearchManager#FLAG_QUERY_REFINEMENT
498     */
499    public static void setQueryRefinementEnabled(View searchView, boolean enable) {
500        IMPL.setQueryRefinementEnabled(searchView, enable);
501    }
502
503    /**
504     * Returns whether query refinement is enabled for all items or only specific ones.
505     * @param searchView The SearchView to operate on.
506     * @return true if enabled for all items, false otherwise.
507     */
508    public static boolean isQueryRefinementEnabled(View searchView) {
509        return IMPL.isQueryRefinementEnabled(searchView);
510    }
511
512    /**
513     * Makes the view at most this many pixels wide
514     * @param searchView The SearchView to operate on.
515     */
516    public static void setMaxWidth(View searchView, int maxpixels) {
517        IMPL.setMaxWidth(searchView, maxpixels);
518    }
519}
520