├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ ├── Open_Source.xml
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations
│ └── AllTests.xml
├── scopes
│ └── scope_settings.xml
└── vcs.xml
├── .travis.yml
├── Android-ClockSeekbar.iml
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── ScreenShot.png
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── lib
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── proguard-rules.txt
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ ├── Roboto-Bold.ttf
│ ├── Roboto-Light.ttf
│ ├── Roboto-Medium.ttf
│ └── Roboto-Regular.ttf
│ ├── java
│ └── com
│ │ └── tripadvisor
│ │ └── seekbar
│ │ ├── CircularClockSeekBar.java
│ │ ├── ClockView.java
│ │ ├── LetterSpacingTextView.java
│ │ ├── RobotoTextView.java
│ │ └── util
│ │ ├── Utils.java
│ │ └── annotations
│ │ └── VisibleForTesting.java
│ └── res
│ ├── drawable-hdpi
│ ├── clock_dial.png
│ ├── ic_launcher.png
│ ├── scrubber_control_normal_holo_green.png
│ ├── scrubber_control_normal_holo_orange.png
│ ├── scrubber_control_normal_holo_red.png
│ ├── scrubber_control_pressed_holo_green.png
│ ├── scrubber_control_pressed_holo_orange.png
│ ├── scrubber_control_pressed_holo_red.png
│ ├── time_longhand.png
│ └── time_shorthand.png
│ ├── drawable-mdpi
│ ├── clock_dial.png
│ ├── ic_launcher.png
│ ├── scrubber_control_normal_holo_green.png
│ ├── scrubber_control_normal_holo_orange.png
│ ├── scrubber_control_normal_holo_red.png
│ ├── scrubber_control_pressed_holo_green.png
│ ├── scrubber_control_pressed_holo_orange.png
│ ├── scrubber_control_pressed_holo_red.png
│ ├── time_longhand.png
│ └── time_shorthand.png
│ ├── drawable-xhdpi
│ ├── clock_dial.png
│ ├── ic_launcher.png
│ ├── scrubber_control_normal_holo_green.png
│ ├── scrubber_control_normal_holo_orange.png
│ ├── scrubber_control_normal_holo_red.png
│ ├── scrubber_control_pressed_holo_green.png
│ ├── scrubber_control_pressed_holo_orange.png
│ ├── scrubber_control_pressed_holo_red.png
│ ├── time_longhand.png
│ └── time_shorthand.png
│ ├── drawable-xxhdpi
│ ├── ic_launcher.png
│ ├── scrubber_control_normal_holo_green.png
│ ├── scrubber_control_normal_holo_orange.png
│ ├── scrubber_control_normal_holo_red.png
│ ├── scrubber_control_pressed_holo_green.png
│ ├── scrubber_control_pressed_holo_orange.png
│ ├── scrubber_control_pressed_holo_red.png
│ ├── time_longhand.png
│ └── time_shorthand.png
│ ├── layout
│ └── clock_view.xml
│ ├── values-da
│ └── strings.xml
│ ├── values-de
│ └── strings.xml
│ ├── values-el
│ └── strings.xml
│ ├── values-en
│ └── strings.xml
│ ├── values-es
│ └── strings.xml
│ ├── values-fi
│ └── strings.xml
│ ├── values-fr
│ └── strings.xml
│ ├── values-in
│ └── strings.xml
│ ├── values-it
│ └── strings.xml
│ ├── values-ja
│ └── strings.xml
│ ├── values-ko
│ └── strings.xml
│ ├── values-nb
│ └── strings.xml
│ ├── values-nl
│ └── strings.xml
│ ├── values-pl
│ └── strings.xml
│ ├── values-pt
│ └── strings.xml
│ ├── values-ru
│ └── strings.xml
│ ├── values-sv
│ └── strings.xml
│ ├── values-th
│ └── strings.xml
│ ├── values-tr
│ └── strings.xml
│ ├── values-vi
│ └── strings.xml
│ ├── values-zh-rHK
│ └── strings.xml
│ ├── values-zh-rTW
│ └── strings.xml
│ ├── values-zh
│ └── strings.xml
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ └── strings.xml
├── sample
├── .gitignore
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── libs
│ └── espresso-1.1-bundled.jar
├── proguard-rules.txt
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── tripadvisor
│ │ └── seekbar
│ │ ├── sample
│ │ └── MainActivityTest.java
│ │ └── util
│ │ └── UtilsTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── tripadvisor
│ │ └── seekbar
│ │ └── sample
│ │ └── MainActivity.java
│ └── res
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ └── ic_launcher.png
│ ├── layout
│ ├── activity_main.xml
│ ├── fragment_main.xml
│ └── fragment_second.xml
│ ├── menu
│ └── main.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | local.properties
3 | *.iml
4 | build/
5 | .DS_Store
6 | .bundle
7 | bin
8 |
9 | # See https://intellij-support.jetbrains.com/entries/23393067
10 | .idea/workspace.xml
11 | .idea/tasks.xml
12 | .idea/libraries
13 | .idea/uiDesigner.xml
14 | .idea/misc.xml
15 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | Android-ClockSeekbar
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/copyright/Open_Source.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/AllTests.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk: oraclejdk7
3 | env:
4 | global:
5 | - ADB_INSTALL_TIMEOUT=8 # minutes (2 minutes by default)
6 | matrix:
7 | - ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a
8 |
9 | android:
10 | components:
11 | - build-tools-19.1.0
12 | before_script:
13 | # Create and start emulator
14 | - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
15 | - emulator -avd test -no-skin -no-audio -no-window &
16 | - adb wait-for-device
17 | - adb shell input keyevent 82 &
18 |
--------------------------------------------------------------------------------
/Android-ClockSeekbar.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | mtooke@tripadvisor.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
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 2014 TripAdvisor
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 | TripAdvisor Android ClockSeekbar
2 | ==========================
3 | [](https://travis-ci.org/tripadvisor/android-clockseekbar.svg)
4 |
5 | Tested against API level 14+.
6 |
7 | Standalone Android widget for picking a single time or range from a clock view.
8 |
9 | 
10 |
11 | Usage
12 | -----
13 |
14 | Include `ClockView` in your layout XML.
15 |
16 | ```xml
17 |
21 | ```
22 |
23 |
24 | or
25 |
26 |
27 | ```xml
28 |
32 | ```
33 |
34 |
35 | This is a fairly large control so it is wise to give it ample space in your layout. On small
36 | devices it is recommended to use a dialog, full-screen fragment, or dedicated activity. On larger
37 | devices like tablets, displaying full-screen is not recommended. A fragment occupying part of the
38 | layout or a dialog is a better choice.
39 |
40 | If using `ClockView` , Then in `onCreate` of your activity/dialog or the `onCreateView` of your fragment, initialize the
41 | view with a range of valid dates.
42 |
43 | ```java
44 | final ClockView minDepartTime = (ClockView) rootView.findViewById(R.id.min_depart_time_clock_view);
45 | DateTime minTime = new DateTime(2014, 4, 25, 7, 0);
46 | DateTime maxTime = new DateTime(2014, 4, 26, 4, 0);
47 | minDepartTime.setBounds(minTime, maxTime, false);
48 | ```
49 |
50 |
51 | The `ClockView` does not allow the user to exceed the date ranges. It animates the hands back if user scrolls beyond the valid time range.
52 |
53 |
54 | If using `CircularClockSeekBar`, Then in `onCreate` of your activity/dialog or the `onCreateView` of your fragment, You don't
55 | have to initialize it with anything. The max progress value is defaults to 120 this can be changed as per requirement.
56 |
57 | User can add `OnSeekBarChangeListener` that has callbacks for various events.
58 |
59 | ```java
60 | /**
61 | * A callback that notifies clients when the progress level has been
62 | * changed. This includes changes that were initiated by the user through a
63 | * touch gesture or arrow key/trackball as well as changes that were initiated
64 | * programmatically.
65 | */
66 | public interface OnSeekBarChangeListener {
67 | /**
68 | * Notification that the progress level has changed. Clients can use the fromUser parameter
69 | * to distinguish user-initiated changes from those that occurred programmatically.
70 | *
71 | * @param seekBar The CircularClockSeekBar whose progress has changed
72 | * @param progress The current progress level. This will be in the range 0..max where max
73 | * was set by {@link android.widget.ProgressBar#setMax(int)}. (The default value for max is 100.)
74 | * @param fromUser True if the progress change was initiated by the user.
75 | */
76 | void onProgressChanged(CircularClockSeekBar seekBar, int progress, boolean fromUser);
77 |
78 | /**
79 | * Notification that the user has started a touch gesture. Clients may want to use this
80 | * to disable advancing the seekbar.
81 | *
82 | * @param seekBar The CircularClockSeekBar in which the touch gesture began
83 | */
84 | void onStartTrackingTouch(CircularClockSeekBar seekBar);
85 |
86 | /**
87 | * Notification that the user has finished a touch gesture. Clients may want to use this
88 | * to re-enable advancing the seekbar.
89 | *
90 | * @param seekBar The CircularClockSeekBar in which the touch gesture began
91 | */
92 | void onStopTrackingTouch(CircularClockSeekBar seekBar);
93 |
94 | /**
95 | * Notification that all the animations in this seekbar are finished. Clients may use this to trigger
96 | * future events within their code.
97 | *
98 | * @param seekBar The CircularClockSeekBar for which animations are complete.
99 | */
100 | void onAnimationComplete(CircularClockSeekBar seekBar);
101 | }
102 | ```
103 |
104 | License
105 | --------
106 |
107 | Copyright 2014 TripAdvisor, Inc.
108 |
109 | Licensed under the Apache License, Version 2.0 (the "License");
110 | you may not use this file except in compliance with the License.
111 | You may obtain a copy of the License at
112 |
113 | http://www.apache.org/licenses/LICENSE-2.0
114 |
115 | Unless required by applicable law or agreed to in writing, software
116 | distributed under the License is distributed on an "AS IS" BASIS,
117 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
118 | See the License for the specific language governing permissions and
119 | limitations under the License.
120 |
121 | Inspiration
122 | -----------
123 |
124 | http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.6_r2/android/widget/AnalogClock.java
125 |
126 | https://github.com/RaghavSood/AndroidCircularSeekBar
127 |
--------------------------------------------------------------------------------
/ScreenShot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/ScreenShot.png
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:1.3.1'
8 | }
9 | }
10 | allprojects {
11 | repositories {
12 | mavenCentral()
13 | }
14 | }
15 |
16 | task clean(type: Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Nov 21 21:44:14 EST 2015
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-2.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/lib/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | local.properties
3 | *.iml
4 | build/
5 | .DS_Store
6 | .bundle
7 | bin/
8 |
9 | # See https://intellij-support.jetbrains.com/entries/23393067
10 | .idea/workspace.xml
11 | .idea/tasks.xml
12 | .idea/libraries
13 | .idea/uiDesigner.xml
14 | .idea
15 |
--------------------------------------------------------------------------------
/lib/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 | dependencies {
6 | classpath 'com.android.tools.build:gradle:1.3.0'
7 | }
8 | }
9 |
10 | apply plugin: 'com.android.library'
11 | apply plugin: 'maven'
12 |
13 | repositories {
14 | mavenCentral()
15 | }
16 |
17 | android {
18 | compileSdkVersion 21
19 | buildToolsVersion "21.1.2"
20 |
21 | defaultConfig {
22 | minSdkVersion 14
23 | targetSdkVersion 21
24 | versionCode 1
25 | versionName "1.0"
26 | }
27 |
28 | buildTypes {
29 | release {
30 | minifyEnabled false
31 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
32 | }
33 | }
34 |
35 | lintOptions {
36 | // if true, stop the gradle build if errors are found
37 | abortOnError false
38 | // if true, check all issues, including those that are off by default
39 | checkAllWarnings true
40 | // turn on the given issue id's
41 | enable 'HardcodedText','ExtraText','StringFormatInvalid','StringFormatCount',
42 | 'StringFormatMatches','DefaultLocale','ExtraTranslation','MissingTranslation',
43 | 'MissingQuantity', 'UnusedQuantity', 'RtlHardcoded', 'RtlSymmetry', 'RtlCompat',
44 | 'GradleDependency', 'ContentDescription'
45 | // turn off checking the given issue id's
46 | disable 'UnusedIds', 'SelectableText', 'IconExpectedSize', 'SetJavaScriptEnabled',
47 | 'SpUsage', 'TypographyQuotes', 'IconColors', 'ClickableViewAccessibility',
48 | 'OldTargetApi', 'MisplacedStatement', 'Overdraw'
49 | // if true, show all locations for an error, do not truncate lists, etc.
50 | showAll true
51 | // if true, generate a text report of issues (false by default)
52 | textReport true
53 | // location to write the output; can be a file or 'stdout'
54 | textOutput 'stdout'
55 | // set to true to have all release builds run lint on issues with severity=fatal
56 | // and abort the build (controlled by abortOnError above) if fatal issues are found
57 | checkReleaseBuilds true
58 | // Set the severity of the given issues to fatal (which means they will be
59 | // checked during release builds (even if the lint target is not included)
60 | fatal 'NewApi', 'InlineApi'
61 | // Set the severity of the given issues to error
62 | error 'Wakelock', 'TextViewEdits', 'HardcodedText'
63 | // Set the severity of the given issues to warning
64 | warning 'ResourceAsColor'
65 | }
66 | }
67 |
68 | def clockAARFile = file('build/libs/lib.aar')
69 | artifacts {
70 | archives clockAARFile
71 | }
72 |
73 | uploadArchives {
74 | repositories {
75 | mavenDeployer {
76 | repository(url: System.getenv('ARTIFACTORY_RELEASE_URL')){
77 | authentication(userName: System.getProperty('taUsername'),
78 | password: System.getProperty('taPassword'))
79 | }
80 | snapshotRepository(url: System.getenv('ARTIFACTORY_SNAPSHOT_URL')){
81 | authentication(userName: System.getProperty('taUsername'),
82 | password: System.getProperty('taPassword'))
83 | }
84 | pom.project {
85 | groupId 'com.tripadvisor.flights'
86 | artifactId 'android-clockseekbar'
87 | name 'Android Clock Seekbar'
88 | packaging 'aar'
89 | version '1.0.3'
90 | }
91 | }
92 | }
93 | }
94 |
95 | dependencies {
96 | compile fileTree(dir: 'libs', include: ['*.jar'])
97 | compile 'com.intellij:annotations:12.0@jar'
98 | compile 'joda-time:joda-time:2.3'
99 | }
100 |
--------------------------------------------------------------------------------
/lib/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/lib/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 06 10:47:22 EDT 2014
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-2.4-all.zip
7 |
--------------------------------------------------------------------------------
/lib/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/lib/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/lib/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/ksarmalkar/android-sdk-macosx/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the ProGuard
5 | # include property in project.properties.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
--------------------------------------------------------------------------------
/lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/lib/src/main/assets/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/assets/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/lib/src/main/assets/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/assets/Roboto-Light.ttf
--------------------------------------------------------------------------------
/lib/src/main/assets/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/assets/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/lib/src/main/assets/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/assets/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/lib/src/main/java/com/tripadvisor/seekbar/CircularClockSeekBar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 TripAdvisor
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @author ksarmalkar 7/28/2014
17 | */
18 |
19 | package com.tripadvisor.seekbar;
20 |
21 | import android.content.Context;
22 | import android.content.res.Resources;
23 | import android.content.res.TypedArray;
24 | import android.graphics.Bitmap;
25 | import android.graphics.BitmapFactory;
26 | import android.graphics.Canvas;
27 | import android.graphics.Paint;
28 | import android.graphics.drawable.Drawable;
29 | import android.os.AsyncTask;
30 | import android.util.AttributeSet;
31 | import android.view.MotionEvent;
32 | import android.view.View;
33 | import android.view.animation.DecelerateInterpolator;
34 | import android.view.animation.Interpolator;
35 |
36 | import org.jetbrains.annotations.NotNull;
37 |
38 | import static android.graphics.Paint.Style.STROKE;
39 | import static android.os.AsyncTask.Status.RUNNING;
40 | import static com.tripadvisor.seekbar.util.Utils.getDelta;
41 | import static java.lang.Math.PI;
42 | import static java.lang.Math.abs;
43 | import static java.lang.Math.atan2;
44 | import static java.lang.Math.cos;
45 | import static java.lang.Math.min;
46 | import static java.lang.Math.pow;
47 | import static java.lang.Math.round;
48 | import static java.lang.Math.sin;
49 | import static java.lang.Math.sqrt;
50 | import static java.lang.Math.toDegrees;
51 | import static java.lang.Math.toRadians;
52 |
53 | /**
54 | * Created by ksarmalkar on 4/23/14.
55 | */
56 | public final class CircularClockSeekBar extends View {
57 |
58 | public static final int MAX_SLEEP_NANOS = 999999;
59 | public static final int NANO_SECS = 1000000;
60 | public static final int TOTAL_DEGREES_INT = 360;
61 | public static final double TOTAL_DEGREES_DOUBLE = 360.0;
62 | public static final int HAND_OFFSET = 25;
63 |
64 | /**
65 | * The listener to listen for changes
66 | */
67 | private OnSeekBarChangeListener mListener;
68 |
69 | /**
70 | * The angle of progress
71 | */
72 | private float mAngle;
73 |
74 | /**
75 | * The width of the progress ring
76 | */
77 | private int mBarWidth;
78 |
79 | /**
80 | * The maximum progress amount
81 | */
82 | private int mMaxProgress = 120;
83 |
84 | /**
85 | * The current progress
86 | */
87 | private int mProgress;
88 |
89 | /**
90 | * The change in progress
91 | */
92 | private int mDeltaProgress;
93 |
94 | /**
95 | * The progress percent
96 | */
97 | private int mProgressPercent;
98 |
99 | /**
100 | * The X coordinate for the top left corner of the marking drawable
101 | */
102 | private float mMarkerPositionX;
103 |
104 | /**
105 | * The Y coordinate for the top left corner of the marking drawable
106 | */
107 | private float mMarkerPositionY;
108 |
109 | /**
110 | * The X coordinate for the current position of the marker, pre adjustment
111 | * to center
112 | */
113 | private float markPointX;
114 |
115 | /**
116 | * The Y coordinate for the current position of the marker, pre adjustment
117 | * to center
118 | */
119 | private float markPointY;
120 |
121 | /**
122 | * The adjustment factor. This adds an adjustment of the specified size to
123 | * both sides of the progress bar, allowing touch events to be processed
124 | * more user friendlily (yes, I know that's not a word)
125 | */
126 | private float mAdjustmentFactor = 40.0f;
127 |
128 | /**
129 | * The flag to see if the setProgress() method was called from our own
130 | * View's setAngle() method, or externally by a user.
131 | */
132 | private boolean mFromUser;
133 |
134 | private Context mContext;
135 | private float mInnerRadius;
136 | private float mOuterRadius;
137 | private float mCircleCenterX;
138 | private float mCircleCenterY;
139 | private boolean mIsPressed;
140 | private boolean mChanged;
141 | private boolean mIsProgressSetViaApi;
142 | private Bitmap mScrubberNormal;
143 | private Bitmap mScrubberPressed;
144 | private Paint mCirclePaint;
145 |
146 | private Drawable mDial;
147 | private Drawable mHourHand;
148 | private Drawable mMinuteHand;
149 |
150 | private int mDialWidth;
151 | private int mDialHeight;
152 |
153 | private RotateAnimationTask mRotateAnimationTask;
154 | private DecelerateInterpolator mInterpolator;
155 |
156 | public CircularClockSeekBar(Context context, AttributeSet attrs, int defStyle) {
157 | super(context, attrs, defStyle);
158 | mContext = context;
159 | initDrawable(context, attrs, defStyle);
160 | }
161 |
162 | public CircularClockSeekBar(Context context, AttributeSet attrs) {
163 | this(context, attrs, 0);
164 | }
165 |
166 | public CircularClockSeekBar(Context context) {
167 | this(context, null, 0);
168 | }
169 |
170 | public final void initDrawable(Context context, AttributeSet attrs, int defStyle) {
171 |
172 | mListener = new OnSeekBarChangeListener() {
173 | @Override
174 | public void onProgressChanged(CircularClockSeekBar seekBar, int progress, boolean fromUser) {
175 | }
176 |
177 | @Override
178 | public void onStartTrackingTouch(CircularClockSeekBar seekBar) {
179 | }
180 |
181 | @Override
182 | public void onStopTrackingTouch(CircularClockSeekBar seekBar) {
183 | }
184 |
185 | @Override
186 | public void onAnimationComplete(CircularClockSeekBar seekBar) {
187 | }
188 | };
189 |
190 | mCirclePaint = new Paint();
191 | mCirclePaint.setStyle(STROKE);
192 | mCirclePaint.setColor(getResources().getColor(R.color.seekbar_color));// Set default background color to Gray
193 | mCirclePaint.setAntiAlias(true);
194 | mCirclePaint.setStrokeWidth(getResources().getDimension(R.dimen.seekbar_width));
195 |
196 | mBarWidth = (int) getResources().getDimension(R.dimen.seekbar_width);
197 |
198 | mScrubberNormal = BitmapFactory.decodeResource(mContext.getResources(),
199 | R.drawable.scrubber_control_normal_holo_green);
200 | mScrubberPressed = BitmapFactory.decodeResource(mContext.getResources(),
201 | R.drawable.scrubber_control_pressed_holo_green);
202 |
203 | Resources r = context.getResources();
204 | TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CircularClockSeekBar, defStyle, 0);
205 | try {
206 | mHourHand = attributes.getDrawable(R.styleable.CircularClockSeekBar_hand_hour);
207 | if (mHourHand == null) {
208 | mHourHand = r.getDrawable(R.drawable.time_shorthand);
209 | }
210 |
211 | mMinuteHand = attributes.getDrawable(R.styleable.CircularClockSeekBar_hand_minute);
212 | if (mMinuteHand == null) {
213 | mMinuteHand = r.getDrawable(R.drawable.time_longhand);
214 | }
215 | } finally {
216 | attributes.recycle();
217 | }
218 |
219 | mDial = r.getDrawable(R.drawable.clock_dial);
220 |
221 | mDialWidth = mDial.getIntrinsicWidth();
222 | mDialHeight = mDial.getIntrinsicHeight();
223 | }
224 |
225 | public void roundToNearestDegree(int degree) {
226 | int mod = mDeltaProgress % degree;
227 | int halfOfDegree = degree / 2;
228 | final int oldDegree = round(mAngle);
229 | final int newDegree;
230 | if (mod < 0) {
231 | if (-mod <= halfOfDegree) {
232 | // round to previous
233 | newDegree = round(mAngle - mod);
234 | } else {
235 | // round to next
236 | newDegree = round(mAngle - (degree + mod));
237 | }
238 | } else {
239 | if (mod <= halfOfDegree) {
240 | // round to previous
241 | newDegree = round(mAngle - mod);
242 | } else {
243 | // round to next
244 | newDegree = round(mAngle + (degree - mod));
245 | }
246 | }
247 | final int delta = abs(newDegree - oldDegree);
248 | startAnimation(delta, oldDegree, newDegree, true, false);
249 | }
250 |
251 | public void moveToDelta(int fromDelta, int toDelta) {
252 | final int delta = abs(toDelta - fromDelta);
253 | final int oldDegrees = round(mAngle);
254 | // final int newDegrees = round(mAngle + (toDelta - fromDelta));
255 | final int newDegrees = (int) ((mAngle + toDelta) % 360);
256 | mDeltaProgress += toDelta;
257 | startAnimation(delta, oldDegrees, newDegrees, false, true);
258 | }
259 |
260 | public void animateToDelta(int fromDelta, int toDelta) {
261 | final int delta = abs(toDelta - fromDelta);
262 | final int oldDegrees = round(mAngle);
263 | final int newDegrees = round((mAngle + (toDelta - fromDelta)) % 360);
264 | mDeltaProgress = toDelta;
265 | startAnimation(delta, oldDegrees, newDegrees, true, true);
266 | }
267 |
268 | private void startAnimation(final int delta, final int oldDegrees, final int newDegrees, final boolean animate, boolean isDeltaPreComputed) {
269 | mInterpolator = new DecelerateInterpolator();
270 | if (newDegrees - oldDegrees < 0) {
271 | // we are going anti-clock wise
272 | mRotateAnimationTask = new RotateAnimationTask(newDegrees, oldDegrees, animate, isDeltaPreComputed) {
273 | @Override
274 | protected Void doInBackground(Void... params) {
275 | for (int i = oldDegrees, j = 0; i >= newDegrees; i--, j++) {
276 | try {
277 | if (animate) {
278 | animationSleep(j, mInterpolator, delta);
279 | }
280 | } catch (InterruptedException e) {
281 | e.printStackTrace();
282 | }
283 | publishProgress(i);
284 | }
285 | return null;
286 | }
287 | };
288 | mRotateAnimationTask.execute();
289 | } else {
290 | // we are going clock wise
291 | mRotateAnimationTask = new RotateAnimationTask(newDegrees, oldDegrees, animate, isDeltaPreComputed) {
292 | @Override
293 | protected Void doInBackground(Void... params) {
294 | for (int i = oldDegrees, j = 0; i <= newDegrees; i++, j++) {
295 | try {
296 | if (animate) {
297 | animationSleep(j, mInterpolator, delta);
298 | }
299 | } catch (InterruptedException e) {
300 | e.printStackTrace();
301 | }
302 | publishProgress(i);
303 | }
304 | return null;
305 | }
306 | };
307 | mRotateAnimationTask.execute();
308 | }
309 | }
310 |
311 | private void animationSleep(int j, Interpolator interpolator, float delta) throws InterruptedException {
312 | int interpolation = (int) (interpolator.getInterpolation(j / delta) * 10000000);
313 | int milliseconds = 0;
314 | if (interpolation >= MAX_SLEEP_NANOS) {
315 | milliseconds = interpolation / NANO_SECS;
316 | interpolation = interpolation % NANO_SECS;
317 | }
318 | try {
319 | Thread.sleep(milliseconds, interpolation);
320 | } catch (IllegalArgumentException ignore) {
321 | }
322 | }
323 |
324 | private abstract class RotateAnimationTask extends AsyncTask {
325 |
326 | private int mNewDegrees;
327 | private int mOldDegrees;
328 | private boolean mAnimate;
329 | private boolean mIsDeltaPreComputed;
330 |
331 | public RotateAnimationTask(int newDegrees, int oldDegrees, boolean animate, boolean isDeltaPreComputed) {
332 | mNewDegrees = newDegrees;
333 | mOldDegrees = oldDegrees;
334 | mAnimate = animate;
335 | mIsDeltaPreComputed = isDeltaPreComputed;
336 | }
337 |
338 | @Override
339 | protected void onProgressUpdate(Integer... values) {
340 | super.onProgressUpdate(values[0]);
341 | setAngle(values[0]);
342 | mIsProgressSetViaApi = true;
343 | invalidate();
344 | }
345 |
346 | @Override
347 | protected void onPostExecute(Void aVoid) {
348 | super.onPostExecute(aVoid);
349 | // adjust and make it positive to maintain consistency of internal state.
350 | // as our angle never goes negative.
351 | mNewDegrees = mNewDegrees <= 0 ? TOTAL_DEGREES_INT + mNewDegrees : mNewDegrees;
352 | setAngle(mNewDegrees);
353 | if (!mIsDeltaPreComputed) {
354 | mDeltaProgress += getDelta(mOldDegrees, mNewDegrees);
355 | }
356 | mListener.onProgressChanged(CircularClockSeekBar.this, mProgress, mFromUser);
357 | boolean isRoundingRequired = mDeltaProgress % 30 != 0;
358 | if (isRoundingRequired) {
359 | roundToNearestDegree(30);
360 | } else {
361 | mListener.onAnimationComplete(CircularClockSeekBar.this);
362 | }
363 | if (!mAnimate) {
364 | mIsProgressSetViaApi = true;
365 | }
366 | invalidate();
367 | }
368 | }
369 |
370 | public enum ClockRangeStatus {
371 | VALID_RANGE,
372 | DIFFERENT_DAY_OF_WEEK,
373 | INVALID_RANGE
374 | }
375 |
376 | public void setSeekBarStatus(ClockRangeStatus status) {
377 | switch (status) {
378 | case DIFFERENT_DAY_OF_WEEK:
379 | mCirclePaint.setColor(getResources().getColor(R.color.orange));
380 | mScrubberNormal = BitmapFactory.decodeResource(mContext.getResources(),
381 | R.drawable.scrubber_control_normal_holo_orange);
382 | mScrubberPressed = BitmapFactory.decodeResource(mContext.getResources(),
383 | R.drawable.scrubber_control_pressed_holo_orange);
384 | break;
385 | case INVALID_RANGE:
386 | mCirclePaint.setColor(getResources().getColor(R.color.error_clock));
387 | mScrubberNormal = BitmapFactory.decodeResource(mContext.getResources(),
388 | R.drawable.scrubber_control_normal_holo_red);
389 | mScrubberPressed = BitmapFactory.decodeResource(mContext.getResources(),
390 | R.drawable.scrubber_control_pressed_holo_red);
391 | break;
392 | default:
393 | mCirclePaint.setColor(getResources().getColor(R.color.seekbar_color));
394 | mScrubberNormal = BitmapFactory.decodeResource(mContext.getResources(),
395 | R.drawable.scrubber_control_normal_holo_green);
396 | mScrubberPressed = BitmapFactory.decodeResource(mContext.getResources(),
397 | R.drawable.scrubber_control_pressed_holo_green);
398 | break;
399 | }
400 | }
401 |
402 | @Override
403 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
404 |
405 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
406 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
407 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
408 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
409 |
410 | float hScale = 1.0f;
411 | float vScale = 1.0f;
412 |
413 | if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {
414 | hScale = widthSize / (float) mDialWidth;
415 | }
416 |
417 | if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) {
418 | vScale = heightSize / (float) mDialHeight;
419 | }
420 |
421 | float scale = min(hScale, vScale);
422 |
423 | setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec),
424 | resolveSize((int) (mDialHeight * scale), heightMeasureSpec));
425 |
426 | int width = getMeasuredWidth();// Get View Width
427 | int height = getMeasuredHeight();// Get View Height
428 |
429 | int size = (width > height) ? height : width; // Choose the smaller
430 | // between width and
431 | // height to make a
432 | // square
433 |
434 | mCircleCenterX = width / 2; // Center X for circle
435 | mCircleCenterY = height / 2; // Center Y for circle
436 | mOuterRadius = size / 2 - mScrubberPressed.getWidth() / 2; // Radius of the outer circle
437 |
438 | mInnerRadius = mOuterRadius - mBarWidth; // Radius of the circle
439 |
440 | float startPointX = mCircleCenterX; //The X coordinate for 12 O'Clock
441 | float startPointY = mCircleCenterY - mOuterRadius;// 12 O'clock Y coordinate
442 | if (!mIsProgressSetViaApi) {
443 | markPointX = startPointX;// Initial location of the marker X coordinate
444 | markPointY = startPointY;// Initial location of the marker Y coordinate
445 | }
446 | }
447 |
448 | @Override
449 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
450 | super.onSizeChanged(w, h, oldw, oldh);
451 | mChanged = true;
452 | }
453 |
454 | @Override
455 | protected void onDraw(Canvas canvas) {
456 | super.onDraw(canvas);
457 |
458 | boolean changed = mChanged;
459 | if (changed) {
460 | mChanged = false;
461 | }
462 |
463 | int availableWidth = getRight() - getLeft();
464 | int availableHeight = getBottom() - getTop();
465 |
466 | int x = availableWidth / 2;
467 | int y = availableHeight / 2;
468 |
469 | final Drawable dial = mDial;
470 | int w = dial.getIntrinsicWidth();
471 | int h = dial.getIntrinsicHeight();
472 |
473 | boolean scaled = false;
474 |
475 | if (availableWidth < w || availableHeight < h) {
476 | scaled = true;
477 | float scale = min(availableWidth / (float) w,
478 | (float) availableHeight / h);
479 | canvas.save();
480 | canvas.scale(scale, scale, x, y);
481 | }
482 |
483 | if (changed) {
484 | dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
485 | }
486 | canvas.save();
487 |
488 | markPointX = mCircleCenterX - (float) (mOuterRadius * cos(toRadians((mProgress * 360.0d / mMaxProgress) + 90.0d)));
489 | markPointY = mCircleCenterY - (float) (mOuterRadius * sin(toRadians((mProgress * 360.0d / mMaxProgress) + 90.0d)));
490 | mMarkerPositionX = getXFromAngle();
491 | mMarkerPositionY = getYFromAngle();
492 |
493 | canvas.drawCircle(mCircleCenterX, mCircleCenterY, mOuterRadius, mCirclePaint);
494 | drawMarkerAtProgress(canvas);
495 |
496 | canvas.rotate(0, x, y);
497 |
498 | final Drawable minuteHand = mMinuteHand;
499 | if (changed) {
500 | w = minuteHand.getIntrinsicWidth();
501 | h = (int) (minuteHand.getIntrinsicHeight() - (HAND_OFFSET * getResources().getDisplayMetrics().density));
502 | minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
503 | }
504 | minuteHand.draw(canvas);
505 | canvas.restore();
506 | canvas.save();
507 |
508 | canvas.rotate(mProgress * 360.0f / mMaxProgress, x, y);
509 | final Drawable hourHand = mHourHand;
510 | if (changed) {
511 | w = hourHand.getIntrinsicWidth();
512 | h = (int) (hourHand.getIntrinsicHeight() - (HAND_OFFSET * getResources().getDisplayMetrics().density));
513 | hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
514 | }
515 | hourHand.draw(canvas);
516 | canvas.restore();
517 | canvas.save();
518 |
519 | if (scaled) {
520 | canvas.restore();
521 | }
522 | }
523 |
524 | /**
525 | * Draw marker at the current progress point onto the given canvas.
526 | *
527 | * @param canvas the canvas
528 | */
529 | private void drawMarkerAtProgress(Canvas canvas) {
530 | if (mIsPressed) {
531 | canvas.drawBitmap(mScrubberPressed, mMarkerPositionX, mMarkerPositionY, null);
532 | } else {
533 | canvas.drawBitmap(mScrubberNormal, mMarkerPositionX, mMarkerPositionY, null);
534 | }
535 | }
536 |
537 | /**
538 | * Gets the X coordinate of the arc's end arm's point of intersection with
539 | * the circle
540 | *
541 | * @return the X coordinate
542 | */
543 | private float getXFromAngle() {
544 | int size1 = mScrubberNormal.getWidth();
545 | int size2 = mScrubberPressed.getWidth();
546 | int adjust = (size1 > size2) ? size1 : size2;
547 | return markPointX - (adjust / 2.0f);
548 | }
549 |
550 | /**
551 | * Gets the Y coordinate of the arc's end arm's point of intersection with
552 | * the circle
553 | *
554 | * @return the Y coordinate
555 | */
556 | private float getYFromAngle() {
557 | int size1 = mScrubberNormal.getHeight();
558 | int size2 = mScrubberPressed.getHeight();
559 | int adjust = (size1 > size2) ? size1 : size2;
560 | return markPointY - (adjust / 2.0f);
561 | }
562 |
563 | public float getAngle() {
564 | return mAngle;
565 | }
566 |
567 | private void setAngle(float angle) {
568 | this.mAngle = angle;
569 | float donePercent = (this.mAngle / 360.0f) * mMaxProgress;
570 | float progress = (donePercent / mMaxProgress) * getMaxProgress();
571 | setProgressPercent(round(donePercent));
572 | mFromUser = true;
573 | mIsProgressSetViaApi = false;
574 | setProgressInternal(round(progress));
575 | }
576 |
577 | public int getProgress() {
578 | return mProgress;
579 | }
580 |
581 | public void setProgress(int progress) {
582 | mFromUser = true;
583 | // since this is set by api
584 | // we need to calculate markPointX and markPointY
585 | mIsProgressSetViaApi = true;
586 | setProgressInternal(progress);
587 | }
588 |
589 | private void setProgressInternal(int progress) {
590 | if (this.mProgress != progress) {
591 | this.mProgress = progress;
592 | if (!mFromUser) {
593 | int newPercent = (this.mProgress / this.mMaxProgress) * mMaxProgress;
594 | int newAngle = (newPercent / mMaxProgress) * TOTAL_DEGREES_INT;
595 | this.setAngle(newAngle);
596 | mProgressPercent = newPercent;
597 | }
598 | if (mIsProgressSetViaApi) {
599 | mAngle = mProgress * 360.0f / mMaxProgress;
600 | }
601 | }
602 | mListener.onProgressChanged(this, mProgress, mFromUser);
603 | mFromUser = false;
604 | }
605 |
606 | @Override
607 | public boolean onTouchEvent(@NotNull MotionEvent event) {
608 | performClick();
609 | if (mRotateAnimationTask != null && mRotateAnimationTask.getStatus() == RUNNING) {
610 | return true;
611 | }
612 | float x = event.getX();
613 | float y = event.getY();
614 | switch (event.getAction()) {
615 | case MotionEvent.ACTION_DOWN:
616 | return moved(x, y, false);
617 | case MotionEvent.ACTION_MOVE:
618 | mListener.onStartTrackingTouch(this);
619 | return moved(x, y, false);
620 | case MotionEvent.ACTION_UP:
621 | mListener.onStopTrackingTouch(this);
622 | return moved(x, y, true);
623 | }
624 | return false;
625 | }
626 |
627 | /**
628 | * This prevents the touch event from bubbling up if this views parent is scrollable
629 | *
630 | * @param event
631 | * @return
632 | */
633 | @Override
634 | public boolean dispatchTouchEvent(@NotNull MotionEvent event) {
635 | if (getParent() != null && event.getAction() == MotionEvent.ACTION_DOWN) {
636 | getParent().requestDisallowInterceptTouchEvent(true);
637 | }
638 | return super.dispatchTouchEvent(event);
639 | }
640 |
641 | /**
642 | * Moved.
643 | *
644 | * @param x the x
645 | * @param y the y
646 | * @param up the up
647 | */
648 | private boolean moved(float x, float y, boolean up) {
649 | float distance = (float) sqrt(pow((x - mCircleCenterX), 2) + pow((y - mCircleCenterY), 2));
650 | if (distance < mOuterRadius + mAdjustmentFactor && distance > mInnerRadius - mAdjustmentFactor && !up) {
651 | mIsPressed = true;
652 |
653 | markPointX = (float) (mCircleCenterX + mOuterRadius * cos(atan2(x - mCircleCenterX, mCircleCenterY - y) - (PI / 2)));
654 | markPointY = (float) (mCircleCenterY + mOuterRadius * sin(atan2(x - mCircleCenterX, mCircleCenterY - y) - (PI / 2)));
655 |
656 | float degrees = (float) ((float) ((toDegrees(atan2(x - mCircleCenterX, mCircleCenterY - y)) + TOTAL_DEGREES_DOUBLE)) % TOTAL_DEGREES_DOUBLE);
657 | // and to make it count 0-360
658 | if (degrees < 0) {
659 | degrees += 2 * PI;
660 | }
661 |
662 | int newDegrees = round(degrees);
663 | int oldDegrees = (int) mAngle;
664 | int newDelta;
665 | if ((newDelta = getDelta(oldDegrees, newDegrees)) == 0) {
666 | // no need to invalidate or set new values nothing changed
667 | return false;
668 | }
669 | mDeltaProgress += newDelta;
670 | setAngle(newDegrees);
671 | invalidate();
672 | return true;
673 | } else {
674 | mIsPressed = false;
675 | invalidate();
676 | return false;
677 | }
678 | }
679 |
680 | /**
681 | * Sets the adjustment factor.
682 | *
683 | * @param adjustmentFactor the new adjustment factor
684 | */
685 | public void setAdjustmentFactor(float adjustmentFactor) {
686 | this.mAdjustmentFactor = adjustmentFactor;
687 | }
688 |
689 | public int getMaxProgress() {
690 | return mMaxProgress;
691 | }
692 |
693 | public void setMaxProgress(int maxProgress) {
694 | mMaxProgress = maxProgress;
695 | }
696 |
697 | public int getProgressPercent() {
698 | return mProgressPercent;
699 | }
700 |
701 | public void setProgressPercent(int progressPercent) {
702 | this.mProgressPercent = progressPercent;
703 | }
704 |
705 | /**
706 | * Gets the total progress since instantiation.
707 | *
708 | * @return the total progress
709 | */
710 | public int getProgressDelta() {
711 | return mDeltaProgress;
712 | }
713 |
714 | /**
715 | * A callback that notifies clients when the progress level has been
716 | * changed. This includes changes that were initiated by the user through a
717 | * touch gesture or arrow key/trackball as well as changes that were initiated
718 | * programmatically.
719 | */
720 | public interface OnSeekBarChangeListener {
721 |
722 | /**
723 | * Notification that the progress level has changed. Clients can use the fromUser parameter
724 | * to distinguish user-initiated changes from those that occurred programmatically.
725 | *
726 | * @param seekBar The CircularClockSeekBar whose progress has changed
727 | * @param progress The current progress level. This will be in the range 0..max where max
728 | * was set by {@link android.widget.ProgressBar#setMax(int)}. (The default value for max is 100.)
729 | * @param fromUser True if the progress change was initiated by the user.
730 | */
731 | void onProgressChanged(CircularClockSeekBar seekBar, int progress, boolean fromUser);
732 |
733 | /**
734 | * Notification that the user has started a touch gesture. Clients may want to use this
735 | * to disable advancing the seekbar.
736 | *
737 | * @param seekBar The CircularClockSeekBar in which the touch gesture began
738 | */
739 | void onStartTrackingTouch(CircularClockSeekBar seekBar);
740 |
741 | /**
742 | * Notification that the user has finished a touch gesture. Clients may want to use this
743 | * to re-enable advancing the seekbar.
744 | *
745 | * @param seekBar The CircularClockSeekBar in which the touch gesture began
746 | */
747 | void onStopTrackingTouch(CircularClockSeekBar seekBar);
748 |
749 | /**
750 | * Notification that all the animations in this seekbar are finished. Clients may use this to trigger
751 | * future events within their code.
752 | *
753 | * @param seekBar The CircularClockSeekBar for which animations are complete.
754 | */
755 | void onAnimationComplete(CircularClockSeekBar seekBar);
756 | }
757 |
758 | public void setSeekBarChangeListener(OnSeekBarChangeListener listener) {
759 | mListener = listener;
760 | }
761 |
762 | public void reset() {
763 | mDeltaProgress = 0;
764 | mAngle = 0;
765 | mProgress = 0;
766 | mIsProgressSetViaApi = true;
767 | if (mRotateAnimationTask != null) {
768 | mRotateAnimationTask.cancel(true);
769 | mRotateAnimationTask = null;
770 | }
771 | invalidate();
772 | }
773 | }
774 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/tripadvisor/seekbar/ClockView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 TripAdvisor
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @author ksarmalkar 7/28/2014
17 | */
18 |
19 | package com.tripadvisor.seekbar;
20 |
21 | import android.content.Context;
22 | import android.text.format.DateFormat;
23 | import android.util.AttributeSet;
24 | import android.view.LayoutInflater;
25 | import android.view.View;
26 | import android.widget.LinearLayout;
27 |
28 | import com.tripadvisor.seekbar.util.Utils;
29 |
30 | import org.jetbrains.annotations.Nullable;
31 | import org.joda.time.DateTime;
32 | import org.joda.time.DateTimeFieldType;
33 | import org.joda.time.Interval;
34 | import org.joda.time.Minutes;
35 |
36 | import java.util.Locale;
37 |
38 | import static com.tripadvisor.seekbar.CircularClockSeekBar.ClockRangeStatus.DIFFERENT_DAY_OF_WEEK;
39 | import static com.tripadvisor.seekbar.CircularClockSeekBar.ClockRangeStatus.INVALID_RANGE;
40 | import static com.tripadvisor.seekbar.CircularClockSeekBar.ClockRangeStatus.VALID_RANGE;
41 | import static com.tripadvisor.seekbar.util.Utils.FontType.REGULAR;
42 | import static com.tripadvisor.seekbar.util.Utils.SIMPLE_DATE_FORMAT_AM_PM;
43 | import static com.tripadvisor.seekbar.util.Utils.SIMPLE_DATE_FORMAT_HOURS;
44 | import static com.tripadvisor.seekbar.util.Utils.SIMPLE_DATE_FORMAT_MERIDIAN;
45 |
46 | public class ClockView extends LinearLayout {
47 |
48 | public static final float LETTER_SPACING = -3.0f;
49 | private final LetterSpacingTextView mTimeText;
50 | private final LetterSpacingTextView mTimeMeridianText;
51 | private final RobotoTextView mTimeWeekDayText;
52 | private final CircularClockSeekBar mCircularClockSeekBar;
53 | private Interval mValidTimeInterval;
54 | private DateTime mOriginalTime;
55 | private final boolean mIs24HourFormat;
56 | private DateTime mNewCurrentTime;
57 | private int mCurrentValidProgressDelta;
58 | private DateTime mCurrentValidTime;
59 | private ClockTimeUpdateListener mClockTimeUpdateListener;
60 |
61 | public ClockView(Context context, AttributeSet attrs) {
62 | super(context, attrs);
63 | mClockTimeUpdateListener = new ClockTimeUpdateListener() {
64 | @Override
65 | public void onClockTimeUpdate(ClockView clockView, DateTime currentTime) {
66 | }
67 | };
68 | mIs24HourFormat = DateFormat.is24HourFormat(context);
69 | final View view = LayoutInflater.from(context).inflate(R.layout.clock_view, this);
70 | mTimeText = (LetterSpacingTextView) view.findViewById(R.id.time_text_view);
71 | mTimeMeridianText = (LetterSpacingTextView) view.findViewById(R.id.time_meredian_text_view);
72 | mTimeWeekDayText = (RobotoTextView) view.findViewById(R.id.time_week_day_text);
73 | if (!isInEditMode()) {
74 | mTimeText.setLetterSpacing(LETTER_SPACING);
75 | mTimeMeridianText.setTypeface(Utils.getRobotoTypeface(context, REGULAR));
76 | mTimeMeridianText.setLetterSpacing(LETTER_SPACING);
77 | }
78 | mCircularClockSeekBar = (CircularClockSeekBar) view.findViewById(R.id.clock_seek_bar);
79 | mCircularClockSeekBar.setSeekBarChangeListener(new CircularClockSeekBar.OnSeekBarChangeListener() {
80 | @Override
81 | public void onProgressChanged(CircularClockSeekBar seekBar, int progress, boolean fromUser) {
82 | updateProgressWithDelta(seekBar.getProgressDelta());
83 | }
84 |
85 | @Override
86 | public void onStartTrackingTouch(CircularClockSeekBar seekBar) {
87 | }
88 |
89 | @Override
90 | public void onStopTrackingTouch(CircularClockSeekBar seekBar) {
91 | // snap to correct position
92 | if (mValidTimeInterval.contains(mNewCurrentTime)) {
93 | // snap to nearest hour.
94 | int progressDelta = (int) (mCircularClockSeekBar.getAngle() % 30);
95 | if (progressDelta != 0) {
96 | // snap either to previous/next hour
97 | mCircularClockSeekBar.roundToNearestDegree(30);
98 | } else {
99 | // we are trigering onClockTimeUpdate because the user was perfect and moved the
100 | // clock by exact multiple of 30 degrees so there is no animation and the time is still
101 | // within valid time interval
102 | mClockTimeUpdateListener.onClockTimeUpdate(ClockView.this, mNewCurrentTime);
103 | }
104 | } else {
105 | // slide-animate back or forward
106 | mCircularClockSeekBar.animateToDelta(mCircularClockSeekBar.getProgressDelta(), mCurrentValidProgressDelta);
107 | setClockText(mCurrentValidTime);
108 | }
109 | }
110 |
111 | @Override
112 | public void onAnimationComplete(CircularClockSeekBar seekBar) {
113 | if (mValidTimeInterval != null && mNewCurrentTime != null
114 | && mValidTimeInterval.contains(mNewCurrentTime)) {
115 | mClockTimeUpdateListener.onClockTimeUpdate(ClockView.this, mNewCurrentTime);
116 | }
117 | }
118 | });
119 | }
120 |
121 | public interface ClockTimeUpdateListener {
122 | public void onClockTimeUpdate(ClockView clockView, DateTime currentTime);
123 | }
124 |
125 | public void setNewCurrentTime(DateTime newCurrentTime) {
126 | if (mValidTimeInterval != null && newCurrentTime != null && mNewCurrentTime != null
127 | && mValidTimeInterval.contains(newCurrentTime)) {
128 | int diffInMinutes = Minutes.minutesBetween(mNewCurrentTime, newCurrentTime).getMinutes();
129 | mCircularClockSeekBar.moveToDelta(mCurrentValidProgressDelta, diffInMinutes / 2);
130 | setClockText(mCurrentValidTime);
131 | }
132 | }
133 |
134 | private void updateProgressWithDelta(int progressDelta) {
135 | // 1 deg = 2 min
136 | mNewCurrentTime = mOriginalTime.plusMinutes(progressDelta * 2);
137 | setClockText(mNewCurrentTime);
138 | if (mValidTimeInterval != null && mNewCurrentTime != null
139 | && mValidTimeInterval.contains(mNewCurrentTime)) {
140 | mCurrentValidProgressDelta = progressDelta;
141 | mCurrentValidTime = mNewCurrentTime.minusMinutes(progressDelta * 2);
142 | }
143 | }
144 |
145 | private void setClockText(DateTime newCurrentTime) {
146 | if (mIs24HourFormat) {
147 | mTimeText.setText(SIMPLE_DATE_FORMAT_HOURS.print(newCurrentTime));
148 | mTimeMeridianText.setText(R.string.flights_app_short_hrs_cbd);
149 | } else {
150 | mTimeText.setText(SIMPLE_DATE_FORMAT_AM_PM.print(newCurrentTime));
151 | mTimeMeridianText.setText(SIMPLE_DATE_FORMAT_MERIDIAN.print(newCurrentTime).toLowerCase(Locale.US));
152 | }
153 | setSeekBarStatus(newCurrentTime);
154 | }
155 |
156 | private void setSeekBarStatus(DateTime newCurrentTime) {
157 | if (mValidTimeInterval.contains(newCurrentTime)) {
158 | if (newCurrentTime.getDayOfWeek() == mValidTimeInterval.getStart().getDayOfWeek()) {
159 | mCircularClockSeekBar.setSeekBarStatus(VALID_RANGE);
160 | mTimeWeekDayText.setVisibility(GONE);
161 | } else {
162 | mCircularClockSeekBar.setSeekBarStatus(DIFFERENT_DAY_OF_WEEK);
163 | mTimeWeekDayText.setVisibility(VISIBLE);
164 | mTimeWeekDayText.setText(newCurrentTime.toString("EEE"));
165 | }
166 | } else {
167 | mCircularClockSeekBar.setSeekBarStatus(INVALID_RANGE);
168 | mTimeWeekDayText.setVisibility(GONE);
169 | }
170 | }
171 |
172 | public void setBounds(DateTime minTime, DateTime maxTime, boolean isMaxClock) {
173 | // NOTE: To show correct end time on clock, since the Interval.contains() checks for
174 | // millisInstant >= thisStart && millisInstant < thisEnd
175 | // however we want
176 | // millisInstant >= thisStart && millisInstant <= thisEnd
177 | maxTime = maxTime.plusMillis(1);
178 | mValidTimeInterval = new Interval(minTime, maxTime);
179 | maxTime = maxTime.minusMillis(1);
180 | mCircularClockSeekBar.reset();
181 | if (isMaxClock) {
182 | mOriginalTime = maxTime;
183 | mCurrentValidTime = maxTime;
184 | int hourOfDay = maxTime.get(DateTimeFieldType.clockhourOfDay()) % 12;
185 | mCircularClockSeekBar.setProgress(hourOfDay * 10);
186 | setClockText(mOriginalTime);
187 | } else {
188 | mOriginalTime = minTime;
189 | mCurrentValidTime = minTime;
190 | int hourOfDay = minTime.get(DateTimeFieldType.clockhourOfDay()) % 12;
191 | mCircularClockSeekBar.setProgress(hourOfDay * 10);
192 | setClockText(mOriginalTime);
193 | }
194 | }
195 |
196 | @Nullable
197 | @Override
198 | public CharSequence getContentDescription() {
199 | return String.format("%s%s", mTimeText.getText(), mTimeMeridianText.getText());
200 | }
201 |
202 | public void setClockTimeUpdateListener(ClockTimeUpdateListener clockTimeUpdateListener) {
203 | mClockTimeUpdateListener = clockTimeUpdateListener;
204 | }
205 |
206 | public void removeClockTimeUpdateListener(ClockTimeUpdateListener clockTimeUpdateListener) {
207 | if (mClockTimeUpdateListener.equals(clockTimeUpdateListener)) {
208 | mClockTimeUpdateListener = new ClockTimeUpdateListener() {
209 | @Override
210 | public void onClockTimeUpdate(ClockView clockView, DateTime currentTime) {
211 | }
212 | };
213 | }
214 | }
215 |
216 | public DateTime getCurrentValidTime() {
217 | return mCurrentValidTime;
218 | }
219 |
220 | public DateTime getNewCurrentTime() {
221 | return mNewCurrentTime;
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/tripadvisor/seekbar/LetterSpacingTextView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 TripAdvisor
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @author ksarmalkar 7/28/2014
17 | */
18 |
19 | package com.tripadvisor.seekbar;
20 |
21 | import android.content.Context;
22 | import android.text.SpannableString;
23 | import android.text.style.ScaleXSpan;
24 | import android.util.AttributeSet;
25 |
26 | import static android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE;
27 |
28 | /**
29 | * Text view that allows changing the letter spacing of the text.
30 | * Source: http://stackoverflow.com/questions/1640659/how-to-adjust-text-kerning-in-android-textview/16429758#16429758
31 | *
32 | * @author Pedro Barros (pedrobarros.dev at gmail.com)
33 | * @since May 7, 2013
34 | */
35 | public class LetterSpacingTextView extends RobotoTextView {
36 |
37 | private float letterSpacing = 0.0F;
38 | private CharSequence originalText = "";
39 |
40 |
41 | public LetterSpacingTextView(Context context, AttributeSet attrs) {
42 | super(context, attrs);
43 | }
44 |
45 | public float getLetterSpacing() {
46 | return letterSpacing;
47 | }
48 |
49 | public void setLetterSpacing(float mLetterSpacing) {
50 | this.letterSpacing = mLetterSpacing;
51 | requestLayout();
52 | invalidate();
53 | }
54 |
55 | @Override
56 | public void setText(CharSequence text, BufferType type) {
57 | // Below if block taken from android source code, this ensures we dont have NPE in future.
58 | // However note that this was not causing FLT-1507
59 | if (text == null) {
60 | text = "";
61 | }
62 | originalText = text;
63 | applyLetterSpacing();
64 | }
65 |
66 | @Override
67 | public CharSequence getText() {
68 | return originalText;
69 | }
70 |
71 | private void applyLetterSpacing() {
72 | StringBuilder builder = new StringBuilder();
73 | for (int i = 0; i < originalText.length(); i++) {
74 | builder.append(originalText.charAt(i));
75 | if (i + 1 < originalText.length()) {
76 | builder.append('\u00A0');
77 | }
78 | }
79 | SpannableString finalText = new SpannableString(builder.toString());
80 | if (builder.toString().length() > 1) {
81 | for (int i = 1; i < builder.toString().length(); i += 2) {
82 | finalText.setSpan(new ScaleXSpan((letterSpacing + 1.0F) / 10.0F), i, i + 1, SPAN_EXCLUSIVE_EXCLUSIVE);
83 | }
84 | }
85 | super.setText(finalText, BufferType.SPANNABLE);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/tripadvisor/seekbar/RobotoTextView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 TripAdvisor
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @author ksarmalkar 7/28/2014
17 | */
18 |
19 | package com.tripadvisor.seekbar;
20 |
21 | import android.content.Context;
22 | import android.content.res.TypedArray;
23 | import android.graphics.Paint;
24 | import android.util.AttributeSet;
25 | import android.widget.TextView;
26 |
27 | import com.tripadvisor.seekbar.util.Utils;
28 |
29 | import static com.tripadvisor.seekbar.util.Utils.FontType.BOLD;
30 | import static com.tripadvisor.seekbar.util.Utils.FontType.LIGHT;
31 | import static com.tripadvisor.seekbar.util.Utils.FontType.MEDIUM;
32 | import static com.tripadvisor.seekbar.util.Utils.FontType.REGULAR;
33 | import static com.tripadvisor.seekbar.util.Utils.FontType.values;
34 | import static com.tripadvisor.seekbar.util.Utils.getRobotoTypeface;
35 |
36 |
37 | /**
38 | * Created by ksarmalkar on 2/24/14.
39 | */
40 | public class RobotoTextView extends TextView {
41 |
42 | private Utils.FontType mFontType;
43 | private Context mContext;
44 |
45 | public RobotoTextView(Context context, AttributeSet attrs) {
46 | super(context, attrs, 0);
47 | this.mContext = context;
48 |
49 | if (!isInEditMode()) {
50 | TypedArray attributesArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RobotoTextView, 0, 0);
51 | try {
52 | mFontType = values()[attributesArray.getInteger(R.styleable.RobotoTextView_fontType, 0)];
53 | setFontType(mFontType);
54 | // Note: This flag is required for proper typeface rendering
55 | setPaintFlags(getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
56 | } finally {
57 | attributesArray.recycle();
58 | }
59 | }
60 | }
61 |
62 | public void setFontType(Utils.FontType fontType) {
63 | this.mFontType = fontType;
64 | switch (mFontType) {
65 | case LIGHT:
66 | setTypeface(getRobotoTypeface(mContext, LIGHT));
67 | return;
68 | case MEDIUM:
69 | setTypeface(getRobotoTypeface(mContext, MEDIUM));
70 | return;
71 | case REGULAR:
72 | setTypeface(getRobotoTypeface(mContext, REGULAR));
73 | return;
74 | case BOLD:
75 | setTypeface(getRobotoTypeface(mContext, BOLD));
76 | return;
77 | default:
78 | setTypeface(getRobotoTypeface(mContext, REGULAR));
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/tripadvisor/seekbar/util/Utils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 TripAdvisor
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @author ksarmalkar 7/28/2014
17 | */
18 |
19 | package com.tripadvisor.seekbar.util;
20 |
21 | import android.content.Context;
22 | import android.graphics.Typeface;
23 |
24 | import com.tripadvisor.seekbar.CircularClockSeekBar;
25 |
26 | import org.joda.time.format.DateTimeFormat;
27 | import org.joda.time.format.DateTimeFormatter;
28 |
29 | import java.util.EnumMap;
30 | import java.util.Map;
31 |
32 | import static java.lang.Math.abs;
33 |
34 | /**
35 | * Created by ksarmalkar on 4/30/14.
36 | */
37 | public class Utils {
38 |
39 | public static final DateTimeFormatter SIMPLE_DATE_FORMAT_AM_PM = DateTimeFormat.forPattern("h");
40 | public static final DateTimeFormatter SIMPLE_DATE_FORMAT_MERIDIAN = DateTimeFormat.forPattern("a");
41 | public static final DateTimeFormatter SIMPLE_DATE_FORMAT_HOURS = DateTimeFormat.forPattern("H");
42 |
43 | public static boolean shouldMoveClockwise(int oldValue, int newValue) {
44 | int dist = abs(newValue - oldValue);
45 | int direction = oldValue < newValue ? 1 : -1;
46 | direction = dist < CircularClockSeekBar.TOTAL_DEGREES_INT / 2 ? direction : -direction;
47 | return direction == 1;
48 | }
49 |
50 | public static int getDistanceTo(int oldValue, int newValue) {
51 | int totalDegrees = CircularClockSeekBar.TOTAL_DEGREES_INT;
52 | boolean isClockWise = shouldMoveClockwise(oldValue, newValue);
53 | int dist = (newValue - oldValue) % totalDegrees;
54 |
55 | if (isClockWise) {
56 | if (abs(dist) > totalDegrees / 2) {
57 | dist = (totalDegrees - oldValue) + newValue;
58 | } else {
59 | dist = newValue - oldValue;
60 | }
61 | } else {
62 | if (abs(dist) > totalDegrees / 2) {
63 | dist = totalDegrees - (newValue - oldValue);
64 | dist = -dist;
65 | } else {
66 | dist = newValue - oldValue;
67 | }
68 | }
69 | return dist;
70 | }
71 |
72 | public static int getDelta(int oldDegrees, int newDegrees) {
73 | if ((oldDegrees == CircularClockSeekBar.TOTAL_DEGREES_INT && newDegrees == 0) || (newDegrees == CircularClockSeekBar.TOTAL_DEGREES_INT && oldDegrees == 0)
74 | || (oldDegrees == 0 && newDegrees == 0)) {
75 | // dont worry about delta for this condition as this basically means they are same.
76 | // we have this granular values when user touches/scrolls
77 | return 0;
78 | }
79 | return getDistanceTo(oldDegrees, newDegrees);
80 | }
81 |
82 | @SuppressWarnings("PublicInnerClass")
83 | public enum FontType {
84 | LIGHT,
85 | MEDIUM,
86 | REGULAR,
87 | BOLD
88 | }
89 |
90 | private static final Map sFontMap = new EnumMap(FontType.class);
91 |
92 | static {
93 | sFontMap.put(FontType.LIGHT, "Roboto-Light.ttf");
94 | sFontMap.put(FontType.MEDIUM, "Roboto-Medium.ttf");
95 | sFontMap.put(FontType.REGULAR, "Roboto-Regular.ttf");
96 | sFontMap.put(FontType.BOLD, "Roboto-Bold.ttf");
97 | }
98 |
99 | /**
100 | * Cache for loaded Roboto typefaces.
101 | */
102 | private static final Map sTypefaceCache = new EnumMap(FontType.class);
103 |
104 | /**
105 | * Creates and returns Roboto typeface and caches it.
106 | *
107 | * @param context {@link android.content.Context} Context that will obtain resources.
108 | * @param fontType {@link com.tripadvisor.seekbar.util.Utils.FontType} LIGHT, MEDIUM, REGULAR, or BOLD
109 | * @return Returns the {@link android.graphics.Typeface} corresponding to the {@link com.tripadvisor.seekbar.util.Utils.FontType}
110 | */
111 | public static Typeface getRobotoTypeface(Context context, FontType fontType) {
112 | String fontPath = sFontMap.get(fontType);
113 | if (!sTypefaceCache.containsKey(fontType)) {
114 | sTypefaceCache.put(fontType, Typeface.createFromAsset(context.getAssets(), fontPath));
115 | }
116 | return sTypefaceCache.get(fontType);
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/tripadvisor/seekbar/util/annotations/VisibleForTesting.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 TripAdvisor
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @author ksarmalkar 7/28/2014
17 | */
18 |
19 | package com.tripadvisor.seekbar.util.annotations;
20 |
21 | /**
22 | * Created by ksarmalkar on 7/28/14.
23 | */
24 | public @interface VisibleForTesting {
25 | }
26 |
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/clock_dial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/clock_dial.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/scrubber_control_normal_holo_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/scrubber_control_normal_holo_green.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/scrubber_control_normal_holo_orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/scrubber_control_normal_holo_orange.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/scrubber_control_normal_holo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/scrubber_control_normal_holo_red.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/scrubber_control_pressed_holo_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/scrubber_control_pressed_holo_green.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/scrubber_control_pressed_holo_orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/scrubber_control_pressed_holo_orange.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/scrubber_control_pressed_holo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/scrubber_control_pressed_holo_red.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/time_longhand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/time_longhand.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-hdpi/time_shorthand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-hdpi/time_shorthand.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/clock_dial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/clock_dial.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/scrubber_control_normal_holo_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/scrubber_control_normal_holo_green.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/scrubber_control_normal_holo_orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/scrubber_control_normal_holo_orange.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/scrubber_control_normal_holo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/scrubber_control_normal_holo_red.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/scrubber_control_pressed_holo_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/scrubber_control_pressed_holo_green.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/scrubber_control_pressed_holo_orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/scrubber_control_pressed_holo_orange.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/scrubber_control_pressed_holo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/scrubber_control_pressed_holo_red.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/time_longhand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/time_longhand.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-mdpi/time_shorthand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-mdpi/time_shorthand.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/clock_dial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/clock_dial.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/scrubber_control_normal_holo_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/scrubber_control_normal_holo_green.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/scrubber_control_normal_holo_orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/scrubber_control_normal_holo_orange.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/scrubber_control_normal_holo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/scrubber_control_normal_holo_red.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/scrubber_control_pressed_holo_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/scrubber_control_pressed_holo_green.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/scrubber_control_pressed_holo_orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/scrubber_control_pressed_holo_orange.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/scrubber_control_pressed_holo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/scrubber_control_pressed_holo_red.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/time_longhand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/time_longhand.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xhdpi/time_shorthand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xhdpi/time_shorthand.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xxhdpi/scrubber_control_normal_holo_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xxhdpi/scrubber_control_normal_holo_green.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xxhdpi/scrubber_control_normal_holo_orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xxhdpi/scrubber_control_normal_holo_orange.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xxhdpi/scrubber_control_normal_holo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xxhdpi/scrubber_control_normal_holo_red.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xxhdpi/scrubber_control_pressed_holo_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xxhdpi/scrubber_control_pressed_holo_green.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xxhdpi/scrubber_control_pressed_holo_orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xxhdpi/scrubber_control_pressed_holo_orange.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xxhdpi/scrubber_control_pressed_holo_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xxhdpi/scrubber_control_pressed_holo_red.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xxhdpi/time_longhand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xxhdpi/time_longhand.png
--------------------------------------------------------------------------------
/lib/src/main/res/drawable-xxhdpi/time_shorthand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/lib/src/main/res/drawable-xxhdpi/time_shorthand.png
--------------------------------------------------------------------------------
/lib/src/main/res/layout/clock_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
14 |
15 |
21 |
22 |
29 |
30 |
40 |
41 |
42 |
59 |
60 |
61 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-da/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | t.
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Stdn.
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-el/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ώρες
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-en/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | hrs
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | horas
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-fi/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | h
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | heures
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-in/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | jam
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-it/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ore
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-ja/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 時間
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-ko/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 시간
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-nb/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | uur
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-nl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | uur
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-pl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | godz.
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-pt/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | h
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ч
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-sv/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | tim
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-th/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ชม.
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-tr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | saat
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-vi/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | h
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-zh-rHK/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 小時
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 小時
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF999999
4 | #FFE46715
5 | #FF9FDC00
6 | #FFE40000
7 |
8 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2dp
5 |
6 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | hrs
3 |
4 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | local.properties
3 | *.iml
4 | build/
5 | .DS_Store
6 | .bundle
7 | bin/
8 |
9 | # See https://intellij-support.jetbrains.com/entries/23393067
10 | .idea/workspace.xml
11 | .idea/tasks.xml
12 | .idea/libraries
13 | .idea/uiDesigner.xml
14 | .idea
15 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 |
4 |
5 | buildscript {
6 | repositories {
7 | mavenCentral()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:1.3.0'
11 | }
12 | }
13 |
14 | repositories {
15 | mavenCentral()
16 | }
17 |
18 | android {
19 | compileSdkVersion 21
20 | buildToolsVersion "21.1.2"
21 |
22 | defaultConfig {
23 | minSdkVersion 14
24 | targetSdkVersion 21
25 | versionCode 1
26 | versionName "1.0"
27 | testInstrumentationRunner "com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
28 | }
29 |
30 | buildTypes {
31 | release {
32 | minifyEnabled false
33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
34 | }
35 | }
36 | }
37 |
38 | dependencies {
39 | compile 'com.android.support:appcompat-v7:21.0.3'
40 | compile project(':lib')
41 | androidTestCompile files('libs/espresso-1.1-bundled.jar')
42 | androidTestCompile 'com.squareup:fest-android:1.0.7+'
43 | }
44 |
--------------------------------------------------------------------------------
/sample/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/sample/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/sample/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 06 10:47:22 EDT 2014
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-1.10-bin.zip
7 |
--------------------------------------------------------------------------------
/sample/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/sample/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/sample/libs/espresso-1.1-bundled.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/sample/libs/espresso-1.1-bundled.jar
--------------------------------------------------------------------------------
/sample/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/ksarmalkar/android-sdk-macosx/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the ProGuard
5 | # include property in project.properties.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/tripadvisor/seekbar/sample/MainActivityTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 TripAdvisor
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @author ksarmalkar 7/28/2014
17 | */
18 |
19 | package com.tripadvisor.seekbar.sample;
20 |
21 | import android.test.ActivityInstrumentationTestCase2;
22 |
23 | import org.joda.time.DateTime;
24 |
25 | import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
26 | import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.matches;
27 | import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isDisplayed;
28 | import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withContentDescription;
29 | import static com.tripadvisor.seekbar.sample.MainActivity.PlaceholderFragment;
30 |
31 | public class MainActivityTest extends ActivityInstrumentationTestCase2 {
32 |
33 | public MainActivityTest() {
34 | super(MainActivity.class);
35 | }
36 |
37 | public void testThatWhenUserTapsTheClocksItChangesTheValuesOnScreen() throws Throwable {
38 | getActivity();
39 | onView(withContentDescription("7am"))
40 | .check(matches(isDisplayed()));
41 | Thread.sleep(1000L);
42 | runTestOnUiThread(new Runnable() {
43 | @Override
44 | public void run() {
45 | PlaceholderFragment placeholderFragment = MainActivity.getPlaceholderFragment();
46 | placeholderFragment.changeClockTimeForTests(
47 | new DateTime(2014, 4, 25, 18, 0), false);
48 | }
49 | });
50 | onView(withContentDescription("6pm"))
51 | .check(matches(isDisplayed()));
52 | runTestOnUiThread(new Runnable() {
53 | @Override
54 | public void run() {
55 | PlaceholderFragment placeholderFragment = MainActivity.getPlaceholderFragment();
56 | placeholderFragment.changeClockTimeForTests(
57 | new DateTime(2014, 4, 25, 19, 0), true);
58 | }
59 | });
60 | onView(withContentDescription("7pm"))
61 | .check(matches(isDisplayed()));
62 | }
63 |
64 | public void testThatMinClockCannotBeSetBeyondMaxClock() throws Throwable {
65 | getActivity();
66 | onView(withContentDescription("7am"))
67 | .check(matches(isDisplayed()));
68 | Thread.sleep(1000L);
69 | runTestOnUiThread(new Runnable() {
70 | @Override
71 | public void run() {
72 | PlaceholderFragment placeholderFragment = MainActivity.getPlaceholderFragment();
73 | placeholderFragment.changeClockTimeForTests(
74 | new DateTime(2014, 4, 25, 18, 0), false);
75 | }
76 | });
77 | onView(withContentDescription("6pm"))
78 | .check(matches(isDisplayed()));
79 | runTestOnUiThread(new Runnable() {
80 | @Override
81 | public void run() {
82 | PlaceholderFragment placeholderFragment = MainActivity.getPlaceholderFragment();
83 | placeholderFragment.changeClockTimeForTests(
84 | new DateTime(2014, 4, 25, 19, 0), true);
85 | }
86 | });
87 | onView(withContentDescription("7pm"))
88 | .check(matches(isDisplayed()));
89 | runTestOnUiThread(new Runnable() {
90 | @Override
91 | public void run() {
92 | PlaceholderFragment placeholderFragment = MainActivity.getPlaceholderFragment();
93 | placeholderFragment.changeClockTimeForTests(
94 | new DateTime(2014, 4, 25, 20, 0), false);
95 | }
96 | });
97 | onView(withContentDescription("6pm"))
98 | .check(matches(isDisplayed()));
99 | }
100 |
101 | public void testThatMaxClockCannotBeSetBeyondMinClock() throws Throwable {
102 | getActivity();
103 | onView(withContentDescription("7am"))
104 | .check(matches(isDisplayed()));
105 | Thread.sleep(1000L);
106 | runTestOnUiThread(new Runnable() {
107 | @Override
108 | public void run() {
109 | PlaceholderFragment placeholderFragment = MainActivity.getPlaceholderFragment();
110 | placeholderFragment.changeClockTimeForTests(
111 | new DateTime(2014, 4, 25, 18, 0), false);
112 | }
113 | });
114 | onView(withContentDescription("6pm"))
115 | .check(matches(isDisplayed()));
116 | runTestOnUiThread(new Runnable() {
117 | @Override
118 | public void run() {
119 | PlaceholderFragment placeholderFragment = MainActivity.getPlaceholderFragment();
120 | placeholderFragment.changeClockTimeForTests(
121 | new DateTime(2014, 4, 25, 19, 0), true);
122 | }
123 | });
124 | onView(withContentDescription("7pm"))
125 | .check(matches(isDisplayed()));
126 | runTestOnUiThread(new Runnable() {
127 | @Override
128 | public void run() {
129 | PlaceholderFragment placeholderFragment = MainActivity.getPlaceholderFragment();
130 | placeholderFragment.changeClockTimeForTests(
131 | new DateTime(2014, 4, 25, 17, 0), true);
132 | }
133 | });
134 | onView(withContentDescription("7pm"))
135 | .check(matches(isDisplayed()));
136 | }
137 | }
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/tripadvisor/seekbar/util/UtilsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 TripAdvisor
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @author ksarmalkar 7/28/2014
17 | */
18 |
19 | package com.tripadvisor.seekbar.util;
20 |
21 | import android.test.InstrumentationTestCase;
22 |
23 | import static com.tripadvisor.seekbar.util.Utils.getDelta;
24 | import static com.tripadvisor.seekbar.util.Utils.getDistanceTo;
25 | import static com.tripadvisor.seekbar.util.Utils.shouldMoveClockwise;
26 | import static org.fest.assertions.api.Assertions.assertThat;
27 |
28 | public class UtilsTest extends InstrumentationTestCase {
29 |
30 | public void testThatIsClockwiseReturnsCorrectValues() throws Exception {
31 | assertThat(shouldMoveClockwise(80, 170)).isTrue();
32 | assertThat(shouldMoveClockwise(170, 80)).isFalse();
33 | assertThat(shouldMoveClockwise(358, 3)).isTrue();
34 | assertThat(shouldMoveClockwise(3, 358)).isFalse();
35 | assertThat(shouldMoveClockwise(160, 190)).isTrue();
36 | assertThat(shouldMoveClockwise(190, 160)).isFalse();
37 | assertThat(shouldMoveClockwise(315, 45)).isTrue();
38 | assertThat(shouldMoveClockwise(45, 315)).isFalse();
39 | assertThat(shouldMoveClockwise(89, 268)).isTrue();
40 | assertThat(shouldMoveClockwise(268, 89)).isFalse();
41 | assertThat(shouldMoveClockwise(92, 271)).isTrue();
42 | assertThat(shouldMoveClockwise(271, 92)).isFalse();
43 | assertThat(shouldMoveClockwise(3, 181)).isTrue();
44 | assertThat(shouldMoveClockwise(181, 3)).isFalse();
45 | }
46 |
47 | public void testThatCircularDistanceReturnTheCorrectValues() throws Exception {
48 | assertThat(getDistanceTo(80, 170)).isEqualTo(90);
49 | assertThat(getDistanceTo(170, 80)).isEqualTo(-90);
50 | assertThat(getDistanceTo(358, 3)).isEqualTo(5);
51 | assertThat(getDistanceTo(3, 358)).isEqualTo(-5);
52 | assertThat(getDistanceTo(160, 190)).isEqualTo(30);
53 | assertThat(getDistanceTo(190, 160)).isEqualTo(-30);
54 | assertThat(getDistanceTo(315, 45)).isEqualTo(90);
55 | assertThat(getDistanceTo(45, 315)).isEqualTo(-90);
56 | assertThat(getDistanceTo(89, 268)).isEqualTo(179);
57 | assertThat(getDistanceTo(268, 89)).isEqualTo(-179);
58 | assertThat(getDistanceTo(92, 271)).isEqualTo(179);
59 | assertThat(getDistanceTo(271, 92)).isEqualTo(-179);
60 | assertThat(getDistanceTo(3, 181)).isEqualTo(178);
61 | assertThat(getDistanceTo(181, 3)).isEqualTo(-178);
62 | }
63 |
64 | public void testThatCalculateNewDeltaReturnCorrectValues() throws Exception {
65 | assertThat(getDelta(0, 49)).isEqualTo(49);
66 | assertThat(getDelta(40, 0)).isEqualTo(-40);
67 | assertThat(getDelta(0, 1)).isEqualTo(1);
68 | assertThat(getDelta(1, 0)).isEqualTo(-1);
69 | assertThat(getDelta(0, 99)).isEqualTo(99);
70 | assertThat(getDelta(80, 170)).isEqualTo(90);
71 | assertThat(getDelta(170, 80)).isEqualTo(-90);
72 | assertThat(getDelta(358, 3)).isEqualTo(5);
73 | assertThat(getDelta(3, 358)).isEqualTo(-5);
74 | assertThat(getDelta(160, 190)).isEqualTo(30);
75 | assertThat(getDelta(190, 160)).isEqualTo(-30);
76 | assertThat(getDelta(315, 45)).isEqualTo(90);
77 | assertThat(getDelta(45, 315)).isEqualTo(-90);
78 | assertThat(getDelta(89, 268)).isEqualTo(179);
79 | assertThat(getDelta(268, 89)).isEqualTo(-179);
80 | assertThat(getDelta(92, 271)).isEqualTo(179);
81 | assertThat(getDelta(271, 92)).isEqualTo(-179);
82 | assertThat(getDelta(3, 181)).isEqualTo(178);
83 | assertThat(getDelta(181, 3)).isEqualTo(-178);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/tripadvisor/seekbar/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 TripAdvisor
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @author ksarmalkar 7/28/2014
17 | */
18 |
19 | package com.tripadvisor.seekbar.sample;
20 |
21 | import android.app.Activity;
22 | import android.app.Fragment;
23 | import android.os.Bundle;
24 | import android.view.LayoutInflater;
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 |
28 | import com.tripadvisor.seekbar.ClockView;
29 | import com.tripadvisor.seekbar.util.annotations.VisibleForTesting;
30 |
31 | import org.joda.time.DateTime;
32 |
33 | import static com.tripadvisor.seekbar.ClockView.ClockTimeUpdateListener;
34 |
35 | public class MainActivity extends Activity {
36 |
37 | private static ClockView sMinDepartTimeClockView;
38 | private static ClockView sMaxDepartTimeClockView;
39 | private static PlaceholderFragment sPlaceholderFragment;
40 |
41 | @Override
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | setContentView(R.layout.activity_main);
45 | if (savedInstanceState == null) {
46 | sPlaceholderFragment = new PlaceholderFragment();
47 | getFragmentManager().beginTransaction()
48 | .add(R.id.container, sPlaceholderFragment)
49 | .commit();
50 | }
51 | }
52 |
53 | public static ClockView getMinDepartTimeClockView() {
54 | return sMinDepartTimeClockView;
55 | }
56 |
57 | public static ClockView getMaxDepartTimeClockView() {
58 | return sMaxDepartTimeClockView;
59 | }
60 |
61 | public static PlaceholderFragment getPlaceholderFragment() {
62 | return sPlaceholderFragment;
63 | }
64 |
65 | /**
66 | * A placeholder fragment containing a simple view.
67 | */
68 | public static class PlaceholderFragment extends Fragment implements ClockTimeUpdateListener {
69 |
70 | public PlaceholderFragment() {
71 | }
72 |
73 | @Override
74 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
75 | Bundle savedInstanceState) {
76 | View rootView = inflater.inflate(R.layout.fragment_main, container, false);
77 |
78 | final DateTime minTime = new DateTime(2014, 4, 25, 7, 0);
79 | final DateTime maxTime = new DateTime(2014, 4, 26, 5, 0);
80 |
81 | sMinDepartTimeClockView = (ClockView) rootView.findViewById(R.id.min_depart_time_clock_view);
82 | sMinDepartTimeClockView.setBounds(minTime, maxTime, false);
83 |
84 | sMaxDepartTimeClockView = (ClockView) rootView.findViewById(R.id.max_depart_time_clock_view);
85 | sMaxDepartTimeClockView.setBounds(minTime, maxTime, true);
86 |
87 | ClockView mMinArrivalTimeClockView = (ClockView) rootView.findViewById(R.id
88 | .min_arrive_time_clock_view);
89 | mMinArrivalTimeClockView.setBounds(minTime, maxTime, false);
90 | mMinArrivalTimeClockView.setNewCurrentTime(new DateTime(2014, 4, 25, 10, 0));
91 | ClockView mMaxArrivalTimeClockView = (ClockView) rootView.findViewById(R.id
92 | .max_arrive_time_clock_view);
93 | mMaxArrivalTimeClockView.setBounds(minTime, maxTime, true);
94 | mMaxArrivalTimeClockView.setNewCurrentTime(new DateTime(2014, 4, 25, 10, 0));
95 |
96 | final ClockView minRandomTime = (ClockView) rootView.findViewById(R.id.min_random_time_clock_view);
97 | minRandomTime.setBounds(minTime, maxTime, false);
98 | minRandomTime.setNewCurrentTime(new DateTime(2014, 4, 25, 10, 0));
99 |
100 | final ClockView maxRandomTime = (ClockView) rootView.findViewById(R.id.max_random_time_clock_view);
101 | maxRandomTime.setBounds(minTime, maxTime, false);
102 | maxRandomTime.setNewCurrentTime(new DateTime(2014, 4, 25, 10, 0));
103 |
104 | return rootView;
105 | }
106 |
107 | @VisibleForTesting
108 | public void changeClockTimeForTests(DateTime dateTime, boolean isMaxTime) {
109 | if (isMaxTime) {
110 | sMaxDepartTimeClockView.setClockTimeUpdateListener(this);
111 | sMaxDepartTimeClockView.setNewCurrentTime(dateTime);
112 | } else {
113 | sMinDepartTimeClockView.setClockTimeUpdateListener(this);
114 | sMinDepartTimeClockView.setNewCurrentTime(dateTime);
115 | }
116 | }
117 |
118 | @Override
119 | public void onClockTimeUpdate(ClockView clockView, DateTime currentTime) {
120 | if (clockView.equals(sMinDepartTimeClockView)) {
121 | if (currentTime.compareTo(sMaxDepartTimeClockView.getNewCurrentTime()) >= 0) {
122 | sMinDepartTimeClockView.setNewCurrentTime(sMinDepartTimeClockView.getNewCurrentTime().minusHours(1));
123 | }
124 | } else if (clockView.equals(sMaxDepartTimeClockView)) {
125 | if (currentTime.compareTo(sMinDepartTimeClockView.getNewCurrentTime()) <= 0) {
126 | sMaxDepartTimeClockView.setNewCurrentTime(sMaxDepartTimeClockView.getNewCurrentTime().plusHours(1));
127 | }
128 | }
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/sample/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/sample/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/sample/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tripadvisor/android-clockseekbar/cb9bbed87c612eb0ae3453ab03c61396ad4cc17f/sample/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
15 |
16 |
20 |
21 |
27 |
28 |
34 |
35 |
36 |
46 |
47 |
48 |
51 |
52 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
82 |
83 |
84 |
87 |
88 |
92 |
93 |
99 |
100 |
106 |
107 |
108 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_second.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF999999
4 |
5 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Example
5 | Hello world!
6 | Settings
7 |
8 |
9 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':lib', ':sample'
2 |
--------------------------------------------------------------------------------