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