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