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