• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.contacts.group;
18 
19 import com.android.contacts.ContactPhotoManager;
20 import com.android.contacts.GroupMemberLoader;
21 import com.android.contacts.GroupMetaDataLoader;
22 import com.android.contacts.R;
23 import com.android.contacts.interactions.GroupDeletionDialogFragment;
24 import com.android.contacts.list.ContactTileAdapter;
25 import com.android.contacts.list.ContactTileAdapter.DisplayType;
26 import com.android.contacts.model.AccountType;
27 import com.android.contacts.model.AccountTypeManager;
28 
29 import android.app.Activity;
30 import android.app.Fragment;
31 import android.app.LoaderManager;
32 import android.app.LoaderManager.LoaderCallbacks;
33 import android.content.ContentUris;
34 import android.content.Context;
35 import android.content.CursorLoader;
36 import android.content.Intent;
37 import android.content.Loader;
38 import android.content.res.Resources;
39 import android.database.Cursor;
40 import android.net.Uri;
41 import android.os.Bundle;
42 import android.provider.ContactsContract.Groups;
43 import android.text.TextUtils;
44 import android.view.LayoutInflater;
45 import android.view.Menu;
46 import android.view.MenuInflater;
47 import android.view.MenuItem;
48 import android.view.View;
49 import android.view.View.OnClickListener;
50 import android.view.ViewGroup;
51 import android.widget.AbsListView;
52 import android.widget.AbsListView.OnScrollListener;
53 import android.widget.ListView;
54 import android.widget.TextView;
55 
56 /**
57  * Displays the details of a group and shows a list of actions possible for the group.
58  */
59 public class GroupDetailFragment extends Fragment implements OnScrollListener {
60 
61     public static interface Listener {
62         /**
63          * The group title has been loaded
64          */
onGroupTitleUpdated(String title)65         public void onGroupTitleUpdated(String title);
66 
67         /**
68          * The number of group members has been determined
69          */
onGroupSizeUpdated(String size)70         public void onGroupSizeUpdated(String size);
71 
72         /**
73          * The account type and dataset have been determined.
74          */
onAccountTypeUpdated(String accountTypeString, String dataSet)75         public void onAccountTypeUpdated(String accountTypeString, String dataSet);
76 
77         /**
78          * User decided to go to Edit-Mode
79          */
onEditRequested(Uri groupUri)80         public void onEditRequested(Uri groupUri);
81 
82         /**
83          * Contact is selected and should launch details page
84          */
onContactSelected(Uri contactUri)85         public void onContactSelected(Uri contactUri);
86     }
87 
88     private static final String TAG = "GroupDetailFragment";
89 
90     private static final int LOADER_METADATA = 0;
91     private static final int LOADER_MEMBERS = 1;
92 
93     private Context mContext;
94 
95     private View mRootView;
96     private ViewGroup mGroupSourceViewContainer;
97     private View mGroupSourceView;
98     private TextView mGroupTitle;
99     private TextView mGroupSize;
100     private ListView mMemberListView;
101 
102     private Listener mListener;
103 
104     private ContactTileAdapter mAdapter;
105     private ContactPhotoManager mPhotoManager;
106     private AccountTypeManager mAccountTypeManager;
107 
108     private Uri mGroupUri;
109     private long mGroupId;
110     private String mGroupName;
111     private String mAccountTypeString;
112     private String mDataSet;
113     private boolean mIsReadOnly;
114 
115     private boolean mShowGroupActionInActionBar;
116     private boolean mOptionsMenuGroupDeletable;
117     private boolean mOptionsMenuGroupPresent;
118     private boolean mCloseActivityAfterDelete;
119 
GroupDetailFragment()120     public GroupDetailFragment() {
121     }
122 
123     @Override
onAttach(Activity activity)124     public void onAttach(Activity activity) {
125         super.onAttach(activity);
126         mContext = activity;
127         mAccountTypeManager = AccountTypeManager.getInstance(mContext);
128 
129         Resources res = getResources();
130         int columnCount = res.getInteger(R.integer.contact_tile_column_count);
131 
132         mAdapter = new ContactTileAdapter(activity, mContactTileListener, columnCount,
133                 DisplayType.GROUP_MEMBERS);
134 
135         configurePhotoLoader();
136     }
137 
138     @Override
onDetach()139     public void onDetach() {
140         super.onDetach();
141         mContext = null;
142     }
143 
144     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState)145     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
146         setHasOptionsMenu(true);
147         mRootView = inflater.inflate(R.layout.group_detail_fragment, container, false);
148         mGroupTitle = (TextView) mRootView.findViewById(R.id.group_title);
149         mGroupSize = (TextView) mRootView.findViewById(R.id.group_size);
150         mGroupSourceViewContainer = (ViewGroup) mRootView.findViewById(
151                 R.id.group_source_view_container);
152         mMemberListView = (ListView) mRootView.findViewById(android.R.id.list);
153         mMemberListView.setAdapter(mAdapter);
154 
155         return mRootView;
156     }
157 
loadGroup(Uri groupUri)158     public void loadGroup(Uri groupUri) {
159         mGroupUri= groupUri;
160         startGroupMetadataLoader();
161     }
162 
setQuickContact(boolean enableQuickContact)163     public void setQuickContact(boolean enableQuickContact) {
164         mAdapter.enableQuickContact(enableQuickContact);
165     }
166 
configurePhotoLoader()167     private void configurePhotoLoader() {
168         if (mContext != null) {
169             if (mPhotoManager == null) {
170                 mPhotoManager = ContactPhotoManager.getInstance(mContext);
171             }
172             if (mMemberListView != null) {
173                 mMemberListView.setOnScrollListener(this);
174             }
175             if (mAdapter != null) {
176                 mAdapter.setPhotoLoader(mPhotoManager);
177             }
178         }
179     }
180 
setListener(Listener value)181     public void setListener(Listener value) {
182         mListener = value;
183     }
184 
setShowGroupSourceInActionBar(boolean show)185     public void setShowGroupSourceInActionBar(boolean show) {
186         mShowGroupActionInActionBar = show;
187     }
188 
189     /**
190      * Start the loader to retrieve the metadata for this group.
191      */
startGroupMetadataLoader()192     private void startGroupMetadataLoader() {
193         getLoaderManager().destroyLoader(LOADER_METADATA);
194         getLoaderManager().restartLoader(LOADER_METADATA, null, mGroupMetadataLoaderListener);
195     }
196 
197     /**
198      * Start the loader to retrieve the list of group members.
199      */
startGroupMembersLoader()200     private void startGroupMembersLoader() {
201         getLoaderManager().destroyLoader(LOADER_MEMBERS);
202         getLoaderManager().restartLoader(LOADER_MEMBERS, null, mGroupMemberListLoaderListener);
203     }
204 
205     private final ContactTileAdapter.Listener mContactTileListener =
206             new ContactTileAdapter.Listener() {
207 
208         @Override
209         public void onContactSelected(Uri contactUri) {
210             mListener.onContactSelected(contactUri);
211         }
212     };
213 
214     /**
215      * The listener for the group metadata loader.
216      */
217     private final LoaderManager.LoaderCallbacks<Cursor> mGroupMetadataLoaderListener =
218             new LoaderCallbacks<Cursor>() {
219 
220         @Override
221         public CursorLoader onCreateLoader(int id, Bundle args) {
222             return new GroupMetaDataLoader(mContext, mGroupUri);
223         }
224 
225         @Override
226         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
227             data.moveToPosition(-1);
228             if (data.moveToNext()) {
229                 boolean deleted = data.getInt(GroupMetaDataLoader.DELETED) == 1;
230                 if (!deleted) {
231                     bindGroupMetaData(data);
232 
233                     // Retrieve the list of members
234                     startGroupMembersLoader();
235                     return;
236                 }
237             }
238             updateSize(-1);
239             updateTitle(null);
240         }
241 
242         @Override
243         public void onLoaderReset(Loader<Cursor> loader) {}
244     };
245 
246     /**
247      * The listener for the group members list loader
248      */
249     private final LoaderManager.LoaderCallbacks<Cursor> mGroupMemberListLoaderListener =
250             new LoaderCallbacks<Cursor>() {
251 
252         @Override
253         public CursorLoader onCreateLoader(int id, Bundle args) {
254             return new GroupMemberLoader(mContext, mGroupId);
255         }
256 
257         @Override
258         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
259             updateSize(data.getCount());
260             mAdapter.setContactCursor(data);
261         }
262 
263         @Override
264         public void onLoaderReset(Loader<Cursor> loader) {}
265     };
266 
bindGroupMetaData(Cursor cursor)267     private void bindGroupMetaData(Cursor cursor) {
268         cursor.moveToPosition(-1);
269         if (cursor.moveToNext()) {
270             mAccountTypeString = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
271             mDataSet = cursor.getString(GroupMetaDataLoader.DATA_SET);
272             mGroupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
273             mGroupName = cursor.getString(GroupMetaDataLoader.TITLE);
274             mIsReadOnly = cursor.getInt(GroupMetaDataLoader.IS_READ_ONLY) == 1;
275             updateTitle(mGroupName);
276             // Must call invalidate so that the option menu will get updated
277             getActivity().invalidateOptionsMenu ();
278 
279             final String accountTypeString = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
280             final String dataSet = cursor.getString(GroupMetaDataLoader.DATA_SET);
281             updateAccountType(accountTypeString, dataSet);
282         }
283     }
284 
updateTitle(String title)285     private void updateTitle(String title) {
286         if (mGroupTitle != null) {
287             mGroupTitle.setText(title);
288         } else {
289             mListener.onGroupTitleUpdated(title);
290         }
291     }
292 
293     /**
294      * Display the count of the number of group members.
295      * @param size of the group (can be -1 if no size could be determined)
296      */
updateSize(int size)297     private void updateSize(int size) {
298         String groupSizeString;
299         if (size == -1) {
300             groupSizeString = null;
301         } else {
302             String groupSizeTemplateString = getResources().getQuantityString(
303                     R.plurals.num_contacts_in_group, size);
304             AccountType accountType = mAccountTypeManager.getAccountType(mAccountTypeString,
305                     mDataSet);
306             groupSizeString = String.format(groupSizeTemplateString, size,
307                     accountType.getDisplayLabel(mContext));
308         }
309 
310         if (mGroupSize != null) {
311             mGroupSize.setText(groupSizeString);
312         } else {
313             mListener.onGroupSizeUpdated(groupSizeString);
314         }
315     }
316 
317     /**
318      * Once the account type, group source action, and group source URI have been determined
319      * (based on the result from the {@link Loader}), then we can display this to the user in 1 of
320      * 2 ways depending on screen size and orientation: either as a button in the action bar or as
321      * a button in a static header on the page.
322      */
updateAccountType(final String accountTypeString, final String dataSet)323     private void updateAccountType(final String accountTypeString, final String dataSet) {
324 
325         // If the group action should be shown in the action bar, then pass the data to the
326         // listener who will take care of setting up the view and click listener. There is nothing
327         // else to be done by this {@link Fragment}.
328         if (mShowGroupActionInActionBar) {
329             mListener.onAccountTypeUpdated(accountTypeString, dataSet);
330             return;
331         }
332 
333         final AccountTypeManager manager = AccountTypeManager.getInstance(getActivity());
334         final AccountType accountType =
335                 manager.getAccountType(accountTypeString, dataSet);
336 
337         // Otherwise, if the {@link Fragment} needs to create and setup the button, then first
338         // verify that there is a valid action.
339         if (!TextUtils.isEmpty(accountType.getViewGroupActivity())) {
340             if (mGroupSourceView == null) {
341                 mGroupSourceView = GroupDetailDisplayUtils.getNewGroupSourceView(mContext);
342                 // Figure out how to add the view to the fragment.
343                 // If there is a static header with a container for the group source view, insert
344                 // the view there.
345                 if (mGroupSourceViewContainer != null) {
346                     mGroupSourceViewContainer.addView(mGroupSourceView);
347                 }
348             }
349 
350             // Rebind the data since this action can change if the loader returns updated data
351             mGroupSourceView.setVisibility(View.VISIBLE);
352             GroupDetailDisplayUtils.bindGroupSourceView(mContext, mGroupSourceView,
353                     accountTypeString, dataSet);
354             mGroupSourceView.setOnClickListener(new OnClickListener() {
355                 @Override
356                 public void onClick(View v) {
357                     final Uri uri = ContentUris.withAppendedId(Groups.CONTENT_URI, mGroupId);
358                     final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
359                     intent.setClassName(accountType.resPackageName,
360                             accountType.getViewGroupActivity());
361                     startActivity(intent);
362                 }
363             });
364         } else if (mGroupSourceView != null) {
365             mGroupSourceView.setVisibility(View.GONE);
366         }
367     }
368 
369     @Override
onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)370     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
371             int totalItemCount) {
372     }
373 
374     @Override
onScrollStateChanged(AbsListView view, int scrollState)375     public void onScrollStateChanged(AbsListView view, int scrollState) {
376         if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
377             mPhotoManager.pause();
378         } else {
379             mPhotoManager.resume();
380         }
381     }
382 
383     @Override
onCreateOptionsMenu(Menu menu, final MenuInflater inflater)384     public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
385         inflater.inflate(R.menu.view_group, menu);
386     }
387 
isOptionsMenuChanged()388     public boolean isOptionsMenuChanged() {
389         return mOptionsMenuGroupDeletable != isGroupDeletable() &&
390                 mOptionsMenuGroupPresent != isGroupPresent();
391     }
392 
isGroupDeletable()393     public boolean isGroupDeletable() {
394         return mGroupUri != null && !mIsReadOnly;
395     }
396 
isGroupPresent()397     public boolean isGroupPresent() {
398         return mGroupUri != null;
399     }
400 
401     @Override
onPrepareOptionsMenu(Menu menu)402     public void onPrepareOptionsMenu(Menu menu) {
403         mOptionsMenuGroupDeletable = isGroupDeletable() && isVisible();
404         mOptionsMenuGroupPresent = isGroupPresent() && isVisible();
405 
406         final MenuItem editMenu = menu.findItem(R.id.menu_edit_group);
407         editMenu.setVisible(mOptionsMenuGroupPresent);
408 
409         final MenuItem deleteMenu = menu.findItem(R.id.menu_delete_group);
410         deleteMenu.setVisible(mOptionsMenuGroupDeletable);
411     }
412 
413     @Override
onOptionsItemSelected(MenuItem item)414     public boolean onOptionsItemSelected(MenuItem item) {
415         switch (item.getItemId()) {
416             case R.id.menu_edit_group: {
417                 if (mListener != null) mListener.onEditRequested(mGroupUri);
418                 break;
419             }
420             case R.id.menu_delete_group: {
421                 GroupDeletionDialogFragment.show(getFragmentManager(), mGroupId, mGroupName,
422                         mCloseActivityAfterDelete);
423                 return true;
424             }
425         }
426         return false;
427     }
428 
closeActivityAfterDelete(boolean closeActivity)429     public void closeActivityAfterDelete(boolean closeActivity) {
430         mCloseActivityAfterDelete = closeActivity;
431     }
432 
getGroupId()433     public long getGroupId() {
434         return mGroupId;
435     }
436 }
437