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