blob: 093bdd328e021bf55dd432bb7a658dd1e815dd0e [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mail.adapter;
import android.support.v4.text.BidiFormatter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.mail.R;
import com.android.mail.providers.Account;
import com.android.mail.providers.Folder;
import com.android.mail.ui.AccountItemView;
import com.android.mail.ui.ControllableActivity;
import com.android.mail.ui.FolderItemView;
import com.android.mail.utils.FolderUri;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
/**
* An element that is shown in the {@link com.android.mail.ui.FolderListFragment}. This class is
* only used for elements that are shown in the {@link com.android.mail.ui.DrawerFragment}.
* This class is an enumeration of a few element types: Account, a folder, a recent folder,
* or a header (a resource string). A {@link DrawerItem} can only be one type and can never
* switch types. Items are created using methods like
* {@link DrawerItem#ofAccount(com.android.mail.ui.ControllableActivity,
com.android.mail.providers.Account, int, boolean, BidiFormatter)},
* {@link DrawerItem#ofWaitView(com.android.mail.ui.ControllableActivity, BidiFormatter)}, etc.
*
* Once created, the item can create a view using
* {@link #getView(android.view.View, android.view.ViewGroup)}.
*/
public class DrawerItem {
private static final String LOG_TAG = LogTag.getLogTag();
public final Folder mFolder;
public final Account mAccount;
public final int mResource;
/** True if the drawer item represents the current account, false otherwise */
public boolean mIsSelected;
/** Either {@link #VIEW_ACCOUNT}, {@link #VIEW_FOLDER} or {@link #VIEW_HEADER} */
public final int mType;
/** A normal folder, also a child, if a parent is specified. */
public static final int VIEW_FOLDER = 0;
/** A text-label which serves as a header in sectioned lists. */
public static final int VIEW_HEADER = 1;
/** An account object, which allows switching accounts rather than folders. */
public static final int VIEW_ACCOUNT = 2;
/** An expandable object for expanding/collapsing more of the list */
public static final int VIEW_WAITING_FOR_SYNC = 3;
/** The value (1-indexed) of the last View type. Useful when returning the number of types. */
private static final int LAST_FIELD = VIEW_WAITING_FOR_SYNC + 1;
/** TODO: On adding another type, be sure to change getViewTypes() */
/** The parent activity */
private final ControllableActivity mActivity;
private final LayoutInflater mInflater;
private final BidiFormatter mBidiFormatter;
// TODO(viki): Put all these constants in an interface.
/**
* Either {@link #FOLDER_INBOX}, {@link #FOLDER_RECENT} or {@link #FOLDER_OTHER} when
* {@link #mType} is {@link #VIEW_FOLDER}, or an {@link #ACCOUNT} in the case of
* accounts, and {@link #INERT_HEADER} otherwise.
*/
public final int mFolderType;
/** Non existent item or folder type not yet set */
public static final int UNSET = 0;
/** An unclickable text-header visually separating the different types. */
public static final int INERT_HEADER = 0;
/** An inbox folder: Inbox, ...*/
public static final int FOLDER_INBOX = 1;
/** A folder from whom a conversation was recently viewed */
public static final int FOLDER_RECENT = 2;
/** A non-inbox folder that is shown in the "everything else" group. */
public static final int FOLDER_OTHER = 3;
/** An entry for the accounts the user has on the device. */
public static final int ACCOUNT = 4;
/** True if this view is enabled, false otherwise. */
private final boolean mIsEnabled;
@Override
public String toString() {
switch(mType) {
case VIEW_FOLDER:
return folderToString();
case VIEW_HEADER:
return headerToString();
case VIEW_ACCOUNT:
return accountToString();
case VIEW_WAITING_FOR_SYNC:
return waitToString();
}
// Should never come here.
return null;
}
/**
* Creates a drawer item with every instance variable specified.
*
* @param type the type of the item. This must be a VIEW_* element
* @param activity the underlying activity
* @param folder a non-null folder, if this is a folder type
* @param folderType the type of the folder. For folders this is:
* {@link #FOLDER_INBOX}, {@link #FOLDER_RECENT},
* {@link #FOLDER_OTHER}, or for non-folders this is
* {@link #ACCOUNT}, or {@link #INERT_HEADER}
* @param account the account object, for an account drawer element
* @param resource either the string resource for a header, or the unread
* count for an account.
* @param isCurrentAccount true if this item is the current account
*/
private DrawerItem(int type, ControllableActivity activity, Folder folder, int folderType,
Account account, int resource, boolean isCurrentAccount, BidiFormatter bidiFormatter) {
mActivity = activity;
mFolder = folder;
mFolderType = folderType;
mAccount = account;
mResource = resource;
mIsSelected = isCurrentAccount;
mInflater = LayoutInflater.from(activity.getActivityContext());
mType = type;
mIsEnabled = calculateEnabled();
mBidiFormatter = bidiFormatter;
}
/**
* Create a folder item with the given type.
*
* @param activity the underlying activity
* @param folder a folder that this item represents
* @param folderType one of {@link #FOLDER_INBOX}, {@link #FOLDER_RECENT} or
* {@link #FOLDER_OTHER}
* @return a drawer item for the folder.
*/
public static DrawerItem ofFolder(ControllableActivity activity, Folder folder,
int folderType, BidiFormatter bidiFormatter) {
return new DrawerItem(VIEW_FOLDER, activity, folder, folderType, null, -1, false,
bidiFormatter);
}
private String folderToString() {
final StringBuilder sb = new StringBuilder("[DrawerItem ");
sb.append(" VIEW_FOLDER ");
sb.append(", mFolder=");
sb.append(mFolder);
sb.append(", mFolderType=");
sb.append(mFolderType);
sb.append("]");
return sb.toString();
}
/**
* Creates an item from an account.
* @param activity the underlying activity
* @param account the account to create a drawer item for
* @param unreadCount the unread count of the account, pass zero if
* @param isCurrentAccount true if the account is the current account, false otherwise
* @return a drawer item for the account.
*/
public static DrawerItem ofAccount(ControllableActivity activity, Account account,
int unreadCount, boolean isCurrentAccount, BidiFormatter bidiFormatter) {
return new DrawerItem(VIEW_ACCOUNT, activity, null, ACCOUNT, account, unreadCount,
isCurrentAccount, bidiFormatter);
}
private String accountToString() {
final StringBuilder sb = new StringBuilder("[DrawerItem ");
sb.append(" VIEW_ACCOUNT ");
sb.append(", mAccount=");
sb.append(mAccount);
sb.append("]");
return sb.toString();
}
/**
* Create a header item with a string resource.
*
* @param activity the underlying activity
* @param resource the string resource: R.string.all_folders_heading
* @return a drawer item for the header.
*/
public static DrawerItem ofHeader(ControllableActivity activity, int resource,
BidiFormatter bidiFormatter) {
return new DrawerItem(VIEW_HEADER, activity, null, INERT_HEADER, null, resource, false,
bidiFormatter);
}
private String headerToString() {
final StringBuilder sb = new StringBuilder("[DrawerItem ");
sb.append(" VIEW_HEADER ");
sb.append(", mResource=");
sb.append(mResource);
sb.append("]");
return sb.toString();
}
/**
* Create a "waiting for initialization" item.
*
* @param activity the underlying activity
* @return a drawer item with an indeterminate progress indicator.
*/
public static DrawerItem ofWaitView(ControllableActivity activity,
BidiFormatter bidiFormatter) {
return new DrawerItem(VIEW_WAITING_FOR_SYNC, activity, null, INERT_HEADER, null, -1, false,
bidiFormatter);
}
private static String waitToString() {
return "[DrawerItem VIEW_WAITING_FOR_SYNC ]";
}
/**
* Returns a view for the given item. The method signature is identical to that required by a
* {@link android.widget.ListAdapter#getView(int, android.view.View, android.view.ViewGroup)}.
*/
public View getView(View convertView, ViewGroup parent) {
final View result;
switch (mType) {
case VIEW_FOLDER:
result = getFolderView(convertView, parent);
break;
case VIEW_HEADER:
result = getHeaderView(convertView, parent);
break;
case VIEW_ACCOUNT:
result = getAccountView(convertView, parent);
break;
case VIEW_WAITING_FOR_SYNC:
result = getEmptyView(convertView, parent);
break;
default:
LogUtils.wtf(LOG_TAG, "DrawerItem.getView(%d) for an invalid type!", mType);
result = null;
}
return result;
}
/**
* Book-keeping for how many different view types there are. Be sure to
* increment this appropriately once adding more types as drawer items
* @return number of different types of view items
*/
public static int getViewTypes() {
return LAST_FIELD;
}
/**
* Returns whether this view is enabled or not. An enabled view is one that accepts user taps
* and acts upon them.
* @return true if this view is enabled, false otherwise.
*/
public boolean isItemEnabled() {
return mIsEnabled;
}
/** Calculate whether the item is enabled */
private boolean calculateEnabled() {
switch (mType) {
case VIEW_HEADER :
// Headers are never enabled.
return false;
case VIEW_FOLDER :
// Folders are always enabled.
return true;
case VIEW_ACCOUNT:
// Accounts are always enabled.
return true;
case VIEW_WAITING_FOR_SYNC:
// Waiting for sync cannot be tapped, so never enabled.
return false;
default:
LogUtils.wtf(LOG_TAG, "DrawerItem.isItemEnabled() for invalid type %d", mType);
return false;
}
}
/**
* Returns whether this view is highlighted or not.
*
*
* @param currentFolder The current folder, according to the
* {@link com.android.mail.ui.FolderListFragment}
* @param currentType The type of the current folder. We want to only highlight a folder once.
* A folder might be in two places at once: in "All Folders", and in
* "Recent Folder". Valid types of selected folders are :
* {@link DrawerItem#FOLDER_INBOX}, {@link DrawerItem#FOLDER_RECENT} or
* {@link DrawerItem#FOLDER_OTHER}, or {@link DrawerItem#UNSET}.
* @return true if this DrawerItem results in a view that is highlighted (this DrawerItem is
* the current folder.
*/
public boolean isHighlighted(FolderUri currentFolder, int currentType) {
switch (mType) {
case VIEW_HEADER :
// Headers are never highlighted
return false;
case VIEW_FOLDER:
// True if folder types and URIs are the same
if (currentFolder != null && mFolder != null && mFolder.folderUri != null) {
return (mFolderType == currentType) && mFolder.folderUri.equals(currentFolder);
}
return false;
case VIEW_ACCOUNT:
// Accounts are never highlighted
return false;
case VIEW_WAITING_FOR_SYNC:
// Waiting for sync cannot be tapped, so never highlighted.
return false;
default:
LogUtils.wtf(LOG_TAG, "DrawerItem.isHighlighted() for invalid type %d", mType);
return false;
}
}
/**
* Return a view for an account object.
*
* @param convertView a view, possibly null, to be recycled.
* @param parent the parent viewgroup to attach to.
* @return a view to display at this position.
*/
private View getAccountView(View convertView, ViewGroup parent) {
final AccountItemView accountItemView;
if (convertView != null) {
accountItemView = (AccountItemView) convertView;
} else {
accountItemView =
(AccountItemView) mInflater.inflate(R.layout.account_item, parent, false);
}
accountItemView.bind(mAccount, mIsSelected, mResource, mBidiFormatter);
View v = accountItemView.findViewById(R.id.account_graphic);
v.setBackgroundColor(mAccount.color);
return accountItemView;
}
/**
* Returns a text divider between divisions.
*
* @param convertView a previous view, perhaps null
* @param parent the parent of this view
* @return a text header at the given position.
*/
private View getHeaderView(View convertView, ViewGroup parent) {
final TextView headerView;
if (convertView != null) {
headerView = (TextView) convertView;
} else {
headerView = (TextView) mInflater.inflate(R.layout.folder_list_header, parent, false);
}
headerView.setText(mResource);
return headerView;
}
/**
* Return a folder: either a parent folder or a normal (child or flat)
* folder.
*
* @param convertView a view, possibly null, to be recycled.
* @return a view showing a folder at the given position.
*/
private View getFolderView(View convertView, ViewGroup parent) {
final FolderItemView folderItemView;
if (convertView != null) {
folderItemView = (FolderItemView) convertView;
} else {
folderItemView =
(FolderItemView) mInflater.inflate(R.layout.folder_item, parent, false);
}
folderItemView.bind(mFolder, mActivity, mBidiFormatter);
Folder.setFolderBlockColor(mFolder, folderItemView.findViewById(R.id.color_block));
folderItemView.setIcon(mFolder);
return folderItemView;
}
/**
* Return a view for the 'Waiting for sync' item with the indeterminate progress indicator.
*
* @param convertView a view, possibly null, to be recycled.
* @param parent the parent hosting this view.
* @return a view for "Waiting for sync..." at given position.
*/
private View getEmptyView(View convertView, ViewGroup parent) {
final ViewGroup emptyView;
if (convertView != null) {
emptyView = (ViewGroup) convertView;
} else {
emptyView = (ViewGroup) mInflater.inflate(R.layout.drawer_empty_view, parent, false);
}
return emptyView;
}
}