1e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov/* 2e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project 3e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * 4e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); 5e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * you may not use this file except in compliance with the License. 6e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * You may obtain a copy of the License at 7e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * 8e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 9e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * 10e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 11e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, 12e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * See the License for the specific language governing permissions and 14e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * limitations under the License. 15e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov */ 16e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 1718ffaa2561cc7dd2e3ef81737e6537931c0a9a11Dmitri Plotnikovpackage com.android.contacts.editor; 18e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 191ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikovimport android.app.Activity; 20e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.content.Context; 21fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuanimport android.content.res.Resources; 22e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.database.Cursor; 23e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership; 2493a0025b87079e0d36431a2c096f96e145053a28Makoto Onukiimport android.text.TextUtils; 25e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.util.AttributeSet; 26e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.view.View; 27e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.view.View.OnClickListener; 28b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chuimport android.view.ViewGroup; 29e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.widget.AdapterView; 30e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.widget.AdapterView.OnItemClickListener; 31e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.widget.ArrayAdapter; 32b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chuimport android.widget.CheckedTextView; 33e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.widget.LinearLayout; 34e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.widget.ListPopupWindow; 35e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.widget.ListView; 36e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport android.widget.TextView; 37e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 38e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.GroupMetaDataLoader; 39e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.R; 40e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.interactions.GroupCreationDialogFragment; 41e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.interactions.GroupCreationDialogFragment.OnGroupCreatedListener; 42851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactModifier; 43851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactDelta; 44851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactDelta.ValuesDelta; 45851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.dataitem.DataKind; 46e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.internal.util.Objects; 47e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng 48e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovimport java.util.ArrayList; 49e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 50e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov/** 51e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * An editor for group membership. Displays the current group membership list and 52e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov * brings up a dialog to change it. 53e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov */ 54e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikovpublic class GroupMembershipView extends LinearLayout 55e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov implements OnClickListener, OnItemClickListener { 56e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 571ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov private static final int CREATE_NEW_GROUP_GROUP_ID = 133; 581ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 59e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public static final class GroupSelectionItem { 60e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private final long mGroupId; 61e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private final String mTitle; 62e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private boolean mChecked; 63e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 64e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public GroupSelectionItem(long groupId, String title, boolean checked) { 65e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov this.mGroupId = groupId; 66e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov this.mTitle = title; 67e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mChecked = checked; 68e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 69e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 70e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public long getGroupId() { 71e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov return mGroupId; 72e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 73e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 74e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public boolean isChecked() { 75e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov return mChecked; 76e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 77e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 78e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public void setChecked(boolean checked) { 79e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mChecked = checked; 80e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 81e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 82e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov @Override 83e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public String toString() { 84e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov return mTitle; 85e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 86e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 87e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 88b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu /** 89b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu * Extends the array adapter to show checkmarks on all but the last list item for 90b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu * the group membership popup. Note that this is highly specific to the fact that the 91b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu * group_membership_list_item.xml is a CheckedTextView object. 92b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu */ 93b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu private class GroupMembershipAdapter<T> extends ArrayAdapter<T> { 94b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu 95b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu public GroupMembershipAdapter(Context context, int textViewResourceId) { 96b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu super(context, textViewResourceId); 97b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 98b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu 99b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu public boolean getItemIsCheckable(int position) { 100b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu // Item is checkable if it is NOT the last one in the list 101b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu return position != getCount()-1; 102b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 103b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu 104b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu @Override 105b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu public int getItemViewType(int position) { 106b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu return getItemIsCheckable(position) ? 0 : 1; 107b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 108b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu 109b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu @Override 110b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu public int getViewTypeCount() { 111b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu return 2; 112b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 113b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu 114b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu @Override 115b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu public View getView(int position, View convertView, ViewGroup parent) { 116b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu final View itemView = super.getView(position, convertView, parent); 117b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu 118b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu // Hide the checkable drawable. This assumes that the item views 119b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu // are CheckedTextView objects 120b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu final CheckedTextView checkedTextView = (CheckedTextView)itemView; 121b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu if (!getItemIsCheckable(position)) { 122b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu checkedTextView.setCheckMarkDrawable(null); 123b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 124b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu 125b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu return checkedTextView; 126b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 127b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 128b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu 129851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private RawContactDelta mState; 130e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private Cursor mGroupMetaData; 131e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private String mAccountName; 132e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private String mAccountType; 1332b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro private String mDataSet; 134e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private TextView mGroupList; 135b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu private GroupMembershipAdapter<GroupSelectionItem> mAdapter; 136025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov private long mDefaultGroupId; 137e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private long mFavoritesGroupId; 138e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private ListPopupWindow mPopup; 139e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private DataKind mKind; 140ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov private boolean mDefaultGroupVisibilityKnown; 141ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov private boolean mDefaultGroupVisible; 142b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu private boolean mCreatedNewGroup; 143e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 144fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan private String mNoGroupString; 145fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan private int mPrimaryTextColor; 146fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan private int mSecondaryTextColor; 147fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan 148e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public GroupMembershipView(Context context) { 149e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov super(context); 150e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 151e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 152e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public GroupMembershipView(Context context, AttributeSet attrs) { 153e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov super(context, attrs); 154e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 155e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 15602bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov @Override 157fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan protected void onFinishInflate() { 158fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan super.onFinishInflate(); 159fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan Resources resources = mContext.getResources(); 160fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan mPrimaryTextColor = resources.getColor(R.color.primary_text_color); 161fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan mSecondaryTextColor = resources.getColor(R.color.secondary_text_color); 1629574314dd24a045761453b51244fa75a1da36f6eKatherine Kuan mNoGroupString = mContext.getString(R.string.group_edit_field_hint_text); 163fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan } 164fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan 165fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan @Override 16602bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov public void setEnabled(boolean enabled) { 16702bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov super.setEnabled(enabled); 16802bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov if (mGroupList != null) { 16902bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov mGroupList.setEnabled(enabled); 17002bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov } 17102bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov } 17202bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov 173e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public void setKind(DataKind kind) { 174e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mKind = kind; 175e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov TextView kindTitle = (TextView) findViewById(R.id.kind_title); 176a7c3d4d726b91f645431af14d8cceadd2245f4ceKatherine Kuan kindTitle.setText(getResources().getString(kind.titleRes).toUpperCase()); 177e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 178e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 179e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public void setGroupMetaData(Cursor groupMetaData) { 180e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov this.mGroupMetaData = groupMetaData; 181e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov updateView(); 182b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu // Open up the list of groups if a new group was just created. 183b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu if (mCreatedNewGroup) { 184b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu mCreatedNewGroup = false; 185b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu onClick(this); // This causes the popup to open. 186b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu if (mPopup != null) { 187b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu // Ensure that the newly created group is checked. 188b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu int position = mAdapter.getCount() - 2; 189b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu ListView listView = mPopup.getListView(); 190b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu if (!listView.isItemChecked(position)) { 191b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu // Newly created group is not checked, so check it. 192b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu listView.setItemChecked(position, true); 193b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu onItemClick(listView, null, position, listView.getItemIdAtPosition(position)); 194b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 195b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 196b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 197e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 198e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 199851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public void setState(RawContactDelta state) { 200e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mState = state; 201851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu mAccountType = mState.getAccountType(); 202851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu mAccountName = mState.getAccountName(); 203851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu mDataSet = mState.getDataSet(); 204ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov mDefaultGroupVisibilityKnown = false; 205b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu mCreatedNewGroup = false; 206e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov updateView(); 207e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 208e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 209e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private void updateView() { 21008d233405a0418073254356a772c9954998e1707Dmitri Plotnikov if (mGroupMetaData == null || mGroupMetaData.isClosed() || mAccountType == null 21108d233405a0418073254356a772c9954998e1707Dmitri Plotnikov || mAccountName == null) { 212e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov setVisibility(GONE); 213e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov return; 214e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 215e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 216e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov boolean accountHasGroups = false; 217025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov mFavoritesGroupId = 0; 218025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov mDefaultGroupId = 0; 219025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov 220e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov StringBuilder sb = new StringBuilder(); 221e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mGroupMetaData.moveToPosition(-1); 222e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov while (mGroupMetaData.moveToNext()) { 223e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME); 224e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE); 2252b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET); 2262b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (accountName.equals(mAccountName) && accountType.equals(mAccountType) 2272b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro && Objects.equal(dataSet, mDataSet)) { 228e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID); 229e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov if (!mGroupMetaData.isNull(GroupMetaDataLoader.FAVORITES) 230e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov && mGroupMetaData.getInt(GroupMetaDataLoader.FAVORITES) != 0) { 231e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mFavoritesGroupId = groupId; 232025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov } else if (!mGroupMetaData.isNull(GroupMetaDataLoader.AUTO_ADD) 233025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov && mGroupMetaData.getInt(GroupMetaDataLoader.AUTO_ADD) != 0) { 234025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov mDefaultGroupId = groupId; 235e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } else { 236e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov accountHasGroups = true; 237e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 238e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 239e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov // Exclude favorites from the list - they are handled with special UI (star) 2405d11ccd7894b8c53a6867f7aba0dbbad3fc00c90Dmitri Plotnikov // Also exclude the default group. 2415d11ccd7894b8c53a6867f7aba0dbbad3fc00c90Dmitri Plotnikov if (groupId != mFavoritesGroupId && groupId != mDefaultGroupId 2425d11ccd7894b8c53a6867f7aba0dbbad3fc00c90Dmitri Plotnikov && hasMembership(groupId)) { 243e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE); 24493a0025b87079e0d36431a2c096f96e145053a28Makoto Onuki if (!TextUtils.isEmpty(title)) { 24593a0025b87079e0d36431a2c096f96e145053a28Makoto Onuki if (sb.length() != 0) { 24693a0025b87079e0d36431a2c096f96e145053a28Makoto Onuki sb.append(", "); 24793a0025b87079e0d36431a2c096f96e145053a28Makoto Onuki } 24893a0025b87079e0d36431a2c096f96e145053a28Makoto Onuki sb.append(title); 249e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 250e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 251e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 252e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 253e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 254e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov if (!accountHasGroups) { 255e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov setVisibility(GONE); 256e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov return; 257e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 258e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 259e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov if (mGroupList == null) { 260e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mGroupList = (TextView) findViewById(R.id.group_list); 26182b49b8eb8a806393fc59833fab96f924e74e9d4Daniel Lehmann mGroupList.setOnClickListener(this); 262e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 263e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 26402bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov mGroupList.setEnabled(isEnabled()); 265135b44c8604e375699e22594e65f0daec14f5bbeDmitri Plotnikov if (sb.length() == 0) { 266fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan mGroupList.setText(mNoGroupString); 267fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan mGroupList.setTextColor(mSecondaryTextColor); 268135b44c8604e375699e22594e65f0daec14f5bbeDmitri Plotnikov } else { 269135b44c8604e375699e22594e65f0daec14f5bbeDmitri Plotnikov mGroupList.setText(sb); 270fd70903236883f47b21f23b8cb49a1ccacdfcdf1Katherine Kuan mGroupList.setTextColor(mPrimaryTextColor); 271135b44c8604e375699e22594e65f0daec14f5bbeDmitri Plotnikov } 272e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov setVisibility(VISIBLE); 273ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov 274ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov if (!mDefaultGroupVisibilityKnown) { 275ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov // Only show the default group (My Contacts) if the contact is NOT in it 276ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov mDefaultGroupVisible = mDefaultGroupId != 0 && !hasMembership(mDefaultGroupId); 277ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov mDefaultGroupVisibilityKnown = true; 278ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov } 279e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 280e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 281e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov @Override 282e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public void onClick(View v) { 283e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov if (mPopup != null && mPopup.isShowing()) { 284e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mPopup.dismiss(); 285e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov return; 286e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 287e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 288b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu mAdapter = new GroupMembershipAdapter<GroupSelectionItem>( 2892b95e624bc35851987fb621091f20263d635d31aDmitri Plotnikov getContext(), R.layout.group_membership_list_item); 290e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 291e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mGroupMetaData.moveToPosition(-1); 292e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov while (mGroupMetaData.moveToNext()) { 293e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME); 294e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE); 2952b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET); 2962b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro if (accountName.equals(mAccountName) && accountType.equals(mAccountType) 2972b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro && Objects.equal(dataSet, mDataSet)) { 298e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID); 299ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov if (groupId != mFavoritesGroupId 300ad9659e62604309b0d95b0a1f9b19fd6a33990e9Dmitri Plotnikov && (groupId != mDefaultGroupId || mDefaultGroupVisible)) { 301e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE); 302e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov boolean checked = hasMembership(groupId); 303e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mAdapter.add(new GroupSelectionItem(groupId, title, checked)); 304e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 305e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 306e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 307e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 3081ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov mAdapter.add(new GroupSelectionItem(CREATE_NEW_GROUP_GROUP_ID, 3091ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov getContext().getString(R.string.create_group_item_label), false)); 3101ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 311b93d626ebf95f3381184bf8a560630bcdedce9b9Dmitri Plotnikov mPopup = new ListPopupWindow(getContext(), null); 312e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mPopup.setAnchorView(mGroupList); 313e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mPopup.setAdapter(mAdapter); 3145d11ccd7894b8c53a6867f7aba0dbbad3fc00c90Dmitri Plotnikov mPopup.setModal(true); 3156d172800844a3f23a918a099c90c10701159bf77Daniel Lehmann mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); 316e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mPopup.show(); 317e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 318e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov ListView listView = mPopup.getListView(); 319e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); 320d85b98628ad7434d51b4bbf7c92b41917fd234e7Katherine Kuan listView.setOverScrollMode(OVER_SCROLL_ALWAYS); 321e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov int count = mAdapter.getCount(); 322e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov for (int i = 0; i < count; i++) { 323e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov listView.setItemChecked(i, mAdapter.getItem(i).isChecked()); 324e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 325e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 326e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov listView.setOnItemClickListener(this); 327e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 328e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 329e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov @Override 330e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov protected void onDetachedFromWindow() { 331e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov super.onDetachedFromWindow(); 332e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov if (mPopup != null) { 333e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mPopup.dismiss(); 3341ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov mPopup = null; 335e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 336e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 337e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 338e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov @Override 339e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 340e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov ListView list = (ListView) parent; 341e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov int count = mAdapter.getCount(); 3421ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 3431ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov if (list.isItemChecked(count - 1)) { 3441ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov list.setItemChecked(count - 1, false); 3451ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov createNewGroup(); 3461ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov return; 3471ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov } 3481ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 349e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov for (int i = 0; i < count; i++) { 350e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov mAdapter.getItem(i).setChecked(list.isItemChecked(i)); 351e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 352e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 353e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov // First remove the memberships that have been unchecked 354e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE); 355e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov if (entries != null) { 356e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov for (ValuesDelta entry : entries) { 357025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov if (!entry.isDelete()) { 358851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu Long groupId = entry.getGroupRowId(); 359025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov if (groupId != null && groupId != mFavoritesGroupId 360a61e0fbb6e057f9c3b8bcf4b68ae1df41d90f3baDmitri Plotnikov && (groupId != mDefaultGroupId || mDefaultGroupVisible) 361025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov && !isGroupChecked(groupId)) { 362025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov entry.markDeleted(); 363025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov } 364e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 365e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 366e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 367e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 368e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov // Now add the newly selected items 369e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov for (int i = 0; i < count; i++) { 370e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov GroupSelectionItem item = mAdapter.getItem(i); 371e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov long groupId = item.getGroupId(); 372e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov if (item.isChecked() && !hasMembership(groupId)) { 373851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu ValuesDelta entry = RawContactModifier.insertChild(mState, mKind); 374851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu entry.setGroupRowId(groupId); 375e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 376e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 377e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 378e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov updateView(); 379e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 380e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 381e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private boolean isGroupChecked(long groupId) { 382e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov int count = mAdapter.getCount(); 383e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov for (int i = 0; i < count; i++) { 384e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov GroupSelectionItem item = mAdapter.getItem(i); 385e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov if (groupId == item.getGroupId()) { 386e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov return item.isChecked(); 387e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 388e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 389e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov return false; 390e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 391e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov 392e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov private boolean hasMembership(long groupId) { 393025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov if (groupId == mDefaultGroupId && mState.isContactInsert()) { 394025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov return true; 395025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov } 396025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov 397e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE); 398e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov if (entries != null) { 399e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov for (ValuesDelta values : entries) { 400025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov if (!values.isDelete()) { 401851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu Long id = values.getGroupRowId(); 402025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov if (id != null && id == groupId) { 403025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov return true; 404025838f0a4b7c19640035a89d42512b892c503baDmitri Plotnikov } 405e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 406e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 407e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 408e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov return false; 409e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov } 4101ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 4111ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov private void createNewGroup() { 4121ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov if (mPopup != null) { 4131ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov mPopup.dismiss(); 4141ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov mPopup = null; 4151ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov } 4161ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 4171ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov GroupCreationDialogFragment.show( 418b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu ((Activity) getContext()).getFragmentManager(), 419b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu mAccountType, 420b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu mAccountName, 421b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu mDataSet, 422b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu new OnGroupCreatedListener() { 423b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu @Override 424b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu public void onGroupCreated() { 425b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu mCreatedNewGroup = true; 426b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu } 427b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu }); 4281ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov } 429b03e6a266e3a4181d22e6be4f3fa0c5dd158d7e1Maurice Chu 430e843f916942fff482ec162eee2a23914e32182cfDmitri Plotnikov} 431