├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── README.md
├── androiddatetimetextprovider
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── sergiandreplace
│ │ └── androiddatetimetextprovider
│ │ ├── DayOfWeekFormattingTest.java
│ │ └── MonthFormattingTest.java
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── sergiandreplace
│ └── androiddatetimetextprovider
│ └── AndroidDateTimeTextProvider.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 | - name: set up JDK 1.8
13 | uses: actions/setup-java@v1
14 | with:
15 | java-version: 1.8
16 | - name: Build with Gradle
17 | run: ./gradlew build
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea/
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidDateTimeTextProvider
2 |
3 | A solution for day and month formatting for languages with standalone configuration for ThreeTenBP for Android
4 |
5 | ## tl;dr
6 |
7 | Do you need to format dates to locales with specific standalone configurations for Month or DayOfWeek like catalan, polish, finnish, or others? This is the solution.
8 |
9 | ## What is this?
10 |
11 | I've been using [ThreeTenBp](https://github.com/ThreeTen/threetenbp) for Android projects for a long time. It's a great project that allows me to use Java 8 date/time framework without any problem.
12 |
13 | But, recently I found an issue in an application I had to translate into catalan. Basically:
14 |
15 | - In catalan, the preposition used in front of month for dates changes depending on the month (8 *de* febrer vs. 8 *d'* abril)
16 | - In order to fix this, the `MMMM` pattern includes the preposition (so, `d MMMM` will generate `8 de febrer` or `8 d'abril` as the `de` and `d'` are part of the month name.
17 | - What happens when you need to generate just the name of the month without preposition? That's when `LLLL` comes to the rescue.
18 | - Theorically, Java supports `LLLL` in date formatting. But not exactly. For languages like catalan `LLLL` and `MMMM` generate exactly the same output.
19 | - The worst part, is that Android does it right, and ye olde `SimpleDateFormat` returns diferent month names for `LLLL` and `MMMM`. Also, `LLLL` vs `MMMM` is well documented in the `SimpleDateFormat` javadoc (even using catalan as example) but no mention about it in `DateTimeFormatter`
20 |
21 | Then I started to investigate, I've spent hours looking at repos, documentation and standards, but basically:
22 |
23 | - As android is using the right standards, the information is there. But, as ThreeTenBP mimics Java behaviour, Java does not expect this info to be there.
24 | - I've claimed about it in this issue (https://github.com/ThreeTen/threetenbp/issues/55) where other users were having the same problems in other languages.
25 | - @sschaap linked to his own answer in other issue with a coherent explanation of what's going on (thanks a lot!)
26 | - Then I saw a link to this issue in another library: https://github.com/gabrielittner/lazythreetenbp/pull/11
27 | - Here @pamalyshev offers a solution in a PR for Android. But the answer of the library author is quite right, do not force it, as is not following the idea of ThreeTenBp (mimic Java 8), but offer as a separate library/solution.
28 | - And then the discussion is dead. @pamalyshev cloned the repo with the solution, but it's never published as a library (or I didn't see it).
29 | - Then I decided to publish this solution as a standalone solution.
30 |
31 | Keep in mind I'm simplifying the investigation process, as it's quite long and I'm sure not many of you look at huge xml of language configurations with the same excitement than me.
32 |
33 | Basically, this solution is not created by me, it's the collected effort of different individuals and put it together for your convenience.
34 |
35 | ## How to use it?
36 |
37 | You need to include three libraries in your app.
38 |
39 | - ThreeTenBP (https://github.com/ThreeTen/threetenbp). The original backport of the JSR-310.
40 | - Lazythreetenbp (https://github.com/gabrielittner/lazythreetenbp). A lazy loading ZoneRuleProvider for ThreeTenBp. This is used in Android as the zones information should be loaded before using ThreeTenBp and it could be a noticeable long time for an Android app startup. This library implements a lazy loading mechanism for that information.
41 | - This cute small library to fix the issue.
42 |
43 | So, add to your gradle:
44 |
45 | 1. The jitpack.io as repository. This is a small library and it's the fastest way of publishing it.
46 |
47 | ```
48 | allprojects {
49 | repositories {
50 | ...
51 | maven { url 'https://jitpack.io' }
52 | }
53 | }
54 | ```
55 |
56 | 2. All the dependencies needed
57 |
58 | ```
59 | implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.7.0"
60 | implementation "org.threeten:threetenbp:1.4.0:no-tzdb"
61 | implementation 'com.github.sergiandreplace:androiddatetimetextprovider:1.0.0'
62 | ```
63 |
64 | 3. And now you can use both LazyThreeTenBp and AndroidDateTimeTextProvider in your init part (tipically in you Application class):
65 | ```
66 | LazyThreeTen.init(context);
67 | DateTimeTextProvider.setInitializer(AndroidDateTimeTextProvider());
68 | ```
69 |
70 | And that's it!
71 |
72 | ## Testing
73 |
74 | If you want to test that it works, there is a couple of test files that will tests the formatting for Months and Days of week for Catalan, Finnish, Polish and Russian.
75 |
76 | If there is another language that could benefit from this solution and I'm not aware, feel free to open an isse to check it, or if you feel extra proactive, open a PR adding it to the tests.
77 |
78 |
79 | ## The real heroes
80 |
81 | As I said, I've just joined other people work, so this people is the one to thank for tihs solution:
82 |
83 | - @jodastephen and the rest of people who worked in the ThreeTenBP project. A real life-saver!
84 | - @gabrielittner for creating LazyThreeTenBp
85 | - @sschaap for his great explanations and patience answering
86 | - @pamalyshev for creating the solution and doing the changes needed to ThreeTenBp to apply it.
87 |
88 | ## Disclaimer
89 |
90 | All the information I wrote here is my understanding of the problem the solution and how the involved people has participated. If there is something wrong or misleading do not hesitate to say so and I will apply the right corrections.
91 |
92 | Have fun!
93 |
--------------------------------------------------------------------------------
/androiddatetimetextprovider/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/androiddatetimetextprovider/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | group='com.github.sergiandreplace'
4 |
5 | android {
6 | compileSdkVersion 29
7 |
8 | defaultConfig {
9 | minSdkVersion 16
10 | targetSdkVersion 29
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | consumerProguardFiles 'consumer-rules.pro'
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | api 'com.gabrielittner.threetenbp:lazythreetenbp:0.7.0'
28 | api 'org.threeten:threetenbp:1.4.0:no-tzdb'
29 | testImplementation 'junit:junit:4.12'
30 | androidTestImplementation 'com.gabrielittner.threetenbp:lazythreetenbp:0.7.0'
31 | androidTestImplementation 'org.threeten:threetenbp:1.4.0:no-tzdb'
32 | androidTestImplementation 'androidx.test:runner:1.2.0'
33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
34 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
35 | }
36 |
--------------------------------------------------------------------------------
/androiddatetimetextprovider/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiandreplace/androiddatetimetextprovider/d8cf719a62597f7cc28076707daf752ee905b292/androiddatetimetextprovider/consumer-rules.pro
--------------------------------------------------------------------------------
/androiddatetimetextprovider/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/androiddatetimetextprovider/src/androidTest/java/com/sergiandreplace/androiddatetimetextprovider/DayOfWeekFormattingTest.java:
--------------------------------------------------------------------------------
1 | package com.sergiandreplace.androiddatetimetextprovider;
2 |
3 | import android.os.Build;
4 | import android.util.Log;
5 | import androidx.test.ext.junit.runners.AndroidJUnit4;
6 | import androidx.test.platform.app.InstrumentationRegistry;
7 | import com.gabrielittner.threetenbp.LazyThreeTen;
8 | import java.text.SimpleDateFormat;
9 | import java.util.Calendar;
10 | import java.util.GregorianCalendar;
11 | import java.util.Locale;
12 | import org.junit.BeforeClass;
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 | import org.threeten.bp.DayOfWeek;
16 | import org.threeten.bp.LocalDate;
17 | import org.threeten.bp.format.DateTimeFormatter;
18 | import org.threeten.bp.format.DateTimeTextProvider;
19 | import org.threeten.bp.temporal.TemporalAdjusters;
20 |
21 | import static org.junit.Assert.assertEquals;
22 | import static org.junit.Assume.assumeTrue;
23 |
24 | @RunWith(AndroidJUnit4.class)
25 | public class DayOfWeekFormattingTest {
26 |
27 | private static Object[][] data;
28 |
29 | @BeforeClass
30 | public static void init() {
31 | LazyThreeTen.init(InstrumentationRegistry.getInstrumentation().getTargetContext());
32 | DateTimeTextProvider.setInitializer(new AndroidDateTimeTextProvider());
33 |
34 | //Not using Parameterized tests because they are slower,
35 | //and if for one month test fails then usually others fail as well.
36 | data = new Object[][] {
37 | { Calendar.SUNDAY, DayOfWeek.SUNDAY },
38 | { Calendar.MONDAY, DayOfWeek.MONDAY },
39 | { Calendar.TUESDAY, DayOfWeek.TUESDAY },
40 | { Calendar.WEDNESDAY, DayOfWeek.WEDNESDAY },
41 | { Calendar.THURSDAY, DayOfWeek.THURSDAY },
42 | { Calendar.FRIDAY, DayOfWeek.FRIDAY },
43 | { Calendar.SATURDAY, DayOfWeek.SATURDAY },
44 | };
45 | }
46 |
47 | private GregorianCalendar getCalendar(int calendarDay) {
48 | GregorianCalendar calendar = new GregorianCalendar(1970, 0, 1);
49 | //Force recalculation.
50 | calendar.getTime();
51 | calendar.set(Calendar.DAY_OF_WEEK, calendarDay);
52 | return calendar;
53 | }
54 |
55 | private LocalDate getLocalDate(DayOfWeek dayOfWeek) {
56 | return LocalDate.of(1970, 1, 1).with(TemporalAdjusters.nextOrSame(dayOfWeek));
57 | }
58 |
59 | private void testPattern(String pattern, Locale locale) {
60 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern, locale);
61 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern, locale);
62 |
63 | Log.d("Month Formatting Test", "Locale: " + locale.toString());
64 |
65 | for (Object[] entry : data) {
66 | int calendarDay = (Integer) entry[0];
67 | DayOfWeek dayOfWeek = (DayOfWeek) entry[1];
68 |
69 | String javaText = simpleDateFormat.format(getCalendar(calendarDay).getTime());
70 | String threeTenText = getLocalDate(dayOfWeek).format(formatter);
71 |
72 | Log.d("Month Formatting Test", " Pattern: " + pattern);
73 | Log.d("Month Formatting Test", " DayOfWeek: " + dayOfWeek);
74 | Log.d("Month Formatting Test", " Java: " + javaText);
75 | Log.d("Month Formatting Test", " ThreeTenBp: " + threeTenText);
76 |
77 | assertEquals(javaText, threeTenText);
78 | }
79 | }
80 |
81 | private void assumeNarrowStyleSupported() {
82 | assumeTrue(Build.VERSION.SDK_INT >= 18);
83 | }
84 |
85 | public void testFull(Locale locale) {
86 | testPattern("EEEE", locale);
87 | }
88 |
89 | public void testShort(Locale locale) {
90 | testPattern("EEE", locale);
91 | }
92 |
93 | public void testNarrow(Locale locale) {
94 | assumeNarrowStyleSupported();
95 | testPattern("EEEEE", locale);
96 | }
97 |
98 | public void testFullStandalone(Locale locale) {
99 | testPattern("cccc", locale);
100 | }
101 |
102 | public void testShortStandalone(Locale locale) {
103 | testPattern("ccc", locale);
104 | }
105 |
106 | public void testNarrowStandalone(Locale locale) {
107 | assumeNarrowStyleSupported();
108 | testPattern("ccccc", locale);
109 | }
110 |
111 | public void testLocale(Locale locale) {
112 | testFull(locale);
113 | testShort(locale);
114 | testNarrow(locale);
115 | testFullStandalone(locale);
116 | testShortStandalone(locale);
117 | testNarrowStandalone(locale);
118 | }
119 |
120 | @Test
121 | public void testLocalesWithStandaloneDaysOfWeek() {
122 | testLocale(new Locale("ru", "RU"));
123 | testLocale(new Locale("pl", "PL"));
124 | testLocale(new Locale("fi", "FI"));
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/androiddatetimetextprovider/src/androidTest/java/com/sergiandreplace/androiddatetimetextprovider/MonthFormattingTest.java:
--------------------------------------------------------------------------------
1 | package com.sergiandreplace.androiddatetimetextprovider;
2 |
3 | import android.os.Build;
4 | import android.util.Log;
5 | import androidx.test.ext.junit.runners.AndroidJUnit4;
6 | import androidx.test.platform.app.InstrumentationRegistry;
7 | import com.gabrielittner.threetenbp.LazyThreeTen;
8 | import java.text.SimpleDateFormat;
9 | import java.util.Calendar;
10 | import java.util.GregorianCalendar;
11 | import java.util.Locale;
12 | import org.junit.BeforeClass;
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 | import org.threeten.bp.LocalDate;
16 | import org.threeten.bp.Month;
17 | import org.threeten.bp.format.DateTimeFormatter;
18 | import org.threeten.bp.format.DateTimeTextProvider;
19 |
20 | import static org.junit.Assert.assertEquals;
21 | import static org.junit.Assume.assumeTrue;
22 |
23 | @RunWith(AndroidJUnit4.class)
24 | public class MonthFormattingTest {
25 |
26 | private static Object[][] data;
27 |
28 | @BeforeClass
29 | public static void init() {
30 | LazyThreeTen.init(InstrumentationRegistry.getInstrumentation().getTargetContext());
31 | DateTimeTextProvider.setInitializer(new AndroidDateTimeTextProvider());
32 |
33 | //Not using Parameterized tests because they are slower,
34 | //and if for one month test fails then usually others fail as well.
35 | data = new Object[][] {
36 | { Calendar.JANUARY, Month.JANUARY },
37 | { Calendar.FEBRUARY, Month.FEBRUARY },
38 | { Calendar.MARCH, Month.MARCH },
39 | { Calendar.APRIL, Month.APRIL },
40 | { Calendar.MAY, Month.MAY },
41 | { Calendar.JUNE, Month.JUNE },
42 | { Calendar.JULY, Month.JULY },
43 | { Calendar.AUGUST, Month.AUGUST },
44 | { Calendar.SEPTEMBER, Month.SEPTEMBER },
45 | { Calendar.OCTOBER, Month.OCTOBER },
46 | { Calendar.NOVEMBER, Month.NOVEMBER },
47 | { Calendar.DECEMBER, Month.DECEMBER }
48 | };
49 | }
50 |
51 | private GregorianCalendar getCalendar(int calendarMonth) {
52 | GregorianCalendar calendar = new GregorianCalendar(1970, 0, 1);
53 | calendar.set(Calendar.MONTH, calendarMonth);
54 | return calendar;
55 | }
56 |
57 | private LocalDate getLocalDate(Month month) {
58 | return LocalDate.of(1970, month, 1);
59 | }
60 |
61 | private void testPattern(String pattern, Locale locale) {
62 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern, locale);
63 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern, locale);
64 |
65 | Log.d("Month Formatting Test", "Locale: " + locale.toString());
66 |
67 | for (Object[] entry : data) {
68 | int calendarMonth = (Integer) entry[0];
69 | Month month = (Month) entry[1];
70 |
71 | String javaText = simpleDateFormat.format(getCalendar(calendarMonth).getTime());
72 | String threeTenText = getLocalDate(month).format(formatter);
73 |
74 | Log.d("Month Formatting Test", " Pattern: " + pattern);
75 | Log.d("Month Formatting Test", " Month: " + month);
76 | Log.d("Month Formatting Test", " Java: " + javaText);
77 | Log.d("Month Formatting Test", " ThreeTenBp: " + threeTenText);
78 |
79 | assertEquals(javaText, threeTenText);
80 | }
81 | }
82 |
83 | private void assumeNarrowStyleSupported() {
84 | assumeTrue(Build.VERSION.SDK_INT >= 18);
85 | }
86 |
87 | public void testFull(Locale locale) {
88 | testPattern("MMMM", locale);
89 | }
90 |
91 | public void testShort(Locale locale) {
92 | testPattern("MMM", locale);
93 | }
94 |
95 | public void testNarrow(Locale locale) {
96 | assumeNarrowStyleSupported();
97 | testPattern("MMMMM", locale);
98 | }
99 |
100 | public void testFullStandalone(Locale locale) {
101 | testPattern("LLLL", locale);
102 | }
103 |
104 | public void testShortStandalone(Locale locale) {
105 | testPattern("LLL", locale);
106 | }
107 |
108 | public void testNarrowStandalone(Locale locale) {
109 | assumeNarrowStyleSupported();
110 | testPattern("LLLLL", locale);
111 | }
112 |
113 | public void testLocale(Locale locale) {
114 | testFull(locale);
115 | testShort(locale);
116 | testNarrow(locale);
117 | testFullStandalone(locale);
118 | testShortStandalone(locale);
119 | testNarrowStandalone(locale);
120 | }
121 |
122 | @Test
123 | public void testLocalesWithStandaloneMonths() {
124 | testLocale(new Locale("ru", "RU"));
125 | testLocale(new Locale("ca", "ES"));
126 | testLocale(new Locale("pl", "PL"));
127 | testLocale(new Locale("fi", "FI"));
128 | }
129 | }
--------------------------------------------------------------------------------
/androiddatetimetextprovider/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/androiddatetimetextprovider/src/main/java/com/sergiandreplace/androiddatetimetextprovider/AndroidDateTimeTextProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
3 | *
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions are met:
8 | *
9 | * * Redistributions of source code must retain the above copyright notice,
10 | * this list of conditions and the following disclaimer.
11 | *
12 | * * Redistributions in binary form must reproduce the above copyright notice,
13 | * this list of conditions and the following disclaimer in the documentation
14 | * and/or other materials provided with the distribution.
15 | *
16 | * * Neither the name of JSR-310 nor the names of its contributors
17 | * may be used to endorse or promote products derived from this software
18 | * without specific prior written permission.
19 | *
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 | */
32 | package com.sergiandreplace.androiddatetimetextprovider;
33 |
34 | import android.os.Build;
35 | import java.text.DateFormatSymbols;
36 | import java.text.SimpleDateFormat;
37 | import java.util.AbstractMap.SimpleImmutableEntry;
38 | import java.util.ArrayList;
39 | import java.util.Calendar;
40 | import java.util.Collections;
41 | import java.util.Comparator;
42 | import java.util.GregorianCalendar;
43 | import java.util.HashMap;
44 | import java.util.Iterator;
45 | import java.util.List;
46 | import java.util.Locale;
47 | import java.util.Map;
48 | import java.util.Map.Entry;
49 | import java.util.concurrent.ConcurrentHashMap;
50 | import java.util.concurrent.ConcurrentMap;
51 | import org.threeten.bp.format.DateTimeTextProvider;
52 | import org.threeten.bp.format.TextStyle;
53 | import org.threeten.bp.temporal.IsoFields;
54 | import org.threeten.bp.temporal.TemporalField;
55 |
56 | import static org.threeten.bp.temporal.ChronoField.AMPM_OF_DAY;
57 | import static org.threeten.bp.temporal.ChronoField.DAY_OF_WEEK;
58 | import static org.threeten.bp.temporal.ChronoField.ERA;
59 | import static org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR;
60 |
61 | /**
62 | * The Service Provider Implementation to obtain date-time text for a field.
63 | *
64 | * This implementation extracts data from {@link DateFormatSymbols} and {@link SimpleDateFormat}.
65 | *
66 | * This implementation is based on {@link org.threeten.bp.format.SimpleDateTimeTextProvider SimpleDateTimeTextProvider},
67 | * but uses Android-specific features of {@link SimpleDateFormat}
68 | * to extract texts for STANDALONE and NARROW styles
69 | * for {@link org.threeten.bp.temporal.ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR}
70 | * and {@link org.threeten.bp.temporal.ChronoField#DAY_OF_WEEK DAY_OF_WEEK}.
71 | *
72 | * Texts for other fields are fetched the same way as in
73 | * {@link org.threeten.bp.format.SimpleDateTimeTextProvider SimpleDateTimeTextProvider}.
74 | *
75 | * Note that texts for NARROW style can be extracted only on devices with Android 4.3 or higher.
76 | * On pre-Android 4.3 devices NARROW style texts are emulated
77 | * by returning only first character of FULL style text.
78 | */
79 | final public class AndroidDateTimeTextProvider extends DateTimeTextProvider {
80 |
81 | /** Comparator. */
82 | private static final Comparator> COMPARATOR = new Comparator>() {
83 | @Override
84 | public int compare(Entry obj1, Entry obj2) {
85 | return obj2.getKey().length() - obj1.getKey().length(); // longest to shortest
86 | }
87 | };
88 |
89 | /** Cache. */
90 | private final ConcurrentMap, Object> cache =
91 | new ConcurrentHashMap, Object>(16, 0.75f, 2);
92 |
93 | //-----------------------------------------------------------------------
94 | @Override
95 | public String getText(TemporalField field, long value, TextStyle style, Locale locale) {
96 | Object store = findStore(field, locale);
97 | if (store instanceof LocaleStore) {
98 | return ((LocaleStore) store).getText(value, style);
99 | }
100 | return null;
101 | }
102 |
103 | @Override
104 | public Iterator> getTextIterator(TemporalField field, TextStyle style, Locale locale) {
105 | Object store = findStore(field, locale);
106 | if (store instanceof LocaleStore) {
107 | return ((LocaleStore) store).getTextIterator(style);
108 | }
109 | return null;
110 | }
111 |
112 | //-----------------------------------------------------------------------
113 | private Object findStore(TemporalField field, Locale locale) {
114 | Entry key = createEntry(field, locale);
115 | Object store = cache.get(key);
116 | if (store == null) {
117 | store = createStore(field, locale);
118 | cache.putIfAbsent(key, store);
119 | store = cache.get(key);
120 | }
121 | return store;
122 | }
123 |
124 | private Object createStore(TemporalField field, Locale locale) {
125 | if (field == MONTH_OF_YEAR) {
126 | DateFormatSymbols oldSymbols = DateFormatSymbols.getInstance(locale);
127 | Map> styleMap = new HashMap>();
128 | SimpleDateFormat dateFormat = new SimpleDateFormat("", locale);
129 |
130 | //Uses the same assumptions about months as SimpleDateTimeTextProvider.
131 |
132 | String[] array = oldSymbols.getMonths();
133 | Map map = createMonthsMapFromSymbolsArray(array);
134 | styleMap.put(TextStyle.FULL, map);
135 |
136 | array = oldSymbols.getShortMonths();
137 | map = createMonthsMapFromSymbolsArray(array);
138 | styleMap.put(TextStyle.SHORT, map);
139 |
140 | if (Build.VERSION.SDK_INT >= 18) {
141 | map = createMonthsMapFromPattern(dateFormat, "MMMMM");
142 | styleMap.put(TextStyle.NARROW, map);
143 | map = createMonthsMapFromPattern(dateFormat, "LLLLL");
144 | styleMap.put(TextStyle.NARROW_STANDALONE, map);
145 | } else {
146 | map = createNarrowMonthsMapFromPattern(dateFormat, "MMMM");
147 | styleMap.put(TextStyle.NARROW, map);
148 | map = createNarrowMonthsMapFromPattern(dateFormat, "LLLL");
149 | styleMap.put(TextStyle.NARROW_STANDALONE, map);
150 | }
151 |
152 | map = createMonthsMapFromPattern(dateFormat, "LLLL");
153 | styleMap.put(TextStyle.FULL_STANDALONE, map);
154 |
155 | map = createMonthsMapFromPattern(dateFormat, "LLL");
156 | styleMap.put(TextStyle.SHORT_STANDALONE, map);
157 |
158 | return createLocaleStore(styleMap);
159 | }
160 | if (field == DAY_OF_WEEK) {
161 | DateFormatSymbols oldSymbols = DateFormatSymbols.getInstance(locale);
162 | Map> styleMap = new HashMap>();
163 | SimpleDateFormat dateFormat = new SimpleDateFormat("", locale);
164 |
165 | String[] array = oldSymbols.getWeekdays();
166 | Map map = createDaysMapFromSymbolsArray(array);
167 | styleMap.put(TextStyle.FULL, map);
168 |
169 | array = oldSymbols.getShortWeekdays();
170 | map = createDaysMapFromSymbolsArray(array);
171 | styleMap.put(TextStyle.SHORT, map);
172 |
173 | if (Build.VERSION.SDK_INT >= 18) {
174 | map = createDaysMapFromPattern(dateFormat, "EEEEE");
175 | styleMap.put(TextStyle.NARROW, map);
176 | map = createDaysMapFromPattern(dateFormat, "ccccc");
177 | styleMap.put(TextStyle.NARROW_STANDALONE, map);
178 | } else {
179 | map = createNarrowDaysMapFromPattern(dateFormat, "EEEE");
180 | styleMap.put(TextStyle.NARROW, map);
181 | map = createNarrowDaysMapFromPattern(dateFormat, "cccc");
182 | styleMap.put(TextStyle.NARROW_STANDALONE, map);
183 | }
184 |
185 | map = createDaysMapFromPattern(dateFormat, "cccc");
186 | styleMap.put(TextStyle.FULL_STANDALONE, map);
187 |
188 | map = createDaysMapFromPattern(dateFormat, "ccc");
189 | styleMap.put(TextStyle.SHORT_STANDALONE, map);
190 |
191 | return createLocaleStore(styleMap);
192 | }
193 | if (field == AMPM_OF_DAY) {
194 | DateFormatSymbols oldSymbols = DateFormatSymbols.getInstance(locale);
195 | Map> styleMap = new HashMap>();
196 | String[] array = oldSymbols.getAmPmStrings();
197 | Map map = new HashMap();
198 | map.put(0L, array[Calendar.AM]);
199 | map.put(1L, array[Calendar.PM]);
200 | styleMap.put(TextStyle.FULL, map);
201 | styleMap.put(TextStyle.SHORT, map); // re-use, as we don't have different data
202 | return createLocaleStore(styleMap);
203 | }
204 | if (field == ERA) {
205 | DateFormatSymbols oldSymbols = DateFormatSymbols.getInstance(locale);
206 | Map> styleMap = new HashMap>();
207 | String[] array = oldSymbols.getEras();
208 | Map map = new HashMap();
209 | map.put(0L, array[GregorianCalendar.BC]);
210 | map.put(1L, array[GregorianCalendar.AD]);
211 | styleMap.put(TextStyle.SHORT, map);
212 | if (locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
213 | map = new HashMap();
214 | map.put(0L, "Before Christ");
215 | map.put(1L, "Anno Domini");
216 | styleMap.put(TextStyle.FULL, map);
217 | } else {
218 | // re-use, as we don't have different data
219 | styleMap.put(TextStyle.FULL, map);
220 | }
221 | map = new HashMap();
222 | map.put(0L, array[GregorianCalendar.BC].substring(0, 1));
223 | map.put(1L, array[GregorianCalendar.AD].substring(0, 1));
224 | styleMap.put(TextStyle.NARROW, map);
225 | return createLocaleStore(styleMap);
226 | }
227 | // hard code English quarter text
228 | if (field == IsoFields.QUARTER_OF_YEAR) {
229 | Map> styleMap = new HashMap>();
230 | Map map = new HashMap();
231 | map.put(1L, "Q1");
232 | map.put(2L, "Q2");
233 | map.put(3L, "Q3");
234 | map.put(4L, "Q4");
235 | styleMap.put(TextStyle.SHORT, map);
236 | map = new HashMap();
237 | map.put(1L, "1st quarter");
238 | map.put(2L, "2nd quarter");
239 | map.put(3L, "3rd quarter");
240 | map.put(4L, "4th quarter");
241 | styleMap.put(TextStyle.FULL, map);
242 | return createLocaleStore(styleMap);
243 | }
244 | return ""; // null marker for map
245 | }
246 |
247 | private Long calMonthToThreeTenMonth(int calMonth) {
248 | //Calendar months are from 0 (JANUARY) to 11 (DECEMBER)
249 | //ThreeTen months are from 1 (JANUARY) to 12 (DECEMBER)
250 |
251 | //Show boxing explicitly.
252 | //noinspection UnnecessaryBoxing
253 | return Long.valueOf(calMonth + 1);
254 | }
255 |
256 | private Long calDayToThreeTenDay(int calDay) {
257 | //Calendar days start from SUNDAY
258 | //ThreeTen days start from MONDAY
259 | //So 1 -> 7, 2 -> 1, ..., 7 -> 6
260 |
261 | //Show boxing explicitly.
262 | //noinspection UnnecessaryBoxing
263 | return Long.valueOf(((calDay + 5) % 7) + 1);
264 | }
265 |
266 | private Map createMonthsMapFromSymbolsArray(String[] array) {
267 | Map map = new HashMap();
268 | for (int calMonth = Calendar.JANUARY; calMonth <= Calendar.DECEMBER; ++calMonth) {
269 | Long threeTenMonth = calMonthToThreeTenMonth(calMonth);
270 | map.put(threeTenMonth, array[calMonth]);
271 | }
272 | return map;
273 | }
274 |
275 | private Map createMonthsMapFromPattern(
276 | SimpleDateFormat dateFormat, String pattern) {
277 | dateFormat.applyPattern(pattern);
278 |
279 | Map map = new HashMap();
280 | for (int calMonth = Calendar.JANUARY; calMonth <= Calendar.DECEMBER; ++calMonth) {
281 | Long threeTenMonth = calMonthToThreeTenMonth(calMonth);
282 | dateFormat.getCalendar().set(Calendar.MONTH, calMonth);
283 | String formattedMonth = dateFormat.format(dateFormat.getCalendar().getTime());
284 | map.put(threeTenMonth, formattedMonth);
285 | }
286 | return map;
287 | }
288 |
289 | private Map createNarrowMonthsMapFromPattern(
290 | SimpleDateFormat dateFormat, String pattern) {
291 | dateFormat.applyPattern(pattern);
292 |
293 | Map map = new HashMap();
294 | for (int calMonth = Calendar.JANUARY; calMonth <= Calendar.DECEMBER; ++calMonth) {
295 | Long threeTenMonth = calMonthToThreeTenMonth(calMonth);
296 | dateFormat.getCalendar().set(Calendar.MONTH, calMonth);
297 | String formattedMonth = dateFormat.format(dateFormat.getCalendar().getTime());
298 | map.put(threeTenMonth, formattedMonth.substring(0, 1));
299 | }
300 | return map;
301 | }
302 |
303 | private Map createDaysMapFromSymbolsArray(String[] array) {
304 | Map map = new HashMap();
305 | for (int calDay = Calendar.SUNDAY; calDay <= Calendar.SATURDAY; ++calDay) {
306 | Long threeTenDay = calDayToThreeTenDay(calDay);
307 | map.put(threeTenDay, array[calDay]);
308 | }
309 | return map;
310 | }
311 |
312 | private Map createDaysMapFromPattern(
313 | SimpleDateFormat dateFormat, String pattern) {
314 | dateFormat.applyPattern(pattern);
315 |
316 | Map map = new HashMap();
317 | for (int calDay = Calendar.SUNDAY; calDay <= Calendar.SATURDAY; ++calDay) {
318 | Long threeTenDay = calDayToThreeTenDay(calDay);
319 | dateFormat.getCalendar().set(Calendar.DAY_OF_WEEK, calDay);
320 | String formattedMonth = dateFormat.format(dateFormat.getCalendar().getTime());
321 | map.put(threeTenDay, formattedMonth);
322 | }
323 | return map;
324 | }
325 |
326 | private Map createNarrowDaysMapFromPattern(
327 | SimpleDateFormat dateFormat, String pattern) {
328 | dateFormat.applyPattern(pattern);
329 |
330 | Map map = new HashMap();
331 | for (int calDay = Calendar.SUNDAY; calDay <= Calendar.SATURDAY; ++calDay) {
332 | Long threeTenDay = calDayToThreeTenDay(calDay);
333 | dateFormat.getCalendar().set(Calendar.DAY_OF_WEEK, calDay);
334 | String formattedMonth = dateFormat.format(dateFormat.getCalendar().getTime());
335 | map.put(threeTenDay, formattedMonth.substring(0, 1));
336 | }
337 | return map;
338 | }
339 |
340 | //-----------------------------------------------------------------------
341 |
342 | /**
343 | * Helper method to create an immutable entry.
344 | *
345 | * @param text the text, not null
346 | * @param field the field, not null
347 | * @return the entry, not null
348 | */
349 | private static Entry createEntry(A text, B field) {
350 | return new SimpleImmutableEntry(text, field);
351 | }
352 |
353 | //-----------------------------------------------------------------------
354 | private static LocaleStore createLocaleStore(Map> valueTextMap) {
355 | if (!valueTextMap.containsKey(TextStyle.FULL_STANDALONE)) {
356 | valueTextMap.put(TextStyle.FULL_STANDALONE, valueTextMap.get(TextStyle.FULL));
357 | }
358 |
359 | if (!valueTextMap.containsKey(TextStyle.SHORT_STANDALONE)) {
360 | valueTextMap.put(TextStyle.SHORT_STANDALONE, valueTextMap.get(TextStyle.SHORT));
361 | }
362 |
363 | if (valueTextMap.containsKey(TextStyle.NARROW) && valueTextMap.containsKey(TextStyle.NARROW_STANDALONE) == false) {
364 | valueTextMap.put(TextStyle.NARROW_STANDALONE, valueTextMap.get(TextStyle.NARROW));
365 | }
366 | return new LocaleStore(valueTextMap);
367 | }
368 |
369 | /**
370 | * Stores the text for a single locale.
371 | *
372 | * Some fields have a textual representation, such as day-of-week or month-of-year.
373 | * These textual representations can be captured in this class for printing
374 | * and parsing.
375 | *
376 | * This class is immutable and thread-safe.
377 | */
378 | static final class LocaleStore {
379 | /**
380 | * Map of value to text.
381 | */
382 | private final Map> valueTextMap;
383 | /**
384 | * Parsable data.
385 | */
386 | private final Map>> parsable;
387 |
388 | //-----------------------------------------------------------------------
389 |
390 | /**
391 | * Constructor.
392 | *
393 | * @param valueTextMap the map of values to text to store, assigned and not altered, not null
394 | */
395 | LocaleStore(Map> valueTextMap) {
396 | this.valueTextMap = valueTextMap;
397 | Map>> map = new HashMap>>();
398 | List> allList = new ArrayList>();
399 | for (TextStyle style : valueTextMap.keySet()) {
400 | Map> reverse = new HashMap>();
401 | for (Entry entry : valueTextMap.get(style).entrySet()) {
402 | if (reverse.put(entry.getValue(), createEntry(entry.getValue(), entry.getKey())) != null) {
403 | continue; // not parsable, try next style
404 | }
405 | }
406 | List> list = new ArrayList>(reverse.values());
407 | Collections.sort(list, COMPARATOR);
408 | map.put(style, list);
409 | allList.addAll(list);
410 | map.put(null, allList);
411 | }
412 | Collections.sort(allList, COMPARATOR);
413 | this.parsable = map;
414 | }
415 |
416 | //-----------------------------------------------------------------------
417 |
418 | /**
419 | * Gets the text for the specified field value, locale and style
420 | * for the purpose of printing.
421 | *
422 | * @param value the value to get text for, not null
423 | * @param style the style to get text for, not null
424 | * @return the text for the field value, null if no text found
425 | */
426 | String getText(long value, TextStyle style) {
427 | Map map = valueTextMap.get(style);
428 | return map != null ? map.get(value) : null;
429 | }
430 |
431 | /**
432 | * Gets an iterator of text to field for the specified style for the purpose of parsing.
433 | *
434 | * The iterator must be returned in order from the longest text to the shortest.
435 | *
436 | * @param style the style to get text for, null for all parsable text
437 | * @return the iterator of text to field pairs, in order from longest text to shortest text,
438 | * null if the style is not parsable
439 | */
440 | Iterator> getTextIterator(TextStyle style) {
441 | List> list = parsable.get(style);
442 | return list != null ? list.iterator() : null;
443 | }
444 | }
445 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.50'
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.5.0'
11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiandreplace/androiddatetimetextprovider/d8cf719a62597f7cc28076707daf752ee905b292/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Sep 07 13:14:34 CEST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':androiddatetimetextprovider'
2 | rootProject.name='AndroidDateTimeTextProvider'
3 |
--------------------------------------------------------------------------------