Home | History | Annotate | Download | only in ui
      1 /**
      2  * Copyright (c) 2012, Google Inc.
      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 package com.android.mail.ui;
     17 
     18 import android.content.Context;
     19 import android.graphics.Color;
     20 import android.support.v4.text.BidiFormatter;
     21 import android.util.AttributeSet;
     22 import android.view.DragEvent;
     23 import android.view.View;
     24 import android.widget.ImageView;
     25 import android.widget.RelativeLayout;
     26 import android.widget.TextView;
     27 
     28 import com.android.mail.R;
     29 import com.android.mail.providers.Folder;
     30 import com.android.mail.utils.LogTag;
     31 import com.android.mail.utils.LogUtils;
     32 import com.android.mail.utils.Utils;
     33 
     34 /**
     35  * The view for each folder in the folder list.
     36  */
     37 public class FolderItemView extends RelativeLayout {
     38     private final String LOG_TAG = LogTag.getLogTag();
     39 
     40     private static final int[] STATE_DRAG_MODE = {R.attr.state_drag_mode};
     41 
     42     private Folder mFolder;
     43     private TextView mFolderTextView;
     44     private TextView mUnreadCountTextView;
     45     private TextView mUnseenCountTextView;
     46     private DropHandler mDropHandler;
     47     private ImageView mFolderParentIcon;
     48 
     49     private boolean mIsDragMode;
     50 
     51     /**
     52      * A delegate for a handler to handle a drop of an item.
     53      */
     54     public interface DropHandler {
     55         /**
     56          * Return whether or not the drag event is supported by the drop handler. The
     57          *     {@code FolderItemView} will present appropriate visual affordances if the drag is
     58          *     supported.
     59          */
     60         boolean supportsDrag(DragEvent event, Folder folder);
     61 
     62         /**
     63          * Handles a drop event, applying the appropriate logic.
     64          */
     65         void handleDrop(DragEvent event, Folder folder);
     66     }
     67 
     68     public FolderItemView(Context context) {
     69         super(context);
     70     }
     71 
     72     public FolderItemView(Context context, AttributeSet attrs) {
     73         super(context, attrs);
     74     }
     75 
     76     public FolderItemView(Context context, AttributeSet attrs, int defStyle) {
     77         super(context, attrs, defStyle);
     78 
     79         mIsDragMode = false;
     80     }
     81 
     82     @Override
     83     protected void onFinishInflate() {
     84         super.onFinishInflate();
     85 
     86         mFolderTextView = (TextView)findViewById(R.id.name);
     87         mUnreadCountTextView = (TextView)findViewById(R.id.unread);
     88         mUnseenCountTextView = (TextView)findViewById(R.id.unseen);
     89         mFolderParentIcon = (ImageView) findViewById(R.id.folder_parent_icon);
     90     }
     91 
     92     /**
     93      * Returns true if the two folders lead to identical {@link FolderItemView} objects.
     94      * @param a
     95      * @param b
     96      * @return true if the two folders would still lead to the same {@link FolderItemView}.
     97      */
     98     public static boolean areSameViews(final Folder a, final Folder b) {
     99         if (a == null) {
    100             return b == null;
    101         }
    102         if (b == null) {
    103             // a is not null because it would have returned above.
    104             return false;
    105         }
    106         return (a == b || (a.folderUri.equals(b.folderUri)
    107                 && a.name.equals(b.name)
    108                 && a.hasChildren == b.hasChildren
    109                 && a.unseenCount == b.unseenCount
    110                 && a.unreadCount == b.unreadCount));
    111     }
    112 
    113     public void bind(final Folder folder, final DropHandler dropHandler,
    114             final BidiFormatter bidiFormatter) {
    115         mFolder = folder;
    116         mDropHandler = dropHandler;
    117 
    118         mFolderTextView.setText(bidiFormatter.unicodeWrap(folder.name));
    119 
    120         mFolderParentIcon.setVisibility(mFolder.hasChildren ? View.VISIBLE : View.GONE);
    121         if (mFolder.isInbox() && mFolder.unseenCount > 0) {
    122             mUnreadCountTextView.setVisibility(View.GONE);
    123             setUnseenCount(mFolder.getBackgroundColor(Color.BLACK), mFolder.unseenCount);
    124         } else {
    125             mUnseenCountTextView.setVisibility(View.GONE);
    126             setUnreadCount(Utils.getFolderUnreadDisplayCount(mFolder));
    127         }
    128     }
    129 
    130     /**
    131      * Sets the icon, if any. If the image view's visibility is set to gone, the text view will
    132      * be moved over to account for the change.
    133      */
    134     public void setIcon(final Folder folder) {
    135         final ImageView folderIconView = (ImageView) findViewById(R.id.folder_icon);
    136         Folder.setIcon(folder, folderIconView);
    137         if (folderIconView.getVisibility() == View.GONE) {
    138             mFolderTextView.setPadding(getContext()
    139                     .getResources().getDimensionPixelSize(R.dimen.folder_list_item_left_offset),
    140                     0, 0, 0 /* No top, right, bottom padding needed */);
    141         } else {
    142             // View recycling case
    143             mFolderTextView.setPadding(0, 0, 0, 0);
    144         }
    145     }
    146 
    147     /**
    148      * Sets the unread count, taking care to hide/show the textview if the count is zero/non-zero.
    149      */
    150     private void setUnreadCount(int count) {
    151         mUnreadCountTextView.setVisibility(count > 0 ? View.VISIBLE : View.GONE);
    152         if (count > 0) {
    153             mUnreadCountTextView.setText(Utils.getUnreadCountString(getContext(), count));
    154         }
    155     }
    156 
    157     /**
    158      * Sets the unseen count, taking care to hide/show the textview if the count is zero/non-zero.
    159      */
    160     private void setUnseenCount(final int color, final int count) {
    161         mUnseenCountTextView.setVisibility(count > 0 ? View.VISIBLE : View.GONE);
    162         if (count > 0) {
    163             mUnseenCountTextView.setBackgroundColor(color);
    164             mUnseenCountTextView.setText(Utils.getUnreadCountString(getContext(), count));
    165         }
    166     }
    167 
    168     /**
    169      * Used if we detect a problem with the unread count and want to force an override.
    170      * @param count
    171      */
    172     public final void overrideUnreadCount(int count) {
    173         LogUtils.e(LOG_TAG, "FLF->FolderItem.getFolderView: unread count mismatch found (%s vs %d)",
    174                 mUnreadCountTextView.getText(), count);
    175         setUnreadCount(count);
    176     }
    177 
    178     private boolean isDroppableTarget(DragEvent event) {
    179         return (mDropHandler != null && mDropHandler.supportsDrag(event, mFolder));
    180     }
    181 
    182     /**
    183      * Handles the drag event.
    184      *
    185      * @param event the drag event to be handled
    186      */
    187     @Override
    188     public boolean onDragEvent(DragEvent event) {
    189         switch (event.getAction()) {
    190             case DragEvent.ACTION_DRAG_STARTED:
    191                 // Set drag mode state to true now that we have entered drag mode.
    192                 // This change updates the states of icons and text colors.
    193                 // Additional drawable states are updated by the framework
    194                 // based on the DragEvent.
    195                 setDragMode(true);
    196             case DragEvent.ACTION_DRAG_ENTERED:
    197             case DragEvent.ACTION_DRAG_EXITED:
    198                 // All of these states return based on isDroppableTarget's return value.
    199                 // If modifying, watch the switch's drop-through effects.
    200                 return isDroppableTarget(event);
    201             case DragEvent.ACTION_DRAG_ENDED:
    202                 // Set drag mode to false since we're leaving drag mode.
    203                 // Updates all the states of icons and text colors back to non-drag values.
    204                 setDragMode(false);
    205                 return true;
    206 
    207             case DragEvent.ACTION_DRAG_LOCATION:
    208                 return true;
    209 
    210             case DragEvent.ACTION_DROP:
    211                 if (mDropHandler == null) {
    212                     return false;
    213                 }
    214 
    215                 mDropHandler.handleDrop(event, mFolder);
    216                 return true;
    217         }
    218         return false;
    219     }
    220 
    221     @Override
    222     protected int[] onCreateDrawableState(int extraSpace) {
    223         final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    224         if (mIsDragMode) {
    225             mergeDrawableStates(drawableState, STATE_DRAG_MODE);
    226         }
    227         return drawableState;
    228     }
    229 
    230     private void setDragMode(boolean isDragMode) {
    231         mIsDragMode = isDragMode;
    232         refreshDrawableState();
    233     }
    234 }
    235