com.android.mail.ui.FolderDisplayer.java Source code

Java tutorial

Introduction

Here is the source code for com.android.mail.ui.FolderDisplayer.java

Source

/*
 * Copyright (C) 2012 Google Inc.
 * Licensed to 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.ui;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.v4.text.BidiFormatter;

import com.android.mail.R;
import com.android.mail.providers.Conversation;
import com.android.mail.providers.Folder;
import com.android.mail.providers.UIProvider.FolderType;
import com.android.mail.utils.FolderUri;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.Utils;
import com.google.common.collect.Sets;

import java.util.NavigableSet;
import java.util.Set;

/**
 * Used to generate folder display information given a raw folders string.
 * (The raw folders string can be obtained from {@link Conversation#getRawFolders()}.)
 */
public abstract class FolderDisplayer {
    public static final String LOG_TAG = LogTag.getLogTag();
    protected Context mContext;
    protected final NavigableSet<Folder> mFoldersSortedSet = Sets.newTreeSet();
    protected final FolderDrawableResources mFolderDrawableResources = new FolderDrawableResources();

    public static class FolderDrawableResources {
        public int defaultFgColor;
        public int defaultBgColor;
        public int folderRoundedCornerRadius;
        public int overflowGradientPadding;
        public int folderHorizontalPadding;
        public int folderInBetweenPadding;
        public int folderFontSize;
        public int folderVerticalOffset;
    }

    public FolderDisplayer(Context context) {
        mContext = context;
        initializeDrawableResources();
    }

    protected void initializeDrawableResources() {
        // Set default values used across all folder chips
        final Resources res = mContext.getResources();
        mFolderDrawableResources.defaultFgColor = res.getColor(R.color.default_folder_foreground_color);
        mFolderDrawableResources.defaultBgColor = res.getColor(R.color.default_folder_background_color);
        mFolderDrawableResources.folderRoundedCornerRadius = res
                .getDimensionPixelOffset(R.dimen.folder_rounded_corner_radius);
        mFolderDrawableResources.folderInBetweenPadding = res.getDimensionPixelOffset(R.dimen.folder_start_padding);
    }

    /**
     * Configure the FolderDisplayer object by filtering and copying from the list of raw folders.
     *
     * @param conv {@link Conversation} containing the folders to display.
     * @param ignoreFolderUri (optional) folder to omit from the displayed set
     * @param ignoreFolderType -1, or the {@link FolderType} to omit from the displayed set
     */
    public void loadConversationFolders(Conversation conv, final FolderUri ignoreFolderUri,
            final int ignoreFolderType) {
        mFoldersSortedSet.clear();
        for (Folder folder : conv.getRawFolders()) {
            // Skip the ignoreFolderType
            if (ignoreFolderType >= 0 && folder.isType(ignoreFolderType)) {
                continue;
            }
            // skip the ignoreFolder
            if (ignoreFolderUri != null && ignoreFolderUri.equals(folder.folderUri)) {
                continue;
            }
            mFoldersSortedSet.add(folder);
        }
    }

    /**
     * Reset this FolderDisplayer so that it can be reused.
     */
    public void reset() {
        mFoldersSortedSet.clear();
    }

    /**
     * Helper function to calculate exactly how much space the displayed folders should take.
     * @param folders the set of folders to display.
     * @param maxCellWidth this signifies the absolute max for each folder cell, no exceptions.
     * @param maxLayoutWidth the view's layout width, aka how much space we have.
     * @param foldersInBetweenPadding the padding between folder chips.
     * @param foldersHorizontalPadding the padding between the edge of the chip and the text.
     * @param maxFolderCount the maximum number of folder chips to display.
     * @param paint work paint.
     * @return an array of integers that signifies the length of each folder chip.
     */
    public static int[] measureFolderDimen(Set<Folder> folders, int maxCellWidth, int maxLayoutWidth,
            int foldersInBetweenPadding, int foldersHorizontalPadding, int maxFolderCount, Paint paint) {

        final int numDisplayedFolders = Math.min(maxFolderCount, folders.size());
        if (numDisplayedFolders == 0) {
            return new int[0];
        }

        // This variable is calculated based on the number of folders we are displaying
        final int maxAllowedCellSize = Math.min(maxCellWidth,
                (maxLayoutWidth - (numDisplayedFolders - 1) * foldersInBetweenPadding) / numDisplayedFolders);
        final int[] measurements = new int[numDisplayedFolders];

        int count = 0;
        int missingWidth = 0;
        int extraWidth = 0;
        for (Folder f : folders) {
            if (count > numDisplayedFolders - 1) {
                break;
            }

            final String folderString = f.name;
            final int neededWidth = (int) paint.measureText(folderString) + 2 * foldersHorizontalPadding;

            if (neededWidth > maxAllowedCellSize) {
                // What we can take from others is the minimum of the width we need to borrow
                // and the width we are allowed to borrow.
                final int borrowedWidth = Math.min(neededWidth - maxAllowedCellSize,
                        maxCellWidth - maxAllowedCellSize);
                final int extraWidthLeftover = extraWidth - borrowedWidth;
                if (extraWidthLeftover >= 0) {
                    measurements[count] = Math.min(neededWidth, maxCellWidth);
                    extraWidth = extraWidthLeftover;
                } else {
                    measurements[count] = maxAllowedCellSize + extraWidth;
                    extraWidth = 0;
                }
                missingWidth = -extraWidthLeftover;
            } else {
                extraWidth = maxAllowedCellSize - neededWidth;
                measurements[count] = neededWidth;
                if (missingWidth > 0) {
                    if (extraWidth >= missingWidth) {
                        measurements[count - 1] += missingWidth;
                        extraWidth -= missingWidth;
                    } else {
                        measurements[count - 1] += extraWidth;
                        extraWidth = 0;
                    }
                }
                missingWidth = 0;
            }

            count++;
        }

        return measurements;
    }

    public static void drawFolder(Canvas canvas, float x, float y, int width, int height, Folder f,
            FolderDisplayer.FolderDrawableResources res, BidiFormatter formatter, Paint paint) {
        drawFolder(canvas, x, y, width, height, f.name, f.getForegroundColor(res.defaultFgColor),
                f.getBackgroundColor(res.defaultBgColor), res, formatter, paint);
    }

    public static void drawFolder(Canvas canvas, float x, float y, int width, int height, String name, int fgColor,
            int bgColor, FolderDisplayer.FolderDrawableResources res, BidiFormatter formatter, Paint paint) {
        canvas.save();
        canvas.translate(x, y + res.folderVerticalOffset);

        // Draw the box.
        paint.setColor(bgColor);
        paint.setStyle(Paint.Style.FILL);
        final RectF rect = new RectF(0, 0, width, height);
        canvas.drawRoundRect(rect, res.folderRoundedCornerRadius, res.folderRoundedCornerRadius, paint);

        // Draw the text based on the language locale and layout direction.
        paint.setColor(fgColor);
        paint.setStyle(Paint.Style.FILL);

        // Compute the text/gradient indices
        final int textLength = (int) paint.measureText(name);
        final int gradientX0;
        final int gradientX1;
        final int textX;

        /***************************************************************************************************
         * width               - the actual folder chip rectangle.                                         *
         * textLength          - the length of the folder's full name (can be longer than                  *
         *                         the actual chip, which is what overflow gradient is for).               *
         * innerPadding        - the padding between the text and the chip edge.                           *
         * overflowPadding     - the padding between start of overflow and the chip edge.                  *
         *                                                                                                 *
         *                                                                                                 *
         * text is in a RTL language                                                                       *
         *                                                                                                 *
         *                   index-0                                                                       *
         *                      |<---------------------------- width ---------------------------->|        *
         *        |<-------------------------textLength------------------>|                       |        *
         *        |             |<----- overflowPadding ----->|                                   |        *
         *        |             |<- innerPadding ->|<-------->|<--------->|<- horizontalPadding ->|        *
         *       textX                            gX1        gX0                                           *
         *                                                                                                 *
         *                                                                                                 *
         * text is in a LTR language.                                                                      *
         *                                                                                                 *
         *     index-0                                                                                     *
         *        |<------------------------------ width ------------------------------->|                 *
         *        |                       |<-------------------------textLength-------------------->|      *
         *        |                                   |<-------- overflowPadding ------->|                 *
         *        |<- horizontalPadding ->|<--------->|<-------->|<- horizontalPadding ->|                 *
         *                              textX        gX0        gX1                                        *
         *                                                                                                 *
         **************************************************************************************************/
        if (formatter.isRtl(name)) {
            gradientX0 = res.overflowGradientPadding;
            gradientX1 = res.folderHorizontalPadding;
            textX = width - res.folderHorizontalPadding - textLength;
        } else {
            gradientX0 = width - res.overflowGradientPadding;
            gradientX1 = width - res.folderHorizontalPadding;
            textX = res.folderHorizontalPadding;
        }

        // Draw the text and the possible overflow gradient
        // Overflow happens when the text is longer than the chip width minus side paddings.
        if (textLength > width - 2 * res.folderHorizontalPadding) {
            final Shader shader = new LinearGradient(gradientX0, 0, gradientX1, 0, fgColor,
                    Utils.getTransparentColor(fgColor), Shader.TileMode.CLAMP);
            paint.setShader(shader);
        }
        final int textY = height / 2 - (int) (paint.descent() + paint.ascent()) / 2;
        canvas.drawText(name, textX, textY, paint);
        paint.setShader(null);

        canvas.restore();
    }
}