GroupDetailFragment.java revision 7979ea27c046dfc2d9305b3282233046acef6ec4
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
17package com.android.contacts.group;
18
19import com.android.contacts.ContactPhotoManager;
20import com.android.contacts.GroupMemberLoader;
21import com.android.contacts.GroupMetaDataLoader;
22import com.android.contacts.R;
23import com.android.contacts.interactions.GroupDeletionDialogFragment;
24import com.android.contacts.list.ContactTileAdapter;
25import com.android.contacts.list.ContactTileAdapter.DisplayType;
26import com.android.contacts.model.AccountType;
27import com.android.contacts.model.AccountTypeManager;
28
29import android.app.Activity;
30import android.app.Fragment;
31import android.app.LoaderManager;
32import android.app.LoaderManager.LoaderCallbacks;
33import android.content.Context;
34import android.content.CursorLoader;
35import android.content.Loader;
36import android.content.res.Resources;
37import android.database.Cursor;
38import android.net.Uri;
39import android.os.Bundle;
40import android.util.Log;
41import android.view.LayoutInflater;
42import android.view.Menu;
43import android.view.MenuInflater;
44import android.view.MenuItem;
45import android.view.View;
46import android.view.ViewGroup;
47import android.widget.AbsListView;
48import android.widget.AbsListView.OnScrollListener;
49import android.widget.ListView;
50import android.widget.TextView;
51
52/**
53 * Displays the details of a group and shows a list of actions possible for the group.
54 */
55public class GroupDetailFragment extends Fragment implements OnScrollListener {
56
57    public static interface Listener {
58        /**
59         * The group title has been loaded
60         */
61        public void onGroupTitleUpdated(String title);
62
63        /**
64         * The number of group members has been determined
65         */
66        public void onGroupSizeUpdated(String size);
67
68        /**
69         * User decided to go to Edit-Mode
70         */
71        public void onEditRequested(Uri groupUri);
72
73        /**
74         * Contact is selected and should launch details page
75         */
76        public void onContactSelected(Uri contactUri);
77    }
78
79    private static final String TAG = "GroupDetailFragment";
80
81    private static final int LOADER_METADATA = 0;
82    private static final int LOADER_MEMBERS = 1;
83
84    private Context mContext;
85
86    private View mRootView;
87    private TextView mGroupTitle;
88    private TextView mGroupSize;
89    private ListView mMemberListView;
90
91    private Listener mListener;
92
93    private ContactTileAdapter mAdapter;
94    private ContactPhotoManager mPhotoManager;
95    private AccountTypeManager mAccountTypeManager;
96
97    private Uri mGroupUri;
98    private long mGroupId;
99    private String mGroupName;
100    private String mAccountTypeString;
101
102    private boolean mOptionsMenuEditable;
103    private boolean mCloseActivityAfterDelete;
104
105    public GroupDetailFragment() {
106    }
107
108    @Override
109    public void onAttach(Activity activity) {
110        super.onAttach(activity);
111        mContext = activity;
112        mAccountTypeManager = AccountTypeManager.getInstance(mContext);
113
114        Resources res = getResources();
115        int columnCount = res.getInteger(R.integer.contact_tile_column_count);
116
117        mAdapter = new ContactTileAdapter(activity, mContactTileListener, columnCount,
118                DisplayType.GROUP_MEMBERS);
119
120        configurePhotoLoader();
121    }
122
123    @Override
124    public void onDetach() {
125        super.onDetach();
126        mContext = null;
127    }
128
129    @Override
130    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
131        setHasOptionsMenu(true);
132        mRootView = inflater.inflate(R.layout.group_detail_fragment, container, false);
133        mGroupTitle = (TextView) mRootView.findViewById(R.id.group_title);
134        mGroupSize = (TextView) mRootView.findViewById(R.id.group_size);
135        mMemberListView = (ListView) mRootView.findViewById(android.R.id.list);
136
137        return mRootView;
138    }
139
140    public void loadGroup(Uri groupUri) {
141        mGroupUri= groupUri;
142        startGroupMetadataLoader();
143    }
144
145    public void setQuickContact(boolean enableQuickContact) {
146        mAdapter.enableQuickContact(enableQuickContact);
147    }
148
149    private void configureAdapter(long groupId) {
150        mGroupId = groupId;
151        mMemberListView.setAdapter(mAdapter);
152    }
153
154    private void configurePhotoLoader() {
155        if (mContext != null) {
156            if (mPhotoManager == null) {
157                mPhotoManager = ContactPhotoManager.getInstance(mContext);
158            }
159            if (mMemberListView != null) {
160                mMemberListView.setOnScrollListener(this);
161            }
162            if (mAdapter != null) {
163                mAdapter.setPhotoLoader(mPhotoManager);
164            }
165        }
166    }
167
168    public void setListener(Listener value) {
169        mListener = value;
170    }
171
172    /**
173     * Start the loader to retrieve the metadata for this group.
174     */
175    private void startGroupMetadataLoader() {
176        getLoaderManager().destroyLoader(LOADER_METADATA);
177        getLoaderManager().restartLoader(LOADER_METADATA, null, mGroupMetadataLoaderListener);
178    }
179
180    /**
181     * Start the loader to retrieve the list of group members.
182     */
183    private void startGroupMembersLoader() {
184        getLoaderManager().destroyLoader(LOADER_MEMBERS);
185        getLoaderManager().restartLoader(LOADER_MEMBERS, null, mGroupMemberListLoaderListener);
186    }
187
188    private final ContactTileAdapter.Listener mContactTileListener =
189            new ContactTileAdapter.Listener() {
190
191        @Override
192        public void onContactSelected(Uri contactUri) {
193            mListener.onContactSelected(contactUri);
194        }
195    };
196
197    /**
198     * The listener for the group metadata loader.
199     */
200    private final LoaderManager.LoaderCallbacks<Cursor> mGroupMetadataLoaderListener =
201            new LoaderCallbacks<Cursor>() {
202
203        @Override
204        public CursorLoader onCreateLoader(int id, Bundle args) {
205            return new GroupMetaDataLoader(mContext, mGroupUri);
206        }
207
208        @Override
209        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
210            data.moveToPosition(-1);
211            if (data.moveToNext()) {
212                boolean deleted = data.getInt(GroupMetaDataLoader.DELETED) == 1;
213                if (!deleted) {
214                    bindGroupMetaData(data);
215
216                    // Retrieve the list of members
217                    configureAdapter(mGroupId);
218                    startGroupMembersLoader();
219                    return;
220                }
221            }
222            updateSize(-1);
223            updateTitle(null);
224        }
225
226        @Override
227        public void onLoaderReset(Loader<Cursor> loader) {}
228    };
229
230    /**
231     * The listener for the group members list loader
232     */
233    private final LoaderManager.LoaderCallbacks<Cursor> mGroupMemberListLoaderListener =
234            new LoaderCallbacks<Cursor>() {
235
236        @Override
237        public CursorLoader onCreateLoader(int id, Bundle args) {
238            return new GroupMemberLoader(mContext, mGroupId);
239        }
240
241        @Override
242        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
243            updateSize(data.getCount());
244            mAdapter.setContactCursor(data);
245        }
246
247        @Override
248        public void onLoaderReset(Loader<Cursor> loader) {}
249    };
250
251    private void bindGroupMetaData(Cursor cursor) {
252        cursor.moveToPosition(-1);
253        if (cursor.moveToNext()) {
254            mAccountTypeString = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
255            mGroupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
256            mGroupName = cursor.getString(GroupMetaDataLoader.TITLE);
257            updateTitle(mGroupName);
258
259            // TODO: Replace by real button
260            final String action = cursor.getString(GroupMetaDataLoader.ACTION);
261            final String actionUri = cursor.getString(GroupMetaDataLoader.ACTION_URI);
262            Log.d(TAG, "Group open action: " + action + ", uri: " + actionUri);
263        }
264    }
265
266    private void updateTitle(String title) {
267        if (mGroupTitle != null) {
268            mGroupTitle.setText(title);
269        } else {
270            mListener.onGroupTitleUpdated(title);
271        }
272    }
273
274    /**
275     * Display the count of the number of group members.
276     * @param size of the group (can be -1 if no size could be determined)
277     */
278    private void updateSize(int size) {
279        String groupSizeString;
280        if (size == -1) {
281            groupSizeString = null;
282        } else {
283            String groupSizeTemplateString = getResources().getQuantityString(
284                    R.plurals.num_contacts_in_group, size);
285            AccountType accountType = mAccountTypeManager.getAccountType(mAccountTypeString);
286            groupSizeString = String.format(groupSizeTemplateString, size,
287                    accountType.getDisplayLabel(mContext));
288        }
289
290        if (mGroupSize != null) {
291            mGroupSize.setText(groupSizeString);
292        } else {
293            mListener.onGroupSizeUpdated(groupSizeString);
294        }
295    }
296
297    @Override
298    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
299            int totalItemCount) {
300    }
301
302    @Override
303    public void onScrollStateChanged(AbsListView view, int scrollState) {
304        if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
305            mPhotoManager.pause();
306        } else {
307            mPhotoManager.resume();
308        }
309    }
310
311    @Override
312    public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
313        inflater.inflate(R.menu.view_group, menu);
314    }
315
316    public boolean isOptionsMenuChanged() {
317        return mOptionsMenuEditable != isGroupEditable();
318    }
319
320    public boolean isGroupEditable() {
321        // TODO: This should check the group_is_read_only flag. Modify GroupMetaDataLoader.
322        // Bug: 4601729.
323        return mGroupUri != null;
324    }
325
326    @Override
327    public void onPrepareOptionsMenu(Menu menu) {
328        mOptionsMenuEditable = isGroupEditable();
329
330        final MenuItem editMenu = menu.findItem(R.id.menu_edit_group);
331        editMenu.setVisible(mOptionsMenuEditable);
332
333        final MenuItem deleteMenu = menu.findItem(R.id.menu_delete_group);
334        deleteMenu.setVisible(mOptionsMenuEditable);
335    }
336
337    @Override
338    public boolean onOptionsItemSelected(MenuItem item) {
339        switch (item.getItemId()) {
340            case R.id.menu_edit_group: {
341                if (mListener != null) mListener.onEditRequested(mGroupUri);
342                break;
343            }
344            case R.id.menu_delete_group: {
345                GroupDeletionDialogFragment.show(getFragmentManager(), mGroupId, mGroupName,
346                        mCloseActivityAfterDelete);
347                return true;
348            }
349        }
350        return false;
351    }
352
353    public void closeActivityAfterDelete(boolean closeActivity) {
354        mCloseActivityAfterDelete = closeActivity;
355    }
356}
357