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 */
16package com.android.contacts.list;
17
18import android.content.CursorLoader;
19import android.content.Intent;
20import android.database.Cursor;
21import android.provider.ContactsContract.Contacts;
22import android.text.TextUtils;
23import android.util.Log;
24import android.view.LayoutInflater;
25import android.view.View;
26import android.view.View.OnClickListener;
27import android.view.ViewGroup;
28import android.view.accessibility.AccessibilityEvent;
29import android.widget.Button;
30import android.widget.FrameLayout;
31import android.widget.ListView;
32import android.widget.TextView;
33
34import com.android.contacts.R;
35import com.android.contacts.editor.ContactEditorFragment;
36import com.android.contacts.util.AccountFilterUtil;
37
38/**
39 * Fragment containing a contact list used for browsing (as compared to
40 * picking a contact with one of the PICK intents).
41 */
42public class DefaultContactBrowseListFragment extends ContactBrowseListFragment {
43    private static final String TAG = DefaultContactBrowseListFragment.class.getSimpleName();
44
45    private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
46
47    private TextView mCounterHeaderView;
48    private View mSearchHeaderView;
49    private View mAccountFilterHeader;
50    private FrameLayout mProfileHeaderContainer;
51    private View mProfileHeader;
52    private Button mProfileMessage;
53    private FrameLayout mMessageContainer;
54    private TextView mProfileTitle;
55    private View mSearchProgress;
56    private TextView mSearchProgressText;
57
58    private class FilterHeaderClickListener implements OnClickListener {
59        @Override
60        public void onClick(View view) {
61            AccountFilterUtil.startAccountFilterActivityForResult(
62                        DefaultContactBrowseListFragment.this,
63                        REQUEST_CODE_ACCOUNT_FILTER,
64                        getFilter());
65        }
66    }
67    private OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
68
69    public DefaultContactBrowseListFragment() {
70        setPhotoLoaderEnabled(true);
71        setSectionHeaderDisplayEnabled(true);
72        setVisibleScrollbarEnabled(true);
73    }
74
75    @Override
76    public CursorLoader createCursorLoader() {
77        return new ProfileAndContactsLoader(getActivity());
78    }
79
80    @Override
81    protected void onItemClick(int position, long id) {
82        viewContact(getAdapter().getContactUri(position));
83    }
84
85    @Override
86    protected ContactListAdapter createListAdapter() {
87        DefaultContactListAdapter adapter = new DefaultContactListAdapter(getContext());
88        adapter.setSectionHeaderDisplayEnabled(isSectionHeaderDisplayEnabled());
89        adapter.setDisplayPhotos(getResources().getBoolean(R.bool.config_browse_list_show_images));
90        return adapter;
91    }
92
93    @Override
94    protected View inflateView(LayoutInflater inflater, ViewGroup container) {
95        return inflater.inflate(R.layout.contact_list_content, null);
96    }
97
98    @Override
99    protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
100        super.onCreateView(inflater, container);
101
102        mAccountFilterHeader = getView().findViewById(R.id.account_filter_header_container);
103        mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
104        mCounterHeaderView = (TextView) getView().findViewById(R.id.contacts_count);
105
106        // Create an empty user profile header and hide it for now (it will be visible if the
107        // contacts list will have no user profile).
108        addEmptyUserProfileHeader(inflater);
109        showEmptyUserProfile(false);
110
111        // Putting the header view inside a container will allow us to make
112        // it invisible later. See checkHeaderViewVisibility()
113        FrameLayout headerContainer = new FrameLayout(inflater.getContext());
114        mSearchHeaderView = inflater.inflate(R.layout.search_header, null, false);
115        headerContainer.addView(mSearchHeaderView);
116        getListView().addHeaderView(headerContainer, null, false);
117        checkHeaderViewVisibility();
118
119        mSearchProgress = getView().findViewById(R.id.search_progress);
120        mSearchProgressText = (TextView) mSearchHeaderView.findViewById(R.id.totalContactsText);
121    }
122
123    @Override
124    protected void setSearchMode(boolean flag) {
125        super.setSearchMode(flag);
126        checkHeaderViewVisibility();
127        if (!flag) showSearchProgress(false);
128    }
129
130    /** Show or hide the directory-search progress spinner. */
131    private void showSearchProgress(boolean show) {
132        mSearchProgress.setVisibility(show ? View.VISIBLE : View.GONE);
133    }
134
135    private void checkHeaderViewVisibility() {
136        if (mCounterHeaderView != null) {
137            mCounterHeaderView.setVisibility(isSearchMode() ? View.GONE : View.VISIBLE);
138        }
139        updateFilterHeaderView();
140
141        // Hide the search header by default. See showCount().
142        if (mSearchHeaderView != null) {
143            mSearchHeaderView.setVisibility(View.GONE);
144        }
145    }
146
147    @Override
148    public void setFilter(ContactListFilter filter) {
149        super.setFilter(filter);
150        updateFilterHeaderView();
151    }
152
153    private void updateFilterHeaderView() {
154        if (mAccountFilterHeader == null) {
155            return; // Before onCreateView -- just ignore it.
156        }
157        final ContactListFilter filter = getFilter();
158        if (filter != null && !isSearchMode()) {
159            final boolean shouldShowHeader = AccountFilterUtil.updateAccountFilterTitleForPeople(
160                    mAccountFilterHeader, filter, false);
161            mAccountFilterHeader.setVisibility(shouldShowHeader ? View.VISIBLE : View.GONE);
162        } else {
163            mAccountFilterHeader.setVisibility(View.GONE);
164        }
165    }
166
167    @Override
168    protected void showCount(int partitionIndex, Cursor data) {
169        if (!isSearchMode() && data != null) {
170            int count = data.getCount();
171            if (count != 0) {
172                count -= (mUserProfileExists ? 1: 0);
173                String format = getResources().getQuantityText(
174                        R.plurals.listTotalAllContacts, count).toString();
175                // Do not count the user profile in the contacts count
176                if (mUserProfileExists) {
177                    getAdapter().setContactsCount(String.format(format, count));
178                } else {
179                    mCounterHeaderView.setText(String.format(format, count));
180                }
181            } else {
182                ContactListFilter filter = getFilter();
183                int filterType = filter != null ? filter.filterType
184                        : ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS;
185                switch (filterType) {
186                    case ContactListFilter.FILTER_TYPE_ACCOUNT:
187                        mCounterHeaderView.setText(getString(
188                                R.string.listTotalAllContactsZeroGroup, filter.accountName));
189                        break;
190                    case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
191                        mCounterHeaderView.setText(R.string.listTotalPhoneContactsZero);
192                        break;
193                    case ContactListFilter.FILTER_TYPE_STARRED:
194                        mCounterHeaderView.setText(R.string.listTotalAllContactsZeroStarred);
195                        break;
196                    case ContactListFilter.FILTER_TYPE_CUSTOM:
197                        mCounterHeaderView.setText(R.string.listTotalAllContactsZeroCustom);
198                        break;
199                    default:
200                        mCounterHeaderView.setText(R.string.listTotalAllContactsZero);
201                        break;
202                }
203            }
204        } else {
205            ContactListAdapter adapter = getAdapter();
206            if (adapter == null) {
207                return;
208            }
209
210            // In search mode we only display the header if there is nothing found
211            if (TextUtils.isEmpty(getQueryString()) || !adapter.areAllPartitionsEmpty()) {
212                mSearchHeaderView.setVisibility(View.GONE);
213                showSearchProgress(false);
214            } else {
215                mSearchHeaderView.setVisibility(View.VISIBLE);
216                if (adapter.isLoading()) {
217                    mSearchProgressText.setText(R.string.search_results_searching);
218                    showSearchProgress(true);
219                } else {
220                    mSearchProgressText.setText(R.string.listFoundAllContactsZero);
221                    mSearchProgressText.sendAccessibilityEvent(
222                            AccessibilityEvent.TYPE_VIEW_SELECTED);
223                    showSearchProgress(false);
224                }
225            }
226            showEmptyUserProfile(false);
227        }
228    }
229
230    @Override
231    protected void setProfileHeader() {
232        mUserProfileExists = getAdapter().hasProfile();
233        showEmptyUserProfile(!mUserProfileExists && !isSearchMode());
234    }
235
236    @Override
237    public void onActivityResult(int requestCode, int resultCode, Intent data) {
238        if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
239            if (getActivity() != null) {
240                AccountFilterUtil.handleAccountFilterResult(
241                        ContactListFilterController.getInstance(getActivity()), resultCode, data);
242            } else {
243                Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
244            }
245        }
246    }
247
248    private void showEmptyUserProfile(boolean show) {
249        // Changing visibility of just the mProfileHeader doesn't do anything unless
250        // you change visibility of its children, hence the call to mCounterHeaderView
251        // and mProfileTitle
252        mProfileHeaderContainer.setVisibility(show ? View.VISIBLE : View.GONE);
253        mProfileHeader.setVisibility(show ? View.VISIBLE : View.GONE);
254        mCounterHeaderView.setVisibility(show ? View.VISIBLE : View.GONE);
255        mProfileTitle.setVisibility(show ? View.VISIBLE : View.GONE);
256        mMessageContainer.setVisibility(show ? View.VISIBLE : View.GONE);
257        mProfileMessage.setVisibility(show ? View.VISIBLE : View.GONE);
258    }
259
260    /**
261     * This method creates a pseudo user profile contact. When the returned query doesn't have
262     * a profile, this methods creates 2 views that are inserted as headers to the listview:
263     * 1. A header view with the "ME" title and the contacts count.
264     * 2. A button that prompts the user to create a local profile
265     */
266    private void addEmptyUserProfileHeader(LayoutInflater inflater) {
267
268        ListView list = getListView();
269        // Put a header with the "ME" name and a view for the number of contacts
270        // The view is embedded in a frame view since you cannot change the visibility of a
271        // view in a ListView without having a parent view.
272        mProfileHeaderContainer = new FrameLayout(inflater.getContext());
273        mProfileHeader = inflater.inflate(R.layout.user_profile_header, null, false);
274        mCounterHeaderView = (TextView) mProfileHeader.findViewById(R.id.contacts_count);
275        mProfileTitle = (TextView) mProfileHeader.findViewById(R.id.profile_title);
276        mProfileTitle.setAllCaps(true);
277        mProfileHeaderContainer.addView(mProfileHeader);
278        list.addHeaderView(mProfileHeaderContainer, null, false);
279
280        // Add a selectable view with a message inviting the user to create a local profile
281        mMessageContainer = new FrameLayout(inflater.getContext());
282        mProfileMessage = (Button)inflater.inflate(R.layout.user_profile_button, null, false);
283        mMessageContainer.addView(mProfileMessage);
284        list.addHeaderView(mMessageContainer, null, true);
285
286        mProfileMessage.setOnClickListener(new View.OnClickListener() {
287            public void onClick(View v) {
288                Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
289                intent.putExtra(ContactEditorFragment.INTENT_EXTRA_NEW_LOCAL_PROFILE, true);
290                startActivity(intent);
291            }
292        });
293    }
294}
295