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 */
16package com.android.contacts.list;
17
18import android.app.Activity;
19import android.app.Fragment;
20import android.app.LoaderManager;
21import android.app.LoaderManager.LoaderCallbacks;
22import android.content.CursorLoader;
23import android.content.Loader;
24import android.content.res.Resources;
25import android.database.Cursor;
26import android.graphics.Rect;
27import android.net.Uri;
28import android.os.Bundle;
29import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32import android.widget.ListView;
33import android.widget.TextView;
34
35import com.android.contacts.common.ContactPhotoManager;
36import com.android.contacts.common.ContactTileLoaderFactory;
37import com.android.contacts.R;
38import com.android.contacts.common.list.ContactTileAdapter;
39import com.android.contacts.common.list.ContactTileView;
40import com.android.contacts.common.list.ContactTileAdapter.DisplayType;
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 */
50public 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