├── gradle.properties ├── .gitignore ├── lib ├── src │ ├── test │ │ └── java │ │ │ └── ch │ │ │ └── poole │ │ │ └── openinghoursfragment │ │ │ └── templates │ │ │ ├── package-info.java │ │ │ └── TemplatesDatabaseTest.java │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ └── styles.xml │ │ │ ├── drawable-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xhdpi │ │ │ │ ├── ic_add_white_36dp.png │ │ │ │ ├── ic_more_vert_white_36dp.png │ │ │ │ └── ic_action_edit_holo_light.png │ │ │ ├── layout │ │ │ │ ├── divider.xml │ │ │ │ ├── menu.xml │ │ │ │ ├── toast.xml │ │ │ │ ├── comment.xml │ │ │ │ ├── twentyfourseven.xml │ │ │ │ ├── valuepicker.xml │ │ │ │ ├── template_list.xml │ │ │ │ ├── interval.xml │ │ │ │ ├── template_list_item.xml │ │ │ │ ├── time_range_row.xml │ │ │ │ ├── rangepicker.xml │ │ │ │ ├── time_row.xml │ │ │ │ ├── datepicker.xml │ │ │ │ ├── modifier.xml │ │ │ │ ├── rule_header.xml │ │ │ │ ├── occurrence_in_month_picker.xml │ │ │ │ ├── time_extended_range_row.xml │ │ │ │ ├── holiday_row.xml │ │ │ │ ├── timerangepicker.xml │ │ │ │ ├── time_end_event_row.xml │ │ │ │ ├── year_range.xml │ │ │ │ ├── week_range.xml │ │ │ │ ├── time_event_row.xml │ │ │ │ ├── time_start_event_row.xml │ │ │ │ ├── daterange_row.xml │ │ │ │ ├── template_item.xml │ │ │ │ ├── daterangepicker.xml │ │ │ │ └── openinghours.xml │ │ │ ├── values-kn │ │ │ │ └── strings.xml │ │ │ ├── values-hr │ │ │ │ └── strings.xml │ │ │ ├── values-no │ │ │ │ └── strings.xml │ │ │ ├── values-ro │ │ │ │ └── strings.xml │ │ │ ├── values-sk │ │ │ │ └── strings.xml │ │ │ ├── values-eu │ │ │ │ └── strings.xml │ │ │ ├── values-el │ │ │ │ └── strings.xml │ │ │ ├── values-br │ │ │ │ └── strings.xml │ │ │ ├── values-da │ │ │ │ └── strings.xml │ │ │ ├── values-pt-rBR │ │ │ │ └── strings.xml │ │ │ ├── values-ko │ │ │ │ └── strings.xml │ │ │ └── values-tr │ │ │ │ └── strings.xml │ │ ├── java │ │ │ └── ch │ │ │ │ └── poole │ │ │ │ └── openinghoursfragment │ │ │ │ ├── pickers │ │ │ │ ├── package-info.java │ │ │ │ ├── SetRangeListener.java │ │ │ │ ├── SetTimeRangeListener.java │ │ │ │ ├── SetDateRangeListener.java │ │ │ │ ├── ValuePicker.java │ │ │ │ └── RangePicker.java │ │ │ │ ├── package-info.java │ │ │ │ ├── templates │ │ │ │ ├── package-info.java │ │ │ │ ├── UpdateTextListener.java │ │ │ │ ├── UpdateCursorListener.java │ │ │ │ └── TemplateDatabaseHelper.java │ │ │ │ ├── Delete.java │ │ │ │ ├── DefaultTextWatcher.java │ │ │ │ ├── OnSaveListener.java │ │ │ │ ├── DateMenuInterface.java │ │ │ │ ├── CancelableDialogFragment.java │ │ │ │ ├── ValueArrayAdapter.java │ │ │ │ ├── ValueWithDescription.java │ │ │ │ └── Util.java │ │ └── AndroidManifest.xml │ └── androidTest │ │ ├── java │ │ └── ch │ │ │ └── poole │ │ │ └── openinghoursfragment │ │ │ ├── TestActivity.java │ │ │ ├── MixedModeTest.java │ │ │ ├── TextInputTest.java │ │ │ └── templates │ │ │ └── TemplatesTest.java │ │ ├── AndroidManifest.xml │ │ └── resources │ │ └── templates.json ├── .gitignore ├── .tx │ └── config └── documentation │ └── docs │ └── help │ ├── zh-rTW │ └── Opening hours.md │ ├── zh-Hans │ └── Opening hours.md │ └── iw │ └── Opening hours.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── documentation └── images │ ├── Screenshot_basic.png │ └── Screenshot_date_offset.png ├── testapp ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher.png │ │ ├── values │ │ │ └── dimens.xml │ │ └── layout │ │ │ └── activity_test.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── ch │ │ └── poole │ │ └── ohfragmenttest │ │ ├── Test.java │ │ └── TestFragment.java └── build.gradle ├── .settings └── org.eclipse.jdt.core.prefs ├── settings.gradle ├── .checkstyle ├── LICENCE.txt ├── .github └── workflows │ └── android.yml ├── gradlew.bat ├── README.md ├── CHANGELOG.txt └── gradlew /gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /gen/ 3 | /libs/ 4 | /.classpath 5 | /.gradle/ 6 | /build/ 7 | /.project 8 | /.settings/ 9 | -------------------------------------------------------------------------------- /lib/src/test/java/ch/poole/openinghoursfragment/templates/package-info.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.templates; -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /documentation/images/Screenshot_basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/documentation/images/Screenshot_basic.png -------------------------------------------------------------------------------- /testapp/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.project 3 | /.classpath 4 | /.settings/ 5 | /bin/ 6 | /project.properties 7 | /libs/ 8 | /.externalToolBuilders/ 9 | -------------------------------------------------------------------------------- /lib/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #E87850 4 | -------------------------------------------------------------------------------- /documentation/images/Screenshot_date_offset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/documentation/images/Screenshot_date_offset.png -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/pickers/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Classes for pickers 3 | */ 4 | package ch.poole.openinghoursfragment.pickers; -------------------------------------------------------------------------------- /lib/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/lib/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /lib/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/lib/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /lib/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/lib/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Classes for the opening hours editor 3 | */ 4 | package ch.poole.openinghoursfragment; -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/templates/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Template related classes 3 | */ 4 | package ch.poole.openinghoursfragment.templates; -------------------------------------------------------------------------------- /lib/src/main/res/drawable-xhdpi/ic_add_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/lib/src/main/res/drawable-xhdpi/ic_add_white_36dp.png -------------------------------------------------------------------------------- /testapp/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/testapp/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.project 3 | /.classpath 4 | /.settings/ 5 | /bin/ 6 | /project.properties 7 | /libs/ 8 | /.externalToolBuilders/ 9 | /jacoco.exec 10 | -------------------------------------------------------------------------------- /lib/src/main/res/drawable-xhdpi/ic_more_vert_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/lib/src/main/res/drawable-xhdpi/ic_more_vert_white_36dp.png -------------------------------------------------------------------------------- /lib/src/main/res/drawable-xhdpi/ic_action_edit_holo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonpoole/OpeningHoursFragment/HEAD/lib/src/main/res/drawable-xhdpi/ic_action_edit_holo_light.png -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 3 | org.eclipse.jdt.core.compiler.compliance=1.8 4 | org.eclipse.jdt.core.compiler.source=1.8 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/divider.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | include ':testapp', ':lib' -------------------------------------------------------------------------------- /testapp/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | -------------------------------------------------------------------------------- /.checkstyle: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/Delete.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment; 2 | 3 | /** 4 | * Interface for call backs that delete a Rule 5 | * 6 | * @author simon 7 | * 8 | */ 9 | public interface Delete { 10 | /** 11 | * Delete the object in question 12 | */ 13 | void delete(); 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/src/main/res/values-kn/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ಹೌದು 5 | ಇಲ್ಲ 6 | End 7 | 8 | ‍ಎಲ್ಲಾ 9 | 10 | Delete 11 | ‍ಮುಚ್ಚಿದ 12 | 13 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/templates/UpdateTextListener.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.templates; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | public interface UpdateTextListener { 6 | 7 | /** 8 | * Update the OH string with a new text 9 | * 10 | * @param newText the new text 11 | */ 12 | public void updateText(@NonNull String newText); 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/templates/UpdateCursorListener.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.templates; 2 | 3 | import android.database.sqlite.SQLiteDatabase; 4 | import androidx.annotation.NonNull; 5 | 6 | public interface UpdateCursorListener { 7 | 8 | /** 9 | * Replace the current cursor for the template database 10 | * 11 | * @param db the template database 12 | */ 13 | public void newCursor(@NonNull final SQLiteDatabase db); 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/androidTest/java/ch/poole/openinghoursfragment/TestActivity.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | public class TestActivity extends AppCompatActivity implements OnSaveListener { 6 | 7 | String result; 8 | 9 | @Override 10 | public void save(String key, String openingHours) { 11 | result = openingHours; 12 | } 13 | 14 | public String getResult() { 15 | return result; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/DefaultTextWatcher.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment; 2 | 3 | import android.text.TextWatcher; 4 | 5 | abstract class DefaultTextWatcher implements TextWatcher { 6 | @Override 7 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 8 | // empty 9 | } 10 | 11 | @Override 12 | public void onTextChanged(CharSequence s, int start, int before, int count) { 13 | // empty 14 | } 15 | } -------------------------------------------------------------------------------- /lib/src/main/res/values-hr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Spremi 5 | Da 6 | Ne 7 | End 8 | 9 | Svi 10 | Komentar 11 | 12 | zatvoreno 13 | nepoznato 14 | 15 | 16 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/OnSaveListener.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment; 2 | 3 | /** 4 | * Interface for listeners to return the constructed openinghous value 5 | * 6 | * @author simon 7 | * 8 | */ 9 | public interface OnSaveListener { 10 | /** 11 | * Save the value 12 | * 13 | * @param key the OSM key (for example opening_hours or service_times 14 | * @param value the constructed value 15 | */ 16 | void save(String key, String value); 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/main/res/values-no/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Lagre 5 | Ja 6 | Nei 7 | End 8 | 9 | Alle 10 | 11 | Slett 12 | lukket 13 | ukjent 14 | 15 | 16 | -------------------------------------------------------------------------------- /lib/src/main/res/values-ro/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Da 5 | Nu 6 | End 7 | 8 | Tot 9 | 10 | Delete 11 | Dată 12 | închis 13 | necunoscut 14 | 15 | 16 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/pickers/SetRangeListener.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.pickers; 2 | 3 | /** 4 | * Interface for listeners that return date values from RangePicker 5 | * 6 | * @author simon 7 | * 8 | */ 9 | public interface SetRangeListener { 10 | /** 11 | * Listener for values from RangePicker 12 | * 13 | * @param start selected start range value 14 | * @param end selected end range value 15 | */ 16 | void setRange(int start, int end); 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/main/res/values-sk/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Áno 5 | Nie 6 | End 7 | 8 | Všetko 9 | 10 | Delete 11 | Dátum 12 | zatvorené 13 | neznáme 14 | 15 | 16 | -------------------------------------------------------------------------------- /lib/src/main/res/values-eu/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Gorde 5 | Egina 6 | Bai 7 | Ez 8 | Eraman gora 9 | Eraman behera 10 | 11 | 12 | Arrunta 13 | ezezaguna 14 | 15 | 16 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/DateMenuInterface.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment; 2 | 3 | import ch.poole.openinghoursparser.DateRange; 4 | 5 | /** 6 | * This is used to reduce code duplication when adding menu items for the various date range variants 7 | * 8 | * @author simon 9 | * 10 | */ 11 | interface DateMenuInterface { 12 | /** 13 | * Add the actual dates that you want included in the config for the menu item 14 | * 15 | * @param dateRange the DateRange to add the dates to 16 | */ 17 | void addDates(DateRange dateRange); 18 | } 19 | -------------------------------------------------------------------------------- /testapp/src/main/res/layout/activity_test.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/toast.xml: -------------------------------------------------------------------------------- 1 | 9 | 14 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/pickers/SetTimeRangeListener.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.pickers; 2 | 3 | /** 4 | * Interface for listeners that return time values from TimeRangePicker 5 | * 6 | * @author simon 7 | * 8 | */ 9 | public interface SetTimeRangeListener { 10 | /** 11 | * Listener for values from DateRangePicker 12 | * 13 | * @param startHour selected start year 14 | * @param startMinute selected start day of the month 15 | * @param endHour selected end year 16 | * @param endMinute selected end day of the month 17 | */ 18 | void setTimeRange(int startHour, int startMinute, int endHour, int endMinute); 19 | } 20 | -------------------------------------------------------------------------------- /lib/.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [o:openstreetmap:p:openinghoursfragment:r:opening-hoursmd] 5 | file_filter = documentation/docs/help//Opening hours.md 6 | source_file = documentation/docs/help/en/Opening hours.md 7 | source_lang = en 8 | minimum_perc = 50 9 | lang_map = zh_TW: zh-rTW, pt_BR: pt-rBR, zh_CN: zh-rCN, sv_SE: sv-rSE, id: in, he: iw, cs: cs-rCZ 10 | 11 | [o:openstreetmap:p:openinghoursfragment:r:stringsxml] 12 | file_filter = src/main/res/values-/strings.xml 13 | source_file = src/main/res/values/strings.xml 14 | source_lang = en 15 | minimum_perc = 20 16 | lang_map = id: in, he: iw, cs: cs-rCZ, zh_TW: zh-rTW, pt_BR: pt-rBR, zh_CN: zh-rCN, sv_SE: sv-rSE 17 | 18 | -------------------------------------------------------------------------------- /lib/src/main/res/values-el/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Αποθήκευση 5 | Έγινε 6 | Ναι 7 | Όχι 8 | Όλα 9 | Ωρες λειτουργίας 10 | 11 | Διαγραφή 12 | Μετακίνηση επάνω 13 | Μετακίνηση κάτω 14 | 15 | 16 | Κανονικό 17 | Σχόλιο 18 | 19 | κλειστό 20 | άγνωστη 21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/comment.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /testapp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/twentyfourseven.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 17 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Simon Poole 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 4 | and associated documentation files (the "Software"), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 7 | subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies 10 | or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 13 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 14 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 15 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 16 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lib/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 11 | 12 | 18 | 26 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/CancelableDialogFragment.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.NonNull; 5 | import androidx.fragment.app.DialogFragment; 6 | import androidx.fragment.app.Fragment; 7 | import androidx.fragment.app.FragmentManager; 8 | import androidx.fragment.app.FragmentTransaction; 9 | 10 | public class CancelableDialogFragment extends DialogFragment { 11 | 12 | @Override 13 | public void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setCancelable(true); 16 | } 17 | 18 | /** 19 | * Dismiss any instance of this dialog 20 | * 21 | * @param parentFragment the Fragment calling this 22 | * @param tag the tag used by the Fragment 23 | */ 24 | protected static void dismissDialog(@NonNull Fragment parentFragment, @NonNull String tag) { 25 | FragmentManager fm = parentFragment.getChildFragmentManager(); 26 | FragmentTransaction ft = fm.beginTransaction(); 27 | Fragment fragment = fm.findFragmentByTag(tag); 28 | if (fragment != null) { 29 | ft.remove(fragment); 30 | } 31 | ft.commit(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/valuepicker.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 14 | 28 | 29 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/template_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 14 | 15 | 29 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/pickers/SetDateRangeListener.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.pickers; 2 | 3 | import androidx.annotation.Nullable; 4 | import ch.poole.openinghoursparser.Month; 5 | import ch.poole.openinghoursparser.VarDate; 6 | import ch.poole.openinghoursparser.WeekDay; 7 | 8 | /** 9 | * Interface for listeners that return date values from DateRangePicker 10 | * 11 | * @author simon 12 | * 13 | */ 14 | public interface SetDateRangeListener { 15 | /** 16 | * Listener for values from DateRangePicker 17 | * 18 | * @param startYear selected start year 19 | * @param startMonth selected start month or null 20 | * @param startWeekday selected start weekday or null 21 | * @param startDay selected start day of the month or occurrence if startWeekday isn't null 22 | * @param startVarDate selected start variable date ie easter or null 23 | * @param endYear selected end year 24 | * @param endMonth selected end month or null 25 | * @param endWeekday selected end weekday or null 26 | * @param endDay selected end day of the month or occurrence if endWeekday isn't null 27 | * @param endVarDate selected end variable date ie easter or null 28 | */ 29 | void setDateRange(int startYear, @Nullable Month startMonth, @Nullable WeekDay startWeekday, int startDay, @Nullable VarDate startVarDate, int endYear, 30 | @Nullable Month endMonth, @Nullable WeekDay endWeekday, int endDay, @Nullable VarDate endVarDate); 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/interval.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 13 | 21 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/ValueArrayAdapter.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment; 2 | 3 | import java.util.List; 4 | 5 | import android.content.Context; 6 | import androidx.annotation.NonNull; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ArrayAdapter; 11 | import android.widget.TextView; 12 | 13 | public class ValueArrayAdapter extends ArrayAdapter { 14 | 15 | final List values; 16 | 17 | /** 18 | * Construct a new instance 19 | * 20 | * @param context Android Context 21 | * @param resource the resource id 22 | * @param values a List of ValuesWithDescription 23 | */ 24 | public ValueArrayAdapter(@NonNull Context context, int resource, @NonNull List values) { 25 | super(context, resource, values); 26 | this.values = values; 27 | } 28 | 29 | @Override 30 | public View getView(int position, View convertView, ViewGroup container) { 31 | View v = super.getView(position, convertView, container); 32 | TextView textView = (TextView) v.findViewById(android.R.id.text1); 33 | if (textView != null) { 34 | ValueWithDescription value = values.get(position); 35 | textView.setText(value.getDescription() != null ? value.getDescription() : value.getValue()); 36 | } else { 37 | Log.e("ValidatorAdapterView", "position " + position + " view is null"); 38 | } 39 | return v; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/androidTest/resources/templates.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Weekdays with lunch break", 4 | "is_default": 1, 5 | "template": "Mo-Fr 09:00-12:00,13:30-18:30;Sa 09:00-17:00;PH closed", 6 | "key": null, 7 | "region": "CH", 8 | "object": null 9 | }, 10 | { 11 | "name": "Weekdays with lunch break and late shopping", 12 | "is_default": 0, 13 | "template": "Mo,Tu,Th,Fr 09:00-12:00,13:30-18:30;We 09:00-12:00,13:30-20:00;Sa 09:00-17:00;PH closed", 14 | "key": null, 15 | "region": "AT", 16 | "object": null 17 | }, 18 | { 19 | "name": "Weekdays", 20 | "is_default": 0, 21 | "template": "Mo-Fr 09:00-18:30;Sa 09:00-17:00;PH closed", 22 | "key": null, 23 | "region": "CH", 24 | "object": null 25 | }, 26 | { 27 | "name": "Weekdays with late shopping", 28 | "is_default": 0, 29 | "template": "Mo,Tu,Th,Fr 09:00-18:30;We 09:00-20:00;Sa 09:00-17:00;PH closed", 30 | "key": "opening_hours", 31 | "region": "CH", 32 | "object": null 33 | }, 34 | { 35 | "name": "24 hours, 7 days a week", 36 | "is_default": 0, 37 | "template": "24/7", 38 | "key": null, 39 | "region": "CH", 40 | "object": "amenity=restaurant" 41 | }, 42 | { 43 | "name": "Once per weekday", 44 | "is_default": 1, 45 | "template": "Mo-Fr 09:00; Sa 07:00; PH closed", 46 | "key": "collection_times", 47 | "region": "CH", 48 | "object": null 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/template_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 20 | 27 | 34 | 35 | 39 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/time_range_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/ValueWithDescription.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment; 2 | 3 | import java.io.Serializable; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | 8 | public class ValueWithDescription implements Serializable { 9 | private static final long serialVersionUID = 1L; 10 | private final String value; 11 | private final String description; 12 | 13 | /** 14 | * Construct a new instance 15 | * 16 | * @param value the value 17 | * @param description the description if any 18 | */ 19 | public ValueWithDescription(@NonNull final String value, @Nullable final String description) { 20 | this.value = value; 21 | this.description = description; 22 | } 23 | 24 | /** 25 | * @return the value 26 | */ 27 | public String getValue() { 28 | return value; 29 | } 30 | 31 | /** 32 | * @return the description 33 | */ 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return getDescription() != null ? getDescription() : getValue(); 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return value == null ? 0 : value.hashCode(); 46 | } 47 | 48 | @Override 49 | public boolean equals(Object obj) { 50 | if (this == obj) { 51 | return true; 52 | } 53 | if (obj == null) { 54 | return false; 55 | } 56 | if (getClass() != obj.getClass()) { 57 | return false; 58 | } 59 | ValueWithDescription other = (ValueWithDescription) obj; 60 | if (value == null) { 61 | if (other.value != null) { 62 | return false; 63 | } 64 | } else if (!value.equals(other.value)) { 65 | return false; 66 | } 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | env: 16 | # Needed to get some information about the pull request, if any 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | # SonarCloud access token should be generated from https://sonarcloud.io/account/security/ 19 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 20 | 21 | steps: 22 | - name: checkout 23 | uses: actions/checkout@v4 24 | - name: set up JDK 17 25 | uses: actions/setup-java@v1 26 | with: 27 | java-version: 17 28 | - name: Enable KVM 29 | run: | 30 | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules 31 | sudo udevadm control --reload-rules 32 | sudo udevadm trigger --name-match=kvm 33 | - name: run tests 34 | uses: reactivecircus/android-emulator-runner@v2 35 | with: 36 | api-level: 28 37 | emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none 38 | disable-animations: true 39 | script: ./gradlew connectedCheck 40 | - name: Upload Test Results 41 | if: ${{ always() }} 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: Test output 45 | path: /Users/runner/work/OpeningHoursFragment/OpeningHoursFragment/lib/build/reports/androidTests/connected 46 | - name: Generate coverage report 47 | run: ./gradlew jacocoTestReport 48 | - name: SonarCloud Scan 49 | # only do this step if we have a token for sonar 50 | if: ${{ env.SONAR_TOKEN }} 51 | # note as we don't wait for processing to complete this will always succeed 52 | run: ./gradlew sonarqube -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=simonpoole-github --info 53 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/rangepicker.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 14 | 29 | 44 | 45 | -------------------------------------------------------------------------------- /lib/src/main/res/values-br/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Nullañ 5 | Mat eo 6 | Enrollañ 7 | Ouzhpennañ ur reolenn 8 | Freskaat 9 | Ya 10 | Ket 11 | Anv: 12 | Dre ziouer: 13 | %1$s (dre ziouer) 14 | Eurioù digeriñ 15 | Evezhiet 16 | Reolenn %1$s 17 | 18 | 19 | Diverkañ 20 | Dre ziouer 21 | 22 | Bloavezhioù 23 | Kemennadenn 24 | 25 | Sav-heol 26 | Kuzh-heol 27 | Deroù-deiz 28 | Vakañsoù foran 29 | Lu 30 | Meu 31 | Mer 32 | Ya 33 | Gw 34 | Sa 35 | Su 36 | 37 | Gwe 38 | C\'hw 39 | Meu 40 | Ebr 41 | Mae 42 | Mez 43 | Goue 44 | Eost 45 | Gwen 46 | Here 47 | Du 48 | Ker 49 | 50 | Serret 51 | dianav 52 | 53 | 54 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/time_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | 23 | 24 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/datepicker.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 13 | 26 | 40 | 54 | 55 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /testapp/src/main/java/ch/poole/ohfragmenttest/Test.java: -------------------------------------------------------------------------------- 1 | package ch.poole.ohfragmenttest; 2 | 3 | import java.util.ArrayList; 4 | 5 | import android.content.res.Configuration; 6 | import android.os.Bundle; 7 | import androidx.fragment.app.Fragment; 8 | import androidx.fragment.app.FragmentManager; 9 | import androidx.fragment.app.FragmentTransaction; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import android.util.Log; 12 | import ch.poole.openinghoursfragment.OnSaveListener; 13 | import ch.poole.openinghoursfragment.OpeningHoursFragment; 14 | import ch.poole.openinghoursfragment.ValueWithDescription; 15 | 16 | public class Test extends AppCompatActivity implements OnSaveListener { 17 | final String LONG_TEST = "Mo[2] -2 days 10:00-12:00;24/7;week 4-40 PH+2days dawn-09:00;dawn-25:00/25;2010-2100/4 12:01-13:02, 14:00 , 10:00-(sunset+02:00) , 13:00+, 11:01-45:00/46, dawn-dusk, sunrise+ ; 12-16 closed \"ein test\" ; Mo, We 12:01-13:02 ; Apr-Sep 10:01-13:03, Dec 13:03-21:01"; 18 | final String SHORT_TEST = "dawn+"; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_test); 24 | 25 | FragmentManager fm = getSupportFragmentManager(); 26 | FragmentTransaction ft = fm.beginTransaction(); 27 | Fragment prev = fm.findFragmentByTag("fragment_openinghours"); 28 | if (prev != null) { 29 | ft.remove(prev); 30 | } 31 | ft.commit(); 32 | 33 | ArrayList textValues = new ArrayList<>(); 34 | ValueWithDescription yes = new ValueWithDescription("yes", "Yes"); 35 | textValues.add(yes); 36 | ValueWithDescription no = new ValueWithDescription("no", "No"); 37 | textValues.add(no); 38 | 39 | // ValueWithDescription key = new ValueWithDescription("opening_hours", "Opening hours"); 40 | ValueWithDescription key = new ValueWithDescription("collection_times", "Collection times"); 41 | OpeningHoursFragment openingHoursDialog = OpeningHoursFragment.newInstance(key, null, ch.poole.openinghoursfragment.R.style.Theme_DialogLight, 5, true, 42 | textValues); 43 | openingHoursDialog.show(fm, "fragment_openinghours"); 44 | } 45 | 46 | @Override 47 | public void save(String key, String openingHours) { 48 | Log.d("Test", "save got " + openingHours + " for key " + key); 49 | TestFragment.showDialog(this, openingHours); 50 | } 51 | 52 | @Override 53 | public void onConfigurationChanged(Configuration newConfig) { 54 | super.onConfigurationChanged(newConfig); 55 | this.recreate(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/modifier.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | 22 | 23 | 32 | 33 | 45 | 46 | 55 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /lib/src/androidTest/java/ch/poole/openinghoursfragment/MixedModeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not 3 | * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | */ 5 | 6 | package ch.poole.openinghoursfragment; 7 | 8 | import java.util.ArrayList; 9 | 10 | import org.junit.Assert; 11 | import org.junit.Before; 12 | import org.junit.Rule; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | 16 | import androidx.fragment.app.Fragment; 17 | import androidx.fragment.app.FragmentActivity; 18 | import androidx.fragment.app.FragmentManager; 19 | import androidx.fragment.app.FragmentTransaction; 20 | import androidx.test.ext.junit.runners.AndroidJUnit4; 21 | import androidx.test.filters.LargeTest; 22 | import androidx.test.platform.app.InstrumentationRegistry; 23 | import androidx.test.rule.ActivityTestRule; 24 | import androidx.test.uiautomator.UiDevice; 25 | import androidx.test.uiautomator.UiObject2; 26 | 27 | @RunWith(AndroidJUnit4.class) 28 | @LargeTest 29 | public class MixedModeTest { 30 | 31 | private FragmentActivity activity; 32 | private UiDevice device; 33 | 34 | @Rule 35 | public ActivityTestRule mActivityRule = new ActivityTestRule<>(TestActivity.class); 36 | 37 | @Before 38 | public void setup() { 39 | device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 40 | activity = mActivityRule.getActivity(); 41 | FragmentManager fm = activity.getSupportFragmentManager(); 42 | FragmentTransaction ft = fm.beginTransaction(); 43 | Fragment prev = fm.findFragmentByTag("fragment_openinghours"); 44 | if (prev != null) { 45 | ft.remove(prev); 46 | } 47 | ft.commit(); 48 | 49 | ValueWithDescription key = new ValueWithDescription("fee", "Fee"); 50 | 51 | ArrayList values = new ArrayList<>(); 52 | values.add(new ValueWithDescription("yes", "Yes")); 53 | values.add(new ValueWithDescription("no", "No")); 54 | 55 | OpeningHoursFragment fragment = OpeningHoursFragment.newInstance(key, null, null, "no", R.style.Theme_DialogLight, 5, false, values, null); 56 | fragment.show(fm, "fragment_openinghours"); 57 | } 58 | 59 | @Test 60 | public void switchMode() { 61 | Assert.assertNotNull(TestUtils.findText(device, false, "no")); 62 | UiObject2 buttonText = TestUtils.findText(device, false, activity.getString(R.string.text_values)); 63 | Assert.assertTrue(buttonText.isChecked()); 64 | UiObject2 buttonOH = TestUtils.findText(device, false, activity.getString(R.string.opening_hours_key)); 65 | buttonOH.click(); 66 | Assert.assertTrue(buttonOH.isChecked()); 67 | Assert.assertNotNull(TestUtils.findText(device, false, "Encountered")); 68 | buttonText.click(); 69 | Assert.assertTrue(buttonText.isChecked()); 70 | Assert.assertNull(TestUtils.findText(device, false, "Encountered")); 71 | } 72 | } -------------------------------------------------------------------------------- /testapp/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add 2 | // configuration options common to all sub-projects/modules. 3 | 4 | // Project level build file starts here 5 | apply plugin: 'eclipse' 6 | apply plugin: 'com.android.application' 7 | 8 | 9 | apply from: 'https://raw.githubusercontent.com/simonpoole/gradle-tasks/master/eclipse-android-3' 10 | // apply from: '../../gradle-tasks/eclipse-android-3-lib' 11 | 12 | allprojects { 13 | repositories { 14 | mavenCentral() 15 | maven { url "https://maven.google.com" } 16 | mavenLocal() 17 | } 18 | } 19 | 20 | configurations.all { 21 | // Check for updates every build 22 | resolutionStrategy.cacheChangingModulesFor 0, 'seconds' 23 | } 24 | 25 | android { 26 | namespace "ch.poole.ohfragmenttest" 27 | 28 | compileSdkVersion 35 29 | 30 | defaultConfig { 31 | minSdkVersion 21 32 | targetSdkVersion 35 33 | versionCode 100 34 | versionName "1" 35 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 36 | } 37 | 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_1_8 40 | targetCompatibility JavaVersion.VERSION_1_8 41 | } 42 | 43 | buildTypes { 44 | debug { 45 | testCoverageEnabled true 46 | } 47 | } 48 | 49 | lintOptions { 50 | // checkReleaseBuilds false 51 | // Or, if you prefer, you can continue to check for errors in release builds, 52 | // but continue the build even when errors are found: 53 | abortOnError false 54 | } 55 | } 56 | 57 | ext { 58 | androidxVersion = "1.0.0" 59 | } 60 | 61 | dependencies { 62 | // this shouldn't be necessary 63 | implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0")) 64 | implementation project(path: ':lib') 65 | 66 | implementation "androidx.appcompat:appcompat:1.7.0" 67 | implementation "androidx.appcompat:appcompat-resources:1.7.0" 68 | implementation "androidx.recyclerview:recyclerview:1.1.0" 69 | implementation "androidx.preference:preference:1.2.1" 70 | implementation "com.google.android.material:material:1.8.0" 71 | implementation "androidx.annotation:annotation:1.1.0" 72 | implementation "androidx.core:core:1.16.0" 73 | implementation "ch.poole:OpeningHoursParser:0.26.0" 74 | implementation "ch.poole.android:rangebar:0.1.6" 75 | implementation 'ch.poole.android:numberpickerview:1.3.0' 76 | implementation "com.google.code.gson:gson:2.8.5" 77 | 78 | // Instrumentation tests 79 | androidTestImplementation 'androidx.annotation:annotation:1.1.0' 80 | androidTestImplementation 'androidx.test.ext:junit:1.2.1' 81 | androidTestImplementation 'androidx.test:rules:1.6.1' 82 | androidTestImplementation "org.hamcrest:hamcrest-library:2.2" 83 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 84 | 85 | // androidTestCompile "com.android.support.test.uiautomator:uiautomator-v18:2.1.2" 86 | } 87 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/rule_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 23 | 24 | 29 | 30 | 40 | 41 | 50 | 51 | 58 | 65 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/occurrence_in_month_picker.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 13 | 26 | 40 | 54 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/time_extended_range_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | 21 | 22 | 45 | 46 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /lib/src/main/res/values-da/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Annuller 5 | OK 6 | Gem 7 | Færdig 8 | Tilføj regel 9 | Tilføj regel for 24/7 10 | Tilføj regel for ferie 11 | Indlæs skabelon 12 | Gem til skabelon 13 | Håndtér skabeloner 14 | Indlæs skabeloner 15 | Håndtér skabeloner 16 | Gem skabelon 17 | Ja 18 | Nej 19 | Start 20 | End 21 | 22 | Rediger skabelon 23 | Standard: 24 | Alle 25 | Åbningstider 26 | Overvåget 27 | Gadebelysning 28 | 29 | Indlæst standard skabelon 30 | 31 | 32 | Delete 33 | Vis regel type 34 | Flyt op 35 | Flyt ned 36 | 37 | 38 | Normal 39 | Datointerval 40 | Dato 41 | Modifikator 42 | Kommentar 43 | 44 | til 45 | 46 | Påske 47 | 48 | Solopgang 49 | Solnedgang 50 | Daggry 51 | Skumring 52 | 53 | Ma 54 | Ti 55 | On 56 | To 57 | Fr 58 | 59 | 60 | 61 | Jan 62 | Feb 63 | Mar 64 | Apr 65 | Maj 66 | Jun 67 | Jul 68 | Aug 69 | Sep 70 | Okt 71 | Nov 72 | Dec 73 | 74 | åben 75 | lukket 76 | ukendt 77 | 78 | før 79 | efter 80 | 81 | 82 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/holiday_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 20 | 33 | 41 | 46 | 50 | 51 | 60 | 65 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![build status](https://github.com/simonpoole/OpeningHoursFragment/actions/workflows/android.yml/badge.svg)](https://github.com/simonpoole/OpeningHoursFragment/actions) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=OpeningHoursFragment%3Alib&metric=alert_status)](https://sonarcloud.io/dashboard?id=OpeningHoursFragment) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=OpeningHoursFragment%3Alib&metric=coverage)](https://sonarcloud.io/dashboard?id=OpeningHoursFragment) [![sonarcloud bugs](https://sonarcloud.io/api/project_badges/measure?project=OpeningHoursFragment%3Alib&metric=bugs)](https://sonarcloud.io/component_measures?id=OpeningHoursFragment%3Alib&metric=bugs) [![sonarcould maintainability](https://sonarcloud.io/api/project_badges/measure?project=OpeningHoursFragment%3Alib&metric=sqale_rating)](https://sonarcloud.io/component_measures?id=OpeningHoursFragment%3Alib&metric=Maintainability) [![sonarcloud security](https://sonarcloud.io/api/project_badges/measure?project=OpeningHoursFragment%3Alib&metric=security_rating)](https://sonarcloud.io/component_measures?id=OpeningHoursFragment%3Alib&metric=Security) [![sonarcloud reliability](https://sonarcloud.io/api/project_badges/measure?project=OpeningHoursFragment%3Alib&metric=reliability_rating)](https://sonarcloud.io/component_measures?id=OpeningHoursFragment%3Alib&metric=Reliability) 2 | 3 | # OpeningHoursFragment 4 | 5 | This is a re-usable UI element for editing opening hour values that covers the complete specification, it is suitable for non-destructive editing (it will not overwrite or destroy valid opening hours values). 6 | 7 | ![Screenshot](documentation/images/Screenshot_basic.png) 8 | 9 | Please see the [end user documentation](lib/documentation/docs/help/en/Opening%20hours.md) for more information. 10 | 11 | ## Usage 12 | 13 | ### From a FragmentActivity 14 | 15 | ``` java 16 | FragmentManager fm = getSupportFragmentManager(); 17 | ... 18 | OpeningHoursFragment openingHoursDialog = OpeningHoursFragment.newInstance(key,finalValue, R.style.Theme_AppCompat_Light_Dialog_Alert, -1, true); 19 | openingHoursDialog.show(fm, "fragment_opening_hours"); 20 | ``` 21 | 22 | The calling activity needs to implement ch.poole.openinghoursfragement.OnSaveListener which will be used when the user saves a OH value. Have a look at the test app for a working trivial example. 23 | 24 | ### From a Fragment 25 | 26 | ``` java 27 | FragmentManager fm = getChildFragmentManager(); 28 | ... 29 | OpeningHoursFragment openingHoursDialog = OpeningHoursFragment.newInstanceForFragment(key,finalValue, R.style.Theme_AppCompat_Light_Dialog_Alert, -1, true); 30 | openingHoursDialog.show(fm, "fragment_opening_hours"); 31 | ``` 32 | 33 | The calling Fragment needs to implement ch.poole.openinghoursfragement.OnSaveListener which will be used when the user saves a OH value. Have a look at the test app for a working trivial example. 34 | 35 | For other variants please see the javadoc. 36 | 37 | If you are overriding onActivityResult in the Activity calling the Fragment, you need to check that you calling through to super, or else loading and saving templates will fail silently. 38 | 39 | ## Including in your project 40 | 41 | Add the following to your *build.gradle* file(s): 42 | 43 | ``` groovy 44 | repositories { 45 | maven { 46 | mavenCentral() 47 | } 48 | } 49 | ``` 50 | 51 | ``` groovy 52 | dependencies { 53 | compile "ch.poole:OpeningHoursFragment:0.15.1" 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/timerangepicker.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 14 | 30 | 44 | 45 | 63 | 79 | 80 | -------------------------------------------------------------------------------- /lib/src/androidTest/java/ch/poole/openinghoursfragment/TextInputTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not 3 | * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | */ 5 | 6 | package ch.poole.openinghoursfragment; 7 | 8 | import static org.junit.Assert.assertNotNull; 9 | import static org.junit.Assert.fail; 10 | 11 | import org.junit.Before; 12 | import org.junit.Rule; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | 16 | import android.app.Instrumentation; 17 | import android.view.KeyEvent; 18 | import androidx.fragment.app.Fragment; 19 | import androidx.fragment.app.FragmentManager; 20 | import androidx.fragment.app.FragmentTransaction; 21 | import androidx.test.ext.junit.runners.AndroidJUnit4; 22 | import androidx.test.filters.LargeTest; 23 | import androidx.test.platform.app.InstrumentationRegistry; 24 | import androidx.test.rule.ActivityTestRule; 25 | import androidx.test.uiautomator.UiDevice; 26 | import androidx.test.uiautomator.UiObject; 27 | import androidx.test.uiautomator.UiObjectNotFoundException; 28 | import androidx.test.uiautomator.UiSelector; 29 | import androidx.test.uiautomator.Until; 30 | 31 | @RunWith(AndroidJUnit4.class) 32 | @LargeTest 33 | public class TextInputTest { 34 | 35 | private OpeningHoursFragment fragment; 36 | private UiDevice device; 37 | private Instrumentation instrumentation; 38 | 39 | @Rule 40 | public ActivityTestRule mActivityRule = new ActivityTestRule<>(TestActivity.class); 41 | 42 | @Before 43 | public void setup() { 44 | instrumentation = InstrumentationRegistry.getInstrumentation(); 45 | device = UiDevice.getInstance(instrumentation); 46 | 47 | FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager(); 48 | FragmentTransaction ft = fm.beginTransaction(); 49 | Fragment prev = fm.findFragmentByTag("fragment_openinghours"); 50 | if (prev != null) { 51 | ft.remove(prev); 52 | } 53 | ft.commit(); 54 | 55 | ValueWithDescription key = new ValueWithDescription("opening_hours", "Opening hours"); 56 | 57 | fragment = OpeningHoursFragment.newInstance(key, null, null, null, R.style.Theme_DialogLight, 5, false, null, null); 58 | fragment.show(fm, "fragment_openinghours"); 59 | UiSelector uiSelector = new UiSelector().resourceIdMatches(".*more.*"); 60 | UiObject fab = device.findObject(uiSelector); 61 | try { 62 | fab.click(); 63 | TestUtils.clickText(device, false, fragment.getString(R.string.clear), true); 64 | } catch (UiObjectNotFoundException e) { 65 | fail(e.getMessage()); 66 | } 67 | } 68 | 69 | @Test 70 | public void twentyfourseven() { 71 | UiSelector uiSelector = new UiSelector().resourceIdMatches(".*openinghours_string_edit.*"); 72 | UiObject text = device.findObject(uiSelector); 73 | try { 74 | text.click(); 75 | device.pressKeyCode(KeyEvent.KEYCODE_2); 76 | device.pressKeyCode(KeyEvent.KEYCODE_4); 77 | device.pressKeyCode(KeyEvent.KEYCODE_SLASH); 78 | device.pressKeyCode(KeyEvent.KEYCODE_7); 79 | device.pressEnter(); 80 | device.performActionAndWait(()->{}, Until.newWindow(), 1000); 81 | } catch (UiObjectNotFoundException e) { 82 | fail(e.getMessage()); 83 | } 84 | assertNotNull(TestUtils.findText(device, false, fragment.getString(R.string.twentyfourseven))); 85 | } 86 | } -------------------------------------------------------------------------------- /lib/src/main/res/layout/time_end_event_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | 23 | 24 | 48 | 49 | 59 | 60 | 69 | 74 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /lib/documentation/docs/help/zh-rTW/Opening hours.md: -------------------------------------------------------------------------------- 1 | # 開放街圖營業時間編輯器 2 | 3 | OpenStreetMap 營業時間規範相當複雜,並不易於一個簡單直觀的使用者介面。 4 | 5 | 然而大部分的時候,你應該只會用到一小部分的定議。編輯器嘗試在選單中隱藏更加模糊的功能,來達成這一點,大部分的時候減少路上設定,儘可能用預先設定的模版,加以小量修改。 6 | 7 | _這份文件是初步的並且正在進展_ 8 | 9 | ## 使用營業時間編輯器 10 | 11 | 在一般的工作流程中,你編輯的物件一般已經有開放時間的標籤 (opening_hour、service_times 和 colllection_times),或是你可以重新設定物件的預設組合,用空白的開放時間欄位。如果你需要手動輸入欄位,像是用 Vespucci,你可以在詳情頁面輸入鍵值,之後切換回欄位頁籤繼續編輯。如果你相信開放時間標籤必須扅於預設組合的話,請在你的編輯器開啟 issue。 12 | 13 | 如果你有定義預設模版(透過「管理模版」選項物件),編輯器啟動時空白值時將會自動載入。而用「載入模版」功能你可以載入任何儲存的模版,而用「儲存模版」選單則會儲存目前的值為模版。你可以定義分開的模版與為特定鍵設定為預設。例如說"opening_hours","collection_times" 與 "service_times" 或客製值。更進一步說你可以限制模版適用的區域與特定識別碼,特別是特定的 OSM 最高層級標籤 (例如 amenity=restaurant)。 14 | 15 | 當然,您可以從頭開始構建營業時間的值,但我們建議使用現有的模板之一作為起點。 16 | 17 | 如果已經有營業時間的數值則會載入,另外會自動修正以符合營業時間規範。如果無法修正,則會在大概的位置顯示錯誤訊息,原始的營業時間數值,等待人工修正。開放街圖資料庫中有大約1/4的營業時間數值有問題,但只有少於10%的狀況是無法修正。詳見 [OpeningHoursParser](https://github.com/simonpoole/OpeningHoursParser),並且看有那些允許的規範變體。 18 | 19 | ### 主選單按鈕 20 | 21 | * __增加規則__:增加一個新規則。 22 | * __增加假日規則__:國家改變時,為假日增加新的規則。 23 | * __為 24/7 增加規則__:為總是開放的物件增加規則,開放時間規範不支持任何其他子值為24/7,但我們允許增加更高階的選擇器 (例如年份範圍)。 24 | * __載入模板__:載入先有的模板。 25 | * __儲存至模板__:將現有的營業時間數值儲存為模板留待日後使用。 26 | * __管理模板__:編輯(如變更名稱)或刪除現有模板 27 | * __重新整理__:重新解析營業時間的數值。 28 | * __全部刪除__:刪除全部規則。 29 | 30 | ### 規則 31 | 32 | 預設規則會依 _normal_ rules 增加,意味將直接套用並且覆寫同一天原先規則的數值。這可能會為套用延伸時間規則帶來困擾,特別是你希望透過 _顯示規則類別_ 選單 _追加_ 轉換規則。 33 | 34 | #### 規則選單 35 | 36 | * __增加修飾符/註釋__:更改此規則的效果並增加可選擇的註釋。 37 | * __增加假日__:為公衆假期或學校假期增加選取器。 38 | * __增加時間跨度 ...__ 39 | * __時間 - 時間__:同一天的開始時間到結束時間。 40 | * __時間 - 延長時間__:開始時間跨到隔天的結束時間 (例如 26:00 是隔天的 02:00 (am))。 41 | * __變化時間 - 時間__:從開始變化時間 (黎明,黃昏,日出和日落) 到同一天的結束時間。 42 | * __變化時間 - 延長時間__:從第二天起始變化時間到隔天的結束時間。 43 | * __時間 - 變化時間__:起啟時間到結束的變化時間 44 | * __變化時間 - 變化時間__:從起始變化時間到結束變化時間。 45 | * __時間__:特定的時間。 46 | * __時間-一直持續__:從起始時間一直持續。 47 | * __變化時間__:特定的變化時間 48 | * __變化時間 - 一直持續__:從變化時間到一直持續 49 | * __增加週間範圍__:增加以週間為依據的選擇器。 50 | * __增加日期範圍...__ 51 | * __日期 - 日期__:從開始日期 (年、月、日) 到結束日期。 52 | * __變化日期 - 日期__:從起始變化日期 (目前規範僅定義_復活節_) 到結束日期。 53 | * __日期 - 變化日期__:從開始日期到變化日期。 54 | * __變化日期 - 變化日期__:從起始變化日期到結束變化日期。 55 | * __發生在月中-發生在月中__:一個月中從開始的週間是相同的。 56 | * __發生在月中 - 日期__:一個月中週間發生,到特定結束日期。 57 | * __日期 - 發生在月中__:從開始日舒到一個月中的週間結束。 58 | * __發生在月中 - 變動日期__:從一個月開始週間發生,到變動日期。 59 | * __變動日期 - 發生在月中__:從開始的變動日期,到一個月中的週間。 60 | * __日期 - 開放結束__:從開始日期起。 61 | * __日期變化 - 開端__:從起始變化日期開始。 62 | * __發生在月中 - 一直持續__:從一個月中開始週間發生。 63 | * __使用偏差量...__:與上述相同的內容,但是指定了偏差量 (這很少使用)。 64 | * __增加年範圍...__ 65 | * __增加年範圍__:增加以年為依據的選擇器。 66 | * __增加啟始年__: 增加一整年範圍。 67 | * __增加週範圍__:增加以週為依據的選擇器。 68 | * __複製__:複製此規則,並且插入目前位置之後。 69 | * __顯示規則類型__:顯示並允許更改規則類型_正常_,_增加_和_倒退_(位於第一條規則中不可用)。 70 | * __向上移動__:將此規則向上移動一個位置(位於第一條規則時不可用)。 71 | * __向下移動__:將此規則向下移動一個位置。 72 | * __刪除__:刪除此一規則。 73 | 74 | ### 時間跨度 75 | 76 | 要讓編輯時間刻度變得更簡單可行,我們嘗試選擇最佳時間範圍,加載現有值時範圍欄的刻度。對新的時間刻度來說,欄從6:00(am)開始,然後每15分鐘增加。上述的設定可以在選單更改。 77 | 78 | 當你直接用時間軸太困難時,點 (不是在別針上) 在時間軸上,會打開比較大的時間檢取器。時間檢取器會延伸到下一天,所有簡單的方式延長時間區間,但不用刪除再加區間。 79 | 80 | #### 時間跨度選單 81 | 82 | * __顯示時間檢取器__:顯示更大的時間檢取器來開始和結束時間,在非常小的裝置上,則有更好的方式改變時間。 83 | * __切換到15分鐘刻度__:對範圍欄使用15分鐘刻度。 84 | * __切換到5分鐘刻度__:對範圍欄使用5分鐘的刻度。 85 | * __切換到1分鐘刻度__:對範圍欄使用1分鐘的刻度,很難在手機上使用。 86 | * __從午夜開始__:午夜開始範圍欄。 87 | * __顯示間隔__:顯示指定間隔(以分鐘為單位)的間隔字段。 88 | * __刪除__:刪除此一時間跨度。 89 | 90 | ### 管理模版 91 | 92 | 模版管理對話框允許你新增、編輯與刪除模版。 93 | 94 | 在 Android 4.4 以及之後的版本 下列的新增功能都能從選單按鈕進去。 95 | 96 | * __Show all__: 顯示資料庫中所有的模版。 97 | * __Save to file__: 寫入模版資料庫的內容到檔案。 98 | * __Load from file (replace)__: 從檔案載入模版並且取代目前資料庫的內容。 99 | * __Load from file__: 從檔案載入模版並且保留目前的內容。 100 | 101 | #### 儲存與編輯模版對話框 102 | 103 | 對話框允許設定 104 | 105 | * __Name__ 模版描述性的名稱。 106 | * __Default__ 如果選取的話則會設定為預設模版 (通常會被其他欄位限制)。 107 | * __Key__ 鍵這模版如果設定 _Custom key_,則會與這個模版相關,你可以在以下的欄位新增非標準的值。鍵的值支援 SQL wild cards,意味 _%_ 符合零或更多字元,*_* 則符合單一字元。所有 wild card 字元都能用 _\\_ 來逃離字面的符合。 108 | * __Region__ 模版適用的區域。 109 | * __Object__ 用於符合的應用程式特定的字元。 110 | 111 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/year_range.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 21 | 29 | 39 | 47 | 55 | 63 | 68 | 69 | 77 | 82 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /lib/src/test/java/ch/poole/openinghoursfragment/templates/TemplatesDatabaseTest.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.templates; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.fail; 6 | 7 | import org.junit.After; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.robolectric.RobolectricTestRunner; 12 | 13 | import android.database.Cursor; 14 | import androidx.test.core.app.ApplicationProvider; 15 | import androidx.test.filters.LargeTest; 16 | 17 | @RunWith(RobolectricTestRunner.class) 18 | @LargeTest 19 | public class TemplatesDatabaseTest { 20 | TemplateDatabaseHelper db; 21 | 22 | /** 23 | * Pre-test setup 24 | */ 25 | @Before 26 | public void setup() { 27 | ApplicationProvider.getApplicationContext().deleteDatabase(TemplateDatabaseHelper.DATABASE_NAME); 28 | db = new TemplateDatabaseHelper(ApplicationProvider.getApplicationContext()); 29 | } 30 | 31 | /** 32 | * Post-test teardown 33 | */ 34 | @After 35 | public void teardown() { 36 | db.close(); 37 | ApplicationProvider.getApplicationContext().deleteDatabase(TemplateDatabaseHelper.DATABASE_NAME); 38 | } 39 | 40 | /** 41 | * Query by key test 42 | */ 43 | @Test 44 | public void queryByKeyTest() { 45 | try (Cursor cursor = TemplateDatabase.queryBy(db.getReadableDatabase(), "opening_hours", null, null)) { 46 | assertEquals(5, cursor.getCount()); 47 | } 48 | TemplateDatabase.add(db.getWritableDatabase(), "opening_hours", "test entry", true, "Mo-Fr 09:00-12:00,13:30-18:30;Sa 09:00-17:00;PH closed", null, 49 | null); 50 | try (Cursor cursor = TemplateDatabase.queryBy(db.getReadableDatabase(), "opening_hours", null, null)) { 51 | assertEquals(6, cursor.getCount()); 52 | } 53 | try (Cursor cursor = TemplateDatabase.queryBy(db.getReadableDatabase(), "opening1hours", null, null)) { 54 | assertEquals(6, cursor.getCount()); 55 | } 56 | TemplateDatabase.add(db.getWritableDatabase(), "opening\\_hours", "test entry", true, "Mo-Fr 09:00-12:00,13:30-18:30;Sa 09:00-17:00;PH closed", null, 57 | null); 58 | try (Cursor cursor = TemplateDatabase.queryBy(db.getReadableDatabase(), "opening_hours", null, null)) { 59 | assertEquals(7, cursor.getCount()); 60 | } 61 | try (Cursor cursor = TemplateDatabase.queryBy(db.getReadableDatabase(), "opening1hours", null, null)) { 62 | assertEquals(6, cursor.getCount()); 63 | } 64 | } 65 | 66 | /** 67 | * Query by region test 68 | */ 69 | @Test 70 | public void queryByRegionTest() { 71 | TemplateDatabase.add(db.getWritableDatabase(), null, "test entry", true, "Mo-Fr 09:00-12:00,13:30-18:30;Sa 09:00-17:00;PH closed", "CH", null); 72 | try (Cursor cursor = TemplateDatabase.queryBy(db.getReadableDatabase(), null, null, null)) { 73 | assertEquals(7, cursor.getCount()); 74 | } 75 | TemplateDatabase.add(db.getWritableDatabase(), null, "test entry", true, "Mo-Fr 09:00-12:00,13:30-18:30;Sa 09:00-17:00;PH closed", "DE", null); 76 | try (Cursor cursor = TemplateDatabase.queryBy(db.getReadableDatabase(), null, null, null)) { 77 | assertEquals(8, cursor.getCount()); 78 | } 79 | try (Cursor cursor = TemplateDatabase.queryBy(db.getReadableDatabase(), null, "CH", null)) { 80 | assertEquals(7, cursor.getCount()); 81 | } 82 | } 83 | 84 | /** 85 | * Default test 86 | */ 87 | @Test 88 | public void defaultTest() { 89 | assertEquals("Mo-Fr 09:00-12:00,13:30-18:30;Sa 09:00-17:00;PH closed", 90 | TemplateDatabase.getDefault(db.getReadableDatabase(), "opening_hours", null, null)); 91 | TemplateDatabase.add(db.getWritableDatabase(), "opening_hours", "test entry", true, "test", null, null); 92 | assertEquals("test", TemplateDatabase.getDefault(db.getReadableDatabase(), "opening_hours", null, null)); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/templates/TemplateDatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.templates; 2 | 3 | import android.content.Context; 4 | import android.database.SQLException; 5 | import android.database.sqlite.SQLiteDatabase; 6 | import android.database.sqlite.SQLiteOpenHelper; 7 | import android.util.Log; 8 | import ch.poole.openinghoursfragment.R; 9 | 10 | public class TemplateDatabaseHelper extends SQLiteOpenHelper { 11 | private static final String DEBUG_TAG = "TemplateDatabase"; 12 | static final String DATABASE_NAME = "openinghours_templates"; 13 | private static final int DATABASE_VERSION = 5; 14 | 15 | private final Context context; 16 | 17 | /** 18 | * Construct a new instance 19 | * 20 | * @param context Android Context 21 | */ 22 | public TemplateDatabaseHelper(final Context context) { 23 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 24 | this.context = context; 25 | } 26 | 27 | @Override 28 | public void onCreate(SQLiteDatabase db) { 29 | try { 30 | db.execSQL( 31 | "CREATE TABLE templates (key TEXT DEFAULT NULL, name TEXT, is_default INTEGER DEFAULT 0, template TEXT DEFAULT '', region TEXT DEFAULT NULL, object TEXT DEFAULT NULL)"); 32 | TemplateDatabase.add(db, null, context.getString(R.string.weekdays_with_lunch), true, "Mo-Fr 09:00-12:00,13:30-18:30;Sa 09:00-17:00;PH closed", 33 | null, null); 34 | TemplateDatabase.add(db, null, context.getString(R.string.weekdays_with_lunch_late_shopping), false, 35 | "Mo,Tu,Th,Fr 09:00-12:00,13:30-18:30;We 09:00-12:00,13:30-20:00;Sa 09:00-17:00;PH closed", null, null); 36 | TemplateDatabase.add(db, null, context.getString(R.string.weekdays), false, "Mo-Fr 09:00-18:30;Sa 09:00-17:00;PH closed", null, null); 37 | TemplateDatabase.add(db, null, context.getString(R.string.weekdays_late_shopping), false, 38 | "Mo,Tu,Th,Fr 09:00-18:30;We 09:00-20:00;Sa 09:00-17:00;PH closed", null, null); 39 | TemplateDatabase.add(db, null, context.getString(R.string.twentyfourseven), false, "24/7", null, null); 40 | TemplateDatabase.add(db, "collection\\_times", context.getString(R.string.collection_times_weekdays), true, "Mo-Fr 09:00; Sa 07:00; PH closed", 41 | null, null); 42 | } catch (SQLException e) { 43 | Log.w(DEBUG_TAG, "Problem creating database", e); 44 | } 45 | } 46 | 47 | @Override 48 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 49 | Log.d(DEBUG_TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); 50 | if (oldVersion <= 1 && newVersion >= 2) { 51 | db.execSQL("ALTER TABLE templates ADD COLUMN key TEXT DEFAULT NULL"); 52 | TemplateDatabase.add(db, "collection_times", context.getString(R.string.collection_times_weekdays), true, "Mo-Fr 09:00; Sa 07:00; PH closed", null, 53 | null); 54 | } 55 | if (oldVersion <= 2 && newVersion >= 3) { 56 | TemplateDatabase.add(db, null, context.getString(R.string.weekdays_with_lunch_late_shopping), false, 57 | "Mo,Tu,Th,Fr 09:00-12:00,13:30-18:30;We 09:00-12:00,13:30-20:00;Sa 09:00-17:00;PH closed", null, null); 58 | TemplateDatabase.add(db, null, context.getString(R.string.weekdays_late_shopping), false, 59 | "Mo,Tu,Th,Fr 09:00-18:30;We 09:00-20:00;Sa 09:00-17:00;PH closed", null, null); 60 | TemplateDatabase.add(db, null, context.getString(R.string.twentyfourseven), false, "24/7", null, null); 61 | } 62 | if (oldVersion <= 3 && newVersion >= 4) { 63 | db.execSQL("ALTER TABLE templates ADD COLUMN region TEXT DEFAULT NULL"); 64 | db.execSQL("ALTER TABLE templates ADD COLUMN object TEXT DEFAULT NULL"); 65 | } 66 | if (oldVersion <= 4 && newVersion >= 5) { 67 | db.rawQuery("SELECT REPLACE('key','_','\\_')", null); 68 | db.rawQuery("SELECT REPLACE('key',':','\\:')", null); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /testapp/src/main/java/ch/poole/ohfragmenttest/TestFragment.java: -------------------------------------------------------------------------------- 1 | package ch.poole.ohfragmenttest; 2 | 3 | import java.util.ArrayList; 4 | 5 | import android.annotation.SuppressLint; 6 | import android.content.Context; 7 | import android.os.Bundle; 8 | import androidx.annotation.NonNull; 9 | import androidx.fragment.app.DialogFragment; 10 | import androidx.fragment.app.Fragment; 11 | import androidx.fragment.app.FragmentActivity; 12 | import androidx.fragment.app.FragmentManager; 13 | import androidx.fragment.app.FragmentTransaction; 14 | import androidx.appcompat.app.AlertDialog; 15 | import androidx.appcompat.app.AlertDialog.Builder; 16 | import androidx.appcompat.app.AppCompatDialog; 17 | import android.util.Log; 18 | import ch.poole.openinghoursfragment.OnSaveListener; 19 | import ch.poole.openinghoursfragment.OpeningHoursFragment; 20 | import ch.poole.openinghoursfragment.ValueWithDescription; 21 | 22 | public class TestFragment extends DialogFragment implements OnSaveListener { 23 | 24 | private static final String DEBUG_TAG = TestFragment.class.getSimpleName(); 25 | 26 | private static final String TAG = "fragment_test"; 27 | 28 | private static final String KEY_OH = "oh"; 29 | 30 | /** 31 | 32 | */ 33 | static public void showDialog(FragmentActivity activity, String oh) { 34 | dismissDialog(activity); 35 | try { 36 | 37 | FragmentManager fm = activity.getSupportFragmentManager(); 38 | TestFragment testFragment = newInstance(oh); 39 | testFragment.show(fm, TAG); 40 | } catch (IllegalStateException isex) { 41 | Log.e(DEBUG_TAG, "showDialog", isex); 42 | } 43 | } 44 | 45 | private static void dismissDialog(FragmentActivity activity) { 46 | try { 47 | FragmentManager fm = activity.getSupportFragmentManager(); 48 | FragmentTransaction ft = fm.beginTransaction(); 49 | Fragment fragment = fm.findFragmentByTag(TAG); 50 | if (fragment != null) { 51 | ft.remove(fragment); 52 | } 53 | ft.commit(); 54 | } catch (IllegalStateException isex) { 55 | Log.e(DEBUG_TAG, "dismissDialog", isex); 56 | } 57 | } 58 | 59 | /** 60 | */ 61 | static private TestFragment newInstance(String oh) { 62 | TestFragment f = new TestFragment(); 63 | 64 | Bundle args = new Bundle(); 65 | args.putSerializable(KEY_OH, oh); 66 | 67 | f.setArguments(args); 68 | f.setShowsDialog(true); 69 | 70 | return f; 71 | } 72 | 73 | private String oh; 74 | 75 | @Override 76 | public void onAttach(Context context) { 77 | super.onAttach(context); 78 | Log.d(DEBUG_TAG, "onAttach"); 79 | } 80 | 81 | @Override 82 | public void onCreate(Bundle savedInstanceState) { 83 | super.onCreate(savedInstanceState); 84 | setCancelable(true); 85 | } 86 | 87 | @NonNull 88 | @SuppressLint("InflateParams") 89 | @Override 90 | public AppCompatDialog onCreateDialog(Bundle savedInstanceState) { 91 | 92 | Builder builder = new AlertDialog.Builder(getActivity()); 93 | builder.setTitle("test fragment mode"); 94 | oh = getArguments().getString(KEY_OH); 95 | 96 | builder.setPositiveButton(android.R.string.ok, null); 97 | 98 | return builder.create(); 99 | } 100 | 101 | @Override 102 | public void onStart() { 103 | super.onStart(); 104 | ArrayList textValues = new ArrayList<>(); 105 | ValueWithDescription textValue = new ValueWithDescription("test_value", "Test description"); 106 | textValues.add(textValue); 107 | OpeningHoursFragment openingHoursDialog = OpeningHoursFragment.newInstanceForFragment(new ValueWithDescription("fee", null), oh, 108 | ch.poole.openinghoursfragment.R.style.Theme_DialogLight, 5, false, textValues); 109 | FragmentManager fm = getChildFragmentManager(); 110 | openingHoursDialog.show(fm, "fragment_openinghours"); 111 | } 112 | 113 | @Override 114 | public void save(String key, String openingHours) { 115 | Log.d("TestFragment", "save got " + openingHours + " for key " + key); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /lib/src/main/res/values-pt-rBR/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cancelar 5 | OK 6 | Salvar 7 | Feito 8 | Adicionar regra 9 | Adicionar regra para 24/7 10 | Adicionar regra para feriados 11 | Carregar modelo 12 | Salvar para modelo 13 | Gerenciar modelos 14 | Atualizar 15 | Excluir tudo 16 | Carregar modelos 17 | Gerenciar modelos 18 | Salvar modelo 19 | Sim 20 | Não 21 | Início 22 | End 23 | 24 | 25 | Editar modelo 26 | Nome: 27 | Padrão: 28 | Chave: 29 | %1$s (padrão) 30 | %1$s %2$s 31 | Tudo 32 | Horário 33 | Tempos de coleta 34 | Supervisionado 35 | Iluminado 36 | 37 | Grupo%1$s 38 | Regra%1$s 39 | 40 | Duplicado 41 | 42 | 43 | Normal 44 | Aditivo 45 | Intervalo (minutos) 46 | Intervalo (anos) 47 | Intervalo (semanas) 48 | 49 | Intervalo de datas 50 | Data 51 | Semanas 52 | Anos 53 | Modificador 54 | Comentário 55 | 56 | 24 horas, 7 dias por semana 57 | 58 | até 59 | 60 | Páscoa 61 | 62 | Nascer do sol 63 | Pôr do sol 64 | Alvorada 65 | Crepúsculo 66 | 67 | Feriados públicos 68 | Feriados escolares 69 | 70 | Seg 71 | Ter 72 | Qua 73 | Qui 74 | Sex 75 | Sáb 76 | Dom 77 | 78 | Jan 79 | Fev 80 | Mar 81 | Abr 82 | Mai 83 | Jun 84 | Jul 85 | Ago 86 | Set 87 | Out 88 | Nov 89 | Dez 90 | 91 | aberto 92 | fechado 93 | desconhecido 94 | 95 | antes 96 | depois 97 | 98 | 99 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/week_range.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 20 | 28 | 38 | 46 | 53 | 61 | 66 | 67 | 74 | 79 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/time_event_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | 22 | 23 | 35 | 36 | 48 | 49 | 58 | 63 | 72 | 73 | 82 | 87 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/pickers/ValuePicker.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.pickers; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.DialogInterface; 5 | import android.os.Bundle; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import androidx.annotation.NonNull; 9 | import androidx.appcompat.app.AlertDialog.Builder; 10 | import androidx.appcompat.app.AppCompatDialog; 11 | import androidx.fragment.app.Fragment; 12 | import androidx.fragment.app.FragmentManager; 13 | import ch.poole.openinghoursfragment.CancelableDialogFragment; 14 | import ch.poole.openinghoursfragment.R; 15 | import ch.poole.openinghoursfragment.Util; 16 | import ch.poole.android.numberpickerview.library.NumberPickerView; 17 | 18 | /** 19 | * Display a dialog allowing the user to set a single numeric value 20 | * 21 | */ 22 | public class ValuePicker extends CancelableDialogFragment { 23 | public static final int NOTHING_SELECTED = Integer.MIN_VALUE; 24 | 25 | private static final String TITLE_KEY = "title"; 26 | private static final String MIN_KEY = "min"; 27 | private static final String MAX_KEY = "max"; 28 | private static final String CURRENT_KEY = "current"; 29 | private static final String STYLE_RES = "styleRes"; 30 | 31 | private static final String TAG = "fragment_valuepicker"; 32 | 33 | /** 34 | * Show the range picker dialog 35 | * 36 | * @param parentFragment Fragment that called this 37 | * @param title resource id for the title to display 38 | * @param min minimum range value 39 | * @param max maximum range value 40 | * @param current initial value 41 | * @param styleRes resource id for style/theme 42 | */ 43 | public static void showDialog(Fragment parentFragment, int title, int min, int max, int current, int styleRes) { 44 | dismissDialog(parentFragment, TAG); 45 | 46 | FragmentManager fm = parentFragment.getChildFragmentManager(); 47 | ValuePicker valuePickerFragment = newInstance(title, min, max, current, styleRes); 48 | valuePickerFragment.show(fm, TAG); 49 | } 50 | 51 | /** 52 | * Create a new instance of RangePicker 53 | * 54 | * @param title resource id for the title to display 55 | * @param min minimum range value 56 | * @param max maximum range value 57 | * @param current initial value 58 | * @param styleRes resource id for style/theme 59 | * @return an instance of ValuePicker 60 | */ 61 | private static ValuePicker newInstance(int title, int min, int max, int current, int styleRes) { 62 | ValuePicker f = new ValuePicker(); 63 | Bundle args = new Bundle(); 64 | args.putInt(TITLE_KEY, title); 65 | args.putInt(MIN_KEY, min); 66 | args.putInt(MAX_KEY, max); 67 | args.putInt(CURRENT_KEY, current); 68 | args.putInt(STYLE_RES, styleRes); 69 | 70 | f.setArguments(args); 71 | f.setShowsDialog(true); 72 | 73 | return f; 74 | } 75 | 76 | @NonNull 77 | @SuppressLint("InflateParams") 78 | @Override 79 | public AppCompatDialog onCreateDialog(Bundle savedInstanceState) { 80 | int title = getArguments().getInt(TITLE_KEY); 81 | final int min = getArguments().getInt(MIN_KEY); 82 | final int max = getArguments().getInt(MAX_KEY); 83 | int current = getArguments().getInt(CURRENT_KEY); 84 | final SetRangeListener listener = (SetRangeListener) getParentFragment(); 85 | 86 | Builder builder = Util.getAlertDialogBuilder(getContext(), getArguments().getInt(STYLE_RES)); 87 | builder.setTitle(title); 88 | 89 | final LayoutInflater inflater = getActivity().getLayoutInflater(); 90 | 91 | View layout = inflater.inflate(R.layout.valuepicker, null); 92 | builder.setView(layout); 93 | 94 | String[] startValues = new String[max - min + 1]; 95 | for (int i = min; i <= max; i++) { 96 | startValues[i - min] = Integer.toString(i); 97 | } 98 | final NumberPickerView npvStart = (NumberPickerView) layout.findViewById(R.id.start); 99 | npvStart.setDisplayedValues(startValues); 100 | npvStart.setMinValue(min); 101 | npvStart.setMaxValue(max); 102 | npvStart.setValue(current); 103 | 104 | builder.setPositiveButton(R.string.spd_ohf_ok, (DialogInterface dialog, int which) -> listener.setRange(npvStart.getValue(), -1)); 105 | builder.setNeutralButton(R.string.spd_ohf_cancel, null); 106 | 107 | return builder.create(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | Please see the github repository for a complete list of changes: https://github.com/simonpoole/OpeningHoursFragment/commits/master 2 | 3 | 0.16.0: 4 | 5 | - Use the provided style for AlertDialog creation 6 | - Add method method for displaying the template management modal with a provided style/theme id. 7 | 8 | 0.15.0: 9 | 10 | - Bump the minimum API to 21 and target API 35. 11 | - Fix for default template loading. 12 | 13 | 0.14.3: 14 | 15 | - Properly constrain selection position when clicking on error message 16 | 17 | 0.14.2: 18 | 19 | - Always run rebuild 20 | 21 | 0.14.1: 22 | 23 | - Fix regression where changes in OH mode wouldn't update the value 24 | 25 | 0.14.0: 26 | 27 | - Improve messaging when updating template 28 | - Improve behaviour when entering/correcting OH text directly 29 | - Fix CI 30 | - Display a message if the user didn't select a file 31 | - Update translations 32 | 33 | 0.13.5: 34 | - Migrate to hard fork of NumberPickerView 35 | 36 | 0.13.4: 37 | - Check that token is actually present in exceptions 38 | 39 | 0.13.3: 40 | - Save the value of useFragmentCallback 41 | 42 | 0.13.2: 43 | - Fix fragment mode if onAttach is called before onCreateView 44 | 45 | 0.13.0: 46 | - Support running as a 1 level deep nested Fragment 47 | - Update translations 48 | 49 | 0.12.0: 50 | - Update parser dependency 51 | 52 | 0.11.0: 53 | - Support custom key values in templates with SQL wild cards 54 | - Update translations 55 | 56 | 0.10.1: 57 | - Always set the editor action listener 58 | - Set IME options to actionDone. 59 | 60 | 0.10.0: 61 | - Don't automatically up date form from string, wait for "Enter" or the like. 62 | - Add support for open ended year ranges. 63 | - Update parser dependency. 64 | - Improve CI. 65 | - Publish on maven central 66 | 67 | 0.9.3: 68 | - Update number picker view and rangebar dependencies 69 | 70 | 0.9.2: 71 | - Update translations 72 | 73 | 0.9.1: 74 | - Move to sonatype / maven central for publishing 75 | 76 | 0.9.0: 77 | - Multi-error support 78 | 79 | 0.8.2: 80 | - Update parser dependency 81 | - Update translations. 82 | 83 | 0.8.1: 84 | - Update parser dependency 85 | - Update translations. 86 | 87 | 0.8.0: 88 | - Migrate to androidx. 89 | 90 | 0.7.0: 91 | - Update parser dependency 92 | - Add locale parameter to factory methods 93 | - Add error message display 94 | - Use current year as default start year for new date ranges. 95 | 96 | 0.6.8: 97 | - Fix issue with using description instead of value in mixed mode. 98 | 99 | 0.6.5: 100 | - Call through to onDismiss in the TemplateManagementDialog, fixes crash in onResume in the parent activity. 101 | 102 | 0.6.4: 103 | - Update Rangebar to 1.5 the previous version had issues with handling clicks on real devices. 104 | 105 | 0.6.3: 106 | - Fix various small issues which turned up when integrating the new functionality in 0.6 with Vespucci. 107 | 108 | 0.5.5: 109 | - Translation improvements. 110 | 111 | 0.5.4: 112 | - Update translations. 113 | 114 | 0.5.3: 115 | - Fix issue with day offsets not being displayed for end dates 116 | - Update translations. 117 | 118 | 0.5.2: 119 | - Enable RTL support (but there are no such translations currently) 120 | - Show templates for specific key at top of list 121 | - Update translations. 122 | 123 | 0.5.1: 124 | - Properly enable save button 125 | 126 | 0.5.0: 127 | - Support showing the list of templates instead of applying the default for empty entries 128 | - Support for mixed type values 129 | 130 | 0.4.2: 131 | - Protect against NPW when enabling save button 132 | 133 | 0.4.1: 134 | - When displaying extended times use "normal" times when possible 135 | 136 | 0.3.5: 137 | - Make dialog fill all available space 138 | - Update support lib and build target 139 | 140 | 0.3.4: 141 | - Protect again NPE 142 | 143 | 0.3.2: 144 | - Fix clearing if string can't be parsed 145 | 146 | 0.3.0: 147 | - Add facilities to duplicate rules and clear existing ones completely 148 | 149 | 0.2.2: 150 | - Support wrap around weekday ranges 151 | 152 | 0.2.1: 153 | - Support "occurrence in month" notation for dates 154 | - Bug fixes 155 | 156 | 0.2.0: 157 | - Support callbacks to a Fragment 158 | - Support separate templates for the major OH like keys 159 | 160 | 0.1.12: 161 | - Show negative day offsets for dates correctly 162 | 163 | 0.1.11: 164 | - Don't overwrite invalid strings when adding a rule 165 | - Try to avoid serialsation issues 166 | 167 | 0.1.10: 168 | - Protect against NPE when adding new rule to invalid string 169 | 170 | 0.1.9: 171 | - User the display string for variable dates (Easter) 172 | 173 | 0.1.8: 174 | - Enforce OSM 255 char tag length restriction 175 | 176 | 0.1.6: 177 | - Add shortcut for adding holidays. 178 | 179 | 0.1.5: 180 | - Typo fixes 181 | - Add missing dependencies to pom file. 182 | 183 | 0.1.4: 184 | - Improved documentation 185 | - Added missing time span menu item. 186 | 187 | 0.1.3: 188 | - First semi-public release. -------------------------------------------------------------------------------- /lib/src/main/res/values-ko/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 취소 5 | 확인 6 | 저장 7 | 완료 8 | 여는 시간 값의 길이가 최대 255자를 넘는 %1$d자입니다.\n\n저장하기 전에 문자열을 간단히 해주세요! 9 | 올바르지 않는 여는 시간 문자열로 규칙을 추가할 수 없습니다. 정정하거나 삭제해주세요! 10 | 11 | 규칙 추가 12 | 휴일에 대한 규칙 추가 13 | 템플릿 불러오기 14 | 템플릿으로 저장 15 | 템플릿 관리 16 | 새로 고침 17 | 템플릿 불러오기 18 | 템플릿 관리 19 | 템플릿 저장 20 | 21 | 아니오 22 | 시작 23 | 24 | 25 | 템플릿 편집 26 | 이름: 27 | 기본값: 28 | 키: 29 | %1$s (기본값) 30 | 모두 31 | 여는 시간 32 | 서비스 시간 33 | 감독 장소 34 | 조명 35 | 36 | 그룹 %1$s 37 | 규칙 %1$s 38 | 39 | 40 | 기본 템플릿 불러옴 41 | 42 | 43 | 삭제 44 | 수정자/댓글 추가 45 | 주 범위 추가 46 | 휴일 추가 47 | 48 | 시간 49 | 자정에 시작 50 | 51 | 날짜 범위 추가 ... 52 | 오프셋과 함께... 53 | 54 | 년 범위 추가 55 | 규칙 유형 보이기 56 | 위로 이동 57 | 아래로 이동 58 | 시간 픽커 표시 59 | 오프셋 보이기 60 | 시작 오프셋 보이기 61 | 끝 오프셋 보이기 62 | 간격 보이기 63 | 사용 보이기 64 | 65 | 66 | 67 | 보통 68 | 폴백 69 | 70 | 간격 (분) 71 | 간격 (년) 72 | 간격 (주) 73 | 74 | 오프셋 (±분) 75 | 오프셋 (±일) 76 | 77 | 날짜 범위 78 | 날짜 79 | 주 범위 80 | 81 | 년 범위 82 | 83 | 수정자 84 | 댓글 85 | 86 | 까지 87 | 88 | 공휴일 89 | 학교 휴일 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 1월 100 | 2월 101 | 3월 102 | 4월 103 | 5월 104 | 6월 105 | 7월 106 | 8월 107 | 9월 108 | 10월 109 | 11월 110 | 12월 111 | 112 | 열림 113 | 닫음 114 | 끄기 115 | 알 수 없음 116 | 117 | 이전 118 | 이후 119 | 120 | 121 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/time_start_event_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | 21 | 22 | 32 | 33 | 42 | 47 | 56 | 57 | 58 | 83 | 84 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/daterange_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 16 | 17 | 23 | 24 | 32 | 39 | 44 | 51 | 57 | 64 | 65 | 76 | 84 | 91 | 96 | 103 | 109 | 116 | 117 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/template_item.xml: -------------------------------------------------------------------------------- 1 | 2 | > 11 | 19 | 29 | 37 | 51 | 59 | 72 | 82 | 90 | 102 | 110 | 119 | -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/Util.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Rect; 7 | import android.util.Log; 8 | import android.view.ContextThemeWrapper; 9 | import android.view.Gravity; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.widget.ScrollView; 13 | import android.widget.Spinner; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | import androidx.annotation.NonNull; 17 | import androidx.annotation.Nullable; 18 | import androidx.appcompat.app.AlertDialog; 19 | import androidx.core.widget.NestedScrollView; 20 | 21 | 22 | public final class Util { 23 | protected static final String DEBUG_TAG = "Util"; 24 | 25 | /** 26 | * Private default constructor 27 | */ 28 | private Util() { 29 | // Empty 30 | } 31 | 32 | /** 33 | * Scroll to the supplied view 34 | * 35 | * This code is from vespucci and licensed under the Apache 2.0 licence 36 | * 37 | * @author simon 38 | * 39 | * @param sv the ScrollView or NestedScrollView to scroll 40 | * @param row the row to display, if null scroll to top or bottom of sv 41 | * @param up if true scroll to top if row is null, otherwise scroll to bottom 42 | * @param force if true always try to scroll even if row is already on screen 43 | */ 44 | public static void scrollToRow(@NonNull final View sv, @Nullable final View row, final boolean up, boolean force) { 45 | Rect scrollBounds = new Rect(); 46 | sv.getHitRect(scrollBounds); 47 | if (row != null && row.getLocalVisibleRect(scrollBounds) && !force) { 48 | return; // already on screen 49 | } 50 | if (row == null) { 51 | sv.post(() -> { 52 | if (sv instanceof ScrollView) { 53 | ((ScrollView) sv).fullScroll(up ? View.FOCUS_UP : View.FOCUS_DOWN); 54 | } else if (sv instanceof NestedScrollView) { 55 | ((NestedScrollView) sv).fullScroll(up ? View.FOCUS_UP : View.FOCUS_DOWN); 56 | } else { 57 | Log.e(DEBUG_TAG, "scrollToRow unexpected view " + sv); 58 | } 59 | }); 60 | } else { 61 | sv.post(() -> { 62 | final int target = up ? row.getTop() : row.getBottom(); 63 | if (sv instanceof ScrollView) { 64 | ((ScrollView) sv).smoothScrollTo(0, target); 65 | } else if (sv instanceof NestedScrollView) { 66 | ((NestedScrollView) sv).smoothScrollTo(0, target); 67 | } else { 68 | Log.e(DEBUG_TAG, "scrollToRow unexpected view " + sv); 69 | } 70 | }); 71 | } 72 | } 73 | 74 | /** 75 | * Display a toast at the top of the screen 76 | * 77 | * @param context android context 78 | * @param msg the message to display 79 | */ 80 | public static void toastTop(@NonNull Context context, String msg) { 81 | LayoutInflater inflater = LayoutInflater.from(context); 82 | View layout = inflater.inflate(R.layout.toast, null); 83 | TextView text = (TextView) layout.findViewById(R.id.text); 84 | text.setText(msg); 85 | Toast toast = new Toast(context); 86 | toast.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP, 0, 0); 87 | toast.setDuration(Toast.LENGTH_LONG); 88 | toast.setView(layout); 89 | toast.show(); 90 | } 91 | 92 | /** 93 | * Display a toast at the top of the screen 94 | * 95 | * @param context android context 96 | * @param msg the message resource id to display 97 | */ 98 | public static void toastTop(@NonNull Context context, int msg) { 99 | LayoutInflater inflater = LayoutInflater.from(context); 100 | View layout = inflater.inflate(R.layout.toast, null); 101 | TextView text = (TextView) layout.findViewById(R.id.text); 102 | text.setText(msg); 103 | Toast toast = new Toast(context); 104 | toast.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP, 0, 0); 105 | toast.setDuration(Toast.LENGTH_LONG); 106 | toast.setView(layout); 107 | toast.show(); 108 | } 109 | 110 | /** 111 | * Set initially selected value on a spinner 112 | * 113 | * @param res the Resources 114 | * @param valuesId the value array resource id 115 | * @param spinner the Spinner 116 | * @param value the value to set 117 | */ 118 | public static void setSpinnerInitialEntryValue(@NonNull Resources res, int valuesId, @NonNull Spinner spinner, @Nullable String value) { 119 | final TypedArray values = res.obtainTypedArray(valuesId); 120 | for (int i = 0; i < values.length(); i++) { 121 | if ((value == null && "".equals(values.getString(i))) || (value != null && value.equals(values.getString(i)))) { 122 | spinner.setSelection(i); 123 | break; 124 | } 125 | } 126 | values.recycle(); 127 | } 128 | 129 | /** 130 | * Get a correctly themed AlerDialog.Builder 131 | * 132 | * @param context an Android Context 133 | * @param themeRes theme to use, if -1 fallback to default 134 | * @return a AlerDialog.Builder 135 | */ 136 | public static AlertDialog.Builder getAlertDialogBuilder(@NonNull Context context, int themeRes) { 137 | return new AlertDialog.Builder(new ContextThemeWrapper(context, themeRes > 0 ? themeRes : R.style.Theme_AlertDialog)); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /lib/src/androidTest/java/ch/poole/openinghoursfragment/templates/TemplatesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not 3 | * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | */ 5 | 6 | package ch.poole.openinghoursfragment.templates; 7 | 8 | import static org.junit.Assert.assertNotNull; 9 | import static org.junit.Assert.assertNull; 10 | import static org.junit.Assert.assertTrue; 11 | import static org.junit.Assert.fail; 12 | 13 | import java.io.File; 14 | import java.io.FileInputStream; 15 | import java.io.FileNotFoundException; 16 | import java.io.FileOutputStream; 17 | import java.io.InputStream; 18 | 19 | import org.junit.Before; 20 | import org.junit.Rule; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | 24 | import androidx.fragment.app.Fragment; 25 | import androidx.fragment.app.FragmentActivity; 26 | import androidx.fragment.app.FragmentManager; 27 | import androidx.fragment.app.FragmentTransaction; 28 | import androidx.test.ext.junit.runners.AndroidJUnit4; 29 | import androidx.test.filters.LargeTest; 30 | import androidx.test.platform.app.InstrumentationRegistry; 31 | import androidx.test.rule.ActivityTestRule; 32 | import androidx.test.uiautomator.UiDevice; 33 | import androidx.test.uiautomator.UiObject; 34 | import androidx.test.uiautomator.UiObjectNotFoundException; 35 | import androidx.test.uiautomator.UiSelector; 36 | import ch.poole.openinghoursfragment.OpeningHoursFragment; 37 | import ch.poole.openinghoursfragment.R; 38 | import ch.poole.openinghoursfragment.TestActivity; 39 | import ch.poole.openinghoursfragment.TestUtils; 40 | import ch.poole.openinghoursfragment.ValueWithDescription; 41 | 42 | @RunWith(AndroidJUnit4.class) 43 | @LargeTest 44 | public class TemplatesTest { 45 | 46 | private OpeningHoursFragment fragment; 47 | private UiDevice device; 48 | FragmentManager fm; 49 | TemplateDatabaseHelper db; 50 | FragmentActivity activity; 51 | 52 | @Rule 53 | public ActivityTestRule mActivityRule = new ActivityTestRule<>(TestActivity.class); 54 | 55 | @Before 56 | public void setup() { 57 | device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 58 | activity = mActivityRule.getActivity(); 59 | fm = activity.getSupportFragmentManager(); 60 | FragmentTransaction ft = fm.beginTransaction(); 61 | Fragment prev = fm.findFragmentByTag("fragment_openinghours"); 62 | if (prev != null) { 63 | ft.remove(prev); 64 | } 65 | ft.commit(); 66 | 67 | // create the template database 68 | db = new TemplateDatabaseHelper(activity); 69 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); 70 | InputStream is = loader.getResourceAsStream("templates.json"); 71 | TemplateDatabase.loadJson(db.getWritableDatabase(), is, true); 72 | } 73 | 74 | @Test 75 | public void manageTemplates() { 76 | ValueWithDescription key = new ValueWithDescription("opening_hours", "Opening hours"); 77 | fragment = OpeningHoursFragment.newInstance(key, "AT", null, null, R.style.Theme_DialogLight, 5, true, null, null); 78 | fragment.show(fm, "fragment_openinghours"); 79 | assertNotNull(TestUtils.findTextContains(device, false, "Austria")); 80 | assertNull(TestUtils.findTextContains(device, false, "Switzerland")); 81 | assertTrue(TestUtils.clickText(device, false, "Weekdays", true)); 82 | assertTrue(TestUtils.clickMenuButton("more button", false, true)); 83 | assertTrue(TestUtils.clickText(device, false, "Manage templates", true)); 84 | assertNotNull(TestUtils.findTextContains(device, false, "Austria")); 85 | assertNull(TestUtils.findTextContains(device, false, "Switzerland")); 86 | assertTrue(TestUtils.clickMenuButton("more button", false, true)); 87 | assertTrue(TestUtils.clickText(device, false, "Show all", true)); 88 | assertNotNull(TestUtils.findTextContains(device, false, "Austria")); 89 | assertNotNull(TestUtils.findTextContains(device, false, "Switzerland")); 90 | UiObject template = device.findObject(new UiSelector().textContains("Austria")); 91 | try { 92 | assertTrue(template.clickAndWaitForNewWindow()); 93 | } catch (UiObjectNotFoundException e) { 94 | fail(e.getMessage()); 95 | } 96 | assertTrue(TestUtils.clickText(device, false, "Delete", true)); 97 | assertNull(TestUtils.findTextContains(device, false, "Austria")); 98 | } 99 | 100 | @Test 101 | public void useDefault() { 102 | ValueWithDescription key = new ValueWithDescription("opening_hours", "Opening hours"); 103 | fragment = OpeningHoursFragment.newInstance(key, "CH", null, null, R.style.Theme_DialogLight, 5, false, null, null); 104 | fragment.show(fm, "fragment_openinghours"); 105 | assertNotNull(TestUtils.findTextContains(device, false, "Mo-Fr 09:00-12:00,13:30-18:30; Sa 09:00-17:00; PH closed")); 106 | } 107 | 108 | @Test 109 | public void exportImportTemplates() { 110 | File dir = activity.getExternalCacheDir(); 111 | final File file = new File(dir, "template_out.json"); 112 | try { 113 | assertTrue(TemplateDatabase.writeJSON(db.getWritableDatabase(), new FileOutputStream(file))); 114 | TemplateDatabase.loadJson(db.getWritableDatabase(), new FileInputStream(file), true); 115 | } catch (FileNotFoundException e) { 116 | fail(e.getMessage()); 117 | } finally { 118 | file.delete(); 119 | } 120 | manageTemplates(); 121 | } 122 | } -------------------------------------------------------------------------------- /lib/src/main/java/ch/poole/openinghoursfragment/pickers/RangePicker.java: -------------------------------------------------------------------------------- 1 | package ch.poole.openinghoursfragment.pickers; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.DialogInterface; 5 | import android.os.Bundle; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import androidx.annotation.NonNull; 9 | import androidx.appcompat.app.AlertDialog.Builder; 10 | import androidx.appcompat.app.AppCompatDialog; 11 | import androidx.fragment.app.Fragment; 12 | import androidx.fragment.app.FragmentManager; 13 | import ch.poole.openinghoursfragment.CancelableDialogFragment; 14 | import ch.poole.openinghoursfragment.R; 15 | import ch.poole.openinghoursfragment.Util; 16 | import ch.poole.android.numberpickerview.library.NumberPickerView; 17 | 18 | /** 19 | * Display a dialog allowing the user to set a numeric range 20 | * 21 | */ 22 | public class RangePicker extends CancelableDialogFragment { 23 | public static final int NOTHING_SELECTED = Integer.MIN_VALUE; 24 | 25 | private static final String TITLE = "title"; 26 | private static final String MIN = "min"; 27 | private static final String MAX = "max"; 28 | private static final String START_CURRENT = "startCurrent"; 29 | private static final String END_CURRENT = "endCurrent"; 30 | private static final String STYLE_RES = "styleRes"; 31 | 32 | private static final String TAG = "fragment_rangepicker"; 33 | 34 | /** 35 | * Show the range picker dialog 36 | * 37 | * @param parentFragment Fragment that called this 38 | * @param title resource id for the title to display 39 | * @param min minimum range value 40 | * @param max maximum range value 41 | * @param startCurrent initial start value 42 | * @param endCurrent initial end value 43 | * @param styleRes resource id for style/theme 44 | */ 45 | public static void showDialog(Fragment parentFragment, int title, int min, int max, int startCurrent, int endCurrent, int styleRes) { 46 | dismissDialog(parentFragment, TAG); 47 | 48 | FragmentManager fm = parentFragment.getChildFragmentManager(); 49 | RangePicker rangePickerFragment = newInstance(title, min, max, startCurrent, endCurrent, styleRes); 50 | rangePickerFragment.show(fm, TAG); 51 | } 52 | 53 | /** 54 | * Create a new instance of RangePicker 55 | * 56 | * @param title resource id for the title to display 57 | * @param min minimum range value 58 | * @param max maximum range value 59 | * @param startCurrent initial start value 60 | * @param endCurrent initial end value 61 | * @param styleRes resource id for style/theme 62 | * @return an instance of RangePicker 63 | */ 64 | private static RangePicker newInstance(int title, int min, int max, int startCurrent, int endCurrent, int styleRes) { 65 | RangePicker f = new RangePicker(); 66 | Bundle args = new Bundle(); 67 | args.putInt(TITLE, title); 68 | args.putInt(MIN, min); 69 | args.putInt(MAX, max); 70 | args.putInt(START_CURRENT, startCurrent); 71 | args.putInt(END_CURRENT, endCurrent); 72 | args.putInt(STYLE_RES, styleRes); 73 | 74 | f.setArguments(args); 75 | f.setShowsDialog(true); 76 | 77 | return f; 78 | } 79 | 80 | @NonNull 81 | @SuppressLint("InflateParams") 82 | @Override 83 | public AppCompatDialog onCreateDialog(Bundle savedInstanceState) { 84 | int title = getArguments().getInt(TITLE); 85 | final int min = getArguments().getInt(MIN); 86 | final int max = getArguments().getInt(MAX); 87 | int startCurrent = getArguments().getInt(START_CURRENT); 88 | int endCurrent = getArguments().getInt(END_CURRENT); 89 | final SetRangeListener listener = (SetRangeListener) getParentFragment(); 90 | 91 | Builder builder = Util.getAlertDialogBuilder(getContext(), getArguments().getInt(STYLE_RES)); 92 | builder.setTitle(title); 93 | 94 | final LayoutInflater inflater = getActivity().getLayoutInflater(); 95 | 96 | View layout = inflater.inflate(R.layout.rangepicker, null); 97 | builder.setView(layout); 98 | 99 | String[] startValues = new String[max - min + 1]; 100 | for (int i = min; i <= max; i++) { 101 | startValues[i - min] = Integer.toString(i); 102 | } 103 | final NumberPickerView npvStart = (NumberPickerView) layout.findViewById(R.id.start); 104 | npvStart.setDisplayedValues(startValues); 105 | npvStart.setMinValue(min); 106 | npvStart.setMaxValue(max); 107 | npvStart.setValue(startCurrent); 108 | 109 | String[] endValues = new String[max - min + 2]; 110 | endValues[0] = "-"; 111 | for (int i = min; i <= max; i++) { 112 | endValues[i - min + 1] = Integer.toString(i); 113 | } 114 | final NumberPickerView npvEnd = (NumberPickerView) layout.findViewById(R.id.end); 115 | npvEnd.setDisplayedValues(endValues); 116 | npvEnd.setMinValue(min - 1); 117 | npvEnd.setMaxValue(max); 118 | if (endCurrent == NOTHING_SELECTED) { 119 | endCurrent = min - 1; 120 | } 121 | npvEnd.setValue(endCurrent); 122 | 123 | builder.setPositiveButton(R.string.spd_ohf_ok, (DialogInterface dialog, int which) -> { 124 | int endValue = npvEnd.getValue(); 125 | if (endValue == min - 1) { 126 | endValue = NOTHING_SELECTED; 127 | } 128 | listener.setRange(npvStart.getValue(), endValue); 129 | }); 130 | builder.setNeutralButton(R.string.spd_ohf_cancel, null); 131 | 132 | return builder.create(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /lib/src/main/res/values-tr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | İptal 5 | 6 | Tamam 7 | Kaydet 8 | Tamam 9 | Kural ekle 10 | 7/24 için kural ekle 11 | Tatil için kural ekle 12 | Şablon yükle 13 | Şablonu kaydet 14 | Şablonları yönet 15 | Yenile 16 | Hepsini sil 17 | Şablonları yükle 18 | Şablonları yönet 19 | Şablonu kaydet 20 | Evet 21 | Hayır 22 | Başla 23 | End 24 | Açılış saatleri dizisi 25 | 26 | Metin değerleri 27 | 28 | 29 | Şablonu düzenle 30 | İsim: 31 | Varsayılan: 32 | Anahtar: 33 | %1$s %2$s 34 | Tüm 35 | Açılış saatleri 36 | Servis zamanları 37 | Denetimli 38 | Işıklandırma 39 | 40 | Hafta içi öğle yemeği molası 41 | Hafta içi 42 | Haftada bir kez 43 | 44 | 45 | Sil 46 | Hafta aralığı ekle 47 | Tatil ekle 48 | 49 | Zaman aralığı ekle ... 50 | Gece yarısında başla 51 | 52 | Hafta içi gün aralığı ekle 53 | Tarih aralığı ekle ... 54 | Tarih - açık uçlu 55 | Değişken tarih - açık uçlu 56 | Yıl aralığı ekle 57 | Kural türünü göster 58 | Tıpkısı 59 | Yukarı taşı 60 | Aşağı taşı 61 | Zaman seçiciyi görüntüle 62 | 15 dakikalık işarete geç 63 | 5 dakikalık işarete geç 64 | 1 dakikalık işarete geç 65 | Aralığı göster 66 | Kullanımı göster 67 | 68 | 69 | 70 | Normal 71 | Aralık (dakika) 72 | Aralık (yıl) 73 | Aralık (hafta) 74 | 75 | Tarih aralığı 76 | Tarih 77 | Tarih (ayın hafta içi) 78 | Hafta aralığı 79 | Haftalar 80 | Yıl aralığı 81 | Yıllar 82 | Yorum 83 | 84 | Haftaiçi gibi kullanın 85 | 86 | Haftada 7 gün 24 saat 87 | 88 | a (e) 89 | 90 | Paskalya 91 | 92 | Gündoğumu 93 | Gün batımı 94 | Şafak 95 | Alacakaranlık 96 | 97 | Resmi tatil 98 | Okul tatilleri 99 | 100 | Ay içinde meydana gelme 101 | 102 | Pzt 103 | Sa 104 | Ça 105 | Pe 106 | Cu 107 | Cmt 108 | Pa 109 | 110 | Oca 111 | Şub 112 | Mar 113 | Nis 114 | May 115 | Haz 116 | Tem 117 | Ağu 118 | Eyl 119 | Eki 120 | Kas 121 | Ara 122 | 123 | 124 | kapalı 125 | devre dışı 126 | bilinmeyen 127 | 128 | önce 129 | sonra 130 | 131 | 132 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/daterangepicker.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 14 | 27 | 41 | 55 | 70 | 85 | 99 | 113 | 128 | 129 | -------------------------------------------------------------------------------- /lib/documentation/docs/help/zh-Hans/Opening hours.md: -------------------------------------------------------------------------------- 1 | # 开放街图营业时间编辑器 2 | 3 | OpenStreetMap 营业时间规范相当复杂,并不易于一个简单直观的使用者介面。 4 | 5 | 然而大部分的时候,你应该只会用到一小部分的定义。编辑器尝试在选单中隐藏更加难懂的功能,来达成这一点,大部分的时候减少“路上”设定,尽可能用预先设定的模版,加以小量修改。 6 | 7 | _本文档是早期文档,正在开发中_ 8 | 9 | ## 使用开放街图编辑器 10 | 11 | 在一般的工作流程中,你编辑的物件一般已经有开放时间的标签 (opening_hour、service_times 和 colllection_times),或是你可以重新设定物件的预设组合,用空白的开放时间栏位。如果你需要手动输入栏位,像是用 Vespucci,你可以在详情页面输入关键值,之后切换回栏位页签继续编辑。如果你相信开放时间标签必须多于预设组合的话,请在你的编辑器开启 issue。 12 | 13 | If you have defined a default template (do this via the "Manage templates" menu item) it will be loaded automatically when the editor is started with an empty value. With the "Load template" function you can load any saved template and with the "Save template" menu you can save the current value as a template. You can define separate templates and defaults for specific key, for example "opening_hours", "collection_times" and "service_times" or custom values. Further you can limit applicability of a template to a region and a specific identifier, typically an OSM top-level tag (for example amenity=restaurant). 14 | 15 | 当然,您可以从头开始构建营业时间的值,但我们建议使用现有的模板之一作为开始。 16 | 17 | 如果已经有营业时间的数值则会载入,另外会自动修正以符合营业时间规范。如果无法修正,则会在大概的位置显示错误讯息,原始的营业时间数值,等待人工修正。开放街图资料库中有大约1/4的营业时间数值有问题,但只有少于10%的状况是无法修正。详见 [OpeningHoursParser](https://github.com/simonpoole/OpeningHoursParser),并且看有那些允许的规范变体。 18 | 19 | ### 主菜单按键 20 | 21 | * __添加规则__:添加新规则。 22 | * __为假期添加规则__:当国家/地区改变时为假期添加新规则 23 | * __为 24/7 增加规则__:为总是开放的物件增加规则,开放时间规范不支持任何其他子值为24/7,但我们允许增加更高阶的选择器 (例如年份范围)。 24 | * __Load template__:加载现有模板。 25 | * __保存到模板__:将当前的营业时间值另存为模板,以作为备份为将来使用。 26 | * __管理模板__:编辑(例如更改名称)并删除现有模板。 27 | * __刷新__:重新加载营业时间值。 28 | * __全部删除__:删除所有规则 29 | 30 | ### 规则 31 | 32 | 默认规则被添加为_normal_规则,这意味着它们将在同一天覆盖先前规则的值。在指定延长时间时中,可能会出现一个问题,通常您随后会希望通过_Show rule type_菜单项将规则切换为_additive_。 33 | 34 | #### 规则菜单 35 | 36 | * __添加修饰符/注释__:更改此规则的效果并添加可选注释。 37 | * __添加假期__:为公共或学校假期添加选择器。 38 | * __增加时间跨度...__ 39 | * __时间-时间__:同一天的开始时间到结束时间。 40 | * __时间-延长时间__:第二天的开始时间到结束时间(例如26:00是第二天的凌晨02:00)。 41 | * __变化时间-时间__:从开始可变时间(黎明,黄昏,日出和日落)到同一天的结束时间。 42 | * __变化时间-延长时间__:从开始变化时间到第二天的结束时间。 43 | * __时间-变化时间__:开始时间到结束变化时间。 44 | * __变化时间-变化时间__:从开始变化时间到结束变化时间。 45 | * __时间__:一个时间点。 46 | * __开放时间__:从某个时间点开始。 47 | * __变化时间__:特定的变化时间 48 | * __变化时间 - 一直持续__:从变化时间到一直持续 49 | * __增加工作日范围__:添加基于工作日的选择器。 50 | * __增加日期范围...__ 51 | * __日期-日期__:从开始日期(年,月,日)到结束日期。 52 | * __变化日期-日期__:从开变化变日期(当前规范仅定义_easter_)到结束日期。 53 | * __日期-变化日期__:从开始日期到变化日期 54 | * __变化日期-变化日期__:从开始变化日期到结束变化日期 55 | * __每月发生-每月发生__:一个月中从开始的周间是相同的。 56 | * __日期- 每月发生__:一个月中周间发生,到特定日期结束 57 | * __日期-发生在月份__:从开始日舒到一个月中的周间结束。 58 | * __Occurrence in month - variable date__: from a start weekday occurrence in a month to an end variable date. 59 | * __Variable date - occurrence in month__: from a start variable date to an end weekday occurrence in a month. 60 | * __Date - open end__: from a start date onwards. 61 | * __Variable date - open end__: from a start variable date onwards. 62 | * __Occurrence in month - open end__: from a start weekday occurrence in a month onwards. 63 | * __With offsets...__: the same entries as above however with offsets specified (this is rarely used). 64 | * __Add year range...__ 65 | * __Add year range__: add a year based selector. 66 | * __Add starting year__: add an open ended year range. 67 | * __Add week range__: add a week number based selector. 68 | * __Duplicate__: create a copy of this rule and insert it after the current position. 69 | * __Show rule type__: display and allow changing of the rule type _normal_, _additive_ and _fallback_ (not available on the first rule). 70 | * __Move up__: move this rule up one position (not available on the first rule). 71 | * __Move down__: move this rule down one position. 72 | * __Delete__: delete this rule. 73 | 74 | ### Time spans 75 | 76 | To make editing time spans as easy as possible, we try to choose an optimal time range and granularity for the range bars when loading existing values. For new time spans the bars start at 6:00 (am) and have 15 minute increments, this can be changed via the menu. 77 | 78 | Clicking (not on the pins) the time bar will open the large time picker, when using the bars directly is too difficult. The time pickers extend in to the next day, so they are a simple way to extend a time range without having to delete and re-add the the range. 79 | 80 | #### Time span menu 81 | 82 | * __Display time picker__: show a large time picker for selecting start and end time, on very small displays this is the preferred way of changing times. 83 | * __Switch to 15 minute ticks__: use 15 minute granularity for the range bar. 84 | * __Switch to 5 minute ticks__: use 5 minute granularity for the range bar. 85 | * __Switch to 1 minute ticks__: use 1 minute granularity for the range bar, very difficult to use on a phone. 86 | * __Start at midnight__: start the range bar at midnight. 87 | * __Show interval__: show the interval field for specifying an interval in minutes. 88 | * __Delete__: delete this time span. 89 | 90 | ### Manage templates 91 | 92 | The template management dialog allows you to add, edit and delete templates. 93 | 94 | In Android 4.4 and later the following additional functionality is available from the menu button. 95 | 96 | * __Show all__: display all templates in the database. 97 | * __Save to file__: write the contents of the template database to a file. 98 | * __Load from file (replace)__: load templates from a file replacing the current contents of the database. 99 | * __Load from file__: load templates from a file retaining the current contents. 100 | 101 | #### Save and edit template dialogs 102 | 103 | The dialog allows you to set 104 | 105 | * __Name__ a descriptive name for the template. 106 | * __Default__ if checked this will be consider as a default template (typically further constrained by the other fields). 107 | * __Key__ the key this template is relevant for, if set to _Custom key_ you can add a non-standard value in the field below. The key values support SQL wild cards, that is _%_ matches zero or more characters, *_* matches a single character. Both wild card characters can be escaped with _\\_ for literal matches. 108 | * __Region__ the region the template is applicable to. 109 | * __Object__ an application specific string to use for matching. 110 | 111 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/openinghours.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 21 | 28 | 45 | 50 | 55 | 56 | 57 | 72 | 77 | 82 | 83 | 84 | 90 | 91 | 97 | 101 | 105 | 106 | 120 | 121 | 124 | 132 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /lib/documentation/docs/help/iw/Opening hours.md: -------------------------------------------------------------------------------- 1 | # עורך שעות הפתיחה של OpenStreetMap 2 | 3 | מפרט שעות הפתיחה של OpenStreetMap הוא די מורכב ולא מסביר פנים בהיבט של מנשק משתמש פשוט וברור. 4 | 5 | עם זאת, סביר להניח שברוב הזמן יידרש לך רק חלק מצומצם מתוך המנגנון. העורך לוקח זאת בחשבון תוך הסתרת תכונות שאינן שימושיות בתפריטים וברוב המקרים מצמצם את השימוש השוטף להתאמות קטנות של תבניות מותאמות אישית. 6 | 7 | _תיעוד זה הוא ראשוני בלבד וכרגע מתבצעות עליו עבודות עריכה נוספות_ 8 | 9 | ## שימוש בעורך שעות הפתיחה 10 | 11 | במצב עבודה טיפוסי לפריט שבחרת לערוך כבר יש תגית שעות פתיחה (opening_hours,‏ service_times ו־collection_times) או שניתן להחיל מחדש את הערכה עבור הפריט כדי לקבל שדה שעות פתיחה ריק. אם יש לך צורך בהוספת השדה ידנית וניסית לעשות זאת דרך וספוצ׳י ניתן להקליד את שם המפתח בעמוד הפרטים ואז לחזור ללשונית מבוססת הטפסים לטובת עריכה. אם לדעתך תגית שעות הפתיחה אמורה להיות חלק מהערכה הראשונית, נא לדווח על תקלה כנגד העורך שלך. 12 | 13 | אם הגדרת תבנית בררת מחדל (אפשר לעשות את זה דרך הפריט „ניהול תבניות” בתפריט) היא תיטען אוטומטית עם הפעלת העורך עם ערך ריק. בעזרת הפונקציה „טעינת תבנית” אפשר לטעון כל תבנית שמורה שהיא ועם התפריט „שמירת תבנית” אפשר לשמור את הערך הנוכחי כתבנית. אפשר להגדיר תבניות נפרדות ובררות מחדל למפתח מסוים, למשל „opening_hours”,‏ „collection_times” ו־„service_times” או ערכים משלך. נוסף על כך, ניתן להגביל את היכולת להחיל תבנית על איזור ומזהה מסוים, בדרך כלל תגית ברמה עליונה של OSM (למשל amenity=restaurant). 14 | 15 | באופן טבעי ניתן להרכיב ערך שעות פתיחה מאפשר, אך אנו ממליצים להשתמש באחת מהתבניות הקיימות כנקודת התחלה. 16 | 17 | אם נטען ערך קיים של שעות פתיחה, מתבצע ניסיון לתקן אותו אוטומטית כדי להתאים למפרט שעות הפתיחה. אם פעולה כזו אינה אפשרית המיקום בקירוב בו התרחשה השגיאה יודגש בתצוגה של ערכי שעות הפתיחה הגולמיים ותהיה לך אפשרות לנסות ולתקן זאת ידנית. בערך ברבע מערכי שעות הפתיחה במסד הנתונים של OpenStreetMap יש תקלות אך פחות מ־10% אינן ניתנות לתיקון, יש לעיין בערך [OpeningHoursParser](https://github.com/simonpoole/OpeningHoursParser) (מנתח שעות פתיחה) לקבלת מידע נוסף על אילו סטיות מהמפרט נסבלות. 18 | 19 | ### כפתור תפריט ראשי 20 | 21 | * __הוספת כלל__: הוספת כלל חדש. 22 | * __הוספת כלל לחגים__: הוספת כלל חדש לחג יחד עם שינוי מצב. 23 | * __הוספת כלל ל־24/7__: הוספת כלל לפריט שתמיד פתוח, מפרט שעות הפתיחה לא תומך בערכים נגזרים עבור 24/7 עם זאת אנו מאפשרים הוספת בוררים ברמה גבוהה יותר (למשל: טווח שנים). 24 | * __טעינת תבנית__: טעינת תבנית קיימת. 25 | * __שמירה לתבנית__: שמירת ערך שעות הפתיחה הנוכחי כתבנית לשימוש עתידי. 26 | * __ניהול תבניות__: עריכה, למשל שינוי השם ומחיקה של תבניות קיימות. 27 | * __רענון__: פענוח ערך שעות הפתיחה מחדש. 28 | * __מחיקת הכול__: הסרת כל הכללים. 29 | 30 | ### כללים 31 | 32 | כללי בררת המחדל נוספו ככללים מסוג _רגיל_, הגדרה זו מרמזת על כך שהם יידרסו על ידי הערכים של הכללים הקודמים עבור אותם ימים. מצב זה עשוי להדאיג בהגדרת זמנים מורחבים, במיוחד עקב החלפת כללים דרך רשומת התפריט _הצגת סוג כלל_ לכלל _מתווסף_. 33 | 34 | #### תפריט כללים 35 | 36 | * __הוספת מצב/תגובה__: שינוי ההשפעה של הכלל הזה והוספת תגובה כרשות. 37 | * __הוספת חג__: הוספת בורר לחגים ציבוריים או חגי בית ספר. 38 | * __הוספת טווח זמן…__ 39 | * __שעה - שעה__: שעת התחלה עד שעת סיום באותו היום. 40 | * __שעה - שעה מורחבת__: שעת התחלה עד שעת סיום ביום שלמחרת (למשל: 26:00 זה 02:00 לפנות בוקר ביום הבא). 41 | * __שעה משתנה - שעה__: משעת התחלה משתנה (שחר, דמדומים, זריחה ושקיעה) עד לשעת סיום באותו היום. 42 | * __שעה משתנה - שעה מורחבת__: משעת התחלה משתנה ועד לשעת סיום ביום שלמחרת. 43 | * __שעה - שעה משתנה__: שעת התחלה עד שעת סיום משתנה. 44 | * __שעה משתנה - שעה משתנה__: שעת התחלה משתנה עד שעת סיום משתנה. 45 | * __שעה__: נקודה בזמן. 46 | * __שעה - הודעה חדשה__: מנקודת התחלה בזמן ואילך. 47 | * __שעה משתנה__: בשעה משתנה 48 | * __שעה משתנה - הודעה חדשה__: משעת התחלה משתנה ואילך 49 | * __הוספת טווח ימים בשבוע__: הוספת בורר על סמך ימים בשבוע. 50 | * __הוספת טווח תאריכים…__ 51 | * __תאריך - תאריך__: מתאריך התחלה (שנה, חודש, יום) עד לתאריך סיום. 52 | * __תאריך משתנה - תאריך__: מתאריך התחלה משתנה (כרגע המפרט מגדיר רק את _פסחא_) עד לתאריך סיום. 53 | * __תאריך - תאריך משתנה__: מתאריך התחלה ועד תאריך משתנה. 54 | * __תאריך משתנה - תאריך משתנה__: מתאריך התחלה משתנה עד תאריך סיום משתנה. 55 | * __פעם בחודש - פעם בחודש__: החל ממופע של יום בשבוע מתוך חודש עד לאחד נוסף כזה. 56 | * __פעם בחודש - תאריך__: החל ממופע של יום בשבוע מתוך חודש עד לתאריך סיום. 57 | * __תאריך - פעם בחודש__: מתאריך התחלה עד מופע של יום בשבוע מתוך חודש. 58 | * __פעם בחודש - תאריך משתנה__: החל ממופע של יום בשבוע מתוך חודש עד לתאריך סיום משתנה. 59 | * __תאריך משתנה - פעם בחודש__: מתאריך התחלה משתנה עד מופע של יום בשבוע מתוך חודש. 60 | * __שעה - הודעה חדשה__: משעה מסוימת ואילך. 61 | * __תאריך משתנה - הודעה חדשה__: מתאריך התחלה משתנה ואילך. 62 | * __פעם בחודש - הודעה חדשה__: החל ממופע של יום בשבוע מתוך חודש ואילך. 63 | * __עם איזונים…__: אותן הרשומות כלעיל רק עם איזונים (בקושי בשימוש). 64 | * __הוספת טווח שנים…__ 65 | * __הוספת טווח שנים__: הוספת בורר לבחירת שנים. 66 | * __הוספת שנת התחלה__: הוספת טווח שנים עם סוף פתוח. 67 | * __הוספת טווח שבועות__: הוספת בורר לבחירת מספרי שבועות. 68 | * __שכפול__: יצירת עותק של הכלל הזה והוספתו לאחר המיקום הנוכחי. 69 | * __הצגת סוג כלל__: להציג ולאפשר לשנות את סוג הכלל _רגיל_, _מתווסף_ ו_עתודה_ (לא זמין בתור הכלל הראשון). 70 | * __העברה למעלה__: העלאת הכלל הזה בשורה אחת למעלה (לא זמין עבור הכלל הראשון). 71 | * __העברה למטה__: הורדת הכלל הזה בשורה אחת למטה. 72 | * __מחיקה__: מחיקת הכלל הזה. 73 | 74 | ### טווחי שעות 75 | 76 | כדי להקל על עריכת טווי שעות עד כמה שניתן, אנו מנסים לבחור טווח שעות מיטבי ויכולת בחירה מוגדרת עבור סרגלי הטווח בעת טעינת ערכים קיימים. לטווחי שעות חדשים, הסרגלים מתחילים ב־6:00 בבוקר וניתן להגדיל ב־15 דקות בכל צעד, ניתן לשנות זאת דרך התפריט. 77 | 78 | לחיצה (לא על הנעצים) על סרגל הזמן פותחת את בורר הזמן הגדול, כאשר השימוש הישיר בסרגלים הוא מסובך מדי. בוררי הזמן מתרחבים ליום הבא, לכן הם מעניקים דרך פשוטה להרחיב טווח זמן מבלי למחוק ולהוסיף את הטווח מחדש. 79 | 80 | #### תפריט טווחי שעות 81 | 82 | * __הצגת בורר זמן__: הצגת בורר זמן גדול לבחירת זמן התחלה וסיום, בתצוגות קטנות מאוד זו הדרך המועדפת לעריכת זמנים. 83 | * __מעבר לפעימות של 15 דקות__: להשתמש בדיוק של 15 דקות עבור סרגל הטווח. 84 | * __מעבר לפעימות של 5 דקות__: להשתמש בדיוק של 5 דקות עבור סרגל הטווח. 85 | * __מעבר לפעימות של דקה__: להשתמש בדיוק של דקה עבור סרגל הטווח, לא נוח לשימוש בטלפון. 86 | * __התחלה בחצות__: התחלת סרגל הטווח בחצות. 87 | * __הצגת הפרש__: הצגת שדה ההפרש לציון הפרש בדקות. 88 | * __מחיקה__: מחיקת טווח הזמן הזה. 89 | 90 | ### ניהול תבניות 91 | 92 | חלונית ניהול התבניות הזאת מאפשרת לך להוסיף, לערוך ולמחוק תבניות. 93 | 94 | ב־Android 4.4 ומעלה היכולת הנוספת הבאה זמינה מכפתור התפריט. 95 | 96 | * __הצגה של הכול__: הצגת כל התבניות במסד הנתונים. 97 | * __שמירה לקובץ__: כתיבת תכני מסד נתוני התבניות לקובץ. 98 | * __טעינה מקובץ (החלפה)__: טעינת תבניות מקובץ שמחליפה את התוכן הנוכחי של מסד הנתונים. 99 | * __טעינה מקובץ__: טעינת תבניות מקובץ תוך שימור התכנים הנוכחיים. 100 | 101 | #### לשמור ולערוך חלוניות תבניות 102 | 103 | החלונית מאפשרת לך להגדיר 104 | 105 | * __שם__ שם ייצוגי לתבנית. 106 | * __בררת מחדל__ אם מסומן, זאת תיחשב לתבנית בררת המחדל (בדרך כלל מקובע אף יותר על ידי השדות האחרים). 107 | * __מפתח__ המפתח שהתבנית הזו מתאימה לו, אם מוגדר לכדי _מפתח אישי_ אפשר להוסיף ערך מחוץ לתקן בשדה שלהלן. ערכי המפתח תומכים בתווים כוללניים של SQL, כאשר _%_ לוכד אפס תווים או יותר, *_* לוכד תו יחיד. את שני התווים הכוללניים אפשר להחריג באמצעות _\\_ ללכידה כפי שהם. 108 | * __אזור__ האזור עליו חלה התבנית. 109 | * __פריט__ מחרוזת ייעודית ליישום לשימוש עבור התאמה. 110 | 111 | --------------------------------------------------------------------------------