/** * Copyright (c) 2012, Google Inc. * * 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.ui; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RoundRectShape; import android.graphics.drawable.shapes.Shape; import android.util.AttributeSet; import android.view.DragEvent; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import com.android.mail.R; import com.android.mail.providers.Folder; import com.android.mail.utils.LogTag; import com.android.mail.utils.LogUtils; import com.android.mail.utils.Utils; /** * The view for each folder in the folder list. */ public class FolderItemView extends RelativeLayout { private final String LOG_TAG = LogTag.getLogTag(); private static final int[] STATE_DRAG_MODE = {R.attr.state_drag_mode}; private static float[] sUnseenCornerRadii; private Folder mFolder; private TextView mFolderTextView; private TextView mUnreadCountTextView; private TextView mUnseenCountTextView; private DropHandler mDropHandler; private ImageView mFolderParentIcon; private boolean mIsDragMode; /** * A delegate for a handler to handle a drop of an item. */ public interface DropHandler { /** * Return whether or not the drag event is supported by the drop handler. The * {@code FolderItemView} will present appropriate visual affordances if the drag is * supported. */ boolean supportsDrag(DragEvent event, Folder folder); /** * Handles a drop event, applying the appropriate logic. */ void handleDrop(DragEvent event, Folder folder); } public FolderItemView(Context context) { super(context); loadResources(context); } public FolderItemView(Context context, AttributeSet attrs) { super(context, attrs); loadResources(context); } public FolderItemView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); loadResources(context); mIsDragMode = false; } private void loadResources(Context context) { if (sUnseenCornerRadii == null) { final float cornerRadius = context.getResources().getDimension(R.dimen.folder_rounded_corner_radius); sUnseenCornerRadii = new float[] { cornerRadius, cornerRadius, // top left cornerRadius, cornerRadius, // top right cornerRadius, cornerRadius, // bottom right cornerRadius, cornerRadius // bottom left }; } } @Override protected void onFinishInflate() { super.onFinishInflate(); mFolderTextView = (TextView)findViewById(R.id.name); mUnreadCountTextView = (TextView)findViewById(R.id.unread); mUnseenCountTextView = (TextView)findViewById(R.id.unseen); mFolderParentIcon = (ImageView) findViewById(R.id.folder_parent_icon); } /** * Returns true if the two folders lead to identical {@link FolderItemView} objects. * @param a * @param b * @return true if the two folders would still lead to the same {@link FolderItemView}. */ public static boolean areSameViews(final Folder a, final Folder b) { if (a == null) { return b == null; } if (b == null) { // a is not null because it would have returned above. return false; } return (a == b || (a.folderUri.equals(b.folderUri) && a.name.equals(b.name) && a.hasChildren == b.hasChildren && a.unseenCount == b.unseenCount && a.unreadCount == b.unreadCount)); } public void bind(final Folder folder, final DropHandler dropHandler) { mFolder = folder; mDropHandler = dropHandler; mFolderTextView.setText(folder.name); mFolderParentIcon.setVisibility(mFolder.hasChildren ? View.VISIBLE : View.GONE); if (mFolder.isInbox() && mFolder.unseenCount > 0) { mUnreadCountTextView.setVisibility(View.GONE); setUnseenCount(mFolder.getBackgroundColor(Color.BLACK), mFolder.unseenCount); } else { mUnseenCountTextView.setVisibility(View.GONE); setUnreadCount(Utils.getFolderUnreadDisplayCount(mFolder)); } } /** * Sets the icon, if any. */ public void setIcon(final Folder folder) { final ImageView folderIconView = (ImageView) findViewById(R.id.folder_icon); Folder.setIcon(folder, folderIconView); } /** * Sets the unread count, taking care to hide/show the textview if the count is zero/non-zero. */ private void setUnreadCount(int count) { mUnreadCountTextView.setVisibility(count > 0 ? View.VISIBLE : View.GONE); if (count > 0) { mUnreadCountTextView.setText(Utils.getUnreadCountString(getContext(), count)); } } /** * Sets the unseen count, taking care to hide/show the textview if the count is zero/non-zero. */ private void setUnseenCount(final int color, final int count) { mUnseenCountTextView.setVisibility(count > 0 ? View.VISIBLE : View.GONE); if (count > 0) { final Shape shape = new RoundRectShape(sUnseenCornerRadii, null, null); final ShapeDrawable drawable = new ShapeDrawable(shape); drawable.getPaint().setColor(color); mUnseenCountTextView.setBackgroundDrawable(drawable); mUnseenCountTextView.setText(Utils.getUnseenCountString(getContext(), count)); } } /** * Used if we detect a problem with the unread count and want to force an override. * @param count */ public final void overrideUnreadCount(int count) { LogUtils.e(LOG_TAG, "FLF->FolderItem.getFolderView: unread count mismatch found (%s vs %d)", mUnreadCountTextView.getText(), count); setUnreadCount(count); } private boolean isDroppableTarget(DragEvent event) { return (mDropHandler != null && mDropHandler.supportsDrag(event, mFolder)); } /** * Handles the drag event. * * @param event the drag event to be handled */ @Override public boolean onDragEvent(DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: // Set drag mode state to true now that we have entered drag mode. // This change updates the states of icons and text colors. // Additional drawable states are updated by the framework // based on the DragEvent. setDragMode(true); case DragEvent.ACTION_DRAG_ENTERED: case DragEvent.ACTION_DRAG_EXITED: // All of these states return based on isDroppableTarget's return value. // If modifying, watch the switch's drop-through effects. return isDroppableTarget(event); case DragEvent.ACTION_DRAG_ENDED: // Set drag mode to false since we're leaving drag mode. // Updates all the states of icons and text colors back to non-drag values. setDragMode(false); return true; case DragEvent.ACTION_DRAG_LOCATION: return true; case DragEvent.ACTION_DROP: if (mDropHandler == null) { return false; } mDropHandler.handleDrop(event, mFolder); return true; } return false; } @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (mIsDragMode) { mergeDrawableStates(drawableState, STATE_DRAG_MODE); } return drawableState; } private void setDragMode(boolean isDragMode) { mIsDragMode = isDragMode; refreshDrawableState(); } }