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 android.content.ContentUris;
20import android.content.Context;
21import android.database.Cursor;
22import android.net.Uri;
23import android.provider.ContactsContract.Groups;
24import android.view.LayoutInflater;
25import android.view.View;
26import android.view.ViewGroup;
27import android.widget.BaseAdapter;
28import android.widget.TextView;
29
30import com.android.contacts.GroupListLoader;
31import com.android.contacts.R;
32import com.android.contacts.common.model.account.AccountType;
33import com.android.contacts.common.model.AccountTypeManager;
34import com.google.common.base.Objects;
35
36/**
37 * Adapter to populate the list of groups.
38 */
39public class GroupBrowseListAdapter extends BaseAdapter {
40
41    private final Context mContext;
42    private final LayoutInflater mLayoutInflater;
43    private final AccountTypeManager mAccountTypeManager;
44
45    private Cursor mCursor;
46
47    private boolean mSelectionVisible;
48    private Uri mSelectedGroupUri;
49
50    public GroupBrowseListAdapter(Context context) {
51        mContext = context;
52        mLayoutInflater = LayoutInflater.from(context);
53        mAccountTypeManager = AccountTypeManager.getInstance(mContext);
54    }
55
56    public void setCursor(Cursor cursor) {
57        mCursor = cursor;
58
59        // If there's no selected group already and the cursor is valid, then by default, select the
60        // first group
61        if (mSelectedGroupUri == null && cursor != null && cursor.getCount() > 0) {
62            GroupListItem firstItem = getItem(0);
63            long groupId = (firstItem == null) ? 0 : firstItem.getGroupId();
64            mSelectedGroupUri = getGroupUriFromId(groupId);
65        }
66
67        notifyDataSetChanged();
68    }
69
70    public int getSelectedGroupPosition() {
71        if (mSelectedGroupUri == null || mCursor == null || mCursor.getCount() == 0) {
72            return -1;
73        }
74
75        int index = 0;
76        mCursor.moveToPosition(-1);
77        while (mCursor.moveToNext()) {
78            long groupId = mCursor.getLong(GroupListLoader.GROUP_ID);
79            Uri uri = getGroupUriFromId(groupId);
80            if (mSelectedGroupUri.equals(uri)) {
81                  return index;
82            }
83            index++;
84        }
85        return -1;
86    }
87
88    public void setSelectionVisible(boolean flag) {
89        mSelectionVisible = flag;
90    }
91
92    public void setSelectedGroup(Uri groupUri) {
93        mSelectedGroupUri = groupUri;
94    }
95
96    private boolean isSelectedGroup(Uri groupUri) {
97        return mSelectedGroupUri != null && mSelectedGroupUri.equals(groupUri);
98    }
99
100    public Uri getSelectedGroup() {
101        return mSelectedGroupUri;
102    }
103
104    @Override
105    public int getCount() {
106        return (mCursor == null || mCursor.isClosed()) ? 0 : mCursor.getCount();
107    }
108
109    @Override
110    public long getItemId(int position) {
111        return position;
112    }
113
114    @Override
115    public GroupListItem getItem(int position) {
116        if (mCursor == null || mCursor.isClosed() || !mCursor.moveToPosition(position)) {
117            return null;
118        }
119        String accountName = mCursor.getString(GroupListLoader.ACCOUNT_NAME);
120        String accountType = mCursor.getString(GroupListLoader.ACCOUNT_TYPE);
121        String dataSet = mCursor.getString(GroupListLoader.DATA_SET);
122        long groupId = mCursor.getLong(GroupListLoader.GROUP_ID);
123        String title = mCursor.getString(GroupListLoader.TITLE);
124        int memberCount = mCursor.getInt(GroupListLoader.MEMBER_COUNT);
125
126        // Figure out if this is the first group for this account name / account type pair by
127        // checking the previous entry. This is to determine whether or not we need to display an
128        // account header in this item.
129        int previousIndex = position - 1;
130        boolean isFirstGroupInAccount = true;
131        if (previousIndex >= 0 && mCursor.moveToPosition(previousIndex)) {
132            String previousGroupAccountName = mCursor.getString(GroupListLoader.ACCOUNT_NAME);
133            String previousGroupAccountType = mCursor.getString(GroupListLoader.ACCOUNT_TYPE);
134            String previousGroupDataSet = mCursor.getString(GroupListLoader.DATA_SET);
135
136            if (accountName.equals(previousGroupAccountName) &&
137                    accountType.equals(previousGroupAccountType) &&
138                    Objects.equal(dataSet, previousGroupDataSet)) {
139                isFirstGroupInAccount = false;
140            }
141        }
142
143        return new GroupListItem(accountName, accountType, dataSet, groupId, title,
144                isFirstGroupInAccount, memberCount);
145    }
146
147    @Override
148    public View getView(int position, View convertView, ViewGroup parent) {
149        GroupListItem entry = getItem(position);
150        View result;
151        GroupListItemViewCache viewCache;
152        if (convertView != null) {
153            result = convertView;
154            viewCache = (GroupListItemViewCache) result.getTag();
155        } else {
156            result = mLayoutInflater.inflate(R.layout.group_browse_list_item, parent, false);
157            viewCache = new GroupListItemViewCache(result);
158            result.setTag(viewCache);
159        }
160
161        // Add a header if this is the first group in an account and hide the divider
162        if (entry.isFirstGroupInAccount()) {
163            bindHeaderView(entry, viewCache);
164            viewCache.accountHeader.setVisibility(View.VISIBLE);
165            viewCache.divider.setVisibility(View.GONE);
166            if (position == 0) {
167                // Have the list's top padding in the first header.
168                //
169                // This allows the ListView to show correct fading effect on top.
170                // If we have topPadding in the ListView itself, an inappropriate padding is
171                // inserted between fading items and the top edge.
172                viewCache.accountHeaderExtraTopPadding.setVisibility(View.VISIBLE);
173            } else {
174                viewCache.accountHeaderExtraTopPadding.setVisibility(View.GONE);
175            }
176        } else {
177            viewCache.accountHeader.setVisibility(View.GONE);
178            viewCache.divider.setVisibility(View.VISIBLE);
179            viewCache.accountHeaderExtraTopPadding.setVisibility(View.GONE);
180        }
181
182        // Bind the group data
183        Uri groupUri = getGroupUriFromId(entry.getGroupId());
184        String memberCountString = mContext.getResources().getQuantityString(
185                R.plurals.group_list_num_contacts_in_group, entry.getMemberCount(),
186                entry.getMemberCount());
187        viewCache.setUri(groupUri);
188        viewCache.groupTitle.setText(entry.getTitle());
189        viewCache.groupMemberCount.setText(memberCountString);
190
191        if (mSelectionVisible) {
192            result.setActivated(isSelectedGroup(groupUri));
193        }
194        return result;
195    }
196
197    private void bindHeaderView(GroupListItem entry, GroupListItemViewCache viewCache) {
198        AccountType accountType = mAccountTypeManager.getAccountType(
199                entry.getAccountType(), entry.getDataSet());
200        viewCache.accountType.setText(accountType.getDisplayLabel(mContext));
201        viewCache.accountName.setText(entry.getAccountName());
202    }
203
204    private static Uri getGroupUriFromId(long groupId) {
205        return ContentUris.withAppendedId(Groups.CONTENT_URI, groupId);
206    }
207
208    /**
209     * Cache of the children views of a contact detail entry represented by a
210     * {@link GroupListItem}
211     */
212    public static class GroupListItemViewCache {
213        public final TextView accountType;
214        public final TextView accountName;
215        public final TextView groupTitle;
216        public final TextView groupMemberCount;
217        public final View accountHeader;
218        public final View accountHeaderExtraTopPadding;
219        public final View divider;
220        private Uri mUri;
221
222        public GroupListItemViewCache(View view) {
223            accountType = (TextView) view.findViewById(R.id.account_type);
224            accountName = (TextView) view.findViewById(R.id.account_name);
225            groupTitle = (TextView) view.findViewById(R.id.label);
226            groupMemberCount = (TextView) view.findViewById(R.id.count);
227            accountHeader = view.findViewById(R.id.group_list_header);
228            accountHeaderExtraTopPadding = view.findViewById(R.id.header_extra_top_padding);
229            divider = view.findViewById(R.id.divider);
230        }
231
232        public void setUri(Uri uri) {
233            mUri = uri;
234        }
235
236        public Uri getUri() {
237            return mUri;
238        }
239    }
240}
241