diff --git a/.github/workflows/android.yml b/.github/workflows/android-detox.yml similarity index 56% rename from .github/workflows/android.yml rename to .github/workflows/android-detox.yml index 697cfe82..591160b5 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android-detox.yml @@ -1,12 +1,18 @@ -name: "Android: build & test" +name: 'Build & Test' -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + branches: + - master jobs: - build_and_test: - name: Build & test + javascript_unit_tests: + name: Unit tests - javascript runs-on: macos-latest - timeout-minutes: 30 + timeout-minutes: 5 steps: - name: Checkout @@ -17,29 +23,73 @@ jobs: - name: Node uses: actions/setup-node@v1 + - name: Install npm dependencies + run: | + yarn install --frozen-lockfile + + - name: Run unit tests + run: | + yarn test + + java_unit_tests: + name: Unit tests - java + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Install npm dependencies + working-directory: ./examples/detox + run: | + yarn install --frozen-lockfile + + - name: Run unit tests + working-directory: ./examples/detox/android + run: ./gradlew testDebugUnitTest + + end_to_end_tests: + name: End to end tests + runs-on: macos-latest + timeout-minutes: 60 + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 1 + + - name: Node + uses: actions/setup-node@v2 + - name: Use specific Java version for sdkmanager to work - uses: joschi/setup-jdk@v1 + uses: joschi/setup-jdk@v2 with: java-version: 'openjdk8' architecture: 'x64' + - name: Install npm dependencies + working-directory: ./examples/detox + run: | + yarn install --frozen-lockfile --network-timeout 60000 + - name: Download Android Emulator Image + timeout-minutes: 15 run: | echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install "system-images;android-29;google_apis;x86" echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd --force --name emu --device "Nexus 5X" -k 'system-images;android-29;google_apis;x86' $ANDROID_HOME/emulator/emulator -list-avds - - name: Install npm dependencies - working-directory: ./examples/detox - run: | - yarn install --frozen-lockfile - - name: Build working-directory: ./examples/detox run: | yarn build:android-ci - name: Start android emulator + timeout-minutes: 5 working-directory: ./examples/detox continue-on-error: true run: | @@ -48,13 +98,14 @@ jobs: $ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82' $ANDROID_HOME/platform-tools/adb devices echo "Emulator started" - - - name: Run tests + + - name: Run e2e tests + timeout-minutes: 30 working-directory: ./examples/detox run: yarn start & yarn test:android-ci - + - name: Upload artifacts - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 if: failure() with: name: Failing tests diff --git a/README.md b/README.md index 3124bd10..4881b64d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # React Native Date Picker [![npm](https://img.shields.io/npm/v/react-native-date-picker.svg)](https://www.npmjs.com/package/react-native-date-picker) [![Build status](https://img.shields.io/github/workflow/status/henninghall/react-native-date-picker/Android:%20build%20&%20test?label=tests)](https://github.com/henninghall/react-native-date-picker/actions) [![npm](https://img.shields.io/npm/dm/react-native-date-picker.svg)](https://www.npmjs.com/package/react-native-date-picker) - This is a React Native Date Picker with following main features: 📱 Supporting iOS and Android
@@ -11,27 +10,37 @@ This is a React Native Date Picker with following main features: - + + + + + + + + - - + -
iOSAndroid
React Native Date Picker
Android
Choose from 2 different variants
React Native Date Picker Android - React Native Datepicker +
A slightly improved DatePickerIOS.A custom made native component.androidVariant="iosClone"androidVariant="nativeAndroid"
## Installation -1. `npm install react-native-date-picker --save` -2. `react-native link react-native-date-picker` (Only needed for React Native <= 0.59) +1. ```npm install react-native-date-picker``` or ```yarn add react-native-date-picker``` +1. `(cd ios && pod install)` (If you're using CocoaPods) +1. Rebuild the project (e.g. `react-native run-android` or `react-native run-ios`) + +If you're having troubles after following these steps, there might be a linking issue or you're using Expo. -Xcode 11 is required to run the latest version of the date picker. Xcode 10 is supported up to version 2.7.2. +## Requirements + +- Xcode >= 11.6 ## Minimal Example @@ -60,13 +69,18 @@ export default () => { | date | The currently selected date. | | onDateChange | Date change handler | | fadeToColor | Android picker is fading towards this background color. {color, 'none'} | -| maximumDate | Maximum selectable date. | -| minimumDate | Minimum selectable date | +| maximumDate | Maximum selectable date.
Example: `new Date("2021-12-31")` | +| minimumDate | Minimum selectable date.
Example: `new Date("2021-01-01")` | +| androidVariant | Choose from 2 android style variants. {'iosClone', 'nativeAndroid'} (default: 'iosClone') | | Datepicker ios clone variantDatepicker android native variant | | minuteInterval | The interval at which minutes can be selected. | Date picker minute interval IOS | Date picker minute interval Android | | mode | The date picker mode. {'datetime', 'date', 'time'} | React native date time pickerReact native datepickerReact native time picker | react native date time picker androidreact native datepicker androidreact native time picker android | | locale | The locale for the date picker. Changes language, date order and am/pm preferences. Value needs to be a Locale ID. | React Native Date picker locale language ios | React Native Date picker locale language android | -| textColor | Changes the text color. ⚠ Colors other than black (#000000) or white (#ffffff) will replace the "Today" string with a date on iOS 13. | react native datepicker text color background color ios | Text color background color android | +| textColor | Changes the text color. ⚠ Colors other than black (#000000) or white (#ffffff) will replace the "Today" string with a date on iOS 13 or higher. | react native datepicker text color background color ios | Text color background color android | | timeZoneOffsetInMinutes | Timezone offset in minutes (default: device's timezone) | +| dividerHeight | Change the divider height (only supported for iosClone) | +| is24hourSource | Change how the 24h mode (am/pm) should be determined, by device settings or by locale. {'locale', 'device'} (android only, default: 'device') | +## Linking +This package supports automatic linking. Usually, the only thing you need to do is to install the package, the cocoapods dependencies (as descripted above). Then rebuild the project by running `react-native run-ios`, `react-native run-android` or start the build from within Xcode/Android Studio. If you'running a React Native version below 0.60 or your setup is having issues with automatic linking, you can run `npx react-native link react-native-date-picker` and rebuild. In some occations you'll have to manually link the package. Instructions in this issue. ## About @@ -74,17 +88,23 @@ export default () => { ## FAQ +### How do I change the divider color? +The color of the divider, separator (or whatever you choose to call it) can only be changed on android for the androidNative variant. To change it, add the following to your android AppTheme. The theme is often found in styles.xml. +```xml +#ff0000 +``` + ### Can I use expo? Unfortunately, expo does not support this date picker at the moment. Upvote this feature request if you would like to have it included. ### How do i change the date order? (To YYYY-MM-DD etc) -The order is determined by the `locale` prop. Set for instance `locale='fr'`to get the France preference. +The order is determined by the `locale` prop. Set for instance `locale='fr'`to get the french preference. ### How do i change the 12/24h or AM/PM format? -On iOS the 12/24h preference is determined by the `locale` prop. Set for instance `locale='fr'`to get the France preference. On Android the 12/24h format is determined by the device setting. When using 12h mode the AM/PM part of the picker will be displayed. +On iOS the 12/24h preference is determined by the `locale` prop. Set for instance `locale='fr'`to get the french preference. On Android the 12/24h format is determined by the device setting by default. Add `is24hourSource="locale"` to let the locale determine the device setting on android as well. When using 12h mode the AM/PM part of the picker will be displayed. It is NOT recommended to force any specific 12/24h format, but this can be achieved by, choosing a locale which has the desired 24h preference and add `is24hourSource="locale"`. ### Is it possible to show only month and year? @@ -99,6 +119,36 @@ If you have enabled iOS cloneNative Android + + date time picker + + date time picker + + +The so called "iOS clone" looks and works similar to the ios version. It shows normaly 5 lines of dates. It is enabled by default. +The "Android Native" version looks more like a standard native implementation on Android. The divider color can be changed by adding the following to you Android theme: +
<item name="colorControlNormal">#03b6fc</item> + + + +```js +androidVariant="iosClone" +``` + + +```js +androidVariant="nativeAndroid" +``` + + + + + ## Three different modes Here are some more info about the three different picker modes that are available. diff --git a/android/build.gradle b/android/build.gradle index 26e7f33e..da57e863 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -25,7 +25,7 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.facebook.react:react-native:+' - implementation 'com.henninghall.android:NumberPickerView:1.1.2' + implementation 'com.henninghall.android:NumberPickerView:1.1.5' implementation 'org.apache.commons:commons-lang3:3.7' implementation group: 'net.time4j', name: 'time4j-android', version: '4.2-2018i' } diff --git a/android/src/main/java/com/henninghall/date_picker/DatePickerManager.java b/android/src/main/java/com/henninghall/date_picker/DatePickerManager.java index f08a0d8b..ff7be9a1 100644 --- a/android/src/main/java/com/henninghall/date_picker/DatePickerManager.java +++ b/android/src/main/java/com/henninghall/date_picker/DatePickerManager.java @@ -7,6 +7,9 @@ import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.annotations.ReactPropGroup; +import com.henninghall.date_picker.props.DividerHeightProp; +import com.henninghall.date_picker.props.Is24hourSourceProp; +import com.henninghall.date_picker.props.VariantProp; import com.henninghall.date_picker.props.DateProp; import com.henninghall.date_picker.props.FadeToColorProp; import com.henninghall.date_picker.props.LocaleProp; @@ -41,7 +44,9 @@ public PickerView createViewInstance(ThemedReactContext reactContext) { } @ReactPropGroup(names = { DateProp.name, ModeProp.name, LocaleProp.name, MaximumDateProp.name, - MinimumDateProp.name, FadeToColorProp.name, TextColorProp.name, UtcProp.name, MinuteIntervalProp.name}) + MinimumDateProp.name, FadeToColorProp.name, TextColorProp.name, UtcProp.name, MinuteIntervalProp.name, + VariantProp.name, DividerHeightProp.name, Is24hourSourceProp.name + }) public void setProps(PickerView view, int index, Dynamic value) { updateProp("setProps", view, index, value); } diff --git a/android/src/main/java/com/henninghall/date_picker/DayFormats.java b/android/src/main/java/com/henninghall/date_picker/DayFormats.java deleted file mode 100644 index 02f3e75c..00000000 --- a/android/src/main/java/com/henninghall/date_picker/DayFormats.java +++ /dev/null @@ -1,752 +0,0 @@ -package com.henninghall.date_picker; - -import java.util.HashMap; - -public class DayFormats { - - public static String get(String locale){ - return map.get(locale); - } - - private static HashMap map = new HashMap(){{ - put("af","EEE dd MMM"); - put("af_NA","EEE dd MMM"); - put("af_ZA","EEE dd MMM"); - put("agq","EEE d MMM"); - put("agq_CM","EEE d MMM"); - put("ak","EEE MMM dd"); - put("ak_GH","EEE MMM dd"); - put("am","MMM d EEE"); - put("am_ET","MMM d EEE"); - put("ar","EEE، d MMM"); - put("ar_001","EEE، d MMM"); - put("ar_AE","EEE، d MMM"); - put("ar_BH","EEE، d MMM"); - put("ar_DJ","EEE، d MMM"); - put("ar_DZ","EEE، d MMM"); - put("ar_EG","EEE، d MMM"); - put("ar_EH","EEE، d MMM"); - put("ar_ER","EEE، d MMM"); - put("ar_IL","EEE، d MMM"); - put("ar_IQ","EEE، d MMM"); - put("ar_JO","EEE، d MMM"); - put("ar_KM","EEE، d MMM"); - put("ar_KW","EEE، d MMM"); - put("ar_LB","EEE، d MMM"); - put("ar_LY","EEE، d MMM"); - put("ar_MA","EEE، d MMM"); - put("ar_MR","EEE، d MMM"); - put("ar_OM","EEE، d MMM"); - put("ar_PS","EEE، d MMM"); - put("ar_QA","EEE، d MMM"); - put("ar_SA","EEE، d MMM"); - put("ar_SD","EEE، d MMM"); - put("ar_SO","EEE، d MMM"); - put("ar_SS","EEE، d MMM"); - put("ar_SY","EEE، d MMM"); - put("ar_TD","EEE، d MMM"); - put("ar_TN","EEE، d MMM"); - put("ar_XB","EEE، d MMM"); - put("ar_YE","EEE، d MMM"); - put("as","EEE d MMM"); - put("as_IN","EEE d MMM"); - put("asa","EEE d MMM"); - put("asa_TZ","EEE d MMM"); - put("ast","EEE d MMM"); - put("ast_ES","EEE d MMM"); - put("az","d MMM EEE"); - put("az_Cyrl","d MMM EEE"); - put("az_Cyrl_AZ","d MMM EEE"); - put("az_Latn","d MMM EEE"); - put("az_Latn_AZ","d MMM EEE"); - put("bas","EEE d MMM"); - put("bas_CM","EEE d MMM"); - put("be","EEE d MMM"); - put("be_BY","EEE d MMM"); - put("bem","EEE d MMM"); - put("bem_ZM","EEE d MMM"); - put("bez","EEE d MMM"); - put("bez_TZ","EEE d MMM"); - put("bg","EEE d MMM"); - put("bg_BG","EEE d MMM"); - put("bm","EEE d MMM"); - put("bm_ML","EEE d MMM"); - put("bn","EEE d MMM"); - put("bn_BD","EEE d MMM"); - put("bn_IN","EEE d MMM"); - put("bo","MMMའི་ཚེས་d EEE"); - put("bo_CN","MMMའི་ཚེས་d EEE"); - put("bo_IN","MMMའི་ཚེས་d EEE"); - put("br","EEE d MMM"); - put("br_FR","EEE d MMM"); - put("brx","EEE MMM d"); - put("brx_IN","EEE MMM d"); - put("bs","EEE d. MMM "); - put("bs_Cyrl","EEE dd. MMM "); - put("bs_Cyrl_BA","EEE dd. MMM "); - put("bs_Latn","EEE d. MMM "); - put("bs_Latn_BA","EEE d. MMM "); - put("ca","EEE d MMM"); - put("ca_AD","EEE d MMM"); - put("ca_ES","EEE d MMM"); - put("ca_FR","EEE d MMM"); - put("ca_IT","EEE d MMM"); - put("ccp","EEE d MMM"); - put("ccp_BD","EEE d MMM"); - put("ccp_IN","EEE d MMM"); - put("ce","MMM d EEE"); - put("ce_RU","MMM d EEE"); - put("cgg","EEE d MMM"); - put("cgg_UG","EEE d MMM"); - put("chr","EEE MMM d"); - put("chr_US","EEE MMM d"); - put("ckb","MMM d EEE"); - put("ckb_IQ","MMM d EEE"); - put("ckb_IR","MMM d EEE"); - put("cs","EEE d. MMM"); - put("cs_CZ","EEE d. MMM"); - put("cy","EEE d MMM"); - put("cy_GB","EEE d MMM"); - put("da","EEE 'den' d. MMM"); - put("da_DK","EEE 'den' d. MMM"); - put("da_GL","EEE 'den' d. MMM"); - put("dav","EEE d MMM"); - put("dav_KE","EEE d MMM"); - put("de","EEE d. MMM"); - put("de_AT","EEE d. MMM"); - put("de_BE","EEE d. MMM"); - put("de_CH","EEE d. MMM"); - put("de_DE","EEE d. MMM"); - put("de_IT","EEE d. MMM"); - put("de_LI","EEE d. MMM"); - put("de_LU","EEE d. MMM"); - put("dje","EEE d MMM"); - put("dje_NE","EEE d MMM"); - put("dsb","EEE d. MMM"); - put("dsb_DE","EEE d. MMM"); - put("dua","EEE d MMM"); - put("dua_CM","EEE d MMM"); - put("dyo","EEE d MMM"); - put("dyo_SN","EEE d MMM"); - put("dz","EEE MMM ཚེས་dd"); - put("dz_BT","EEE MMM ཚེས་dd"); - put("ebu","EEE d MMM"); - put("ebu_KE","EEE d MMM"); - put("ee","EEE MMM d"); - put("ee_GH","EEE MMM d"); - put("ee_TG","EEE MMM d"); - put("el","EEE d MMM"); - put("el_CY","EEE d MMM"); - put("el_GR","EEE d MMM"); - put("en","EEE MMM d"); - put("en_001","EEE d MMM"); - put("en_150","EEE d MMM"); - put("en_AG","EEE d MMM"); - put("en_AI","EEE d MMM"); - put("en_AS","EEE MMM d"); - put("en_AT","EEE d MMM"); - put("en_AU","EEE d MMM"); - put("en_BB","EEE d MMM"); - put("en_BE","EEE d MMM"); - put("en_BI","EEE MMM d"); - put("en_BM","EEE d MMM"); - put("en_BS","EEE d MMM"); - put("en_BW","EEE dd MMM"); - put("en_BZ","EEE dd MMM"); - put("en_CA","EEE MMM d"); - put("en_CC","EEE d MMM"); - put("en_CH","EEE d MMM"); - put("en_CK","EEE d MMM"); - put("en_CM","EEE d MMM"); - put("en_CX","EEE d MMM"); - put("en_CY","EEE d MMM"); - put("en_DE","EEE d MMM"); - put("en_DG","EEE d MMM"); - put("en_DK","EEE d MMM"); - put("en_DM","EEE d MMM"); - put("en_ER","EEE d MMM"); - put("en_FI","EEE d MMM"); - put("en_FJ","EEE d MMM"); - put("en_FK","EEE d MMM"); - put("en_FM","EEE d MMM"); - put("en_GB","EEE d MMM"); - put("en_GD","EEE d MMM"); - put("en_GG","EEE d MMM"); - put("en_GH","EEE d MMM"); - put("en_GI","EEE d MMM"); - put("en_GM","EEE d MMM"); - put("en_GU","EEE MMM d"); - put("en_GY","EEE d MMM"); - put("en_HK","EEE d MMM"); - put("en_IE","EEE d MMM"); - put("en_IL","EEE d MMM"); - put("en_IM","EEE d MMM"); - put("en_IN","EEE d MMM"); - put("en_IO","EEE d MMM"); - put("en_JE","EEE d MMM"); - put("en_JM","EEE d MMM"); - put("en_KE","EEE d MMM"); - put("en_KI","EEE d MMM"); - put("en_KN","EEE d MMM"); - put("en_KY","EEE d MMM"); - put("en_LC","EEE d MMM"); - put("en_LR","EEE d MMM"); - put("en_LS","EEE d MMM"); - put("en_MG","EEE d MMM"); - put("en_MH","EEE MMM d"); - put("en_MO","EEE d MMM"); - put("en_MP","EEE MMM d"); - put("en_MS","EEE d MMM"); - put("en_MT","EEE d MMM"); - put("en_MU","EEE d MMM"); - put("en_MW","EEE d MMM"); - put("en_MY","EEE d MMM"); - put("en_NA","EEE d MMM"); - put("en_NF","EEE d MMM"); - put("en_NG","EEE d MMM"); - put("en_NL","EEE d MMM"); - put("en_NR","EEE d MMM"); - put("en_NU","EEE d MMM"); - put("en_NZ","EEE d MMM"); - put("en_PG","EEE d MMM"); - put("en_PH","EEE d MMM"); - put("en_PK","EEE d MMM"); - put("en_PN","EEE d MMM"); - put("en_PR","EEE MMM d"); - put("en_PW","EEE d MMM"); - put("en_RW","EEE d MMM"); - put("en_SB","EEE d MMM"); - put("en_SC","EEE d MMM"); - put("en_SD","EEE d MMM"); - put("en_SE","EEE d MMM"); - put("en_SG","EEE d MMM"); - put("en_SH","EEE d MMM"); - put("en_SI","EEE d MMM"); - put("en_SL","EEE d MMM"); - put("en_SS","EEE d MMM"); - put("en_SX","EEE d MMM"); - put("en_SZ","EEE d MMM"); - put("en_TC","EEE d MMM"); - put("en_TK","EEE d MMM"); - put("en_TO","EEE d MMM"); - put("en_TT","EEE d MMM"); - put("en_TV","EEE d MMM"); - put("en_TZ","EEE d MMM"); - put("en_UG","EEE d MMM"); - put("en_UM","EEE MMM d"); - put("en_US","EEE MMM d"); - put("en_US_POSIX","EEE MMM d"); - put("en_VC","EEE d MMM"); - put("en_VG","EEE d MMM"); - put("en_VI","EEE MMM d"); - put("en_VU","EEE d MMM"); - put("en_WS","EEE d MMM"); - put("en_XA","[EEE MMM d]"); - put("en_ZA","EEE dd MMM"); - put("en_ZM","EEE d MMM"); - put("en_ZW","EEE dd MMM"); - put("eo","EEE d_'a' 'de' MMM"); - put("es","EEE d 'de' MMM"); - put("es_419","EEE d 'de' MMM"); - put("es_AR","EEE d 'de' MMM"); - put("es_BO","EEE d 'de' MMM"); - put("es_BR","EEE d 'de' MMM"); - put("es_BZ","EEE d 'de' MMM"); - put("es_CL","EEE d 'de' MMM"); - put("es_CO","EEE d 'de' MMM"); - put("es_CR","EEE d 'de' MMM"); - put("es_CU","EEE d 'de' MMM"); - put("es_DO","EEE d 'de' MMM"); - put("es_EA","EEE d 'de' MMM"); - put("es_EC","EEE d 'de' MMM"); - put("es_ES","EEE d 'de' MMM"); - put("es_GQ","EEE d 'de' MMM"); - put("es_GT","EEE d 'de' MMM"); - put("es_HN","EEE dd 'de' MMM"); - put("es_IC","EEE d 'de' MMM"); - put("es_MX","EEE d 'de' MMM"); - put("es_NI","EEE d 'de' MMM"); - put("es_PA","EEE d 'de' MMM"); - put("es_PE","EEE d 'de' MMM"); - put("es_PH","EEE d 'de' MMM"); - put("es_PR","EEE d 'de' MMM"); - put("es_PY","EEE d 'de' MMM"); - put("es_SV","EEE d 'de' MMM"); - put("es_US","EEE d 'de' MMM"); - put("es_UY","EEE d 'de' MMM"); - put("es_VE","EEE d 'de' MMM"); - put("et","EEE d. MMM"); - put("et_EE","EEE d. MMM"); - put("eu","MMM d EEE"); - put("eu_ES","MMM d EEE"); - put("ewo","EEE d MMM"); - put("ewo_CM","EEE d MMM"); - put("fa","EEE d MMM"); - put("fa_AF","EEE d MMM"); - put("fa_IR","EEE d MMM"); - put("ff","EEE d MMM"); - put("fi","cccc d. MMM"); - put("fi_FI","cccc d. MMM"); - put("fil","EEE MMM d"); - put("fil_PH","EEE MMM d"); - put("fo","EEE d. MMM"); - put("fo_DK","EEE d. MMM"); - put("fo_FO","EEE d. MMM"); - put("fr","EEE d MMM"); - put("fr_BE","EEE d MMM"); - put("fr_BF","EEE d MMM"); - put("fr_BI","EEE d MMM"); - put("fr_BJ","EEE d MMM"); - put("fr_BL","EEE d MMM"); - put("fr_CA","EEE d MMM"); - put("fr_CD","EEE d MMM"); - put("fr_CF","EEE d MMM"); - put("fr_CG","EEE d MMM"); - put("fr_CH","EEE d MMM"); - put("fr_CI","EEE d MMM"); - put("fr_CM","EEE d MMM"); - put("fr_DJ","EEE d MMM"); - put("fr_DZ","EEE d MMM"); - put("fr_FR","EEE d MMM"); - put("fr_GA","EEE d MMM"); - put("fr_GF","EEE d MMM"); - put("fr_GN","EEE d MMM"); - put("fr_GP","EEE d MMM"); - put("fr_GQ","EEE d MMM"); - put("fr_HT","EEE d MMM"); - put("fr_KM","EEE d MMM"); - put("fr_LU","EEE d MMM"); - put("fr_MA","EEE d MMM"); - put("fr_MC","EEE d MMM"); - put("fr_MF","EEE d MMM"); - put("fr_MG","EEE d MMM"); - put("fr_ML","EEE d MMM"); - put("fr_MQ","EEE d MMM"); - put("fr_MR","EEE d MMM"); - put("fr_MU","EEE d MMM"); - put("fr_NC","EEE d MMM"); - put("fr_NE","EEE d MMM"); - put("fr_PF","EEE d MMM"); - put("fr_PM","EEE d MMM"); - put("fr_RE","EEE d MMM"); - put("fr_RW","EEE d MMM"); - put("fr_SC","EEE d MMM"); - put("fr_SN","EEE d MMM"); - put("fr_SY","EEE d MMM"); - put("fr_TD","EEE d MMM"); - put("fr_TG","EEE d MMM"); - put("fr_TN","EEE d MMM"); - put("fr_VU","EEE d MMM"); - put("fr_WF","EEE d MMM"); - put("fr_YT","EEE d MMM"); - put("fur","EEE d 'di' MMM"); - put("fur_IT","EEE d 'di' MMM"); - put("fy","EEE d MMM"); - put("fy_NL","EEE d MMM"); - put("ga","EEE d MMM"); - put("ga_IE","EEE d MMM"); - put("gd","EEE d'mh' MMM"); - put("gd_GB","EEE d'mh' MMM"); - put("gl","EEE d 'de' MMM"); - put("gl_ES","EEE d 'de' MMM"); - put("gsw","EEE d. MMM"); - put("gsw_CH","EEE d. MMM"); - put("gsw_FR","EEE d. MMM"); - put("gsw_LI","EEE d. MMM"); - put("gu","EEE d MMM"); - put("gu_IN","EEE d MMM"); - put("guz","EEE d MMM"); - put("guz_KE","EEE d MMM"); - put("gv","MMM d EEE"); - put("gv_IM","MMM d EEE"); - put("ha","EEE d MMM"); - put("ha_GH","EEE d MMM"); - put("ha_NE","EEE d MMM"); - put("ha_NG","EEE d MMM"); - put("haw","EEE d MMM"); - put("haw_US","EEE d MMM"); - put("he","EEE d בMMM"); - put("he_IL","EEE d בMMM"); - put("hi","EEE d MMM"); - put("hi_IN","EEE d MMM"); - put("hr","EEE d. MMM "); - put("hr_BA","EEE d. MMM "); - put("hr_HR","EEE d. MMM "); - put("hsb","EEE d. MMM"); - put("hsb_DE","EEE d. MMM"); - put("hu","MMM d. EEE"); - put("hu_HU","MMM d. EEE"); - put("hy","MMM d EEE"); - put("hy_AM","MMM d EEE"); - put("ia","EEE 'le' d 'de' MMM"); - put("ia_001","EEE 'le' d 'de' MMM"); - put("id","EEE dd MMM"); - put("id_ID","EEE dd MMM"); - put("ig","EEE d MMM"); - put("ig_NG","EEE d MMM"); - put("ii","MMM d EEE"); - put("ii_CN","MMM d EEE"); - put("is","EEE d. MMM"); - put("is_IS","EEE d. MMM"); - put("it","EEE d MMM"); - put("it_CH","EEE d MMM"); - put("it_IT","EEE d MMM"); - put("it_SM","EEE d MMM"); - put("it_VA","EEE d MMM"); - put("ja","M月d日EEE"); - put("ja_JP","M月d日EEE"); - put("jgo","EEE MMM dd"); - put("jgo_CM","EEE MMM dd"); - put("jmc","EEE d MMM"); - put("jmc_TZ","EEE d MMM"); - put("jv","EEE d MMM"); - put("jv_ID","EEE d MMM"); - put("ka","EEE dd MMM"); - put("ka_GE","EEE dd MMM"); - put("kab","EEE d MMM"); - put("kab_DZ","EEE d MMM"); - put("kam","EEE d MMM"); - put("kam_KE","EEE d MMM"); - put("kde","EEE d MMM"); - put("kde_TZ","EEE d MMM"); - put("kea","EEE d 'di' MMM"); - put("kea_CV","EEE d 'di' MMM"); - put("khq","EEE d MMM"); - put("khq_ML","EEE d MMM"); - put("ki","EEE d MMM"); - put("ki_KE","EEE d MMM"); - put("kk","d MMM EEE"); - put("kk_KZ","d MMM EEE"); - put("kkj","EEE dd MMM"); - put("kkj_CM","EEE dd MMM"); - put("kl","MMM d EEE"); - put("kl_GL","MMM d EEE"); - put("kln","EEE d MMM"); - put("kln_KE","EEE d MMM"); - put("km","EEE d MMM"); - put("km_KH","EEE d MMM"); - put("kn","EEE MMM d"); - put("kn_IN","EEE MMM d"); - put("ko","M월 d일 EEE"); - put("ko_KP","M월 d일 EEE"); - put("ko_KR","M월 d일 EEE"); - put("kok","EEE d MMM"); - put("kok_IN","EEE d MMM"); - put("ks","EEE MMM d"); - put("ks_IN","EEE MMM d"); - put("ksb","EEE d MMM"); - put("ksb_TZ","EEE d MMM"); - put("ksf","EEE d MMM"); - put("ksf_CM","EEE d MMM"); - put("ksh","EEE 'dä' d. MMM"); - put("ksh_DE","EEE 'dä' d. MMM"); - put("ku","MMM d EEE"); - put("ku_TR","MMM d EEE"); - put("kw","MMM d EEE"); - put("kw_GB","MMM d EEE"); - put("ky","d_MMM EEE"); - put("ky_KG","d_MMM EEE"); - put("lag","EEE d MMM"); - put("lag_TZ","EEE d MMM"); - put("lb","EEE d. MMM"); - put("lb_LU","EEE d. MMM"); - put("lg","EEE d MMM"); - put("lg_UG","EEE d MMM"); - put("lkt","MMM d EEE"); - put("lkt_US","MMM d EEE"); - put("ln","EEE d MMM"); - put("ln_AO","EEE d MMM"); - put("ln_CD","EEE d MMM"); - put("ln_CF","EEE d MMM"); - put("ln_CG","EEE d MMM"); - put("lo","EEE ທີ d MMM"); - put("lo_LA","EEE ທີ d MMM"); - put("lrc","MMM d EEE"); - put("lrc_IQ","MMM d EEE"); - put("lrc_IR","MMM d EEE"); - put("lt","MMM d 'd'. EEE"); - put("lt_LT","MMM d 'd'. EEE"); - put("lu","EEE d MMM"); - put("lu_CD","EEE d MMM"); - put("luo","EEE d MMM"); - put("luo_KE","EEE d MMM"); - put("luy","EEE d MMM"); - put("luy_KE","EEE d MMM"); - put("lv","EEE 'gada' d. MMM"); - put("lv_LV","EEE 'gada' d. MMM"); - put("mas","EEE d MMM"); - put("mas_KE","EEE d MMM"); - put("mas_TZ","EEE d MMM"); - put("mer","EEE d MMM"); - put("mer_KE","EEE d MMM"); - put("mfe","EEE d MMM"); - put("mfe_MU","EEE d MMM"); - put("mg","EEE d MMM"); - put("mg_MG","EEE d MMM"); - put("mgh","EEE d MMM"); - put("mgh_MZ","EEE d MMM"); - put("mgo","EEE MMM dd"); - put("mgo_CM","EEE MMM dd"); - put("mi","MMM d EEE"); - put("mi_NZ","MMM d EEE"); - put("mk","EEE dd MMM"); - put("mk_MK","EEE dd MMM"); - put("ml","MMM d EEE"); - put("ml_IN","MMM d EEE"); - put("mn","MM.dd EEE"); - put("mn_MN","MM.dd EEE"); - put("mr","EEE d MMM"); - put("mr_IN","EEE d MMM"); - put("ms","EEE d MMM"); - put("ms_BN","dd MMM"); - put("ms_MY","EEE d MMM"); - put("ms_SG","EEE d MMM"); - put("mt","EEE d 'ta'’ MMM"); - put("mt_MT","EEE d 'ta'’ MMM"); - put("mua","EEE d MMM"); - put("mua_CM","EEE d MMM"); - put("my","၊ MMM d၊ EEE"); - put("my_MM","၊ MMM d၊ EEE"); - put("mzn","MMM d EEE"); - put("mzn_IR","MMM d EEE"); - put("naq","EEE d MMM"); - put("naq_NA","EEE d MMM"); - put("nb","EEE d. MMM"); - put("nb_NO","EEE d. MMM"); - put("nb_SJ","EEE d. MMM"); - put("nd","EEE d MMM"); - put("nd_ZW","EEE d MMM"); - put("nds","MMM d EEE"); - put("nds_DE","MMM d EEE"); - put("nds_NL","MMM d EEE"); - put("ne","MMM d EEE"); - put("ne_IN","MMM d EEE"); - put("ne_NP","MMM d EEE"); - put("nl","EEE d MMM"); - put("nl_AW","EEE d MMM"); - put("nl_BE","EEE d MMM"); - put("nl_BQ","EEE d MMM"); - put("nl_CW","EEE d MMM"); - put("nl_NL","EEE d MMM"); - put("nl_SR","EEE d MMM"); - put("nl_SX","EEE d MMM"); - put("nmg","EEE d MMM"); - put("nmg_CM","EEE d MMM"); - put("nn","EEE d. MMM"); - put("nn_NO","EEE d. MMM"); - put("nnh","EEE 'lyɛ'̌ʼ d 'na' MMM"); - put("nnh_CM","EEE 'lyɛ'̌ʼ d 'na' MMM"); - put("nus","EEE d MMM"); - put("nus_SS","EEE d MMM"); - put("nyn","EEE d MMM"); - put("nyn_UG","EEE d MMM"); - put("om","EEE MMM d"); - put("om_ET","EEE MMM d"); - put("om_KE","EEE MMM d"); - put("or","EEE MMM d"); - put("or_IN","EEE MMM d"); - put("os","EEE d MMM"); - put("os_GE","EEE d MMM"); - put("os_RU","EEE d MMM"); - put("pa","EEE d MMM"); - put("pa_Arab","EEE dd MMM"); - put("pa_Arab_PK","EEE dd MMM"); - put("pa_Guru","EEE d MMM"); - put("pa_Guru_IN","EEE d MMM"); - put("pl","EEE d MMM"); - put("pl_PL","EEE d MMM"); - put("ps","EEE MMM d"); - put("ps_AF","EEE MMM d"); - put("pt","EEE d 'de' MMM"); - put("pt_AO","EEE d 'de' MMM"); - put("pt_BR","EEE d 'de' MMM"); - put("pt_CH","EEE d 'de' MMM"); - put("pt_CV","EEE d 'de' MMM"); - put("pt_GQ","EEE d 'de' MMM"); - put("pt_GW","EEE d 'de' MMM"); - put("pt_LU","EEE d 'de' MMM"); - put("pt_MO","EEE d 'de' MMM"); - put("pt_MZ","EEE d 'de' MMM"); - put("pt_PT","EEE d 'de' MMM"); - put("pt_ST","EEE d 'de' MMM"); - put("pt_TL","EEE d 'de' MMM"); - put("qu","EEE d MMM"); - put("qu_BO","EEE d MMM"); - put("qu_EC","EEE d MMM"); - put("qu_PE","EEE d MMM"); - put("rm","EEE d MMM"); - put("rm_CH","EEE d MMM"); - put("rn","EEE d MMM"); - put("rn_BI","EEE d MMM"); - put("ro","EEE d MMM"); - put("ro_MD","EEE d MMM"); - put("ro_RO","EEE d MMM"); - put("rof","EEE d MMM"); - put("rof_TZ","EEE d MMM"); - put("ru","EEE d MMM"); - put("ru_BY","EEE d MMM"); - put("ru_KG","EEE d MMM"); - put("ru_KZ","EEE d MMM"); - put("ru_MD","EEE d MMM"); - put("ru_RU","EEE d MMM"); - put("ru_UA","EEE d MMM"); - put("rw","MMM d EEE"); - put("rw_RW","MMM d EEE"); - put("rwk","EEE d MMM"); - put("rwk_TZ","EEE d MMM"); - put("sah","MMM d EEE"); - put("sah_RU","MMM d EEE"); - put("saq","EEE d MMM"); - put("saq_KE","EEE d MMM"); - put("sbp","EEE d MMM"); - put("sbp_TZ","EEE d MMM"); - put("sd","MMM d EEE"); - put("sd_PK","MMM d EEE"); - put("se","MMM d EEE"); - put("se_FI","EEE d MMM"); - put("se_NO","MMM d EEE"); - put("se_SE","MMM d EEE"); - put("seh","EEE d 'de' MMM"); - put("seh_MZ","EEE d 'de' MMM"); - put("ses","EEE d MMM"); - put("ses_ML","EEE d MMM"); - put("sg","EEE d MMM"); - put("sg_CF","EEE d MMM"); - put("shi","EEE d MMM"); - put("shi_Latn","EEE d MMM"); - put("shi_Latn_MA","EEE d MMM"); - put("shi_Tfng","EEE d MMM"); - put("shi_Tfng_MA","EEE d MMM"); - put("si","MMM d EEE"); - put("si_LK","MMM d EEE"); - put("sk","EEE d. MMM"); - put("sk_SK","EEE d. MMM"); - put("sl","EEE dd. MMM"); - put("sl_SI","EEE dd. MMM"); - put("smn","cccc MMM d."); - put("smn_FI","cccc MMM d."); - put("sn","MMM d EEE"); - put("sn_ZW","MMM d EEE"); - put("so","EEE MMM dd"); - put("so_DJ","EEE MMM dd"); - put("so_ET","EEE MMM dd"); - put("so_KE","EEE MMM dd"); - put("so_SO","EEE MMM dd"); - put("sq","EEE d MMM"); - put("sq_AL","EEE d MMM"); - put("sq_MK","EEE d MMM"); - put("sq_XK","EEE d MMM"); - put("sr","EEE dd. MMM "); - put("sr_Cyrl","EEE dd. MMM "); - put("sr_Cyrl_BA","EEE dd. MMM "); - put("sr_Cyrl_ME","EEE dd. MMM "); - put("sr_Cyrl_RS","EEE dd. MMM "); - put("sr_Cyrl_XK","EEE dd. MMM "); - put("sr_Latn","EEE dd. MMM "); - put("sr_Latn_BA","EEE dd. MMM "); - put("sr_Latn_ME","EEE dd. MMM "); - put("sr_Latn_RS","EEE dd. MMM "); - put("sr_Latn_XK","EEE dd. MMM "); - put("sv","EEE d MMM"); - put("sv_AX","EEE d MMM"); - put("sv_FI","EEE d MMM"); - put("sv_SE","EEE d MMM"); - put("sw","EEE d MMM"); - put("sw_CD","EEE d MMM"); - put("sw_KE","EEE d MMM"); - put("sw_TZ","EEE d MMM"); - put("sw_UG","EEE d MMM"); - put("ta","EEE d MMM"); - put("ta_IN","EEE d MMM"); - put("ta_LK","EEE d MMM"); - put("ta_MY","EEE d MMM"); - put("ta_SG","EEE d MMM"); - put("te","d MMM EEE"); - put("te_IN","d MMM EEE"); - put("teo","EEE d MMM"); - put("teo_KE","EEE d MMM"); - put("teo_UG","EEE d MMM"); - put("tg","EEE dd MMM"); - put("tg_TJ","EEE dd MMM"); - put("th","EEEที่ d MMM"); - put("th_TH","EEEที่ d MMM"); - put("ti","EEE፣ dd MMM"); - put("ti_ER","EEE፣ dd MMM"); - put("ti_ET","EEE፣ dd MMM"); - put("tk","d MMM EEE"); - put("tk_TM","d MMM EEE"); - put("to","EEE d MMM"); - put("to_TO","EEE d MMM"); - put("tr","d MMM EEE"); - put("tr_CY","d MMM EEE"); - put("tr_TR","d MMM EEE"); - put("tt","d MMM EEE"); - put("tt_RU","d MMM EEE"); - put("twq","EEE d MMM"); - put("twq_NE","EEE d MMM"); - put("tzm","EEE d MMM"); - put("tzm_MA","EEE d MMM"); - put("ug","d_MMM، EEE"); - put("ug_CN","d_MMM، EEE"); - put("uk","EEE d MMM "); - put("uk_UA","EEE d MMM "); - put("ur","EEE، d MMM،"); - put("ur_IN","EEE، d MMM،"); - put("ur_PK","EEE، d MMM،"); - put("uz","EEE d_MMM"); - put("uz_Arab","EEE d_MMM"); - put("uz_Arab_AF","EEE d_MMM"); - put("uz_Cyrl","EEE dd MMM"); - put("uz_Cyrl_UZ","EEE dd MMM"); - put("uz_Latn","EEE d_MMM"); - put("uz_Latn_UZ","EEE d_MMM"); - put("vai","EEE d MMM"); - put("vai_Latn","EEE d MMM"); - put("vai_Latn_LR","EEE d MMM"); - put("vai_Vaii","EEE d MMM"); - put("vai_Vaii_LR","EEE d MMM"); - put("vi","EEE d MMM"); - put("vi_VN","EEE d MMM"); - put("vun","EEE d MMM"); - put("vun_TZ","EEE d MMM"); - put("wae","EEE d. MMM"); - put("wae_CH","EEE d. MMM"); - put("wo","EEE d MMM"); - put("wo_SN","EEE d MMM"); - put("xh","MMM d EEE"); - put("xh_ZA","MMM d EEE"); - put("xog","EEE d MMM"); - put("xog_UG","EEE d MMM"); - put("yav","EEE d MMM"); - put("yav_CM","EEE d MMM"); - put("yi","EEE dטן MMM"); - put("yi_001","EEE dטן MMM"); - put("yo","EEE d MMM"); - put("yo_BJ","EEE d MMM"); - put("yo_NG","EEE d MMM"); - put("yue","M月d日 EEE"); - put("yue_Hans","M月d日EEE"); - put("yue_Hans_CN","M月d日EEE"); - put("yue_Hant","M月d日 EEE"); - put("yue_Hant_HK","M月d日 EEE"); - put("zgh","EEE d MMM"); - put("zgh_MA","EEE d MMM"); - put("zh","M月d日EEE"); - put("zh_CH","M月d日EEE"); - put("zh_HK","M月d日EEE"); - put("zh_MO","M月d日EEE"); - put("zh_SG","M月d日EEE"); - put("zh_Hans","M月d日EEE"); - put("zh_Hans_CN","M月d日EEE"); - put("zh_Hans_HK","M月d日EEE"); - put("zh_Hans_MO","M月d日EEE"); - put("zh_Hans_SG","M月d日EEE"); - put("zh_Hant", "M月d日EEE"); - put("zh_Hant_HK","M月d日EEE"); - put("zh_Hant_MO","M月d日EEE"); - put("zh_Hant_TW","M月d日EEE"); - put("zu","EEE MMM d"); - put("zu_ZA","EEE MMM d"); - }}; - -} diff --git a/android/src/main/java/com/henninghall/date_picker/DerivedData.java b/android/src/main/java/com/henninghall/date_picker/DerivedData.java index a1ea1e36..ece342ee 100644 --- a/android/src/main/java/com/henninghall/date_picker/DerivedData.java +++ b/android/src/main/java/com/henninghall/date_picker/DerivedData.java @@ -1,13 +1,18 @@ package com.henninghall.date_picker; +import android.text.format.DateFormat; import android.util.Log; import com.henninghall.date_picker.models.Mode; +import com.henninghall.date_picker.models.Variant; import com.henninghall.date_picker.models.WheelType; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import static com.henninghall.date_picker.models.Is24HourSource.*; + public class DerivedData { private final State state; @@ -37,7 +42,7 @@ public ArrayList getVisibleWheels() { break; } } - if((mode == Mode.time || mode == Mode.datetime) && Utils.usesAmPm()){ + if((mode == Mode.time || mode == Mode.datetime) && state.derived.usesAmPm()){ visibleWheels.add(WheelType.AM_PM); } return visibleWheels; @@ -54,7 +59,10 @@ public ArrayList getOrderedVisibleWheels() { } private ArrayList getOrderedWheels() { - String dateTimePattern = LocaleUtils.getDateTimePattern(state.getLocale()); + String dateTimePatternOld = LocaleUtils.getDateTimePattern(state.getLocale()); + String dateTimePattern = dateTimePatternOld.replaceAll("\\('(.+?)'\\)","\\${$1}") + .replaceAll("'.+?'","") + .replaceAll("\\$\\{(.+?)\\}","('$1')"); ArrayList unorderedTypes = new ArrayList(Arrays.asList(WheelType.values())); ArrayList orderedWheels = new ArrayList<>(); @@ -62,9 +70,9 @@ private ArrayList getOrderedWheels() { unorderedTypes.remove(WheelType.DAY); orderedWheels.add(WheelType.DAY); - for (char ch : dateTimePattern.toCharArray()){ + for (char c: dateTimePattern.toCharArray()){ try { - WheelType wheelType = Utils.patternCharToWheelType(ch); + WheelType wheelType = Utils.patternCharToWheelType(c); if (unorderedTypes.contains(wheelType)) { unorderedTypes.remove(wheelType); orderedWheels.add(wheelType); @@ -96,4 +104,25 @@ public int getShownCount() { return oddShowCount; } + public boolean hasNativeStyle() { + return state.getVariant() == Variant.nativeAndroid; + } + + public int getRootLayout() { + switch (state.getVariant()){ + case nativeAndroid: return R.layout.native_picker; + case iosClone: return R.layout.ios_clone; + default: return R.layout.ios_clone; + } + } + + public boolean usesAmPm(){ + if(state.getIs24HourSource() == locale) return LocaleUtils.localeUsesAmPm(state.getLocale()); + return Utils.deviceUsesAmPm(); + } + + public boolean hasOnly2Wheels(){ + return state.getMode() == Mode.time && !usesAmPm(); + } + } diff --git a/android/src/main/java/com/henninghall/date_picker/Formats.java b/android/src/main/java/com/henninghall/date_picker/Formats.java new file mode 100644 index 00000000..080b4649 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/Formats.java @@ -0,0 +1,153 @@ +package com.henninghall.date_picker; + +import java.util.EnumMap; +import java.util.HashMap; + +public class Formats { + + public static EnumMap defaultFormat = mapOf("EEE, MMM d", "d", "y"); + + public enum Format { + MMMEd, d, y + } + + public static String get(String locale, Format format) throws FormatNotFoundException { + try { + return map.get(locale).get(format) + .replaceAll(",", ""); + } catch (NullPointerException e) { + throw new FormatNotFoundException(); + } + } + + private static HashMap> map = new HashMap>() {{ + put("af", mapOf("EEE d MMM", "d", "y")); + put("am", mapOf("EEE፣ MMM d", "d", "y")); + put("ar", mapOf("EEE، d MMM", "d", "y")); + put("ar_DZ", mapOf("EEE، d MMM", "d", "y")); + put("ar_EG", mapOf("EEE، d MMM", "d", "y")); + put("az", mapOf("d MMM, EEE", "d", "y")); + put("be", mapOf("EEE, d MMM", "d", "y")); + put("bg", mapOf("EEE, d.MM", "d", "y 'г'.")); + put("bn", mapOf("EEE d MMM", "d", "y")); + put("br", mapOf("EEE d MMM", "d", "y")); + put("bs", mapOf("EEE, d. MMM", "d.", "y.")); + put("ca", mapOf("EEE, d MMM", "d", "y")); + put("chr", mapOf("EEE, MMM d", "d", "y")); + put("cs", mapOf("EEE d. M.", "d.", "y")); + put("cy", mapOf("EEE, d MMM", "d", "y")); + put("da", mapOf("EEE d. MMM", "d.", "y")); + put("de", mapOf("EEE, d. MMM", "d", "y")); + put("de_AT", mapOf("EEE, d. MMM", "d", "y")); + put("de_CH", mapOf("EEE, d. MMM", "d", "y")); + put("el", mapOf("EEE, d MMM", "d", "y")); + put("en", mapOf("EEE, MMM d", "d", "y")); + put("en_AU", mapOf("EEE, d MMM", "d", "y")); + put("en_CA", mapOf("EEE, MMM d", "d", "y")); + put("en_GB", mapOf("EEE, d MMM", "d", "y")); + put("en_IE", mapOf("EEE, d MMM", "d", "y")); + put("en_IN", mapOf("EEE, d MMM", "d", "y")); + put("en_SG", mapOf("EEE, d MMM", "d", "y")); + put("en_US", mapOf("EEE, MMM d", "d", "y")); + put("en_ZA", mapOf("EEE, dd MMM", "d", "y")); + put("es", mapOf("EEE, d MMM", "d", "y")); + put("es_419", mapOf("EEE, d MMM", "d", "y")); + put("es_ES", mapOf("EEE, d MMM", "d", "y")); + put("es_MX", mapOf("EEE d 'de' MMM", "d", "y")); + put("es_US", mapOf("EEE, d 'de' MMM", "d", "y")); + put("et", mapOf("EEE, d. MMM", "d", "y")); + put("eu", mapOf("MMM d, EEE", "d", "y")); + put("fa", mapOf("EEE d LLL", "d", "y")); + put("fi", mapOf("EEE d. MMM", "d", "y")); + put("fil", mapOf("EEE, MMM d", "d", "y")); + put("fr", mapOf("EEE d MMM", "d", "y")); + put("fr_CA", mapOf("EEE d MMM", "d", "y")); + put("ga", mapOf("EEE d MMM", "d", "y")); + put("gl", mapOf("EEE, d 'de' MMM", "d", "y")); + put("gsw", mapOf("EEE d. MMM", "d", "y")); + put("gu", mapOf("EEE, d MMM", "d", "y")); + put("haw", mapOf("EEE, d MMM", "d", "y")); + put("he", mapOf("EEE, d בMMM", "d", "y")); + put("hi", mapOf("EEE, d MMM", "d", "y")); + put("hr", mapOf("EEE, d. MMM", "d.", "y.")); + put("hu", mapOf("MMM d., EEE", "d", "y.")); + put("hy", mapOf("d MMM, EEE", "d", "y")); + put("id", mapOf("EEE, d MMM", "d", "y")); + put("in", mapOf("EEE, d MMM", "d", "y")); + put("is", mapOf("EEE, d. MMM", "d", "y")); + put("it", mapOf("EEE d MMM", "d", "y")); + put("iw", mapOf("EEE, d בMMM", "d", "y")); + put("ja", mapOf("M月d日 EEE", "d日", "y年")); + put("ka", mapOf("EEE, d MMM", "d", "y")); + put("kk", mapOf("d MMM, EEE", "d", "y")); + put("km", mapOf("EEE d MMM", "d", "y")); + put("kn", mapOf("EEE, d MMM", "d", "y")); + put("ko", mapOf("MMM d일 EEE", "d일", "y년")); + put("ky", mapOf("d-MMM, EEE", "d", "y")); + put("ln", mapOf("EEE d MMM", "d", "y")); + put("lo", mapOf("EEE d MMM", "d", "y")); + put("lt", mapOf("MM-dd, EEE", "dd", "y")); + put("lv", mapOf("EEE, d. MMM", "d", "y. 'g'.")); + put("mk", mapOf("EEE, d MMM", "d", "y")); + put("ml", mapOf("MMM d, EEE", "d", "y")); + put("mn", mapOf("MMM'ын' d. EEE", "d", "y")); + put("mo", mapOf("EEE, d MMM", "d", "y")); + put("mr", mapOf("EEE, d MMM", "d", "y")); + put("ms", mapOf("EEE, d MMM", "d", "y")); + put("mt", mapOf("EEE, d 'ta'’ MMM", "d", "y")); + put("my", mapOf("MMM d၊ EEE", "d", "y")); + put("nb", mapOf("EEE d. MMM", "d.", "y")); + put("ne", mapOf("MMM d, EEE", "d", "y")); + put("nl", mapOf("EEE d MMM", "d", "y")); + put("nn", mapOf("EEE d. MMM", "d.", "y")); + put("no", mapOf("EEE d. MMM", "d.", "y")); + put("no_NO", mapOf("EEE d. MMM", "d.", "y")); + put("or", mapOf("EEE, MMM d", "d", "y")); + put("pa", mapOf("EEE, d MMM", "d", "y")); + put("pl", mapOf("EEE, d MMM", "d", "y")); + put("pt", mapOf("EEE, d 'de' MMM", "d", "y")); + put("pt_BR", mapOf("EEE, d 'de' MMM", "d", "y")); + put("pt_PT", mapOf("EEE, d/MM", "d", "y")); + put("ro", mapOf("EEE, d MMM", "d", "y")); + put("ru", mapOf("ccc, d MMM", "d", "y")); + put("sh", mapOf("EEE d. MMM", "d", "y.")); + put("si", mapOf("MMM d EEE", "d", "y")); + put("sk", mapOf("EEE d. M.", "d.", "y")); + put("sl", mapOf("EEE, d. MMM", "d.", "y")); + put("sq", mapOf("EEE, d MMM", "d", "y")); + put("sr", mapOf("EEE d. MMM", "d", "y.")); + put("sr_Latn", mapOf("EEE d. MMM", "d", "y.")); + put("sv", mapOf("EEE d MMM", "d", "y")); + put("sw", mapOf("EEE, d MMM", "d", "y")); + put("ta", mapOf("MMM d, EEE", "d", "y")); + put("te", mapOf("d MMM, EEE", "d", "y")); + put("th", mapOf("EEE d MMM", "d", "y")); + put("tl", mapOf("EEE, MMM d", "d", "y")); + put("tr", mapOf("d MMMM EEE", "d", "y")); + put("uk", mapOf("EEE, d MMM", "d", "y")); + put("ur", mapOf("EEE، d MMM", "d", "y")); + put("uz", mapOf("EEE, d-MMM", "d", "y")); + put("vi", mapOf("EEE, d MMM", "d", "y")); + put("zh", mapOf("M月d日EEE", "d日", "y年")); + put("zh_CN", mapOf("M月d日EEE", "d日", "y年")); + put("zh_HK", mapOf("M月d日EEE", "d日", "y年")); + put("zh_TW", mapOf("M月d日 EEE", "d日", "y年")); + put("zu", mapOf("EEE, MMM d", "d", "y")); + put("en_ISO", mapOf("EEE, MMM d", "d", "y")); + put("en_MY", mapOf("EEE, d MMM", "d", "y")); + put("fr_CH", mapOf("EEE d MMM", "d", "y")); + put("it_CH", mapOf("EEE d MMM", "d", "y")); + put("ps", mapOf("MMM d, EEE", "d", "y")); + }}; + + private static EnumMap mapOf(final String MMMed, final String d, final String y) { + return new EnumMap(Format.class) {{ + put(Format.MMMEd, MMMed); + put(Format.d, d); + put(Format.y, y); + }}; + } + + static class FormatNotFoundException extends Exception { + } +} diff --git a/android/src/main/java/com/henninghall/date_picker/LocaleUtils.java b/android/src/main/java/com/henninghall/date_picker/LocaleUtils.java index 1585b6bb..b71b99a0 100644 --- a/android/src/main/java/com/henninghall/date_picker/LocaleUtils.java +++ b/android/src/main/java/com/henninghall/date_picker/LocaleUtils.java @@ -2,29 +2,36 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Locale; public class LocaleUtils { - private static ArrayList getFullPatternPieces(Locale locale){ - return Utils.splitOnSpace(getDatePattern(locale)); + public static String getDay(String locale) { + return getFormat(locale, Formats.Format.MMMEd); } - /** - @return Full pattern including special char. Example: Year pattern char be "y" for most - locales but "y년" for korean. - */ - public static String getPatternIncluding(String format, Locale locale) { - for (String piece: getFullPatternPieces(locale)){ - if(piece.contains(format)) { - return piece; + public static String getYear(String locale) { + return getFormat(locale, Formats.Format.y); + } + + public static String getDate(String locale) { + return getFormat(locale, Formats.Format.d); + } + + private static String getFormat(String languageTag, Formats.Format format) { + try { + return Formats.get(languageTag, format); + } catch (Formats.FormatNotFoundException e) { + try { + String firstPartOfLanguageTag = languageTag.substring(0, languageTag.indexOf("_")); + return Formats.get(firstPartOfLanguageTag, format); + } catch (Formats.FormatNotFoundException ex) { + return Formats.defaultFormat.get(format); } } - return null; } - public static String getDatePattern(Locale locale){ + public static String getDatePattern(Locale locale) { DateFormat df = DateFormat.getDateInstance(DateFormat.FULL, locale); return ((SimpleDateFormat) df).toLocalizedPattern() .replaceAll(",", "") @@ -32,23 +39,26 @@ public static String getDatePattern(Locale locale){ .trim(); } - static String getDateTimePattern(Locale locale){ + static String getDateTimePattern(Locale locale) { DateFormat format = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, locale); - return ((SimpleDateFormat)format).toLocalizedPattern().replace(",", ""); + return ((SimpleDateFormat) format).toLocalizedPattern().replace(",", ""); } - public static Locale getLocale(String languageTag){ + public static Locale getLocale(String languageTag) { Locale locale; - try{ + try { locale = org.apache.commons.lang3.LocaleUtils.toLocale(languageTag); - } catch (Exception e ){ + } catch (Exception e) { // Some locales can only be interpreted from country string (for instance zh_Hans_CN ) - String firstPartOfLanguageTag = languageTag.substring(0, languageTag.indexOf("")); + String firstPartOfLanguageTag = languageTag.substring(0, languageTag.indexOf("_")); locale = org.apache.commons.lang3.LocaleUtils.toLocale(firstPartOfLanguageTag); } return locale; } -} - + public static boolean localeUsesAmPm(Locale locale){ + DateFormat df = DateFormat.getTimeInstance(DateFormat.FULL, locale); + return df instanceof SimpleDateFormat && ((SimpleDateFormat) df).toPattern().contains("a"); + } +} diff --git a/android/src/main/java/com/henninghall/date_picker/PickerView.java b/android/src/main/java/com/henninghall/date_picker/PickerView.java index 725aa6fc..53ee3570 100644 --- a/android/src/main/java/com/henninghall/date_picker/PickerView.java +++ b/android/src/main/java/com/henninghall/date_picker/PickerView.java @@ -1,9 +1,15 @@ package com.henninghall.date_picker; -import android.view.View; import android.widget.RelativeLayout; import com.facebook.react.bridge.Dynamic; +import com.henninghall.date_picker.props.DividerHeightProp; +import com.henninghall.date_picker.props.Is24hourSourceProp; +import com.henninghall.date_picker.props.MaximumDateProp; +import com.henninghall.date_picker.props.MinimumDateProp; +import com.henninghall.date_picker.props.MinuteIntervalProp; +import com.henninghall.date_picker.props.UtcProp; +import com.henninghall.date_picker.props.VariantProp; import com.henninghall.date_picker.props.DateProp; import com.henninghall.date_picker.props.FadeToColorProp; import com.henninghall.date_picker.props.HeightProp; @@ -11,52 +17,58 @@ import com.henninghall.date_picker.props.ModeProp; import com.henninghall.date_picker.props.TextColorProp; import com.henninghall.date_picker.ui.UIManager; - import java.util.ArrayList; public class PickerView extends RelativeLayout { - private final View rootView = inflate(getContext(), R.layout.datepicker_view, this); - private final UIManager uiManager; - private State state; + private UIManager uiManager; + private State state = new State(); private ArrayList updatedProps = new ArrayList<>(); public PickerView() { super(DatePickerManager.context); - state = new State(); - uiManager = new UIManager(state, this); } public void update() { - if(updatedProps.contains(FadeToColorProp.name)) { + if (didUpdate(VariantProp.name)) { + this.removeAllViewsInLayout(); + inflate(getContext(), state.derived.getRootLayout(), this); + uiManager = new UIManager(state, this); + } + + if (didUpdate(FadeToColorProp.name)) { uiManager.updateFadeToColor(); } - if(updatedProps.contains(TextColorProp.name)) { + if (didUpdate(TextColorProp.name)) { uiManager.updateTextColor(); } - if(updatedProps.contains(ModeProp.name)) { + if (didUpdate(ModeProp.name, VariantProp.name, Is24hourSourceProp.name)) { uiManager.updateWheelVisibility(); } - if(updatedProps.contains(HeightProp.name)) { + if (didUpdate(HeightProp.name)) { uiManager.updateHeight(); } - if(updatedProps.contains(ModeProp.name) || updatedProps.contains(LocaleProp.name)) { + if (didUpdate(DividerHeightProp.name)) { + uiManager.updateDividerHeight(); + } + + if (didUpdate(ModeProp.name, LocaleProp.name, VariantProp.name, Is24hourSourceProp.name)) { uiManager.updateWheelOrder(); } - ArrayList noDisplayValueChangeProps = new ArrayList(){{ - add(DateProp.name); - add(FadeToColorProp.name); - add(TextColorProp.name); - }}; - updatedProps.removeAll(noDisplayValueChangeProps); + if (didUpdate(ModeProp.name)) { + uiManager.updateWheelPadding(); + } - if(updatedProps.size() != 0) { + if (didUpdate(DateProp.name, HeightProp.name, LocaleProp.name, + MaximumDateProp.name, MinimumDateProp.name, MinuteIntervalProp.name, ModeProp.name, + UtcProp.name, VariantProp.name + )) { uiManager.updateDisplayValues(); } @@ -65,6 +77,13 @@ public void update() { updatedProps = new ArrayList<>(); } + private boolean didUpdate(String... propNames) { + for (String propName : propNames) { + if (updatedProps.contains(propName)) return true; + } + return false; + } + public void updateProp(String propName, Dynamic value) { state.setProp(propName, value); updatedProps.add(propName); @@ -74,10 +93,6 @@ public void scroll(int wheelIndex, int scrollTimes) { uiManager.scroll(wheelIndex, scrollTimes); } - public View getRootView(){ - return rootView; - } - private final Runnable measureAndLayout = new Runnable() { @Override public void run() { @@ -95,5 +110,4 @@ public void requestLayout() { } - } diff --git a/android/src/main/java/com/henninghall/date_picker/State.java b/android/src/main/java/com/henninghall/date_picker/State.java index f540525d..eb50fd20 100644 --- a/android/src/main/java/com/henninghall/date_picker/State.java +++ b/android/src/main/java/com/henninghall/date_picker/State.java @@ -1,7 +1,12 @@ package com.henninghall.date_picker; import com.facebook.react.bridge.Dynamic; +import com.henninghall.date_picker.models.Is24HourSource; import com.henninghall.date_picker.models.Mode; +import com.henninghall.date_picker.models.Variant; +import com.henninghall.date_picker.props.DividerHeightProp; +import com.henninghall.date_picker.props.Is24hourSourceProp; +import com.henninghall.date_picker.props.VariantProp; import com.henninghall.date_picker.props.DateProp; import com.henninghall.date_picker.props.FadeToColorProp; import com.henninghall.date_picker.props.HeightProp; @@ -22,7 +27,7 @@ public class State { private final DateProp dateProp = new DateProp(); - private final Prop modeProp = new ModeProp(); + private final ModeProp modeProp = new ModeProp(); private final LocaleProp localeProp = new LocaleProp(); private final FadeToColorProp fadeToColorProp = new FadeToColorProp(); private final TextColorProp textColorProp = new TextColorProp(); @@ -31,6 +36,9 @@ public class State { private final MaximumDateProp maximumDateProp = new MaximumDateProp(); private final UtcProp utcProp = new UtcProp(); private final HeightProp heightProp = new HeightProp(); + private final VariantProp variantProp = new VariantProp(); + private final DividerHeightProp dividerHeightProp = new DividerHeightProp(); + private final Is24hourSourceProp is24hourSourceProp = new Is24hourSourceProp(); private final HashMap props = new HashMap() {{ put(DateProp.name, dateProp); @@ -43,6 +51,9 @@ public class State { put(MaximumDateProp.name, maximumDateProp); put(UtcProp.name, utcProp); put(HeightProp.name, heightProp); + put(VariantProp.name, variantProp); + put(DividerHeightProp.name, dividerHeightProp); + put(Is24hourSourceProp.name, is24hourSourceProp); }}; public DerivedData derived; @@ -93,9 +104,12 @@ public TimeZone getTimeZone(){ return utc ? TimeZone.getTimeZone("UTC") : TimeZone.getDefault(); } + public String getDateString() { + return (String) dateProp.getValue(); + } + public Calendar getDate() { - String date = (String) dateProp.getValue(); - return Utils.isoToCalendar(date, getTimeZone()); + return Utils.isoToCalendar(getDateString(), getTimeZone()); } public Integer getHeight() { @@ -106,5 +120,16 @@ public String getLocaleLanguageTag(){ return localeProp.getLanguageTag(); } + public Variant getVariant(){ + return variantProp.getValue(); + } + + public int getDividerHeight() { + return dividerHeightProp.getValue(); + } + + public Is24HourSource getIs24HourSource() { + return is24hourSourceProp.getValue(); + } } diff --git a/android/src/main/java/com/henninghall/date_picker/Utils.java b/android/src/main/java/com/henninghall/date_picker/Utils.java index 1e38a7ad..d6d82421 100644 --- a/android/src/main/java/com/henninghall/date_picker/Utils.java +++ b/android/src/main/java/com/henninghall/date_picker/Utils.java @@ -18,7 +18,7 @@ public class Utils { - public static boolean usesAmPm(){ + public static boolean deviceUsesAmPm(){ return !DateFormat.is24HourFormat(DatePickerManager.context); } @@ -87,4 +87,15 @@ public static WheelType patternCharToWheelType(char patternChar) throws Exceptio } + public static int getShortestScrollOption(int from, int to, final int maxValue, boolean isWrapping) { + int size = maxValue + 1; + int option1 = to - from; + int option2 = option1 > 0 ? option1 - size : option1 + size; + if (isWrapping) { + return Math.abs(option1) < Math.abs(option2) ? option1 : option2; + } + if (from + option1 > maxValue) return option2; + if (from + option1 < 0) return option2; + return option1; + } } diff --git a/android/src/main/java/com/henninghall/date_picker/models/Is24HourSource.java b/android/src/main/java/com/henninghall/date_picker/models/Is24HourSource.java new file mode 100644 index 00000000..02493209 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/models/Is24HourSource.java @@ -0,0 +1,5 @@ +package com.henninghall.date_picker.models; + +public enum Is24HourSource { + device, locale +} diff --git a/android/src/main/java/com/henninghall/date_picker/models/Variant.java b/android/src/main/java/com/henninghall/date_picker/models/Variant.java new file mode 100644 index 00000000..955b152d --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/models/Variant.java @@ -0,0 +1,5 @@ +package com.henninghall.date_picker.models; + +public enum Variant { + iosClone, nativeAndroid +} diff --git a/android/src/main/java/com/henninghall/date_picker/pickers/AndroidNative.java b/android/src/main/java/com/henninghall/date_picker/pickers/AndroidNative.java new file mode 100644 index 00000000..196d2917 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/pickers/AndroidNative.java @@ -0,0 +1,214 @@ +package com.henninghall.date_picker.pickers; + +import android.content.Context; +import android.graphics.Paint; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.EditText; +import android.widget.NumberPicker; + +import com.henninghall.date_picker.Utils; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static android.widget.NumberPicker.OnScrollListener.SCROLL_STATE_FLING; +import static android.widget.NumberPicker.OnScrollListener.SCROLL_STATE_IDLE; + +public class AndroidNative extends NumberPicker implements Picker { + + private Picker.OnValueChangeListener onValueChangedListener; + private int state = SCROLL_STATE_IDLE; + private OnValueChangeListenerInScrolling listenerInScrolling; + private boolean isAnimating; + + public AndroidNative(Context context) { + super(context); + } + + public AndroidNative(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AndroidNative(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void setTextAlign(Paint.Align align) { + } + + @Override + public void smoothScrollToValue(int value, boolean needRespond) { + smoothScrollToValue(value); + } + + @Override + public void setNormalTextColor(int color) { + // Not needed for this picker. It auto fades the color + } + + @Override + public void setSelectedTextColor(int color) { + try { + Field selectorWheelPaintField = getClass().getSuperclass() + .getDeclaredField("mSelectorWheelPaint"); + selectorWheelPaintField.setAccessible(true); + ((Paint) selectorWheelPaintField.get(this)).setColor(color); + } catch (NoSuchFieldException e) { + Log.w("setSelectedTextColor", e); + } catch (IllegalAccessException e) { + Log.w("setSelectedTextColor", e); + } catch (IllegalArgumentException e) { + Log.w("setSelectedTextColor", e); + } + + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child instanceof EditText) + ((EditText) child).setTextColor(color); + } + invalidate(); + } + + @Override + public void setShownCount(int count) { + // always 3 date rows -> nothing needs to be done here + } + + @Override + public View getView() { + return this; + } + + @Override + public void setDividerHeight(int height) { + // not supported + } + + @Override + public void setItemPaddingHorizontal(int padding) { + // Not needed for this picker + } + + @Override + public boolean isSpinning() { + return state == SCROLL_STATE_FLING || isAnimating; + } + + @Override + public void smoothScrollToValue(final int value) { + final AndroidNative self = this; + + int currentValue = self.getValue(); + if (value == currentValue) return; + int shortestScrollOption = Utils.getShortestScrollOption(currentValue, value, getMaxValue(), getWrapSelectorWheel()); + final int moves = Math.abs(shortestScrollOption); + int timeBetweenScrollsMs = 100; + int willStopScrollingInMs = timeBetweenScrollsMs * moves; + isAnimating = true; + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + isAnimating = false; + } + }, willStopScrollingInMs); + + for (int i = 0; i < moves; i++) { + // need some delay between each scroll step to make sure it scrolls to correct value + changeValueByOne(shortestScrollOption > 0, i * timeBetweenScrollsMs, i == moves - 1); + } + } + + private void changeValueByOne(final NumberPicker higherPicker, final boolean increment) { + boolean success = false; + try { + Method method = getClass().getSuperclass().getDeclaredMethod("changeValueByOne", boolean.class); + method.setAccessible(true); + method.invoke(higherPicker, increment); + success = true; + } catch (final NoSuchMethodException e) { + Log.w("changeValueByOne", e); + } catch (final IllegalArgumentException e) { + Log.w("changeValueByOne", e); + } catch (final IllegalAccessException e) { + Log.w("changeValueByOne", e); + } catch (final InvocationTargetException e) { + Log.w("changeValueByOne", e); + } finally { + if (!success) { + // make step without animation if failed to use reflection method + setValue((getValue() + (increment ? 1 : -1)) % getMaxValue()); + } + } + } + + private void changeValueByOne(final boolean increment, final int ms, final boolean isLast) { + final AndroidNative self = this; + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + changeValueByOne(self, increment); + if (isLast) { + sendEventIn500ms(); + } + } + }, ms); + } + + @Override + public void setOnValueChangeListenerInScrolling(final OnValueChangeListenerInScrolling listener) { + listenerInScrolling = listener; + } + + @Override + public void setOnValueChangedListener(final Picker.OnValueChangeListener listener) { + this.onValueChangedListener = listener; + final Picker self = this; + + super.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker numberPicker, int oldVal, int newVal) { + if(listenerInScrolling != null) { + listenerInScrolling.onValueChangeInScrolling(self, oldVal, newVal); + } + // onValueChange is triggered also during scrolling. Since we don't want + // to send event during scrolling we make sure wheel is still. This particular + // case happens when wheel is tapped, not scrolled. + if(state == SCROLL_STATE_IDLE) { + sendEventIn500ms(); + } + } + }); + + super.setOnScrollListener(new OnScrollListener() { + @Override + public void onScrollStateChange(NumberPicker numberPicker, int nextState) { + sendEventIfStopped(nextState); + state = nextState; + } + }); + } + + private void sendEventIfStopped(int nextState){ + boolean stoppedScrolling = state != SCROLL_STATE_IDLE && nextState == SCROLL_STATE_IDLE; + if (stoppedScrolling) { + sendEventIn500ms(); + } + } + + private void sendEventIn500ms(){ + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + onValueChangedListener.onValueChange(); + } + // the delay make sure the wheel has stopped before sending the value change event + }, 500); + } + +} diff --git a/android/src/main/java/com/henninghall/date_picker/pickers/IosClone.java b/android/src/main/java/com/henninghall/date_picker/pickers/IosClone.java new file mode 100644 index 00000000..5d70c8c2 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/pickers/IosClone.java @@ -0,0 +1,52 @@ +package com.henninghall.date_picker.pickers; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import cn.carbswang.android.numberpickerview.library.NumberPickerView; + +public class IosClone extends NumberPickerView implements Picker { + + public IosClone(Context context) { + super(context); + } + + public IosClone(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public IosClone(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void setOnValueChangeListenerInScrolling(final Picker.OnValueChangeListenerInScrolling listener) { + final Picker self = this; + super.setOnValueChangeListenerInScrolling(new NumberPickerView.OnValueChangeListenerInScrolling() { + @Override + public void onValueChangeInScrolling(NumberPickerView picker, int oldVal, int newVal) { + listener.onValueChangeInScrolling(self, oldVal, newVal); + } + }); + } + + @Override + public void setOnValueChangedListener(final Picker.OnValueChangeListener listener) { + super.setOnValueChangedListener(new NumberPickerView.OnValueChangeListener() { + @Override + public void onValueChange(NumberPickerView picker, int oldVal, int newVal) { + listener.onValueChange(); + } + }); + } + + @Override + public View getView() { + return this; + } + + @Override + public boolean isSpinning() { + return super.isScrolling(); + } +} diff --git a/android/src/main/java/com/henninghall/date_picker/pickers/Picker.java b/android/src/main/java/com/henninghall/date_picker/pickers/Picker.java new file mode 100644 index 00000000..0b4a49a5 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/pickers/Picker.java @@ -0,0 +1,36 @@ +package com.henninghall.date_picker.pickers; + +import android.graphics.Paint; +import android.view.View; + +public interface Picker { + void setTextAlign(Paint.Align align); + void smoothScrollToValue(int value); + void smoothScrollToValue(int value, boolean needRespond); + void setMaxValue(int value); + void setMinValue(int value); + int getMaxValue(); + boolean getWrapSelectorWheel(); + void setDisplayedValues(String[] value); + int getValue(); + void setValue(int value); + void setNormalTextColor(int value); + void setSelectedTextColor(int value); + void setOnValueChangeListenerInScrolling(Picker.OnValueChangeListenerInScrolling listener); + void setOnValueChangedListener(Picker.OnValueChangeListener onValueChangeListener); + void setShownCount(int count); + View getView(); + void setVisibility(int visibility); + void setWrapSelectorWheel(boolean wrapSelectorWheel); + void setDividerHeight(int height); + void setItemPaddingHorizontal(int padding); + boolean isSpinning(); + + interface OnValueChangeListenerInScrolling { + void onValueChangeInScrolling(Picker picker, int oldVal, int newVal); + } + + interface OnValueChangeListener { + void onValueChange(); + } +} diff --git a/android/src/main/java/com/henninghall/date_picker/props/DividerHeightProp.java b/android/src/main/java/com/henninghall/date_picker/props/DividerHeightProp.java new file mode 100644 index 00000000..cedd4918 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/props/DividerHeightProp.java @@ -0,0 +1,12 @@ +package com.henninghall.date_picker.props; + +import com.facebook.react.bridge.Dynamic; + +public class DividerHeightProp extends Prop { + public static final String name = "dividerHeight"; + + @Override + public Integer toValue(Dynamic value){ + return value.asInt(); + } +} diff --git a/android/src/main/java/com/henninghall/date_picker/props/Is24hourSourceProp.java b/android/src/main/java/com/henninghall/date_picker/props/Is24hourSourceProp.java new file mode 100644 index 00000000..1895be86 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/props/Is24hourSourceProp.java @@ -0,0 +1,13 @@ +package com.henninghall.date_picker.props; + +import com.facebook.react.bridge.Dynamic; +import com.henninghall.date_picker.models.Is24HourSource; + +public class Is24hourSourceProp extends Prop { + public static final String name = "is24hourSource"; + + @Override + public Is24HourSource toValue(Dynamic value){ + return Is24HourSource.valueOf(value.asString()); + } +} diff --git a/android/src/main/java/com/henninghall/date_picker/props/LocaleProp.java b/android/src/main/java/com/henninghall/date_picker/props/LocaleProp.java index 0d7b2373..1f54eb05 100644 --- a/android/src/main/java/com/henninghall/date_picker/props/LocaleProp.java +++ b/android/src/main/java/com/henninghall/date_picker/props/LocaleProp.java @@ -12,7 +12,6 @@ public class LocaleProp extends Prop { public static final String name = "locale"; private String languageTag = getDefaultLanguageTag(); - public LocaleProp(){ super(getDefaultLocale()); } diff --git a/android/src/main/java/com/henninghall/date_picker/props/VariantProp.java b/android/src/main/java/com/henninghall/date_picker/props/VariantProp.java new file mode 100644 index 00000000..1e84ec1e --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/props/VariantProp.java @@ -0,0 +1,14 @@ +package com.henninghall.date_picker.props; + +import com.facebook.react.bridge.Dynamic; +import com.henninghall.date_picker.models.Variant; + +public class VariantProp extends Prop { + public static final String name = "androidVariant"; + + @Override + public Variant toValue(Dynamic value){ + return Variant.valueOf(value.asString()); + } + +} \ No newline at end of file diff --git a/android/src/main/java/com/henninghall/date_picker/ui/EmptyWheels.java b/android/src/main/java/com/henninghall/date_picker/ui/EmptyWheels.java deleted file mode 100644 index 0500a63f..00000000 --- a/android/src/main/java/com/henninghall/date_picker/ui/EmptyWheels.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.henninghall.date_picker.ui; - -import android.view.View; - -import com.henninghall.date_picker.R; -import com.henninghall.date_picker.State; - -import java.util.ArrayList; - -import cn.carbswang.android.numberpickerview.library.NumberPickerView; - -class EmptyWheels { - - private final ArrayList views; - private final PickerWrapper pickerWrapper; - private View rootView; - private State state; - - private int[] emptyWheelIds = { - R.id.emptyStart, - R.id.empty1, - R.id.empty2, - R.id.empty3, - R.id.emptyEnd - }; - - EmptyWheels(View rootView, PickerWrapper pickerWrapper, State state) { - this.pickerWrapper = pickerWrapper; - this.rootView = rootView; - this.state = state; - this.views = getAll(); - } - - private ArrayList getAll() { - ArrayList wheels = new ArrayList<>(); - for (int id: emptyWheelIds) { - NumberPickerView view = (NumberPickerView) rootView.findViewById(id); - wheels.add(view); - } - return wheels; - } - - void add() { - int numberOfVisibleWheels = state.derived.getVisibleWheels().size(); - int emptyViewsToAdd = numberOfVisibleWheels + 1; - for (int i = 0; i < emptyViewsToAdd; i++) { - int index = i * 2; - pickerWrapper.addPicker(views.get(i), index); - } - } - - void setShownCount(int shownCount) { - for (int id : emptyWheelIds) { - NumberPickerView view = (NumberPickerView) rootView.findViewById(id); - if(view != null) view.setShownCount(shownCount); - } - } - - -} diff --git a/android/src/main/java/com/henninghall/date_picker/ui/UIManager.java b/android/src/main/java/com/henninghall/date_picker/ui/UIManager.java index 08f88223..49f943ef 100644 --- a/android/src/main/java/com/henninghall/date_picker/ui/UIManager.java +++ b/android/src/main/java/com/henninghall/date_picker/ui/UIManager.java @@ -7,8 +7,10 @@ import com.henninghall.date_picker.wheelFunctions.AnimateToDate; import com.henninghall.date_picker.wheelFunctions.Refresh; import com.henninghall.date_picker.wheelFunctions.SetDate; +import com.henninghall.date_picker.wheelFunctions.SetDividerHeight; import com.henninghall.date_picker.wheelFunctions.TextColor; import com.henninghall.date_picker.wheelFunctions.UpdateVisibility; +import com.henninghall.date_picker.wheelFunctions.HorizontalPadding; import com.henninghall.date_picker.wheels.Wheel; import java.text.SimpleDateFormat; @@ -25,7 +27,6 @@ public UIManager(State state, View rootView){ this.state = state; this.rootView = rootView; wheels = new Wheels(state, rootView); - fadingOverlay = new FadingOverlay(state, rootView); addOnChangeListener(); } @@ -38,6 +39,8 @@ public void updateTextColor(){ } public void updateFadeToColor(){ + if(state.derived.hasNativeStyle()) return; + fadingOverlay = new FadingOverlay(state, rootView); fadingOverlay.updateColor(); } @@ -66,11 +69,12 @@ SimpleDateFormat getDateFormat() { return new SimpleDateFormat(wheels.getFormatPattern(), state.getLocale()); } - String getDateString() { + String getDisplayValueString() { return wheels.getDisplayValue(); } void animateToDate(Calendar date) { + wheels.applyOnInVisible(new SetDate(date)); wheels.applyOnVisible(new AnimateToDate(date)); } @@ -79,5 +83,11 @@ private void addOnChangeListener(){ wheels.applyOnAll(new AddOnChangeListener(onWheelChangeListener)); } + public void updateDividerHeight() { + wheels.updateDividerHeight(); + } + public void updateWheelPadding() { + wheels.applyOnVisible(new HorizontalPadding()); + } } diff --git a/android/src/main/java/com/henninghall/date_picker/ui/WheelChangeListenerImpl.java b/android/src/main/java/com/henninghall/date_picker/ui/WheelChangeListenerImpl.java index b3ab16b4..acc0a89b 100644 --- a/android/src/main/java/com/henninghall/date_picker/ui/WheelChangeListenerImpl.java +++ b/android/src/main/java/com/henninghall/date_picker/ui/WheelChangeListenerImpl.java @@ -6,7 +6,6 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.RCTEventEmitter; import com.henninghall.date_picker.DatePickerManager; -import com.henninghall.date_picker.PickerView; import com.henninghall.date_picker.State; import com.henninghall.date_picker.Utils; import com.henninghall.date_picker.wheels.Wheel; @@ -14,7 +13,6 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; -import java.util.Date; import java.util.TimeZone; public class WheelChangeListenerImpl implements WheelChangeListener { @@ -31,31 +29,96 @@ public class WheelChangeListenerImpl implements WheelChangeListener { this.rootView = rootView; } - @Override - public void onChange(Wheel picker) { - WritableMap event = Arguments.createMap(); + private SimpleDateFormat getDateFormat(){ TimeZone timeZone = state.getTimeZone(); SimpleDateFormat dateFormat = uiManager.getDateFormat(); + dateFormat.setTimeZone(timeZone); + return dateFormat; + } + + @Override + public void onChange(Wheel picker) { + if(wheels.hasSpinningWheel()) return; + + if(!dateExists()){ + Calendar closestExistingDate = getClosestExistingDateInPast(); + if(closestExistingDate != null) { + uiManager.animateToDate(closestExistingDate); + } + return; + } + + Calendar selectedDate = getSelectedDate(); + if(selectedDate == null) return; + Calendar minDate = state.getMinimumDate(); + if (minDate != null && selectedDate.before(minDate)) { + uiManager.animateToDate(minDate); + return; + } + Calendar maxDate = state.getMaximumDate(); + if (maxDate != null && selectedDate.after(maxDate)) { + uiManager.animateToDate(maxDate); + return; + } + + emitDateChangeEvent(selectedDate); + } + + // Example: Jan 1 returns true, April 31 returns false. + private boolean dateExists(){ + SimpleDateFormat dateFormat = getDateFormat(); + String toParse = wheels.getDateTimeString(); try { - dateFormat.setTimeZone(timeZone); - Calendar date = Calendar.getInstance(timeZone); - String toParse = wheels.getDateString(); - Date newDate = dateFormat.parse(toParse); - date.setTime(newDate); - - if (minDate != null && date.before(minDate)) uiManager.animateToDate(minDate); - else if (maxDate != null && date.after(maxDate)) uiManager.animateToDate(maxDate); - else { - event.putString("date", Utils.dateToIso(date)); - event.putString("dateString", uiManager.getDateString()); - DatePickerManager.context.getJSModule(RCTEventEmitter.class) - .receiveEvent(rootView.getId(), "dateChange", event); - } + dateFormat.setLenient(false); // disallow parsing invalid dates + dateFormat.parse(toParse); + return true; + } catch (ParseException e) { + return false; + } + } + + private Calendar getSelectedDate(){ + SimpleDateFormat dateFormat = getDateFormat(); + String toParse = wheels.getDateTimeString(); + TimeZone timeZone = state.getTimeZone(); + Calendar date = Calendar.getInstance(timeZone); + try { + dateFormat.setLenient(true); // allow parsing invalid dates + date.setTime(dateFormat.parse(toParse)); + return date; } catch (ParseException e) { e.printStackTrace(); } + return null; + } + + private Calendar getClosestExistingDateInPast(){ + SimpleDateFormat dateFormat = getDateFormat(); + dateFormat.setLenient(false); // disallow parsing invalid dates + + int maxDaysInPastToCheck = 10; + for (int i = 0; i < maxDaysInPastToCheck; i++){ + try { + String toParse = wheels.getDateTimeString(i); + Calendar calendar = Calendar.getInstance(state.getTimeZone()); + calendar.setTime(dateFormat.parse(toParse)); + return calendar; + } catch (ParseException ignored) { + // continue checking if exception (which means invalid date) + } + } + return null; + } + + private void emitDateChangeEvent(Calendar date) { + WritableMap event = Arguments.createMap(); + String dateString = Utils.dateToIso(date); + event.putString("date", dateString); + event.putString("dateString", uiManager.getDisplayValueString()); + DatePickerManager.context.getJSModule(RCTEventEmitter.class) + .receiveEvent(rootView.getId(), "dateChange", event); } } diff --git a/android/src/main/java/com/henninghall/date_picker/ui/WheelScroller.java b/android/src/main/java/com/henninghall/date_picker/ui/WheelScroller.java index 18f2d731..dc20acf5 100644 --- a/android/src/main/java/com/henninghall/date_picker/ui/WheelScroller.java +++ b/android/src/main/java/com/henninghall/date_picker/ui/WheelScroller.java @@ -1,13 +1,12 @@ package com.henninghall.date_picker.ui; +import com.henninghall.date_picker.pickers.Picker; import com.henninghall.date_picker.wheels.Wheel; -import cn.carbswang.android.numberpickerview.library.NumberPickerView; - public class WheelScroller { public void scroll(Wheel wheel, int scrollTimes) { - NumberPickerView picker = wheel.picker; + Picker picker = wheel.picker; int currentIndex = picker.getValue(); int maxValue = picker.getMaxValue(); boolean isWrapping = picker.getWrapSelectorWheel(); diff --git a/android/src/main/java/com/henninghall/date_picker/ui/Wheels.java b/android/src/main/java/com/henninghall/date_picker/ui/Wheels.java index 3b5a700a..db1c8322 100644 --- a/android/src/main/java/com/henninghall/date_picker/ui/Wheels.java +++ b/android/src/main/java/com/henninghall/date_picker/ui/Wheels.java @@ -2,11 +2,13 @@ import android.view.View; +import com.henninghall.date_picker.models.Variant; +import com.henninghall.date_picker.pickers.Picker; import com.henninghall.date_picker.R; import com.henninghall.date_picker.State; -import com.henninghall.date_picker.Utils; import com.henninghall.date_picker.models.WheelType; import com.henninghall.date_picker.models.Mode; +import com.henninghall.date_picker.wheelFunctions.SetDividerHeight; import com.henninghall.date_picker.wheelFunctions.SetShowCount; import com.henninghall.date_picker.wheelFunctions.WheelFunction; import com.henninghall.date_picker.wheels.AmPmWheel; @@ -20,7 +22,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -29,6 +30,8 @@ public class Wheels { private final State state; + private final NumberPickerView emptyStart; + private final NumberPickerView emptyEnd; private HourWheel hourWheel; private DayWheel dayWheel; private MinutesWheel minutesWheel; @@ -38,7 +41,6 @@ public class Wheels { private YearWheel yearWheel; private View rootView; private final PickerWrapper pickerWrapper; - private final EmptyWheels emptyWheels; private HashMap wheelPerWheelType; @@ -54,23 +56,14 @@ public class Wheels { minutesWheel = new MinutesWheel(getPickerWithId(R.id.minutes), state); ampmWheel = new AmPmWheel(getPickerWithId(R.id.ampm), state); hourWheel = new HourWheel(getPickerWithId(R.id.hour), state); + emptyStart = (NumberPickerView) rootView.findViewById(R.id.empty_start); + emptyEnd = (NumberPickerView) rootView.findViewById(R.id.empty_end); wheelPerWheelType = getWheelPerType(); - - emptyWheels = new EmptyWheels(rootView, pickerWrapper, state); changeAmPmWhenPassingMidnightOrNoon(); } - private NumberPickerView getPickerWithId(int id){ - return (NumberPickerView) rootView.findViewById(id); - } - - private Collection getVisible() { - ArrayList wheelTypes = state.derived.getVisibleWheels(); - Collection wheels = new ArrayList<>(); - for (WheelType type: wheelTypes){ - wheels.add(getWheel(type)); - } - return wheels; + private Picker getPickerWithId(int id){ + return (Picker) rootView.findViewById(id); } void applyOnAll(WheelFunction function) { @@ -78,38 +71,81 @@ void applyOnAll(WheelFunction function) { } void applyOnVisible(WheelFunction function) { - for (Wheel wheel: getVisible()) function.apply(wheel); + for(Wheel wheel: getAll()) { + if(wheel.visible()) function.apply(wheel); + } + } + + void applyOnInVisible(WheelFunction function) { + for(Wheel wheel: getAll()) { + if(!wheel.visible()) function.apply(wheel); + } } void updateHeight() { int shownCount = state.derived.getShownCount(); applyOnAll(new SetShowCount(shownCount)); - emptyWheels.setShownCount(shownCount); + if(state.getVariant() == Variant.iosClone) { + emptyStart.setShownCount(shownCount); + emptyEnd.setShownCount(shownCount); + } + } + + void updateDividerHeight() { + int height = state.getDividerHeight(); + applyOnAll(new SetDividerHeight(height)); + if(state.getVariant() == Variant.iosClone) { + emptyStart.setDividerHeight(height); + emptyEnd.setDividerHeight(height); + } } void updateWheelOrder() { pickerWrapper.removeAll(); + if(state.getVariant() == Variant.iosClone) pickerWrapper.addPicker(emptyStart); addInOrder(); - addEmpty(); + if(state.getVariant() == Variant.iosClone) pickerWrapper.addPicker(emptyEnd); } Wheel getWheel(WheelType type){ return wheelPerWheelType.get(type); } - String getDateString() { + String getDateTimeString(int daysToSubtract) { + return getDateString(daysToSubtract) + " " + getTimeString(); + } + + private String getDateModeString(int daysToSubtract) { ArrayList wheels = getOrderedVisibleWheels(); - String dateString = (state.getMode() == Mode.date) - ? wheels.get(0).getValue() + " " - + wheels.get(1).getValue() + " " - + wheels.get(2).getValue() - : dayWheel.getValue(); - return dateString - + " " + hourWheel.getValue() + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 3; i++) { + if (i != 0) sb.append(" "); + Wheel w = wheels.get(i); + if (w instanceof DateWheel) { + sb.append(w.getPastValue(daysToSubtract)); + } + else sb.append(w.getValue()); + } + return sb.toString(); + } + + private String getDateString(int daysToSubtract){ + if(state.getMode() == Mode.date ){ + return getDateModeString(daysToSubtract); + } + return dayWheel.getValue(); + } + + private String getTimeString(){ + return hourWheel.getValue() + " " + minutesWheel.getValue() + ampmWheel.getValue(); } + String getDateTimeString() { + return getDateTimeString(0); + } + String getDisplayValue() { StringBuilder sb = new StringBuilder(); for (Wheel wheel: getOrderedVisibleWheels()) { @@ -122,7 +158,7 @@ private void addInOrder(){ ArrayList wheels = state.derived.getOrderedVisibleWheels(); for (WheelType wheelType : wheels) { Wheel wheel = getWheel(wheelType); - pickerWrapper.addPicker(wheel.picker); + pickerWrapper.addPicker(wheel.picker.getView()); } } @@ -135,10 +171,10 @@ private ArrayList getOrderedVisibleWheels(){ } private void changeAmPmWhenPassingMidnightOrNoon() { - hourWheel.picker.setOnValueChangeListenerInScrolling(new NumberPickerView.OnValueChangeListenerInScrolling() { + hourWheel.picker.setOnValueChangeListenerInScrolling(new Picker.OnValueChangeListenerInScrolling() { @Override - public void onValueChangeInScrolling(NumberPickerView picker, int oldVal, int newVal) { - if(Utils.usesAmPm()){ + public void onValueChangeInScrolling(Picker picker, int oldVal, int newVal) { + if(state.derived.usesAmPm()){ String oldValue = hourWheel.getValueAtIndex(oldVal); String newValue = hourWheel.getValueAtIndex(newVal); boolean passingNoonOrMidnight = (oldValue.equals("12") && newValue.equals("11")) || oldValue.equals("11") && newValue.equals("12"); @@ -181,8 +217,10 @@ private HashMap getWheelPerType(){ }}; } - private void addEmpty() { - emptyWheels.add(); + public boolean hasSpinningWheel() { + for(Wheel wheel: getAll()) { + if(wheel.picker.isSpinning()) return true; + } + return false; } - } diff --git a/android/src/main/java/com/henninghall/date_picker/wheelFunctions/AddOnChangeListener.java b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/AddOnChangeListener.java index 7b7f67d9..af9e4417 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheelFunctions/AddOnChangeListener.java +++ b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/AddOnChangeListener.java @@ -1,5 +1,6 @@ package com.henninghall.date_picker.wheelFunctions; +import com.henninghall.date_picker.pickers.Picker; import com.henninghall.date_picker.ui.WheelChangeListener; import com.henninghall.date_picker.wheels.Wheel; @@ -15,9 +16,9 @@ public AddOnChangeListener(WheelChangeListener onChangeListener){ @Override public void apply(final Wheel wheel) { - wheel.picker.setOnValueChangedListener(new NumberPickerView.OnValueChangeListener() { + wheel.picker.setOnValueChangedListener(new Picker.OnValueChangeListener() { @Override - public void onValueChange(NumberPickerView picker, int oldVal, int newVal) { + public void onValueChange() { onChangeListener.onChange(wheel); } }); diff --git a/android/src/main/java/com/henninghall/date_picker/wheelFunctions/HorizontalPadding.java b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/HorizontalPadding.java new file mode 100644 index 00000000..18a27e27 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/HorizontalPadding.java @@ -0,0 +1,15 @@ +package com.henninghall.date_picker.wheelFunctions; + +import com.henninghall.date_picker.wheels.Wheel; + +import java.util.Calendar; + +public class HorizontalPadding implements WheelFunction { + + @Override + public void apply(Wheel wheel) { + wheel.setHorizontalPadding(); + } +} + + diff --git a/android/src/main/java/com/henninghall/date_picker/wheelFunctions/SetDividerHeight.java b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/SetDividerHeight.java new file mode 100644 index 00000000..91ea2b53 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/SetDividerHeight.java @@ -0,0 +1,16 @@ +package com.henninghall.date_picker.wheelFunctions; + +import com.henninghall.date_picker.wheels.Wheel; + +public class SetDividerHeight implements WheelFunction { + private final int height; + + public SetDividerHeight(int height) { + this.height = height; + } + + @Override + public void apply(Wheel wheel) { + wheel.picker.setDividerHeight(height); + } +} diff --git a/android/src/main/java/com/henninghall/date_picker/wheels/AmPmWheel.java b/android/src/main/java/com/henninghall/date_picker/wheels/AmPmWheel.java index 97a686c6..233e416c 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheels/AmPmWheel.java +++ b/android/src/main/java/com/henninghall/date_picker/wheels/AmPmWheel.java @@ -2,6 +2,7 @@ import android.graphics.Paint; +import com.henninghall.date_picker.pickers.Picker; import com.henninghall.date_picker.State; import com.henninghall.date_picker.Utils; import com.henninghall.date_picker.models.Mode; @@ -9,12 +10,10 @@ import java.util.ArrayList; import java.util.Calendar; -import cn.carbswang.android.numberpickerview.library.NumberPickerView; - public class AmPmWheel extends Wheel { - public AmPmWheel(final NumberPickerView picker, State state) { + public AmPmWheel(final Picker picker, State state) { super(picker, state); } @@ -33,17 +32,22 @@ public ArrayList getValues() { @Override public boolean visible() { - return Utils.usesAmPm() && state.getMode() != Mode.date; + return state.derived.usesAmPm() && state.getMode() != Mode.date; + } + + @Override + public boolean wrapSelectorWheel() { + return false; } @Override public String getFormatPattern() { - return Utils.usesAmPm() ? " a " : ""; + return state.derived.usesAmPm() ? " a " : ""; } @Override public Paint.Align getTextAlign() { - return Paint.Align.LEFT; + return Paint.Align.RIGHT; } } diff --git a/android/src/main/java/com/henninghall/date_picker/wheels/DateWheel.java b/android/src/main/java/com/henninghall/date_picker/wheels/DateWheel.java index b875521a..6486fd2f 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheels/DateWheel.java +++ b/android/src/main/java/com/henninghall/date_picker/wheels/DateWheel.java @@ -5,12 +5,11 @@ import java.util.*; import com.henninghall.date_picker.*; import com.henninghall.date_picker.models.Mode; - -import cn.carbswang.android.numberpickerview.library.NumberPickerView; +import com.henninghall.date_picker.pickers.Picker; public class DateWheel extends Wheel { - public DateWheel(final NumberPickerView picker, final State id) { + public DateWheel(final Picker picker, final State id) { super(picker, id); } @@ -20,7 +19,7 @@ public ArrayList getValues() { Calendar cal = Calendar.getInstance(); ArrayList values = new ArrayList<>(); cal.set(Calendar.MONTH, 0); - cal.set(Calendar.DATE, 0); + cal.set(Calendar.DATE, 1); final int maxDate = 31; final int minDate = 1; for (int i = minDate; i <= maxDate; ++i) { @@ -35,9 +34,14 @@ public boolean visible() { return state.getMode() == Mode.date; } + @Override + public boolean wrapSelectorWheel() { + return true; + } + @Override public String getFormatPattern() { - return LocaleUtils.getPatternIncluding("d", state.getLocale()); + return LocaleUtils.getDate(state.getLocaleLanguageTag()); } @Override diff --git a/android/src/main/java/com/henninghall/date_picker/wheels/DayWheel.java b/android/src/main/java/com/henninghall/date_picker/wheels/DayWheel.java index 59af7d7c..308eea59 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheels/DayWheel.java +++ b/android/src/main/java/com/henninghall/date_picker/wheels/DayWheel.java @@ -1,23 +1,19 @@ package com.henninghall.date_picker.wheels; import android.graphics.Paint; -import android.text.TextUtils; -import com.henninghall.date_picker.DayFormats; import com.henninghall.date_picker.LocaleUtils; +import com.henninghall.date_picker.pickers.Picker; import com.henninghall.date_picker.State; import com.henninghall.date_picker.models.Mode; import com.henninghall.date_picker.Utils; -import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; -import cn.carbswang.android.numberpickerview.library.NumberPickerView; - public class DayWheel extends Wheel { @@ -25,7 +21,7 @@ public class DayWheel extends Wheel { private static int defaultNumberOfDays = Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_YEAR); private HashMap displayValues; - public DayWheel(NumberPickerView picker, State state) { + public DayWheel(Picker picker, State state) { super(picker, state); } @@ -101,7 +97,7 @@ private String getDisplayValue(Calendar cal){ } private String getDisplayValueFormatPattern(){ - return DayFormats.get(state.getLocaleLanguageTag()); + return LocaleUtils.getDay(state.getLocaleLanguageTag()); } private SimpleDateFormat getDisplayValueFormat(){ @@ -113,12 +109,17 @@ public boolean visible() { return state.getMode() == Mode.datetime; } + @Override + public boolean wrapSelectorWheel() { + return false; + } + @Override public String getFormatPattern() { - return LocaleUtils.getDatePattern(state.getLocale()) - .replace("EEEE", "EEE") - .replace("MMMM", "MMM"); + return LocaleUtils.getDatePattern(state.getLocale()) + .replace("EEEE", "EEE") + .replace("MMMM", "MMM"); } @Override diff --git a/android/src/main/java/com/henninghall/date_picker/wheels/HourWheel.java b/android/src/main/java/com/henninghall/date_picker/wheels/HourWheel.java index 2ce30f35..4b51e1b5 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheels/HourWheel.java +++ b/android/src/main/java/com/henninghall/date_picker/wheels/HourWheel.java @@ -2,6 +2,7 @@ import android.graphics.Paint; +import com.henninghall.date_picker.pickers.Picker; import com.henninghall.date_picker.State; import com.henninghall.date_picker.Utils; import com.henninghall.date_picker.models.Mode; @@ -9,11 +10,9 @@ import java.util.ArrayList; import java.util.Calendar; -import cn.carbswang.android.numberpickerview.library.NumberPickerView; - public class HourWheel extends Wheel { - public HourWheel(NumberPickerView picker, State id) { + public HourWheel(Picker picker, State id) { super(picker, id); } @@ -21,7 +20,7 @@ public HourWheel(NumberPickerView picker, State id) { public ArrayList getValues() { Calendar cal = Calendar.getInstance(); ArrayList values = new ArrayList<>(); - int numberOfHours = Utils.usesAmPm() ? 12 : 24; + int numberOfHours = state.derived.usesAmPm() ? 12 : 24; for(int i=0; i getValues() { ArrayList values = new ArrayList<>(); @@ -33,6 +32,11 @@ public boolean visible() { return state.getMode() == Mode.date; } + @Override + public boolean wrapSelectorWheel() { + return true; + } + @Override public String getFormatPattern() { return "LLLL"; @@ -43,5 +47,10 @@ public Paint.Align getTextAlign() { return Paint.Align.LEFT; } + @Override + public int getHorizontalPadding() { + return 1; + } + } \ No newline at end of file diff --git a/android/src/main/java/com/henninghall/date_picker/wheels/Wheel.java b/android/src/main/java/com/henninghall/date_picker/wheels/Wheel.java index 671e009d..ee690d8a 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheels/Wheel.java +++ b/android/src/main/java/com/henninghall/date_picker/wheels/Wheel.java @@ -3,7 +3,8 @@ import android.graphics.Paint; import android.view.View; -import cn.carbswang.android.numberpickerview.library.NumberPickerView; +import com.henninghall.date_picker.models.Mode; +import com.henninghall.date_picker.pickers.Picker; import com.henninghall.date_picker.State; import java.text.SimpleDateFormat; @@ -17,6 +18,7 @@ public abstract class Wheel { private Calendar userSetValue; public abstract boolean visible(); + public abstract boolean wrapSelectorWheel(); public abstract Paint.Align getTextAlign(); public abstract String getFormatPattern(); public abstract ArrayList getValues(); @@ -26,14 +28,15 @@ public String toDisplayValue(String value) { } private ArrayList values = new ArrayList<>(); - public NumberPickerView picker; + public Picker picker; public SimpleDateFormat format; - public Wheel(NumberPickerView picker, State state) { + public Wheel(Picker picker, State state) { this.state = state; this.picker = picker; this.format = new SimpleDateFormat(getFormatPattern(), state.getLocale()); picker.setTextAlign(getTextAlign()); + picker.setWrapSelectorWheel(wrapSelectorWheel()); } private int getIndexOfDate(Calendar date){ @@ -50,6 +53,14 @@ public String getValue() { return getValueAtIndex(getIndex()); } + public String getPastValue(int subtractIndex) { + if(!visible()) return format.format(userSetValue.getTime()); + int size = values.size(); + int pastValueIndex = (getIndex() + size - subtractIndex) % size; + return getValueAtIndex(pastValueIndex); + } + + private int getIndex() { return picker.getValue(); } @@ -113,4 +124,19 @@ private String getString(Calendar cal, Locale locale){ return getFormat(locale).format(cal.getTime()); } + public void setHorizontalPadding(){ + picker.setItemPaddingHorizontal(getHorizontalPadding()); + } + + public int getHorizontalPadding() { + Mode mode = state.getMode(); + if(state.derived.hasOnly2Wheels()) return 10; + switch (mode){ + case date: return 15; + case time: + case datetime: + default: + return 5; + } + } } diff --git a/android/src/main/java/com/henninghall/date_picker/wheels/YearWheel.java b/android/src/main/java/com/henninghall/date_picker/wheels/YearWheel.java index ceb68636..41e0427a 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheels/YearWheel.java +++ b/android/src/main/java/com/henninghall/date_picker/wheels/YearWheel.java @@ -3,21 +3,19 @@ import android.graphics.Paint; import com.henninghall.date_picker.LocaleUtils; +import com.henninghall.date_picker.pickers.Picker; import com.henninghall.date_picker.State; import com.henninghall.date_picker.models.Mode; -import com.henninghall.date_picker.PickerView; import java.util.ArrayList; import java.util.Calendar; -import cn.carbswang.android.numberpickerview.library.NumberPickerView; - public class YearWheel extends Wheel { private int defaultStartYear; private int defaultEndYear; - public YearWheel(final NumberPickerView picker, final State id) { + public YearWheel(final Picker picker, final State id) { super(picker, id); this.defaultStartYear = 1900; this.defaultEndYear = 2100; @@ -61,6 +59,11 @@ public boolean visible() { return state.getMode() == Mode.date; } + @Override + public boolean wrapSelectorWheel() { + return false; + } + @Override public Paint.Align getTextAlign() { return Paint.Align.RIGHT; @@ -68,7 +71,7 @@ public Paint.Align getTextAlign() { @Override public String getFormatPattern() { - return LocaleUtils.getPatternIncluding("y", state.getLocale()); + return LocaleUtils.getYear(state.getLocaleLanguageTag()); } } diff --git a/android/src/main/res/layout/datepicker_view.xml b/android/src/main/res/layout/ios_clone.xml similarity index 57% rename from android/src/main/res/layout/datepicker_view.xml rename to android/src/main/res/layout/ios_clone.xml index ed71829e..feb88e91 100644 --- a/android/src/main/res/layout/datepicker_view.xml +++ b/android/src/main/res/layout/ios_clone.xml @@ -11,24 +11,20 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" + android:gravity="center_horizontal" > - - - - + + - - - - - - - + /> - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/main/res/values/styles.xml b/android/src/main/res/values/styles.xml index dd946c24..915f8232 100644 --- a/android/src/main/res/values/styles.xml +++ b/android/src/main/res/values/styles.xml @@ -1,10 +1,17 @@ - - + + \ No newline at end of file diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000..4bb705d9 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,12 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: 'current', + }, + }, + ], + ], +} diff --git a/defaultProps.js b/defaultProps.js deleted file mode 100644 index 73372a3e..00000000 --- a/defaultProps.js +++ /dev/null @@ -1,4 +0,0 @@ -export default { - mode: 'datetime', - minuteInterval: 1, -} \ No newline at end of file diff --git a/docs/react-native-date-picker-android-native.gif b/docs/react-native-date-picker-android-native.gif new file mode 100644 index 00000000..756cc663 Binary files /dev/null and b/docs/react-native-date-picker-android-native.gif differ diff --git a/docs/react-native-date-picker-android.png b/docs/react-native-date-picker-android.png new file mode 100644 index 00000000..2663890e Binary files /dev/null and b/docs/react-native-date-picker-android.png differ diff --git a/examples/detox/android/app/build.gradle b/examples/detox/android/app/build.gradle index 044df63c..fa690936 100644 --- a/examples/detox/android/app/build.gradle +++ b/examples/detox/android/app/build.gradle @@ -78,6 +78,7 @@ dependencies { androidTestImplementation('com.wix:detox:+') { transitive = true } androidTestImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.12' } // Run this once to be able to run the application with BUCK diff --git a/examples/detox/android/app/src/main/res/values/styles.xml b/examples/detox/android/app/src/main/res/values/styles.xml index 319eb0ca..01b31e7e 100644 --- a/examples/detox/android/app/src/main/res/values/styles.xml +++ b/examples/detox/android/app/src/main/res/values/styles.xml @@ -3,6 +3,9 @@ diff --git a/examples/detox/android/app/src/test/java/ShortestScrollOption.java b/examples/detox/android/app/src/test/java/ShortestScrollOption.java new file mode 100644 index 00000000..85aff8ef --- /dev/null +++ b/examples/detox/android/app/src/test/java/ShortestScrollOption.java @@ -0,0 +1,60 @@ + +import com.henninghall.date_picker.Utils; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ShortestScrollOption { + + @Test + public void decreaseOne() { + assertEquals( -1, Utils.getShortestScrollOption(1, 0, 10, false)); + } + + @Test + public void increaseOne() { + assertEquals(1, Utils.getShortestScrollOption(0, 1, 10, false)); + } + + @Test + public void increaseFive() { + assertEquals( 5, Utils.getShortestScrollOption(0, 5, 10, false)); + } + + @Test + public void noChange() { + assertEquals( 0, Utils.getShortestScrollOption(0, 0, 10, false)); + } + + @Test + public void noWrapping() { + assertEquals( 10, Utils.getShortestScrollOption(0, 10, 10, false)); + } + + @Test + public void wrapping() { + assertEquals( -1, Utils.getShortestScrollOption(0, 10, 10, true)); + } + + @Test + public void findingClosestByIncreaseNoWrap() { + assertEquals( 4, Utils.getShortestScrollOption(0, 4, 9, true)); + } + + @Test + public void findingClosestByIncreaseWrap() { + assertEquals( 4, Utils.getShortestScrollOption(6, 0, 9, true)); + } + + @Test + public void findingClosestByDecreaseNoWrap() { + assertEquals( -4, Utils.getShortestScrollOption(5, 1, 9, true)); + } + + @Test + public void findingClosestByDecreaseWrap() { + assertEquals( -4, Utils.getShortestScrollOption(0, 6, 9, true)); + } + +} \ No newline at end of file diff --git a/examples/detox/android/build.gradle b/examples/detox/android/build.gradle index 9b95715d..bc673633 100644 --- a/examples/detox/android/build.gradle +++ b/examples/detox/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlinVersion = '1.3.0' + ext.kotlinVersion = '1.3.10' ext.compileSdkVersion = 28 ext.buildToolsVersion = '28.0.3' @@ -10,7 +10,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:4.0.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" } } diff --git a/examples/detox/android/gradle.properties b/examples/detox/android/gradle.properties index fd8bcba9..d32a03a3 100644 --- a/examples/detox/android/gradle.properties +++ b/examples/detox/android/gradle.properties @@ -18,3 +18,4 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro # org.gradle.parallel=true android.useDeprecatedNdk=true +android.useAndroidX=true \ No newline at end of file diff --git a/examples/detox/android/gradle/wrapper/gradle-wrapper.properties b/examples/detox/android/gradle/wrapper/gradle-wrapper.properties index 940d143f..5327ab77 100644 --- a/examples/detox/android/gradle/wrapper/gradle-wrapper.properties +++ b/examples/detox/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jun 03 11:38:02 IDT 2019 +#Sun Jul 12 11:24:40 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/examples/detox/e2e/tests/24h-mode/hourWheel.spec.js b/examples/detox/e2e/tests/24h-mode/hourWheel.spec.js index e1a86c0a..a473227a 100644 --- a/examples/detox/e2e/tests/24h-mode/hourWheel.spec.js +++ b/examples/detox/e2e/tests/24h-mode/hourWheel.spec.js @@ -1,28 +1,24 @@ -const { scrollWheel, expectDate } = require("../../utils") - +const { scrollWheel, expectDate } = require('../../utils') describe('Hour wheel', () => { + before(async () => { + await device.reloadReactNative() + await element(by.text('Advanced')).tap() + }) - before(async () => { - await device.reloadReactNative() - await element(by.text('Advanced')).tap() - }) - - it('should have 24 hours', async () => { - await scroll3HoursAndExpect("2000-01-01 03:00:00") - await scroll3HoursAndExpect("2000-01-01 06:00:00") - await scroll3HoursAndExpect("2000-01-01 09:00:00") - await scroll3HoursAndExpect("2000-01-01 12:00:00") - await scroll3HoursAndExpect("2000-01-01 15:00:00") - await scroll3HoursAndExpect("2000-01-01 18:00:00") - await scroll3HoursAndExpect("2000-01-01 21:00:00") - await scroll3HoursAndExpect("2000-01-01 00:00:00") - }) - - const scroll3HoursAndExpect = async (date) => { - await scrollWheel(1, 3) - await expectDate(date) - } + it('should have 24 hours', async () => { + await scroll3HoursAndExpect('2000-01-01 03:00:00') + await scroll3HoursAndExpect('2000-01-01 06:00:00') + await scroll3HoursAndExpect('2000-01-01 09:00:00') + await scroll3HoursAndExpect('2000-01-01 12:00:00') + await scroll3HoursAndExpect('2000-01-01 15:00:00') + await scroll3HoursAndExpect('2000-01-01 18:00:00') + await scroll3HoursAndExpect('2000-01-01 21:00:00') + await scroll3HoursAndExpect('2000-01-01 00:00:00') + }) + const scroll3HoursAndExpect = async date => { + await scrollWheel(1, 3) + await expectDate(date) + } }) - diff --git a/examples/detox/e2e/tests/24h-mode/mode.spec.js b/examples/detox/e2e/tests/24h-mode/mode.spec.js index d22241d5..72296e96 100644 --- a/examples/detox/e2e/tests/24h-mode/mode.spec.js +++ b/examples/detox/e2e/tests/24h-mode/mode.spec.js @@ -1,49 +1,47 @@ -const { setMode } = require("../../utils") +const { setMode } = require('../../utils') describe('Modes - 24h', () => { - - before(async () => { - await device.reloadReactNative() - await element(by.text('Advanced')).tap() - }) - - it('datetime', async () => { - await setMode("datetime") - - await expect(element(by.id('day'))).toBeVisible() - await expect(element(by.id('minutes'))).toBeVisible() - await expect(element(by.id('hour'))).toBeVisible() - - await expect(element(by.id('ampm'))).toNotExist() - await expect(element(by.id('month'))).toNotExist() - await expect(element(by.id('date'))).toNotExist() - await expect(element(by.id('year'))).toNotExist() - }) - - it('date', async () => { - await setMode("date") - - await expect(element(by.id('month'))).toBeVisible() - await expect(element(by.id('date'))).toBeVisible() - await expect(element(by.id('year'))).toBeVisible() - - await expect(element(by.id('day'))).toNotExist() - await expect(element(by.id('minutes'))).toNotExist() - await expect(element(by.id('hour'))).toNotExist() - await expect(element(by.id('ampm'))).toNotExist() - }) - - it('time', async () => { - await setMode("time") - - await expect(element(by.id('minutes'))).toBeVisible() - await expect(element(by.id('hour'))).toBeVisible() - - await expect(element(by.id('ampm'))).toNotExist() - await expect(element(by.id('day'))).toNotExist() - await expect(element(by.id('month'))).toNotExist() - await expect(element(by.id('date'))).toNotExist() - await expect(element(by.id('year'))).toNotExist() - }) - + before(async () => { + await device.reloadReactNative() + await element(by.text('Advanced')).tap() + }) + + it('datetime', async () => { + await setMode('datetime') + + await expect(element(by.id('day'))).toBeVisible() + await expect(element(by.id('minutes'))).toBeVisible() + await expect(element(by.id('hour'))).toBeVisible() + + await expect(element(by.id('ampm'))).toNotExist() + await expect(element(by.id('month'))).toNotExist() + await expect(element(by.id('date'))).toNotExist() + await expect(element(by.id('year'))).toNotExist() + }) + + it('date', async () => { + await setMode('date') + + await expect(element(by.id('month'))).toBeVisible() + await expect(element(by.id('date'))).toBeVisible() + await expect(element(by.id('year'))).toBeVisible() + + await expect(element(by.id('day'))).toNotExist() + await expect(element(by.id('minutes'))).toNotExist() + await expect(element(by.id('hour'))).toNotExist() + await expect(element(by.id('ampm'))).toNotExist() + }) + + it('time', async () => { + await setMode('time') + + await expect(element(by.id('minutes'))).toBeVisible() + await expect(element(by.id('hour'))).toBeVisible() + + await expect(element(by.id('ampm'))).toNotExist() + await expect(element(by.id('day'))).toNotExist() + await expect(element(by.id('month'))).toNotExist() + await expect(element(by.id('date'))).toNotExist() + await expect(element(by.id('year'))).toNotExist() + }) }) diff --git a/examples/detox/e2e/tests/displayText.spec.js b/examples/detox/e2e/tests/displayText.spec.js index 78c8e73e..0981056e 100644 --- a/examples/detox/e2e/tests/displayText.spec.js +++ b/examples/detox/e2e/tests/displayText.spec.js @@ -14,6 +14,9 @@ describe('Display text', () => { }) describe('datetime', () => { + it('vi-VN', async () => { + await expectLocaleDateString('vi-VN', 'CN 2 thg 1' + '1200 SA ') + }) it('en-US', async () => { await expectLocaleDateString('en-US', 'Sun Jan 2' + '1200 AM ') }) @@ -34,9 +37,17 @@ describe('Display text', () => { await expectLocaleDateString('ko', '1월 2일 일 오전 1200') }) + it('ja', async () => { + await expectLocaleDateString('ja', '1月2日 日' + '1200 午前 ') + }) + it('zh-CH', async () => { await expectLocaleDateString('zh-CH', '1月2日周日 上午 1200') }) + + it('eu', async () => { + await expectLocaleDateString('eu', 'urt. 2 ig.' + ' AM 1200') + }) }) describe('date', () => { @@ -71,6 +82,10 @@ describe('Display text', () => { it('zh-CH', async () => { await expectLocaleDateString('zh-CH', '2001年' + '一月' + '1日') }) + + it('eu', async () => { + await expectLocaleDateString('eu', '2001' + 'urtarrila' + '1') + }) }) const expectLocaleDateString = async (locale, dateString) => { diff --git a/examples/detox/e2e/tests/invalidDates.spec.js b/examples/detox/e2e/tests/invalidDates.spec.js new file mode 100644 index 00000000..affa4c3d --- /dev/null +++ b/examples/detox/e2e/tests/invalidDates.spec.js @@ -0,0 +1,52 @@ +const { scrollWheel, expectDate, setDate, setMode, setMaximumDate, setMinimumDate } = require('../utils') + +const scrollDays = (days) => scrollWheel(1, days) + +describe('Invalid dates', () => { + before(async () => { + await device.reloadReactNative() + await element(by.text('Advanced')).tap() + await setMinimumDate(undefined) + await setMaximumDate(undefined) + await setMode('date') + }) + + + it('scrolls back to last valid date', async () => { + await setDate(new Date("2001-02-28 00:00")) + await scrollDays(1) + await expectDate('2001-02-28 00:00:00') + }) + + it('scrolls back after scrolling multiple dates', async () => { + await setDate(new Date("2001-02-27 00:00")) + await scrollDays(2) + await expectDate('2001-02-28 00:00:00') + }) + + it('not scrolling back on unusual valid dates', async () => { + await setDate(new Date("2000-02-28 00:00")) + await scrollDays(1) + await expectDate('2000-02-29 00:00:00') + }) + + it('not scrolling back after scrolling past invalid dates', async () => { + await setDate(new Date("2001-02-28 00:00")) + await scrollDays(4) + await expectDate('2001-02-01 00:00:00') + }) + + it('works on months with 30 days', async () => { + await setDate(new Date("2001-04-30 00:00")) + await scrollDays(1) + await expectDate('2001-04-30 00:00:00') + }) + + it('works on months with 31 days', async () => { + await setDate(new Date("2001-05-30 00:00")) + await scrollDays(1) + await expectDate('2001-05-31 00:00:00') + }) + + +}) diff --git a/examples/detox/e2e/tests/maximumDate.spec.js b/examples/detox/e2e/tests/maximumDate.spec.js index b307d0d7..c9c188ab 100644 --- a/examples/detox/e2e/tests/maximumDate.spec.js +++ b/examples/detox/e2e/tests/maximumDate.spec.js @@ -1,131 +1,116 @@ -const { scrollWheel, expectDate, setMaximumDate, setMode } = require("../utils") +const { scrollWheel, expectDate, setMaximumDate, setMode } = require('../utils') -const initialDate = new Date(2000, 0, 1, 0, 0); -const secondOfJanuary = new Date(2000, 0, 2, 0, 0); -const secondOfJanuary2001 = new Date(2001, 0, 2, 0, 0); +const initialDate = new Date(2000, 0, 1, 0, 0) +const secondOfJanuary = new Date(2000, 0, 2, 0, 0) +const secondOfJanuary2001 = new Date(2001, 0, 2, 0, 0) describe('Maximum date', () => { + before(async () => { + await device.reloadReactNative() + await element(by.text('Advanced')).tap() + }) + describe('cannot pass max date - datetime mode', () => { before(async () => { - await device.reloadReactNative() - await element(by.text('Advanced')).tap() + await setMode('datetime') + await setMaximumDate(initialDate) }) - describe('cannot pass max date - datetime mode', () => { - - before(async () => { - await setMode("datetime") - await setMaximumDate(initialDate) - }) - - it('day wheel', async () => { - await scrollWheel(0, 2) - await expectDate("2000-01-01 00:00:00") - }) - - it('hour wheel', async () => { - await scrollWheel(1, 1) - await expectDate("2000-01-01 00:00:00") - }) - - it('minute wheel', async () => { - await scrollWheel(2, 1) - await expectDate("2000-01-01 00:00:00") - }) - + it('day wheel', async () => { + await scrollWheel(0, 2) + await expectDate('2000-01-01 00:00:00') }) - describe('cannot pass max date - date mode', () => { - - before(async () => { - await setMode("date") - await setMaximumDate(initialDate) - }) - - it('month wheel', async () => { - await scrollWheel(0, 1) - await expectDate("2000-01-01 00:00:00") - }) - - it('date wheel', async () => { - await scrollWheel(1, 1) - await expectDate("2000-01-01 00:00:00") - }) - - it('year wheel', async () => { - await scrollWheel(2, 1) - await expectDate("2000-01-01 00:00:00") - }) - + it('hour wheel', async () => { + await scrollWheel(1, 1) + await expectDate('2000-01-01 00:00:00') }) + it('minute wheel', async () => { + await scrollWheel(2, 1) + await expectDate('2000-01-01 00:00:00') + }) + }) - describe('overshooting max date', () => { - - before(async () => { - await setMaximumDate(secondOfJanuary) - }) - - it('day wheel should not be possible to overshoot since it is not wrapping (no invalid dates exists)', async () => { - await setMode("datetime") - await scrollWheel(0, 1) - await expectDate("2000-01-02 00:00:00") - await scrollWheel(0, 1) - await expectDate("2000-01-02 00:00:00") - }) - - describe('date mode', () => { - - before(async () => { - await setMode("date") - await setMaximumDate(secondOfJanuary) - }) - - it('overshooting month wheel should set all other wheels to maximum possible date', async () => { - await scrollWheel(0, 1) - await expectDate("2000-01-02 00:00:00") - }) - - it('overshooting date wheel should reverse to highest possible date', async () => { - await scrollWheel(1, 5) - await expectDate("2000-01-02 00:00:00") - }) - - it('overshooting year wheel should set all other wheels to maximum possible date', async () => { - await setMaximumDate(secondOfJanuary2001) - await scrollWheel(0, 1) // set month to feb - await scrollWheel(2, 1) - await expectDate("2001-01-02 00:00:00") - }) - - }) - - describe('time mode', () => { - - before(async () => { - await setMode("time") - await setMaximumDate(initialDate) - }) - - it('overshooting hour wheel should reverse to highest possible time', async () => { - await scrollWheel(0, 5) - await expectDate("2000-01-01 00:00:00") - }) + describe('cannot pass max date - date mode', () => { + before(async () => { + await setMode('date') + await setMaximumDate(initialDate) + }) - it('overshooting minute wheel should reverse to highest possible time', async () => { - await scrollWheel(1, 5) - await expectDate("2000-01-01 00:00:00") - }) + it('month wheel', async () => { + await scrollWheel(0, 1) + await expectDate('2000-01-01 00:00:00') + }) - it('overshooting am/pm wheel should reverse to highest possible time', async () => { - await scrollWheel(2, 1) - await expectDate("2000-01-01 00:00:00") - }) + it('date wheel', async () => { + await scrollWheel(1, 1) + await expectDate('2000-01-01 00:00:00') + }) - }) + it('year wheel', async () => { + await scrollWheel(2, 1) + await expectDate('2000-01-01 00:00:00') + }) + }) + describe('overshooting max date', () => { + before(async () => { + await setMaximumDate(secondOfJanuary) + }) + it('day wheel should not be possible to overshoot since it is not wrapping (no invalid dates exists)', async () => { + await setMode('datetime') + await scrollWheel(0, 1) + await expectDate('2000-01-02 00:00:00') + await scrollWheel(0, 1) + await expectDate('2000-01-02 00:00:00') + }) + describe('date mode', () => { + before(async () => { + await setMode('date') + await setMaximumDate(secondOfJanuary) + }) + + it('overshooting month wheel should set all other wheels to maximum possible date', async () => { + await scrollWheel(0, 1) + await expectDate('2000-01-02 00:00:00') + }) + + it('overshooting date wheel should reverse to highest possible date', async () => { + await scrollWheel(1, 5) + await expectDate('2000-01-02 00:00:00') + }) + + it('overshooting year wheel should set all other wheels to maximum possible date', async () => { + await setMaximumDate(secondOfJanuary2001) + await scrollWheel(0, 1) // set month to feb + await scrollWheel(2, 1) + await expectDate('2001-01-02 00:00:00') + }) }) -}) \ No newline at end of file + describe('time mode', () => { + before(async () => { + await setMode('time') + await setMaximumDate(initialDate) + }) + + it('overshooting hour wheel should reverse to highest possible time', async () => { + await scrollWheel(0, 5) + await expectDate('2000-01-01 00:00:00') + }) + + it('overshooting minute wheel should reverse to highest possible time', async () => { + await scrollWheel(1, 5) + await expectDate('2000-01-01 00:00:00') + }) + + it('overshooting am/pm wheel should reverse to highest possible time', async () => { + await scrollWheel(2, 1) + await expectDate('2000-01-01 00:00:00') + }) + }) + }) +}) diff --git a/examples/detox/e2e/tests/minimumDate.spec.js b/examples/detox/e2e/tests/minimumDate.spec.js index d70f4b5b..7a15368e 100644 --- a/examples/detox/e2e/tests/minimumDate.spec.js +++ b/examples/detox/e2e/tests/minimumDate.spec.js @@ -1,134 +1,123 @@ -const { setDate, scrollWheel, expectDate, setMinimumDate, setMode } = require("../utils") +const { + setDate, + scrollWheel, + expectDate, + setMinimumDate, + setMode, +} = require('../utils') -const oneMinuteBeforeJanuary2 = new Date(2000, 0, 1, 23, 59, 0); +const oneMinuteBeforeJanuary2 = new Date(2000, 0, 1, 23, 59, 0) describe('Minimum date', () => { - - before(async () => { - await device.reloadReactNative() - await element(by.text('Advanced')).tap() + before(async () => { + await device.reloadReactNative() + await element(by.text('Advanced')).tap() + }) + + beforeEach(async () => { + await setDate(oneMinuteBeforeJanuary2.toISOString()) + }) + + describe('cannot pass min date - mode: ', () => { + describe('datetime', () => { + before(async () => { + await setMode('datetime') + await setMinimumDate(oneMinuteBeforeJanuary2) + }) + + it('day wheel', async () => { + await scrollWheel(0, -2) + await expectDate('2000-01-01 23:59:00') + }) + + it('hour wheel', async () => { + await scrollWheel(1, -1) + await expectDate('2000-01-01 23:59:00') + }) + + it('minute wheel', async () => { + await scrollWheel(2, -1) + await expectDate('2000-01-01 23:59:00') + }) }) - beforeEach(async () => { - await setDate(oneMinuteBeforeJanuary2.toISOString()) + describe('date', () => { + before(async () => { + await setMode('date') + await setMinimumDate(oneMinuteBeforeJanuary2) + }) + + it('month wheel', async () => { + await scrollWheel(0, -1) + await expectDate('2000-01-06 00:00:00') + }) + + it('date wheel', async () => { + await scrollWheel(1, -1) + await expectDate('2000-01-06 00:00:00') + }) + + it('year wheel', async () => { + await scrollWheel(2, -1) + await expectDate('2000-01-01 23:59:00') + }) }) + }) - describe('cannot pass min date - mode: ', () => { - - describe('datetime', () => { - - before(async () => { - await setMode("datetime") - await setMinimumDate(oneMinuteBeforeJanuary2) - }) - - it('day wheel', async () => { - await scrollWheel(0, -2) - await expectDate("2000-01-01 23:59:00") - }) - - it('hour wheel', async () => { - await scrollWheel(1, -1) - await expectDate("2000-01-01 23:59:00") - }) - - it('minute wheel', async () => { - await scrollWheel(2, -1) - await expectDate("2000-01-01 23:59:00") - }) - - }) - - describe('date', () => { - - before(async () => { - await setMode("date") - await setMinimumDate(oneMinuteBeforeJanuary2) - }) - - it('month wheel', async () => { - await scrollWheel(0, -1) - await expectDate("2000-01-01 23:59:00") - }) - - it('date wheel', async () => { - await scrollWheel(1, -1) - await expectDate("2000-01-01 23:59:00") - }) - - it('year wheel', async () => { - await scrollWheel(2, -1) - await expectDate("2000-01-01 23:59:00") - }) - - }) + describe('overshooting min date - mode:', () => { + before(async () => { + await setMinimumDate(oneMinuteBeforeJanuary2) }) + describe('datetime', () => { + before(async () => { + await setMode('datetime') + }) + + it('day wheel should not be possible to overshoot since it is not wrapping (no invalid dates exists)', async () => { + await scrollWheel(0, -1) + await expectDate('2000-01-01 23:59:00') + await scrollWheel(0, -1) + await expectDate('2000-01-01 23:59:00') + }) + }) - describe('overshooting min date - mode:', () => { - - before(async () => { - await setMinimumDate(oneMinuteBeforeJanuary2) - }) - - describe('datetime', () => { - - before(async () => { - await setMode("datetime") - }) - - it('day wheel should not be possible to overshoot since it is not wrapping (no invalid dates exists)', async () => { - await scrollWheel(0, -1) - await expectDate("2000-01-01 23:59:00") - await scrollWheel(0, -1) - await expectDate("2000-01-01 23:59:00") - }) - - }) - - describe('date', () => { - - before(async () => { - await setMode("date") - }) - - it('overshooting month wheel should set all other wheels to minimum possible date', async () => { - await scrollWheel(0, -1) - await expectDate("2000-01-01 23:59:00") - }) - - it('overshooting date wheel should reverse to minimum possible date', async () => { - await scrollWheel(1, -5) - await expectDate("2000-01-01 23:59:00") - }) - - }) - - describe('time mode', () => { - - before(async () => { - await setMode("time") - await setMinimumDate(oneMinuteBeforeJanuary2) - }) - - it('overshooting hour wheel should reverse to minimum possible time', async () => { - await scrollWheel(0, -5) - await expectDate("2000-01-01 23:59:00") - }) - - it('overshooting minute wheel should reverse to minimum possible time', async () => { - await scrollWheel(1, -5) - await expectDate("2000-01-01 23:59:00") - }) - - it('overshooting am/pm wheel should reverse to minimum possible time', async () => { - await scrollWheel(2, -1) - await expectDate("2000-01-01 23:59:00") - }) - - }) + describe('date', () => { + before(async () => { + await setMode('date') + }) + it('overshooting month wheel should set all other wheels to maximum possible date', async () => { + await scrollWheel(0, -1) + await expectDate('2000-01-06 00:00:00') + }) + it('overshooting date wheel should reverse to maximum possible date', async () => { + await scrollWheel(1, -5) + await expectDate('2000-01-06 00:00:00') + }) }) -}) \ No newline at end of file + describe('time mode', () => { + before(async () => { + await setMode('time') + await setMinimumDate(oneMinuteBeforeJanuary2) + }) + + it('overshooting hour wheel should reverse to minimum possible time', async () => { + await scrollWheel(0, -5) + await expectDate('2000-01-01 23:59:00') + }) + + it('overshooting minute wheel should reverse to minimum possible time', async () => { + await scrollWheel(1, -5) + await expectDate('2000-01-01 23:59:00') + }) + + it('overshooting am/pm wheel should reverse to minimum possible time', async () => { + await scrollWheel(2, -1) + await expectDate('2000-01-01 23:59:00') + }) + }) + }) +}) diff --git a/examples/detox/e2e/tests/minuteInterval.spec.js b/examples/detox/e2e/tests/minuteInterval.spec.js index fbbdb984..506cb224 100644 --- a/examples/detox/e2e/tests/minuteInterval.spec.js +++ b/examples/detox/e2e/tests/minuteInterval.spec.js @@ -1,34 +1,32 @@ -const { scrollWheel, expectDate, setMinuteInterval } = require("../utils") +const { scrollWheel, expectDate, setMinuteInterval } = require('../utils') const scrollMinuteWheel = () => scrollWheel(2, 1) describe('Minute interval', () => { + before(async () => { + await device.reloadReactNative() + await element(by.text('Advanced')).tap() + }) - before(async () => { - await device.reloadReactNative() - await element(by.text('Advanced')).tap() - }) + it('1 minute (default)', async () => { + await setMinuteInterval(1) + await scrollMinuteWheel() + await expectDate('2000-01-01 00:01:00') + }) - it('1 minute (default)', async () => { - await setMinuteInterval(1) - await scrollMinuteWheel() - await expectDate("2000-01-01 00:01:00") - }) + it('5 minutes', async () => { + await setMinuteInterval(5) + await scrollMinuteWheel() + await expectDate('2000-01-01 00:05:00') + }) - it('5 minutes', async () => { - await setMinuteInterval(5) - await scrollMinuteWheel() - await expectDate("2000-01-01 00:05:00") - }) - - it('15 minutes', async () => { - await setMinuteInterval(15) - await scrollMinuteWheel() - await expectDate("2000-01-01 00:15:00") - await scrollMinuteWheel() - await expectDate("2000-01-01 00:30:00") - await scrollMinuteWheel() - await expectDate("2000-01-01 00:45:00") - }) - -}) \ No newline at end of file + it('15 minutes', async () => { + await setMinuteInterval(15) + await scrollMinuteWheel() + await expectDate('2000-01-01 00:15:00') + await scrollMinuteWheel() + await expectDate('2000-01-01 00:30:00') + await scrollMinuteWheel() + await expectDate('2000-01-01 00:45:00') + }) +}) diff --git a/examples/detox/e2e/tests/mode.spec.js b/examples/detox/e2e/tests/mode.spec.js index 9a95a21d..45194f7d 100644 --- a/examples/detox/e2e/tests/mode.spec.js +++ b/examples/detox/e2e/tests/mode.spec.js @@ -1,14 +1,13 @@ -const { setMode } = require("../utils") +const { setMode } = require('../utils') describe('Modes', () => { - before(async () => { await device.reloadReactNative() await element(by.text('Advanced')).tap() }) it('datetime', async () => { - await setMode("datetime") + await setMode('datetime') await expect(element(by.id('day'))).toBeVisible() await expect(element(by.id('minutes'))).toBeVisible() @@ -21,7 +20,7 @@ describe('Modes', () => { }) it('date', async () => { - await setMode("date") + await setMode('date') await expect(element(by.id('month'))).toBeVisible() await expect(element(by.id('date'))).toBeVisible() @@ -34,7 +33,7 @@ describe('Modes', () => { }) it('time', async () => { - await setMode("time") + await setMode('time') await expect(element(by.id('minutes'))).toBeVisible() await expect(element(by.id('hour'))).toBeVisible() @@ -45,5 +44,4 @@ describe('Modes', () => { await expect(element(by.id('date'))).toNotExist() await expect(element(by.id('year'))).toNotExist() }) - }) diff --git a/examples/detox/e2e/tests/scrollAround.spec.js b/examples/detox/e2e/tests/scrollAround.spec.js index 629318e3..4aca776e 100644 --- a/examples/detox/e2e/tests/scrollAround.spec.js +++ b/examples/detox/e2e/tests/scrollAround.spec.js @@ -1,45 +1,41 @@ -const { scrollWheel, expectDate } = require("../utils") - +const { scrollWheel, expectDate } = require('../utils') describe('Scroll around', () => { - - before(async () => { - await device.reloadReactNative() - await element(by.text('Advanced')).tap() - }) - - it.skip('Hour wheel should scroll all way around and switch AM/PM when passing 12', async () => { - await scroll3HoursAndExpect("2000-01-01 03:00:00") - await scroll3HoursAndExpect("2000-01-01 06:00:00") - await scroll3HoursAndExpect("2000-01-01 09:00:00") - await scrollWheel(1, 2) - await expectDate("2000-01-01 11:00:00") - await scrollWheel(1, 2) - await expectDate("2000-01-01 13:00:00") - await scroll3HoursAndExpect("2000-01-01 16:00:00") - await scroll3HoursAndExpect("2000-01-01 19:00:00") - await scroll3HoursAndExpect("2000-01-01 22:00:00") - await scroll3HoursAndExpect("2000-01-01 01:00:00") - }) - - it('Minute wheel should be possible to scroll all way around', async () => { - await scrollWheel(2, 55) - await expectDate("2000-01-01 00:55:00") - await scrollWheel(2, 10) - await expectDate("2000-01-01 00:05:00") - }) - - it('Day wheel should change year when passing new year', async () => { - await scrollWheel(0, -1) - await expectDate("1999-12-31 00:00:00") - await scrollWheel(0, 1) - await expectDate("2000-01-01 00:00:00") - }) - - const scroll3HoursAndExpect = async (date) => { - await scrollWheel(1, 3) - await expectDate(date) - } - + before(async () => { + await device.reloadReactNative() + await element(by.text('Advanced')).tap() + }) + + it.skip('Hour wheel should scroll all way around and switch AM/PM when passing 12', async () => { + await scroll3HoursAndExpect('2000-01-01 03:00:00') + await scroll3HoursAndExpect('2000-01-01 06:00:00') + await scroll3HoursAndExpect('2000-01-01 09:00:00') + await scrollWheel(1, 2) + await expectDate('2000-01-01 11:00:00') + await scrollWheel(1, 2) + await expectDate('2000-01-01 13:00:00') + await scroll3HoursAndExpect('2000-01-01 16:00:00') + await scroll3HoursAndExpect('2000-01-01 19:00:00') + await scroll3HoursAndExpect('2000-01-01 22:00:00') + await scroll3HoursAndExpect('2000-01-01 01:00:00') + }) + + it('Minute wheel should be possible to scroll all way around', async () => { + await scrollWheel(2, 55) + await expectDate('2000-01-01 00:55:00') + await scrollWheel(2, 10) + await expectDate('2000-01-01 00:05:00') + }) + + it('Day wheel should change year when passing new year', async () => { + await scrollWheel(0, -1) + await expectDate('1999-12-31 00:00:00') + await scrollWheel(0, 1) + await expectDate('2000-01-01 00:00:00') + }) + + const scroll3HoursAndExpect = async date => { + await scrollWheel(1, 3) + await expectDate(date) + } }) - diff --git a/examples/detox/e2e/tests/timezoneOffset.spec.js b/examples/detox/e2e/tests/timezoneOffset.spec.js new file mode 100644 index 00000000..469a47d1 --- /dev/null +++ b/examples/detox/e2e/tests/timezoneOffset.spec.js @@ -0,0 +1,81 @@ +const { + setTimeZoneOffsetInMinutes, + expectDate, + scrollWheel, + expectDateString, + setMaximumDate, + setDate, +} = require('../utils') + +const scrollMinuteWheel = () => scrollWheel(2, 1) + +// I haven't found a way to change the timezone on the emulator to be able to run these tests. +// Until possible, run these tests locally when needed with Europe/Stockholm timezone. +describe.skip('Timezone offset', () => { + before(async () => { + await device.reloadReactNative() + await element(by.text('Advanced')).tap() + }) + + it('undefined (default)', async () => { + await setTimeZoneOffsetInMinutes(undefined) + await scrollMinuteWheel() + await expectDate('2000-01-01 00:01:00') + await expectDateString('Sat Jan 11201 AM ') + }) + + it('0', async () => { + await setTimeZoneOffsetInMinutes(0) + await scrollMinuteWheel() + await expectDate('2000-01-01 00:01:00') + await expectDateString('Fri Dec 311101 PM ') + }) + + it('180', async () => { + await setTimeZoneOffsetInMinutes(180) + await scrollMinuteWheel() + await expectDate('2000-01-01 00:01:00') + await expectDateString('Sat Jan 1201 AM ') + }) + + it('-180', async () => { + await setTimeZoneOffsetInMinutes(-180) + await scrollMinuteWheel() + await expectDate('2000-01-01 00:01:00') + await expectDateString('Fri Dec 31801 PM ') + }) + + describe('daylight saving', () => { + const firstOfJuly = new Date(2000, 6, 1, 0, 0) + const firstOfJune = new Date(2000, 5, 1, 0, 0) + + before(async () => { + await setMaximumDate(firstOfJuly) + await setDate(firstOfJune) + }) + + it('undefined', async () => { + await setDate(firstOfJune) + await setTimeZoneOffsetInMinutes(undefined) + await scrollMinuteWheel() + await expectDate('2000-06-01 00:01:00') + await expectDateString('Thu Jun 11201 AM ') + }) + + it('0', async () => { + await setDate(firstOfJune) + await setTimeZoneOffsetInMinutes(0) + await scrollMinuteWheel() + await expectDate('2000-06-01 00:01:00') + await expectDateString('Wed May 311001 PM ') + }) + + it('180', async () => { + await setDate(firstOfJune) + await setTimeZoneOffsetInMinutes(180) + await scrollMinuteWheel() + await expectDate('2000-06-01 00:01:00') + await expectDateString('Thu Jun 1101 AM ') + }) + }) +}) diff --git a/examples/detox/e2e/tests/wheelOrder.spec.js b/examples/detox/e2e/tests/wheelOrder.spec.js index 9a629d92..370571c6 100644 --- a/examples/detox/e2e/tests/wheelOrder.spec.js +++ b/examples/detox/e2e/tests/wheelOrder.spec.js @@ -1,95 +1,90 @@ -const { scrollWheelWithIndexAndExpectDate, setMode, setLocale, setMaximumDate } = require("../utils") - +const { + scrollWheelWithIndexAndExpectDate, + setMode, + setLocale, + setMaximumDate, +} = require('../utils') describe('Wheel order', () => { - before(async () => { await device.reloadReactNative() await element(by.text('Advanced')).tap() - await setMaximumDate("undefined") + await setMaximumDate('undefined') }) describe('datetime', () => { - before(async () => { - await setMode("datetime") + await setMode('datetime') }) it('US', async () => { - await setLocale("en-US") - await scrollWheelWithIndexAndExpectDate(0, "2000-01-02 00:00:00") - await scrollWheelWithIndexAndExpectDate(1, "2000-01-01 01:00:00") - await scrollWheelWithIndexAndExpectDate(2, "2000-01-01 00:01:00") - await scrollWheelWithIndexAndExpectDate(3, "2000-01-01 12:00:00") + await setLocale('en-US') + await scrollWheelWithIndexAndExpectDate(0, '2000-01-02 00:00:00') + await scrollWheelWithIndexAndExpectDate(1, '2000-01-01 01:00:00') + await scrollWheelWithIndexAndExpectDate(2, '2000-01-01 00:01:00') + await scrollWheelWithIndexAndExpectDate(3, '2000-01-01 12:00:00') }) it('Korean', async () => { - await setLocale("ko-KR") - await scrollWheelWithIndexAndExpectDate(0, "2000-01-02 00:00:00") - await scrollWheelWithIndexAndExpectDate(1, "2000-01-01 12:00:00") - await scrollWheelWithIndexAndExpectDate(2, "2000-01-01 01:00:00") - await scrollWheelWithIndexAndExpectDate(3, "2000-01-01 00:01:00") + await setLocale('ko-KR') + await scrollWheelWithIndexAndExpectDate(0, '2000-01-02 00:00:00') + await scrollWheelWithIndexAndExpectDate(1, '2000-01-01 12:00:00') + await scrollWheelWithIndexAndExpectDate(2, '2000-01-01 01:00:00') + await scrollWheelWithIndexAndExpectDate(3, '2000-01-01 00:01:00') }) - }) describe('date', () => { - before(async () => { - await setMode("date") + await setMode('date') }) it('US', async () => { - await setLocale("en-US") - await scrollWheelWithIndexAndExpectDate(0, "2000-02-01 00:00:00") - await scrollWheelWithIndexAndExpectDate(1, "2000-01-02 00:00:00") - await scrollWheelWithIndexAndExpectDate(2, "2001-01-01 00:00:00") + await setLocale('en-US') + await scrollWheelWithIndexAndExpectDate(0, '2000-02-01 00:00:00') + await scrollWheelWithIndexAndExpectDate(1, '2000-01-02 00:00:00') + await scrollWheelWithIndexAndExpectDate(2, '2001-01-01 00:00:00') }) it('UK', async () => { - await setLocale("en-GB") - await scrollWheelWithIndexAndExpectDate(0, "2000-01-02 00:00:00") - await scrollWheelWithIndexAndExpectDate(1, "2000-02-01 00:00:00") - await scrollWheelWithIndexAndExpectDate(2, "2001-01-01 00:00:00") + await setLocale('en-GB') + await scrollWheelWithIndexAndExpectDate(0, '2000-01-02 00:00:00') + await scrollWheelWithIndexAndExpectDate(1, '2000-02-01 00:00:00') + await scrollWheelWithIndexAndExpectDate(2, '2001-01-01 00:00:00') }) it('Korean', async () => { - await setLocale("ko-KR") - await scrollWheelWithIndexAndExpectDate(0, "2001-01-01 00:00:00") - await scrollWheelWithIndexAndExpectDate(1, "2000-02-01 00:00:00") - await scrollWheelWithIndexAndExpectDate(2, "2000-01-02 00:00:00") + await setLocale('ko-KR') + await scrollWheelWithIndexAndExpectDate(0, '2001-01-01 00:00:00') + await scrollWheelWithIndexAndExpectDate(1, '2000-02-01 00:00:00') + await scrollWheelWithIndexAndExpectDate(2, '2000-01-02 00:00:00') }) - }) describe('time', () => { - before(async () => { - await setMode("time") + await setMode('time') }) it('US', async () => { - await setLocale("en-US") - await scrollWheelWithIndexAndExpectDate(0, "2000-01-01 01:00:00") - await scrollWheelWithIndexAndExpectDate(1, "2000-01-01 00:01:00") - await scrollWheelWithIndexAndExpectDate(2, "2000-01-01 12:00:00") + await setLocale('en-US') + await scrollWheelWithIndexAndExpectDate(0, '2000-01-01 01:00:00') + await scrollWheelWithIndexAndExpectDate(1, '2000-01-01 00:01:00') + await scrollWheelWithIndexAndExpectDate(2, '2000-01-01 12:00:00') }) it('UK', async () => { - await setLocale("en-GB") - await scrollWheelWithIndexAndExpectDate(0, "2000-01-01 01:00:00") - await scrollWheelWithIndexAndExpectDate(1, "2000-01-01 00:01:00") - await scrollWheelWithIndexAndExpectDate(2, "2000-01-01 12:00:00") + await setLocale('en-GB') + await scrollWheelWithIndexAndExpectDate(0, '2000-01-01 01:00:00') + await scrollWheelWithIndexAndExpectDate(1, '2000-01-01 00:01:00') + await scrollWheelWithIndexAndExpectDate(2, '2000-01-01 12:00:00') }) it('Korean', async () => { - await setLocale("ko-KR") - await scrollWheelWithIndexAndExpectDate(0, "2000-01-01 12:00:00") - await scrollWheelWithIndexAndExpectDate(1, "2000-01-01 01:00:00") - await scrollWheelWithIndexAndExpectDate(2, "2000-01-01 00:01:00") + await setLocale('ko-KR') + await scrollWheelWithIndexAndExpectDate(0, '2000-01-01 12:00:00') + await scrollWheelWithIndexAndExpectDate(1, '2000-01-01 01:00:00') + await scrollWheelWithIndexAndExpectDate(2, '2000-01-01 00:01:00') }) - }) - - }) diff --git a/examples/detox/e2e/utils.js b/examples/detox/e2e/utils.js index c7e73e53..9b29ae0d 100644 --- a/examples/detox/e2e/utils.js +++ b/examples/detox/e2e/utils.js @@ -35,6 +35,7 @@ exports.setLocale = changeProp('locale') exports.setMinimumDate = changeProp('minimumDate') exports.setMaximumDate = changeProp('maximumDate') exports.setMinuteInterval = changeProp('minuteInterval') +exports.setTimeZoneOffsetInMinutes = changeProp('timeZoneOffsetInMinutes') exports.setMode = changeProp('mode') exports.scrollWheel = scrollWheel exports.expectDate = expectDate diff --git a/examples/detox/package.json b/examples/detox/package.json index ce67ea76..0c61cd0b 100644 --- a/examples/detox/package.json +++ b/examples/detox/package.json @@ -23,7 +23,7 @@ "react-native": "0.59.9" }, "devDependencies": { - "detox": "^14.0.1", + "detox": "17.14.9", "mocha": "^6.1.3" }, "detox": { @@ -56,4 +56,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/detox/src/CustomPropValue.js b/examples/detox/src/CustomPropValue.js index 03092861..25cd537d 100644 --- a/examples/detox/src/CustomPropValue.js +++ b/examples/detox/src/CustomPropValue.js @@ -8,6 +8,7 @@ export default function CustomPropValue(props) { const getPropValue = () => { if (propValue === "undefined") return undefined if (propName === "minuteInterval") return parseInt(propValue) + if (propName === "timeZoneOffsetInMinutes") return parseInt(propValue) if (["date", "maximumDate", "minimumDate"].includes(propName)) return new Date(propValue) return propValue } diff --git a/examples/detox/src/examples/Advanced.js b/examples/detox/src/examples/Advanced.js index fd318a38..a6227dad 100644 --- a/examples/detox/src/examples/Advanced.js +++ b/examples/detox/src/examples/Advanced.js @@ -13,6 +13,7 @@ import MinuteInterval from '../propPickers/MinuteInterval' import Scroll from '../propPickers/Scroll' import CustomPropValue from '../CustomPropValue' import { readableDate } from '../utils' +import Variant from '../propPickers/Variant' Date.prototype.addHours = function(h) { this.setTime(this.getTime() + h * 60 * 60 * 1000) @@ -37,6 +38,7 @@ export default class Advanced extends Component { timeZoneOffsetInMinutes: undefined, minuteInterval: 1, dateString: '', + androidVariant: 'iosClone', } render() { @@ -48,10 +50,11 @@ export default class Advanced extends Component { } /> (this.ref = ref)} + innerRef={ref => (this.ref = ref)} date={this.state.date} onDateChange={this.setDate} onDateStringChange={this.setDateString} + androidVariant={this.state.androidVariant} locale={this.state.locale} minuteInterval={this.state.minuteInterval} minimumDate={this.state.minimumDate} @@ -88,6 +91,15 @@ export default class Advanced extends Component { /> ), }, + { + name: 'androidVariant', + component: ( + this.setState({ androidVariant })} + /> + ), + }, { name: 'mode', component: ( @@ -161,7 +173,10 @@ export default class Advanced extends Component { { name: 'fadeToColor', component: ( - this.props.setBackground(randomColor())} /> + this.props.setBackground(randomColor())} + setNone={() => this.props.setBackground("none")} + /> ), }, { diff --git a/examples/detox/src/propPickers/DateChange.js b/examples/detox/src/propPickers/DateChange.js index ff210147..05f0f6ff 100644 --- a/examples/detox/src/propPickers/DateChange.js +++ b/examples/detox/src/propPickers/DateChange.js @@ -1,13 +1,5 @@ import React, { Component } from 'react' -import { - Dimensions, - Button, - View, - StyleSheet, - ScrollView, - Text, - TouchableOpacity, -} from 'react-native' +import { Button, Text } from 'react-native' export default class extends Component { render() { @@ -21,7 +13,6 @@ export default class extends Component { ) } /> -