├── .gitignore
├── LICENSE
├── README.md
├── app
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── hmspickerview
│ │ └── app
│ │ └── MainActivity.kt
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ ├── activity_main.xml
│ └── layout_picker.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── values-night
│ └── styles.xml
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── land.webp
└── port.webp
├── library
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── hmspickerview
│ │ ├── HmsPickerView.kt
│ │ └── Utils.kt
│ └── res
│ ├── drawable
│ └── hpv_ic_backspace.xml
│ ├── layout-land
│ └── hpv_content.xml
│ ├── layout
│ ├── hpv_content.xml
│ ├── hpv_digits.xml
│ ├── hpv_divider.xml
│ ├── hpv_time.xml
│ └── hpv_view.xml
│ ├── values-ar
│ └── strings.xml
│ ├── values-de
│ └── strings.xml
│ ├── values-es
│ └── strings.xml
│ ├── values-fr
│ └── strings.xml
│ ├── values-hi
│ └── strings.xml
│ ├── values-ja
│ └── strings.xml
│ ├── values-land
│ └── dimens.xml
│ ├── values-nl
│ └── strings.xml
│ ├── values-pl
│ └── strings.xml
│ ├── values-pt
│ └── strings.xml
│ ├── values-ru
│ └── strings.xml
│ ├── values-sw600dp-land
│ └── dimens.xml
│ ├── values-sw600dp
│ └── dimens.xml
│ ├── values-sw720dp-land
│ └── dimens.xml
│ ├── values-zh-rCN
│ └── strings.xml
│ ├── values-zh-rHK
│ └── strings.xml
│ ├── values-zh-rTW
│ └── strings.xml
│ └── values
│ ├── attrs.xml
│ ├── dimens.xml
│ ├── public.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/java,linux,macos,gradle,kotlin,windows,android,jetbrains,jetbrains+iml,jetbrains+all,androidstudio
3 | # Edit at https://www.gitignore.io/?templates=java,linux,macos,gradle,kotlin,windows,android,jetbrains,jetbrains+iml,jetbrains+all,androidstudio
4 |
5 | ### Android ###
6 | # Built application files
7 | *.apk
8 | *.ap_
9 | *.aab
10 |
11 | # Files for the ART/Dalvik VM
12 | *.dex
13 |
14 | # Java class files
15 | *.class
16 |
17 | # Generated files
18 | bin/
19 | gen/
20 | out/
21 |
22 | # Gradle files
23 | .gradle/
24 | build/
25 |
26 | # Local configuration file (sdk path, etc)
27 | local.properties
28 |
29 | # Proguard folder generated by Eclipse
30 | proguard/
31 |
32 | # Log Files
33 | *.log
34 |
35 | # Android Studio Navigation editor temp files
36 | .navigation/
37 |
38 | # Android Studio captures folder
39 | captures/
40 |
41 | # IntelliJ
42 | *.iml
43 | .idea/workspace.xml
44 | .idea/tasks.xml
45 | .idea/gradle.xml
46 | .idea/assetWizardSettings.xml
47 | .idea/dictionaries
48 | .idea/libraries
49 | .idea/caches
50 |
51 | # Keystore files
52 | # Uncomment the following lines if you do not want to check your keystore files in.
53 | #*.jks
54 | #*.keystore
55 |
56 | # External native build folder generated in Android Studio 2.2 and later
57 | .externalNativeBuild
58 |
59 | # Google Services (e.g. APIs or Firebase)
60 | google-services.json
61 |
62 | # Freeline
63 | freeline.py
64 | freeline/
65 | freeline_project_description.json
66 |
67 | # fastlane
68 | fastlane/report.xml
69 | fastlane/Preview.html
70 | fastlane/screenshots
71 | fastlane/test_output
72 | fastlane/readme.md
73 |
74 | ### Android Patch ###
75 | gen-external-apklibs
76 |
77 | ### AndroidStudio ###
78 | # Covers files to be ignored for android development using Android Studio.
79 |
80 | # Built application files
81 |
82 | # Files for the ART/Dalvik VM
83 |
84 | # Java class files
85 |
86 | # Generated files
87 |
88 | # Gradle files
89 | .gradle
90 |
91 | # Signing files
92 | .signing/
93 |
94 | # Local configuration file (sdk path, etc)
95 |
96 | # Proguard folder generated by Eclipse
97 |
98 | # Log Files
99 |
100 | # Android Studio
101 | /*/build/
102 | /*/local.properties
103 | /*/out
104 | /*/*/build
105 | /*/*/production
106 | *.ipr
107 | *~
108 | *.swp
109 |
110 | # Android Patch
111 |
112 | # External native build folder generated in Android Studio 2.2 and later
113 |
114 | # NDK
115 | obj/
116 |
117 | # IntelliJ IDEA
118 | *.iws
119 | /out/
120 |
121 | # User-specific configurations
122 | .idea/caches/
123 | .idea/libraries/
124 | .idea/shelf/
125 | .idea/.name
126 | .idea/compiler.xml
127 | .idea/copyright/profiles_settings.xml
128 | .idea/encodings.xml
129 | .idea/misc.xml
130 | .idea/modules.xml
131 | .idea/scopes/scope_settings.xml
132 | .idea/vcs.xml
133 | .idea/jsLibraryMappings.xml
134 | .idea/datasources.xml
135 | .idea/dataSources.ids
136 | .idea/sqlDataSources.xml
137 | .idea/dynamic.xml
138 | .idea/uiDesigner.xml
139 |
140 | # OS-specific files
141 | .DS_Store
142 | .DS_Store?
143 | ._*
144 | .Spotlight-V100
145 | .Trashes
146 | ehthumbs.db
147 | Thumbs.db
148 |
149 | # Legacy Eclipse project files
150 | .classpath
151 | .project
152 | .cproject
153 | .settings/
154 |
155 | # Mobile Tools for Java (J2ME)
156 | .mtj.tmp/
157 |
158 | # Package Files #
159 | *.war
160 | *.ear
161 |
162 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
163 | hs_err_pid*
164 |
165 | ## Plugin-specific files:
166 |
167 | # mpeltonen/sbt-idea plugin
168 | .idea_modules/
169 |
170 | # JIRA plugin
171 | atlassian-ide-plugin.xml
172 |
173 | # Mongo Explorer plugin
174 | .idea/mongoSettings.xml
175 |
176 | # Crashlytics plugin (for Android Studio and IntelliJ)
177 | com_crashlytics_export_strings.xml
178 | crashlytics.properties
179 | crashlytics-build.properties
180 | fabric.properties
181 |
182 | ### AndroidStudio Patch ###
183 |
184 | !/gradle/wrapper/gradle-wrapper.jar
185 |
186 | ### Java ###
187 | # Compiled class file
188 |
189 | # Log file
190 |
191 | # BlueJ files
192 | *.ctxt
193 |
194 | # Mobile Tools for Java (J2ME)
195 |
196 | # Package Files #
197 | *.jar
198 | *.nar
199 | *.zip
200 | *.tar.gz
201 | *.rar
202 |
203 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
204 |
205 | ### JetBrains ###
206 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
207 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
208 |
209 | # User-specific stuff
210 | .idea/**/workspace.xml
211 | .idea/**/tasks.xml
212 | .idea/**/usage.statistics.xml
213 | .idea/**/dictionaries
214 | .idea/**/shelf
215 |
216 | # Generated files
217 | .idea/**/contentModel.xml
218 |
219 | # Sensitive or high-churn files
220 | .idea/**/dataSources/
221 | .idea/**/dataSources.ids
222 | .idea/**/dataSources.local.xml
223 | .idea/**/sqlDataSources.xml
224 | .idea/**/dynamic.xml
225 | .idea/**/uiDesigner.xml
226 | .idea/**/dbnavigator.xml
227 |
228 | # Gradle
229 | .idea/**/gradle.xml
230 | .idea/**/libraries
231 |
232 | # Gradle and Maven with auto-import
233 | # When using Gradle or Maven with auto-import, you should exclude module files,
234 | # since they will be recreated, and may cause churn. Uncomment if using
235 | # auto-import.
236 | # .idea/modules.xml
237 | # .idea/*.iml
238 | # .idea/modules
239 |
240 | # CMake
241 | cmake-build-*/
242 |
243 | # Mongo Explorer plugin
244 | .idea/**/mongoSettings.xml
245 |
246 | # File-based project format
247 |
248 | # IntelliJ
249 |
250 | # mpeltonen/sbt-idea plugin
251 |
252 | # JIRA plugin
253 |
254 | # Cursive Clojure plugin
255 | .idea/replstate.xml
256 |
257 | # Crashlytics plugin (for Android Studio and IntelliJ)
258 |
259 | # Editor-based Rest Client
260 | .idea/httpRequests
261 |
262 | # Android studio 3.1+ serialized cache file
263 | .idea/caches/build_file_checksums.ser
264 |
265 | ### JetBrains Patch ###
266 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
267 |
268 | # *.iml
269 | # modules.xml
270 | # .idea/misc.xml
271 | # *.ipr
272 |
273 | # Sonarlint plugin
274 | .idea/sonarlint
275 |
276 | ### JetBrains+all ###
277 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
278 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
279 |
280 | # User-specific stuff
281 |
282 | # Generated files
283 |
284 | # Sensitive or high-churn files
285 |
286 | # Gradle
287 |
288 | # Gradle and Maven with auto-import
289 | # When using Gradle or Maven with auto-import, you should exclude module files,
290 | # since they will be recreated, and may cause churn. Uncomment if using
291 | # auto-import.
292 | # .idea/modules.xml
293 | # .idea/*.iml
294 | # .idea/modules
295 |
296 | # CMake
297 |
298 | # Mongo Explorer plugin
299 |
300 | # File-based project format
301 |
302 | # IntelliJ
303 |
304 | # mpeltonen/sbt-idea plugin
305 |
306 | # JIRA plugin
307 |
308 | # Cursive Clojure plugin
309 |
310 | # Crashlytics plugin (for Android Studio and IntelliJ)
311 |
312 | # Editor-based Rest Client
313 |
314 | # Android studio 3.1+ serialized cache file
315 |
316 | ### JetBrains+all Patch ###
317 | # Ignores the whole .idea folder and all .iml files
318 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
319 |
320 | .idea/
321 |
322 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
323 |
324 | modules.xml
325 |
326 | ### JetBrains+iml ###
327 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
328 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
329 |
330 | # User-specific stuff
331 |
332 | # Generated files
333 |
334 | # Sensitive or high-churn files
335 |
336 | # Gradle
337 |
338 | # Gradle and Maven with auto-import
339 | # When using Gradle or Maven with auto-import, you should exclude module files,
340 | # since they will be recreated, and may cause churn. Uncomment if using
341 | # auto-import.
342 | # .idea/modules.xml
343 | # .idea/*.iml
344 | # .idea/modules
345 |
346 | # CMake
347 |
348 | # Mongo Explorer plugin
349 |
350 | # File-based project format
351 |
352 | # IntelliJ
353 |
354 | # mpeltonen/sbt-idea plugin
355 |
356 | # JIRA plugin
357 |
358 | # Cursive Clojure plugin
359 |
360 | # Crashlytics plugin (for Android Studio and IntelliJ)
361 |
362 | # Editor-based Rest Client
363 |
364 | # Android studio 3.1+ serialized cache file
365 |
366 | ### JetBrains+iml Patch ###
367 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
368 |
369 |
370 | ### Kotlin ###
371 | # Compiled class file
372 |
373 | # Log file
374 |
375 | # BlueJ files
376 |
377 | # Mobile Tools for Java (J2ME)
378 |
379 | # Package Files #
380 |
381 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
382 |
383 | ### Linux ###
384 |
385 | # temporary files which can be created if a process still has a handle open of a deleted file
386 | .fuse_hidden*
387 |
388 | # KDE directory preferences
389 | .directory
390 |
391 | # Linux trash folder which might appear on any partition or disk
392 | .Trash-*
393 |
394 | # .nfs files are created when an open file is removed but is still being accessed
395 | .nfs*
396 |
397 | ### macOS ###
398 | # General
399 | .AppleDouble
400 | .LSOverride
401 |
402 | # Icon must end with two \r
403 | Icon
404 |
405 | # Thumbnails
406 |
407 | # Files that might appear in the root of a volume
408 | .DocumentRevisions-V100
409 | .fseventsd
410 | .TemporaryItems
411 | .VolumeIcon.icns
412 | .com.apple.timemachine.donotpresent
413 |
414 | # Directories potentially created on remote AFP share
415 | .AppleDB
416 | .AppleDesktop
417 | Network Trash Folder
418 | Temporary Items
419 | .apdisk
420 |
421 | ### Windows ###
422 | # Windows thumbnail cache files
423 | ehthumbs_vista.db
424 |
425 | # Dump file
426 | *.stackdump
427 |
428 | # Folder config file
429 | [Dd]esktop.ini
430 |
431 | # Recycle Bin used on file shares
432 | $RECYCLE.BIN/
433 |
434 | # Windows Installer files
435 | *.cab
436 | *.msi
437 | *.msix
438 | *.msm
439 | *.msp
440 |
441 | # Windows shortcuts
442 | *.lnk
443 |
444 | ### Gradle ###
445 | /build/
446 |
447 | # Ignore Gradle GUI config
448 | gradle-app.setting
449 |
450 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
451 | !gradle-wrapper.jar
452 |
453 | # Cache of project
454 | .gradletasknamecache
455 |
456 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
457 | # gradle/wrapper/gradle-wrapper.properties
458 |
459 | ### Gradle Patch ###
460 | **/build/
461 |
462 | # End of https://www.gitignore.io/api/java,linux,macos,gradle,kotlin,windows,android,jetbrains,jetbrains+iml,jetbrains+all,androidstudio
463 |
464 | *.aar
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://jitpack.io/#DeweyReed/HmsPickerView)
2 | []( https://android-arsenal.com/details/1/7644 )
3 |
4 | # HmsPickerView
5 |
6 | A beautiful little Android view to pick hours, minutes and seconds.
7 |
8 |
9 |
10 |
11 | ## Installation
12 |
13 | Step 1. Add it in your root build.gradle at the end of repositories:
14 |
15 | ```Groovy
16 | allprojects {
17 | repositories {
18 | ...
19 | maven { url 'https://jitpack.io' }
20 | }
21 | }
22 | ```
23 |
24 | Step 2. Add the dependency
25 |
26 | ```Groovy
27 | dependencies {
28 | implementation "com.github.DeweyReed:HmsPickerView:${version}"
29 | }
30 | ```
31 |
32 | [](https://jitpack.io/#DeweyReed/HmsPickerView)
33 |
34 | ## Usage
35 |
36 | In the XML:
37 |
38 | ```XML
39 |
43 | ```
44 |
45 | ```XML
46 |
47 |
48 |
49 |
50 | ```
51 |
52 | In the code:
53 |
54 | ```Kotlin
55 | fun getHours(): Int
56 | fun setHours(hours: Int)
57 |
58 | fun getMinutes(): Int
59 | fun setMinutes(minutes: Int)
60 |
61 | fun getSeconds(): Int
62 | fun setSeconds(seconds: Int)
63 |
64 | fun getTimeInMillis(): Long
65 | fun setTimeInMillis(time: Long)
66 |
67 | fun setTimeTextSize(@Px textSize: Int)
68 | fun setDigitTextSize(@Px textSize: Int)
69 |
70 | fun setListener(l: HmsPickerView.Listener)
71 | interface Listener {
72 | /**
73 | * Indicates [HmsPickerView] now has an valid input(anything except 00h 00m 00s).
74 | * This methods can be used to allow user to go forward (such as enabling "next" button).
75 | */
76 | fun onHmsPickerViewHasValidInput(hmsPickerView: HmsPickerView)
77 |
78 | /**
79 | * Indicates [HmsPickerView]'s input becomes 00h 00m 00s.
80 | * This methods can be used to prevent user from going forward(such as disabling "next" button).
81 | */
82 | fun onHmsPickerViewHasNoInput(hmsPickerView: HmsPickerView)
83 | }
84 | ```
85 |
86 | ### Use this view in a dialog
87 |
88 | 1. Create a XML file like [this one](https://github.com/DeweyReed/HmsPickerView/blob/master/app/src/main/res/layout/layout_picker.xml#L1).
89 | 1. Wrap it into an AlertDialog like [this one](https://github.com/DeweyReed/HmsPickerView/blob/master/app/src/main/java/xyz/aprildown/hmspickerview/app/MainActivity.kt#L29).
90 |
91 | **🦄 Please star this repo if you like it 🦄**
92 |
93 | ## License
94 |
95 | [LICENSE](./LICENSE)
96 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'kotlin-android-extensions'
6 |
7 | android {
8 | compileSdkVersion compile_sdk
9 | defaultConfig {
10 | applicationId "xyz.aprildown.hmspickerview.app"
11 | minSdkVersion min_sdk
12 | targetSdkVersion target_sdk
13 | versionCode version_code
14 | versionName version_name
15 | vectorDrawables.useSupportLibrary true
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation project(':library')
27 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
28 | implementation "androidx.appcompat:appcompat:$appcompat_version"
29 |
30 | implementation 'com.google.android.material:material:1.1.0'
31 | }
32 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/xyz/aprildown/hmspickerview/app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.hmspickerview.app
2 |
3 | import android.os.Bundle
4 | import android.widget.Toast
5 | import androidx.appcompat.app.AlertDialog
6 | import androidx.appcompat.app.AppCompatActivity
7 | import androidx.appcompat.app.AppCompatDelegate
8 | import com.google.android.material.dialog.MaterialAlertDialogBuilder
9 | import kotlinx.android.synthetic.main.activity_main.*
10 | import xyz.aprildown.hmspickerview.HmsPickerView
11 |
12 | class MainActivity : AppCompatActivity() {
13 |
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | setContentView(R.layout.activity_main)
17 |
18 | btnNight.setOnClickListener {
19 | val isNight =
20 | AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES
21 | if (isNight) {
22 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
23 | } else {
24 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
25 | }
26 | recreate()
27 | }
28 |
29 | btnDialog.setOnClickListener {
30 | val dialog = MaterialAlertDialogBuilder(this)
31 | .setView(R.layout.layout_picker)
32 | // Because the picker is long, remove vertical insets to make sure the view not get clipped.
33 | .setBackgroundInsetBottom(0)
34 | .setBackgroundInsetTop(0)
35 | .setPositiveButton(android.R.string.ok, null)
36 | .setNegativeButton(android.R.string.cancel, null)
37 | .show()
38 |
39 | val hmsPickerView = dialog.findViewById(R.id.hmsPickerView)!!
40 | dialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(android.R.string.ok)) { _, _ ->
41 | Toast.makeText(
42 | this, "%2dh %2dm %2ds".format(
43 | hmsPickerView.getHours(),
44 | hmsPickerView.getMinutes(),
45 | hmsPickerView.getSeconds()
46 | ),
47 | Toast.LENGTH_SHORT
48 | ).show()
49 | }
50 | }
51 |
52 | hmsPickerView2.run {
53 | setSeconds(12)
54 | setMinutes(30)
55 | setHours(9)
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
17 |
22 |
23 |
28 |
29 |
33 |
34 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | HmsPickerView
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | compile_sdk = 29
6 | min_sdk = 19
7 | target_sdk = compile_sdk
8 | version_code = 20
9 | version_name = '0.2.0'
10 |
11 | kotlin_version = '1.3.72'
12 | appcompat_version = '1.1.0'
13 | }
14 | repositories {
15 | google()
16 | jcenter()
17 |
18 | }
19 | dependencies {
20 | classpath 'com.android.tools.build:gradle:3.6.3'
21 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
22 | // NOTE: Do not place your application dependencies here; they belong
23 | // in the individual module build.gradle files
24 | }
25 | }
26 |
27 | allprojects {
28 | repositories {
29 | google()
30 | jcenter()
31 |
32 | }
33 | }
34 |
35 | task clean(type: Delete) {
36 | delete rootProject.buildDir
37 | }
38 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Apr 19 18:41:42 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/images/land.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/images/land.webp
--------------------------------------------------------------------------------
/images/port.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeweyReed/HmsPickerView/982617f3ea1274c07a722e14c1e854e965032576/images/port.webp
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | android {
6 | compileSdkVersion compile_sdk
7 | defaultConfig {
8 | minSdkVersion min_sdk
9 | targetSdkVersion target_sdk
10 | versionCode version_code
11 | versionName version_name
12 | vectorDrawables.useSupportLibrary true
13 | }
14 | resourcePrefix 'hpv_'
15 | }
16 |
17 | dependencies {
18 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
19 | implementation "androidx.appcompat:appcompat:$appcompat_version"
20 | }
21 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/library/src/main/java/xyz/aprildown/hmspickerview/HmsPickerView.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("MemberVisibilityCanBePrivate", "unused")
2 |
3 | /*
4 | * Copyright (C) 2008 The Android Open Source Project
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package xyz.aprildown.hmspickerview
20 |
21 | import android.content.Context
22 | import android.content.res.ColorStateList
23 | import android.graphics.PorterDuff
24 | import android.os.Parcel
25 | import android.os.Parcelable
26 | import android.text.BidiFormatter
27 | import android.text.TextUtils
28 | import android.text.format.DateUtils
29 | import android.text.style.RelativeSizeSpan
30 | import android.util.AttributeSet
31 | import android.util.TypedValue
32 | import android.view.KeyEvent
33 | import android.view.LayoutInflater
34 | import android.view.View
35 | import android.widget.LinearLayout
36 | import android.widget.TextView
37 | import androidx.annotation.IdRes
38 | import androidx.annotation.Px
39 | import androidx.core.view.ViewCompat
40 | import androidx.customview.view.AbsSavedState
41 | import java.util.Arrays
42 |
43 | /**
44 | * A custom view to pick hours, minutes and seconds.
45 | * Inspired by and optimized from AOSP DeskClock com.android.deskclock.timer.TimerSetupView.
46 | */
47 | class HmsPickerView @JvmOverloads constructor(
48 | context: Context,
49 | attrs: AttributeSet? = null
50 | ) : LinearLayout(context, attrs), View.OnClickListener, View.OnLongClickListener {
51 |
52 | interface Listener {
53 | /**
54 | * Indicates [HmsPickerView] now has an valid input(anything except 00h 00m 00s).
55 | * This methods can be used to allow user to go forward (such as enabling "next" button).
56 | */
57 | fun onHmsPickerViewHasValidInput(hmsPickerView: HmsPickerView)
58 |
59 | /**
60 | * Indicates [HmsPickerView]'s input becomes 00h 00m 00s.
61 | * This methods can be used to prevent user from going forward(such as disabling "next" button).
62 | */
63 | fun onHmsPickerViewHasNoInput(hmsPickerView: HmsPickerView)
64 |
65 | /**
66 | * Indicates [HmsPickerView] that input changed
67 | * This method returns new time in milliseconds including when time is 0:00:0
68 | */
69 | fun onHmsPickerViewInputChanged(hmsPickerView: HmsPickerView, input: Long)
70 | }
71 |
72 | abstract class SimpleListener : Listener {
73 | override fun onHmsPickerViewHasValidInput(hmsPickerView: HmsPickerView) = Unit
74 | override fun onHmsPickerViewHasNoInput(hmsPickerView: HmsPickerView) = Unit
75 | override fun onHmsPickerViewInputChanged(hmsPickerView: HmsPickerView, input: Long) = Unit
76 | }
77 |
78 | private val input = intArrayOf(0, 0, 0, 0, 0, 0)
79 |
80 | private var inputPointer = -1
81 | private val timeTemplate: CharSequence
82 |
83 | private val timeView: TextView
84 | private val deleteView: View
85 | private val dividerView: View
86 | private val digitViews: Array
87 |
88 | private val hasValidInput: Boolean
89 | get() = inputPointer != -1
90 |
91 | private var listener: Listener? = null
92 |
93 | init {
94 | val bf = BidiFormatter.getInstance(false /* rtlContext */)
95 | val hoursLabel = bf.unicodeWrap(context.getString(R.string.hpv_hours_label))
96 | val minutesLabel = bf.unicodeWrap(context.getString(R.string.hpv_minutes_label))
97 | val secondsLabel = bf.unicodeWrap(context.getString(R.string.hpv_seconds_label))
98 |
99 | // Create a formatted template for "00h 00m 00s".
100 | timeTemplate = TextUtils.expandTemplate(
101 | "^1^4 ^2^5 ^3^6",
102 | bf.unicodeWrap("^1"),
103 | bf.unicodeWrap("^2"),
104 | bf.unicodeWrap("^3"),
105 | formatText(hoursLabel, RelativeSizeSpan(0.3f)),
106 | formatText(minutesLabel, RelativeSizeSpan(0.3f)),
107 | formatText(secondsLabel, RelativeSizeSpan(0.3f))
108 | )
109 |
110 | LayoutInflater.from(context).inflate(R.layout.hpv_view, this)
111 |
112 | timeView = findViewById(R.id.timer_setup_time)
113 | deleteView = findViewById(R.id.timer_setup_delete)
114 | dividerView = findViewById(R.id.timer_setup_divider)
115 | digitViews = arrayOf(
116 | findViewById(R.id.timer_setup_digit_0),
117 | findViewById(R.id.timer_setup_digit_1),
118 | findViewById(R.id.timer_setup_digit_2),
119 | findViewById(R.id.timer_setup_digit_3),
120 | findViewById(R.id.timer_setup_digit_4),
121 | findViewById(R.id.timer_setup_digit_5),
122 | findViewById(R.id.timer_setup_digit_6),
123 | findViewById(R.id.timer_setup_digit_7),
124 | findViewById(R.id.timer_setup_digit_8),
125 | findViewById(R.id.timer_setup_digit_9)
126 | )
127 |
128 | // Tint the divider to match the disabled control color by default and used the activated
129 | // control color when there is valid input.
130 | val dividerContext = dividerView.context
131 | val colorControlActivated = resolveColor(
132 | dividerContext,
133 | R.attr.colorControlActivated
134 | )
135 | val colorControlDisabled = resolveColor(
136 | dividerContext,
137 | R.attr.colorControlNormal, intArrayOf(android.R.attr.state_enabled.inv())
138 | )
139 | ViewCompat.setBackgroundTintList(
140 | dividerView, ColorStateList(
141 | arrayOf(intArrayOf(android.R.attr.state_activated), intArrayOf()),
142 | intArrayOf(colorControlActivated, colorControlDisabled)
143 | )
144 | )
145 | ViewCompat.setBackgroundTintMode(dividerView, PorterDuff.Mode.SRC)
146 |
147 | // Initialize the digit buttons.
148 | for (digitView in digitViews) {
149 | val digit = getDigitForId(digitView.id)
150 | digitView.text = getFormattedNumber(digit, 1)
151 | digitView.setOnClickListener(this)
152 | }
153 |
154 | deleteView.setOnClickListener(this)
155 | deleteView.setOnLongClickListener(this)
156 |
157 | updateTime()
158 | updateDeleteAndDivider()
159 |
160 | context.theme.obtainStyledAttributes(attrs, R.styleable.HmsPickerView, 0, 0).apply {
161 | try {
162 | getDimensionPixelOffset(R.styleable.HmsPickerView_hpv_time_text_size, 0).let {
163 | if (it != 0) {
164 | setTimeTextSize(it)
165 | }
166 | }
167 | getDimensionPixelOffset(R.styleable.HmsPickerView_hpv_digit_text_size, 0).let {
168 | if (it != 0) {
169 | setDigitTextSize(it)
170 | }
171 | }
172 | } finally {
173 | recycle()
174 | }
175 | }
176 | }
177 |
178 | private fun getDigitForId(@IdRes id: Int): Int = when (id) {
179 | R.id.timer_setup_digit_0 -> 0
180 | R.id.timer_setup_digit_1 -> 1
181 | R.id.timer_setup_digit_2 -> 2
182 | R.id.timer_setup_digit_3 -> 3
183 | R.id.timer_setup_digit_4 -> 4
184 | R.id.timer_setup_digit_5 -> 5
185 | R.id.timer_setup_digit_6 -> 6
186 | R.id.timer_setup_digit_7 -> 7
187 | R.id.timer_setup_digit_8 -> 8
188 | R.id.timer_setup_digit_9 -> 9
189 | else -> throw IllegalArgumentException("Invalid id: $id")
190 | }
191 |
192 | private fun updateTime() {
193 | val seconds = input[1] * 10 + input[0]
194 | val minutes = input[3] * 10 + input[2]
195 | val hours = input[5] * 10 + input[4]
196 |
197 | timeView.text = TextUtils.expandTemplate(
198 | timeTemplate,
199 | getFormattedNumber(hours, 2),
200 | getFormattedNumber(minutes, 2),
201 | getFormattedNumber(seconds, 2)
202 | )
203 |
204 | val r = resources
205 | timeView.contentDescription = r.getString(
206 | R.string.hpv_time_description,
207 | r.getQuantityString(R.plurals.hpv_hours, hours, hours),
208 | r.getQuantityString(R.plurals.hpv_minutes, minutes, minutes),
209 | r.getQuantityString(R.plurals.hpv_seconds, seconds, seconds)
210 | )
211 | }
212 |
213 | private fun updateDeleteAndDivider() {
214 | val enabled = hasValidInput
215 | deleteView.isEnabled = enabled
216 | dividerView.isActivated = enabled
217 | }
218 |
219 | override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
220 | var view: View? = null
221 | if (keyCode == KeyEvent.KEYCODE_DEL) {
222 | view = deleteView
223 | } else if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
224 | view = digitViews[keyCode - KeyEvent.KEYCODE_0]
225 | }
226 |
227 | if (view != null) {
228 | val result = view.performClick()
229 | if (result && hasValidInput) {
230 | listener?.onHmsPickerViewHasValidInput(this)
231 | }
232 | return result
233 | }
234 |
235 | return false
236 | }
237 |
238 | override fun onClick(view: View) {
239 | if (view === deleteView) {
240 | delete()
241 | } else {
242 | append(getDigitForId(view.id))
243 | }
244 | }
245 |
246 | override fun onLongClick(view: View): Boolean {
247 | if (view === deleteView) {
248 | reset()
249 | notifyNoInput()
250 | return true
251 | }
252 | return false
253 | }
254 |
255 | private fun notifyNoInput() {
256 | listener?.onHmsPickerViewHasNoInput(this)
257 | }
258 |
259 | private fun append(digit: Int) {
260 | if (digit < 0 || digit > 9) {
261 | throw IllegalArgumentException("Invalid digit: $digit")
262 | }
263 |
264 | // Pressing "0" as the first digit does nothing.
265 | if (inputPointer == -1 && digit == 0) {
266 | return
267 | }
268 |
269 | // No space for more digits, so ignore input.
270 | if (inputPointer == input.size - 1) {
271 | return
272 | }
273 |
274 | // Append the new digit.
275 | System.arraycopy(input, 0, input, 1, inputPointer + 1)
276 | input[0] = digit
277 | inputPointer++
278 | updateTime()
279 |
280 | // Update TalkBack to read the number being deleted.
281 | deleteView.contentDescription = context.getString(
282 | R.string.hpv_delete_number,
283 | getFormattedNumber(digit)
284 | )
285 |
286 | listener?.onHmsPickerViewInputChanged(this, getTimeInMillis())
287 | // Update the fab, delete, and divider when we have valid input.
288 | if (inputPointer == 0) {
289 | listener?.onHmsPickerViewHasValidInput(this)
290 | updateDeleteAndDivider()
291 | }
292 | }
293 |
294 | private fun delete() {
295 | // Nothing exists to delete so return.
296 | if (inputPointer < 0) {
297 | return
298 | }
299 |
300 | System.arraycopy(input, 1, input, 0, inputPointer)
301 | input[inputPointer] = 0
302 | inputPointer--
303 | updateTime()
304 |
305 | // Update TalkBack to read the number being deleted or its original description.
306 | if (inputPointer >= 0) {
307 | deleteView.contentDescription = context.getString(
308 | R.string.hpv_delete_number,
309 | getFormattedNumber(input[0])
310 | )
311 | } else {
312 | deleteView.contentDescription = context.getString(R.string.hpv_delete)
313 | }
314 |
315 | listener?.onHmsPickerViewInputChanged(this, getTimeInMillis())
316 | // Update the fab, delete, and divider when we no longer have valid input.
317 | if (inputPointer == -1) {
318 | notifyNoInput()
319 | updateDeleteAndDivider()
320 | }
321 | }
322 |
323 | /**
324 | * Set time to 00h 00m 00s.
325 | */
326 | fun reset() {
327 | if (inputPointer != -1) {
328 | Arrays.fill(input, 0)
329 | inputPointer = -1
330 | updateTime()
331 | updateDeleteAndDivider()
332 | }
333 | }
334 |
335 | override fun onSaveInstanceState(): Parcelable? {
336 | val superState = super.onSaveInstanceState()
337 | val ss = if (superState == null) SavedState() else SavedState(superState)
338 | ss.timeInMills = getTimeInMillis()
339 | return ss
340 | }
341 |
342 | override fun onRestoreInstanceState(state: Parcelable?) {
343 | val ss = state as SavedState
344 |
345 | super.onRestoreInstanceState(ss.superState)
346 |
347 | val newTime = ss.timeInMills
348 | if (newTime > 0) {
349 | setTimeInMillis(newTime)
350 | }
351 | }
352 |
353 | /**
354 | * Set a listener to listen if the [HmsPickerView] has or loses a valid input.
355 | */
356 | fun setListener(l: Listener) {
357 | listener = l
358 | }
359 |
360 | /**
361 | * Get current seconds.
362 | * @return 0 ~ 99 seconds.
363 | */
364 | fun getSeconds(): Int = input[1] * 10 + input[0]
365 |
366 | /**
367 | * Get current minutes.
368 | * @return 0 ~ 99 minutes.
369 | */
370 | fun getMinutes(): Int = input[3] * 10 + input[2]
371 |
372 | /**
373 | * Get current hours.
374 | * @return 0 ~ 99 hours.
375 | */
376 | fun getHours(): Int = input[5] * 10 + input[4]
377 |
378 | /**
379 | * Get current time in milliseconds.
380 | * @return 0 ~ 99 hours 99 minutes 99 seconds in milliseconds.
381 | */
382 | fun getTimeInMillis(): Long =
383 | getSeconds() * DateUtils.SECOND_IN_MILLIS +
384 | getMinutes() * DateUtils.MINUTE_IN_MILLIS +
385 | getHours() * DateUtils.HOUR_IN_MILLIS
386 |
387 | /**
388 | * Set current hours. Minutes and seconds stay the same.
389 | */
390 | fun setHours(hours: Int) {
391 | setTimeInMillis(
392 | getSeconds() * DateUtils.SECOND_IN_MILLIS +
393 | getMinutes() * DateUtils.MINUTE_IN_MILLIS +
394 | hours * DateUtils.HOUR_IN_MILLIS
395 | )
396 | }
397 |
398 | /**
399 | * Set current minutes. Hours and seconds stay the same.
400 | */
401 | fun setMinutes(minutes: Int) {
402 | setTimeInMillis(
403 | getSeconds() * DateUtils.SECOND_IN_MILLIS +
404 | minutes * DateUtils.MINUTE_IN_MILLIS +
405 | getHours() * DateUtils.HOUR_IN_MILLIS
406 | )
407 | }
408 |
409 | /**
410 | * Set seconds hours. Hours and minutes stay the same.
411 | */
412 | fun setSeconds(seconds: Int) {
413 | setTimeInMillis(
414 | seconds * DateUtils.SECOND_IN_MILLIS +
415 | getMinutes() * DateUtils.MINUTE_IN_MILLIS +
416 | getHours() * DateUtils.HOUR_IN_MILLIS
417 | )
418 | }
419 |
420 | /**
421 | * Set current time in milliseconds.
422 | */
423 | fun setTimeInMillis(time: Long) {
424 | var remaining = time
425 |
426 | val hours = (remaining / DateUtils.HOUR_IN_MILLIS).toInt()
427 | remaining %= DateUtils.HOUR_IN_MILLIS
428 |
429 | val minutes = (remaining / DateUtils.MINUTE_IN_MILLIS).toInt()
430 | remaining %= DateUtils.MINUTE_IN_MILLIS
431 |
432 | val seconds = (remaining / DateUtils.SECOND_IN_MILLIS).toInt()
433 |
434 | val newInput = intArrayOf(
435 | seconds % 10, seconds / 10,
436 | minutes % 10, minutes / 10,
437 | hours % 10, hours / 10
438 | )
439 |
440 | val size = input.size
441 |
442 | System.arraycopy(newInput, 0, input, 0, size)
443 |
444 | val firstNonZeroPos = newInput.reversed().indexOfFirst { it != 0 }
445 | // Magic formula
446 | inputPointer = if (firstNonZeroPos == -1) -1 else size - firstNonZeroPos - 1
447 |
448 | updateTime()
449 | updateDeleteAndDivider()
450 | }
451 |
452 | fun setTimeTextSize(@Px textSize: Int) {
453 | timeView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize.toFloat())
454 | }
455 |
456 | fun setDigitTextSize(@Px textSize: Int) {
457 | val textSizeF = textSize.toFloat()
458 | digitViews.forEach {
459 | it.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizeF)
460 | }
461 | }
462 |
463 | private class SavedState : AbsSavedState {
464 |
465 | var timeInMills: Long = 0L
466 |
467 | constructor() : super(Parcel.obtain())
468 | constructor(superState: Parcelable) : super(superState)
469 |
470 | constructor(source: Parcel) : this(source, null)
471 | constructor(source: Parcel, loader: ClassLoader?) : super(source, loader) {
472 | timeInMills = source.readLong()
473 | }
474 |
475 | override fun writeToParcel(dest: Parcel?, flags: Int) {
476 | super.writeToParcel(dest, flags)
477 | dest?.writeLong(timeInMills)
478 | }
479 |
480 | companion object {
481 | @JvmField
482 | val CREATOR = object : Parcelable.Creator {
483 | override fun createFromParcel(parcel: Parcel): SavedState {
484 | return SavedState(parcel)
485 | }
486 |
487 | override fun newArray(size: Int): Array {
488 | return arrayOfNulls(size)
489 | }
490 | }
491 | }
492 | }
493 | }
--------------------------------------------------------------------------------
/library/src/main/java/xyz/aprildown/hmspickerview/Utils.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.hmspickerview
2 |
3 | import android.content.Context
4 | import android.content.res.TypedArray
5 | import android.graphics.Color
6 | import android.text.Spannable
7 | import android.text.SpannableString
8 | import androidx.annotation.AttrRes
9 | import androidx.annotation.ColorInt
10 | import java.util.*
11 |
12 | /**
13 | * Applies a span over the length of the given text.
14 | *
15 | * @param text the [CharSequence] to be formatted
16 | * @param span the span to apply
17 | * @return the text with the span applied
18 | */
19 | internal fun formatText(text: CharSequence?, span: Any): CharSequence? {
20 | if (text == null) {
21 | return null
22 | }
23 |
24 | val formattedText = SpannableString.valueOf(text)
25 | formattedText.setSpan(span, 0, formattedText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
26 | return formattedText
27 | }
28 |
29 | /**
30 | * Convenience method for retrieving a themed color value.
31 | *
32 | * @param context the [Context] to resolve the theme attribute against
33 | * @param attr the attribute corresponding to the color to resolve
34 | * @return the color value of the resolved attribute
35 | */
36 | @ColorInt
37 | internal fun resolveColor(context: Context, @AttrRes attr: Int): Int {
38 | return resolveColor(context, attr, null /* stateSet */)
39 | }
40 |
41 | /**
42 | * Convenience method for retrieving a themed color value.
43 | *
44 | * @param context the [Context] to resolve the theme attribute against
45 | * @param attr the attribute corresponding to the color to resolve
46 | * @param stateSet an array of [android.view.View] states
47 | * @return the color value of the resolved attribute
48 | */
49 | @ColorInt
50 | internal fun resolveColor(context: Context, @AttrRes attr: Int, @AttrRes stateSet: IntArray?): Int {
51 |
52 | /** Temporary array used internally to resolve attributes. */
53 | val tempAttr = IntArray(1)
54 |
55 | val a: TypedArray
56 | tempAttr[0] = attr
57 | a = context.obtainStyledAttributes(tempAttr)
58 |
59 | try {
60 | if (stateSet == null) {
61 | return a.getColor(0, Color.RED)
62 | }
63 |
64 | val colorStateList = a.getColorStateList(0)
65 | return colorStateList?.getColorForState(stateSet, Color.RED) ?: Color.RED
66 | } finally {
67 | a.recycle()
68 | }
69 | }
70 |
71 | internal fun getFormattedNumber(value: Int): String {
72 | val length = if (value == 0) 1 else Math.log10(value.toDouble()).toInt() + 1
73 | return getFormattedNumber(value, length)
74 | }
75 |
76 | internal fun getFormattedNumber(value: Int, length: Int): String {
77 | return String.format(Locale.getDefault(), "%0${length}d", value)
78 | }
--------------------------------------------------------------------------------
/library/src/main/res/drawable/hpv_ic_backspace.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/library/src/main/res/layout-land/hpv_content.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/hpv_content.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
28 |
29 |
30 |
31 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/hpv_digits.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
20 |
21 |
28 |
29 |
36 |
37 |
38 |
39 |
43 |
44 |
51 |
52 |
59 |
60 |
67 |
68 |
69 |
70 |
74 |
75 |
82 |
83 |
90 |
91 |
98 |
99 |
100 |
101 |
105 |
106 |
110 |
111 |
118 |
119 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/hpv_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/hpv_time.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
25 |
26 |
39 |
40 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/hpv_view.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
20 |
21 |
22 |
26 |
27 |
28 |
33 |
34 |
35 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/library/src/main/res/values-ar/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | حذف
5 |
6 | "حذف %s"
7 |
8 | س
9 | د
10 | ث
11 |
12 | %1$s، %2$s، %3$s
13 |
14 |
15 | - %s من الساعات
16 | - ساعتان (%s)
17 | - %s ساعات
18 | - %s ساعة
19 | - %s من الساعات
20 | - ساعة واحدة
21 |
22 |
23 |
24 | - %s من الدقائق
25 | - دقيقتان (%s)
26 | - %s دقائق
27 | - %s دقيقة
28 | - %s من الدقائق
29 | - دقيقة واحدة
30 |
31 |
32 |
33 | - %s ثانية
34 | - ثانيتان (%s)
35 | - %s ثوانٍ
36 | - %s ثانية
37 | - %s ثانية
38 | - ثانية واحدة
39 |
40 |
--------------------------------------------------------------------------------
/library/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Löschen
5 |
6 | %s löschen
7 |
8 | h
9 | m
10 | s
11 |
12 | %1$s, %2$s, %3$s
13 |
14 |
15 | - %s Stunden
16 | - 1 Stunde
17 |
18 |
19 |
20 | - %s Minuten
21 | - 1 Minute
22 |
23 |
24 |
25 | - %s Sekunden
26 | - 1 Sekunde
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/library/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Eliminar
5 |
6 | Eliminar %s
7 |
8 | h
9 | m
10 | s
11 |
12 | %1$s, %2$s y %3$s
13 |
14 |
15 | - %s horas
16 | - 1 hora
17 |
18 |
19 |
20 | - %s minutos
21 | - 1 minuto
22 |
23 |
24 |
25 | - %s segundos
26 | - 1 segundo
27 |
28 |
29 |
--------------------------------------------------------------------------------
/library/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Supprimer
5 |
6 | Supprimer le numéro \"%s\"
7 |
8 | h
9 | min
10 | s
11 |
12 | %1$s, %2$s, %3$s
13 |
14 |
15 | - %s heure
16 | - %s heures
17 |
18 |
19 |
20 | - %s minute
21 | - %s minutes
22 |
23 |
24 |
25 | - %s seconde
26 | - %s secondes
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/library/src/main/res/values-hi/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | हटाएं
5 |
6 | %s हटाएं
7 |
8 | घं.
9 | मि.
10 | से.
11 |
12 | %1$s, %2$s, %3$s
13 |
14 |
15 | - %s घंटे
16 | - %s घंटे
17 |
18 |
19 |
20 | - %s मिनट
21 | - %s मिनट
22 |
23 |
24 |
25 | - %s सेकंड
26 | - %s सेकंड
27 |
28 |
29 |
--------------------------------------------------------------------------------
/library/src/main/res/values-ja/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 削除
5 |
6 | %sを削除
7 |
8 | h
9 | m
10 | s
11 |
12 | %1$s、%2$s、%3$s
13 |
14 |
15 | - %s時間
16 |
17 |
18 |
19 | - %s分
20 |
21 |
22 |
23 | - %s秒
24 |
25 |
26 |
--------------------------------------------------------------------------------
/library/src/main/res/values-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 12sp
4 |
5 | 24sp
6 | 8dp
7 |
8 | 32sp
9 |
--------------------------------------------------------------------------------
/library/src/main/res/values-nl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Verwijderen
5 |
6 | %s verwijderen
7 |
8 | u
9 | m
10 | s
11 |
12 | %1$s, %2$s, %3$s
13 |
14 |
15 | - %s uur
16 | - 1 uur
17 |
18 |
19 |
20 | - %s minuten
21 | - 1 minuut
22 |
23 |
24 |
25 | - %s seconden
26 | - 1 seconde
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/library/src/main/res/values-pl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Usuń
5 |
6 | Usuń %s
7 |
8 | g
9 | m
10 | s
11 |
12 | %1$s, %2$s, %3$s
13 |
14 |
15 | - %s godziny
16 | - %s godzin
17 | - %s godziny
18 | - 1 godzinę
19 |
20 |
21 |
22 | - %s minuty
23 | - %s minut
24 | - %s minuty
25 | - 1 minuta
26 |
27 |
28 |
29 | - %s sekundy
30 | - %s sekund
31 | - %s sekundy
32 | - 1 sekunda
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/library/src/main/res/values-pt/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Excluir
5 |
6 | Excluir %s
7 |
8 | h
9 | m
10 | s
11 |
12 | %1$s, %2$s, %3$s
13 |
14 |
15 | - %s hora
16 | - %s horas
17 |
18 |
19 |
20 | - %s minuto
21 | - %s minutos
22 |
23 |
24 |
25 | - %s segundo
26 | - %s segundos
27 |
28 |
29 |
--------------------------------------------------------------------------------
/library/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Удалить
5 |
6 | Удалить %s
7 |
8 | ч.
9 | м.
10 | с.
11 |
12 | %1$s, %2$s, %3$s
13 |
14 |
15 | - %s час
16 | - %s часа
17 | - %s часов
18 | - %s часа
19 |
20 |
21 |
22 | - %s минута
23 | - %s минуты
24 | - %s минут
25 | - %s минуты
26 |
27 |
28 |
29 | - %s секунда
30 | - %s секунды
31 | - %s секунд
32 | - %s секунды
33 |
34 |
35 |
--------------------------------------------------------------------------------
/library/src/main/res/values-sw600dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 19sp
4 |
--------------------------------------------------------------------------------
/library/src/main/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 21sp
4 | 32dp
5 |
--------------------------------------------------------------------------------
/library/src/main/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 19sp
4 |
--------------------------------------------------------------------------------
/library/src/main/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 删除
5 |
6 | 删除 %s
7 |
8 | 小时
9 | 分
10 | 秒
11 |
12 | %1$s %2$s %3$s
13 |
14 |
15 | - %s 小时
16 |
17 |
18 |
19 | - %s 分钟
20 |
21 |
22 |
23 | - %s 秒
24 |
25 |
26 |
--------------------------------------------------------------------------------
/library/src/main/res/values-zh-rHK/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 刪除
5 |
6 | 刪除 %s
7 |
8 | 時
9 | 分
10 | 秒
11 |
12 | %1$s %2$s %3$s
13 |
14 |
15 | - %s 小時
16 |
17 |
18 |
19 | - %s 分鐘
20 |
21 |
22 |
23 | - %s 秒
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/library/src/main/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 刪除
5 |
6 | 刪除 %s
7 |
8 | 時
9 | 分
10 | 秒
11 |
12 | %1$s,%2$s,%3$s
13 |
14 |
15 | - %s 小時
16 |
17 |
18 |
19 | - %s 分鐘
20 |
21 |
22 |
23 | - %s 秒
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/library/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 14sp
5 |
6 | 24dp
7 |
8 | 40sp
9 | 16dp
10 |
11 | 40sp
12 |
--------------------------------------------------------------------------------
/library/src/main/res/values/public.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Delete
6 |
7 |
8 | Delete %s
9 |
10 |
11 | h
12 |
13 | m
14 |
15 | s
16 |
17 |
18 | %1$s, %2$s, %3$s
19 |
20 |
21 |
22 |
23 | - 1 hour
24 |
25 | - %s hours
26 |
27 |
28 |
29 |
30 |
31 | - 1 minute
32 |
33 | - %s minutes
34 |
35 |
36 |
37 |
38 |
39 | - 1 second
40 |
41 | - %s seconds
42 |
43 |
44 |
--------------------------------------------------------------------------------
/library/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
20 |
21 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------