├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── picker
│ │ └── ugurtekbas
│ │ └── com
│ │ └── DialTimePicker
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── picker
│ │ └── ugurtekbas
│ │ └── com
│ │ └── DialTimePicker
│ │ ├── ActivityAdapter.kt
│ │ ├── Main.kt
│ │ └── MainFragment.kt
│ └── res
│ ├── layout
│ ├── ampm_picker.xml
│ ├── main.xml
│ └── normal_picker.xml
│ ├── menu
│ └── menu_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── 1.png
└── 2.png
├── library
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── picker
│ │ └── ugurtekbas
│ │ └── com
│ │ └── Picker
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── picker
│ │ └── ugurtekbas
│ │ └── com
│ │ └── Picker
│ │ └── Picker.kt
│ └── res
│ └── values
│ └── attrs.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # IntelliJ
40 | *.iml
41 | .idea/workspace.xml
42 | .idea/tasks.xml
43 | .idea/gradle.xml
44 | .idea/assetWizardSettings.xml
45 | .idea/dictionaries
46 | .idea/libraries
47 | # Android Studio 3 in .gitignore file.
48 | .idea/caches
49 | .idea/modules.xml
50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
51 | .idea/navEditor.xml
52 |
53 | # Keystore files
54 | # Uncomment the following lines if you do not want to check your keystore files in.
55 | #*.jks
56 | #*.keystore
57 |
58 | # External native build folder generated in Android Studio 2.2 and later
59 | .externalNativeBuild
60 | .cxx/
61 |
62 | # Google Services (e.g. APIs or Firebase)
63 | # google-services.json
64 |
65 | # Freeline
66 | freeline.py
67 | freeline/
68 | freeline_project_description.json
69 |
70 | # fastlane
71 | fastlane/report.xml
72 | fastlane/Preview.html
73 | fastlane/screenshots
74 | fastlane/test_output
75 | fastlane/readme.md
76 |
77 | # Version control
78 | vcs.xml
79 |
80 | # lint
81 | lint/intermediates/
82 | lint/generated/
83 | lint/outputs/
84 | lint/tmp/
85 | # lint/reports/
86 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dialTimePicker
2 | A custom time picker library for Android.
3 |
4 | As a result of needing a fixed time picker for pre-lollipop devices, for my
5 | [alarm app](https://play.google.com/store/apps/details?id=com.ugurtekbas.alarmshuffle) i developed a dial time picker view.
6 | Calculations in the main class mostly based on [erz05's view.](https://github.com/erz05/TimePicker)
7 |
8 | [](https://android-arsenal.com/details/1/3244)
9 |
10 |
Images
11 |
12 |
13 |
14 |
15 | Usage
16 | Gradle Import:
17 |
18 | Add the specific repository to your build file:
19 | ```groovy
20 | repositories {
21 | maven {
22 | url "https://jitpack.io"
23 | }
24 | }
25 | ```
26 | Add the dependency in your build file (do not forget to specify the correct qualifier, usually 'aar'):
27 | ```groovy
28 | dependencies {
29 | compile 'com.github.ugurtekbas:dialTimePicker:8d263fc3a1'
30 | }
31 | ```
32 |
33 | ```xml
34 |
35 |
40 |
41 |
42 |
54 | ```
55 |
56 | ```java
57 | Picker picker = (Picker) findViewById(R.id.picker);
58 | //or
59 | Picker picker = new Picker(context);
60 |
61 | //Set background color
62 | picker.setCanvasColor(Color.WHITE);
63 | //Set dial color
64 | picker.setDialColor(Color.ORANGE);
65 | //Set clock color
66 | picker.setClockColor(Color.CYAN);
67 | //Set text color
68 | picker.setTextColor(Color.BLACK);
69 | //Enable 24 or 12 hour clock
70 | picker.setHourFormat(true);
71 | //Get current hour
72 | picker.getCurrentHour();
73 | //Get current minutes
74 | picker.getCurrentMin();
75 | //Diasble/enable the picker
76 | picker.setEnabled(false);
77 | //Set dial's size
78 | picker.setTrackSize(20);
79 | //Set adjuster's size
80 | picker.setDialRadiusDP(60);
81 | //Initialize picker's time
82 | picker.setTime(9,0);
83 | //Set picker's time with calendar object
84 | picker.setTime(Calendar.getInstance());
85 | //Set dialAdjust to adjust time by touching on clock's dial
86 | picker.setDialAdjust(true);
87 | //Set TimeChangedListener
88 | picker.setTimeChangedListener(this);
89 |
90 | //TimeChangeListener method
91 | public void timeChanged(Date date){
92 | }
93 |
94 | ```
95 | License
96 | Copyright 2018 Ugur Tekbas
97 |
98 | Licensed under the Apache License, Version 2.0 (the "License");
99 | you may not use this file except in compliance with the License.
100 | You may obtain a copy of the License at
101 |
102 | http://www.apache.org/licenses/LICENSE-2.0
103 |
104 | Unless required by applicable law or agreed to in writing, software
105 | distributed under the License is distributed on an "AS IS" BASIS,
106 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107 | See the License for the specific language governing permissions and
108 | limitations under the License.
109 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 30
6 | defaultConfig {
7 | applicationId 'ugurtekbas.com.dialtimepicker'
8 | minSdkVersion 21
9 | targetSdkVersion 30
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | productFlavors {
20 | }
21 | }
22 |
23 | dependencies {
24 | implementation fileTree(include: ['*.jar'], dir: 'libs')
25 | implementation "androidx.appcompat:appcompat:1.2.0"
26 | implementation project(':library')
27 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
28 | }
29 | repositories {
30 | mavenCentral()
31 | }
32 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Program Files (x86)\Android/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
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 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/picker/ugurtekbas/com/DialTimePicker/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package picker.ugurtekbas.com.DialTimePicker;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/picker/ugurtekbas/com/DialTimePicker/ActivityAdapter.kt:
--------------------------------------------------------------------------------
1 | package picker.ugurtekbas.com.DialTimePicker
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.fragment.app.FragmentStatePagerAdapter
6 |
7 | class ActivityAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
8 |
9 | var layoutIDs = intArrayOf(R.layout.normal_picker, R.layout.ampm_picker)
10 |
11 | override fun getItem(position: Int): Fragment {
12 | return MainFragment.newInstance(layoutIDs[position])
13 | }
14 |
15 | override fun getCount(): Int {
16 | return layoutIDs.size
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/picker/ugurtekbas/com/DialTimePicker/Main.kt:
--------------------------------------------------------------------------------
1 | package picker.ugurtekbas.com.DialTimePicker
2 |
3 | import android.os.Bundle
4 | import android.view.Menu
5 | import android.view.MenuItem
6 | import android.view.View
7 | import androidx.appcompat.app.AppCompatActivity
8 | import androidx.viewpager.widget.ViewPager
9 |
10 | class Main : AppCompatActivity() {
11 |
12 | private var viewPager: ViewPager? = null
13 |
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | setContentView(R.layout.main)
17 |
18 | title = resources.getString(R.string.appTitle)
19 | (findViewById(R.id.viewPager) as ViewPager).apply {
20 | adapter = ActivityAdapter(supportFragmentManager)
21 | }
22 | }
23 |
24 | override fun onCreateOptionsMenu(menu: Menu): Boolean {
25 | menuInflater.inflate(R.menu.menu_main, menu)
26 | return true
27 | }
28 |
29 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
30 | val id = item.itemId
31 | return super.onOptionsItemSelected(item)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/picker/ugurtekbas/com/DialTimePicker/MainFragment.kt:
--------------------------------------------------------------------------------
1 | package picker.ugurtekbas.com.DialTimePicker
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.Button
9 | import android.widget.CheckBox
10 | import android.widget.TextView
11 | import androidx.fragment.app.Fragment
12 | import picker.ugurtekbas.com.Picker.Picker
13 | import picker.ugurtekbas.com.Picker.TimeChangedListener
14 | import java.util.*
15 |
16 | class MainFragment : Fragment(), TimeChangedListener {
17 |
18 | companion object {
19 | const val idKey = "layoutID"
20 | fun newInstance(layoutID: Int): MainFragment {
21 | val fragment = MainFragment()
22 | fragment.layoutID = layoutID
23 | return fragment
24 | }
25 | }
26 |
27 | private var layoutID = 0
28 |
29 | override fun onSaveInstanceState(bundle: Bundle) {
30 | super.onSaveInstanceState(bundle)
31 | bundle.putInt(idKey, layoutID)
32 | }
33 |
34 | override fun onCreate(bundle: Bundle?) {
35 | super.onCreate(bundle)
36 | if (bundle?.getInt(idKey) != null) {
37 | layoutID = bundle.getInt(idKey)
38 | }
39 | }
40 |
41 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
42 | val v = inflater.inflate(layoutID, container, false)
43 | if (layoutID == R.layout.ampm_picker) {
44 | with(v.findViewById(R.id.amPicker) as Picker) {
45 | setClockColor(resources.getColor(R.color.clockColor))
46 | setDialColor(resources.getColor(R.color.dialColor))
47 | setTime(12, 45, Picker.AM)
48 | setTrackSize(20)
49 | setDialRadiusDP(60)
50 | val checkBox = v.findViewById(R.id.checkbox) as CheckBox
51 | this.isEnabled = checkBox.isChecked
52 | checkBox.setOnCheckedChangeListener { buttonView, isChecked -> this.isEnabled = isChecked }
53 | }
54 | } else {
55 | val picker = v.findViewById(R.id.picker) as Picker
56 | picker.isDialAdjust = false
57 | picker.timeListener = this
58 | val et = v.findViewById(R.id.et) as TextView
59 | val btn = v.findViewById(R.id.btn) as Button
60 | btn.setOnClickListener {
61 | var minute = picker.currentMin.toString()
62 | if (picker.currentMin < 10) {
63 | minute = "0$minute"
64 | }
65 | et.text = "It's " + picker.currentHour + ":" + minute
66 | }
67 | }
68 | return v
69 | }
70 |
71 | override fun timeChanged(date: Date?) {
72 | Log.i("Time changed: ", date.toString())
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/ampm_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/normal_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
26 |
33 |
34 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugurtekbas/dialTimePicker/64bc7860f05429c6e33728142daf5176ddba8035/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugurtekbas/dialTimePicker/64bc7860f05429c6e33728142daf5176ddba8035/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugurtekbas/dialTimePicker/64bc7860f05429c6e33728142daf5176ddba8035/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugurtekbas/dialTimePicker/64bc7860f05429c6e33728142daf5176ddba8035/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #FF9F5B
5 | #0F9280
6 |
7 | #6200EE
8 | #3700B3
9 | #03DAC5
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Picker
4 | Hello world!
5 | Settings
6 | Dial Time Picker
7 | Show time
8 | Picker enabled
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.4.21'
5 | repositories {
6 | jcenter()
7 | google()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:4.1.1'
11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | jcenter()
21 | google()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
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 | android.useAndroidX=true
20 | android.enableJetifier=true
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugurtekbas/dialTimePicker/64bc7860f05429c6e33728142daf5176ddba8035/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Dec 09 21:45:23 CET 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.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 |
--------------------------------------------------------------------------------
/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugurtekbas/dialTimePicker/64bc7860f05429c6e33728142daf5176ddba8035/images/1.png
--------------------------------------------------------------------------------
/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugurtekbas/dialTimePicker/64bc7860f05429c6e33728142daf5176ddba8035/images/2.png
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
3 | *.iml
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 30
6 |
7 | defaultConfig {
8 | minSdkVersion 21
9 | targetSdkVersion 30
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
24 | }
25 | repositories {
26 | mavenCentral()
27 | }
28 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Program Files (x86)\Android/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
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 | #}
18 |
--------------------------------------------------------------------------------
/library/src/androidTest/java/picker/ugurtekbas/com/Picker/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package picker.ugurtekbas.com.Picker;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/library/src/main/java/picker/ugurtekbas/com/Picker/Picker.kt:
--------------------------------------------------------------------------------
1 | package picker.ugurtekbas.com.Picker
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.graphics.Canvas
6 | import android.graphics.Color
7 | import android.graphics.Paint
8 | import android.graphics.PorterDuff
9 | import android.graphics.PorterDuffXfermode
10 | import android.graphics.RectF
11 | import android.graphics.Xfermode
12 | import android.text.format.DateFormat
13 | import android.util.AttributeSet
14 | import android.util.TypedValue
15 | import android.view.MotionEvent
16 | import android.view.View
17 | import picker.ugurtekbas.com.library.R
18 | import java.util.*
19 | import kotlin.math.atan2
20 | import kotlin.math.cos
21 | import kotlin.math.min
22 | import kotlin.math.pow
23 | import kotlin.math.sin
24 |
25 | interface TimeChangedListener {
26 | fun timeChanged(date: Date?)
27 | }
28 |
29 | class Picker @JvmOverloads constructor(
30 | context: Context?,
31 | attrs: AttributeSet? = null,
32 | defStyleAttr: Int = 0
33 | ) : View(context, attrs, defStyleAttr) {
34 |
35 | private val paint: Paint
36 | private val rectF: RectF
37 | private val dialXferMode: Xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
38 | private var min = 0f
39 | private var radius = 0f
40 | private var dialRadius = 0f
41 | private var offset = 0f
42 | private var slopX = 0f
43 | private var slopY = 0f
44 | private var dialX = 0f
45 | private var dialY = 0f
46 | private var hour = 0
47 | var currentMin = 0
48 | private set
49 | private var previousHour = 0
50 | private var textColor = Color.BLACK
51 | private var clockColor = Color.parseColor("#0f9280")
52 | private var dialColor = Color.parseColor("#FF9F5B")
53 | private var canvasColor = Color.TRANSPARENT
54 | private var trackSize = -1
55 | private var dialRadiusDP = -1
56 | private var angle: Double
57 | private var degrees = 0.0
58 | private var isMoving = false
59 | private var amPm: Boolean
60 | var hourFormat: Boolean
61 | private var firstRun = true
62 | private var manualAdjust = false
63 | /**
64 | * To enable adjusting time by touching on clock's dial
65 | * @param dialAdjust
66 | */
67 | var isDialAdjust = true
68 | private var hStr: String? = null
69 | var amPM: String? = null
70 | get() = amPmStr
71 | private set
72 | private var amPmStr: String? = null
73 | var timeListener: TimeChangedListener? = null
74 |
75 | companion object {
76 | private const val AN_HOUR_AS_MINUTES = 60
77 | private const val A_DAY_AS_HOURS = 24
78 | private const val HALF_DAY_AS_HOURS = 12
79 | const val AM = true
80 | const val PM = false
81 | }
82 |
83 | init {
84 | setLayerType(LAYER_TYPE_SOFTWARE, null)
85 | paint = Paint()
86 | paint.isAntiAlias = true
87 | paint.strokeCap = Paint.Cap.ROUND
88 | paint.textAlign = Paint.Align.CENTER
89 | rectF = RectF()
90 | angle = -Math.PI / 2 + .001
91 | hourFormat = DateFormat.is24HourFormat(getContext())
92 | amPm = Calendar.getInstance()[Calendar.AM_PM] == 0
93 | loadAppThemeDefaults()
94 | loadAttributes(attrs)
95 | }
96 |
97 | /**
98 | * Sets default theme attributes for picker
99 | * These will be used if picker's attributes aren't set
100 | */
101 | @SuppressLint("ResourceType")
102 | private fun loadAppThemeDefaults() {
103 | val typedValue = TypedValue()
104 | val typedArray = context.obtainStyledAttributes(
105 | typedValue.data, intArrayOf(
106 | android.R.attr.textColorPrimaryInverse,
107 | android.R.attr.textColorPrimary,
108 | android.R.attr.colorControlNormal)
109 | )
110 | setDialColor(typedArray.getColor(0, dialColor))
111 | setTextColor(typedArray.getColor(1, textColor))
112 | setClockColor(typedArray.getColor(2, clockColor))
113 | typedArray.recycle()
114 | }
115 |
116 | /**
117 | * Sets picker's attributes from xml file
118 | * @param attrs
119 | */
120 | private fun loadAttributes(attrs: AttributeSet?) {
121 | if (attrs != null) {
122 | val typedArray = context.obtainStyledAttributes(attrs, R.styleable.Picker)
123 | setTextColor(typedArray.getColor(R.styleable.Picker_textColor, textColor))
124 | setDialColor(typedArray.getColor(R.styleable.Picker_dialColor, dialColor))
125 | setClockColor(typedArray.getColor(R.styleable.Picker_clockColor, clockColor))
126 | setCanvasColor(typedArray.getColor(R.styleable.Picker_canvasColor, canvasColor))
127 | hourFormat = typedArray.getBoolean(R.styleable.Picker_hourFormat, hourFormat)
128 | setTrackSize(typedArray.getDimensionPixelSize(R.styleable.Picker_trackSize, trackSize))
129 | setDialRadiusDP(typedArray.getDimensionPixelSize(R.styleable.Picker_dialRadius, dialRadiusDP))
130 | typedArray.recycle()
131 | }
132 | }
133 |
134 | override fun setEnabled(enabled: Boolean) {
135 | super.setEnabled(enabled)
136 | invalidate()
137 | }
138 |
139 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
140 | val width = MeasureSpec.getSize(widthMeasureSpec).toFloat()
141 | val height = MeasureSpec.getSize(heightMeasureSpec).toFloat()
142 | min = min(width, height)
143 | setMeasuredDimension(min.toInt(), min.toInt())
144 | offset = min * 0.5f
145 | val padding = min / 20
146 | radius = min / 2 - padding * 2
147 | setDialRadiusDP(dialRadiusDP)
148 | dialRadius = dialRadiusDP.toFloat()
149 | rectF[-radius, -radius, radius] = radius
150 | }
151 |
152 | override fun onDraw(canvas: Canvas) {
153 | super.onDraw(canvas)
154 | canvas.translate(offset, offset)
155 | canvas.drawColor(canvasColor)
156 | if (firstRun) {
157 | val cal = Calendar.getInstance()
158 | currentMin = cal[Calendar.MINUTE]
159 | hour = cal[Calendar.HOUR_OF_DAY]
160 | initTime(hour, currentMin)
161 | } else {
162 | //Rad to Deg
163 | degrees = (Math.toDegrees(angle) + 90) % 360
164 | degrees = (degrees + 360) % 360
165 |
166 | //get AM/PM
167 | if (hourFormat) {
168 | hour = degrees.toInt() / 15 % A_DAY_AS_HOURS
169 | /**
170 | * When minutes are set programmatically, because of rounding issues,
171 | * new value of minutes might be different than the one is set.
172 | * To avoid that if statement checks if time setting is done programmatically or
173 | * by touch gestures.
174 | */
175 | if (manualAdjust) {
176 | currentMin = (degrees * 4).toInt() % AN_HOUR_AS_MINUTES
177 | manualAdjust = false
178 | }
179 | amPM = if (currentMin < 10) "0" + currentMin else currentMin.toString() + ""
180 | amPmStr = ""
181 | } else {
182 | if (manualAdjust) {
183 | //get Minutes
184 | currentMin = (degrees * 2).toInt() % AN_HOUR_AS_MINUTES
185 | manualAdjust = false
186 | }
187 | hour = degrees.toInt() / 30 % HALF_DAY_AS_HOURS
188 | if (hour == 0) hour = HALF_DAY_AS_HOURS
189 | amPM = if (currentMin < 10) "0" + currentMin else currentMin.toString() + ""
190 | //AM-PM
191 | if (hour == 12 && previousHour == 11 || hour == 11 && previousHour == 12) {
192 | amPm = !amPm
193 | }
194 | amPmStr = if (amPm) "AM" else "PM"
195 | }
196 | }
197 | previousHour = hour
198 | with(paint) {
199 | style = Paint.Style.FILL
200 | color = textColor
201 | alpha = if (isEnabled) paint.alpha else 77
202 | textSize = min / 5
203 | //the text which shows time
204 | hStr = if (hour < 10) "0$hour" else hour.toString() + ""
205 | canvas.drawText(hStr + ":" + amPM, 0f, paint.textSize / 4, paint)
206 | textSize = min / 10
207 | canvas.drawText(amPmStr!!, 0f, paint.textSize * 2, paint)
208 | }
209 |
210 | //clocks dial
211 | with(paint) {
212 | style = Paint.Style.STROKE
213 | setTrackSize(trackSize)
214 | strokeWidth = trackSize.toFloat()
215 | color = clockColor
216 | alpha = if (isEnabled) paint.alpha else 77
217 | canvas.drawOval(rectF, paint)
218 | }
219 |
220 | //small circle t adjust time
221 | with(paint) {
222 | calculatePointerPosition(angle)
223 | style = Paint.Style.FILL
224 | alpha = 0
225 | xfermode = dialXferMode
226 | canvas.drawCircle(dialX, dialY, dialRadius, paint)
227 | color = dialColor
228 | alpha = if (isEnabled) paint.alpha else 77
229 | xfermode = null
230 | canvas.drawCircle(dialX, dialY, dialRadius, paint)
231 | }
232 | }
233 |
234 | override fun onTouchEvent(event: MotionEvent): Boolean {
235 | if (!isEnabled) return false
236 | manualAdjust = true
237 | parent.requestDisallowInterceptTouchEvent(true)
238 | val posX = event.x - offset
239 | val posY = event.y - offset
240 | when (event.action) {
241 | MotionEvent.ACTION_DOWN -> {
242 | calculatePointerPosition(angle)
243 | if (posX >= dialX - dialRadius && posX <= dialX + dialRadius && posY >= dialY - dialRadius && posY <= dialY + dialRadius) {
244 | slopX = posX - dialX
245 | slopY = posY - dialY
246 | isMoving = true
247 | invalidate()
248 | } else if (isDialAdjust) {
249 | val xSqr = posX.toDouble().pow(2.0).toFloat()
250 | val ySqr = posY.toDouble().pow(2.0).toFloat()
251 | val distance = Math.sqrt(xSqr + ySqr.toDouble()).toFloat()
252 | //check if touched point is on dial
253 | if (distance <= radius + trackSize && distance >= radius - trackSize) {
254 | angle = atan2(posY.toDouble(), posX.toDouble())
255 | timeListener?.timeChanged(time)
256 | invalidate()
257 | }
258 | } else {
259 | parent.requestDisallowInterceptTouchEvent(false)
260 | return false
261 | }
262 | }
263 | MotionEvent.ACTION_MOVE -> if (isMoving) {
264 | angle = atan2(posY - slopY.toDouble(), posX - slopX.toDouble())
265 | timeListener?.timeChanged(time)
266 | invalidate()
267 | } else {
268 | parent.requestDisallowInterceptTouchEvent(false)
269 | return false
270 | }
271 | MotionEvent.ACTION_UP -> {
272 | isMoving = false
273 | invalidate()
274 | }
275 | MotionEvent.ACTION_CANCEL -> {
276 | }
277 | }
278 | return true
279 | }
280 |
281 | private fun calculatePointerPosition(angle: Double) {
282 | dialX = (radius * cos(angle)).toFloat()
283 | dialY = (radius * sin(angle)).toFloat()
284 | }
285 |
286 | //PM
287 | val currentHour: Int
288 | get() {
289 | var currentHour = hour
290 | return if (hourFormat) {
291 | currentHour
292 | } else {
293 | if (amPm) {
294 | if (currentHour == 12) {
295 | currentHour = 0
296 | }
297 | } else {
298 | //PM
299 | if (currentHour < 12) {
300 | currentHour += 12
301 | }
302 | }
303 | currentHour
304 | }
305 | }
306 |
307 | private fun setTextColor(textColor: Int) {
308 | this.textColor = textColor
309 | invalidate()
310 | }
311 |
312 | fun setClockColor(clockColor: Int) {
313 | this.clockColor = clockColor
314 | invalidate()
315 | }
316 |
317 | fun setDialColor(dialColor: Int) {
318 | this.dialColor = dialColor
319 | invalidate()
320 | }
321 |
322 | private fun setCanvasColor(canvasColor: Int) {
323 | this.canvasColor = canvasColor
324 | invalidate()
325 | }
326 |
327 | /**
328 | * To set dial's size
329 | * @param inTrackSize
330 | */
331 | fun setTrackSize(inTrackSize: Int) {
332 | //track's default size
333 | trackSize = if (inTrackSize <= 0) {
334 | (min / 25).toInt()
335 | } else if (dialRadiusDP > 0 && inTrackSize > 2 * dialRadiusDP) {
336 | (min / 25).toInt()
337 | } else {
338 | inTrackSize
339 | }
340 | }
341 |
342 | /**
343 | * To set adjuster's size
344 | * @param inDialRadiusDP
345 | */
346 | fun setDialRadiusDP(inDialRadiusDP: Int) {
347 | //adjuster's default size
348 | dialRadiusDP = if (inDialRadiusDP <= 0 || inDialRadiusDP > 100) {
349 | (radius / 7).toInt()
350 | } else {
351 | inDialRadiusDP
352 | }
353 | }
354 |
355 | private val time: Date
356 | get() {
357 | val calendar = Calendar.getInstance()
358 | var tmp = hour
359 | if (!amPm) {
360 | if (tmp < 12) tmp += 12
361 | } else {
362 | if (tmp == 12) tmp = 0
363 | }
364 | calendar[Calendar.HOUR_OF_DAY] = tmp
365 | calendar[Calendar.MINUTE] = currentMin
366 | return calendar.time
367 | }
368 |
369 | /**
370 | * This method is used to set picker's time
371 | * @param hour
372 | * @param minute
373 | */
374 | fun setTime(hour: Int, minute: Int) {
375 | check(isTimeValid(hour, minute, true)) { resources.getString(R.string.outOfRangeExceptionMessage) }
376 |
377 | // To handle AM/PM decision when time is set
378 | if (amPm && hour > 11) {
379 | amPm = !amPm
380 | } else if (!amPm && (hour < HALF_DAY_AS_HOURS || hour == A_DAY_AS_HOURS)) {
381 | amPm = !amPm
382 | }
383 | initTime(hour, minute)
384 | this.invalidate()
385 | }
386 |
387 | /**
388 | * This method is used to set picker's time with AM/PM value
389 | * @param hour
390 | * @param minute
391 | * @param midday
392 | */
393 | fun setTime(hour: Int, minute: Int, midday: Boolean) {
394 | check(isTimeValid(hour, minute, false)) { resources.getString(R.string.outOfRangeExceptionMessage2) }
395 | hourFormat = false
396 | amPm = midday
397 | initTime(hour, minute)
398 | this.invalidate()
399 | }
400 |
401 | /**
402 | * This method is used to set picker's time with calendar object
403 | * @param inCalendar
404 | */
405 | fun setTime(inCalendar: Calendar) {
406 | val midday = inCalendar[Calendar.AM_PM] == 0
407 | this.setTime(inCalendar[Calendar.HOUR], inCalendar[Calendar.MINUTE], midday)
408 | }
409 |
410 | /***
411 | * This method is used to initialize picker's time
412 | * @param hour
413 | * @param minutes
414 | */
415 | private fun initTime(hour: Int, minutes: Int) {
416 | var hour = hour
417 | this.hour = hour
418 | currentMin = minutes
419 | firstRun = true
420 | amPM = if (minutes < 10) "0$minutes" else minutes.toString() + ""
421 | if (hourFormat) {
422 | amPmStr = ""
423 | degrees = hour % A_DAY_AS_HOURS * 15 + (minutes % AN_HOUR_AS_MINUTES / 4).toDouble()
424 | } else {
425 | if (hour == 0) hour = HALF_DAY_AS_HOURS
426 | if (hour == 12 && previousHour == 11 || hour == 11 && previousHour == 12) {
427 | amPm = !amPm
428 | }
429 | amPmStr = if (amPm) "AM" else "PM"
430 | degrees = hour % HALF_DAY_AS_HOURS * 30 + (minutes % AN_HOUR_AS_MINUTES / 2).toDouble()
431 | }
432 | angle = Math.toRadians(degrees) - Math.PI / 2
433 | firstRun = false
434 | }
435 |
436 | /**
437 | * Checks if time values are between valid range
438 | * @param hour
439 | * @param minute
440 | * @param is24Hour if time is set as 24hour format
441 | * @return true if value is valid, false otherwise
442 | */
443 | private fun isTimeValid(hour: Int, minute: Int, is24Hour: Boolean): Boolean {
444 | return if (is24Hour) {
445 | (hour in 0..A_DAY_AS_HOURS && minute >= 0 && minute <= AN_HOUR_AS_MINUTES)
446 | } else {
447 | (hour in 0..HALF_DAY_AS_HOURS && minute >= 0 && minute <= AN_HOUR_AS_MINUTES)
448 | }
449 | }
450 | }
451 |
--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
14 |
15 |
19 |
20 |
24 |
25 |
29 |
30 |
34 |
35 |
39 |
40 |
41 |
42 | Time value is out of range. \n "hour" value must be between 0 and 24, "minute" value must be between 0 and 60
43 | Time value is out of range. \n "hour" value must be between 0 and 12, "minute" value must be between 0 and 60
44 |
45 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------