1 /* 2 * Copyright (C) 2017 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.drawer; 18 19 import android.app.Activity; 20 import android.graphics.PorterDuff; 21 import androidx.annotation.LayoutRes; 22 import android.view.LayoutInflater; 23 import android.view.View; 24 import android.view.ViewGroup; 25 import android.widget.BaseAdapter; 26 import android.widget.ImageView; 27 import android.widget.TextView; 28 29 import com.android.contacts.R; 30 import com.android.contacts.activities.PeopleActivity.ContactsView; 31 import com.android.contacts.group.GroupListItem; 32 import com.android.contacts.list.ContactListFilter; 33 import com.android.contacts.model.account.AccountDisplayInfo; 34 import com.android.contacts.model.account.AccountDisplayInfoFactory; 35 import com.android.contacts.util.SharedPreferenceUtil; 36 import com.android.contactsbind.HelpUtils; 37 import com.android.contactsbind.ObjectFactory; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 42 public class DrawerAdapter extends BaseAdapter { 43 44 private static final int VIEW_TYPE_PRIMARY_ITEM = 0; 45 private static final int VIEW_TYPE_MISC_ITEM = 1; 46 private static final int VIEW_TYPE_HEADER_ITEM = 2; 47 private static final int VIEW_TYPE_GROUP_ENTRY = 3; 48 private static final int VIEW_TYPE_ACCOUNT_ENTRY = 4; 49 private static final int VIEW_TYPE_CREATE_LABEL = 5; 50 private static final int VIEW_TYPE_NAV_SPACER = 6; 51 private static final int VIEW_TYPE_NAV_DIVIDER = 7; 52 53 // This count must be updated if we add more view types. 54 private static final int VIEW_TYPE_COUNT = 9; 55 56 private static final int TYPEFACE_STYLE_ACTIVATE = R.style.DrawerItemTextActiveStyle; 57 private static final int TYPEFACE_STYLE_INACTIVE = R.style.DrawerItemTextInactiveStyle; 58 59 private final Activity mActivity; 60 private final LayoutInflater mInflater; 61 private ContactsView mSelectedView; 62 private boolean mAreGroupWritableAccountsAvailable; 63 64 // The group/account that was last clicked. 65 private long mSelectedGroupId; 66 private ContactListFilter mSelectedAccount; 67 68 // Adapter elements, ordered in this way mItemsList. The ordering is based on: 69 // [Navigation spacer item] 70 // [Primary items] (Contacts, Suggestions) 71 // [Group Header] 72 // [Groups] 73 // [Create Label button] 74 // [Account Header] 75 // [Accounts] 76 // [Misc items] (a divider, Settings, Help & Feedback) 77 // [Navigation spacer item] 78 private NavSpacerItem mNavSpacerItem = null; 79 private List<PrimaryItem> mPrimaryItems = new ArrayList<>(); 80 private HeaderItem mGroupHeader = null; 81 private List<GroupEntryItem> mGroupEntries = new ArrayList<>(); 82 private BaseDrawerItem mCreateLabelButton = null; 83 private HeaderItem mAccountHeader = null; 84 private List<AccountEntryItem> mAccountEntries = new ArrayList<>(); 85 private List<BaseDrawerItem> mMiscItems = new ArrayList<>(); 86 87 private List<BaseDrawerItem> mItemsList = new ArrayList<>(); 88 private AccountDisplayInfoFactory mAccountDisplayFactory; 89 DrawerAdapter(Activity activity)90 public DrawerAdapter(Activity activity) { 91 super(); 92 mInflater = LayoutInflater.from(activity); 93 mActivity = activity; 94 initializeDrawerMenuItems(); 95 } 96 initializeDrawerMenuItems()97 private void initializeDrawerMenuItems() { 98 // Spacer item for dividing sections in drawer 99 mNavSpacerItem = new NavSpacerItem(R.id.nav_drawer_spacer); 100 // Primary items 101 mPrimaryItems.add(new PrimaryItem(R.id.nav_all_contacts, R.string.contactsList, 102 R.drawable.quantum_ic_account_circle_vd_theme_24, ContactsView.ALL_CONTACTS)); 103 if (ObjectFactory.getAssistantFragment() != null) { 104 mPrimaryItems.add(new PrimaryItem(R.id.nav_assistant, R.string.menu_assistant, 105 R.drawable.quantum_ic_assistant_vd_theme_24, ContactsView.ASSISTANT)); 106 } 107 // Group Header 108 mGroupHeader = new HeaderItem(R.id.nav_groups, R.string.menu_title_groups); 109 // Account Header 110 mAccountHeader = new HeaderItem(R.id.nav_filters, R.string.menu_title_filters); 111 // Create Label Button 112 mCreateLabelButton = new BaseDrawerItem(VIEW_TYPE_CREATE_LABEL, R.id.nav_create_label, 113 R.string.menu_new_group_action_bar, R.drawable.quantum_ic_add_vd_theme_24); 114 // Misc Items 115 mMiscItems.add(new DividerItem()); 116 mMiscItems.add(new MiscItem(R.id.nav_settings, R.string.menu_settings, 117 R.drawable.quantum_ic_settings_vd_theme_24)); 118 if (HelpUtils.isHelpAndFeedbackAvailable()) { 119 mMiscItems.add(new MiscItem(R.id.nav_help, R.string.menu_help, 120 R.drawable.quantum_ic_help_vd_theme_24)); 121 } 122 rebuildItemsList(); 123 } 124 rebuildItemsList()125 private void rebuildItemsList() { 126 mItemsList.clear(); 127 mItemsList.add(mNavSpacerItem); 128 mItemsList.addAll(mPrimaryItems); 129 if (mAreGroupWritableAccountsAvailable || !mGroupEntries.isEmpty()) { 130 mItemsList.add(mGroupHeader); 131 } 132 mItemsList.addAll(mGroupEntries); 133 if (mAreGroupWritableAccountsAvailable) { 134 mItemsList.add(mCreateLabelButton); 135 } 136 if (mAccountEntries.size() > 0) { 137 mItemsList.add(mAccountHeader); 138 } 139 mItemsList.addAll(mAccountEntries); 140 mItemsList.addAll(mMiscItems); 141 mItemsList.add(mNavSpacerItem); 142 } 143 setGroups(List<GroupListItem> groupListItems, boolean areGroupWritable)144 public void setGroups(List<GroupListItem> groupListItems, boolean areGroupWritable) { 145 final ArrayList<GroupEntryItem> groupEntries = new ArrayList<GroupEntryItem>(); 146 for (GroupListItem group : groupListItems) { 147 groupEntries.add(new GroupEntryItem(R.id.nav_group, group)); 148 } 149 mGroupEntries.clear(); 150 mGroupEntries.addAll(groupEntries); 151 mAreGroupWritableAccountsAvailable = areGroupWritable; 152 notifyChangeAndRebuildList(); 153 } 154 setAccounts(List<ContactListFilter> accountFilterItems)155 public void setAccounts(List<ContactListFilter> accountFilterItems) { 156 ArrayList<AccountEntryItem> accountItems = new ArrayList<AccountEntryItem>(); 157 for (ContactListFilter filter : accountFilterItems) { 158 accountItems.add(new AccountEntryItem(R.id.nav_filter, filter)); 159 } 160 mAccountDisplayFactory = AccountDisplayInfoFactory.fromListFilters(mActivity, 161 accountFilterItems); 162 mAccountEntries.clear(); 163 mAccountEntries.addAll(accountItems); 164 // TODO investigate performance of calling notifyDataSetChanged 165 notifyChangeAndRebuildList(); 166 } 167 168 @Override getCount()169 public int getCount() { 170 return mItemsList.size(); 171 } 172 getItem(int position)173 public BaseDrawerItem getItem(int position) { 174 return mItemsList.get(position); 175 } 176 177 @Override getItemId(int position)178 public long getItemId(int position) { 179 return getItem(position).id; 180 } 181 182 @Override getViewTypeCount()183 public int getViewTypeCount() { 184 return VIEW_TYPE_COUNT; 185 } 186 187 @Override getView(int position, View view, ViewGroup viewGroup)188 public View getView(int position, View view, ViewGroup viewGroup) { 189 final BaseDrawerItem drawerItem = getItem(position); 190 switch (drawerItem.viewType) { 191 case VIEW_TYPE_PRIMARY_ITEM: 192 return getPrimaryItemView((PrimaryItem) drawerItem, view, viewGroup); 193 case VIEW_TYPE_HEADER_ITEM: 194 return getHeaderItemView((HeaderItem) drawerItem, view, viewGroup); 195 case VIEW_TYPE_CREATE_LABEL: 196 return getDrawerItemView(drawerItem, view, viewGroup); 197 case VIEW_TYPE_GROUP_ENTRY: 198 return getGroupEntryView((GroupEntryItem) drawerItem, view, viewGroup); 199 case VIEW_TYPE_ACCOUNT_ENTRY: 200 return getAccountItemView((AccountEntryItem) drawerItem, view, viewGroup); 201 case VIEW_TYPE_MISC_ITEM: 202 return getDrawerItemView(drawerItem, view, viewGroup); 203 case VIEW_TYPE_NAV_SPACER: 204 return getBaseItemView(R.layout.nav_drawer_spacer, view, viewGroup); 205 case VIEW_TYPE_NAV_DIVIDER: 206 return getBaseItemView(R.layout.drawer_horizontal_divider, view, viewGroup); 207 } 208 throw new IllegalStateException("Unknown drawer item " + drawerItem); 209 } 210 getBaseItemView(@ayoutRes int layoutResID, View result,ViewGroup parent)211 private View getBaseItemView(@LayoutRes int layoutResID, View result,ViewGroup parent) { 212 if (result == null) { 213 result = mInflater.inflate(layoutResID, parent, false); 214 } 215 return result; 216 } 217 getPrimaryItemView(PrimaryItem item, View result, ViewGroup parent)218 private View getPrimaryItemView(PrimaryItem item, View result, ViewGroup parent) { 219 if (result == null) { 220 result = mInflater.inflate(R.layout.drawer_primary_item, parent, false); 221 } 222 final TextView titleView = (TextView) result.findViewById(R.id.title); 223 titleView.setText(item.text); 224 final ImageView iconView = (ImageView) result.findViewById(R.id.icon); 225 iconView.setImageResource(item.icon); 226 final TextView newBadge = (TextView) result.findViewById(R.id.assistant_new_badge); 227 final boolean showWelcomeBadge = !SharedPreferenceUtil.isWelcomeCardDismissed(mActivity); 228 newBadge.setVisibility(item.contactsView == ContactsView.ASSISTANT && showWelcomeBadge 229 ? View.VISIBLE : View.GONE); 230 result.setActivated(item.contactsView == mSelectedView); 231 updateSelectedStatus(titleView, iconView, item.contactsView == mSelectedView); 232 result.setId(item.id); 233 return result; 234 } 235 getHeaderItemView(HeaderItem item, View result, ViewGroup parent)236 private View getHeaderItemView(HeaderItem item, View result, ViewGroup parent) { 237 if (result == null) { 238 result = mInflater.inflate(R.layout.drawer_header, parent, false); 239 } 240 final TextView textView = (TextView) result.findViewById(R.id.title); 241 textView.setText(item.text); 242 result.setId(item.id); 243 return result; 244 } 245 getGroupEntryView(GroupEntryItem item, View result, ViewGroup parent)246 private View getGroupEntryView(GroupEntryItem item, View result, ViewGroup parent) { 247 if (result == null || !(result.getTag() instanceof GroupEntryItem)) { 248 result = mInflater.inflate(R.layout.drawer_item, parent, false); 249 result.setId(item.id); 250 } 251 252 final GroupListItem groupListItem = item.group; 253 final TextView title = (TextView) result.findViewById(R.id.title); 254 title.setText(groupListItem.getTitle()); 255 final ImageView icon = (ImageView) result.findViewById(R.id.icon); 256 icon.setImageResource(R.drawable.quantum_ic_label_vd_theme_24); 257 final boolean activated = groupListItem.getGroupId() == mSelectedGroupId && 258 mSelectedView == ContactsView.GROUP_VIEW; 259 updateSelectedStatus(title, icon, activated); 260 result.setActivated(activated); 261 262 result.setTag(groupListItem); 263 result.setContentDescription( 264 mActivity.getString(R.string.navigation_drawer_label, groupListItem.getTitle())); 265 return result; 266 } 267 getAccountItemView(AccountEntryItem item, View result, ViewGroup parent)268 private View getAccountItemView(AccountEntryItem item, View result, ViewGroup parent) { 269 if (result == null || !(result.getTag() instanceof ContactListFilter)) { 270 result = mInflater.inflate(R.layout.drawer_item, parent, false); 271 result.setId(item.id); 272 } 273 final ContactListFilter account = item.account; 274 final AccountDisplayInfo displayableAccount = 275 mAccountDisplayFactory.getAccountDisplayInfoFor(item.account); 276 final TextView textView = ((TextView) result.findViewById(R.id.title)); 277 textView.setText(displayableAccount.getNameLabel()); 278 final boolean activated = account.equals(mSelectedAccount) 279 && mSelectedView == ContactsView.ACCOUNT_VIEW; 280 textView.setTextAppearance(mActivity, activated 281 ? TYPEFACE_STYLE_ACTIVATE : TYPEFACE_STYLE_INACTIVE); 282 283 final ImageView icon = (ImageView) result.findViewById(R.id.icon); 284 icon.setScaleType(ImageView.ScaleType.FIT_CENTER); 285 icon.setImageDrawable(displayableAccount.getIcon()); 286 287 result.setTag(account); 288 result.setActivated(activated); 289 result.setContentDescription( 290 displayableAccount.getTypeLabel() + " " + item.account.accountName); 291 return result; 292 } 293 getDrawerItemView(BaseDrawerItem item, View result, ViewGroup parent)294 private View getDrawerItemView(BaseDrawerItem item, View result, ViewGroup parent) { 295 if (result == null) { 296 result = mInflater.inflate(R.layout.drawer_item, parent, false); 297 } 298 299 final TextView textView = (TextView) result.findViewById(R.id.title); 300 textView.setText(item.text); 301 final ImageView iconView = (ImageView) result.findViewById(R.id.icon); 302 iconView.setImageResource(item.icon); 303 result.setId(item.id); 304 updateSelectedStatus(textView, iconView, false); 305 return result; 306 } 307 308 @Override getItemViewType(int position)309 public int getItemViewType(int position) { 310 return getItem(position).viewType; 311 } 312 updateSelectedStatus(TextView textView, ImageView imageView, boolean activated)313 private void updateSelectedStatus(TextView textView, ImageView imageView, boolean activated) { 314 textView.setTextAppearance(mActivity, activated 315 ? TYPEFACE_STYLE_ACTIVATE : TYPEFACE_STYLE_INACTIVE); 316 if (activated) { 317 imageView.setColorFilter(mActivity.getResources().getColor(R.color.primary_color), 318 PorterDuff.Mode.SRC_ATOP); 319 } else { 320 imageView.clearColorFilter(); 321 } 322 } 323 notifyChangeAndRebuildList()324 private void notifyChangeAndRebuildList() { 325 rebuildItemsList(); 326 notifyDataSetChanged(); 327 } 328 setSelectedContactsView(ContactsView contactsView)329 public void setSelectedContactsView(ContactsView contactsView) { 330 if (mSelectedView == contactsView) { 331 return; 332 } 333 mSelectedView = contactsView; 334 notifyChangeAndRebuildList(); 335 } 336 337 setSelectedGroupId(long groupId)338 public void setSelectedGroupId(long groupId) { 339 if (mSelectedGroupId == groupId) { 340 return; 341 } 342 mSelectedGroupId = groupId; 343 notifyChangeAndRebuildList(); 344 } 345 getSelectedGroupId()346 public long getSelectedGroupId() { 347 return mSelectedGroupId; 348 } 349 setSelectedAccount(ContactListFilter filter)350 public void setSelectedAccount(ContactListFilter filter) { 351 if (mSelectedAccount == filter) { 352 return; 353 } 354 mSelectedAccount = filter; 355 notifyChangeAndRebuildList(); 356 } 357 getSelectedAccount()358 public ContactListFilter getSelectedAccount() { 359 return mSelectedAccount; 360 } 361 362 public static class BaseDrawerItem { 363 public final int viewType; 364 public final int id; 365 public final int text; 366 public final int icon; 367 BaseDrawerItem(int adapterViewType, int viewId, int textResId, int iconResId)368 public BaseDrawerItem(int adapterViewType, int viewId, int textResId, int iconResId) { 369 viewType = adapterViewType; 370 id = viewId; 371 text = textResId; 372 icon = iconResId; 373 } 374 } 375 376 // Navigation drawer item for Contacts or Suggestions view which contains a name, an icon and 377 // contacts view. 378 public static class PrimaryItem extends BaseDrawerItem { 379 public final ContactsView contactsView; 380 PrimaryItem(int id, int pageName, int iconId, ContactsView contactsView)381 public PrimaryItem(int id, int pageName, int iconId, ContactsView contactsView) { 382 super(VIEW_TYPE_PRIMARY_ITEM, id, pageName, iconId); 383 this.contactsView = contactsView; 384 } 385 } 386 387 // Navigation drawer item for Settings, help and feedback, etc. 388 public static class MiscItem extends BaseDrawerItem { MiscItem(int id, int textId, int iconId)389 public MiscItem(int id, int textId, int iconId) { 390 super(VIEW_TYPE_MISC_ITEM, id, textId, iconId); 391 } 392 } 393 394 // Header for a list of sub-items in the drawer. 395 public static class HeaderItem extends BaseDrawerItem { HeaderItem(int id, int textId)396 public HeaderItem(int id, int textId) { 397 super(VIEW_TYPE_HEADER_ITEM, id, textId, /* iconResId */ 0); 398 } 399 } 400 401 // Navigation drawer item for spacer item for dividing sections in the drawer. 402 public static class NavSpacerItem extends BaseDrawerItem { NavSpacerItem(int id)403 public NavSpacerItem(int id) { 404 super(VIEW_TYPE_NAV_SPACER, id, /* textResId */ 0, /* iconResId */ 0); 405 } 406 } 407 408 // Divider for drawing a line between sections in the drawer. 409 public static class DividerItem extends BaseDrawerItem { DividerItem()410 public DividerItem() { 411 super(VIEW_TYPE_NAV_DIVIDER, /* id */ 0, /* textResId */ 0, /* iconResId */ 0); 412 } 413 } 414 415 // Navigation drawer item for a group. 416 public static class GroupEntryItem extends BaseDrawerItem { 417 private final GroupListItem group; 418 GroupEntryItem(int id, GroupListItem group)419 public GroupEntryItem(int id, GroupListItem group) { 420 super(VIEW_TYPE_GROUP_ENTRY, id, /* textResId */ 0, /* iconResId */ 0); 421 this.group = group; 422 } 423 } 424 425 // Navigation drawer item for an account. 426 public static class AccountEntryItem extends BaseDrawerItem { 427 private final ContactListFilter account; 428 AccountEntryItem(int id, ContactListFilter account)429 public AccountEntryItem(int id, ContactListFilter account) { 430 super(VIEW_TYPE_ACCOUNT_ENTRY, id, /* textResId */ 0, /* iconResId */ 0); 431 this.account = account; 432 } 433 } 434 } 435