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