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