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