1083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta/*
2083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * Copyright (C) 2013 The Android Open Source Project
3083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta *
4083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
5083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * you may not use this file except in compliance with the License.
6083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * You may obtain a copy of the License at
7083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta *
8083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
9083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta *
10083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * Unless required by applicable law or agreed to in writing, software
11083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
12083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * See the License for the specific language governing permissions and
14083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * limitations under the License.
15083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta */
16083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta
17083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Guptapackage android.text.format;
18083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta
19083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Guptaimport java.util.Calendar;
20eb3c5459eb893061779b102236a81069efdbef73Deepanshu Guptaimport java.util.TimeZone;
21083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Guptaimport java.util.UnknownFormatConversionException;
22083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Guptaimport java.util.regex.Pattern;
23083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta
24083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Guptaimport com.android.ide.common.rendering.api.LayoutLog;
25083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Guptaimport com.android.layoutlib.bridge.Bridge;
26083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Guptaimport com.android.tools.layoutlib.annotations.LayoutlibDelegate;
27083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta
28083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta/**
29083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * Delegate used to provide new implementation for native methods of {@link Time}
30083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta *
31083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * Through the layoutlib_create tool, some native methods of Time have been replaced by calls to
32083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta * methods of the same name in this delegate class.
33083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta */
34083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Guptapublic class Time_Delegate {
35083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta
36083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta    // Regex to match odd number of '%'.
37083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta    private static final Pattern p = Pattern.compile("(?<!%)(%%)*%(?!%)");
38083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta
39eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    // Format used by toString()
40eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    private static final String FORMAT = "%1$tY%1$tm%1$tdT%1$tH%1$tM%1$tS<%1$tZ>";
41eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
42eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
43eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static long normalize(Time thisTime, boolean ignoreDst) {
44eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        long millis = toMillis(thisTime, ignoreDst);
45eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        set(thisTime, millis);
46eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        return millis;
47eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
48eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
49eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
50eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static void switchTimezone(Time thisTime, String timezone) {
51eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        Calendar c = timeToCalendar(thisTime);
52eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        c.setTimeZone(TimeZone.getTimeZone(timezone));
53eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        calendarToTime(c, thisTime);
54eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
55eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
56eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
57eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static int nativeCompare(Time a, Time b) {
58eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta      return timeToCalendar(a).compareTo(timeToCalendar(b));
59eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
60eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
61083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta    @LayoutlibDelegate
62083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta    /*package*/ static String format1(Time thisTime, String format) {
63083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta
64083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta        try {
65083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta            // Change the format by adding changing '%' to "%1$t". This is required to tell the
66083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta            // formatter which argument to use from the argument list. '%%' is left as is. In the
67083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta            // replacement string, $0 refers to matched pattern. \\1 means '1', written this way to
68083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta            // separate it from 0. \\$ means '$', written this way to suppress the special meaning
69083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta            // of $.
70083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta            return String.format(
71083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta                    p.matcher(format).replaceAll("$0\\1\\$t"),
72eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta                    timeToCalendar(thisTime));
73083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta        } catch (UnknownFormatConversionException e) {
74083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta            Bridge.getLog().fidelityWarning(LayoutLog.TAG_STRFTIME, "Unrecognized format", e, format);
75083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta            return format;
76083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta        }
77083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta    }
78083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta
79eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /**
80eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta     * Return the current time in YYYYMMDDTHHMMSS<tz> format
81eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta     */
82eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
83eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static String toString(Time thisTime) {
84eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        Calendar c = timeToCalendar(thisTime);
85eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        return String.format(FORMAT, c);
86eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
87eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
88eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
89eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static boolean nativeParse(Time thisTime, String s) {
90eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
91eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta                "android.text.format.Time.parse() not supported.", null);
92eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        return false;
93eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
94eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
95eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
96eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static boolean nativeParse3339(Time thisTime, String s) {
97eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
98eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta                "android.text.format.Time.parse3339() not supported.", null);
99eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        return false;
100eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
101eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
102eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
103eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static void setToNow(Time thisTime) {
104eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        calendarToTime(getCalendarInstance(thisTime), thisTime);
105eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
106eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
107eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
108eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static long toMillis(Time thisTime, boolean ignoreDst) {
109eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        // TODO: Respect ignoreDst.
110eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        return timeToCalendar(thisTime).getTimeInMillis();
111eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
112eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
113eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
114eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static void set(Time thisTime, long millis) {
115eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        Calendar c = getCalendarInstance(thisTime);
116eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        c.setTimeInMillis(millis);
117eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        calendarToTime(c,thisTime);
118eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
119eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
120eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    @LayoutlibDelegate
121eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /*package*/ static String format2445(Time thisTime) {
122eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
123eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta                "android.text.format.Time.format2445() not supported.", null);
124eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        return "";
125eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
126eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
127eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    // ---- private helper methods ----
128eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
129eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    private static Calendar timeToCalendar(Time time) {
130eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        Calendar calendar = getCalendarInstance(time);
131083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta        calendar.set(time.year, time.month, time.monthDay, time.hour, time.minute, time.second);
132083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta        return calendar;
133083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta    }
134083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta
135eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    private static void calendarToTime(Calendar c, Time time) {
136eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        time.timezone = c.getTimeZone().getID();
137eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        time.set(c.get(Calendar.SECOND), c.get(Calendar.MINUTE), c.get(Calendar.HOUR_OF_DAY),
138eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta                c.get(Calendar.DATE), c.get(Calendar.MONTH), c.get(Calendar.YEAR));
139eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        time.weekDay = c.get(Calendar.DAY_OF_WEEK);
140eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        time.yearDay = c.get(Calendar.DAY_OF_YEAR);
141eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        time.isDst = c.getTimeZone().inDaylightTime(c.getTime()) ? 1 : 0;
142eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        // gmtoff is in seconds and TimeZone.getOffset() returns milliseconds.
143eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        time.gmtoff = c.getTimeZone().getOffset(c.getTimeInMillis()) / DateUtils.SECOND_IN_MILLIS;
144eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
145eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta
146eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    /**
147eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta     * Return a calendar instance with the correct timezone.
148eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta     *
149eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta     * @param time Time to obtain the timezone from.
150eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta     */
151eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    private static Calendar getCalendarInstance(Time time) {
152eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        // TODO: Check platform code to make sure the behavior is same for null/invalid timezone.
153eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        if (time == null || time.timezone == null) {
154eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta            // Default to local timezone.
155eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta            return Calendar.getInstance();
156eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        }
157eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        // If timezone is invalid, use GMT.
158eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta        return Calendar.getInstance(TimeZone.getTimeZone(time.timezone));
159eb3c5459eb893061779b102236a81069efdbef73Deepanshu Gupta    }
160083e3caf66c21f7cc9511db479726c38d90e2d2fDeepanshu Gupta}
161