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