1326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira/*
2326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * Copyright (C) 2012 Google Inc.
3326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * Licensed to The Android Open Source Project.
4326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira *
5326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
6326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * you may not use this file except in compliance with the License.
7326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * You may obtain a copy of the License at
8326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira *
9326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira *      http://www.apache.org/licenses/LICENSE-2.0
10326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira *
11326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * Unless required by applicable law or agreed to in writing, software
12326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
13326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * See the License for the specific language governing permissions and
15326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * limitations under the License.
16326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira */
1730e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangpackage com.android.mail;
18326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
19326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.content.Context;
20326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport android.text.format.DateUtils;
21326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
22b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdonimport java.util.Calendar;
23326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereiraimport java.util.Formatter;
24326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
25326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira/**
26326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * Convenience class to efficiently make multiple short date strings. Instantiating and reusing
27326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira * one of these builders is faster than repeatedly bringing up all the locale stuff.
28326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira *
29326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira */
30326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereirapublic class FormattedDateBuilder {
31326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
32b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    private final StringBuilder sb;
33b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    private final Formatter dateFormatter;
34b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    private final Context mContext;
35326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
36326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    public FormattedDateBuilder(Context context) {
37326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        mContext = context;
38326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        sb = new StringBuilder();
39326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira        dateFormatter = new Formatter(sb);
40326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
41326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
42b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    /**
43b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * This is used in the conversation list, and headers of collapsed messages in
44b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * threaded conversations.
45b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Times on today's date will just display time, e.g. 8:15 AM
46b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Times not today, but within the same calendar year will display absolute date, e.g. Nov 6
47b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Times not in the same year display a numeric absolute date, e.g. 11/18/12
48b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     *
49b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * @param when The time to generate a formatted date for
50b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * @return The formatted date
51b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     */
52b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    public CharSequence formatShortDateTime(long when) {
53b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        if (DateUtils.isToday(when)) {
54b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon            return formatDateTime(when, DateUtils.FORMAT_SHOW_TIME);
55b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        } else if (isCurrentYear(when)) {
56b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon            return formatDateTime(when, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH);
57b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        } else {
58b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon            return formatDateTime(when, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE);
59b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        }
60326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
61326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
62b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    /**
63b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * This is used in regular message headers.
64b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Times on today's date will just display time, e.g. 8:15 AM
65b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Times not today, but within two weeks ago will display relative date and time,
66b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * e.g. 6 days ago, 8:15 AM
67b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Times more than two weeks ago but within the same calendar year will display
68b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * absolute date and time, e.g. Nov 6, 8:15 AM
69b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Times not in the same year display a numeric absolute date, e.g. 11/18/12
70b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     *
71b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * @param when The time to generate a formatted date for
72b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * @return The formatted date
73b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     */
7435c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein    public CharSequence formatLongDateTime(long when) {
7535c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein        if (DateUtils.isToday(when)) {
76b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon            return formatDateTime(when, DateUtils.FORMAT_SHOW_TIME);
77b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        } else if (isCurrentYear(when)) {
78f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein            return getRelativeDateTimeString(mContext, when, DateUtils.DAY_IN_MILLIS,
79b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon                    2 * DateUtils.WEEK_IN_MILLIS,
80de40dd3a292531c7dd8a5741eb0980f666a88b84Andrew Sapperstein                    DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH);
8135c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein        } else {
82b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon            return formatDateTime(when, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE);
8335c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein        }
8435c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein    }
8535c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein
86b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    /**
87b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * This is used in expanded details headers.
88b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Displays full date and time e.g. Tue, Nov 18, 2012, 8:15 AM, or
89b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Yesterday, Nov 18, 2012, 8:15 AM
90b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     *
91b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * @param when The time to generate a formatted date for
92b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * @return The formatted date
93b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     */
94b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    public CharSequence formatFullDateTime(long when) {
9535c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein        sb.setLength(0);
96de40dd3a292531c7dd8a5741eb0980f666a88b84Andrew Sapperstein        DateUtils.formatDateRange(mContext, dateFormatter, when, when,
97de40dd3a292531c7dd8a5741eb0980f666a88b84Andrew Sapperstein                DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
98de40dd3a292531c7dd8a5741eb0980f666a88b84Andrew Sapperstein                DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_ALL);
99de40dd3a292531c7dd8a5741eb0980f666a88b84Andrew Sapperstein        return sb.toString();
100326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira    }
101326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira
10235c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein    /**
103b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * This is used for displaying dates when printing.
104b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * Displays the full date, e.g. Tue, Nov 18, 2012 at 8:15 PM
105b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     *
106b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * @param when The time to generate a formatted date for
107b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon     * @return The formatted date
10835c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein     */
109b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    public String formatDateTimeForPrinting(long when) {
110b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        return mContext.getString(R.string.date_message_received_print,
111b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon                formatDateTime(when, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY |
112b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon                        DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_ALL),
113b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon                        formatDateTime(when, DateUtils.FORMAT_SHOW_TIME));
114b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    }
115b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon
116b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    private boolean isCurrentYear(long when) {
117b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        final Calendar nowCal = Calendar.getInstance();
118b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        final Calendar whenCal = Calendar.getInstance();
119b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        whenCal.setTimeInMillis(when);
120b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        return (nowCal.get(Calendar.YEAR) == whenCal.get(Calendar.YEAR));
121b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    }
12235c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein
123b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon    private CharSequence formatDateTime(long when, int flags) {
124b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        sb.setLength(0);
125b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        DateUtils.formatDateRange(mContext, dateFormatter, when, when, flags);
126b7eac4739e2180b68d5d428438393581a3b5cfa7Martin Hibdon        return sb.toString();
12735c3bb793ac3ea8f63ff73d282c0dabfee5a49ceAndrew Sapperstein    }
128f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein
129f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein    /**
130f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein     * A port of
131f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein     * {@link DateUtils#getRelativeDateTimeString(android.content.Context, long, long, long, int)}
132f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein     * that does not include the time in strings like "2 days ago".
133f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein     */
134f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein    private static CharSequence getRelativeDateTimeString(Context c, long time, long minResolution,
135f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein            long transitionResolution, int flags) {
136f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        final long now = System.currentTimeMillis();
137f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        final long duration = Math.abs(now - time);
138f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein
139f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        // getRelativeTimeSpanString() doesn't correctly format relative dates
140f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        // above a week or exact dates below a day, so clamp
141f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        // transitionResolution as needed.
142f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        if (transitionResolution > DateUtils.WEEK_IN_MILLIS) {
143f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein            transitionResolution = DateUtils.WEEK_IN_MILLIS;
144f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        } else if (transitionResolution < DateUtils.DAY_IN_MILLIS) {
145f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein            transitionResolution = DateUtils.DAY_IN_MILLIS;
146f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        }
147f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein
148f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        if (duration < transitionResolution) {
149f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein            return DateUtils.getRelativeTimeSpanString(time, now, minResolution, flags);
150f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        } else {
151f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein            return DateUtils.getRelativeTimeSpanString(c, time, false);
152f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein        }
153f290ba322a5d6d60496650ed37d65c3af758d354Andrew Sapperstein    }
154326c660df0a0a36a52ae74d8bafaa22d6f253561Mindy Pereira}
155