• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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          */
supportsDrag(DragEvent event, Folder folder)64         boolean supportsDrag(DragEvent event, Folder folder);
65 
66         /**
67          * Handles a drop event, applying the appropriate logic.
68          */
handleDrop(DragEvent event, Folder folder)69         void handleDrop(DragEvent event, Folder folder);
70     }
71 
FolderItemView(Context context)72     public FolderItemView(Context context) {
73         super(context);
74 
75         loadResources(context);
76     }
77 
FolderItemView(Context context, AttributeSet attrs)78     public FolderItemView(Context context, AttributeSet attrs) {
79         super(context, attrs);
80 
81         loadResources(context);
82     }
83 
FolderItemView(Context context, AttributeSet attrs, int defStyle)84     public FolderItemView(Context context, AttributeSet attrs, int defStyle) {
85         super(context, attrs, defStyle);
86 
87         loadResources(context);
88         mIsDragMode = false;
89     }
90 
loadResources(Context context)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
onFinishInflate()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      */
areSameViews(final Folder a, final Folder b)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 
bind(final Folder folder, final DropHandler dropHandler)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      */
setIcon(final Folder folder)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      */
setUnreadCount(int count)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      */
setUnseenCount(final int color, final int count)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      */
overrideUnreadCount(int count)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 
isDroppableTarget(DragEvent event)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
onDragEvent(DragEvent event)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
onCreateDrawableState(int extraSpace)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 
setDragMode(boolean isDragMode)245     private void setDragMode(boolean isDragMode) {
246         mIsDragMode = isDragMode;
247         refreshDrawableState();
248     }
249 }
250