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