1 /* 2 * Copyright (C) 2011 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.ContactPhotoManager; 19 import com.android.contacts.ContactTileLoaderFactory; 20 import com.android.contacts.R; 21 import com.android.contacts.list.ContactTileAdapter.DisplayType; 22 import com.android.contacts.util.PhoneCapabilityTester; 23 24 import android.app.Activity; 25 import android.app.Fragment; 26 import android.app.LoaderManager; 27 import android.app.LoaderManager.LoaderCallbacks; 28 import android.content.CursorLoader; 29 import android.content.Loader; 30 import android.content.res.Resources; 31 import android.database.Cursor; 32 import android.graphics.Rect; 33 import android.net.Uri; 34 import android.os.Bundle; 35 import android.util.Log; 36 import android.view.LayoutInflater; 37 import android.view.View; 38 import android.view.ViewGroup; 39 import android.widget.ListView; 40 import android.widget.TextView; 41 42 /** 43 * Fragment containing a list of starred contacts followed by a list of frequently contacted. 44 * 45 * TODO: Make this an abstract class so that the favorites, frequent, and group list functionality 46 * can be separated out. This will make it easier to customize any of those lists if necessary 47 * (i.e. adding header views to the ListViews in the fragment). This work was started 48 * by creating {@link ContactTileFrequentFragment}. 49 */ 50 public class ContactTileListFragment extends Fragment { 51 private static final String TAG = ContactTileListFragment.class.getSimpleName(); 52 53 public interface Listener { 54 void onContactSelected(Uri contactUri, Rect targetRect); 55 void onCallNumberDirectly(String phoneNumber); 56 } 57 58 private Listener mListener; 59 private ContactTileAdapter mAdapter; 60 private DisplayType mDisplayType; 61 private TextView mEmptyView; 62 private ListView mListView; 63 64 private boolean mOptionsMenuHasFrequents; 65 66 @Override 67 public void onAttach(Activity activity) { 68 super.onAttach(activity); 69 70 Resources res = getResources(); 71 int columnCount = res.getInteger(R.integer.contact_tile_column_count_in_favorites); 72 73 mAdapter = new ContactTileAdapter(activity, mAdapterListener, 74 columnCount, mDisplayType); 75 mAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity)); 76 } 77 78 @Override 79 public View onCreateView(LayoutInflater inflater, ViewGroup container, 80 Bundle savedInstanceState) { 81 return inflateAndSetupView(inflater, container, savedInstanceState, 82 R.layout.contact_tile_list); 83 } 84 85 protected View inflateAndSetupView(LayoutInflater inflater, ViewGroup container, 86 Bundle savedInstanceState, int layoutResourceId) { 87 View listLayout = inflater.inflate(layoutResourceId, container, false); 88 89 mEmptyView = (TextView) listLayout.findViewById(R.id.contact_tile_list_empty); 90 mListView = (ListView) listLayout.findViewById(R.id.contact_tile_list); 91 92 mListView.setItemsCanFocus(true); 93 mListView.setAdapter(mAdapter); 94 return listLayout; 95 } 96 97 @Override 98 public void onStart() { 99 super.onStart(); 100 101 // initialize the loader for this display type and destroy all others 102 final DisplayType[] loaderTypes = mDisplayType.values(); 103 for (int i = 0; i < loaderTypes.length; i++) { 104 if (loaderTypes[i] == mDisplayType) { 105 getLoaderManager().initLoader(mDisplayType.ordinal(), null, 106 mContactTileLoaderListener); 107 } else { 108 getLoaderManager().destroyLoader(loaderTypes[i].ordinal()); 109 } 110 } 111 } 112 113 /** 114 * Returns whether there are any frequents with the side effect of setting the 115 * internal flag mOptionsMenuHasFrequents to the value. This should be called externally 116 * by the activity that is about to prepare the options menu with the clear frequents 117 * menu item. 118 */ 119 public boolean hasFrequents() { 120 mOptionsMenuHasFrequents = internalHasFrequents(); 121 return mOptionsMenuHasFrequents; 122 } 123 124 /** 125 * Returns whether there are any frequents. 126 */ 127 private boolean internalHasFrequents() { 128 return mAdapter.getNumFrequents() > 0; 129 } 130 131 public void setColumnCount(int columnCount) { 132 mAdapter.setColumnCount(columnCount); 133 } 134 135 public void setDisplayType(DisplayType displayType) { 136 mDisplayType = displayType; 137 mAdapter.setDisplayType(mDisplayType); 138 } 139 140 public void enableQuickContact(boolean enableQuickContact) { 141 mAdapter.enableQuickContact(enableQuickContact); 142 } 143 144 private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener = 145 new LoaderCallbacks<Cursor>() { 146 147 @Override 148 public CursorLoader onCreateLoader(int id, Bundle args) { 149 switch (mDisplayType) { 150 case STARRED_ONLY: 151 return ContactTileLoaderFactory.createStarredLoader(getActivity()); 152 case STREQUENT: 153 return ContactTileLoaderFactory.createStrequentLoader(getActivity()); 154 case STREQUENT_PHONE_ONLY: 155 return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity()); 156 case FREQUENT_ONLY: 157 return ContactTileLoaderFactory.createFrequentLoader(getActivity()); 158 default: 159 throw new IllegalStateException( 160 "Unrecognized DisplayType " + mDisplayType); 161 } 162 } 163 164 @Override 165 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 166 mAdapter.setContactCursor(data); 167 mEmptyView.setText(getEmptyStateText()); 168 mListView.setEmptyView(mEmptyView); 169 170 // invalidate the menu options if needed 171 invalidateOptionsMenuIfNeeded(); 172 } 173 174 @Override 175 public void onLoaderReset(Loader<Cursor> loader) {} 176 }; 177 178 private boolean isOptionsMenuChanged() { 179 return mOptionsMenuHasFrequents != internalHasFrequents(); 180 } 181 182 private void invalidateOptionsMenuIfNeeded() { 183 if (isOptionsMenuChanged()) { 184 getActivity().invalidateOptionsMenu(); 185 } 186 } 187 188 private String getEmptyStateText() { 189 String emptyText; 190 switch (mDisplayType) { 191 case STREQUENT: 192 case STREQUENT_PHONE_ONLY: 193 case STARRED_ONLY: 194 emptyText = getString(R.string.listTotalAllContactsZeroStarred); 195 break; 196 case FREQUENT_ONLY: 197 case GROUP_MEMBERS: 198 emptyText = getString(R.string.noContacts); 199 break; 200 default: 201 throw new IllegalArgumentException("Unrecognized DisplayType " + mDisplayType); 202 } 203 return emptyText; 204 } 205 206 public void setListener(Listener listener) { 207 mListener = listener; 208 } 209 210 private ContactTileView.Listener mAdapterListener = 211 new ContactTileView.Listener() { 212 @Override 213 public void onContactSelected(Uri contactUri, Rect targetRect) { 214 if (mListener != null) { 215 mListener.onContactSelected(contactUri, targetRect); 216 } 217 } 218 219 @Override 220 public void onCallNumberDirectly(String phoneNumber) { 221 if (mListener != null) { 222 mListener.onCallNumberDirectly(phoneNumber); 223 } 224 } 225 226 @Override 227 public int getApproximateTileWidth() { 228 return getView().getWidth() / mAdapter.getColumnCount(); 229 } 230 }; 231 } 232