1 /*
2  * Copyright (C) 2012 Google Inc.
3  * Licensed to The Android Open Source Project.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package com.android.mail;
18 
19 import android.content.Context;
20 import android.text.format.DateUtils;
21 
22 import java.util.Calendar;
23 import java.util.Formatter;
24 
25 /**
26  * Convenience class to efficiently make multiple short date strings. Instantiating and reusing
27  * one of these builders is faster than repeatedly bringing up all the locale stuff.
28  *
29  */
30 public class FormattedDateBuilder {
31 
32     private final StringBuilder sb;
33     private final Formatter dateFormatter;
34     private final Context mContext;
35 
FormattedDateBuilder(Context context)36     public FormattedDateBuilder(Context context) {
37         mContext = context;
38         sb = new StringBuilder();
39         dateFormatter = new Formatter(sb);
40     }
41 
42     /**
43      * This is used in the conversation list, and headers of collapsed messages in
44      * threaded conversations.
45      * Times on today's date will just display time, e.g. 8:15 AM
46      * Times not today, but within the same calendar year will display absolute date, e.g. Nov 6
47      * Times not in the same year display a numeric absolute date, e.g. 11/18/12
48      *
49      * @param when The time to generate a formatted date for
50      * @return The formatted date
51      */
formatShortDateTime(long when)52     public CharSequence formatShortDateTime(long when) {
53         if (DateUtils.isToday(when)) {
54             return formatDateTime(when, DateUtils.FORMAT_SHOW_TIME);
55         } else if (isCurrentYear(when)) {
56             return formatDateTime(when, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH);
57         } else {
58             return formatDateTime(when, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE);
59         }
60     }
61 
62     /**
63      * This is used in regular message headers.
64      * Times on today's date will just display time, e.g. 8:15 AM
65      * Times not today, but within two weeks ago will display relative date and time,
66      * e.g. 6 days ago, 8:15 AM
67      * Times more than two weeks ago but within the same calendar year will display
68      * absolute date and time, e.g. Nov 6, 8:15 AM
69      * Times not in the same year display a numeric absolute date, e.g. 11/18/12
70      *
71      * @param when The time to generate a formatted date for
72      * @return The formatted date
73      */
formatLongDateTime(long when)74     public CharSequence formatLongDateTime(long when) {
75         if (DateUtils.isToday(when)) {
76             return formatDateTime(when, DateUtils.FORMAT_SHOW_TIME);
77         } else if (isCurrentYear(when)) {
78             return getRelativeDateTimeString(mContext, when, DateUtils.DAY_IN_MILLIS,
79                     2 * DateUtils.WEEK_IN_MILLIS,
80                     DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH);
81         } else {
82             return formatDateTime(when, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE);
83         }
84     }
85 
86     /**
87      * This is used in expanded details headers.
88      * Displays full date and time e.g. Tue, Nov 18, 2012, 8:15 AM, or
89      * Yesterday, Nov 18, 2012, 8:15 AM
90      *
91      * @param when The time to generate a formatted date for
92      * @return The formatted date
93      */
formatFullDateTime(long when)94     public CharSequence formatFullDateTime(long when) {
95         sb.setLength(0);
96         DateUtils.formatDateRange(mContext, dateFormatter, when, when,
97                 DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
98                 DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_ALL);
99         return sb.toString();
100     }
101 
102     /**
103      * This is used for displaying dates when printing.
104      * Displays the full date, e.g. Tue, Nov 18, 2012 at 8:15 PM
105      *
106      * @param when The time to generate a formatted date for
107      * @return The formatted date
108      */
formatDateTimeForPrinting(long when)109     public String formatDateTimeForPrinting(long when) {
110         return mContext.getString(R.string.date_message_received_print,
111                 formatDateTime(when, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY |
112                         DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_ALL),
113                         formatDateTime(when, DateUtils.FORMAT_SHOW_TIME));
114     }
115 
isCurrentYear(long when)116     private boolean isCurrentYear(long when) {
117         final Calendar nowCal = Calendar.getInstance();
118         final Calendar whenCal = Calendar.getInstance();
119         whenCal.setTimeInMillis(when);
120         return (nowCal.get(Calendar.YEAR) == whenCal.get(Calendar.YEAR));
121     }
122 
formatDateTime(long when, int flags)123     private CharSequence formatDateTime(long when, int flags) {
124         sb.setLength(0);
125         DateUtils.formatDateRange(mContext, dateFormatter, when, when, flags);
126         return sb.toString();
127     }
128 
129     /**
130      * A port of
131      * {@link DateUtils#getRelativeDateTimeString(android.content.Context, long, long, long, int)}
132      * that does not include the time in strings like "2 days ago".
133      */
getRelativeDateTimeString(Context c, long time, long minResolution, long transitionResolution, int flags)134     private static CharSequence getRelativeDateTimeString(Context c, long time, long minResolution,
135             long transitionResolution, int flags) {
136         final long now = System.currentTimeMillis();
137         final long duration = Math.abs(now - time);
138 
139         // getRelativeTimeSpanString() doesn't correctly format relative dates
140         // above a week or exact dates below a day, so clamp
141         // transitionResolution as needed.
142         if (transitionResolution > DateUtils.WEEK_IN_MILLIS) {
143             transitionResolution = DateUtils.WEEK_IN_MILLIS;
144         } else if (transitionResolution < DateUtils.DAY_IN_MILLIS) {
145             transitionResolution = DateUtils.DAY_IN_MILLIS;
146         }
147 
148         if (duration < transitionResolution) {
149             return DateUtils.getRelativeTimeSpanString(time, now, minResolution, flags);
150         } else {
151             return DateUtils.getRelativeTimeSpanString(c, time, false);
152         }
153     }
154 }
155