ActionBarAdapter.java revision d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9
1/*
2 * Copyright (C) 2010 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 com.android.contacts.activities;
18
19import com.android.contacts.R;
20import com.android.contacts.activities.ActionBarAdapter.Listener.Action;
21import com.android.contacts.list.ContactsRequest;
22
23import android.app.ActionBar;
24import android.app.ActionBar.LayoutParams;
25import android.app.ActionBar.Tab;
26import android.app.FragmentTransaction;
27import android.content.Context;
28import android.content.res.TypedArray;
29import android.os.Bundle;
30import android.text.TextUtils;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.widget.SearchView;
34import android.widget.SearchView.OnCloseListener;
35import android.widget.SearchView.OnQueryTextListener;
36
37/**
38 * Adapter for the action bar at the top of the Contacts activity.
39 */
40public class ActionBarAdapter implements OnQueryTextListener, OnCloseListener {
41
42    public interface Listener {
43        public enum Action {
44            CHANGE_SEARCH_QUERY, START_SEARCH_MODE, STOP_SEARCH_MODE
45        }
46
47        void onAction(Action action);
48
49        /**
50         * Called when the user selects a tab.  The new tab can be obtained using
51         * {@link #getCurrentTab}.
52         */
53        void onSelectedTabChanged();
54    }
55
56    private static final String EXTRA_KEY_SEARCH_MODE = "navBar.searchMode";
57    private static final String EXTRA_KEY_QUERY = "navBar.query";
58    private static final String EXTRA_KEY_SELECTED_TAB = "navBar.selectedTab";
59
60    private boolean mSearchMode;
61    private String mQueryString;
62
63    private String mSearchLabelText;
64    private SearchView mSearchView;
65
66    private final Context mContext;
67    private final boolean mAlwaysShowSearchView;
68
69    private Listener mListener;
70
71    private final ActionBar mActionBar;
72    private final MyTabListener mTabListener = new MyTabListener();
73
74    private boolean mShowHomeIcon;
75
76    public enum TabState {
77        FAVORITES, ALL, GROUPS;
78
79        public static TabState fromInt(int value) {
80            switch (value) {
81                case 0:
82                    return FAVORITES;
83                case 1:
84                    return ALL;
85                case 2:
86                    return GROUPS;
87            }
88            throw new IllegalArgumentException("Invalid value: " + value);
89        }
90    }
91
92    private TabState mCurrentTab = TabState.FAVORITES;
93
94    public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar) {
95        mContext = context;
96        mListener = listener;
97        mActionBar = actionBar;
98        mSearchLabelText = mContext.getString(R.string.search_label);
99        mAlwaysShowSearchView = mContext.getResources().getBoolean(R.bool.always_show_search_view);
100
101        mShowHomeIcon = mContext.getResources().getBoolean(R.bool.show_home_icon);
102
103        // Set up search view.
104        View customSearchView = LayoutInflater.from(mContext).inflate(R.layout.custom_action_bar,
105                null);
106        int searchViewWidth = mContext.getResources().getDimensionPixelSize(
107                R.dimen.search_view_width);
108        if (searchViewWidth == 0) {
109            searchViewWidth = LayoutParams.MATCH_PARENT;
110        }
111        LayoutParams layoutParams = new LayoutParams(searchViewWidth, LayoutParams.WRAP_CONTENT);
112        mSearchView = (SearchView) customSearchView.findViewById(R.id.search_view);
113        mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts));
114        mSearchView.setOnQueryTextListener(this);
115        mSearchView.setOnCloseListener(this);
116        mSearchView.setQuery(mQueryString, false);
117        mActionBar.setCustomView(customSearchView, layoutParams);
118
119        // Set up tabs
120        addTab(TabState.FAVORITES, mContext.getString(R.string.contactsFavoritesLabel));
121        addTab(TabState.ALL, mContext.getString(R.string.contactsAllLabel));
122        addTab(TabState.GROUPS, mContext.getString(R.string.contactsGroupsLabel));
123    }
124
125    public void initialize(Bundle savedState, ContactsRequest request) {
126        if (savedState == null) {
127            mSearchMode = request.isSearchMode();
128            mQueryString = request.getQueryString();
129        } else {
130            mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE);
131            mQueryString = savedState.getString(EXTRA_KEY_QUERY);
132
133            // Just set to the field here.  The listener will be notified by update().
134            mCurrentTab = TabState.fromInt(savedState.getInt(EXTRA_KEY_SELECTED_TAB));
135        }
136        update();
137    }
138
139    public void setListener(Listener listener) {
140        mListener = listener;
141    }
142
143    private void addTab(TabState tabState, String text) {
144        final Tab tab = mActionBar.newTab();
145        tab.setTag(tabState);
146        tab.setText(text);
147        tab.setTabListener(mTabListener);
148        mActionBar.addTab(tab);
149    }
150
151    private class MyTabListener implements ActionBar.TabListener {
152        /**
153         * If true, it won't call {@link #setCurrentTab} in {@link #onTabSelected}.
154         * This flag is used when we want to programmatically update the current tab without
155         * {@link #onTabSelected} getting called.
156         */
157        public boolean mIgnoreTabSelected;
158
159        @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { }
160        @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { }
161
162        @Override public void onTabSelected(Tab tab, FragmentTransaction ft) {
163            if (!mIgnoreTabSelected) {
164                setCurrentTab((TabState)tab.getTag());
165            }
166        }
167    }
168
169    /**
170     * Change the current tab, and notify the listener.
171     */
172    public void setCurrentTab(TabState tab) {
173        setCurrentTab(tab, true);
174    }
175
176    /**
177     * Change the current tab
178     */
179    public void setCurrentTab(TabState tab, boolean notifyListener) {
180        if (tab == null) throw new NullPointerException();
181        if (tab == mCurrentTab) {
182            return;
183        }
184        mCurrentTab = tab;
185
186        int index = mCurrentTab.ordinal();
187        if ((mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS)
188                && (index != mActionBar.getSelectedNavigationIndex())) {
189            mActionBar.setSelectedNavigationItem(index);
190        }
191
192        if (notifyListener && mListener != null) mListener.onSelectedTabChanged();
193    }
194
195    public TabState getCurrentTab() {
196        return mCurrentTab;
197    }
198
199    public boolean isSearchMode() {
200        return mSearchMode;
201    }
202
203    public void setSearchMode(boolean flag) {
204        if (mSearchMode != flag) {
205            mSearchMode = flag;
206            update();
207            if (mSearchView == null) {
208                return;
209            }
210            if (mSearchMode) {
211                setFocusOnSearchView();
212            } else {
213                mSearchView.setQuery(null, false);
214            }
215        }
216    }
217
218    public String getQueryString() {
219        return mQueryString;
220    }
221
222    public void setQueryString(String query) {
223        mQueryString = query;
224        if (mSearchView != null) {
225            mSearchView.setQuery(query, false);
226        }
227    }
228
229    /** @return true if the "UP" icon is showing. */
230    public boolean isUpShowing() {
231        return mSearchMode; // Only shown on the search mode.
232    }
233
234    private void updateDisplayOptions() {
235        // All the flags we may change in this method.
236        final int MASK = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME
237                | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_CUSTOM;
238
239        // The current flags set to the action bar.  (only the ones that we may change here)
240        final int current = mActionBar.getDisplayOptions() & MASK;
241
242        // Build the new flags...
243        int newFlags = 0;
244        newFlags |= ActionBar.DISPLAY_SHOW_TITLE;
245        if (mShowHomeIcon) {
246            newFlags |= ActionBar.DISPLAY_SHOW_HOME;
247        }
248        if (mSearchMode) {
249            newFlags |= ActionBar.DISPLAY_SHOW_HOME;
250            newFlags |= ActionBar.DISPLAY_HOME_AS_UP;
251            newFlags |= ActionBar.DISPLAY_SHOW_CUSTOM;
252        } else if (mAlwaysShowSearchView) {
253            newFlags |= ActionBar.DISPLAY_SHOW_CUSTOM;
254        }
255        mActionBar.setHomeButtonEnabled(mSearchMode);
256
257        if (current != newFlags) {
258            // Pass the mask here to preserve other flags that we're not interested here.
259            mActionBar.setDisplayOptions(newFlags, MASK);
260        }
261    }
262
263    private void update() {
264        if (mSearchMode) {
265            if (mAlwaysShowSearchView) {
266                // Tablet -- change the app title for the search mode
267                mActionBar.setTitle(mSearchLabelText);
268            } else {
269                // Phone -- search view gets focus
270                setFocusOnSearchView();
271            }
272            if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) {
273                mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
274            }
275            if (mListener != null) {
276                mListener.onAction(Action.START_SEARCH_MODE);
277            }
278        } else {
279            if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_TABS) {
280                // setNavigationMode will trigger onTabSelected() with the tab which was previously
281                // selected.
282                // The issue is that when we're first switching to the tab navigation mode after
283                // screen orientation changes, onTabSelected() will get called with the first tab
284                // (i.e. favorite), which would results in mCurrentTab getting set to FAVORITES and
285                // we'd lose restored tab.
286                // So let's just disable the callback here temporarily.  We'll notify the listener
287                // after this anyway.
288                mTabListener.mIgnoreTabSelected = true;
289                mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
290                mActionBar.setSelectedNavigationItem(mCurrentTab.ordinal());
291                mTabListener.mIgnoreTabSelected = false;
292            }
293            mActionBar.setTitle(null);
294            if (mListener != null) {
295                mListener.onAction(Action.STOP_SEARCH_MODE);
296                mListener.onSelectedTabChanged();
297            }
298        }
299        updateDisplayOptions();
300    }
301
302    @Override
303    public boolean onQueryTextChange(String queryString) {
304        // TODO: Clean up SearchView code because it keeps setting the SearchView query,
305        // invoking onQueryChanged, setting up the fragment again, invalidating the options menu,
306        // storing the SearchView again, and etc... unless we add in the early return statements.
307        if (queryString.equals(mQueryString)) {
308            return false;
309        }
310        mQueryString = queryString;
311        if (!mSearchMode) {
312            if (!TextUtils.isEmpty(queryString)) {
313                setSearchMode(true);
314            }
315        } else if (mListener != null) {
316            mListener.onAction(Action.CHANGE_SEARCH_QUERY);
317        }
318
319        return true;
320    }
321
322    @Override
323    public boolean onQueryTextSubmit(String query) {
324        return true;
325    }
326
327    @Override
328    public boolean onClose() {
329        setSearchMode(false);
330        return false;
331    }
332
333    public void onSaveInstanceState(Bundle outState) {
334        outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode);
335        outState.putString(EXTRA_KEY_QUERY, mQueryString);
336        outState.putInt(EXTRA_KEY_SELECTED_TAB, mCurrentTab.ordinal());
337    }
338
339    private void setFocusOnSearchView() {
340        mSearchView.requestFocus();
341        mSearchView.setIconified(false); // Workaround for the "IME not popping up" issue.
342    }
343}
344