1 /* 2 * Copyright (C) 2010 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.editor; 18 19 import com.android.contacts.GroupMetaDataLoader; 20 import com.android.contacts.R; 21 import com.android.contacts.interactions.GroupCreationDialogFragment; 22 import com.android.contacts.model.DataKind; 23 import com.android.contacts.model.EntityDelta; 24 import com.android.contacts.model.EntityDelta.ValuesDelta; 25 import com.android.contacts.model.EntityModifier; 26 import com.android.internal.util.Objects; 27 28 import android.app.Activity; 29 import android.content.Context; 30 import android.content.res.ColorStateList; 31 import android.content.res.Resources; 32 import android.database.Cursor; 33 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 34 import android.provider.ContactsContract.RawContacts; 35 import android.util.AttributeSet; 36 import android.view.View; 37 import android.view.View.OnClickListener; 38 import android.widget.AdapterView; 39 import android.widget.AdapterView.OnItemClickListener; 40 import android.widget.ArrayAdapter; 41 import android.widget.LinearLayout; 42 import android.widget.ListPopupWindow; 43 import android.widget.ListView; 44 import android.widget.TextView; 45 46 import java.util.ArrayList; 47 48 /** 49 * An editor for group membership. Displays the current group membership list and 50 * brings up a dialog to change it. 51 */ 52 public class GroupMembershipView extends LinearLayout 53 implements OnClickListener, OnItemClickListener { 54 55 private static final int CREATE_NEW_GROUP_GROUP_ID = 133; 56 57 public static final class GroupSelectionItem { 58 private final long mGroupId; 59 private final String mTitle; 60 private boolean mChecked; 61 GroupSelectionItem(long groupId, String title, boolean checked)62 public GroupSelectionItem(long groupId, String title, boolean checked) { 63 this.mGroupId = groupId; 64 this.mTitle = title; 65 mChecked = checked; 66 } 67 getGroupId()68 public long getGroupId() { 69 return mGroupId; 70 } 71 isChecked()72 public boolean isChecked() { 73 return mChecked; 74 } 75 setChecked(boolean checked)76 public void setChecked(boolean checked) { 77 mChecked = checked; 78 } 79 80 @Override toString()81 public String toString() { 82 return mTitle; 83 } 84 } 85 86 private EntityDelta mState; 87 private Cursor mGroupMetaData; 88 private String mAccountName; 89 private String mAccountType; 90 private String mDataSet; 91 private TextView mGroupList; 92 private ArrayAdapter<GroupSelectionItem> mAdapter; 93 private long mDefaultGroupId; 94 private long mFavoritesGroupId; 95 private ListPopupWindow mPopup; 96 private DataKind mKind; 97 private boolean mDefaultGroupVisibilityKnown; 98 private boolean mDefaultGroupVisible; 99 100 private String mNoGroupString; 101 private int mPrimaryTextColor; 102 private int mSecondaryTextColor; 103 GroupMembershipView(Context context)104 public GroupMembershipView(Context context) { 105 super(context); 106 } 107 GroupMembershipView(Context context, AttributeSet attrs)108 public GroupMembershipView(Context context, AttributeSet attrs) { 109 super(context, attrs); 110 } 111 112 @Override onFinishInflate()113 protected void onFinishInflate() { 114 super.onFinishInflate(); 115 Resources resources = mContext.getResources(); 116 mPrimaryTextColor = resources.getColor(R.color.primary_text_color); 117 mSecondaryTextColor = resources.getColor(R.color.secondary_text_color); 118 mNoGroupString = mContext.getString(R.string.group_edit_field_hint_text); 119 } 120 121 @Override setEnabled(boolean enabled)122 public void setEnabled(boolean enabled) { 123 super.setEnabled(enabled); 124 if (mGroupList != null) { 125 mGroupList.setEnabled(enabled); 126 } 127 } 128 setKind(DataKind kind)129 public void setKind(DataKind kind) { 130 mKind = kind; 131 TextView kindTitle = (TextView) findViewById(R.id.kind_title); 132 kindTitle.setText(getResources().getString(kind.titleRes).toUpperCase()); 133 } 134 setGroupMetaData(Cursor groupMetaData)135 public void setGroupMetaData(Cursor groupMetaData) { 136 this.mGroupMetaData = groupMetaData; 137 updateView(); 138 } 139 setState(EntityDelta state)140 public void setState(EntityDelta state) { 141 mState = state; 142 ValuesDelta values = state.getValues(); 143 mAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE); 144 mAccountName = values.getAsString(RawContacts.ACCOUNT_NAME); 145 mDataSet = values.getAsString(RawContacts.DATA_SET); 146 mDefaultGroupVisibilityKnown = false; 147 updateView(); 148 } 149 updateView()150 private void updateView() { 151 if (mGroupMetaData == null || mGroupMetaData.isClosed() || mAccountType == null 152 || mAccountName == null) { 153 setVisibility(GONE); 154 return; 155 } 156 157 boolean accountHasGroups = false; 158 mFavoritesGroupId = 0; 159 mDefaultGroupId = 0; 160 161 StringBuilder sb = new StringBuilder(); 162 mGroupMetaData.moveToPosition(-1); 163 while (mGroupMetaData.moveToNext()) { 164 String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME); 165 String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE); 166 String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET); 167 if (accountName.equals(mAccountName) && accountType.equals(mAccountType) 168 && Objects.equal(dataSet, mDataSet)) { 169 long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID); 170 if (!mGroupMetaData.isNull(GroupMetaDataLoader.FAVORITES) 171 && mGroupMetaData.getInt(GroupMetaDataLoader.FAVORITES) != 0) { 172 mFavoritesGroupId = groupId; 173 } else if (!mGroupMetaData.isNull(GroupMetaDataLoader.AUTO_ADD) 174 && mGroupMetaData.getInt(GroupMetaDataLoader.AUTO_ADD) != 0) { 175 mDefaultGroupId = groupId; 176 } else { 177 accountHasGroups = true; 178 } 179 180 // Exclude favorites from the list - they are handled with special UI (star) 181 // Also exclude the default group. 182 if (groupId != mFavoritesGroupId && groupId != mDefaultGroupId 183 && hasMembership(groupId)) { 184 String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE); 185 if (sb.length() != 0) { 186 sb.append(", "); 187 } 188 sb.append(title); 189 } 190 } 191 } 192 193 if (!accountHasGroups) { 194 setVisibility(GONE); 195 return; 196 } 197 198 if (mGroupList == null) { 199 mGroupList = (TextView) findViewById(R.id.group_list); 200 mGroupList.setOnClickListener(this); 201 } 202 203 mGroupList.setEnabled(isEnabled()); 204 if (sb.length() == 0) { 205 mGroupList.setText(mNoGroupString); 206 mGroupList.setTextColor(mSecondaryTextColor); 207 } else { 208 mGroupList.setText(sb); 209 mGroupList.setTextColor(mPrimaryTextColor); 210 } 211 setVisibility(VISIBLE); 212 213 if (!mDefaultGroupVisibilityKnown) { 214 // Only show the default group (My Contacts) if the contact is NOT in it 215 mDefaultGroupVisible = mDefaultGroupId != 0 && !hasMembership(mDefaultGroupId); 216 mDefaultGroupVisibilityKnown = true; 217 } 218 } 219 220 @Override onClick(View v)221 public void onClick(View v) { 222 if (mPopup != null && mPopup.isShowing()) { 223 mPopup.dismiss(); 224 return; 225 } 226 227 mAdapter = new ArrayAdapter<GroupSelectionItem>( 228 getContext(), R.layout.group_membership_list_item); 229 230 mGroupMetaData.moveToPosition(-1); 231 while (mGroupMetaData.moveToNext()) { 232 String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME); 233 String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE); 234 String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET); 235 if (accountName.equals(mAccountName) && accountType.equals(mAccountType) 236 && Objects.equal(dataSet, mDataSet)) { 237 long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID); 238 if (groupId != mFavoritesGroupId 239 && (groupId != mDefaultGroupId || mDefaultGroupVisible)) { 240 String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE); 241 boolean checked = hasMembership(groupId); 242 mAdapter.add(new GroupSelectionItem(groupId, title, checked)); 243 } 244 } 245 } 246 247 mAdapter.add(new GroupSelectionItem(CREATE_NEW_GROUP_GROUP_ID, 248 getContext().getString(R.string.create_group_item_label), false)); 249 250 mPopup = new ListPopupWindow(getContext(), null); 251 mPopup.setAnchorView(mGroupList); 252 mPopup.setAdapter(mAdapter); 253 mPopup.setModal(true); 254 mPopup.show(); 255 256 ListView listView = mPopup.getListView(); 257 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); 258 int count = mAdapter.getCount(); 259 for (int i = 0; i < count; i++) { 260 listView.setItemChecked(i, mAdapter.getItem(i).isChecked()); 261 } 262 263 listView.setOnItemClickListener(this); 264 } 265 266 @Override onDetachedFromWindow()267 protected void onDetachedFromWindow() { 268 super.onDetachedFromWindow(); 269 if (mPopup != null) { 270 mPopup.dismiss(); 271 mPopup = null; 272 } 273 } 274 275 @Override onItemClick(AdapterView<?> parent, View view, int position, long id)276 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 277 ListView list = (ListView) parent; 278 int count = mAdapter.getCount(); 279 280 if (list.isItemChecked(count - 1)) { 281 list.setItemChecked(count - 1, false); 282 createNewGroup(); 283 return; 284 } 285 286 for (int i = 0; i < count; i++) { 287 mAdapter.getItem(i).setChecked(list.isItemChecked(i)); 288 } 289 290 // First remove the memberships that have been unchecked 291 ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE); 292 if (entries != null) { 293 for (ValuesDelta entry : entries) { 294 if (!entry.isDelete()) { 295 Long groupId = entry.getAsLong(GroupMembership.GROUP_ROW_ID); 296 if (groupId != null && groupId != mFavoritesGroupId 297 && (groupId != mDefaultGroupId || mDefaultGroupVisible) 298 && !isGroupChecked(groupId)) { 299 entry.markDeleted(); 300 } 301 } 302 } 303 } 304 305 // Now add the newly selected items 306 for (int i = 0; i < count; i++) { 307 GroupSelectionItem item = mAdapter.getItem(i); 308 long groupId = item.getGroupId(); 309 if (item.isChecked() && !hasMembership(groupId)) { 310 ValuesDelta entry = EntityModifier.insertChild(mState, mKind); 311 entry.put(GroupMembership.GROUP_ROW_ID, groupId); 312 } 313 } 314 315 updateView(); 316 } 317 isGroupChecked(long groupId)318 private boolean isGroupChecked(long groupId) { 319 int count = mAdapter.getCount(); 320 for (int i = 0; i < count; i++) { 321 GroupSelectionItem item = mAdapter.getItem(i); 322 if (groupId == item.getGroupId()) { 323 return item.isChecked(); 324 } 325 } 326 return false; 327 } 328 hasMembership(long groupId)329 private boolean hasMembership(long groupId) { 330 if (groupId == mDefaultGroupId && mState.isContactInsert()) { 331 return true; 332 } 333 334 ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE); 335 if (entries != null) { 336 for (ValuesDelta values : entries) { 337 if (!values.isDelete()) { 338 Long id = values.getAsLong(GroupMembership.GROUP_ROW_ID); 339 if (id != null && id == groupId) { 340 return true; 341 } 342 } 343 } 344 } 345 return false; 346 } 347 createNewGroup()348 private void createNewGroup() { 349 if (mPopup != null) { 350 mPopup.dismiss(); 351 mPopup = null; 352 } 353 354 GroupCreationDialogFragment.show( 355 ((Activity) getContext()).getFragmentManager(), mAccountType, mAccountName, 356 mDataSet); 357 } 358 } 359