• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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.mail.adapter;
18 
19 import android.view.LayoutInflater;
20 import android.view.View;
21 import android.view.ViewGroup;
22 import android.widget.TextView;
23 
24 import com.android.bitmap.BitmapCache;
25 import com.android.mail.R;
26 import com.android.mail.bitmap.ContactResolver;
27 import com.android.mail.providers.Account;
28 import com.android.mail.providers.Folder;
29 import com.android.mail.ui.AccountItemView;
30 import com.android.mail.ui.ControllableActivity;
31 import com.android.mail.ui.FolderItemView;
32 import com.android.mail.utils.FolderUri;
33 import com.android.mail.utils.LogTag;
34 import com.android.mail.utils.LogUtils;
35 
36 
37 /**
38  * An element that is shown in the {@link com.android.mail.ui.FolderListFragment}. This class is
39  * only used for elements that are shown in the {@link com.android.mail.ui.DrawerFragment}.
40  * This class is an enumeration of a few element types: Account, a folder, a recent folder,
41  * or a header (a resource string). A {@link DrawerItem} can only be one type and can never
42  * switch types. Items are created using methods like
43  * {@link DrawerItem#ofAccount(ControllableActivity, Account, int, boolean, BitmapCache,
44  * ContactResolver)},
45  * {@link DrawerItem#ofWaitView(ControllableActivity)}, etc.
46  *
47  * Once created, the item can create a view using
48  * {@link #getView(android.view.View, android.view.ViewGroup)}.
49  */
50 public class DrawerItem {
51     private static final String LOG_TAG = LogTag.getLogTag();
52     public final Folder mFolder;
53     public final Account mAccount;
54     private final int mResource;
55     /** True if the drawer item represents the current account, false otherwise */
56     private final boolean mIsSelected;
57     /** Either {@link #VIEW_ACCOUNT}, {@link #VIEW_FOLDER} or {@link #VIEW_HEADER} */
58     public final int mType;
59     /** A normal folder, also a child, if a parent is specified. */
60     public static final int VIEW_FOLDER = 0;
61     /** A text-label which serves as a header in sectioned lists. */
62     public static final int VIEW_HEADER = 1;
63     /** A text-label which serves as a header in sectioned lists. */
64     public static final int VIEW_BLANK_HEADER = 2;
65     /** An account object, which allows switching accounts rather than folders. */
66     public static final int VIEW_ACCOUNT = 3;
67     /** An expandable object for expanding/collapsing more of the list */
68     public static final int VIEW_WAITING_FOR_SYNC = 4;
69     /** The value (1-indexed) of the last View type.  Useful when returning the number of types. */
70     private static final int LAST_FIELD = VIEW_WAITING_FOR_SYNC + 1;
71 
72     /** TODO: On adding another type, be sure to change getViewTypes() */
73 
74     /** The parent activity */
75     private final ControllableActivity mActivity;
76     private final LayoutInflater mInflater;
77 
78     // TODO(viki): Put all these constants in an interface.
79     /**
80      * Either {@link #FOLDER_INBOX}, {@link #FOLDER_RECENT} or {@link #FOLDER_OTHER} when
81      * {@link #mType} is {@link #VIEW_FOLDER}, or an {@link #ACCOUNT} in the case of
82      * accounts, and {@link #INERT_HEADER} otherwise.
83      */
84     public final int mFolderType;
85     /** Non existent item or folder type not yet set */
86     public static final int UNSET = 0;
87     /** An unclickable text-header visually separating the different types. */
88     public static final int INERT_HEADER = 0;
89     /** An inbox folder: Inbox, ...*/
90     public static final int FOLDER_INBOX = 1;
91     /** A folder from whom a conversation was recently viewed */
92     public static final int FOLDER_RECENT = 2;
93     /** A non-inbox folder that is shown in the "everything else" group. */
94     public static final int FOLDER_OTHER = 3;
95     /** An entry for the accounts the user has on the device. */
96     public static final int ACCOUNT = 4;
97 
98     /** True if this view is enabled, false otherwise. */
99     private final boolean mIsEnabled;
100 
101     private BitmapCache mImagesCache;
102     private ContactResolver mContactResolver;
103 
104     @Override
toString()105     public String toString() {
106         switch(mType) {
107             case VIEW_FOLDER:
108                 return folderToString();
109             case VIEW_HEADER:
110                 return headerToString();
111             case VIEW_BLANK_HEADER:
112                 return blankHeaderToString();
113             case VIEW_ACCOUNT:
114                 return accountToString();
115             case VIEW_WAITING_FOR_SYNC:
116                 return waitToString();
117         }
118         // Should never come here.
119         return null;
120     }
121 
122     /**
123      * Creates a drawer item with every instance variable specified.
124      *
125      * @param type the type of the item. This must be a VIEW_* element
126      * @param activity the underlying activity
127      * @param folder a non-null folder, if this is a folder type
128      * @param folderType the type of the folder. For folders this is:
129      *            {@link #FOLDER_INBOX}, {@link #FOLDER_RECENT},
130      *            {@link #FOLDER_OTHER}, or for non-folders this is
131      *            {@link #ACCOUNT}, or {@link #INERT_HEADER}
132      * @param account the account object, for an account drawer element
133      * @param resource either the string resource for a header, or the unread
134      *            count for an account.
135      * @param isCurrentAccount true if this item is the current account
136      */
DrawerItem(int type, ControllableActivity activity, Folder folder, int folderType, Account account, int resource, boolean isCurrentAccount, BitmapCache cache, ContactResolver contactResolver)137     private DrawerItem(int type, ControllableActivity activity, Folder folder, int folderType,
138             Account account, int resource, boolean isCurrentAccount, BitmapCache cache,
139             ContactResolver contactResolver) {
140         mActivity = activity;
141         mFolder = folder;
142         mFolderType = folderType;
143         mAccount = account;
144         mResource = resource;
145         mIsSelected = isCurrentAccount;
146         mInflater = LayoutInflater.from(activity.getActivityContext());
147         mType = type;
148         mIsEnabled = calculateEnabled();
149         mImagesCache = cache;
150         mContactResolver = contactResolver;
151     }
152 
153     /**
154      * Create a folder item with the given type.
155      *
156      * @param activity the underlying activity
157      * @param folder a folder that this item represents
158      * @param folderType one of {@link #FOLDER_INBOX}, {@link #FOLDER_RECENT} or
159      * {@link #FOLDER_OTHER}
160      * @return a drawer item for the folder.
161      */
ofFolder(ControllableActivity activity, Folder folder, int folderType)162     public static DrawerItem ofFolder(ControllableActivity activity, Folder folder,
163             int folderType) {
164         return new DrawerItem(VIEW_FOLDER, activity, folder,  folderType, null, -1, false,
165                 null, null);
166     }
167 
folderToString()168     private String folderToString() {
169         final StringBuilder sb = new StringBuilder("[DrawerItem ");
170         sb.append(" VIEW_FOLDER ");
171         sb.append(", mFolder=");
172         sb.append(mFolder);
173         sb.append(", mFolderType=");
174         sb.append(mFolderType);
175         sb.append("]");
176         return sb.toString();
177     }
178 
179     /**
180      * Creates an item from an account.
181      * @param activity the underlying activity
182      * @param account the account to create a drawer item for
183      * @param unreadCount the unread count of the account, pass zero if
184      * @param isCurrentAccount true if the account is the current account, false otherwise
185      * @return a drawer item for the account.
186      */
ofAccount(ControllableActivity activity, Account account, int unreadCount, boolean isCurrentAccount, BitmapCache cache, ContactResolver contactResolver)187     public static DrawerItem ofAccount(ControllableActivity activity, Account account,
188             int unreadCount, boolean isCurrentAccount, BitmapCache cache,
189             ContactResolver contactResolver) {
190         return new DrawerItem(VIEW_ACCOUNT, activity, null, ACCOUNT, account, unreadCount,
191                 isCurrentAccount, cache, contactResolver);
192     }
193 
accountToString()194     private String accountToString() {
195         final StringBuilder sb = new StringBuilder("[DrawerItem ");
196         sb.append(" VIEW_ACCOUNT ");
197         sb.append(", mAccount=");
198         sb.append(mAccount);
199         sb.append("]");
200         return sb.toString();
201     }
202 
203     /**
204      * Create a header item with a string resource.
205      *
206      * @param activity the underlying activity
207      * @param resource the string resource: R.string.all_folders_heading
208      * @return a drawer item for the header.
209      */
ofHeader(ControllableActivity activity, int resource)210     public static DrawerItem ofHeader(ControllableActivity activity, int resource) {
211         return new DrawerItem(VIEW_HEADER, activity, null, INERT_HEADER, null, resource, false,
212                 null, null);
213     }
214 
headerToString()215     private String headerToString() {
216         final StringBuilder sb = new StringBuilder("[DrawerItem ");
217         sb.append(" VIEW_HEADER ");
218         sb.append(", mResource=");
219         sb.append(mResource);
220         sb.append("]");
221         return sb.toString();
222     }
223 
ofBlankHeader(ControllableActivity activity)224     public static DrawerItem ofBlankHeader(ControllableActivity activity) {
225         return new DrawerItem(VIEW_BLANK_HEADER, activity, null, INERT_HEADER, null, 0, false, null,
226                 null);
227     }
228 
blankHeaderToString()229     private String blankHeaderToString() {
230         return "[DrawerItem VIEW_BLANK_HEADER]";
231     }
232 
233     /**
234      * Create a "waiting for initialization" item.
235      *
236      * @param activity the underlying activity
237      * @return a drawer item with an indeterminate progress indicator.
238      */
ofWaitView(ControllableActivity activity)239     public static DrawerItem ofWaitView(ControllableActivity activity) {
240         return new DrawerItem(VIEW_WAITING_FOR_SYNC, activity, null, INERT_HEADER, null, -1, false,
241                 null, null);
242     }
243 
waitToString()244     private static String waitToString() {
245         return "[DrawerItem VIEW_WAITING_FOR_SYNC ]";
246     }
247 
248     /**
249      * Returns a view for the given item. The method signature is identical to that required by a
250      * {@link android.widget.ListAdapter#getView(int, android.view.View, android.view.ViewGroup)}.
251      */
getView(View convertView, ViewGroup parent)252     public View getView(View convertView, ViewGroup parent) {
253         final View result;
254         switch (mType) {
255             case VIEW_FOLDER:
256                 result = getFolderView(convertView, parent);
257                 break;
258             case VIEW_HEADER:
259                 result = getHeaderView(convertView, parent);
260                 break;
261             case VIEW_BLANK_HEADER:
262                 result = getBlankHeaderView(convertView, parent);
263                 break;
264             case VIEW_ACCOUNT:
265                 result = getAccountView(convertView, parent);
266                 break;
267             case VIEW_WAITING_FOR_SYNC:
268                 result = getEmptyView(convertView, parent);
269                 break;
270             default:
271                 LogUtils.wtf(LOG_TAG, "DrawerItem.getView(%d) for an invalid type!", mType);
272                 result = null;
273         }
274         return result;
275     }
276 
277     /**
278      * Book-keeping for how many different view types there are. Be sure to
279      * increment this appropriately once adding more types as drawer items
280      * @return number of different types of view items
281      */
getViewTypes()282     public static int getViewTypes() {
283         return LAST_FIELD;
284     }
285 
286     /**
287      * Returns whether this view is enabled or not. An enabled view is one that accepts user taps
288      * and acts upon them.
289      * @return true if this view is enabled, false otherwise.
290      */
isItemEnabled()291     public boolean isItemEnabled() {
292         return mIsEnabled;
293     }
294 
295     /** Calculate whether the item is enabled */
calculateEnabled()296     private boolean calculateEnabled() {
297         switch (mType) {
298             case VIEW_HEADER:
299             case VIEW_BLANK_HEADER:
300                 // Headers are never enabled.
301                 return false;
302             case VIEW_FOLDER:
303                 // Folders are always enabled.
304                 return true;
305             case VIEW_ACCOUNT:
306                 // Accounts are always enabled.
307                 return true;
308             case VIEW_WAITING_FOR_SYNC:
309                 // Waiting for sync cannot be tapped, so never enabled.
310                 return false;
311             default:
312                 LogUtils.wtf(LOG_TAG, "DrawerItem.isItemEnabled() for invalid type %d", mType);
313                 return false;
314         }
315     }
316 
317     /**
318      * Returns whether this view is highlighted or not.
319      *
320      *
321      * @param currentFolder The current folder, according to the
322      *                      {@link com.android.mail.ui.FolderListFragment}
323      * @param currentType The type of the current folder. We want to only highlight a folder once.
324      *                    A folder might be in two places at once: in "All Folders", and in
325      *                    "Recent Folder". Valid types of selected folders are :
326      *                    {@link DrawerItem#FOLDER_INBOX}, {@link DrawerItem#FOLDER_RECENT} or
327      *                    {@link DrawerItem#FOLDER_OTHER}, or {@link DrawerItem#UNSET}.
328 
329      * @return true if this DrawerItem results in a view that is highlighted (this DrawerItem is
330      *              the current folder.
331      */
isHighlighted(FolderUri currentFolder, int currentType)332     public boolean isHighlighted(FolderUri currentFolder, int currentType) {
333         switch (mType) {
334             case VIEW_HEADER:
335             case VIEW_BLANK_HEADER:
336                 // Headers are never highlighted
337                 return false;
338             case VIEW_FOLDER:
339                 // True if folder types and URIs are the same
340                 if (currentFolder != null && mFolder != null && mFolder.folderUri != null) {
341                     return (mFolderType == currentType) && mFolder.folderUri.equals(currentFolder);
342                 }
343                 return false;
344             case VIEW_ACCOUNT:
345                 // Accounts are never highlighted
346                 return false;
347             case VIEW_WAITING_FOR_SYNC:
348                 // Waiting for sync cannot be tapped, so never highlighted.
349                 return false;
350             default:
351                 LogUtils.wtf(LOG_TAG, "DrawerItem.isHighlighted() for invalid type %d", mType);
352                 return false;
353         }
354     }
355 
356     /**
357      * Return a view for an account object.
358      *
359      * @param convertView a view, possibly null, to be recycled.
360      * @param parent the parent viewgroup to attach to.
361      * @return a view to display at this position.
362      */
getAccountView(View convertView, ViewGroup parent)363     private View getAccountView(View convertView, ViewGroup parent) {
364         final AccountItemView accountItemView;
365         if (convertView != null) {
366             accountItemView = (AccountItemView) convertView;
367         } else {
368             accountItemView =
369                     (AccountItemView) mInflater.inflate(R.layout.account_item, parent, false);
370         }
371         accountItemView.bind(mActivity.getActivityContext(), mAccount, mIsSelected, mImagesCache,
372                 mContactResolver);
373         return accountItemView;
374     }
375 
376     /**
377      * Returns a text divider between divisions.
378      *
379      * @param convertView a previous view, perhaps null
380      * @param parent the parent of this view
381      * @return a text header at the given position.
382      */
getHeaderView(View convertView, ViewGroup parent)383     private View getHeaderView(View convertView, ViewGroup parent) {
384         final View headerView;
385         if (convertView != null) {
386             headerView = convertView;
387         } else {
388             headerView = mInflater.inflate(R.layout.folder_list_header, parent, false);
389         }
390         final TextView textView = (TextView) headerView.findViewById(R.id.header_text);
391         textView.setText(mResource);
392         return headerView;
393     }
394 
395     /**
396      * Returns a blank divider
397      *
398      * @param convertView A previous view, perhaps null
399      * @param parent the parent of this view
400      * @return a blank header
401      */
getBlankHeaderView(View convertView, ViewGroup parent)402     private View getBlankHeaderView(View convertView, ViewGroup parent) {
403         final View blankHeaderView;
404         if (convertView != null) {
405             blankHeaderView = convertView;
406         } else {
407             blankHeaderView = mInflater.inflate(R.layout.folder_list_blank_header, parent, false);
408         }
409         return blankHeaderView;
410     }
411 
412     /**
413      * Return a folder: either a parent folder or a normal (child or flat)
414      * folder.
415      *
416      * @param convertView a view, possibly null, to be recycled.
417      * @return a view showing a folder at the given position.
418      */
getFolderView(View convertView, ViewGroup parent)419     private View getFolderView(View convertView, ViewGroup parent) {
420         final FolderItemView folderItemView;
421         if (convertView != null) {
422             folderItemView = (FolderItemView) convertView;
423         } else {
424             folderItemView =
425                     (FolderItemView) mInflater.inflate(R.layout.folder_item, parent, false);
426         }
427         folderItemView.bind(mFolder, mActivity);
428         folderItemView.setIcon(mFolder);
429         return folderItemView;
430     }
431 
432     /**
433      * Return a view for the 'Waiting for sync' item with the indeterminate progress indicator.
434      *
435      * @param convertView a view, possibly null, to be recycled.
436      * @param parent the parent hosting this view.
437      * @return a view for "Waiting for sync..." at given position.
438      */
getEmptyView(View convertView, ViewGroup parent)439     private View getEmptyView(View convertView, ViewGroup parent) {
440         final ViewGroup emptyView;
441         if (convertView != null) {
442             emptyView = (ViewGroup) convertView;
443         } else {
444             emptyView = (ViewGroup) mInflater.inflate(R.layout.drawer_empty_view, parent, false);
445         }
446         return emptyView;
447     }
448 
449 }
450 
451