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