From 472162d2e1a6c2d31d7584e31626e90f2b0502c7 Mon Sep 17 00:00:00 2001 From: AndroidDeveloperLB Date: Mon, 7 May 2018 11:09:52 +0300 Subject: [PATCH 01/45] -updated sdks -added ability to scroll whole view (entire week, for example) like a ViewPager. -made default first day of week to be the one of the calendar. --- build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 4 +- library/build.gradle | 13 +- .../java/com/alamkanak/weekview/WeekView.java | 125 ++++++++++++++---- library/src/main/res/values/attrs.xml | 121 ++++++++--------- sample/build.gradle | 7 +- 6 files changed, 171 insertions(+), 103 deletions(-) diff --git a/build.gradle b/build.gradle index bd1e1fd48..f400b29bf 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,10 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.2.0-alpha13' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -19,5 +20,6 @@ allprojects { repositories { jcenter() + google() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 66b2b75d1..3dc5c89ac 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Aug 06 18:02:35 CEST 2017 +#Mon May 07 11:03:02 IDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip diff --git a/library/build.gradle b/library/build.gradle index a9291dd32..1df6cb954 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -5,12 +5,11 @@ repositories { } android { - compileSdkVersion 25 - buildToolsVersion '25.0.2' + compileSdkVersion 27 defaultConfig { - minSdkVersion 9 - targetSdkVersion 25 + minSdkVersion 14 + targetSdkVersion 27 } } @@ -18,8 +17,8 @@ configurations { javadocDeps } dependencies { - compile 'com.android.support:appcompat-v7:25.1.0' - javadocDeps 'com.android.support:appcompat-v7:25.1.0' + compile 'com.android.support:appcompat-v7:27.1.1' + javadocDeps 'com.android.support:appcompat-v7:27.1.1' } -apply from: 'gradle-mvn-push.gradle' \ No newline at end of file +apply from: 'gradle-mvn-push.gradle' diff --git a/library/src/main/java/com/alamkanak/weekview/WeekView.java b/library/src/main/java/com/alamkanak/weekview/WeekView.java index f6685e020..79f40eaee 100755 --- a/library/src/main/java/com/alamkanak/weekview/WeekView.java +++ b/library/src/main/java/com/alamkanak/weekview/WeekView.java @@ -110,6 +110,11 @@ private enum Direction { private int mScaledTouchSlop = 0; private EventRect mNewEventRect; private TextColorPicker textColorPicker; + private float mSizeOfWeekView; + private float mDistanceDone = 0; + private float mDistanceMin; + protected int mOffsetValueToSecureScreen = 9; + private float mStartOriginForScroll = 0; // Attributes and their default values. private int mHourHeight = 50; @@ -118,7 +123,7 @@ private enum Direction { private int mEffectiveMinHourHeight = mMinHourHeight; //compensates for the fact that you can't keep zooming out. private int mMaxHourHeight = 250; private int mColumnGap = 10; - private int mFirstDayOfWeek = Calendar.MONDAY; + private int mFirstDayOfWeek = Calendar.getInstance().getFirstDayOfWeek(); private int mTextSize = 12; private int mHeaderColumnPadding = 10; private int mHeaderColumnTextColor = Color.BLACK; @@ -174,6 +179,7 @@ private enum Direction { private boolean mAutoLimitTime = false; private boolean mEnableDropListener = false; private int mMinOverlappingMinutes = 0; + private boolean mIsScrollNumberOfVisibleDays = false; // Listeners. private EventClickListener mEventClickListener; @@ -190,6 +196,7 @@ private enum Direction { @Override public boolean onDown(MotionEvent e) { + mStartOriginForScroll = mCurrentOrigin.x; goToNearestOrigin(); return true; } @@ -238,6 +245,13 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d case RIGHT: float minX = getXMinLimit(); float maxX = getXMaxLimit(); + + if (e2.getX() < 0) { + mDistanceDone = e2.getX() - e1.getX(); + } else { + mDistanceDone = e1.getX() - e2.getX(); + } + if ((mCurrentOrigin.x - (distanceX * mXScrollingSpeed)) > maxX) { mCurrentOrigin.x = maxX; } else if ((mCurrentOrigin.x - (distanceX * mXScrollingSpeed)) < minX) { @@ -282,7 +296,9 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve switch (mCurrentFlingDirection) { case LEFT: case RIGHT: - mScroller.fling((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) (velocityX * mXScrollingSpeed), 0, (int) getXMinLimit(), (int) getXMaxLimit(), (int) getYMinLimit(), (int) getYMaxLimit()); + if (!mIsScrollNumberOfVisibleDays) { + mScroller.fling((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) (velocityX * mXScrollingSpeed), 0, (int) getXMinLimit(), (int) getXMaxLimit(), (int) getYMinLimit(), (int) getYMaxLimit()); + } break; case VERTICAL: mScroller.fling((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, 0, (int) velocityY, (int) getXMinLimit(), (int) getXMaxLimit(), (int) getYMinLimit(), (int) getYMaxLimit()); @@ -494,6 +510,7 @@ public WeekView(Context context, AttributeSet attrs, int defStyleAttr) { if (a.getBoolean(R.styleable.WeekView_dropListenerEnabled, false)) this.enableDropListener(); mMinOverlappingMinutes = a.getInt(R.styleable.WeekView_minOverlappingMinutes, 0); + mIsScrollNumberOfVisibleDays = a.getBoolean(R.styleable.WeekView_isScrollNumberOfVisibleDays, false); } finally { a.recycle(); } @@ -2517,6 +2534,15 @@ public int getMinOverlappingMinutes() { return this.mMinOverlappingMinutes; } + public boolean isScrollNumberOfVisibleDays() { + return this.mIsScrollNumberOfVisibleDays; + } + + public void setScrollNumberOfVisibleDays(boolean scrollNumberOfVisibleDays) { + this.mIsScrollNumberOfVisibleDays = scrollNumberOfVisibleDays; + invalidate(); + } + ///////////////////////////////////////////////////////////////// // // Functions related to scrolling. @@ -2525,6 +2551,10 @@ public int getMinOverlappingMinutes() { @Override public boolean onTouchEvent(MotionEvent event) { + + mSizeOfWeekView = (mWidthPerDay + mColumnGap) * getNumberOfVisibleDays(); + mDistanceMin = mSizeOfWeekView / mOffsetValueToSecureScreen; + mScaleDetector.onTouchEvent(event); boolean val = mGestureDetector.onTouchEvent(event); @@ -2542,41 +2572,78 @@ public boolean onTouchEvent(MotionEvent event) { private void goToNearestOrigin() { double leftDays = mCurrentOrigin.x / (mWidthPerDay + mColumnGap); - if (mCurrentFlingDirection != Direction.NONE) { - // snap to nearest day - leftDays = Math.round(leftDays); - } else if (mCurrentScrollDirection == Direction.LEFT) { - // snap to last day - leftDays = Math.floor(leftDays); - } else if (mCurrentScrollDirection == Direction.RIGHT) { - // snap to next day - leftDays = Math.ceil(leftDays); - } else { - // snap to nearest day - leftDays = Math.round(leftDays); - } + float beforeScroll = mStartOriginForScroll; + boolean isPassed = false; + + if (mDistanceDone > mDistanceMin || mDistanceDone < -mDistanceMin || !mIsScrollNumberOfVisibleDays) { + + if (!mIsScrollNumberOfVisibleDays && mCurrentFlingDirection != Direction.NONE) { + // snap to nearest day + leftDays = Math.round(leftDays); + } else if (mCurrentScrollDirection == Direction.LEFT) { + // snap to last day + leftDays = Math.floor(leftDays); + mStartOriginForScroll -= mSizeOfWeekView; + isPassed = true; + } else if (mCurrentScrollDirection == Direction.RIGHT) { + // snap to next day + leftDays = Math.floor(leftDays); + mStartOriginForScroll += mSizeOfWeekView; + isPassed = true; + } else { + // snap to nearest day + leftDays = Math.round(leftDays); + } - int nearestOrigin = (int) (mCurrentOrigin.x - leftDays * (mWidthPerDay + mColumnGap)); - boolean mayScrollHorizontal = mCurrentOrigin.x - nearestOrigin < getXMaxLimit() - && mCurrentOrigin.x - nearestOrigin > getXMinLimit(); - if (mayScrollHorizontal) { - mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, -nearestOrigin, 0); - ViewCompat.postInvalidateOnAnimation(WeekView.this); - } + if (mIsScrollNumberOfVisibleDays) { + boolean mayScrollHorizontal = beforeScroll - mStartOriginForScroll < getXMaxLimit() && mCurrentOrigin.x - mStartOriginForScroll > getXMinLimit(); + if (isPassed && mayScrollHorizontal) { + // Stop current animation. + mScroller.forceFinished(true); + // Snap to date. + if (mCurrentScrollDirection == Direction.LEFT) { + mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) ((beforeScroll - mCurrentOrigin.x) - mSizeOfWeekView), 0, 200); + } else if (mCurrentScrollDirection == Direction.RIGHT) { + mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) (mSizeOfWeekView - (mCurrentOrigin.x - beforeScroll)), 0, 200); + } + ViewCompat.postInvalidateOnAnimation(WeekView.this); + } + } else { + int nearestOrigin = (int) (mCurrentOrigin.x - leftDays * (mWidthPerDay + mColumnGap)); + boolean mayScrollHorizontal = mCurrentOrigin.x - nearestOrigin < getXMaxLimit() && mCurrentOrigin.x - nearestOrigin > getXMinLimit(); + if (mayScrollHorizontal) { + mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, -nearestOrigin, 0); + ViewCompat.postInvalidateOnAnimation(WeekView.this); + } - if (nearestOrigin != 0 && mayScrollHorizontal) { - // Stop current animation. + if (nearestOrigin != 0 && mayScrollHorizontal) { + // Stop current animation. + mScroller.forceFinished(true); + // Snap to date. + mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, -nearestOrigin, 0, (int) (Math.abs(nearestOrigin) / mWidthPerDay * mScrollDuration)); + ViewCompat.postInvalidateOnAnimation(WeekView.this); + } + } + + // Reset scrolling and fling direction. + mCurrentScrollDirection = mCurrentFlingDirection = Direction.NONE; + + + } else { mScroller.forceFinished(true); - // Snap to date. - mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, -nearestOrigin, 0, (int) (Math.abs(nearestOrigin) / mWidthPerDay * mScrollDuration)); + if (mCurrentScrollDirection == Direction.LEFT) { + mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) beforeScroll - (int) mCurrentOrigin.x, 0, 200); + } else if (mCurrentScrollDirection == Direction.RIGHT) { + mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) beforeScroll - (int) mCurrentOrigin.x, 0, 200); + } ViewCompat.postInvalidateOnAnimation(WeekView.this); + + // Reset scrolling and fling direction. + mCurrentScrollDirection = mCurrentFlingDirection = Direction.NONE; } - // Reset scrolling and fling direction. - mCurrentScrollDirection = mCurrentFlingDirection = Direction.NONE; } - @Override public void computeScroll() { super.computeScroll(); diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 516f90227..a12fdebae 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -1,69 +1,70 @@ - - - - + + + + - - + + - - - - - + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample/build.gradle b/sample/build.gradle index 23e0f47cc..c7734ab7b 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,13 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion '25.0.2' + compileSdkVersion 27 defaultConfig { applicationId "com.alamkanak.weekview" minSdkVersion 14 - targetSdkVersion 25 + targetSdkVersion 27 versionCode 1 versionName "1.0" } @@ -22,6 +21,6 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':library') - compile 'com.android.support:appcompat-v7:25.1.0' + compile 'com.android.support:appcompat-v7:27.1.1' compile 'com.squareup.retrofit:retrofit:1.9.0' } From 2b59062d24728f76d0979b84db1da36a8052ffe2 Mon Sep 17 00:00:00 2001 From: AndroidDeveloperLB Date: Mon, 7 May 2018 12:50:02 +0300 Subject: [PATCH 02/45] -converted project to Kotlin. Code can be shortened a lot, but requires some delicate work. -fixed some time/date formatting related issues: https://github.com/alamkanak/Android-Week-View/issues/497 https://github.com/alamkanak/Android-Week-View/issues/495 (but not fixed RTL alignment issue) -created a new activity to demonstrate the paging of entire view (example: week by week snapping), based on this pull request: https://github.com/Quivr/Android-Week-View/pull/88 --- build.gradle | 2 + library/build.gradle | 3 + library/gradle-mvn-push.gradle | 4 +- library/src/main/AndroidManifest.xml | 2 +- .../weekview/DateTimeInterpreter.java | 12 - .../alamkanak/weekview/DateTimeInterpreter.kt | 12 + .../com/alamkanak/weekview/MonthLoader.java | 44 - .../com/alamkanak/weekview/MonthLoader.kt | 30 + .../alamkanak/weekview/TextColorPicker.java | 10 - .../com/alamkanak/weekview/TextColorPicker.kt | 10 + .../java/com/alamkanak/weekview/WeekView.java | 2921 ----------------- .../java/com/alamkanak/weekview/WeekView.kt | 2513 ++++++++++++++ .../com/alamkanak/weekview/WeekViewEvent.java | 343 -- .../com/alamkanak/weekview/WeekViewEvent.kt | 206 ++ ...{WeekViewLoader.java => WeekViewLoader.kt} | 14 +- .../{WeekViewUtil.java => WeekViewUtil.kt} | 52 +- sample/build.gradle | 6 + sample/src/main/AndroidManifest.xml | 26 +- .../weekview/sample/AsynchronousActivity.java | 78 - .../weekview/sample/AsynchronousActivity.kt | 70 + .../weekview/sample/BaseActivity.java | 258 -- .../alamkanak/weekview/sample/BaseActivity.kt | 252 ++ .../weekview/sample/BasicActivity.java | 195 -- .../weekview/sample/BasicActivity.kt | 188 ++ .../weekview/sample/MainActivity.java | 36 - .../alamkanak/weekview/sample/MainActivity.kt | 32 + .../sample/WholeViewSnappingActivity.kt | 38 + .../weekview/sample/apiclient/Event.java | 119 - .../weekview/sample/apiclient/Event.kt | 77 + .../sample/apiclient/MyJsonService.java | 17 - .../sample/apiclient/MyJsonService.kt | 15 + sample/src/main/res/layout/activity_main.xml | 28 +- sample/src/main/res/values/strings.xml | 1 + 33 files changed, 3515 insertions(+), 4099 deletions(-) delete mode 100644 library/src/main/java/com/alamkanak/weekview/DateTimeInterpreter.java create mode 100644 library/src/main/java/com/alamkanak/weekview/DateTimeInterpreter.kt delete mode 100644 library/src/main/java/com/alamkanak/weekview/MonthLoader.java create mode 100644 library/src/main/java/com/alamkanak/weekview/MonthLoader.kt delete mode 100644 library/src/main/java/com/alamkanak/weekview/TextColorPicker.java create mode 100644 library/src/main/java/com/alamkanak/weekview/TextColorPicker.kt delete mode 100755 library/src/main/java/com/alamkanak/weekview/WeekView.java create mode 100644 library/src/main/java/com/alamkanak/weekview/WeekView.kt delete mode 100644 library/src/main/java/com/alamkanak/weekview/WeekViewEvent.java create mode 100644 library/src/main/java/com/alamkanak/weekview/WeekViewEvent.kt rename library/src/main/java/com/alamkanak/weekview/{WeekViewLoader.java => WeekViewLoader.kt} (69%) rename library/src/main/java/com/alamkanak/weekview/{WeekViewUtil.java => WeekViewUtil.kt} (56%) delete mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/AsynchronousActivity.java create mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/AsynchronousActivity.kt delete mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.java create mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.kt delete mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/BasicActivity.java create mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/BasicActivity.kt delete mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/MainActivity.java create mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/MainActivity.kt create mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/WholeViewSnappingActivity.kt delete mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/apiclient/Event.java create mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/apiclient/Event.kt delete mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/apiclient/MyJsonService.java create mode 100644 sample/src/main/java/com/alamkanak/weekview/sample/apiclient/MyJsonService.kt diff --git a/build.gradle b/build.gradle index f400b29bf..8d5cb2300 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,14 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.2.41' repositories { jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:3.2.0-alpha13' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/library/build.gradle b/library/build.gradle index 1df6cb954..8ccec35df 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,4 +1,6 @@ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' repositories { mavenCentral() @@ -19,6 +21,7 @@ configurations { dependencies { compile 'com.android.support:appcompat-v7:27.1.1' javadocDeps 'com.android.support:appcompat-v7:27.1.1' + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } apply from: 'gradle-mvn-push.gradle' diff --git a/library/gradle-mvn-push.gradle b/library/gradle-mvn-push.gradle index 167f0388d..f59176b26 100644 --- a/library/gradle-mvn-push.gradle +++ b/library/gradle-mvn-push.gradle @@ -18,7 +18,7 @@ apply plugin: 'maven' apply plugin: 'signing' def isReleaseBuild() { - return VERSION_NAME.contains("SNAPSHOT") == false + return !VERSION_NAME.contains("SNAPSHOT") } def getReleaseRepositoryUrl() { @@ -112,4 +112,4 @@ afterEvaluate { project -> archives androidSourcesJar // archives androidJavadocsJar } -} \ No newline at end of file +} diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml index e8432516e..4dab7b2f0 100644 --- a/library/src/main/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -1 +1 @@ - + diff --git a/library/src/main/java/com/alamkanak/weekview/DateTimeInterpreter.java b/library/src/main/java/com/alamkanak/weekview/DateTimeInterpreter.java deleted file mode 100644 index 942c8a653..000000000 --- a/library/src/main/java/com/alamkanak/weekview/DateTimeInterpreter.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.alamkanak.weekview; - -import java.util.Calendar; - -/** - * Created by Raquib on 1/6/2015. - */ -public interface DateTimeInterpreter { - public String interpretDate(Calendar date); - - public String interpretTime(int hour, int minutes); -} diff --git a/library/src/main/java/com/alamkanak/weekview/DateTimeInterpreter.kt b/library/src/main/java/com/alamkanak/weekview/DateTimeInterpreter.kt new file mode 100644 index 000000000..b49d170c3 --- /dev/null +++ b/library/src/main/java/com/alamkanak/weekview/DateTimeInterpreter.kt @@ -0,0 +1,12 @@ +package com.alamkanak.weekview + +import java.util.* + +/** + * Created by Raquib on 1/6/2015. + */ +interface DateTimeInterpreter { + fun interpretDate(date: Calendar): String + + fun interpretTime(hour: Int, minutes: Int): String +} diff --git a/library/src/main/java/com/alamkanak/weekview/MonthLoader.java b/library/src/main/java/com/alamkanak/weekview/MonthLoader.java deleted file mode 100644 index cec26bd13..000000000 --- a/library/src/main/java/com/alamkanak/weekview/MonthLoader.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.alamkanak.weekview; - -import java.util.Calendar; -import java.util.List; - -public class MonthLoader implements WeekViewLoader { - - private MonthChangeListener mOnMonthChangeListener; - - public MonthLoader(MonthChangeListener listener) { - this.mOnMonthChangeListener = listener; - } - - @Override - public double toWeekViewPeriodIndex(Calendar instance) { - return instance.get(Calendar.YEAR) * 12 + instance.get(Calendar.MONTH) + (instance.get(Calendar.DAY_OF_MONTH) - 1) / 30.0; - } - - @Override - public List onLoad(int periodIndex) { - return mOnMonthChangeListener.onMonthChange(periodIndex / 12, periodIndex % 12 + 1); - } - - public MonthChangeListener getOnMonthChangeListener() { - return mOnMonthChangeListener; - } - - public void setOnMonthChangeListener(MonthChangeListener onMonthChangeListener) { - this.mOnMonthChangeListener = onMonthChangeListener; - } - - public interface MonthChangeListener { - /** - *

Very important interface, it's the base to load events in the calendar. - * This method is called three times: once to load the previous month, once to load the next month and once to load the current month.

- * That's why you can have three times the same event at the same place if you mess up with the configuration - * - * @param newYear : year of the events required by the view. - * @param newMonth :

month of the events required by the view

1 based (not like JAVA API) : January = 1 and December = 12. - * @return a list of the events happening during the specified month. - */ - List onMonthChange(int newYear, int newMonth); - } -} diff --git a/library/src/main/java/com/alamkanak/weekview/MonthLoader.kt b/library/src/main/java/com/alamkanak/weekview/MonthLoader.kt new file mode 100644 index 000000000..e8739c476 --- /dev/null +++ b/library/src/main/java/com/alamkanak/weekview/MonthLoader.kt @@ -0,0 +1,30 @@ +package com.alamkanak.weekview + +import java.util.* + +class MonthLoader(var onMonthChangeListener: MonthChangeListener?) : WeekViewLoader { + + override fun toWeekViewPeriodIndex(instance: Calendar): Double { + return (instance.get(Calendar.YEAR) * 12).toDouble() + instance.get(Calendar.MONTH).toDouble() + (instance.get(Calendar.DAY_OF_MONTH) - 1) / 30.0 + } + + override fun onLoad(periodIndex: Int): List? { + return onMonthChangeListener!!.onMonthChange(periodIndex / 12, periodIndex % 12 + 1) + } + + interface MonthChangeListener { + /** + * + * Very important interface, it's the base to load events in the calendar. + * This method is called three times: once to load the previous month, once to load the next month and once to load the current month. + * **That's why you can have three times the same event at the same place if you mess up with the configuration** + * + * @param newYear : year of the events required by the view. + * @param newMonth : + * + *month of the events required by the view **1 based (not like JAVA API) : January = 1 and December = 12**. + * @return a list of the events happening **during the specified month**. + */ + fun onMonthChange(newYear: Int, newMonth: Int): List? + } +} diff --git a/library/src/main/java/com/alamkanak/weekview/TextColorPicker.java b/library/src/main/java/com/alamkanak/weekview/TextColorPicker.java deleted file mode 100644 index 9aa48feec..000000000 --- a/library/src/main/java/com/alamkanak/weekview/TextColorPicker.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alamkanak.weekview; - -import android.support.annotation.ColorInt; - -public interface TextColorPicker { - - @ColorInt - int getTextColor(WeekViewEvent event); - -} diff --git a/library/src/main/java/com/alamkanak/weekview/TextColorPicker.kt b/library/src/main/java/com/alamkanak/weekview/TextColorPicker.kt new file mode 100644 index 000000000..8fdd7b27c --- /dev/null +++ b/library/src/main/java/com/alamkanak/weekview/TextColorPicker.kt @@ -0,0 +1,10 @@ +package com.alamkanak.weekview + +import android.support.annotation.ColorInt + +interface TextColorPicker { + + @ColorInt + fun getTextColor(event: WeekViewEvent): Int + +} diff --git a/library/src/main/java/com/alamkanak/weekview/WeekView.java b/library/src/main/java/com/alamkanak/weekview/WeekView.java deleted file mode 100755 index 79f40eaee..000000000 --- a/library/src/main/java/com/alamkanak/weekview/WeekView.java +++ /dev/null @@ -1,2921 +0,0 @@ -package com.alamkanak.weekview; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Region; -import android.graphics.Typeface; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.support.annotation.Nullable; -import android.support.annotation.RequiresApi; -import android.support.v4.view.GestureDetectorCompat; -import android.support.v4.view.ViewCompat; -import android.support.v4.view.animation.FastOutLinearInInterpolator; -import android.text.Layout; -import android.text.SpannableStringBuilder; -import android.text.StaticLayout; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.style.StyleSpan; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.DragEvent; -import android.view.GestureDetector; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.SoundEffectConstants; -import android.view.View; -import android.view.ViewConfiguration; -import android.widget.OverScroller; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; - -import static com.alamkanak.weekview.WeekViewUtil.daysBetween; -import static com.alamkanak.weekview.WeekViewUtil.getPassedMinutesInDay; -import static com.alamkanak.weekview.WeekViewUtil.isSameDay; -import static com.alamkanak.weekview.WeekViewUtil.today; - -/** - * Created by Raquib-ul-Alam Kanak on 7/21/2014. - * Website: http://alamkanak.github.io/ - */ -public class WeekView extends View { - - private enum Direction { - NONE, LEFT, RIGHT, VERTICAL - } - - @Deprecated - public static final int LENGTH_SHORT = 1; - @Deprecated - public static final int LENGTH_LONG = 2; - private final Context mContext; - private Calendar mHomeDate; - private Calendar mMinDate; - private Calendar mMaxDate; - private Paint mTimeTextPaint; - private float mTimeTextWidth; - private float mTimeTextHeight; - private Paint mHeaderTextPaint; - private float mHeaderTextHeight; - private float mHeaderHeight; - private GestureDetectorCompat mGestureDetector; - private OverScroller mScroller; - private PointF mCurrentOrigin = new PointF(0f, 0f); - private Direction mCurrentScrollDirection = Direction.NONE; - private Paint mHeaderBackgroundPaint; - private float mWidthPerDay; - private Paint mDayBackgroundPaint; - private Paint mHourSeparatorPaint; - private float mHeaderMarginBottom; - private Paint mTodayBackgroundPaint; - private Paint mFutureBackgroundPaint; - private Paint mPastBackgroundPaint; - private Paint mFutureWeekendBackgroundPaint; - private Paint mPastWeekendBackgroundPaint; - private Paint mNowLinePaint; - private Paint mTodayHeaderTextPaint; - private Paint mEventBackgroundPaint; - private Paint mNewEventBackgroundPaint; - private float mHeaderColumnWidth; - private List mEventRects; - private List mEvents; - private TextPaint mEventTextPaint; - private TextPaint mNewEventTextPaint; - private Paint mHeaderColumnBackgroundPaint; - private int mFetchedPeriod = -1; // the middle period the calendar has fetched. - private boolean mRefreshEvents = false; - private Direction mCurrentFlingDirection = Direction.NONE; - private ScaleGestureDetector mScaleDetector; - private boolean mIsZooming; - private Calendar mFirstVisibleDay; - private Calendar mLastVisibleDay; - private int mMinimumFlingVelocity = 0; - private int mScaledTouchSlop = 0; - private EventRect mNewEventRect; - private TextColorPicker textColorPicker; - private float mSizeOfWeekView; - private float mDistanceDone = 0; - private float mDistanceMin; - protected int mOffsetValueToSecureScreen = 9; - private float mStartOriginForScroll = 0; - - // Attributes and their default values. - private int mHourHeight = 50; - private int mNewHourHeight = -1; - private int mMinHourHeight = 0; //no minimum specified (will be dynamic, based on screen) - private int mEffectiveMinHourHeight = mMinHourHeight; //compensates for the fact that you can't keep zooming out. - private int mMaxHourHeight = 250; - private int mColumnGap = 10; - private int mFirstDayOfWeek = Calendar.getInstance().getFirstDayOfWeek(); - private int mTextSize = 12; - private int mHeaderColumnPadding = 10; - private int mHeaderColumnTextColor = Color.BLACK; - private int mNumberOfVisibleDays = 3; - private int mHeaderRowPadding = 10; - private int mHeaderRowBackgroundColor = Color.WHITE; - private int mDayBackgroundColor = Color.rgb(245, 245, 245); - private int mPastBackgroundColor = Color.rgb(227, 227, 227); - private int mFutureBackgroundColor = Color.rgb(245, 245, 245); - private int mPastWeekendBackgroundColor = 0; - private int mFutureWeekendBackgroundColor = 0; - private int mNowLineColor = Color.rgb(102, 102, 102); - private int mNowLineThickness = 5; - private int mHourSeparatorColor = Color.rgb(230, 230, 230); - private int mTodayBackgroundColor = Color.rgb(239, 247, 254); - private int mHourSeparatorHeight = 2; - private int mTodayHeaderTextColor = Color.rgb(39, 137, 228); - private int mEventTextSize = 12; - private int mEventTextColor = Color.BLACK; - private int mEventPadding = 8; - private int mHeaderColumnBackgroundColor = Color.WHITE; - private int mDefaultEventColor; - private int mNewEventColor; - private String mNewEventIdentifier = "-100"; - private Drawable mNewEventIconDrawable; - private int mNewEventLengthInMinutes = 60; - private int mNewEventTimeResolutionInMinutes = 15; - private boolean mShowFirstDayOfWeekFirst = false; - - private boolean mIsFirstDraw = true; - private boolean mAreDimensionsInvalid = true; - @Deprecated - private int mDayNameLength = LENGTH_LONG; - private int mOverlappingEventGap = 0; - private int mEventMarginVertical = 0; - private float mXScrollingSpeed = 1f; - private Calendar mScrollToDay = null; - private double mScrollToHour = -1; - private int mEventCornerRadius = 0; - private boolean mShowDistinctWeekendColor = false; - private boolean mShowNowLine = false; - private boolean mShowDistinctPastFutureColor = false; - private boolean mHorizontalFlingEnabled = true; - private boolean mVerticalFlingEnabled = true; - private int mAllDayEventHeight = 100; - private float mZoomFocusPoint = 0; - private boolean mZoomFocusPointEnabled = true; - private int mScrollDuration = 250; - private int mTimeColumnResolution = 60; - private Typeface mTypeface = Typeface.DEFAULT_BOLD; - private int mMinTime = 0; - private int mMaxTime = 24; - private boolean mAutoLimitTime = false; - private boolean mEnableDropListener = false; - private int mMinOverlappingMinutes = 0; - private boolean mIsScrollNumberOfVisibleDays = false; - - // Listeners. - private EventClickListener mEventClickListener; - private EventLongPressListener mEventLongPressListener; - private WeekViewLoader mWeekViewLoader; - private EmptyViewClickListener mEmptyViewClickListener; - private EmptyViewLongPressListener mEmptyViewLongPressListener; - private DateTimeInterpreter mDateTimeInterpreter; - private ScrollListener mScrollListener; - private AddEventClickListener mAddEventClickListener; - private DropListener mDropListener; - - private final GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { - - @Override - public boolean onDown(MotionEvent e) { - mStartOriginForScroll = mCurrentOrigin.x; - goToNearestOrigin(); - return true; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - // Check if view is zoomed. - if (mIsZooming) - return true; - - switch (mCurrentScrollDirection) { - case NONE: { - // Allow scrolling only in one direction. - if (Math.abs(distanceX) > Math.abs(distanceY)) { - if (distanceX > 0) { - mCurrentScrollDirection = Direction.LEFT; - } else { - mCurrentScrollDirection = Direction.RIGHT; - } - } else { - mCurrentScrollDirection = Direction.VERTICAL; - } - break; - } - case LEFT: { - // Change direction if there was enough change. - if (Math.abs(distanceX) > Math.abs(distanceY) && (distanceX < -mScaledTouchSlop)) { - mCurrentScrollDirection = Direction.RIGHT; - } - break; - } - case RIGHT: { - // Change direction if there was enough change. - if (Math.abs(distanceX) > Math.abs(distanceY) && (distanceX > mScaledTouchSlop)) { - mCurrentScrollDirection = Direction.LEFT; - } - break; - } - default: - break; - } - - // Calculate the new origin after scroll. - switch (mCurrentScrollDirection) { - case LEFT: - case RIGHT: - float minX = getXMinLimit(); - float maxX = getXMaxLimit(); - - if (e2.getX() < 0) { - mDistanceDone = e2.getX() - e1.getX(); - } else { - mDistanceDone = e1.getX() - e2.getX(); - } - - if ((mCurrentOrigin.x - (distanceX * mXScrollingSpeed)) > maxX) { - mCurrentOrigin.x = maxX; - } else if ((mCurrentOrigin.x - (distanceX * mXScrollingSpeed)) < minX) { - mCurrentOrigin.x = minX; - } else { - mCurrentOrigin.x -= distanceX * mXScrollingSpeed; - } - ViewCompat.postInvalidateOnAnimation(WeekView.this); - break; - case VERTICAL: - float minY = getYMinLimit(); - float maxY = getYMaxLimit(); - if ((mCurrentOrigin.y - (distanceY)) > maxY) { - mCurrentOrigin.y = maxY; - } else if ((mCurrentOrigin.y - (distanceY)) < minY) { - mCurrentOrigin.y = minY; - } else { - mCurrentOrigin.y -= distanceY; - } - ViewCompat.postInvalidateOnAnimation(WeekView.this); - break; - default: - break; - } - return true; - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (mIsZooming) - return true; - - if ((mCurrentFlingDirection == Direction.LEFT && !mHorizontalFlingEnabled) || - (mCurrentFlingDirection == Direction.RIGHT && !mHorizontalFlingEnabled) || - (mCurrentFlingDirection == Direction.VERTICAL && !mVerticalFlingEnabled)) { - return true; - } - - mScroller.forceFinished(true); - - mCurrentFlingDirection = mCurrentScrollDirection; - switch (mCurrentFlingDirection) { - case LEFT: - case RIGHT: - if (!mIsScrollNumberOfVisibleDays) { - mScroller.fling((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) (velocityX * mXScrollingSpeed), 0, (int) getXMinLimit(), (int) getXMaxLimit(), (int) getYMinLimit(), (int) getYMaxLimit()); - } - break; - case VERTICAL: - mScroller.fling((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, 0, (int) velocityY, (int) getXMinLimit(), (int) getXMaxLimit(), (int) getYMinLimit(), (int) getYMaxLimit()); - break; - default: - break; - } - - ViewCompat.postInvalidateOnAnimation(WeekView.this); - return true; - } - - - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - - // If the tap was on an event then trigger the callback. - if (mEventRects != null && mEventClickListener != null) { - List reversedEventRects = mEventRects; - Collections.reverse(reversedEventRects); - for (EventRect eventRect : reversedEventRects) { - if (!mNewEventIdentifier.equals(eventRect.event.getIdentifier()) && eventRect.rectF != null && e.getX() > eventRect.rectF.left && e.getX() < eventRect.rectF.right && e.getY() > eventRect.rectF.top && e.getY() < eventRect.rectF.bottom) { - mEventClickListener.onEventClick(eventRect.originalEvent, eventRect.rectF); - playSoundEffect(SoundEffectConstants.CLICK); - return super.onSingleTapConfirmed(e); - } - } - } - - float xOffset = getXStartPixel(); - - float x = e.getX() - xOffset; - float y = e.getY() - mCurrentOrigin.y; - // If the tap was on add new Event space, then trigger the callback - if (mAddEventClickListener != null && mNewEventRect != null && mNewEventRect.rectF != null && - mNewEventRect.rectF.contains(x, y)) { - mAddEventClickListener.onAddEventClicked(mNewEventRect.event.getStartTime(), mNewEventRect.event.getEndTime()); - return super.onSingleTapConfirmed(e); - } - - // If the tap was on an empty space, then trigger the callback. - if ((mEmptyViewClickListener != null || mAddEventClickListener != null) && e.getX() > mHeaderColumnWidth && e.getY() > (mHeaderHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom)) { - Calendar selectedTime = getTimeFromPoint(e.getX(), e.getY()); - - if (selectedTime != null) { - List tempEvents = new ArrayList<>(mEvents); - if (mNewEventRect != null) { - tempEvents.remove(mNewEventRect.event); - mNewEventRect = null; - } - - playSoundEffect(SoundEffectConstants.CLICK); - - if (mEmptyViewClickListener != null) - mEmptyViewClickListener.onEmptyViewClicked((Calendar) selectedTime.clone()); - - if (mAddEventClickListener != null) { - //round selectedTime to resolution - selectedTime.add(Calendar.MINUTE, -(mNewEventLengthInMinutes / 2)); - //Fix selected time if before the minimum hour - if (selectedTime.get(Calendar.HOUR_OF_DAY) < mMinTime) { - selectedTime.set(Calendar.HOUR_OF_DAY, mMinTime); - selectedTime.set(Calendar.MINUTE, 0); - } - int unroundedMinutes = selectedTime.get(Calendar.MINUTE); - int mod = unroundedMinutes % mNewEventTimeResolutionInMinutes; - selectedTime.add(Calendar.MINUTE, mod < Math.ceil(mNewEventTimeResolutionInMinutes / 2) ? -mod : (mNewEventTimeResolutionInMinutes - mod)); - - Calendar endTime = (Calendar) selectedTime.clone(); - - //Minus one to ensure it is the same day and not midnight (next day) - int maxMinutes = (mMaxTime - selectedTime.get(Calendar.HOUR_OF_DAY)) * 60 - selectedTime.get(Calendar.MINUTE) - 1; - endTime.add(Calendar.MINUTE, Math.min(maxMinutes, mNewEventLengthInMinutes)); - //If clicked at end of the day, fix selected startTime - if (maxMinutes < mNewEventLengthInMinutes) { - selectedTime.add(Calendar.MINUTE, maxMinutes - mNewEventLengthInMinutes); - } - - WeekViewEvent newEvent = new WeekViewEvent(mNewEventIdentifier, "", null, selectedTime, endTime); - - float top = mHourHeight * getPassedMinutesInDay(selectedTime) / 60 + getEventsTop(); - float bottom = mHourHeight * getPassedMinutesInDay(endTime) / 60 + getEventsTop(); - - // Calculate left and right. - float left = mWidthPerDay * WeekViewUtil.daysBetween(getFirstVisibleDay(), selectedTime); - float right = left + mWidthPerDay; - - // Add the new event if its bounds are valid - if (left < right && - left < getWidth() && - top < getHeight() && - right > mHeaderColumnWidth && - bottom > 0 - ) { - RectF dayRectF = new RectF(left, top, right, bottom - mCurrentOrigin.y); - newEvent.setColor(mNewEventColor); - mNewEventRect = new EventRect(newEvent, newEvent, dayRectF); - tempEvents.add(newEvent); - WeekView.this.clearEvents(); - cacheAndSortEvents(tempEvents); - computePositionOfEvents(mEventRects); - invalidate(); - } - - } - } - - } - return super.onSingleTapConfirmed(e); - } - - @Override - public void onLongPress(MotionEvent e) { - super.onLongPress(e); - - if (mEventLongPressListener != null && mEventRects != null) { - List reversedEventRects = mEventRects; - Collections.reverse(reversedEventRects); - for (EventRect event : reversedEventRects) { - if (event.rectF != null && e.getX() > event.rectF.left && e.getX() < event.rectF.right && e.getY() > event.rectF.top && e.getY() < event.rectF.bottom) { - mEventLongPressListener.onEventLongPress(event.originalEvent, event.rectF); - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - return; - } - } - } - - // If the tap was on in an empty space, then trigger the callback. - if (mEmptyViewLongPressListener != null && e.getX() > mHeaderColumnWidth && e.getY() > (mHeaderHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom)) { - Calendar selectedTime = getTimeFromPoint(e.getX(), e.getY()); - if (selectedTime != null) { - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - mEmptyViewLongPressListener.onEmptyViewLongPress(selectedTime); - } - } - } - }; - - public WeekView(Context context) { - this(context, null); - } - - public WeekView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public WeekView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - // Hold references. - mContext = context; - - // Get the attribute values (if any). - TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.WeekView, 0, 0); - try { - mFirstDayOfWeek = a.getInteger(R.styleable.WeekView_firstDayOfWeek, mFirstDayOfWeek); - mHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourHeight, mHourHeight); - mMinHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_minHourHeight, mMinHourHeight); - mEffectiveMinHourHeight = mMinHourHeight; - mMaxHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_maxHourHeight, mMaxHourHeight); - mTextSize = a.getDimensionPixelSize(R.styleable.WeekView_textSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize, context.getResources().getDisplayMetrics())); - mHeaderColumnPadding = a.getDimensionPixelSize(R.styleable.WeekView_headerColumnPadding, mHeaderColumnPadding); - mColumnGap = a.getDimensionPixelSize(R.styleable.WeekView_columnGap, mColumnGap); - mHeaderColumnTextColor = a.getColor(R.styleable.WeekView_headerColumnTextColor, mHeaderColumnTextColor); - mNumberOfVisibleDays = a.getInteger(R.styleable.WeekView_noOfVisibleDays, mNumberOfVisibleDays); - mShowFirstDayOfWeekFirst = a.getBoolean(R.styleable.WeekView_showFirstDayOfWeekFirst, mShowFirstDayOfWeekFirst); - mHeaderRowPadding = a.getDimensionPixelSize(R.styleable.WeekView_headerRowPadding, mHeaderRowPadding); - mHeaderRowBackgroundColor = a.getColor(R.styleable.WeekView_headerRowBackgroundColor, mHeaderRowBackgroundColor); - mDayBackgroundColor = a.getColor(R.styleable.WeekView_dayBackgroundColor, mDayBackgroundColor); - mFutureBackgroundColor = a.getColor(R.styleable.WeekView_futureBackgroundColor, mFutureBackgroundColor); - mPastBackgroundColor = a.getColor(R.styleable.WeekView_pastBackgroundColor, mPastBackgroundColor); - mFutureWeekendBackgroundColor = a.getColor(R.styleable.WeekView_futureWeekendBackgroundColor, mFutureBackgroundColor); // If not set, use the same color as in the week - mPastWeekendBackgroundColor = a.getColor(R.styleable.WeekView_pastWeekendBackgroundColor, mPastBackgroundColor); - mNowLineColor = a.getColor(R.styleable.WeekView_nowLineColor, mNowLineColor); - mNowLineThickness = a.getDimensionPixelSize(R.styleable.WeekView_nowLineThickness, mNowLineThickness); - mHourSeparatorColor = a.getColor(R.styleable.WeekView_hourSeparatorColor, mHourSeparatorColor); - mTodayBackgroundColor = a.getColor(R.styleable.WeekView_todayBackgroundColor, mTodayBackgroundColor); - mHourSeparatorHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourSeparatorHeight, mHourSeparatorHeight); - mTodayHeaderTextColor = a.getColor(R.styleable.WeekView_todayHeaderTextColor, mTodayHeaderTextColor); - mEventTextSize = a.getDimensionPixelSize(R.styleable.WeekView_eventTextSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mEventTextSize, context.getResources().getDisplayMetrics())); - mEventTextColor = a.getColor(R.styleable.WeekView_eventTextColor, mEventTextColor); - mNewEventColor = a.getColor(R.styleable.WeekView_newEventColor, mNewEventColor); - mNewEventIconDrawable = a.getDrawable(R.styleable.WeekView_newEventIconResource); - // For backward compatibility : Set "mNewEventIdentifier" if the attribute is "WeekView_newEventId" of type int - setNewEventId(a.getInt(R.styleable.WeekView_newEventId, Integer.parseInt(mNewEventIdentifier))); - mNewEventIdentifier = (a.getString(R.styleable.WeekView_newEventIdentifier) != null) ? a.getString(R.styleable.WeekView_newEventIdentifier) : mNewEventIdentifier; - mNewEventLengthInMinutes = a.getInt(R.styleable.WeekView_newEventLengthInMinutes, mNewEventLengthInMinutes); - mNewEventTimeResolutionInMinutes = a.getInt(R.styleable.WeekView_newEventTimeResolutionInMinutes, mNewEventTimeResolutionInMinutes); - mEventPadding = a.getDimensionPixelSize(R.styleable.WeekView_eventPadding, mEventPadding); - mHeaderColumnBackgroundColor = a.getColor(R.styleable.WeekView_headerColumnBackground, mHeaderColumnBackgroundColor); - mDayNameLength = a.getInteger(R.styleable.WeekView_dayNameLength, mDayNameLength); - mOverlappingEventGap = a.getDimensionPixelSize(R.styleable.WeekView_overlappingEventGap, mOverlappingEventGap); - mEventMarginVertical = a.getDimensionPixelSize(R.styleable.WeekView_eventMarginVertical, mEventMarginVertical); - mXScrollingSpeed = a.getFloat(R.styleable.WeekView_xScrollingSpeed, mXScrollingSpeed); - mEventCornerRadius = a.getDimensionPixelSize(R.styleable.WeekView_eventCornerRadius, mEventCornerRadius); - mShowDistinctPastFutureColor = a.getBoolean(R.styleable.WeekView_showDistinctPastFutureColor, mShowDistinctPastFutureColor); - mShowDistinctWeekendColor = a.getBoolean(R.styleable.WeekView_showDistinctWeekendColor, mShowDistinctWeekendColor); - mShowNowLine = a.getBoolean(R.styleable.WeekView_showNowLine, mShowNowLine); - mHorizontalFlingEnabled = a.getBoolean(R.styleable.WeekView_horizontalFlingEnabled, mHorizontalFlingEnabled); - mVerticalFlingEnabled = a.getBoolean(R.styleable.WeekView_verticalFlingEnabled, mVerticalFlingEnabled); - mAllDayEventHeight = a.getDimensionPixelSize(R.styleable.WeekView_allDayEventHeight, mAllDayEventHeight); - mZoomFocusPoint = a.getFraction(R.styleable.WeekView_zoomFocusPoint, 1, 1, mZoomFocusPoint); - mZoomFocusPointEnabled = a.getBoolean(R.styleable.WeekView_zoomFocusPointEnabled, mZoomFocusPointEnabled); - mScrollDuration = a.getInt(R.styleable.WeekView_scrollDuration, mScrollDuration); - mTimeColumnResolution = a.getInt(R.styleable.WeekView_timeColumnResolution, mTimeColumnResolution); - mAutoLimitTime = a.getBoolean(R.styleable.WeekView_autoLimitTime, mAutoLimitTime); - mMinTime = a.getInt(R.styleable.WeekView_minTime, mMinTime); - mMaxTime = a.getInt(R.styleable.WeekView_maxTime, mMaxTime); - if (a.getBoolean(R.styleable.WeekView_dropListenerEnabled, false)) - this.enableDropListener(); - mMinOverlappingMinutes = a.getInt(R.styleable.WeekView_minOverlappingMinutes, 0); - mIsScrollNumberOfVisibleDays = a.getBoolean(R.styleable.WeekView_isScrollNumberOfVisibleDays, false); - } finally { - a.recycle(); - } - - init(); - } - - private void init() { - resetHomeDate(); - - // Scrolling initialization. - mGestureDetector = new GestureDetectorCompat(mContext, mGestureListener); - mScroller = new OverScroller(mContext, new FastOutLinearInInterpolator()); - - mMinimumFlingVelocity = ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity(); - mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); - - // Measure settings for time column. - mTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mTimeTextPaint.setTextAlign(Paint.Align.RIGHT); - mTimeTextPaint.setTextSize(mTextSize); - mTimeTextPaint.setColor(mHeaderColumnTextColor); - - Rect rect = new Rect(); - final String exampleTime = (mTimeColumnResolution % 60 != 0) ? "00:00 PM" : "00 PM"; - mTimeTextPaint.getTextBounds(exampleTime, 0, exampleTime.length(), rect); - mTimeTextWidth = mTimeTextPaint.measureText(exampleTime); - mTimeTextHeight = rect.height(); - mHeaderMarginBottom = mTimeTextHeight / 2; - initTextTimeWidth(); - - // Measure settings for header row. - mHeaderTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHeaderTextPaint.setColor(mHeaderColumnTextColor); - mHeaderTextPaint.setTextAlign(Paint.Align.CENTER); - mHeaderTextPaint.setTextSize(mTextSize); - mHeaderTextPaint.getTextBounds(exampleTime, 0, exampleTime.length(), rect); - mHeaderTextHeight = rect.height(); - mHeaderTextPaint.setTypeface(mTypeface); - - - // Prepare header background paint. - mHeaderBackgroundPaint = new Paint(); - mHeaderBackgroundPaint.setColor(mHeaderRowBackgroundColor); - - // Prepare day background color paint. - mDayBackgroundPaint = new Paint(); - mDayBackgroundPaint.setColor(mDayBackgroundColor); - mFutureBackgroundPaint = new Paint(); - mFutureBackgroundPaint.setColor(mFutureBackgroundColor); - mPastBackgroundPaint = new Paint(); - mPastBackgroundPaint.setColor(mPastBackgroundColor); - mFutureWeekendBackgroundPaint = new Paint(); - mFutureWeekendBackgroundPaint.setColor(mFutureWeekendBackgroundColor); - mPastWeekendBackgroundPaint = new Paint(); - mPastWeekendBackgroundPaint.setColor(mPastWeekendBackgroundColor); - - // Prepare hour separator color paint. - mHourSeparatorPaint = new Paint(); - mHourSeparatorPaint.setStyle(Paint.Style.STROKE); - mHourSeparatorPaint.setStrokeWidth(mHourSeparatorHeight); - mHourSeparatorPaint.setColor(mHourSeparatorColor); - - // Prepare the "now" line color paint - mNowLinePaint = new Paint(); - mNowLinePaint.setStrokeWidth(mNowLineThickness); - mNowLinePaint.setColor(mNowLineColor); - - // Prepare today background color paint. - mTodayBackgroundPaint = new Paint(); - mTodayBackgroundPaint.setColor(mTodayBackgroundColor); - - // Prepare today header text color paint. - mTodayHeaderTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mTodayHeaderTextPaint.setTextAlign(Paint.Align.CENTER); - mTodayHeaderTextPaint.setTextSize(mTextSize); - mTodayHeaderTextPaint.setTypeface(mTypeface); - - mTodayHeaderTextPaint.setColor(mTodayHeaderTextColor); - - // Prepare event background color. - mEventBackgroundPaint = new Paint(); - mEventBackgroundPaint.setColor(Color.rgb(174, 208, 238)); - // Prepare empty event background color. - mNewEventBackgroundPaint = new Paint(); - mNewEventBackgroundPaint.setColor(Color.rgb(60, 147, 217)); - - // Prepare header column background color. - mHeaderColumnBackgroundPaint = new Paint(); - mHeaderColumnBackgroundPaint.setColor(mHeaderColumnBackgroundColor); - - // Prepare event text size and color. - mEventTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG); - mEventTextPaint.setStyle(Paint.Style.FILL); - mEventTextPaint.setColor(mEventTextColor); - mEventTextPaint.setTextSize(mEventTextSize); - - // Set default event color. - mDefaultEventColor = Color.parseColor("#9fc6e7"); - // Set default empty event color. - mNewEventColor = Color.parseColor("#3c93d9"); - - mScaleDetector = new ScaleGestureDetector(mContext, new WeekViewGestureListener()); - } - - private void resetHomeDate() { - Calendar newHomeDate = today(); - - if (mMinDate != null && newHomeDate.before(mMinDate)) { - newHomeDate = (Calendar) mMinDate.clone(); - } - if (mMaxDate != null && newHomeDate.after(mMaxDate)) { - newHomeDate = (Calendar) mMaxDate.clone(); - } - - if (mMaxDate != null) { - Calendar date = (Calendar) mMaxDate.clone(); - date.add(Calendar.DATE, 1 - getRealNumberOfVisibleDays()); - while (date.before(mMinDate)) { - date.add(Calendar.DATE, 1); - } - - if (newHomeDate.after(date)) { - newHomeDate = date; - } - } - - mHomeDate = newHomeDate; - } - - private float getXOriginForDate(Calendar date) { - return -daysBetween(mHomeDate, date) * (mWidthPerDay + mColumnGap); - } - - private int getNumberOfPeriods() { - return (int) ((mMaxTime - mMinTime) * (60.0 / mTimeColumnResolution)); - } - - private float getYMinLimit() { - return -(mHourHeight * (mMaxTime - mMinTime) - + mHeaderHeight - + mHeaderRowPadding * 2 - + mHeaderMarginBottom - + mTimeTextHeight / 2 - - getHeight()); - } - - private float getYMaxLimit() { - return 0; - } - - private float getXMinLimit() { - if (mMaxDate == null) { - return Integer.MIN_VALUE; - } else { - Calendar date = (Calendar) mMaxDate.clone(); - date.add(Calendar.DATE, 1 - getRealNumberOfVisibleDays()); - while (date.before(mMinDate)) { - date.add(Calendar.DATE, 1); - } - - return getXOriginForDate(date); - } - } - - private float getXMaxLimit() { - if (mMinDate == null) { - return Integer.MAX_VALUE; - } else { - return getXOriginForDate(mMinDate); - } - } - - // fix rotation changes - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - mAreDimensionsInvalid = true; - } - - /** - * Initialize time column width. Calculate value with all possible hours (supposed widest text). - */ - private void initTextTimeWidth() { - mTimeTextWidth = 0; - for (int i = 0; i < getNumberOfPeriods(); i++) { - // Measure time string and get max width. - String time = getDateTimeInterpreter().interpretTime(i, (i % 2) * 30); - if (time == null) - throw new IllegalStateException("A DateTimeInterpreter must not return null time"); - mTimeTextWidth = Math.max(mTimeTextWidth, mTimeTextPaint.measureText(time)); - } - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - // Draw the header row. - drawHeaderRowAndEvents(canvas); - - // Draw the time column and all the axes/separators. - drawTimeColumnAndAxes(canvas); - } - - private void calculateHeaderHeight() { - //Make sure the header is the right size (depends on AllDay events) - boolean containsAllDayEvent = false; - if (mEventRects != null && mEventRects.size() > 0) { - for (int dayNumber = 0; - dayNumber < getRealNumberOfVisibleDays(); - dayNumber++) { - Calendar day = (Calendar) getFirstVisibleDay().clone(); - day.add(Calendar.DATE, dayNumber); - for (int i = 0; i < mEventRects.size(); i++) { - - if (isSameDay(mEventRects.get(i).event.getStartTime(), day) && mEventRects.get(i).event.isAllDay()) { - containsAllDayEvent = true; - break; - } - } - if (containsAllDayEvent) { - break; - } - } - } - if (containsAllDayEvent) { - mHeaderHeight = mHeaderTextHeight + (mAllDayEventHeight + mHeaderMarginBottom); - } else { - mHeaderHeight = mHeaderTextHeight; - } - } - - private void drawTimeColumnAndAxes(Canvas canvas) { - // Draw the background color for the header column. - canvas.drawRect(0, mHeaderHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), mHeaderColumnBackgroundPaint); - - // Clip to paint in left column only. - canvas.clipRect(0, mHeaderHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), Region.Op.REPLACE); - - for (int i = 0; i < getNumberOfPeriods(); i++) { - // If we are showing half hours (eg. 5:30am), space the times out by half the hour height - // and need to provide 30 minutes on each odd period, otherwise, minutes is always 0. - float timeSpacing; - int minutes; - int hour; - - float timesPerHour = (float) 60.0 / mTimeColumnResolution; - - timeSpacing = mHourHeight / timesPerHour; - hour = mMinTime + i / (int) (timesPerHour); - minutes = i % ((int) timesPerHour) * (60 / (int) timesPerHour); - - - // Calculate the top of the rectangle where the time text will go - float top = mHeaderHeight + mHeaderRowPadding * 2 + mCurrentOrigin.y + timeSpacing * i + mHeaderMarginBottom; - - // Get the time to be displayed, as a String. - String time = getDateTimeInterpreter().interpretTime(hour, minutes); - // Draw the text if its y position is not outside of the visible area. The pivot point of the text is the point at the bottom-right corner. - if (time == null) - throw new IllegalStateException("A DateTimeInterpreter must not return null time"); - if (top < getHeight()) - canvas.drawText(time, mTimeTextWidth + mHeaderColumnPadding, top + mTimeTextHeight, mTimeTextPaint); - } - } - - private void drawHeaderRowAndEvents(Canvas canvas) { - // Calculate the available width for each day. - mHeaderColumnWidth = mTimeTextWidth + mHeaderColumnPadding * 2; - mWidthPerDay = getWidth() - mHeaderColumnWidth - mColumnGap * (getRealNumberOfVisibleDays() - 1); - mWidthPerDay = mWidthPerDay / getRealNumberOfVisibleDays(); - - calculateHeaderHeight(); //Make sure the header is the right size (depends on AllDay events) - - Calendar today = today(); - - if (mAreDimensionsInvalid) { - mEffectiveMinHourHeight = Math.max(mMinHourHeight, (int) ((getHeight() - mHeaderHeight - mHeaderRowPadding * 2 - mHeaderMarginBottom) / (mMaxTime - mMinTime))); - - mAreDimensionsInvalid = false; - if (mScrollToDay != null) - goToDate(mScrollToDay); - - mAreDimensionsInvalid = false; - if (mScrollToHour >= 0) - goToHour(mScrollToHour); - - mScrollToDay = null; - mScrollToHour = -1; - mAreDimensionsInvalid = false; - } - if (mIsFirstDraw) { - mIsFirstDraw = false; - - // If the week view is being drawn for the first time, then consider the first day of the week. - if (getRealNumberOfVisibleDays() >= 7 && mHomeDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek && mShowFirstDayOfWeekFirst) { - int difference = (mHomeDate.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek); - mCurrentOrigin.x += (mWidthPerDay + mColumnGap) * difference; - } - setLimitTime(mMinTime, mMaxTime); - } - - // Calculate the new height due to the zooming. - if (mNewHourHeight > 0) { - if (mNewHourHeight < mEffectiveMinHourHeight) - mNewHourHeight = mEffectiveMinHourHeight; - else if (mNewHourHeight > mMaxHourHeight) - mNewHourHeight = mMaxHourHeight; - - mHourHeight = mNewHourHeight; - mNewHourHeight = -1; - } - - // If the new mCurrentOrigin.y is invalid, make it valid. - if (mCurrentOrigin.y < getHeight() - mHourHeight * (mMaxTime - mMinTime) - mHeaderHeight - mHeaderRowPadding * 2 - mHeaderMarginBottom - mTimeTextHeight / 2) - mCurrentOrigin.y = getHeight() - mHourHeight * (mMaxTime - mMinTime) - mHeaderHeight - mHeaderRowPadding * 2 - mHeaderMarginBottom - mTimeTextHeight / 2; - - // Don't put an "else if" because it will trigger a glitch when completely zoomed out and - // scrolling vertically. - if (mCurrentOrigin.y > 0) { - mCurrentOrigin.y = 0; - } - - int leftDaysWithGaps = getLeftDaysWithGaps(); - // Consider scroll offset. - float startFromPixel = getXStartPixel(); - float startPixel = startFromPixel; - - // Prepare to iterate for each day. - Calendar day = (Calendar) today.clone(); - day.add(Calendar.HOUR_OF_DAY, 6); - - // Prepare to iterate for each hour to draw the hour lines. - int lineCount = (int) ((getHeight() - mHeaderHeight - mHeaderRowPadding * 2 - - mHeaderMarginBottom) / mHourHeight) + 1; - - lineCount = (lineCount) * (getRealNumberOfVisibleDays() + 1); - - float[] hourLines = new float[lineCount * 4]; - - // Clear the cache for event rectangles. - if (mEventRects != null) { - for (EventRect eventRect : mEventRects) { - eventRect.rectF = null; - } - } - - // Clip to paint events only. - canvas.clipRect(mHeaderColumnWidth, mHeaderHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight / 2, getWidth(), getHeight(), Region.Op.REPLACE); - - // Iterate through each day. - Calendar oldFirstVisibleDay = mFirstVisibleDay; - mFirstVisibleDay = (Calendar) mHomeDate.clone(); - mFirstVisibleDay.add(Calendar.DATE, -(Math.round(mCurrentOrigin.x / (mWidthPerDay + mColumnGap)))); - if (!mFirstVisibleDay.equals(oldFirstVisibleDay) && mScrollListener != null) { - mScrollListener.onFirstVisibleDayChanged(mFirstVisibleDay, oldFirstVisibleDay); - } - - if (mAutoLimitTime) { - List days = new ArrayList<>(); - for (int dayNumber = leftDaysWithGaps + 1; - dayNumber <= leftDaysWithGaps + getRealNumberOfVisibleDays(); - dayNumber++) { - day = (Calendar) mHomeDate.clone(); - day.add(Calendar.DATE, dayNumber - 1); - days.add(day); - } - limitEventTime(days); - } - - for (int dayNumber = leftDaysWithGaps + 1; - dayNumber <= leftDaysWithGaps + getRealNumberOfVisibleDays() + 1; - dayNumber++) { - - // Check if the day is today. - day = (Calendar) mHomeDate.clone(); - mLastVisibleDay = (Calendar) day.clone(); - day.add(Calendar.DATE, dayNumber - 1); - mLastVisibleDay.add(Calendar.DATE, dayNumber - 2); - boolean isToday = isSameDay(day, today); - - // Don't draw days which are outside requested range - if (!dateIsValid(day)) { - continue; - } - - // Get more events if necessary. We want to store the events 3 months beforehand. Get - // events only when it is the first iteration of the loop. - if (mEventRects == null || mRefreshEvents || - (dayNumber == leftDaysWithGaps + 1 && mFetchedPeriod != (int) mWeekViewLoader.toWeekViewPeriodIndex(day) && - Math.abs(mFetchedPeriod - mWeekViewLoader.toWeekViewPeriodIndex(day)) > 0.5)) { - getMoreEvents(day); - mRefreshEvents = false; - } - - // Draw background color for each day. - float start = (startPixel < mHeaderColumnWidth ? mHeaderColumnWidth : startPixel); - if (mWidthPerDay + startPixel - start > 0) { - if (mShowDistinctPastFutureColor) { - boolean isWeekend = day.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || day.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY; - Paint pastPaint = isWeekend && mShowDistinctWeekendColor ? mPastWeekendBackgroundPaint : mPastBackgroundPaint; - Paint futurePaint = isWeekend && mShowDistinctWeekendColor ? mFutureWeekendBackgroundPaint : mFutureBackgroundPaint; - float startY = mHeaderHeight + mHeaderRowPadding * 2 + mTimeTextHeight / 2 + mHeaderMarginBottom + mCurrentOrigin.y; - - if (isToday) { - Calendar now = Calendar.getInstance(); - float beforeNow = (now.get(Calendar.HOUR_OF_DAY) - mMinTime + now.get(Calendar.MINUTE) / 60.0f) * mHourHeight; - canvas.drawRect(start, startY, startPixel + mWidthPerDay, startY + beforeNow, pastPaint); - canvas.drawRect(start, startY + beforeNow, startPixel + mWidthPerDay, getHeight(), futurePaint); - } else if (day.before(today)) { - canvas.drawRect(start, startY, startPixel + mWidthPerDay, getHeight(), pastPaint); - } else { - canvas.drawRect(start, startY, startPixel + mWidthPerDay, getHeight(), futurePaint); - } - } else { - canvas.drawRect(start, mHeaderHeight + mHeaderRowPadding * 2 + mTimeTextHeight / 2 + mHeaderMarginBottom, startPixel + mWidthPerDay, getHeight(), isToday ? mTodayBackgroundPaint : mDayBackgroundPaint); - } - } - - // Prepare the separator lines for hours. - int i = 0; - for (int hourNumber = mMinTime; hourNumber < mMaxTime; hourNumber++) { - float top = mHeaderHeight + mHeaderRowPadding * 2 + mCurrentOrigin.y + mHourHeight * (hourNumber - mMinTime) + mTimeTextHeight / 2 + mHeaderMarginBottom; - if (top > mHeaderHeight + mHeaderRowPadding * 2 + mTimeTextHeight / 2 + mHeaderMarginBottom - mHourSeparatorHeight && top < getHeight() && startPixel + mWidthPerDay - start > 0) { - hourLines[i * 4] = start; - hourLines[i * 4 + 1] = top; - hourLines[i * 4 + 2] = startPixel + mWidthPerDay; - hourLines[i * 4 + 3] = top; - i++; - } - } - - // Draw the lines for hours. - canvas.drawLines(hourLines, mHourSeparatorPaint); - - // Draw the events. - drawEvents(day, startPixel, canvas); - - // Draw the line at the current time. - if (mShowNowLine && isToday) { - float startY = mHeaderHeight + mHeaderRowPadding * 2 + mTimeTextHeight / 2 + mHeaderMarginBottom + mCurrentOrigin.y; - Calendar now = Calendar.getInstance(); - float beforeNow = (now.get(Calendar.HOUR_OF_DAY) - mMinTime + now.get(Calendar.MINUTE) / 60.0f) * mHourHeight; - float top = startY + beforeNow; - canvas.drawLine(start, top, startPixel + mWidthPerDay, top, mNowLinePaint); - } - - // In the next iteration, start from the next day. - startPixel += mWidthPerDay + mColumnGap; - } - - // Hide everything in the first cell (top left corner). - canvas.clipRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2, Region.Op.REPLACE); - canvas.drawRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint); - - // Clip to paint header row only. - canvas.clipRect(mHeaderColumnWidth, 0, getWidth(), mHeaderHeight + mHeaderRowPadding * 2, Region.Op.REPLACE); - - // Draw the header background. - canvas.drawRect(0, 0, getWidth(), mHeaderHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint); - - // Draw the header row texts. - startPixel = startFromPixel; - for (int dayNumber = leftDaysWithGaps + 1; dayNumber <= leftDaysWithGaps + getRealNumberOfVisibleDays() + 1; dayNumber++) { - // Check if the day is today. - day = (Calendar) mHomeDate.clone(); - day.add(Calendar.DATE, dayNumber - 1); - boolean isToday = isSameDay(day, today); - - // Don't draw days which are outside requested range - if (!dateIsValid(day)) - continue; - - // Draw the day labels. - String dayLabel = getDateTimeInterpreter().interpretDate(day); - if (dayLabel == null) - throw new IllegalStateException("A DateTimeInterpreter must not return null date"); - canvas.drawText(dayLabel, startPixel + mWidthPerDay / 2, mHeaderTextHeight + mHeaderRowPadding, isToday ? mTodayHeaderTextPaint : mHeaderTextPaint); - drawAllDayEvents(day, startPixel, canvas); - startPixel += mWidthPerDay + mColumnGap; - } - - } - - /** - * Get the time and date where the user clicked on. - * - * @param x The x position of the touch event. - * @param y The y position of the touch event. - * @return The time and date at the clicked position. - */ - private Calendar getTimeFromPoint(float x, float y) { - int leftDaysWithGaps = getLeftDaysWithGaps(); - float startPixel = getXStartPixel(); - for (int dayNumber = leftDaysWithGaps + 1; - dayNumber <= leftDaysWithGaps + getRealNumberOfVisibleDays() + 1; - dayNumber++) { - float start = (startPixel < mHeaderColumnWidth ? mHeaderColumnWidth : startPixel); - if (mWidthPerDay + startPixel - start > 0 && x > start && x < startPixel + mWidthPerDay) { - Calendar day = (Calendar) mHomeDate.clone(); - day.add(Calendar.DATE, dayNumber - 1); - float pixelsFromZero = y - mCurrentOrigin.y - mHeaderHeight - - mHeaderRowPadding * 2 - mTimeTextHeight / 2 - mHeaderMarginBottom; - int hour = (int) (pixelsFromZero / mHourHeight); - int minute = (int) (60 * (pixelsFromZero - hour * mHourHeight) / mHourHeight); - day.add(Calendar.HOUR_OF_DAY, hour + mMinTime); - day.set(Calendar.MINUTE, minute); - return day; - } - startPixel += mWidthPerDay + mColumnGap; - } - return null; - } - - /** - * limit current time of event by update mMinTime & mMaxTime - * find smallest of start time & latest of end time - */ - private void limitEventTime(List dates) { - if (mEventRects != null && mEventRects.size() > 0) { - Calendar startTime = null; - Calendar endTime = null; - - for (EventRect eventRect : mEventRects) { - for (Calendar date : dates) { - if (isSameDay(eventRect.event.getStartTime(), date) && !eventRect.event.isAllDay()) { - - if (startTime == null || getPassedMinutesInDay(startTime) > getPassedMinutesInDay(eventRect.event.getStartTime())) { - startTime = eventRect.event.getStartTime(); - } - - if (endTime == null || getPassedMinutesInDay(endTime) < getPassedMinutesInDay(eventRect.event.getEndTime())) { - endTime = eventRect.event.getEndTime(); - } - } - } - } - - if (startTime != null && endTime != null && startTime.before(endTime)) { - setLimitTime(Math.max(0, startTime.get(Calendar.HOUR_OF_DAY)), - Math.min(24, endTime.get(Calendar.HOUR_OF_DAY) + 1)); - return; - } - } - } - - private int getMinHourOffset() { - return mHourHeight * mMinTime; - } - - private float getEventsTop() { - // Calculate top. - return mCurrentOrigin.y + mHeaderHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight / 2 + mEventMarginVertical - getMinHourOffset(); - - } - - private int getLeftDaysWithGaps() { - return (int) -(Math.ceil(mCurrentOrigin.x / (mWidthPerDay + mColumnGap))); - } - - private float getXStartPixel() { - return mCurrentOrigin.x + (mWidthPerDay + mColumnGap) * getLeftDaysWithGaps() + - mHeaderColumnWidth; - } - - /** - * Draw all the events of a particular day. - * - * @param date The day. - * @param startFromPixel The left position of the day area. The events will never go any left from this value. - * @param canvas The canvas to draw upon. - */ - private void drawEvents(Calendar date, float startFromPixel, Canvas canvas) { - if (mEventRects != null && mEventRects.size() > 0) { - for (int i = 0; i < mEventRects.size(); i++) { - if (isSameDay(mEventRects.get(i).event.getStartTime(), date) && !mEventRects.get(i).event.isAllDay()) { - float top = mHourHeight * mEventRects.get(i).top / 60 + getEventsTop(); - float bottom = mHourHeight * mEventRects.get(i).bottom / 60 + getEventsTop(); - - // Calculate left and right. - float left = startFromPixel + mEventRects.get(i).left * mWidthPerDay; - if (left < startFromPixel) - left += mOverlappingEventGap; - float right = left + mEventRects.get(i).width * mWidthPerDay; - if (right < startFromPixel + mWidthPerDay) - right -= mOverlappingEventGap; - - // Draw the event and the event name on top of it. - if (left < right && - left < getWidth() && - top < getHeight() && - right > mHeaderColumnWidth && - bottom > mHeaderHeight + mHeaderRowPadding * 2 + mTimeTextHeight / 2 + mHeaderMarginBottom - ) { - mEventRects.get(i).rectF = new RectF(left, top, right, bottom); - mEventBackgroundPaint.setColor(mEventRects.get(i).event.getColor() == 0 ? mDefaultEventColor : mEventRects.get(i).event.getColor()); - mEventBackgroundPaint.setShader(mEventRects.get(i).event.getShader()); - canvas.drawRoundRect(mEventRects.get(i).rectF, mEventCornerRadius, mEventCornerRadius, mEventBackgroundPaint); - float topToUse = top; - if (mEventRects.get(i).event.getStartTime().get(Calendar.HOUR_OF_DAY) < mMinTime) - topToUse = mHourHeight * getPassedMinutesInDay(mMinTime, 0) / 60 + getEventsTop(); - - if (!mNewEventIdentifier.equals(mEventRects.get(i).event.getIdentifier())) - drawEventTitle(mEventRects.get(i).event, mEventRects.get(i).rectF, canvas, topToUse, left); - else - drawEmptyImage(mEventRects.get(i).event, mEventRects.get(i).rectF, canvas, topToUse, left); - - } else - mEventRects.get(i).rectF = null; - } - } - } - } - - /** - * Draw all the Allday-events of a particular day. - * - * @param date The day. - * @param startFromPixel The left position of the day area. The events will never go any left from this value. - * @param canvas The canvas to draw upon. - */ - private void drawAllDayEvents(Calendar date, float startFromPixel, Canvas canvas) { - if (mEventRects != null && mEventRects.size() > 0) { - for (int i = 0; i < mEventRects.size(); i++) { - if (isSameDay(mEventRects.get(i).event.getStartTime(), date) && mEventRects.get(i).event.isAllDay()) { - - // Calculate top. - float top = mHeaderRowPadding * 2 + mHeaderMarginBottom + +mTimeTextHeight / 2 + mEventMarginVertical; - - // Calculate bottom. - float bottom = top + mEventRects.get(i).bottom; - - // Calculate left and right. - float left = startFromPixel + mEventRects.get(i).left * mWidthPerDay; - if (left < startFromPixel) - left += mOverlappingEventGap; - float right = left + mEventRects.get(i).width * mWidthPerDay; - if (right < startFromPixel + mWidthPerDay) - right -= mOverlappingEventGap; - - // Draw the event and the event name on top of it. - if (left < right && - left < getWidth() && - top < getHeight() && - right > mHeaderColumnWidth && - bottom > 0 - ) { - mEventRects.get(i).rectF = new RectF(left, top, right, bottom); - mEventBackgroundPaint.setColor(mEventRects.get(i).event.getColor() == 0 ? mDefaultEventColor : mEventRects.get(i).event.getColor()); - mEventBackgroundPaint.setShader(mEventRects.get(i).event.getShader()); - canvas.drawRoundRect(mEventRects.get(i).rectF, mEventCornerRadius, mEventCornerRadius, mEventBackgroundPaint); - drawEventTitle(mEventRects.get(i).event, mEventRects.get(i).rectF, canvas, top, left); - } else - mEventRects.get(i).rectF = null; - } - } - } - } - - /** - * Draw the name of the event on top of the event rectangle. - * - * @param event The event of which the title (and location) should be drawn. - * @param rect The rectangle on which the text is to be drawn. - * @param canvas The canvas to draw upon. - * @param originalTop The original top position of the rectangle. The rectangle may have some of its portion outside of the visible area. - * @param originalLeft The original left position of the rectangle. The rectangle may have some of its portion outside of the visible area. - */ - private void drawEventTitle(WeekViewEvent event, RectF rect, Canvas canvas, float originalTop, float originalLeft) { - if (rect.right - rect.left - mEventPadding * 2 < 0) return; - if (rect.bottom - rect.top - mEventPadding * 2 < 0) return; - - // Prepare the name of the event. - SpannableStringBuilder bob = new SpannableStringBuilder(); - if (!TextUtils.isEmpty(event.getName())) { - bob.append(event.getName()); - bob.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0); - } - // Prepare the location of the event. - if (!TextUtils.isEmpty(event.getLocation())) { - if (bob.length() > 0) - bob.append(' '); - bob.append(event.getLocation()); - } - - int availableHeight = (int) (rect.bottom - originalTop - mEventPadding * 2); - int availableWidth = (int) (rect.right - originalLeft - mEventPadding * 2); - - // Get text color if necessary - if (textColorPicker != null) { - mEventTextPaint.setColor(textColorPicker.getTextColor(event)); - } - // Get text dimensions. - StaticLayout textLayout = new StaticLayout(bob, mEventTextPaint, availableWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - if (textLayout.getLineCount() > 0) { - int lineHeight = textLayout.getHeight() / textLayout.getLineCount(); - - if (availableHeight >= lineHeight) { - // Calculate available number of line counts. - int availableLineCount = availableHeight / lineHeight; - do { - // Ellipsize text to fit into event rect. - if (!mNewEventIdentifier.equals(event.getIdentifier())) - textLayout = new StaticLayout(TextUtils.ellipsize(bob, mEventTextPaint, availableLineCount * availableWidth, TextUtils.TruncateAt.END), mEventTextPaint, (int) (rect.right - originalLeft - mEventPadding * 2), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - - // Reduce line count. - availableLineCount--; - - // Repeat until text is short enough. - } while (textLayout.getHeight() > availableHeight); - - // Draw text. - canvas.save(); - canvas.translate(originalLeft + mEventPadding, originalTop + mEventPadding); - textLayout.draw(canvas); - canvas.restore(); - } - } - } - - /** - * Draw the text on top of the rectangle in the empty event. - */ - private void drawEmptyImage(WeekViewEvent event, RectF rect, Canvas canvas, float originalTop, float originalLeft) { - int size = Math.max(1, (int) Math.floor(Math.min(0.8 * rect.height(), 0.8 * rect.width()))); - if (mNewEventIconDrawable == null) - mNewEventIconDrawable = getResources().getDrawable(android.R.drawable.ic_input_add); - Bitmap icon = ((BitmapDrawable) mNewEventIconDrawable).getBitmap(); - icon = Bitmap.createScaledBitmap(icon, size, size, false); - canvas.drawBitmap(icon, originalLeft + (rect.width() - icon.getWidth()) / 2, originalTop + (rect.height() - icon.getHeight()) / 2, new Paint()); - - } - - /** - * A class to hold reference to the events and their visual representation. An EventRect is - * actually the rectangle that is drawn on the calendar for a given event. There may be more - * than one rectangle for a single event (an event that expands more than one day). In that - * case two instances of the EventRect will be used for a single event. The given event will be - * stored in "originalEvent". But the event that corresponds to rectangle the rectangle - * instance will be stored in "event". - */ - private class EventRect { - public WeekViewEvent event; - public WeekViewEvent originalEvent; - public RectF rectF; - public float left; - public float width; - public float top; - public float bottom; - - /** - * Create a new instance of event rect. An EventRect is actually the rectangle that is drawn - * on the calendar for a given event. There may be more than one rectangle for a single - * event (an event that expands more than one day). In that case two instances of the - * EventRect will be used for a single event. The given event will be stored in - * "originalEvent". But the event that corresponds to rectangle the rectangle instance will - * be stored in "event". - * - * @param event Represents the event which this instance of rectangle represents. - * @param originalEvent The original event that was passed by the user. - * @param rectF The rectangle. - */ - public EventRect(WeekViewEvent event, WeekViewEvent originalEvent, RectF rectF) { - this.event = event; - this.rectF = rectF; - this.originalEvent = originalEvent; - } - } - - - /** - * Gets more events of one/more month(s) if necessary. This method is called when the user is - * scrolling the week view. The week view stores the events of three months: the visible month, - * the previous month, the next month. - * - * @param day The day where the user is currently is. - */ - private void getMoreEvents(Calendar day) { - - // Get more events if the month is changed. - if (mEventRects == null) - mEventRects = new ArrayList<>(); - - if (mEvents == null) - mEvents = new ArrayList<>(); - - if (mWeekViewLoader == null && !isInEditMode()) - throw new IllegalStateException("You must provide a MonthChangeListener"); - - // If a refresh was requested then reset some variables. - if (mRefreshEvents) { - this.clearEvents(); - mFetchedPeriod = -1; - } - - if (mWeekViewLoader != null) { - int periodToFetch = (int) mWeekViewLoader.toWeekViewPeriodIndex(day); - if (!isInEditMode() && (mFetchedPeriod < 0 || mFetchedPeriod != periodToFetch || mRefreshEvents)) { - List newEvents = mWeekViewLoader.onLoad(periodToFetch); - - // Clear events. - this.clearEvents(); - cacheAndSortEvents(newEvents); - calculateHeaderHeight(); - - mFetchedPeriod = periodToFetch; - } - } - - // Prepare to calculate positions of each events. - List tempEvents = mEventRects; - mEventRects = new ArrayList<>(); - - // Iterate through each day with events to calculate the position of the events. - while (tempEvents.size() > 0) { - ArrayList eventRects = new ArrayList<>(tempEvents.size()); - - // Get first event for a day. - EventRect eventRect1 = tempEvents.remove(0); - eventRects.add(eventRect1); - - int i = 0; - while (i < tempEvents.size()) { - // Collect all other events for same day. - EventRect eventRect2 = tempEvents.get(i); - if (isSameDay(eventRect1.event.getStartTime(), eventRect2.event.getStartTime())) { - tempEvents.remove(i); - eventRects.add(eventRect2); - } else { - i++; - } - } - computePositionOfEvents(eventRects); - } - } - - private void clearEvents() { - mEventRects.clear(); - mEvents.clear(); - } - - /** - * Cache the event for smooth scrolling functionality. - * - * @param event The event to cache. - */ - private void cacheEvent(WeekViewEvent event) { - if (event.getStartTime().compareTo(event.getEndTime()) >= 0) - return; - List splitedEvents = event.splitWeekViewEvents(); - for (WeekViewEvent splitedEvent : splitedEvents) { - mEventRects.add(new EventRect(splitedEvent, event, null)); - } - - mEvents.add(event); - } - - /** - * Cache and sort events. - * - * @param events The events to be cached and sorted. - */ - private void cacheAndSortEvents(List events) { - for (WeekViewEvent event : events) { - cacheEvent(event); - } - sortEventRects(mEventRects); - } - - /** - * Sorts the events in ascending order. - * - * @param eventRects The events to be sorted. - */ - private void sortEventRects(List eventRects) { - Collections.sort(eventRects, new Comparator() { - @Override - public int compare(EventRect left, EventRect right) { - long start1 = left.event.getStartTime().getTimeInMillis(); - long start2 = right.event.getStartTime().getTimeInMillis(); - int comparator = start1 > start2 ? 1 : (start1 < start2 ? -1 : 0); - if (comparator == 0) { - long end1 = left.event.getEndTime().getTimeInMillis(); - long end2 = right.event.getEndTime().getTimeInMillis(); - comparator = end1 > end2 ? 1 : (end1 < end2 ? -1 : 0); - } - return comparator; - } - }); - } - - /** - * Calculates the left and right positions of each events. This comes handy specially if events - * are overlapping. - * - * @param eventRects The events along with their wrapper class. - */ - private void computePositionOfEvents(List eventRects) { - // Make "collision groups" for all events that collide with others. - List> collisionGroups = new ArrayList>(); - for (EventRect eventRect : eventRects) { - boolean isPlaced = false; - - outerLoop: - for (List collisionGroup : collisionGroups) { - for (EventRect groupEvent : collisionGroup) { - if (isEventsCollide(groupEvent.event, eventRect.event) && groupEvent.event.isAllDay() == eventRect.event.isAllDay()) { - collisionGroup.add(eventRect); - isPlaced = true; - break outerLoop; - } - } - } - - if (!isPlaced) { - List newGroup = new ArrayList(); - newGroup.add(eventRect); - collisionGroups.add(newGroup); - } - } - - for (List collisionGroup : collisionGroups) { - expandEventsToMaxWidth(collisionGroup); - } - } - - /** - * Expands all the events to maximum possible width. The events will try to occupy maximum - * space available horizontally. - * - * @param collisionGroup The group of events which overlap with each other. - */ - private void expandEventsToMaxWidth(List collisionGroup) { - // Expand the events to maximum possible width. - List> columns = new ArrayList>(); - columns.add(new ArrayList()); - for (EventRect eventRect : collisionGroup) { - boolean isPlaced = false; - for (List column : columns) { - if (column.size() == 0) { - column.add(eventRect); - isPlaced = true; - } else if (!isEventsCollide(eventRect.event, column.get(column.size() - 1).event)) { - column.add(eventRect); - isPlaced = true; - break; - } - } - if (!isPlaced) { - List newColumn = new ArrayList(); - newColumn.add(eventRect); - columns.add(newColumn); - } - } - - // Calculate left and right position for all the events. - // Get the maxRowCount by looking in all columns. - int maxRowCount = 0; - for (List column : columns) { - maxRowCount = Math.max(maxRowCount, column.size()); - } - for (int i = 0; i < maxRowCount; i++) { - // Set the left and right values of the event. - float j = 0; - for (List column : columns) { - if (column.size() >= i + 1) { - EventRect eventRect = column.get(i); - eventRect.width = 1f / columns.size(); - eventRect.left = j / columns.size(); - if (!eventRect.event.isAllDay()) { - eventRect.top = getPassedMinutesInDay(eventRect.event.getStartTime()); - eventRect.bottom = getPassedMinutesInDay(eventRect.event.getEndTime()); - } else { - eventRect.top = 0; - eventRect.bottom = mAllDayEventHeight; - } - mEventRects.add(eventRect); - } - j++; - } - } - } - - /** - * Checks if two events overlap. - * - * @param event1 The first event. - * @param event2 The second event. - * @return true if the events overlap. - */ - private boolean isEventsCollide(WeekViewEvent event1, WeekViewEvent event2) { - long start1 = event1.getStartTime().getTimeInMillis(); - long end1 = event1.getEndTime().getTimeInMillis(); - long start2 = event2.getStartTime().getTimeInMillis(); - long end2 = event2.getEndTime().getTimeInMillis(); - - long minOverlappingMillis = mMinOverlappingMinutes * 60 * 1000; - - return !((start1 + minOverlappingMillis >= end2) || (end1 <= start2 + minOverlappingMillis)); - } - - - /** - * Checks if time1 occurs after (or at the same time) time2. - * - * @param time1 The time to check. - * @param time2 The time to check against. - * @return true if time1 and time2 are equal or if time1 is after time2. Otherwise false. - */ - private boolean isTimeAfterOrEquals(Calendar time1, Calendar time2) { - return !(time1 == null || time2 == null) && time1.getTimeInMillis() >= time2.getTimeInMillis(); - } - - @Override - public void invalidate() { - super.invalidate(); - mAreDimensionsInvalid = true; - } - - ///////////////////////////////////////////////////////////////// - // - // Functions related to setting and getting the properties. - // - ///////////////////////////////////////////////////////////////// - - public void setOnEventClickListener(EventClickListener listener) { - this.mEventClickListener = listener; - } - - public void setDropListener(DropListener dropListener) { - this.mDropListener = dropListener; - } - - public EventClickListener getEventClickListener() { - return mEventClickListener; - } - - public - @Nullable - MonthLoader.MonthChangeListener getMonthChangeListener() { - if (mWeekViewLoader instanceof MonthLoader) - return ((MonthLoader) mWeekViewLoader).getOnMonthChangeListener(); - return null; - } - - public void setMonthChangeListener(MonthLoader.MonthChangeListener monthChangeListener) { - this.mWeekViewLoader = new MonthLoader(monthChangeListener); - } - - /** - * Get event loader in the week view. Event loaders define the interval after which the events - * are loaded in week view. For a MonthLoader events are loaded for every month. You can define - * your custom event loader by extending WeekViewLoader. - * - * @return The event loader. - */ - public WeekViewLoader getWeekViewLoader() { - return mWeekViewLoader; - } - - /** - * Set event loader in the week view. For example, a MonthLoader. Event loaders define the - * interval after which the events are loaded in week view. For a MonthLoader events are loaded - * for every month. You can define your custom event loader by extending WeekViewLoader. - * - * @param loader The event loader. - */ - public void setWeekViewLoader(WeekViewLoader loader) { - this.mWeekViewLoader = loader; - } - - public EventLongPressListener getEventLongPressListener() { - return mEventLongPressListener; - } - - public void setEventLongPressListener(EventLongPressListener eventLongPressListener) { - this.mEventLongPressListener = eventLongPressListener; - } - - public void setEmptyViewClickListener(EmptyViewClickListener emptyViewClickListener) { - this.mEmptyViewClickListener = emptyViewClickListener; - } - - public EmptyViewClickListener getEmptyViewClickListener() { - return mEmptyViewClickListener; - } - - public void setEmptyViewLongPressListener(EmptyViewLongPressListener emptyViewLongPressListener) { - this.mEmptyViewLongPressListener = emptyViewLongPressListener; - } - - public EmptyViewLongPressListener getEmptyViewLongPressListener() { - return mEmptyViewLongPressListener; - } - - public void setScrollListener(ScrollListener scrolledListener) { - this.mScrollListener = scrolledListener; - } - - public ScrollListener getScrollListener() { - return mScrollListener; - } - - public void setTimeColumnResolution(int resolution) { - mTimeColumnResolution = resolution; - } - - public int getTimeColumnResolution() { - return mTimeColumnResolution; - } - - public void setAddEventClickListener(AddEventClickListener addEventClickListener) { - this.mAddEventClickListener = addEventClickListener; - } - - public AddEventClickListener getAddEventClickListener() { - return mAddEventClickListener; - } - - /** - * Get the interpreter which provides the text to show in the header column and the header row. - * - * @return The date, time interpreter. - */ - public DateTimeInterpreter getDateTimeInterpreter() { - if (mDateTimeInterpreter == null) { - mDateTimeInterpreter = new DateTimeInterpreter() { - @Override - public String interpretDate(Calendar date) { - try { - SimpleDateFormat sdf = mDayNameLength == LENGTH_SHORT ? new SimpleDateFormat("EEEEE M/dd", Locale.getDefault()) : new SimpleDateFormat("EEE M/dd", Locale.getDefault()); - return sdf.format(date.getTime()).toUpperCase(); - } catch (Exception e) { - e.printStackTrace(); - return ""; - } - } - - @Override - public String interpretTime(int hour, int minutes) { - Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.HOUR_OF_DAY, hour); - calendar.set(Calendar.MINUTE, minutes); - - try { - SimpleDateFormat sdf; - if (DateFormat.is24HourFormat(getContext())) { - sdf = new SimpleDateFormat("HH:mm", Locale.getDefault()); - } else { - if ((mTimeColumnResolution % 60 != 0)) { - sdf = new SimpleDateFormat("hh:mm a", Locale.getDefault()); - } else { - sdf = new SimpleDateFormat("hh a", Locale.getDefault()); - } - } - return sdf.format(calendar.getTime()); - } catch (Exception e) { - e.printStackTrace(); - return ""; - } - } - }; - } - return mDateTimeInterpreter; - } - - /** - * Set the interpreter which provides the text to show in the header column and the header row. - * - * @param dateTimeInterpreter The date, time interpreter. - */ - public void setDateTimeInterpreter(DateTimeInterpreter dateTimeInterpreter) { - this.mDateTimeInterpreter = dateTimeInterpreter; - - // Refresh time column width. - initTextTimeWidth(); - } - - - /** - * Get the real number of visible days - * If the amount of days between max date and min date is smaller, that value is returned - * - * @return The real number of visible days - */ - public int getRealNumberOfVisibleDays() { - if (mMinDate == null || mMaxDate == null) - return getNumberOfVisibleDays(); - - return Math.min(mNumberOfVisibleDays, daysBetween(mMinDate, mMaxDate) + 1); - } - - /** - * Get the number of visible days - * - * @return The set number of visible days. - */ - public int getNumberOfVisibleDays() { - return mNumberOfVisibleDays; - } - - /** - * Set the number of visible days in a week. - * - * @param numberOfVisibleDays The number of visible days in a week. - */ - public void setNumberOfVisibleDays(int numberOfVisibleDays) { - this.mNumberOfVisibleDays = numberOfVisibleDays; - resetHomeDate(); - mCurrentOrigin.x = 0; - mCurrentOrigin.y = 0; - invalidate(); - } - - public int getHourHeight() { - return mHourHeight; - } - - public void setHourHeight(int hourHeight) { - mNewHourHeight = hourHeight; - invalidate(); - } - - public int getColumnGap() { - return mColumnGap; - } - - public void setColumnGap(int columnGap) { - mColumnGap = columnGap; - invalidate(); - } - - public int getFirstDayOfWeek() { - return mFirstDayOfWeek; - } - - /** - * Set the first day of the week. First day of the week is used only when the week view is first - * drawn. It does not of any effect after user starts scrolling horizontally. - *

- * Note: This method will only work if the week view is set to display more than 6 days at - * once. - *

- * - * @param firstDayOfWeek The supported values are {@link java.util.Calendar#SUNDAY}, - * {@link java.util.Calendar#MONDAY}, {@link java.util.Calendar#TUESDAY}, - * {@link java.util.Calendar#WEDNESDAY}, {@link java.util.Calendar#THURSDAY}, - * {@link java.util.Calendar#FRIDAY}. - */ - public void setFirstDayOfWeek(int firstDayOfWeek) { - mFirstDayOfWeek = firstDayOfWeek; - invalidate(); - } - - public boolean isShowFirstDayOfWeekFirst() { - return mShowFirstDayOfWeekFirst; - } - - public void setShowFirstDayOfWeekFirst(boolean show) { - mShowFirstDayOfWeekFirst = show; - } - - public int getTextSize() { - return mTextSize; - } - - public void setTextSize(int textSize) { - mTextSize = textSize; - mTodayHeaderTextPaint.setTextSize(mTextSize); - mHeaderTextPaint.setTextSize(mTextSize); - mTimeTextPaint.setTextSize(mTextSize); - invalidate(); - } - - public int getHeaderColumnPadding() { - return mHeaderColumnPadding; - } - - public void setHeaderColumnPadding(int headerColumnPadding) { - mHeaderColumnPadding = headerColumnPadding; - invalidate(); - } - - public int getHeaderColumnTextColor() { - return mHeaderColumnTextColor; - } - - public void setHeaderColumnTextColor(int headerColumnTextColor) { - mHeaderColumnTextColor = headerColumnTextColor; - mHeaderTextPaint.setColor(mHeaderColumnTextColor); - mTimeTextPaint.setColor(mHeaderColumnTextColor); - invalidate(); - } - - public void setTypeface(Typeface typeface) { - if (typeface != null) { - mEventTextPaint.setTypeface(typeface); - mTodayHeaderTextPaint.setTypeface(typeface); - mTimeTextPaint.setTypeface(typeface); - mTypeface = typeface; - init(); - } - } - - public int getHeaderRowPadding() { - return mHeaderRowPadding; - } - - public void setHeaderRowPadding(int headerRowPadding) { - mHeaderRowPadding = headerRowPadding; - invalidate(); - } - - public int getHeaderRowBackgroundColor() { - return mHeaderRowBackgroundColor; - } - - public void setHeaderRowBackgroundColor(int headerRowBackgroundColor) { - mHeaderRowBackgroundColor = headerRowBackgroundColor; - mHeaderBackgroundPaint.setColor(mHeaderRowBackgroundColor); - invalidate(); - } - - public int getDayBackgroundColor() { - return mDayBackgroundColor; - } - - public void setDayBackgroundColor(int dayBackgroundColor) { - mDayBackgroundColor = dayBackgroundColor; - mDayBackgroundPaint.setColor(mDayBackgroundColor); - invalidate(); - } - - public int getHourSeparatorColor() { - return mHourSeparatorColor; - } - - public void setHourSeparatorColor(int hourSeparatorColor) { - mHourSeparatorColor = hourSeparatorColor; - mHourSeparatorPaint.setColor(mHourSeparatorColor); - invalidate(); - } - - public int getTodayBackgroundColor() { - return mTodayBackgroundColor; - } - - public void setTodayBackgroundColor(int todayBackgroundColor) { - mTodayBackgroundColor = todayBackgroundColor; - mTodayBackgroundPaint.setColor(mTodayBackgroundColor); - invalidate(); - } - - public int getHourSeparatorHeight() { - return mHourSeparatorHeight; - } - - public void setHourSeparatorHeight(int hourSeparatorHeight) { - mHourSeparatorHeight = hourSeparatorHeight; - mHourSeparatorPaint.setStrokeWidth(mHourSeparatorHeight); - invalidate(); - } - - public int getTodayHeaderTextColor() { - return mTodayHeaderTextColor; - } - - public void setTodayHeaderTextColor(int todayHeaderTextColor) { - mTodayHeaderTextColor = todayHeaderTextColor; - mTodayHeaderTextPaint.setColor(mTodayHeaderTextColor); - invalidate(); - } - - public int getEventTextSize() { - return mEventTextSize; - } - - public void setEventTextSize(int eventTextSize) { - mEventTextSize = eventTextSize; - mEventTextPaint.setTextSize(mEventTextSize); - invalidate(); - } - - public int getEventTextColor() { - return mEventTextColor; - } - - public void setEventTextColor(int eventTextColor) { - mEventTextColor = eventTextColor; - mEventTextPaint.setColor(mEventTextColor); - invalidate(); - } - - public void setTextColorPicker(TextColorPicker textColorPicker) { - this.textColorPicker = textColorPicker; - } - - public TextColorPicker getTextColorPicker() { - return textColorPicker; - } - - public int getEventPadding() { - return mEventPadding; - } - - public void setEventPadding(int eventPadding) { - mEventPadding = eventPadding; - invalidate(); - } - - public int getHeaderColumnBackgroundColor() { - return mHeaderColumnBackgroundColor; - } - - public void setHeaderColumnBackgroundColor(int headerColumnBackgroundColor) { - mHeaderColumnBackgroundColor = headerColumnBackgroundColor; - mHeaderColumnBackgroundPaint.setColor(mHeaderColumnBackgroundColor); - invalidate(); - } - - public int getDefaultEventColor() { - return mDefaultEventColor; - } - - public void setDefaultEventColor(int defaultEventColor) { - mDefaultEventColor = defaultEventColor; - invalidate(); - } - - public int getNewEventColor() { - return mNewEventColor; - } - - public void setNewEventColor(int defaultNewEventColor) { - mNewEventColor = defaultNewEventColor; - invalidate(); - } - - public String getNewEventIdentifier() { - return mNewEventIdentifier; - } - - @Deprecated - public int getNewEventId() { - return Integer.parseInt(mNewEventIdentifier); - } - - public void setNewEventIdentifier(String newEventId) { - this.mNewEventIdentifier = newEventId; - } - - @Deprecated - public void setNewEventId(int newEventId) { - this.mNewEventIdentifier = String.valueOf(newEventId); - } - - public int getNewEventLengthInMinutes() { - return mNewEventLengthInMinutes; - } - - public void setNewEventLengthInMinutes(int newEventLengthInMinutes) { - this.mNewEventLengthInMinutes = newEventLengthInMinutes; - } - - public int getNewEventTimeResolutionInMinutes() { - return mNewEventTimeResolutionInMinutes; - } - - public void setNewEventTimeResolutionInMinutes(int newEventTimeResolutionInMinutes) { - this.mNewEventTimeResolutionInMinutes = newEventTimeResolutionInMinutes; - } - - /** - * Note: Use {@link #setDateTimeInterpreter(DateTimeInterpreter)} and - * {@link #getDateTimeInterpreter()} instead. - * - * @return Either long or short day name is being used. - */ - @Deprecated - public int getDayNameLength() { - return mDayNameLength; - } - - /** - * Set the length of the day name displayed in the header row. Example of short day names is - * 'M' for 'Monday' and example of long day names is 'Mon' for 'Monday'. - *

- * Note: Use {@link #setDateTimeInterpreter(DateTimeInterpreter)} instead. - *

- * - * @param length Supported values are {@link com.alamkanak.weekview.WeekView#LENGTH_SHORT} and - * {@link com.alamkanak.weekview.WeekView#LENGTH_LONG}. - */ - @Deprecated - public void setDayNameLength(int length) { - if (length != LENGTH_LONG && length != LENGTH_SHORT) { - throw new IllegalArgumentException("length parameter must be either LENGTH_LONG or LENGTH_SHORT"); - } - this.mDayNameLength = length; - } - - public int getOverlappingEventGap() { - return mOverlappingEventGap; - } - - /** - * Set the gap between overlapping events. - * - * @param overlappingEventGap The gap between overlapping events. - */ - public void setOverlappingEventGap(int overlappingEventGap) { - this.mOverlappingEventGap = overlappingEventGap; - invalidate(); - } - - public int getEventCornerRadius() { - return mEventCornerRadius; - } - - /** - * Set corner radius for event rect. - * - * @param eventCornerRadius the radius in px. - */ - public void setEventCornerRadius(int eventCornerRadius) { - mEventCornerRadius = eventCornerRadius; - } - - public int getEventMarginVertical() { - return mEventMarginVertical; - } - - /** - * Set the top and bottom margin of the event. The event will release this margin from the top - * and bottom edge. This margin is useful for differentiation consecutive events. - * - * @param eventMarginVertical The top and bottom margin. - */ - public void setEventMarginVertical(int eventMarginVertical) { - this.mEventMarginVertical = eventMarginVertical; - invalidate(); - } - - /** - * Returns the first visible day in the week view. - * - * @return The first visible day in the week view. - */ - public Calendar getFirstVisibleDay() { - return mFirstVisibleDay; - } - - /** - * Returns the last visible day in the week view. - * - * @return The last visible day in the week view. - */ - public Calendar getLastVisibleDay() { - return mLastVisibleDay; - } - - /** - * Get the scrolling speed factor in horizontal direction. - * - * @return The speed factor in horizontal direction. - */ - public float getXScrollingSpeed() { - return mXScrollingSpeed; - } - - /** - * Sets the speed for horizontal scrolling. - * - * @param xScrollingSpeed The new horizontal scrolling speed. - */ - public void setXScrollingSpeed(float xScrollingSpeed) { - this.mXScrollingSpeed = xScrollingSpeed; - } - - /** - * Get the earliest day that can be displayed. Will return null if no minimum date is set. - * - * @return the earliest day that can be displayed, null if no minimum date set - */ - public Calendar getMinDate() { - return mMinDate; - } - - /** - * Set the earliest day that can be displayed. This will determine the left horizontal scroll - * limit. The default value is null (allow unlimited scrolling into the past). - * - * @param minDate The new minimum date (pass null for no minimum) - */ - public void setMinDate(Calendar minDate) { - if (minDate != null) { - minDate.set(Calendar.HOUR_OF_DAY, 0); - minDate.set(Calendar.MINUTE, 0); - minDate.set(Calendar.SECOND, 0); - minDate.set(Calendar.MILLISECOND, 0); - if (mMaxDate != null && minDate.after(mMaxDate)) { - throw new IllegalArgumentException("minDate cannot be later than maxDate"); - } - } - - mMinDate = minDate; - resetHomeDate(); - mCurrentOrigin.x = 0; - invalidate(); - } - - /** - * Get the latest day that can be displayed. Will return null if no maximum date is set. - * - * @return the latest day the can be displayed, null if no max date set - */ - public Calendar getMaxDate() { - return mMaxDate; - } - - /** - * Set the latest day that can be displayed. This will determine the right horizontal scroll - * limit. The default value is null (allow unlimited scrolling in to the future). - * - * @param maxDate The new maximum date (pass null for no maximum) - */ - public void setMaxDate(Calendar maxDate) { - if (maxDate != null) { - maxDate.set(Calendar.HOUR_OF_DAY, 0); - maxDate.set(Calendar.MINUTE, 0); - maxDate.set(Calendar.SECOND, 0); - maxDate.set(Calendar.MILLISECOND, 0); - if (mMinDate != null && maxDate.before(mMinDate)) { - throw new IllegalArgumentException("maxDate has to be after minDate"); - } - } - - mMaxDate = maxDate; - resetHomeDate(); - mCurrentOrigin.x = 0; - invalidate(); - } - - /** - * Whether weekends should have a background color different from the normal day background - * color. The weekend background colors are defined by the attributes - * `futureWeekendBackgroundColor` and `pastWeekendBackgroundColor`. - * - * @return True if weekends should have different background colors. - */ - public boolean isShowDistinctWeekendColor() { - return mShowDistinctWeekendColor; - } - - /** - * Set whether weekends should have a background color different from the normal day background - * color. The weekend background colors are defined by the attributes - * `futureWeekendBackgroundColor` and `pastWeekendBackgroundColor`. - * - * @param showDistinctWeekendColor True if weekends should have different background colors. - */ - public void setShowDistinctWeekendColor(boolean showDistinctWeekendColor) { - this.mShowDistinctWeekendColor = showDistinctWeekendColor; - invalidate(); - } - - /** - * auto calculate limit time on events in visible days. - */ - public void setAutoLimitTime(boolean isAuto) { - this.mAutoLimitTime = isAuto; - invalidate(); - } - - private void recalculateHourHeight() { - int height = (int) ((getHeight() - (mHeaderHeight + mHeaderRowPadding * 2 + mTimeTextHeight / 2 + mHeaderMarginBottom)) / (this.mMaxTime - this.mMinTime)); - if (height > mHourHeight) { - if (height > mMaxHourHeight) - mMaxHourHeight = height; - mNewHourHeight = height; - } - } - - /** - * Set visible time span. - * - * @param startHour limit time display on top (between 0~24) - * @param endHour limit time display at bottom (between 0~24 and larger than startHour) - */ - public void setLimitTime(int startHour, int endHour) { - if (endHour <= startHour) { - throw new IllegalArgumentException("endHour must larger startHour."); - } else if (startHour < 0) { - throw new IllegalArgumentException("startHour must be at least 0."); - } else if (endHour > 24) { - throw new IllegalArgumentException("endHour can't be higher than 24."); - } - this.mMinTime = startHour; - this.mMaxTime = endHour; - recalculateHourHeight(); - invalidate(); - } - - /** - * Set minimal shown time - * - * @param startHour limit time display on top (between 0~24) and smaller than endHour - */ - public void setMinTime(int startHour) { - if (mMaxTime <= startHour) { - throw new IllegalArgumentException("startHour must smaller than endHour"); - } else if (startHour < 0) { - throw new IllegalArgumentException("startHour must be at least 0."); - } - this.mMinTime = startHour; - recalculateHourHeight(); - } - - /** - * Set highest shown time - * - * @param endHour limit time display at bottom (between 0~24 and larger than startHour) - */ - public void setMaxTime(int endHour) { - if (endHour <= mMinTime) { - throw new IllegalArgumentException("endHour must larger startHour."); - } else if (endHour > 24) { - throw new IllegalArgumentException("endHour can't be higher than 24."); - } - this.mMaxTime = endHour; - recalculateHourHeight(); - invalidate(); - } - - /** - * Whether past and future days should have two different background colors. The past and - * future day colors are defined by the attributes `futureBackgroundColor` and - * `pastBackgroundColor`. - * - * @return True if past and future days should have two different background colors. - */ - public boolean isShowDistinctPastFutureColor() { - return mShowDistinctPastFutureColor; - } - - /** - * Set whether weekends should have a background color different from the normal day background - * color. The past and future day colors are defined by the attributes `futureBackgroundColor` - * and `pastBackgroundColor`. - * - * @param showDistinctPastFutureColor True if past and future should have two different - * background colors. - */ - public void setShowDistinctPastFutureColor(boolean showDistinctPastFutureColor) { - this.mShowDistinctPastFutureColor = showDistinctPastFutureColor; - invalidate(); - } - - /** - * Get whether "now" line should be displayed. "Now" line is defined by the attributes - * `nowLineColor` and `nowLineThickness`. - * - * @return True if "now" line should be displayed. - */ - public boolean isShowNowLine() { - return mShowNowLine; - } - - /** - * Set whether "now" line should be displayed. "Now" line is defined by the attributes - * `nowLineColor` and `nowLineThickness`. - * - * @param showNowLine True if "now" line should be displayed. - */ - public void setShowNowLine(boolean showNowLine) { - this.mShowNowLine = showNowLine; - invalidate(); - } - - /** - * Get the "now" line color. - * - * @return The color of the "now" line. - */ - public int getNowLineColor() { - return mNowLineColor; - } - - /** - * Set the "now" line color. - * - * @param nowLineColor The color of the "now" line. - */ - public void setNowLineColor(int nowLineColor) { - this.mNowLineColor = nowLineColor; - invalidate(); - } - - /** - * Get the "now" line thickness. - * - * @return The thickness of the "now" line. - */ - public int getNowLineThickness() { - return mNowLineThickness; - } - - /** - * Set the "now" line thickness. - * - * @param nowLineThickness The thickness of the "now" line. - */ - public void setNowLineThickness(int nowLineThickness) { - this.mNowLineThickness = nowLineThickness; - invalidate(); - } - - /** - * Get whether the week view should fling horizontally. - * - * @return True if the week view has horizontal fling enabled. - */ - public boolean isHorizontalFlingEnabled() { - return mHorizontalFlingEnabled; - } - - /** - * Set whether the week view should fling horizontally. - * - * @param enabled whether the week view should fling horizontally - */ - public void setHorizontalFlingEnabled(boolean enabled) { - mHorizontalFlingEnabled = enabled; - } - - /** - * Get whether the week view should fling vertically. - * - * @return True if the week view has vertical fling enabled. - */ - public boolean isVerticalFlingEnabled() { - return mVerticalFlingEnabled; - } - - /** - * Set whether the week view should fling vertically. - * - * @param enabled whether the week view should fling vertically - */ - public void setVerticalFlingEnabled(boolean enabled) { - mVerticalFlingEnabled = enabled; - } - - /** - * Get the height of AllDay-events. - * - * @return Height of AllDay-events. - */ - public int getAllDayEventHeight() { - return mAllDayEventHeight; - } - - /** - * Set the height of AllDay-events. - * - * @param height the new height of AllDay-events - */ - public void setAllDayEventHeight(int height) { - mAllDayEventHeight = height; - } - - /** - * Enable zoom focus point - * If you set this to false the `zoomFocusPoint` won't take effect any more while zooming. - * The zoom will always be focused at the center of your gesture. - * - * @param zoomFocusPointEnabled whether the zoomFocusPoint is enabled - */ - public void setZoomFocusPointEnabled(boolean zoomFocusPointEnabled) { - mZoomFocusPointEnabled = zoomFocusPointEnabled; - } - - /* - * Is focus point enabled - * @return fixed focus point enabled? - */ - public boolean isZoomFocusPointEnabled() { - return mZoomFocusPointEnabled; - } - - /* - * Get focus point - * 0 = top of view, 1 = bottom of view - * The focused point (multiplier of the view height) where the week view is zoomed around. - * This point will not move while zooming. - * @return focus point - */ - public float getZoomFocusPoint() { - return mZoomFocusPoint; - } - - /** - * Set focus point - * 0 = top of view, 1 = bottom of view - * The focused point (multiplier of the view height) where the week view is zoomed around. - * This point will not move while zooming. - * - * @param zoomFocusPoint the new zoomFocusPoint - */ - public void setZoomFocusPoint(float zoomFocusPoint) { - if (0 > zoomFocusPoint || zoomFocusPoint > 1) - throw new IllegalStateException("The zoom focus point percentage has to be between 0 and 1"); - mZoomFocusPoint = zoomFocusPoint; - } - - - /** - * Get scroll duration - * - * @return scroll duration - */ - public int getScrollDuration() { - return mScrollDuration; - } - - /** - * Set the scroll duration - * - * @param scrollDuration the new scrollDuraction - */ - public void setScrollDuration(int scrollDuration) { - mScrollDuration = scrollDuration; - } - - public int getMaxHourHeight() { - return mMaxHourHeight; - } - - public void setMaxHourHeight(int maxHourHeight) { - mMaxHourHeight = maxHourHeight; - } - - public int getMinHourHeight() { - return mMinHourHeight; - } - - public void setMinHourHeight(int minHourHeight) { - this.mMinHourHeight = minHourHeight; - } - - public int getPastBackgroundColor() { - return mPastBackgroundColor; - } - - public void setPastBackgroundColor(int pastBackgroundColor) { - this.mPastBackgroundColor = pastBackgroundColor; - mPastBackgroundPaint.setColor(mPastBackgroundColor); - } - - public int getFutureBackgroundColor() { - return mFutureBackgroundColor; - } - - public void setFutureBackgroundColor(int futureBackgroundColor) { - this.mFutureBackgroundColor = futureBackgroundColor; - mFutureBackgroundPaint.setColor(mFutureBackgroundColor); - } - - public int getPastWeekendBackgroundColor() { - return mPastWeekendBackgroundColor; - } - - public void setPastWeekendBackgroundColor(int pastWeekendBackgroundColor) { - this.mPastWeekendBackgroundColor = pastWeekendBackgroundColor; - this.mPastWeekendBackgroundPaint.setColor(mPastWeekendBackgroundColor); - } - - public int getFutureWeekendBackgroundColor() { - return mFutureWeekendBackgroundColor; - } - - public void setFutureWeekendBackgroundColor(int futureWeekendBackgroundColor) { - this.mFutureWeekendBackgroundColor = futureWeekendBackgroundColor; - this.mFutureWeekendBackgroundPaint.setColor(mFutureWeekendBackgroundColor); - } - - public Drawable getNewEventIconDrawable() { - return mNewEventIconDrawable; - } - - public void setNewEventIconDrawable(Drawable newEventIconDrawable) { - this.mNewEventIconDrawable = newEventIconDrawable; - } - - public void enableDropListener() { - this.mEnableDropListener = true; - //set drag and drop listener, required Honeycomb+ Api level - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - setOnDragListener(new DragListener()); - } - } - - public void disableDropListener() { - this.mEnableDropListener = false; - //set drag and drop listener, required Honeycomb+ Api level - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - setOnDragListener(null); - } - } - - public boolean isDropListenerEnabled() { - return this.mEnableDropListener; - } - - public void setMinOverlappingMinutes(int minutes) { - this.mMinOverlappingMinutes = minutes; - } - - public int getMinOverlappingMinutes() { - return this.mMinOverlappingMinutes; - } - - public boolean isScrollNumberOfVisibleDays() { - return this.mIsScrollNumberOfVisibleDays; - } - - public void setScrollNumberOfVisibleDays(boolean scrollNumberOfVisibleDays) { - this.mIsScrollNumberOfVisibleDays = scrollNumberOfVisibleDays; - invalidate(); - } - - ///////////////////////////////////////////////////////////////// - // - // Functions related to scrolling. - // - ///////////////////////////////////////////////////////////////// - - @Override - public boolean onTouchEvent(MotionEvent event) { - - mSizeOfWeekView = (mWidthPerDay + mColumnGap) * getNumberOfVisibleDays(); - mDistanceMin = mSizeOfWeekView / mOffsetValueToSecureScreen; - - mScaleDetector.onTouchEvent(event); - boolean val = mGestureDetector.onTouchEvent(event); - - // Check after call of mGestureDetector, so mCurrentFlingDirection and mCurrentScrollDirection are set. - if (event.getAction() == MotionEvent.ACTION_UP && !mIsZooming && mCurrentFlingDirection == Direction.NONE) { - if (mCurrentScrollDirection == Direction.RIGHT || mCurrentScrollDirection == Direction.LEFT) { - goToNearestOrigin(); - } - mCurrentScrollDirection = Direction.NONE; - } - - return val; - } - - private void goToNearestOrigin() { - double leftDays = mCurrentOrigin.x / (mWidthPerDay + mColumnGap); - - float beforeScroll = mStartOriginForScroll; - boolean isPassed = false; - - if (mDistanceDone > mDistanceMin || mDistanceDone < -mDistanceMin || !mIsScrollNumberOfVisibleDays) { - - if (!mIsScrollNumberOfVisibleDays && mCurrentFlingDirection != Direction.NONE) { - // snap to nearest day - leftDays = Math.round(leftDays); - } else if (mCurrentScrollDirection == Direction.LEFT) { - // snap to last day - leftDays = Math.floor(leftDays); - mStartOriginForScroll -= mSizeOfWeekView; - isPassed = true; - } else if (mCurrentScrollDirection == Direction.RIGHT) { - // snap to next day - leftDays = Math.floor(leftDays); - mStartOriginForScroll += mSizeOfWeekView; - isPassed = true; - } else { - // snap to nearest day - leftDays = Math.round(leftDays); - } - - - if (mIsScrollNumberOfVisibleDays) { - boolean mayScrollHorizontal = beforeScroll - mStartOriginForScroll < getXMaxLimit() && mCurrentOrigin.x - mStartOriginForScroll > getXMinLimit(); - if (isPassed && mayScrollHorizontal) { - // Stop current animation. - mScroller.forceFinished(true); - // Snap to date. - if (mCurrentScrollDirection == Direction.LEFT) { - mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) ((beforeScroll - mCurrentOrigin.x) - mSizeOfWeekView), 0, 200); - } else if (mCurrentScrollDirection == Direction.RIGHT) { - mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) (mSizeOfWeekView - (mCurrentOrigin.x - beforeScroll)), 0, 200); - } - ViewCompat.postInvalidateOnAnimation(WeekView.this); - } - } else { - int nearestOrigin = (int) (mCurrentOrigin.x - leftDays * (mWidthPerDay + mColumnGap)); - boolean mayScrollHorizontal = mCurrentOrigin.x - nearestOrigin < getXMaxLimit() && mCurrentOrigin.x - nearestOrigin > getXMinLimit(); - if (mayScrollHorizontal) { - mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, -nearestOrigin, 0); - ViewCompat.postInvalidateOnAnimation(WeekView.this); - } - - if (nearestOrigin != 0 && mayScrollHorizontal) { - // Stop current animation. - mScroller.forceFinished(true); - // Snap to date. - mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, -nearestOrigin, 0, (int) (Math.abs(nearestOrigin) / mWidthPerDay * mScrollDuration)); - ViewCompat.postInvalidateOnAnimation(WeekView.this); - } - } - - // Reset scrolling and fling direction. - mCurrentScrollDirection = mCurrentFlingDirection = Direction.NONE; - - - } else { - mScroller.forceFinished(true); - if (mCurrentScrollDirection == Direction.LEFT) { - mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) beforeScroll - (int) mCurrentOrigin.x, 0, 200); - } else if (mCurrentScrollDirection == Direction.RIGHT) { - mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) beforeScroll - (int) mCurrentOrigin.x, 0, 200); - } - ViewCompat.postInvalidateOnAnimation(WeekView.this); - - // Reset scrolling and fling direction. - mCurrentScrollDirection = mCurrentFlingDirection = Direction.NONE; - } - } - - @Override - public void computeScroll() { - super.computeScroll(); - - if (mScroller.isFinished()) { - if (mCurrentFlingDirection != Direction.NONE) { - // Snap to day after fling is finished. - goToNearestOrigin(); - } - } else { - if (mCurrentFlingDirection != Direction.NONE && forceFinishScroll()) { - goToNearestOrigin(); - } else if (mScroller.computeScrollOffset()) { - mCurrentOrigin.y = mScroller.getCurrY(); - mCurrentOrigin.x = mScroller.getCurrX(); - ViewCompat.postInvalidateOnAnimation(this); - } - } - } - - /** - * Check if scrolling should be stopped. - * - * @return true if scrolling should be stopped before reaching the end of animation. - */ - private boolean forceFinishScroll() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - // current velocity only available since api 14 - return mScroller.getCurrVelocity() <= mMinimumFlingVelocity; - } else { - return false; - } - } - - - ///////////////////////////////////////////////////////////////// - // - // Public methods. - // - ///////////////////////////////////////////////////////////////// - - /** - * Show today on the week view. - */ - public void goToToday() { - Calendar today = Calendar.getInstance(); - goToDate(today); - } - - /** - * Show a specific day on the week view. - * - * @param date The date to show. - */ - public void goToDate(Calendar date) { - mScroller.forceFinished(true); - mCurrentScrollDirection = mCurrentFlingDirection = Direction.NONE; - - date.set(Calendar.HOUR_OF_DAY, 0); - date.set(Calendar.MINUTE, 0); - date.set(Calendar.SECOND, 0); - date.set(Calendar.MILLISECOND, 0); - - if (mAreDimensionsInvalid) { - mScrollToDay = date; - return; - } - - mRefreshEvents = true; - - mCurrentOrigin.x = -daysBetween(mHomeDate, date) * (mWidthPerDay + mColumnGap); - invalidate(); - } - - /** - * Refreshes the view and loads the events again. - */ - public void notifyDatasetChanged() { - mRefreshEvents = true; - invalidate(); - } - - /** - * Vertically scroll to a specific hour in the week view. - * - * @param hour The hour to scroll to in 24-hour format. Supported values are 0-24. - */ - public void goToHour(double hour) { - if (mAreDimensionsInvalid) { - mScrollToHour = hour; - return; - } - - int verticalOffset = 0; - if (hour > mMaxTime) - verticalOffset = mHourHeight * (mMaxTime - mMinTime); - else if (hour > mMinTime) - verticalOffset = (int) (mHourHeight * hour); - - if (verticalOffset > mHourHeight * (mMaxTime - mMinTime) - getHeight() + mHeaderHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom) - verticalOffset = (int) (mHourHeight * (mMaxTime - mMinTime) - getHeight() + mHeaderHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom); - - mCurrentOrigin.y = -verticalOffset; - invalidate(); - } - - /** - * Get the first hour that is visible on the screen. - * - * @return The first hour that is visible. - */ - public double getFirstVisibleHour() { - return -mCurrentOrigin.y / mHourHeight; - } - - /** - * Determine whether a given calendar day falls within the scroll limits set for this view. - * - * @param day the day to check - * @return True if there are no limit or the date is within the limits. - * @see #setMinDate(Calendar) - * @see #setMaxDate(Calendar) - */ - public boolean dateIsValid(Calendar day) { - if (mMinDate != null && day.before(mMinDate)) { - return false; - } - if (mMaxDate != null && day.after(mMaxDate)) { - return false; - } - return true; - } - - ///////////////////////////////////////////////////////////////// - // - // Interfaces. - // - ///////////////////////////////////////////////////////////////// - - public interface DropListener { - /** - * Triggered when view dropped - * - * @param view: dropped view. - * @param date: object set with the date and time of the dropped coordinates on the view. - */ - void onDrop(View view, Calendar date); - } - - public interface EventClickListener { - /** - * Triggered when clicked on one existing event - * - * @param event: event clicked. - * @param eventRect: view containing the clicked event. - */ - void onEventClick(WeekViewEvent event, RectF eventRect); - } - - public interface EventLongPressListener { - /** - * Similar to {@link com.alamkanak.weekview.WeekView.EventClickListener} but with a long press. - * - * @param event: event clicked. - * @param eventRect: view containing the clicked event. - */ - void onEventLongPress(WeekViewEvent event, RectF eventRect); - } - - public interface EmptyViewClickListener { - /** - * Triggered when the users clicks on a empty space of the calendar. - * - * @param date: {@link Calendar} object set with the date and time of the clicked position on the view. - */ - void onEmptyViewClicked(Calendar date); - - } - - public interface EmptyViewLongPressListener { - /** - * Similar to {@link com.alamkanak.weekview.WeekView.EmptyViewClickListener} but with long press. - * - * @param time: {@link Calendar} object set with the date and time of the long pressed position on the view. - */ - void onEmptyViewLongPress(Calendar time); - } - - public interface ScrollListener { - /** - * Called when the first visible day has changed. - *

- * (this will also be called during the first draw of the weekview) - * - * @param newFirstVisibleDay The new first visible day - * @param oldFirstVisibleDay The old first visible day (is null on the first call). - */ - void onFirstVisibleDayChanged(Calendar newFirstVisibleDay, Calendar oldFirstVisibleDay); - } - - public interface AddEventClickListener { - /** - * Triggered when the users clicks to create a new event. - * - * @param startTime The startTime of a new event - * @param endTime The endTime of a new event - */ - void onAddEventClicked(Calendar startTime, Calendar endTime); - } - - /** - * A simple GestureListener that holds the focused hour while scaling. - */ - private class WeekViewGestureListener implements ScaleGestureDetector.OnScaleGestureListener { - float mFocusedPointY; - - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - mIsZooming = false; - } - - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - mIsZooming = true; - goToNearestOrigin(); - - // Calculate focused point for scale action - if (mZoomFocusPointEnabled) { - // Use fractional focus, percentage of height - mFocusedPointY = (getHeight() - mHeaderHeight - mHeaderRowPadding * 2 - mHeaderMarginBottom) * mZoomFocusPoint; - } else { - // Grab focus - mFocusedPointY = detector.getFocusY(); - } - - return true; - } - - @Override - public boolean onScale(ScaleGestureDetector detector) { - final float scale = detector.getScaleFactor(); - - mNewHourHeight = Math.round(mHourHeight * scale); - - // Calculating difference - float diffY = mFocusedPointY - mCurrentOrigin.y; - // Scaling difference - diffY = diffY * scale - diffY; - // Updating week view origin - mCurrentOrigin.y -= diffY; - - invalidate(); - return true; - } - - } - - @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) - private class DragListener implements View.OnDragListener { - @Override - public boolean onDrag(View v, DragEvent e) { - switch (e.getAction()) { - case DragEvent.ACTION_DROP: - if (e.getX() > mHeaderColumnWidth && e.getY() > (mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom)) { - Calendar selectedTime = getTimeFromPoint(e.getX(), e.getY()); - if (selectedTime != null) { - mDropListener.onDrop(v, selectedTime); - } - } - break; - } - return true; - } - } -} diff --git a/library/src/main/java/com/alamkanak/weekview/WeekView.kt b/library/src/main/java/com/alamkanak/weekview/WeekView.kt new file mode 100644 index 000000000..ad8227647 --- /dev/null +++ b/library/src/main/java/com/alamkanak/weekview/WeekView.kt @@ -0,0 +1,2513 @@ +package com.alamkanak.weekview + +import android.content.Context +import android.graphics.* +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.support.v4.view.GestureDetectorCompat +import android.support.v4.view.ViewCompat +import android.support.v4.view.animation.FastOutLinearInInterpolator +import android.text.* +import android.text.format.DateFormat +import android.text.format.DateUtils +import android.text.style.StyleSpan +import android.util.AttributeSet +import android.util.TypedValue +import android.view.* +import android.widget.OverScroller +import com.alamkanak.weekview.WeekViewUtil.daysBetween +import com.alamkanak.weekview.WeekViewUtil.getPassedMinutesInDay +import com.alamkanak.weekview.WeekViewUtil.isSameDay +import com.alamkanak.weekview.WeekViewUtil.today +import java.text.SimpleDateFormat +import java.util.* + +/** + * Created by Raquib-ul-Alam Kanak on 7/21/2014. + * Website: http://alamkanak.github.io/ + */ +class WeekView @JvmOverloads constructor(private val mContext: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(mContext, attrs, defStyleAttr) { + private var mHomeDate: Calendar? = null + /** + * Get the earliest day that can be displayed. Will return null if no minimum date is set. + * + * @return the earliest day that can be displayed, null if no minimum date set + */ + /** + * Set the earliest day that can be displayed. This will determine the left horizontal scroll + * limit. The default value is null (allow unlimited scrolling into the past). + * + * @param minDate The new minimum date (pass null for no minimum) + */ + var minDate: Calendar? = null + set(minDate) { + if (minDate != null) { + minDate.set(Calendar.HOUR_OF_DAY, 0) + minDate.set(Calendar.MINUTE, 0) + minDate.set(Calendar.SECOND, 0) + minDate.set(Calendar.MILLISECOND, 0) + if (maxDate != null && minDate.after(maxDate)) { + throw IllegalArgumentException("minDate cannot be later than maxDate") + } + } + + field = minDate + resetHomeDate() + mCurrentOrigin.x = 0f + invalidate() + } + /** + * Get the latest day that can be displayed. Will return null if no maximum date is set. + * + * @return the latest day the can be displayed, null if no max date set + */ + /** + * Set the latest day that can be displayed. This will determine the right horizontal scroll + * limit. The default value is null (allow unlimited scrolling in to the future). + * + * @param maxDate The new maximum date (pass null for no maximum) + */ + var maxDate: Calendar? = null + set(maxDate) { + if (maxDate != null) { + maxDate.set(Calendar.HOUR_OF_DAY, 0) + maxDate.set(Calendar.MINUTE, 0) + maxDate.set(Calendar.SECOND, 0) + maxDate.set(Calendar.MILLISECOND, 0) + if (minDate != null && maxDate.before(minDate)) { + throw IllegalArgumentException("maxDate has to be after minDate") + } + } + + field = maxDate + resetHomeDate() + mCurrentOrigin.x = 0f + invalidate() + } + private var mTimeTextPaint: Paint? = null + private var mTimeTextWidth: Float = 0.toFloat() + private var mTimeTextHeight: Float = 0.toFloat() + private var mHeaderTextPaint: Paint? = null + private var mHeaderTextHeight: Float = 0.toFloat() + private var mHeaderHeight: Float = 0.toFloat() + private var mGestureDetector: GestureDetectorCompat? = null + private var mScroller: OverScroller? = null + private val mCurrentOrigin = PointF(0f, 0f) + private var mCurrentScrollDirection = Direction.NONE + private var mHeaderBackgroundPaint: Paint? = null + private var mWidthPerDay: Float = 0.toFloat() + private var mDayBackgroundPaint: Paint? = null + private var mHourSeparatorPaint: Paint? = null + private var mHeaderMarginBottom: Float = 0.toFloat() + private var mTodayBackgroundPaint: Paint? = null + private var mFutureBackgroundPaint: Paint? = null + private var mPastBackgroundPaint: Paint? = null + private var mFutureWeekendBackgroundPaint: Paint? = null + private var mPastWeekendBackgroundPaint: Paint? = null + private var mNowLinePaint: Paint? = null + private var mTodayHeaderTextPaint: Paint? = null + private var mEventBackgroundPaint: Paint? = null + private var mNewEventBackgroundPaint: Paint? = null + private var mHeaderColumnWidth: Float = 0.toFloat() + private var mEventRects: MutableList? = null + private var mEvents: MutableList? = null + private var mEventTextPaint: TextPaint? = null + private val mNewEventTextPaint: TextPaint? = null + private var mHeaderColumnBackgroundPaint: Paint? = null + private var mFetchedPeriod = -1 // the middle period the calendar has fetched. + private var mRefreshEvents = false + private var mCurrentFlingDirection = Direction.NONE + private var mScaleDetector: ScaleGestureDetector? = null + private var mIsZooming: Boolean = false + /** + * Returns the first visible day in the week view. + * + * @return The first visible day in the week view. + */ + var firstVisibleDay: Calendar? = null + private set + /** + * Returns the last visible day in the week view. + * + * @return The last visible day in the week view. + */ + var lastVisibleDay: Calendar? = null + private set + private var mMinimumFlingVelocity = 0 + private var mScaledTouchSlop = 0 + private var mNewEventRect: EventRect? = null + var textColorPicker: TextColorPicker? = null + private var mSizeOfWeekView: Float = 0.toFloat() + private var mDistanceDone = 0f + private var mDistanceMin: Float = 0.toFloat() + protected var mOffsetValueToSecureScreen = 9 + private var mStartOriginForScroll = 0f + + // Attributes and their default values. + private var mHourHeight = 50 + private var mNewHourHeight = -1 + var minHourHeight = 0 //no minimum specified (will be dynamic, based on screen) + private var mEffectiveMinHourHeight = minHourHeight //compensates for the fact that you can't keep zooming out. + var maxHourHeight = 250 + private var mColumnGap = 10 + private var mFirstDayOfWeek = Calendar.getInstance().firstDayOfWeek + private var mTextSize = 12 + private var mHeaderColumnPadding = 10 + private var mHeaderColumnTextColor = Color.BLACK + private var mNumberOfVisibleDays = 3 + private var mHeaderRowPadding = 10 + private var mHeaderRowBackgroundColor = Color.WHITE + private var mDayBackgroundColor = Color.rgb(245, 245, 245) + private var mPastBackgroundColor = Color.rgb(227, 227, 227) + private var mFutureBackgroundColor = Color.rgb(245, 245, 245) + private var mPastWeekendBackgroundColor = 0 + private var mFutureWeekendBackgroundColor = 0 + private var mNowLineColor = Color.rgb(102, 102, 102) + private var mNowLineThickness = 5 + private var mHourSeparatorColor = Color.rgb(230, 230, 230) + private var mTodayBackgroundColor = Color.rgb(239, 247, 254) + private var mHourSeparatorHeight = 2 + private var mTodayHeaderTextColor = Color.rgb(39, 137, 228) + private var mEventTextSize = 12 + private var mEventTextColor = Color.BLACK + private var mEventPadding = 8 + private var mHeaderColumnBackgroundColor = Color.WHITE + private var mDefaultEventColor: Int = 0 + private var mNewEventColor: Int = 0 + var newEventIdentifier: String? = "-100" + var newEventIconDrawable: Drawable? = null + var newEventLengthInMinutes = 60 + var newEventTimeResolutionInMinutes = 15 + var isShowFirstDayOfWeekFirst = false + + private var mIsFirstDraw = true + private var mAreDimensionsInvalid = true + @Deprecated("") + private var mDayNameLength = LENGTH_LONG + private var mOverlappingEventGap = 0 + private var mEventMarginVertical = 0 + /** + * Get the scrolling speed factor in horizontal direction. + * + * @return The speed factor in horizontal direction. + */ + /** + * Sets the speed for horizontal scrolling. + * + * @param xScrollingSpeed The new horizontal scrolling speed. + */ + var xScrollingSpeed = 1f + private var mScrollToDay: Calendar? = null + private var mScrollToHour = -1.0 + /** + * Set corner radius for event rect. + * + * @param eventCornerRadius the radius in px. + */ + var eventCornerRadius = 0 + private var mShowDistinctWeekendColor = false + private var mShowNowLine = false + private var mShowDistinctPastFutureColor = false + /** + * Get whether the week view should fling horizontally. + * + * @return True if the week view has horizontal fling enabled. + */ + /** + * Set whether the week view should fling horizontally. + * + * @param enabled whether the week view should fling horizontally + */ + var isHorizontalFlingEnabled = true + /** + * Get whether the week view should fling vertically. + * + * @return True if the week view has vertical fling enabled. + */ + /** + * Set whether the week view should fling vertically. + * + * @param enabled whether the week view should fling vertically + */ + var isVerticalFlingEnabled = true + /** + * Get the height of AllDay-events. + * + * @return Height of AllDay-events. + */ + /** + * Set the height of AllDay-events. + * + * @param height the new height of AllDay-events + */ + var allDayEventHeight = 100 + private var mZoomFocusPoint = 0f + /* + * Is focus point enabled + * @return fixed focus point enabled? + */ + /** + * Enable zoom focus point + * If you set this to false the `zoomFocusPoint` won't take effect any more while zooming. + * The zoom will always be focused at the center of your gesture. + * + * @param zoomFocusPointEnabled whether the zoomFocusPoint is enabled + */ + var isZoomFocusPointEnabled = true + /** + * Get scroll duration + * + * @return scroll duration + */ + /** + * Set the scroll duration + * + * @param scrollDuration the new scrollDuraction + */ + var scrollDuration = 250 + var timeColumnResolution = 60 + private var mTypeface = Typeface.DEFAULT_BOLD + private var mMinTime = 0 + private var mMaxTime = 24 + private var mAutoLimitTime = false + var isDropListenerEnabled = false + private set + var minOverlappingMinutes = 0 + private var mIsScrollNumberOfVisibleDays = false + + // Listeners. + var eventClickListener: EventClickListener? = null + private set + var eventLongPressListener: EventLongPressListener? = null + /** + * Get event loader in the week view. Event loaders define the interval after which the events + * are loaded in week view. For a MonthLoader events are loaded for every month. You can define + * your custom event loader by extending WeekViewLoader. + * + * @return The event loader. + */ + /** + * Set event loader in the week view. For example, a MonthLoader. Event loaders define the + * interval after which the events are loaded in week view. For a MonthLoader events are loaded + * for every month. You can define your custom event loader by extending WeekViewLoader. + * + * @param loader The event loader. + */ + var weekViewLoader: WeekViewLoader? = null + var emptyViewClickListener: EmptyViewClickListener? = null + var emptyViewLongPressListener: EmptyViewLongPressListener? = null + private var mDateTimeInterpreter: DateTimeInterpreter? = null + var scrollListener: ScrollListener? = null + var addEventClickListener: AddEventClickListener? = null + private var mDropListener: DropListener? = null + + private val mGestureListener = object : GestureDetector.SimpleOnGestureListener() { + + override fun onDown(e: MotionEvent): Boolean { + mStartOriginForScroll = mCurrentOrigin.x + goToNearestOrigin() + return true + } + + override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean { + // Check if view is zoomed. + if (mIsZooming) + return true + + when (mCurrentScrollDirection) { + WeekView.Direction.NONE -> { + // Allow scrolling only in one direction. + if (Math.abs(distanceX) > Math.abs(distanceY)) { + if (distanceX > 0) { + mCurrentScrollDirection = Direction.LEFT + } else { + mCurrentScrollDirection = Direction.RIGHT + } + } else { + mCurrentScrollDirection = Direction.VERTICAL + } + } + WeekView.Direction.LEFT -> { + // Change direction if there was enough change. + if (Math.abs(distanceX) > Math.abs(distanceY) && distanceX < -mScaledTouchSlop) { + mCurrentScrollDirection = Direction.RIGHT + } + } + WeekView.Direction.RIGHT -> { + // Change direction if there was enough change. + if (Math.abs(distanceX) > Math.abs(distanceY) && distanceX > mScaledTouchSlop) { + mCurrentScrollDirection = Direction.LEFT + } + } + else -> { + } + } + + // Calculate the new origin after scroll. + when (mCurrentScrollDirection) { + WeekView.Direction.LEFT, WeekView.Direction.RIGHT -> { + val minX = xMinLimit + val maxX = xMaxLimit + + if (e2.x < 0) { + mDistanceDone = e2.x - e1.x + } else { + mDistanceDone = e1.x - e2.x + } + + if (mCurrentOrigin.x - distanceX * xScrollingSpeed > maxX) { + mCurrentOrigin.x = maxX + } else if (mCurrentOrigin.x - distanceX * xScrollingSpeed < minX) { + mCurrentOrigin.x = minX + } else { + mCurrentOrigin.x -= distanceX * xScrollingSpeed + } + ViewCompat.postInvalidateOnAnimation(this@WeekView) + } + WeekView.Direction.VERTICAL -> { + val minY = yMinLimit + val maxY = yMaxLimit + if (mCurrentOrigin.y - distanceY > maxY) { + mCurrentOrigin.y = maxY + } else if (mCurrentOrigin.y - distanceY < minY) { + mCurrentOrigin.y = minY + } else { + mCurrentOrigin.y -= distanceY + } + ViewCompat.postInvalidateOnAnimation(this@WeekView) + } + else -> { + } + } + return true + } + + override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { + if (mIsZooming) + return true + + if (mCurrentFlingDirection == Direction.LEFT && !isHorizontalFlingEnabled || + mCurrentFlingDirection == Direction.RIGHT && !isHorizontalFlingEnabled || + mCurrentFlingDirection == Direction.VERTICAL && !isVerticalFlingEnabled) { + return true + } + + mScroller!!.forceFinished(true) + + mCurrentFlingDirection = mCurrentScrollDirection + when (mCurrentFlingDirection) { + WeekView.Direction.LEFT, WeekView.Direction.RIGHT -> if (!mIsScrollNumberOfVisibleDays) { + mScroller!!.fling(mCurrentOrigin.x.toInt(), mCurrentOrigin.y.toInt(), (velocityX * xScrollingSpeed).toInt(), 0, xMinLimit.toInt(), xMaxLimit.toInt(), yMinLimit.toInt(), yMaxLimit.toInt()) + } + WeekView.Direction.VERTICAL -> mScroller!!.fling(mCurrentOrigin.x.toInt(), mCurrentOrigin.y.toInt(), 0, velocityY.toInt(), xMinLimit.toInt(), xMaxLimit.toInt(), yMinLimit.toInt(), yMaxLimit.toInt()) + else -> { + } + } + + ViewCompat.postInvalidateOnAnimation(this@WeekView) + return true + } + + + override fun onSingleTapConfirmed(e: MotionEvent): Boolean { + + // If the tap was on an event then trigger the callback. + if (mEventRects != null && eventClickListener != null) { + val reversedEventRects = mEventRects + Collections.reverse(reversedEventRects!!) + for (eventRect in reversedEventRects) { + if (newEventIdentifier != eventRect.event.identifier && eventRect.rectF != null && e.x > eventRect.rectF!!.left && e.x < eventRect.rectF!!.right && e.y > eventRect.rectF!!.top && e.y < eventRect.rectF!!.bottom) { + eventClickListener!!.onEventClick(eventRect.originalEvent, eventRect.rectF!!) + playSoundEffect(SoundEffectConstants.CLICK) + return super.onSingleTapConfirmed(e) + } + } + } + + val xOffset = xStartPixel + + val x = e.x - xOffset + val y = e.y - mCurrentOrigin.y + // If the tap was on add new Event space, then trigger the callback + if (addEventClickListener != null && mNewEventRect != null && mNewEventRect!!.rectF != null && + mNewEventRect!!.rectF!!.contains(x, y)) { + addEventClickListener!!.onAddEventClicked(mNewEventRect!!.event.startTime!!, mNewEventRect!!.event.endTime!!) + return super.onSingleTapConfirmed(e) + } + + // If the tap was on an empty space, then trigger the callback. + if ((emptyViewClickListener != null || addEventClickListener != null) && e.x > mHeaderColumnWidth && e.y > mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mHeaderMarginBottom) { + val selectedTime = getTimeFromPoint(e.x, e.y) + + if (selectedTime != null) { + val tempEvents = ArrayList(mEvents!!) + if (mNewEventRect != null) { + tempEvents.remove(mNewEventRect!!.event) + mNewEventRect = null + } + + playSoundEffect(SoundEffectConstants.CLICK) + + if (emptyViewClickListener != null) + emptyViewClickListener!!.onEmptyViewClicked(selectedTime.clone() as Calendar) + + if (addEventClickListener != null) { + //round selectedTime to resolution + selectedTime.add(Calendar.MINUTE, -(newEventLengthInMinutes / 2)) + //Fix selected time if before the minimum hour + if (selectedTime.get(Calendar.HOUR_OF_DAY) < mMinTime) { + selectedTime.set(Calendar.HOUR_OF_DAY, mMinTime) + selectedTime.set(Calendar.MINUTE, 0) + } + val unroundedMinutes = selectedTime.get(Calendar.MINUTE) + val mod = unroundedMinutes % newEventTimeResolutionInMinutes + selectedTime.add(Calendar.MINUTE, if (mod < Math.ceil((newEventTimeResolutionInMinutes / 2).toDouble())) -mod else newEventTimeResolutionInMinutes - mod) + + val endTime = selectedTime.clone() as Calendar + + //Minus one to ensure it is the same day and not midnight (next day) + val maxMinutes = (mMaxTime - selectedTime.get(Calendar.HOUR_OF_DAY)) * 60 - selectedTime.get(Calendar.MINUTE) - 1 + endTime.add(Calendar.MINUTE, Math.min(maxMinutes, newEventLengthInMinutes)) + //If clicked at end of the day, fix selected startTime + if (maxMinutes < newEventLengthInMinutes) { + selectedTime.add(Calendar.MINUTE, maxMinutes - newEventLengthInMinutes) + } + + val newEvent = WeekViewEvent(newEventIdentifier!!, "", null, selectedTime, endTime) + + val top = mHourHeight * getPassedMinutesInDay(selectedTime) / 60 + eventsTop + val bottom = mHourHeight * getPassedMinutesInDay(endTime) / 60 + eventsTop + + // Calculate left and right. + val left = mWidthPerDay * WeekViewUtil.daysBetween(firstVisibleDay!!, selectedTime) + val right = left + mWidthPerDay + + // Add the new event if its bounds are valid + if (left < right && + left < width && + top < height && + right > mHeaderColumnWidth && + bottom > 0) { + val dayRectF = RectF(left, top, right, bottom - mCurrentOrigin.y) + newEvent.color = mNewEventColor + mNewEventRect = EventRect(newEvent, newEvent, dayRectF) + tempEvents.add(newEvent) + this@WeekView.clearEvents() + cacheAndSortEvents(tempEvents) + computePositionOfEvents(mEventRects!!) + invalidate() + } + + } + } + + } + return super.onSingleTapConfirmed(e) + } + + override fun onLongPress(e: MotionEvent) { + super.onLongPress(e) + + if (eventLongPressListener != null && mEventRects != null) { + val reversedEventRects = mEventRects + Collections.reverse(reversedEventRects!!) + for (event in reversedEventRects) { + if (event.rectF != null && e.x > event.rectF!!.left && e.x < event.rectF!!.right && e.y > event.rectF!!.top && e.y < event.rectF!!.bottom) { + eventLongPressListener!!.onEventLongPress(event.originalEvent, event.rectF!!) + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) + return + } + } + } + + // If the tap was on in an empty space, then trigger the callback. + if (emptyViewLongPressListener != null && e.x > mHeaderColumnWidth && e.y > mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mHeaderMarginBottom) { + val selectedTime = getTimeFromPoint(e.x, e.y) + if (selectedTime != null) { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) + emptyViewLongPressListener!!.onEmptyViewLongPress(selectedTime) + } + } + } + } + + private val numberOfPeriods: Int + get() = ((mMaxTime - mMinTime) * (60.0 / timeColumnResolution)).toInt() + + private val yMinLimit: Float + get() = -(((mHourHeight * (mMaxTime - mMinTime)).toFloat() + + mHeaderHeight + + (mHeaderRowPadding * 2).toFloat() + + mHeaderMarginBottom + + mTimeTextHeight / 2) - height) + + private val yMaxLimit: Float + get() = 0f + + private val xMinLimit: Float + get() { + if (maxDate == null) { + return Integer.MIN_VALUE.toFloat() + } else { + val date = maxDate!!.clone() as Calendar + date.add(Calendar.DATE, 1 - realNumberOfVisibleDays) + while (date.before(minDate)) { + date.add(Calendar.DATE, 1) + } + + return getXOriginForDate(date) + } + } + + private val xMaxLimit: Float + get() = if (minDate == null) { + Integer.MAX_VALUE.toFloat() + } else { + getXOriginForDate(minDate!!) + } + + private val minHourOffset: Int + get() = mHourHeight * mMinTime + + private// Calculate top. + val eventsTop: Float + get() = mCurrentOrigin.y + mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mHeaderMarginBottom + mTimeTextHeight / 2 + mEventMarginVertical.toFloat() - minHourOffset + + private val leftDaysWithGaps: Int + get() = (-Math.ceil((mCurrentOrigin.x / (mWidthPerDay + mColumnGap)).toDouble())).toInt() + + private val xStartPixel: Float + get() = mCurrentOrigin.x + (mWidthPerDay + mColumnGap) * leftDaysWithGaps + + mHeaderColumnWidth + + var monthChangeListener: MonthLoader.MonthChangeListener? + get() = if (weekViewLoader is MonthLoader) (weekViewLoader as MonthLoader).onMonthChangeListener else null + set(monthChangeListener) { + this.weekViewLoader = MonthLoader(monthChangeListener) + } + + /** + * Get the interpreter which provides the text to show in the header column and the header row. + * + * @return The date, time interpreter. + */ + /** + * Set the interpreter which provides the text to show in the header column and the header row. + * + * @param dateTimeInterpreter The date, time interpreter. + */ + // Refresh time column width. + var dateTimeInterpreter: DateTimeInterpreter + get() { + if (mDateTimeInterpreter == null) { + val calendar = Calendar.getInstance() + calendar.set(Calendar.MINUTE, 0) + calendar.set(Calendar.SECOND, 0) + calendar.set(Calendar.MILLISECOND, 0) + val dateFormat = DateFormat.getTimeFormat(context) + ?: SimpleDateFormat("HH:mm", Locale.getDefault()) + val format = SimpleDateFormat(" M/d", Locale.getDefault()) + mDateTimeInterpreter = object : DateTimeInterpreter { + override fun interpretTime(hour: Int, minutes: Int): String { + calendar.set(Calendar.HOUR_OF_DAY, hour) + calendar.set(Calendar.MINUTE, minutes) + return dateFormat.format(calendar.time) + } + + override fun interpretDate(date: Calendar): String { + var weekday = DateUtils.getDayOfWeekString(date.get(Calendar.DAY_OF_WEEK), DateUtils.LENGTH_SHORT) + if (mDayNameLength == LENGTH_SHORT) { + val dayOfWeekString = DateUtils.getDayOfWeekString(date.get(Calendar.DAY_OF_WEEK), DateUtils.LENGTH_SHORTEST) + weekday = dayOfWeekString + } + return weekday + format.format(date.time) + } + } + } + return mDateTimeInterpreter!! + } + set(dateTimeInterpreter) { + this.mDateTimeInterpreter = dateTimeInterpreter + initTextTimeWidth() + } + + + /** + * Get the real number of visible days + * If the amount of days between max date and min date is smaller, that value is returned + * + * @return The real number of visible days + */ + val realNumberOfVisibleDays: Int + get() = if (minDate == null || maxDate == null) numberOfVisibleDays else Math.min(mNumberOfVisibleDays, daysBetween(minDate!!, maxDate!!) + 1) + + /** + * Get the number of visible days + * + * @return The set number of visible days. + */ + /** + * Set the number of visible days in a week. + * + * @param numberOfVisibleDays The number of visible days in a week. + */ + var numberOfVisibleDays: Int + get() = mNumberOfVisibleDays + set(numberOfVisibleDays) { + this.mNumberOfVisibleDays = numberOfVisibleDays + resetHomeDate() + mCurrentOrigin.x = 0f + mCurrentOrigin.y = 0f + invalidate() + } + + var hourHeight: Int + get() = mHourHeight + set(hourHeight) { + mNewHourHeight = hourHeight + invalidate() + } + + var columnGap: Int + get() = mColumnGap + set(columnGap) { + mColumnGap = columnGap + invalidate() + } + + /** + * Set the first day of the week. First day of the week is used only when the week view is first + * drawn. It does not of any effect after user starts scrolling horizontally. + * + * + * **Note:** This method will only work if the week view is set to display more than 6 days at + * once. + * + * + * @param firstDayOfWeek The supported values are [java.util.Calendar.SUNDAY], + * [java.util.Calendar.MONDAY], [java.util.Calendar.TUESDAY], + * [java.util.Calendar.WEDNESDAY], [java.util.Calendar.THURSDAY], + * [java.util.Calendar.FRIDAY]. + */ + var firstDayOfWeek: Int + get() = mFirstDayOfWeek + set(firstDayOfWeek) { + mFirstDayOfWeek = firstDayOfWeek + invalidate() + } + + var textSize: Int + get() = mTextSize + set(textSize) { + mTextSize = textSize + mTodayHeaderTextPaint!!.textSize = mTextSize.toFloat() + mHeaderTextPaint!!.textSize = mTextSize.toFloat() + mTimeTextPaint!!.textSize = mTextSize.toFloat() + invalidate() + } + + var headerColumnPadding: Int + get() = mHeaderColumnPadding + set(headerColumnPadding) { + mHeaderColumnPadding = headerColumnPadding + invalidate() + } + + var headerColumnTextColor: Int + get() = mHeaderColumnTextColor + set(headerColumnTextColor) { + mHeaderColumnTextColor = headerColumnTextColor + mHeaderTextPaint!!.color = mHeaderColumnTextColor + mTimeTextPaint!!.color = mHeaderColumnTextColor + invalidate() + } + + var headerRowPadding: Int + get() = mHeaderRowPadding + set(headerRowPadding) { + mHeaderRowPadding = headerRowPadding + invalidate() + } + + var headerRowBackgroundColor: Int + get() = mHeaderRowBackgroundColor + set(headerRowBackgroundColor) { + mHeaderRowBackgroundColor = headerRowBackgroundColor + mHeaderBackgroundPaint!!.color = mHeaderRowBackgroundColor + invalidate() + } + + var dayBackgroundColor: Int + get() = mDayBackgroundColor + set(dayBackgroundColor) { + mDayBackgroundColor = dayBackgroundColor + mDayBackgroundPaint!!.color = mDayBackgroundColor + invalidate() + } + + var hourSeparatorColor: Int + get() = mHourSeparatorColor + set(hourSeparatorColor) { + mHourSeparatorColor = hourSeparatorColor + mHourSeparatorPaint!!.color = mHourSeparatorColor + invalidate() + } + + var todayBackgroundColor: Int + get() = mTodayBackgroundColor + set(todayBackgroundColor) { + mTodayBackgroundColor = todayBackgroundColor + mTodayBackgroundPaint!!.color = mTodayBackgroundColor + invalidate() + } + + var hourSeparatorHeight: Int + get() = mHourSeparatorHeight + set(hourSeparatorHeight) { + mHourSeparatorHeight = hourSeparatorHeight + mHourSeparatorPaint!!.strokeWidth = mHourSeparatorHeight.toFloat() + invalidate() + } + + var todayHeaderTextColor: Int + get() = mTodayHeaderTextColor + set(todayHeaderTextColor) { + mTodayHeaderTextColor = todayHeaderTextColor + mTodayHeaderTextPaint!!.color = mTodayHeaderTextColor + invalidate() + } + + var eventTextSize: Int + get() = mEventTextSize + set(eventTextSize) { + mEventTextSize = eventTextSize + mEventTextPaint!!.textSize = mEventTextSize.toFloat() + invalidate() + } + + var eventTextColor: Int + get() = mEventTextColor + set(eventTextColor) { + mEventTextColor = eventTextColor + mEventTextPaint!!.color = mEventTextColor + invalidate() + } + + var eventPadding: Int + get() = mEventPadding + set(eventPadding) { + mEventPadding = eventPadding + invalidate() + } + + var headerColumnBackgroundColor: Int + get() = mHeaderColumnBackgroundColor + set(headerColumnBackgroundColor) { + mHeaderColumnBackgroundColor = headerColumnBackgroundColor + mHeaderColumnBackgroundPaint!!.color = mHeaderColumnBackgroundColor + invalidate() + } + + var defaultEventColor: Int + get() = mDefaultEventColor + set(defaultEventColor) { + mDefaultEventColor = defaultEventColor + invalidate() + } + + var newEventColor: Int + get() = mNewEventColor + set(defaultNewEventColor) { + mNewEventColor = defaultNewEventColor + invalidate() + } + + var newEventId: Int + @Deprecated("") + get() = Integer.parseInt(newEventIdentifier) + @Deprecated("") + set(newEventId) { + this.newEventIdentifier = newEventId.toString() + } + + /** + * **Note:** Use [.setDateTimeInterpreter] and + * [.getDateTimeInterpreter] instead. + * + * @return Either long or short day name is being used. + */ + /** + * Set the length of the day name displayed in the header row. Example of short day names is + * 'M' for 'Monday' and example of long day names is 'Mon' for 'Monday'. + * + * + * **Note:** Use [.setDateTimeInterpreter] instead. + * + * + * @param length Supported values are [com.alamkanak.weekview.WeekView.LENGTH_SHORT] and + * [com.alamkanak.weekview.WeekView.LENGTH_LONG]. + */ + var dayNameLength: Int + @Deprecated("") + get() = mDayNameLength + @Deprecated("") + set(length) { + if (length != LENGTH_LONG && length != LENGTH_SHORT) { + throw IllegalArgumentException("length parameter must be either LENGTH_LONG or LENGTH_SHORT") + } + this.mDayNameLength = length + } + + /** + * Set the gap between overlapping events. + * + * @param overlappingEventGap The gap between overlapping events. + */ + var overlappingEventGap: Int + get() = mOverlappingEventGap + set(overlappingEventGap) { + this.mOverlappingEventGap = overlappingEventGap + invalidate() + } + + /** + * Set the top and bottom margin of the event. The event will release this margin from the top + * and bottom edge. This margin is useful for differentiation consecutive events. + * + * @param eventMarginVertical The top and bottom margin. + */ + var eventMarginVertical: Int + get() = mEventMarginVertical + set(eventMarginVertical) { + this.mEventMarginVertical = eventMarginVertical + invalidate() + } + + /** + * Whether weekends should have a background color different from the normal day background + * color. The weekend background colors are defined by the attributes + * `futureWeekendBackgroundColor` and `pastWeekendBackgroundColor`. + * + * @return True if weekends should have different background colors. + */ + /** + * Set whether weekends should have a background color different from the normal day background + * color. The weekend background colors are defined by the attributes + * `futureWeekendBackgroundColor` and `pastWeekendBackgroundColor`. + * + * @param showDistinctWeekendColor True if weekends should have different background colors. + */ + var isShowDistinctWeekendColor: Boolean + get() = mShowDistinctWeekendColor + set(showDistinctWeekendColor) { + this.mShowDistinctWeekendColor = showDistinctWeekendColor + invalidate() + } + + /** + * Whether past and future days should have two different background colors. The past and + * future day colors are defined by the attributes `futureBackgroundColor` and + * `pastBackgroundColor`. + * + * @return True if past and future days should have two different background colors. + */ + /** + * Set whether weekends should have a background color different from the normal day background + * color. The past and future day colors are defined by the attributes `futureBackgroundColor` + * and `pastBackgroundColor`. + * + * @param showDistinctPastFutureColor True if past and future should have two different + * background colors. + */ + var isShowDistinctPastFutureColor: Boolean + get() = mShowDistinctPastFutureColor + set(showDistinctPastFutureColor) { + this.mShowDistinctPastFutureColor = showDistinctPastFutureColor + invalidate() + } + + /** + * Get whether "now" line should be displayed. "Now" line is defined by the attributes + * `nowLineColor` and `nowLineThickness`. + * + * @return True if "now" line should be displayed. + */ + /** + * Set whether "now" line should be displayed. "Now" line is defined by the attributes + * `nowLineColor` and `nowLineThickness`. + * + * @param showNowLine True if "now" line should be displayed. + */ + var isShowNowLine: Boolean + get() = mShowNowLine + set(showNowLine) { + this.mShowNowLine = showNowLine + invalidate() + } + + /** + * Get the "now" line color. + * + * @return The color of the "now" line. + */ + /** + * Set the "now" line color. + * + * @param nowLineColor The color of the "now" line. + */ + var nowLineColor: Int + get() = mNowLineColor + set(nowLineColor) { + this.mNowLineColor = nowLineColor + invalidate() + } + + /** + * Get the "now" line thickness. + * + * @return The thickness of the "now" line. + */ + /** + * Set the "now" line thickness. + * + * @param nowLineThickness The thickness of the "now" line. + */ + var nowLineThickness: Int + get() = mNowLineThickness + set(nowLineThickness) { + this.mNowLineThickness = nowLineThickness + invalidate() + } + + /* + * Get focus point + * 0 = top of view, 1 = bottom of view + * The focused point (multiplier of the view height) where the week view is zoomed around. + * This point will not move while zooming. + * @return focus point + */ + /** + * Set focus point + * 0 = top of view, 1 = bottom of view + * The focused point (multiplier of the view height) where the week view is zoomed around. + * This point will not move while zooming. + * + * @param zoomFocusPoint the new zoomFocusPoint + */ + var zoomFocusPoint: Float + get() = mZoomFocusPoint + set(zoomFocusPoint) { + if (0 > zoomFocusPoint || zoomFocusPoint > 1) + throw IllegalStateException("The zoom focus point percentage has to be between 0 and 1") + mZoomFocusPoint = zoomFocusPoint + } + + var pastBackgroundColor: Int + get() = mPastBackgroundColor + set(pastBackgroundColor) { + this.mPastBackgroundColor = pastBackgroundColor + mPastBackgroundPaint!!.color = mPastBackgroundColor + } + + var futureBackgroundColor: Int + get() = mFutureBackgroundColor + set(futureBackgroundColor) { + this.mFutureBackgroundColor = futureBackgroundColor + mFutureBackgroundPaint!!.color = mFutureBackgroundColor + } + + var pastWeekendBackgroundColor: Int + get() = mPastWeekendBackgroundColor + set(pastWeekendBackgroundColor) { + this.mPastWeekendBackgroundColor = pastWeekendBackgroundColor + this.mPastWeekendBackgroundPaint!!.color = mPastWeekendBackgroundColor + } + + var futureWeekendBackgroundColor: Int + get() = mFutureWeekendBackgroundColor + set(futureWeekendBackgroundColor) { + this.mFutureWeekendBackgroundColor = futureWeekendBackgroundColor + this.mFutureWeekendBackgroundPaint!!.color = mFutureWeekendBackgroundColor + } + + var isScrollNumberOfVisibleDays: Boolean + get() = this.mIsScrollNumberOfVisibleDays + set(scrollNumberOfVisibleDays) { + this.mIsScrollNumberOfVisibleDays = scrollNumberOfVisibleDays + invalidate() + } + + /** + * Get the first hour that is visible on the screen. + * + * @return The first hour that is visible. + */ + val firstVisibleHour: Double + get() = (-mCurrentOrigin.y / mHourHeight).toDouble() + + private enum class Direction { + NONE, LEFT, RIGHT, VERTICAL + } + + init { + + // Get the attribute values (if any). + val a = mContext.theme.obtainStyledAttributes(attrs, R.styleable.WeekView, 0, 0) + try { + mFirstDayOfWeek = a.getInteger(R.styleable.WeekView_firstDayOfWeek, mFirstDayOfWeek) + mHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourHeight, mHourHeight) + minHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_minHourHeight, minHourHeight) + mEffectiveMinHourHeight = minHourHeight + maxHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_maxHourHeight, maxHourHeight) + mTextSize = a.getDimensionPixelSize(R.styleable.WeekView_textSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize.toFloat(), mContext.resources.displayMetrics).toInt()) + mHeaderColumnPadding = a.getDimensionPixelSize(R.styleable.WeekView_headerColumnPadding, mHeaderColumnPadding) + mColumnGap = a.getDimensionPixelSize(R.styleable.WeekView_columnGap, mColumnGap) + mHeaderColumnTextColor = a.getColor(R.styleable.WeekView_headerColumnTextColor, mHeaderColumnTextColor) + mNumberOfVisibleDays = a.getInteger(R.styleable.WeekView_noOfVisibleDays, mNumberOfVisibleDays) + isShowFirstDayOfWeekFirst = a.getBoolean(R.styleable.WeekView_showFirstDayOfWeekFirst, isShowFirstDayOfWeekFirst) + mHeaderRowPadding = a.getDimensionPixelSize(R.styleable.WeekView_headerRowPadding, mHeaderRowPadding) + mHeaderRowBackgroundColor = a.getColor(R.styleable.WeekView_headerRowBackgroundColor, mHeaderRowBackgroundColor) + mDayBackgroundColor = a.getColor(R.styleable.WeekView_dayBackgroundColor, mDayBackgroundColor) + mFutureBackgroundColor = a.getColor(R.styleable.WeekView_futureBackgroundColor, mFutureBackgroundColor) + mPastBackgroundColor = a.getColor(R.styleable.WeekView_pastBackgroundColor, mPastBackgroundColor) + mFutureWeekendBackgroundColor = a.getColor(R.styleable.WeekView_futureWeekendBackgroundColor, mFutureBackgroundColor) // If not set, use the same color as in the week + mPastWeekendBackgroundColor = a.getColor(R.styleable.WeekView_pastWeekendBackgroundColor, mPastBackgroundColor) + mNowLineColor = a.getColor(R.styleable.WeekView_nowLineColor, mNowLineColor) + mNowLineThickness = a.getDimensionPixelSize(R.styleable.WeekView_nowLineThickness, mNowLineThickness) + mHourSeparatorColor = a.getColor(R.styleable.WeekView_hourSeparatorColor, mHourSeparatorColor) + mTodayBackgroundColor = a.getColor(R.styleable.WeekView_todayBackgroundColor, mTodayBackgroundColor) + mHourSeparatorHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourSeparatorHeight, mHourSeparatorHeight) + mTodayHeaderTextColor = a.getColor(R.styleable.WeekView_todayHeaderTextColor, mTodayHeaderTextColor) + mEventTextSize = a.getDimensionPixelSize(R.styleable.WeekView_eventTextSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mEventTextSize.toFloat(), mContext.resources.displayMetrics).toInt()) + mEventTextColor = a.getColor(R.styleable.WeekView_eventTextColor, mEventTextColor) + mNewEventColor = a.getColor(R.styleable.WeekView_newEventColor, mNewEventColor) + newEventIconDrawable = a.getDrawable(R.styleable.WeekView_newEventIconResource) + // For backward compatibility : Set "mNewEventIdentifier" if the attribute is "WeekView_newEventId" of type int + newEventId = a.getInt(R.styleable.WeekView_newEventId, Integer.parseInt(newEventIdentifier)) + newEventIdentifier = if (a.getString(R.styleable.WeekView_newEventIdentifier) != null) a.getString(R.styleable.WeekView_newEventIdentifier) else newEventIdentifier + newEventLengthInMinutes = a.getInt(R.styleable.WeekView_newEventLengthInMinutes, newEventLengthInMinutes) + newEventTimeResolutionInMinutes = a.getInt(R.styleable.WeekView_newEventTimeResolutionInMinutes, newEventTimeResolutionInMinutes) + mEventPadding = a.getDimensionPixelSize(R.styleable.WeekView_eventPadding, mEventPadding) + mHeaderColumnBackgroundColor = a.getColor(R.styleable.WeekView_headerColumnBackground, mHeaderColumnBackgroundColor) + mDayNameLength = a.getInteger(R.styleable.WeekView_dayNameLength, mDayNameLength) + mOverlappingEventGap = a.getDimensionPixelSize(R.styleable.WeekView_overlappingEventGap, mOverlappingEventGap) + mEventMarginVertical = a.getDimensionPixelSize(R.styleable.WeekView_eventMarginVertical, mEventMarginVertical) + xScrollingSpeed = a.getFloat(R.styleable.WeekView_xScrollingSpeed, xScrollingSpeed) + eventCornerRadius = a.getDimensionPixelSize(R.styleable.WeekView_eventCornerRadius, eventCornerRadius) + mShowDistinctPastFutureColor = a.getBoolean(R.styleable.WeekView_showDistinctPastFutureColor, mShowDistinctPastFutureColor) + mShowDistinctWeekendColor = a.getBoolean(R.styleable.WeekView_showDistinctWeekendColor, mShowDistinctWeekendColor) + mShowNowLine = a.getBoolean(R.styleable.WeekView_showNowLine, mShowNowLine) + isHorizontalFlingEnabled = a.getBoolean(R.styleable.WeekView_horizontalFlingEnabled, isHorizontalFlingEnabled) + isVerticalFlingEnabled = a.getBoolean(R.styleable.WeekView_verticalFlingEnabled, isVerticalFlingEnabled) + allDayEventHeight = a.getDimensionPixelSize(R.styleable.WeekView_allDayEventHeight, allDayEventHeight) + mZoomFocusPoint = a.getFraction(R.styleable.WeekView_zoomFocusPoint, 1, 1, mZoomFocusPoint) + isZoomFocusPointEnabled = a.getBoolean(R.styleable.WeekView_zoomFocusPointEnabled, isZoomFocusPointEnabled) + scrollDuration = a.getInt(R.styleable.WeekView_scrollDuration, scrollDuration) + timeColumnResolution = a.getInt(R.styleable.WeekView_timeColumnResolution, timeColumnResolution) + mAutoLimitTime = a.getBoolean(R.styleable.WeekView_autoLimitTime, mAutoLimitTime) + mMinTime = a.getInt(R.styleable.WeekView_minTime, mMinTime) + mMaxTime = a.getInt(R.styleable.WeekView_maxTime, mMaxTime) + if (a.getBoolean(R.styleable.WeekView_dropListenerEnabled, false)) + this.enableDropListener() + minOverlappingMinutes = a.getInt(R.styleable.WeekView_minOverlappingMinutes, 0) + mIsScrollNumberOfVisibleDays = a.getBoolean(R.styleable.WeekView_isScrollNumberOfVisibleDays, false) + } finally { + a.recycle() + } + + init() + }// Hold references. + + private fun init() { + resetHomeDate() + + // Scrolling initialization. + mGestureDetector = GestureDetectorCompat(mContext, mGestureListener) + mScroller = OverScroller(mContext, FastOutLinearInInterpolator()) + + mMinimumFlingVelocity = ViewConfiguration.get(mContext).scaledMinimumFlingVelocity + mScaledTouchSlop = ViewConfiguration.get(mContext).scaledTouchSlop + + // Measure settings for time column. + mTimeTextPaint = Paint(Paint.ANTI_ALIAS_FLAG) + mTimeTextPaint!!.textAlign = Paint.Align.RIGHT + mTimeTextPaint!!.textSize = mTextSize.toFloat() + mTimeTextPaint!!.color = mHeaderColumnTextColor + + val rect = Rect() + val exampleTime = if (timeColumnResolution % 60 != 0) "00:00 PM" else "00 PM" + mTimeTextPaint!!.getTextBounds(exampleTime, 0, exampleTime.length, rect) + mTimeTextWidth = mTimeTextPaint!!.measureText(exampleTime) + mTimeTextHeight = rect.height().toFloat() + mHeaderMarginBottom = mTimeTextHeight / 2 + initTextTimeWidth() + + // Measure settings for header row. + mHeaderTextPaint = Paint(Paint.ANTI_ALIAS_FLAG) + mHeaderTextPaint!!.color = mHeaderColumnTextColor + mHeaderTextPaint!!.textAlign = Paint.Align.CENTER + mHeaderTextPaint!!.textSize = mTextSize.toFloat() + mHeaderTextPaint!!.getTextBounds(exampleTime, 0, exampleTime.length, rect) + mHeaderTextHeight = rect.height().toFloat() + mHeaderTextPaint!!.typeface = mTypeface + + + // Prepare header background paint. + mHeaderBackgroundPaint = Paint() + mHeaderBackgroundPaint!!.color = mHeaderRowBackgroundColor + + // Prepare day background color paint. + mDayBackgroundPaint = Paint() + mDayBackgroundPaint!!.color = mDayBackgroundColor + mFutureBackgroundPaint = Paint() + mFutureBackgroundPaint!!.color = mFutureBackgroundColor + mPastBackgroundPaint = Paint() + mPastBackgroundPaint!!.color = mPastBackgroundColor + mFutureWeekendBackgroundPaint = Paint() + mFutureWeekendBackgroundPaint!!.color = mFutureWeekendBackgroundColor + mPastWeekendBackgroundPaint = Paint() + mPastWeekendBackgroundPaint!!.color = mPastWeekendBackgroundColor + + // Prepare hour separator color paint. + mHourSeparatorPaint = Paint() + mHourSeparatorPaint!!.style = Paint.Style.STROKE + mHourSeparatorPaint!!.strokeWidth = mHourSeparatorHeight.toFloat() + mHourSeparatorPaint!!.color = mHourSeparatorColor + + // Prepare the "now" line color paint + mNowLinePaint = Paint() + mNowLinePaint!!.strokeWidth = mNowLineThickness.toFloat() + mNowLinePaint!!.color = mNowLineColor + + // Prepare today background color paint. + mTodayBackgroundPaint = Paint() + mTodayBackgroundPaint!!.color = mTodayBackgroundColor + + // Prepare today header text color paint. + mTodayHeaderTextPaint = Paint(Paint.ANTI_ALIAS_FLAG) + mTodayHeaderTextPaint!!.textAlign = Paint.Align.CENTER + mTodayHeaderTextPaint!!.textSize = mTextSize.toFloat() + mTodayHeaderTextPaint!!.typeface = mTypeface + + mTodayHeaderTextPaint!!.color = mTodayHeaderTextColor + + // Prepare event background color. + mEventBackgroundPaint = Paint() + mEventBackgroundPaint!!.color = Color.rgb(174, 208, 238) + // Prepare empty event background color. + mNewEventBackgroundPaint = Paint() + mNewEventBackgroundPaint!!.color = Color.rgb(60, 147, 217) + + // Prepare header column background color. + mHeaderColumnBackgroundPaint = Paint() + mHeaderColumnBackgroundPaint!!.color = mHeaderColumnBackgroundColor + + // Prepare event text size and color. + mEventTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG or Paint.LINEAR_TEXT_FLAG) + mEventTextPaint!!.style = Paint.Style.FILL + mEventTextPaint!!.color = mEventTextColor + mEventTextPaint!!.textSize = mEventTextSize.toFloat() + + // Set default event color. + mDefaultEventColor = Color.parseColor("#9fc6e7") + // Set default empty event color. + mNewEventColor = Color.parseColor("#3c93d9") + + mScaleDetector = ScaleGestureDetector(mContext, WeekViewGestureListener()) + } + + private fun resetHomeDate() { + var newHomeDate = today() + + if (minDate != null && newHomeDate.before(minDate)) { + newHomeDate = minDate!!.clone() as Calendar + } + if (maxDate != null && newHomeDate.after(maxDate)) { + newHomeDate = maxDate!!.clone() as Calendar + } + + if (maxDate != null) { + val date = maxDate!!.clone() as Calendar + date.add(Calendar.DATE, 1 - realNumberOfVisibleDays) + while (date.before(minDate)) { + date.add(Calendar.DATE, 1) + } + + if (newHomeDate.after(date)) { + newHomeDate = date + } + } + + mHomeDate = newHomeDate + } + + private fun getXOriginForDate(date: Calendar): Float { + return -daysBetween(mHomeDate!!, date) * (mWidthPerDay + mColumnGap) + } + + // fix rotation changes + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + mAreDimensionsInvalid = true + } + + /** + * Initialize time column width. Calculate value with all possible hours (supposed widest text). + */ + private fun initTextTimeWidth() { + mTimeTextWidth = 0f + for (i in 0 until numberOfPeriods) { + // Measure time string and get max width. + val time = dateTimeInterpreter.interpretTime(i, i % 2 * 30) + ?: throw IllegalStateException("A DateTimeInterpreter must not return null time") + mTimeTextWidth = Math.max(mTimeTextWidth, mTimeTextPaint!!.measureText(time)) + } + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + // Draw the header row. + drawHeaderRowAndEvents(canvas) + + // Draw the time column and all the axes/separators. + drawTimeColumnAndAxes(canvas) + } + + private fun calculateHeaderHeight() { + //Make sure the header is the right size (depends on AllDay events) + var containsAllDayEvent = false + if (mEventRects != null && mEventRects!!.size > 0) { + for (dayNumber in 0 until realNumberOfVisibleDays) { + val day = firstVisibleDay!!.clone() as Calendar + day.add(Calendar.DATE, dayNumber) + for (i in mEventRects!!.indices) { + + if (isSameDay(mEventRects!![i].event.startTime!!, day) && mEventRects!![i].event.isAllDay) { + containsAllDayEvent = true + break + } + } + if (containsAllDayEvent) { + break + } + } + } + if (containsAllDayEvent) { + mHeaderHeight = mHeaderTextHeight + (allDayEventHeight + mHeaderMarginBottom) + } else { + mHeaderHeight = mHeaderTextHeight + } + } + + private fun drawTimeColumnAndAxes(canvas: Canvas) { + // Draw the background color for the header column. + canvas.drawRect(0f, mHeaderHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, height.toFloat(), mHeaderColumnBackgroundPaint!!) + + // Clip to paint in left column only. + canvas.clipRect(0f, mHeaderHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, height.toFloat(), Region.Op.REPLACE) + + for (i in 0 until numberOfPeriods) { + // If we are showing half hours (eg. 5:30am), space the times out by half the hour height + // and need to provide 30 minutes on each odd period, otherwise, minutes is always 0. + val timeSpacing: Float + val minutes: Int + val hour: Int + + val timesPerHour = 60.0.toFloat() / timeColumnResolution + + timeSpacing = mHourHeight / timesPerHour + hour = mMinTime + i / timesPerHour.toInt() + minutes = i % timesPerHour.toInt() * (60 / timesPerHour.toInt()) + + + // Calculate the top of the rectangle where the time text will go + val top = mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mCurrentOrigin.y + timeSpacing * i + mHeaderMarginBottom + + // Get the time to be displayed, as a String. + val time = dateTimeInterpreter.interpretTime(hour, minutes) + ?: throw IllegalStateException("A DateTimeInterpreter must not return null time") + // Draw the text if its y position is not outside of the visible area. The pivot point of the text is the point at the bottom-right corner. + if (top < height) + canvas.drawText(time, mTimeTextWidth + mHeaderColumnPadding, top + mTimeTextHeight, mTimeTextPaint!!) + } + } + + private fun drawHeaderRowAndEvents(canvas: Canvas) { + // Calculate the available width for each day. + mHeaderColumnWidth = mTimeTextWidth + mHeaderColumnPadding * 2 + mWidthPerDay = width.toFloat() - mHeaderColumnWidth - (mColumnGap * (realNumberOfVisibleDays - 1)).toFloat() + mWidthPerDay = mWidthPerDay / realNumberOfVisibleDays + + calculateHeaderHeight() //Make sure the header is the right size (depends on AllDay events) + + val today = today() + + if (mAreDimensionsInvalid) { + mEffectiveMinHourHeight = Math.max(minHourHeight, ((height.toFloat() - mHeaderHeight - (mHeaderRowPadding * 2).toFloat() - mHeaderMarginBottom) / (mMaxTime - mMinTime)).toInt()) + + mAreDimensionsInvalid = false + if (mScrollToDay != null) + goToDate(mScrollToDay!!) + + mAreDimensionsInvalid = false + if (mScrollToHour >= 0) + goToHour(mScrollToHour) + + mScrollToDay = null + mScrollToHour = -1.0 + mAreDimensionsInvalid = false + } + if (mIsFirstDraw) { + mIsFirstDraw = false + + // If the week view is being drawn for the first time, then consider the first day of the week. + if (realNumberOfVisibleDays >= 7 && mHomeDate!!.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek && isShowFirstDayOfWeekFirst) { + val difference = mHomeDate!!.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek + mCurrentOrigin.x += (mWidthPerDay + mColumnGap) * difference + } + setLimitTime(mMinTime, mMaxTime) + } + + // Calculate the new height due to the zooming. + if (mNewHourHeight > 0) { + if (mNewHourHeight < mEffectiveMinHourHeight) + mNewHourHeight = mEffectiveMinHourHeight + else if (mNewHourHeight > maxHourHeight) + mNewHourHeight = maxHourHeight + + mHourHeight = mNewHourHeight + mNewHourHeight = -1 + } + + // If the new mCurrentOrigin.y is invalid, make it valid. + if (mCurrentOrigin.y < height.toFloat() - (mHourHeight * (mMaxTime - mMinTime)).toFloat() - mHeaderHeight - (mHeaderRowPadding * 2).toFloat() - mHeaderMarginBottom - mTimeTextHeight / 2) + mCurrentOrigin.y = height.toFloat() - (mHourHeight * (mMaxTime - mMinTime)).toFloat() - mHeaderHeight - (mHeaderRowPadding * 2).toFloat() - mHeaderMarginBottom - mTimeTextHeight / 2 + + // Don't put an "else if" because it will trigger a glitch when completely zoomed out and + // scrolling vertically. + if (mCurrentOrigin.y > 0) { + mCurrentOrigin.y = 0f + } + + val leftDaysWithGaps = leftDaysWithGaps + // Consider scroll offset. + val startFromPixel = xStartPixel + var startPixel = startFromPixel + + // Prepare to iterate for each day. + var day = today.clone() as Calendar + day.add(Calendar.HOUR_OF_DAY, 6) + + // Prepare to iterate for each hour to draw the hour lines. + var lineCount = ((height.toFloat() - mHeaderHeight - (mHeaderRowPadding * 2).toFloat() - + mHeaderMarginBottom) / mHourHeight).toInt() + 1 + + lineCount = lineCount * (realNumberOfVisibleDays + 1) + + val hourLines = FloatArray(lineCount * 4) + + // Clear the cache for event rectangles. + if (mEventRects != null) { + for (eventRect in mEventRects!!) { + eventRect.rectF = null + } + } + + // Clip to paint events only. + canvas.clipRect(mHeaderColumnWidth, mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mHeaderMarginBottom + mTimeTextHeight / 2, width.toFloat(), height.toFloat(), Region.Op.REPLACE) + + // Iterate through each day. + val oldFirstVisibleDay = firstVisibleDay + firstVisibleDay = mHomeDate!!.clone() as Calendar + firstVisibleDay!!.add(Calendar.DATE, -Math.round(mCurrentOrigin.x / (mWidthPerDay + mColumnGap))) + if (firstVisibleDay != oldFirstVisibleDay && scrollListener != null) { + scrollListener!!.onFirstVisibleDayChanged(firstVisibleDay!!, oldFirstVisibleDay) + } + + if (mAutoLimitTime) { + val days = ArrayList() + for (dayNumber in leftDaysWithGaps + 1..leftDaysWithGaps + realNumberOfVisibleDays) { + day = mHomeDate!!.clone() as Calendar + day.add(Calendar.DATE, dayNumber - 1) + days.add(day) + } + limitEventTime(days) + } + + for (dayNumber in leftDaysWithGaps + 1..leftDaysWithGaps + realNumberOfVisibleDays + 1) { + + // Check if the day is today. + day = mHomeDate!!.clone() as Calendar + lastVisibleDay = day.clone() as Calendar + day.add(Calendar.DATE, dayNumber - 1) + lastVisibleDay!!.add(Calendar.DATE, dayNumber - 2) + val isToday = isSameDay(day, today) + + // Don't draw days which are outside requested range + if (!dateIsValid(day)) { + continue + } + + // Get more events if necessary. We want to store the events 3 months beforehand. Get + // events only when it is the first iteration of the loop. + if (mEventRects == null || mRefreshEvents || + dayNumber == leftDaysWithGaps + 1 && mFetchedPeriod != weekViewLoader!!.toWeekViewPeriodIndex(day).toInt() && + Math.abs(mFetchedPeriod - weekViewLoader!!.toWeekViewPeriodIndex(day)) > 0.5) { + getMoreEvents(day) + mRefreshEvents = false + } + + // Draw background color for each day. + val start = if (startPixel < mHeaderColumnWidth) mHeaderColumnWidth else startPixel + if (mWidthPerDay + startPixel - start > 0) { + if (mShowDistinctPastFutureColor) { + val isWeekend = day.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || day.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY + val pastPaint = if (isWeekend && mShowDistinctWeekendColor) mPastWeekendBackgroundPaint else mPastBackgroundPaint + val futurePaint = if (isWeekend && mShowDistinctWeekendColor) mFutureWeekendBackgroundPaint else mFutureBackgroundPaint + val startY = mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mTimeTextHeight / 2 + mHeaderMarginBottom + mCurrentOrigin.y + + if (isToday) { + val now = Calendar.getInstance() + val beforeNow = (now.get(Calendar.HOUR_OF_DAY) - mMinTime + now.get(Calendar.MINUTE) / 60.0f) * mHourHeight + canvas.drawRect(start, startY, startPixel + mWidthPerDay, startY + beforeNow, pastPaint!!) + canvas.drawRect(start, startY + beforeNow, startPixel + mWidthPerDay, height.toFloat(), futurePaint!!) + } else if (day.before(today)) { + canvas.drawRect(start, startY, startPixel + mWidthPerDay, height.toFloat(), pastPaint!!) + } else { + canvas.drawRect(start, startY, startPixel + mWidthPerDay, height.toFloat(), futurePaint!!) + } + } else { + canvas.drawRect(start, mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mTimeTextHeight / 2 + mHeaderMarginBottom, startPixel + mWidthPerDay, height.toFloat(), if (isToday) mTodayBackgroundPaint else mDayBackgroundPaint) + } + } + + // Prepare the separator lines for hours. + var i = 0 + for (hourNumber in mMinTime until mMaxTime) { + val top = mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mCurrentOrigin.y + (mHourHeight * (hourNumber - mMinTime)).toFloat() + mTimeTextHeight / 2 + mHeaderMarginBottom + if (top > mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mTimeTextHeight / 2 + mHeaderMarginBottom - mHourSeparatorHeight && top < height && startPixel + mWidthPerDay - start > 0) { + hourLines[i * 4] = start + hourLines[i * 4 + 1] = top + hourLines[i * 4 + 2] = startPixel + mWidthPerDay + hourLines[i * 4 + 3] = top + i++ + } + } + + // Draw the lines for hours. + canvas.drawLines(hourLines, mHourSeparatorPaint!!) + + // Draw the events. + drawEvents(day, startPixel, canvas) + + // Draw the line at the current time. + if (mShowNowLine && isToday) { + val startY = mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mTimeTextHeight / 2 + mHeaderMarginBottom + mCurrentOrigin.y + val now = Calendar.getInstance() + val beforeNow = (now.get(Calendar.HOUR_OF_DAY) - mMinTime + now.get(Calendar.MINUTE) / 60.0f) * mHourHeight + val top = startY + beforeNow + canvas.drawLine(start, top, startPixel + mWidthPerDay, top, mNowLinePaint!!) + } + + // In the next iteration, start from the next day. + startPixel += mWidthPerDay + mColumnGap + } + + // Hide everything in the first cell (top left corner). + canvas.clipRect(0f, 0f, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2, Region.Op.REPLACE) + canvas.drawRect(0f, 0f, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint!!) + + // Clip to paint header row only. + canvas.clipRect(mHeaderColumnWidth, 0f, width.toFloat(), mHeaderHeight + mHeaderRowPadding * 2, Region.Op.REPLACE) + + // Draw the header background. + canvas.drawRect(0f, 0f, width.toFloat(), mHeaderHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint!!) + + // Draw the header row texts. + startPixel = startFromPixel + for (dayNumber in leftDaysWithGaps + 1..leftDaysWithGaps + realNumberOfVisibleDays + 1) { + // Check if the day is today. + day = mHomeDate!!.clone() as Calendar + day.add(Calendar.DATE, dayNumber - 1) + val isToday = isSameDay(day, today) + + // Don't draw days which are outside requested range + if (!dateIsValid(day)) + continue + + // Draw the day labels. + val dayLabel = dateTimeInterpreter.interpretDate(day) + ?: throw IllegalStateException("A DateTimeInterpreter must not return null date") + canvas.drawText(dayLabel, startPixel + mWidthPerDay / 2, mHeaderTextHeight + mHeaderRowPadding, if (isToday) mTodayHeaderTextPaint else mHeaderTextPaint) + drawAllDayEvents(day, startPixel, canvas) + startPixel += mWidthPerDay + mColumnGap + } + + } + + /** + * Get the time and date where the user clicked on. + * + * @param x The x position of the touch event. + * @param y The y position of the touch event. + * @return The time and date at the clicked position. + */ + private fun getTimeFromPoint(x: Float, y: Float): Calendar? { + val leftDaysWithGaps = leftDaysWithGaps + var startPixel = xStartPixel + for (dayNumber in leftDaysWithGaps + 1..leftDaysWithGaps + realNumberOfVisibleDays + 1) { + val start = if (startPixel < mHeaderColumnWidth) mHeaderColumnWidth else startPixel + if (mWidthPerDay + startPixel - start > 0 && x > start && x < startPixel + mWidthPerDay) { + val day = mHomeDate!!.clone() as Calendar + day.add(Calendar.DATE, dayNumber - 1) + val pixelsFromZero = (y - mCurrentOrigin.y - mHeaderHeight + - (mHeaderRowPadding * 2).toFloat() - mTimeTextHeight / 2 - mHeaderMarginBottom) + val hour = (pixelsFromZero / mHourHeight).toInt() + val minute = (60 * (pixelsFromZero - hour * mHourHeight) / mHourHeight).toInt() + day.add(Calendar.HOUR_OF_DAY, hour + mMinTime) + day.set(Calendar.MINUTE, minute) + return day + } + startPixel += mWidthPerDay + mColumnGap + } + return null + } + + /** + * limit current time of event by update mMinTime & mMaxTime + * find smallest of start time & latest of end time + */ + private fun limitEventTime(dates: List) { + if (mEventRects != null && mEventRects!!.size > 0) { + var startTime: Calendar? = null + var endTime: Calendar? = null + + for (eventRect in mEventRects!!) { + for (date in dates) { + if (isSameDay(eventRect.event.startTime!!, date) && !eventRect.event.isAllDay) { + + if (startTime == null || getPassedMinutesInDay(startTime) > getPassedMinutesInDay(eventRect.event.startTime!!)) { + startTime = eventRect.event.startTime + } + + if (endTime == null || getPassedMinutesInDay(endTime) < getPassedMinutesInDay(eventRect.event.endTime!!)) { + endTime = eventRect.event.endTime + } + } + } + } + + if (startTime != null && endTime != null && startTime.before(endTime)) { + setLimitTime(Math.max(0, startTime.get(Calendar.HOUR_OF_DAY)), + Math.min(24, endTime.get(Calendar.HOUR_OF_DAY) + 1)) + return + } + } + } + + /** + * Draw all the events of a particular day. + * + * @param date The day. + * @param startFromPixel The left position of the day area. The events will never go any left from this value. + * @param canvas The canvas to draw upon. + */ + private fun drawEvents(date: Calendar, startFromPixel: Float, canvas: Canvas) { + if (mEventRects != null && mEventRects!!.size > 0) { + for (i in mEventRects!!.indices) { + if (isSameDay(mEventRects!![i].event.startTime!!, date) && !mEventRects!![i].event.isAllDay) { + val top = mHourHeight * mEventRects!![i].top / 60 + eventsTop + val bottom = mHourHeight * mEventRects!![i].bottom / 60 + eventsTop + + // Calculate left and right. + var left = startFromPixel + mEventRects!![i].left * mWidthPerDay + if (left < startFromPixel) + left += mOverlappingEventGap.toFloat() + var right = left + mEventRects!![i].width * mWidthPerDay + if (right < startFromPixel + mWidthPerDay) + right -= mOverlappingEventGap.toFloat() + + // Draw the event and the event name on top of it. + if (left < right && + left < width && + top < height && + right > mHeaderColumnWidth && + bottom > mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mTimeTextHeight / 2 + mHeaderMarginBottom) { + mEventRects!![i].rectF = RectF(left, top, right, bottom) + mEventBackgroundPaint!!.color = if (mEventRects!![i].event.color == 0) mDefaultEventColor else mEventRects!![i].event.color + mEventBackgroundPaint!!.shader = mEventRects!![i].event.shader + canvas.drawRoundRect(mEventRects!![i].rectF!!, eventCornerRadius.toFloat(), eventCornerRadius.toFloat(), mEventBackgroundPaint!!) + var topToUse = top + if (mEventRects!![i].event.startTime!!.get(Calendar.HOUR_OF_DAY) < mMinTime) + topToUse = mHourHeight * getPassedMinutesInDay(mMinTime, 0) / 60 + eventsTop + + if (newEventIdentifier != mEventRects!![i].event.identifier) + drawEventTitle(mEventRects!![i].event, mEventRects!![i].rectF!!, canvas, topToUse, left) + else + drawEmptyImage(mEventRects!![i].event, mEventRects!![i].rectF!!, canvas, topToUse, left) + + } else + mEventRects!![i].rectF = null + } + } + } + } + + /** + * Draw all the Allday-events of a particular day. + * + * @param date The day. + * @param startFromPixel The left position of the day area. The events will never go any left from this value. + * @param canvas The canvas to draw upon. + */ + private fun drawAllDayEvents(date: Calendar, startFromPixel: Float, canvas: Canvas) { + if (mEventRects != null && mEventRects!!.size > 0) { + for (i in mEventRects!!.indices) { + if (isSameDay(mEventRects!![i].event.startTime!!, date) && mEventRects!![i].event.isAllDay) { + + // Calculate top. + val top = (mHeaderRowPadding * 2).toFloat() + mHeaderMarginBottom + +mTimeTextHeight / 2 + mEventMarginVertical.toFloat() + + // Calculate bottom. + val bottom = top + mEventRects!![i].bottom + + // Calculate left and right. + var left = startFromPixel + mEventRects!![i].left * mWidthPerDay + if (left < startFromPixel) + left += mOverlappingEventGap.toFloat() + var right = left + mEventRects!![i].width * mWidthPerDay + if (right < startFromPixel + mWidthPerDay) + right -= mOverlappingEventGap.toFloat() + + // Draw the event and the event name on top of it. + if (left < right && + left < width && + top < height && + right > mHeaderColumnWidth && + bottom > 0) { + mEventRects!![i].rectF = RectF(left, top, right, bottom) + mEventBackgroundPaint!!.color = if (mEventRects!![i].event.color == 0) mDefaultEventColor else mEventRects!![i].event.color + mEventBackgroundPaint!!.shader = mEventRects!![i].event.shader + canvas.drawRoundRect(mEventRects!![i].rectF!!, eventCornerRadius.toFloat(), eventCornerRadius.toFloat(), mEventBackgroundPaint!!) + drawEventTitle(mEventRects!![i].event, mEventRects!![i].rectF!!, canvas, top, left) + } else + mEventRects!![i].rectF = null + } + } + } + } + + /** + * Draw the name of the event on top of the event rectangle. + * + * @param event The event of which the title (and location) should be drawn. + * @param rect The rectangle on which the text is to be drawn. + * @param canvas The canvas to draw upon. + * @param originalTop The original top position of the rectangle. The rectangle may have some of its portion outside of the visible area. + * @param originalLeft The original left position of the rectangle. The rectangle may have some of its portion outside of the visible area. + */ + private fun drawEventTitle(event: WeekViewEvent, rect: RectF, canvas: Canvas, originalTop: Float, originalLeft: Float) { + if (rect.right - rect.left - (mEventPadding * 2).toFloat() < 0) return + if (rect.bottom - rect.top - (mEventPadding * 2).toFloat() < 0) return + + // Prepare the name of the event. + val bob = SpannableStringBuilder() + if (!TextUtils.isEmpty(event.name)) { + bob.append(event.name) + bob.setSpan(StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length, 0) + } + // Prepare the location of the event. + if (!TextUtils.isEmpty(event.location)) { + if (bob.length > 0) + bob.append(' ') + bob.append(event.location) + } + + val availableHeight = (rect.bottom - originalTop - (mEventPadding * 2).toFloat()).toInt() + val availableWidth = (rect.right - originalLeft - (mEventPadding * 2).toFloat()).toInt() + + // Get text color if necessary + if (textColorPicker != null) { + mEventTextPaint!!.color = textColorPicker!!.getTextColor(event) + } + // Get text dimensions. + var textLayout = StaticLayout(bob, mEventTextPaint, availableWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false) + if (textLayout.lineCount > 0) { + val lineHeight = textLayout.height / textLayout.lineCount + + if (availableHeight >= lineHeight) { + // Calculate available number of line counts. + var availableLineCount = availableHeight / lineHeight + do { + // Ellipsize text to fit into event rect. + if (newEventIdentifier != event.identifier) + textLayout = StaticLayout(TextUtils.ellipsize(bob, mEventTextPaint!!, (availableLineCount * availableWidth).toFloat(), TextUtils.TruncateAt.END), mEventTextPaint, (rect.right - originalLeft - (mEventPadding * 2).toFloat()).toInt(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false) + + // Reduce line count. + availableLineCount-- + + // Repeat until text is short enough. + } while (textLayout.height > availableHeight) + + // Draw text. + canvas.save() + canvas.translate(originalLeft + mEventPadding, originalTop + mEventPadding) + textLayout.draw(canvas) + canvas.restore() + } + } + } + + /** + * Draw the text on top of the rectangle in the empty event. + */ + private fun drawEmptyImage(event: WeekViewEvent, rect: RectF, canvas: Canvas, originalTop: Float, originalLeft: Float) { + val size = Math.max(1, Math.floor(Math.min(0.8 * rect.height(), 0.8 * rect.width())).toInt()) + if (newEventIconDrawable == null) + newEventIconDrawable = resources.getDrawable(android.R.drawable.ic_input_add) + var icon = (newEventIconDrawable as BitmapDrawable).bitmap + icon = Bitmap.createScaledBitmap(icon, size, size, false) + canvas.drawBitmap(icon, originalLeft + (rect.width() - icon.width) / 2, originalTop + (rect.height() - icon.height) / 2, Paint()) + + } + + /** + * A class to hold reference to the events and their visual representation. An EventRect is + * actually the rectangle that is drawn on the calendar for a given event. There may be more + * than one rectangle for a single event (an event that expands more than one day). In that + * case two instances of the EventRect will be used for a single event. The given event will be + * stored in "originalEvent". But the event that corresponds to rectangle the rectangle + * instance will be stored in "event". + */ + private inner class EventRect + /** + * Create a new instance of event rect. An EventRect is actually the rectangle that is drawn + * on the calendar for a given event. There may be more than one rectangle for a single + * event (an event that expands more than one day). In that case two instances of the + * EventRect will be used for a single event. The given event will be stored in + * "originalEvent". But the event that corresponds to rectangle the rectangle instance will + * be stored in "event". + * + * @param event Represents the event which this instance of rectangle represents. + * @param originalEvent The original event that was passed by the user. + * @param rectF The rectangle. + */ + (var event: WeekViewEvent, var originalEvent: WeekViewEvent, var rectF: RectF?) { + var left: Float = 0.toFloat() + var width: Float = 0.toFloat() + var top: Float = 0.toFloat() + var bottom: Float = 0.toFloat() + } + + + /** + * Gets more events of one/more month(s) if necessary. This method is called when the user is + * scrolling the week view. The week view stores the events of three months: the visible month, + * the previous month, the next month. + * + * @param day The day where the user is currently is. + */ + private fun getMoreEvents(day: Calendar) { + + // Get more events if the month is changed. + if (mEventRects == null) + mEventRects = ArrayList() + + if (mEvents == null) + mEvents = ArrayList() + + if (weekViewLoader == null && !isInEditMode) + throw IllegalStateException("You must provide a MonthChangeListener") + + // If a refresh was requested then reset some variables. + if (mRefreshEvents) { + this.clearEvents() + mFetchedPeriod = -1 + } + + if (weekViewLoader != null) { + val periodToFetch = weekViewLoader!!.toWeekViewPeriodIndex(day).toInt() + if (!isInEditMode && (mFetchedPeriod < 0 || mFetchedPeriod != periodToFetch || mRefreshEvents)) { + val newEvents = weekViewLoader!!.onLoad(periodToFetch) + + // Clear events. + this.clearEvents() + cacheAndSortEvents(newEvents!!) + calculateHeaderHeight() + + mFetchedPeriod = periodToFetch + } + } + + // Prepare to calculate positions of each events. + val tempEvents = mEventRects + mEventRects = ArrayList() + + // Iterate through each day with events to calculate the position of the events. + while (tempEvents!!.size > 0) { + val eventRects = ArrayList(tempEvents.size) + + // Get first event for a day. + val eventRect1 = tempEvents.removeAt(0) + eventRects.add(eventRect1) + + var i = 0 + while (i < tempEvents.size) { + // Collect all other events for same day. + val eventRect2 = tempEvents[i] + if (isSameDay(eventRect1.event.startTime!!, eventRect2.event.startTime!!)) { + tempEvents.removeAt(i) + eventRects.add(eventRect2) + } else { + i++ + } + } + computePositionOfEvents(eventRects) + } + } + + private fun clearEvents() { + mEventRects!!.clear() + mEvents!!.clear() + } + + /** + * Cache the event for smooth scrolling functionality. + * + * @param event The event to cache. + */ + private fun cacheEvent(event: WeekViewEvent) { + if (event.startTime!!.compareTo(event.endTime) >= 0) + return + val splitedEvents = event.splitWeekViewEvents() + for (splitedEvent in splitedEvents) { + mEventRects!!.add(EventRect(splitedEvent, event, null)) + } + + mEvents!!.add(event) + } + + /** + * Cache and sort events. + * + * @param events The events to be cached and sorted. + */ + private fun cacheAndSortEvents(events: List) { + for (event in events) { + cacheEvent(event) + } + sortEventRects(mEventRects) + } + + /** + * Sorts the events in ascending order. + * + * @param eventRects The events to be sorted. + */ + private fun sortEventRects(eventRects: List?) { + Collections.sort(eventRects!!) { left, right -> + val start1 = left.event.startTime!!.timeInMillis + val start2 = right.event.startTime!!.timeInMillis + var comparator = if (start1 > start2) 1 else if (start1 < start2) -1 else 0 + if (comparator == 0) { + val end1 = left.event.endTime!!.timeInMillis + val end2 = right.event.endTime!!.timeInMillis + comparator = if (end1 > end2) 1 else if (end1 < end2) -1 else 0 + } + comparator + } + } + + /** + * Calculates the left and right positions of each events. This comes handy specially if events + * are overlapping. + * + * @param eventRects The events along with their wrapper class. + */ + private fun computePositionOfEvents(eventRects: List) { + // Make "collision groups" for all events that collide with others. + val collisionGroups = ArrayList>() + for (eventRect in eventRects) { + var isPlaced = false + + outerLoop@ for (collisionGroup in collisionGroups) { + for (groupEvent in collisionGroup) { + if (isEventsCollide(groupEvent.event, eventRect.event) && groupEvent.event.isAllDay == eventRect.event.isAllDay) { + collisionGroup.add(eventRect) + isPlaced = true + break@outerLoop + } + } + } + + if (!isPlaced) { + val newGroup = ArrayList() + newGroup.add(eventRect) + collisionGroups.add(newGroup) + } + } + + for (collisionGroup in collisionGroups) { + expandEventsToMaxWidth(collisionGroup) + } + } + + /** + * Expands all the events to maximum possible width. The events will try to occupy maximum + * space available horizontally. + * + * @param collisionGroup The group of events which overlap with each other. + */ + private fun expandEventsToMaxWidth(collisionGroup: List) { + // Expand the events to maximum possible width. + val columns = ArrayList>() + columns.add(ArrayList()) + for (eventRect in collisionGroup) { + var isPlaced = false + for (column in columns) { + if (column.size == 0) { + column.add(eventRect) + isPlaced = true + } else if (!isEventsCollide(eventRect.event, column[column.size - 1].event)) { + column.add(eventRect) + isPlaced = true + break + } + } + if (!isPlaced) { + val newColumn = ArrayList() + newColumn.add(eventRect) + columns.add(newColumn) + } + } + + // Calculate left and right position for all the events. + // Get the maxRowCount by looking in all columns. + var maxRowCount = 0 + for (column in columns) { + maxRowCount = Math.max(maxRowCount, column.size) + } + for (i in 0 until maxRowCount) { + // Set the left and right values of the event. + var j = 0f + for (column in columns) { + if (column.size >= i + 1) { + val eventRect = column[i] + eventRect.width = 1f / columns.size + eventRect.left = j / columns.size + if (!eventRect.event.isAllDay) { + eventRect.top = getPassedMinutesInDay(eventRect.event.startTime!!).toFloat() + eventRect.bottom = getPassedMinutesInDay(eventRect.event.endTime!!).toFloat() + } else { + eventRect.top = 0f + eventRect.bottom = allDayEventHeight.toFloat() + } + mEventRects!!.add(eventRect) + } + j++ + } + } + } + + /** + * Checks if two events overlap. + * + * @param event1 The first event. + * @param event2 The second event. + * @return true if the events overlap. + */ + private fun isEventsCollide(event1: WeekViewEvent, event2: WeekViewEvent): Boolean { + val start1 = event1.startTime!!.timeInMillis + val end1 = event1.endTime!!.timeInMillis + val start2 = event2.startTime!!.timeInMillis + val end2 = event2.endTime!!.timeInMillis + + val minOverlappingMillis = (minOverlappingMinutes * 60 * 1000).toLong() + + return !(start1 + minOverlappingMillis >= end2 || end1 <= start2 + minOverlappingMillis) + } + + + /** + * Checks if time1 occurs after (or at the same time) time2. + * + * @param time1 The time to check. + * @param time2 The time to check against. + * @return true if time1 and time2 are equal or if time1 is after time2. Otherwise false. + */ + private fun isTimeAfterOrEquals(time1: Calendar?, time2: Calendar?): Boolean { + return !(time1 == null || time2 == null) && time1.timeInMillis >= time2.timeInMillis + } + + override fun invalidate() { + super.invalidate() + mAreDimensionsInvalid = true + } + + ///////////////////////////////////////////////////////////////// + // + // Functions related to setting and getting the properties. + // + ///////////////////////////////////////////////////////////////// + + fun setOnEventClickListener(listener: EventClickListener) { + this.eventClickListener = listener + } + + fun setDropListener(dropListener: DropListener) { + this.mDropListener = dropListener + } + + fun setTypeface(typeface: Typeface?) { + if (typeface != null) { + mEventTextPaint!!.typeface = typeface + mTodayHeaderTextPaint!!.typeface = typeface + mTimeTextPaint!!.typeface = typeface + mTypeface = typeface + init() + } + } + + /** + * auto calculate limit time on events in visible days. + */ + fun setAutoLimitTime(isAuto: Boolean) { + this.mAutoLimitTime = isAuto + invalidate() + } + + private fun recalculateHourHeight() { + val height = ((height - (mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mTimeTextHeight / 2 + mHeaderMarginBottom)) / (this.mMaxTime - this.mMinTime)).toInt() + if (height > mHourHeight) { + if (height > maxHourHeight) + maxHourHeight = height + mNewHourHeight = height + } + } + + /** + * Set visible time span. + * + * @param startHour limit time display on top (between 0~24) + * @param endHour limit time display at bottom (between 0~24 and larger than startHour) + */ + fun setLimitTime(startHour: Int, endHour: Int) { + if (endHour <= startHour) { + throw IllegalArgumentException("endHour must larger startHour.") + } else if (startHour < 0) { + throw IllegalArgumentException("startHour must be at least 0.") + } else if (endHour > 24) { + throw IllegalArgumentException("endHour can't be higher than 24.") + } + this.mMinTime = startHour + this.mMaxTime = endHour + recalculateHourHeight() + invalidate() + } + + /** + * Set minimal shown time + * + * @param startHour limit time display on top (between 0~24) and smaller than endHour + */ + fun setMinTime(startHour: Int) { + if (mMaxTime <= startHour) { + throw IllegalArgumentException("startHour must smaller than endHour") + } else if (startHour < 0) { + throw IllegalArgumentException("startHour must be at least 0.") + } + this.mMinTime = startHour + recalculateHourHeight() + } + + /** + * Set highest shown time + * + * @param endHour limit time display at bottom (between 0~24 and larger than startHour) + */ + fun setMaxTime(endHour: Int) { + if (endHour <= mMinTime) { + throw IllegalArgumentException("endHour must be larger than startHour.") + } else if (endHour > 24) { + throw IllegalArgumentException("endHour can't be higher than 24.") + } + this.mMaxTime = endHour + recalculateHourHeight() + invalidate() + } + + fun enableDropListener() { + this.isDropListenerEnabled = true + setOnDragListener(DragListener()) + } + + fun disableDropListener() { + this.isDropListenerEnabled = false + setOnDragListener(null) + } + + ///////////////////////////////////////////////////////////////// + // + // Functions related to scrolling. + // + ///////////////////////////////////////////////////////////////// + + override fun onTouchEvent(event: MotionEvent): Boolean { + + mSizeOfWeekView = (mWidthPerDay + mColumnGap) * numberOfVisibleDays + mDistanceMin = mSizeOfWeekView / mOffsetValueToSecureScreen + + mScaleDetector!!.onTouchEvent(event) + val `val` = mGestureDetector!!.onTouchEvent(event) + + // Check after call of mGestureDetector, so mCurrentFlingDirection and mCurrentScrollDirection are set. + if (event.action == MotionEvent.ACTION_UP && !mIsZooming && mCurrentFlingDirection == Direction.NONE) { + if (mCurrentScrollDirection == Direction.RIGHT || mCurrentScrollDirection == Direction.LEFT) { + goToNearestOrigin() + } + mCurrentScrollDirection = Direction.NONE + } + + return `val` + } + + private fun goToNearestOrigin() { + var leftDays = (mCurrentOrigin.x / (mWidthPerDay + mColumnGap)).toDouble() + + val beforeScroll = mStartOriginForScroll + var isPassed = false + + if (mDistanceDone > mDistanceMin || mDistanceDone < -mDistanceMin || !mIsScrollNumberOfVisibleDays) { + + if (!mIsScrollNumberOfVisibleDays && mCurrentFlingDirection != Direction.NONE) { + // snap to nearest day + leftDays = Math.round(leftDays).toDouble() + } else if (mCurrentScrollDirection == Direction.LEFT) { + // snap to last day + leftDays = Math.floor(leftDays) + mStartOriginForScroll -= mSizeOfWeekView + isPassed = true + } else if (mCurrentScrollDirection == Direction.RIGHT) { + // snap to next day + leftDays = Math.floor(leftDays) + mStartOriginForScroll += mSizeOfWeekView + isPassed = true + } else { + // snap to nearest day + leftDays = Math.round(leftDays).toDouble() + } + + + if (mIsScrollNumberOfVisibleDays) { + val mayScrollHorizontal = beforeScroll - mStartOriginForScroll < xMaxLimit && mCurrentOrigin.x - mStartOriginForScroll > xMinLimit + if (isPassed && mayScrollHorizontal) { + // Stop current animation. + mScroller!!.forceFinished(true) + // Snap to date. + if (mCurrentScrollDirection == Direction.LEFT) { + mScroller!!.startScroll(mCurrentOrigin.x.toInt(), mCurrentOrigin.y.toInt(), (beforeScroll - mCurrentOrigin.x - mSizeOfWeekView).toInt(), 0, 200) + } else if (mCurrentScrollDirection == Direction.RIGHT) { + mScroller!!.startScroll(mCurrentOrigin.x.toInt(), mCurrentOrigin.y.toInt(), (mSizeOfWeekView - (mCurrentOrigin.x - beforeScroll)).toInt(), 0, 200) + } + ViewCompat.postInvalidateOnAnimation(this@WeekView) + } + } else { + val nearestOrigin = (mCurrentOrigin.x - leftDays * (mWidthPerDay + mColumnGap)).toInt() + val mayScrollHorizontal = mCurrentOrigin.x - nearestOrigin < xMaxLimit && mCurrentOrigin.x - nearestOrigin > xMinLimit + if (mayScrollHorizontal) { + mScroller!!.startScroll(mCurrentOrigin.x.toInt(), mCurrentOrigin.y.toInt(), -nearestOrigin, 0) + ViewCompat.postInvalidateOnAnimation(this@WeekView) + } + + if (nearestOrigin != 0 && mayScrollHorizontal) { + // Stop current animation. + mScroller!!.forceFinished(true) + // Snap to date. + mScroller!!.startScroll(mCurrentOrigin.x.toInt(), mCurrentOrigin.y.toInt(), -nearestOrigin, 0, (Math.abs(nearestOrigin) / mWidthPerDay * scrollDuration).toInt()) + ViewCompat.postInvalidateOnAnimation(this@WeekView) + } + } + + // Reset scrolling and fling direction. + mCurrentFlingDirection = Direction.NONE + mCurrentScrollDirection = mCurrentFlingDirection + + + } else { + mScroller!!.forceFinished(true) + if (mCurrentScrollDirection == Direction.LEFT) { + mScroller!!.startScroll(mCurrentOrigin.x.toInt(), mCurrentOrigin.y.toInt(), beforeScroll.toInt() - mCurrentOrigin.x.toInt(), 0, 200) + } else if (mCurrentScrollDirection == Direction.RIGHT) { + mScroller!!.startScroll(mCurrentOrigin.x.toInt(), mCurrentOrigin.y.toInt(), beforeScroll.toInt() - mCurrentOrigin.x.toInt(), 0, 200) + } + ViewCompat.postInvalidateOnAnimation(this@WeekView) + + // Reset scrolling and fling direction. + mCurrentFlingDirection = Direction.NONE + mCurrentScrollDirection = mCurrentFlingDirection + } + } + + override fun computeScroll() { + super.computeScroll() + + if (mScroller!!.isFinished) { + if (mCurrentFlingDirection != Direction.NONE) { + // Snap to day after fling is finished. + goToNearestOrigin() + } + } else { + if (mCurrentFlingDirection != Direction.NONE && forceFinishScroll()) { + goToNearestOrigin() + } else if (mScroller!!.computeScrollOffset()) { + mCurrentOrigin.y = mScroller!!.currY.toFloat() + mCurrentOrigin.x = mScroller!!.currX.toFloat() + ViewCompat.postInvalidateOnAnimation(this) + } + } + } + + /** + * Check if scrolling should be stopped. + * + * @return true if scrolling should be stopped before reaching the end of animation. + */ + private fun forceFinishScroll(): Boolean { + return mScroller!!.currVelocity <= mMinimumFlingVelocity + } + + + ///////////////////////////////////////////////////////////////// + // + // Public methods. + // + ///////////////////////////////////////////////////////////////// + + /** + * Show today on the week view. + */ + fun goToToday() { + val today = Calendar.getInstance() + goToDate(today) + } + + /** + * Show a specific day on the week view. + * + * @param date The date to show. + */ + fun goToDate(date: Calendar) { + mScroller!!.forceFinished(true) + mCurrentFlingDirection = Direction.NONE + mCurrentScrollDirection = mCurrentFlingDirection + + date.set(Calendar.HOUR_OF_DAY, 0) + date.set(Calendar.MINUTE, 0) + date.set(Calendar.SECOND, 0) + date.set(Calendar.MILLISECOND, 0) + + if (mAreDimensionsInvalid) { + mScrollToDay = date + return + } + + mRefreshEvents = true + + mCurrentOrigin.x = -daysBetween(mHomeDate!!, date) * (mWidthPerDay + mColumnGap) + invalidate() + } + + /** + * Refreshes the view and loads the events again. + */ + fun notifyDatasetChanged() { + mRefreshEvents = true + invalidate() + } + + /** + * Vertically scroll to a specific hour in the week view. + * + * @param hour The hour to scroll to in 24-hour format. Supported values are 0-24. + */ + fun goToHour(hour: Double) { + if (mAreDimensionsInvalid) { + mScrollToHour = hour + return + } + + var verticalOffset = 0 + if (hour > mMaxTime) + verticalOffset = mHourHeight * (mMaxTime - mMinTime) + else if (hour > mMinTime) + verticalOffset = (mHourHeight * hour).toInt() + + if (verticalOffset > (mHourHeight * (mMaxTime - mMinTime) - height).toFloat() + mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mHeaderMarginBottom) + verticalOffset = ((mHourHeight * (mMaxTime - mMinTime) - height).toFloat() + mHeaderHeight + (mHeaderRowPadding * 2).toFloat() + mHeaderMarginBottom).toInt() + + mCurrentOrigin.y = (-verticalOffset).toFloat() + invalidate() + } + + /** + * Determine whether a given calendar day falls within the scroll limits set for this view. + * + * @param day the day to check + * @return True if there are no limit or the date is within the limits. + * @see .setMinDate + * @see .setMaxDate + */ + fun dateIsValid(day: Calendar): Boolean { + if (minDate != null && day.before(minDate)) { + return false + } + return if (maxDate != null && day.after(maxDate)) { + false + } else true + } + + ///////////////////////////////////////////////////////////////// + // + // Interfaces. + // + ///////////////////////////////////////////////////////////////// + + interface DropListener { + /** + * Triggered when view dropped + * + * @param view: dropped view. + * @param date: object set with the date and time of the dropped coordinates on the view. + */ + fun onDrop(view: View, date: Calendar) + } + + interface EventClickListener { + /** + * Triggered when clicked on one existing event + * + * @param event: event clicked. + * @param eventRect: view containing the clicked event. + */ + fun onEventClick(event: WeekViewEvent, eventRect: RectF) + } + + interface EventLongPressListener { + /** + * Similar to [com.alamkanak.weekview.WeekView.EventClickListener] but with a long press. + * + * @param event: event clicked. + * @param eventRect: view containing the clicked event. + */ + fun onEventLongPress(event: WeekViewEvent, eventRect: RectF) + } + + interface EmptyViewClickListener { + /** + * Triggered when the users clicks on a empty space of the calendar. + * + * @param date: [Calendar] object set with the date and time of the clicked position on the view. + */ + fun onEmptyViewClicked(date: Calendar) + + } + + interface EmptyViewLongPressListener { + /** + * Similar to [com.alamkanak.weekview.WeekView.EmptyViewClickListener] but with long press. + * + * @param time: [Calendar] object set with the date and time of the long pressed position on the view. + */ + fun onEmptyViewLongPress(time: Calendar) + } + + interface ScrollListener { + /** + * Called when the first visible day has changed. + * + * + * (this will also be called during the first draw of the weekview) + * + * @param newFirstVisibleDay The new first visible day + * @param oldFirstVisibleDay The old first visible day (is null on the first call). + */ + fun onFirstVisibleDayChanged(newFirstVisibleDay: Calendar, oldFirstVisibleDay: Calendar?) + } + + interface AddEventClickListener { + /** + * Triggered when the users clicks to create a new event. + * + * @param startTime The startTime of a new event + * @param endTime The endTime of a new event + */ + fun onAddEventClicked(startTime: Calendar, endTime: Calendar) + } + + /** + * A simple GestureListener that holds the focused hour while scaling. + */ + private inner class WeekViewGestureListener : ScaleGestureDetector.OnScaleGestureListener { + internal var mFocusedPointY: Float = 0.toFloat() + + override fun onScaleEnd(detector: ScaleGestureDetector) { + mIsZooming = false + } + + override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { + mIsZooming = true + goToNearestOrigin() + + // Calculate focused point for scale action + if (isZoomFocusPointEnabled) { + // Use fractional focus, percentage of height + mFocusedPointY = (height.toFloat() - mHeaderHeight - (mHeaderRowPadding * 2).toFloat() - mHeaderMarginBottom) * mZoomFocusPoint + } else { + // Grab focus + mFocusedPointY = detector.focusY + } + + return true + } + + override fun onScale(detector: ScaleGestureDetector): Boolean { + val scale = detector.scaleFactor + + mNewHourHeight = Math.round(mHourHeight * scale) + + // Calculating difference + var diffY = mFocusedPointY - mCurrentOrigin.y + // Scaling difference + diffY = diffY * scale - diffY + // Updating week view origin + mCurrentOrigin.y -= diffY + + invalidate() + return true + } + + } + + private inner class DragListener : View.OnDragListener { + override fun onDrag(v: View, e: DragEvent): Boolean { + when (e.action) { + DragEvent.ACTION_DROP -> if (e.x > mHeaderColumnWidth && e.y > mHeaderTextHeight + (mHeaderRowPadding * 2).toFloat() + mHeaderMarginBottom) { + val selectedTime = getTimeFromPoint(e.x, e.y) + if (selectedTime != null) { + mDropListener!!.onDrop(v, selectedTime) + } + } + } + return true + } + } + + companion object { + + @Deprecated("") + val LENGTH_SHORT = 1 + @Deprecated("") + val LENGTH_LONG = 2 + } +} diff --git a/library/src/main/java/com/alamkanak/weekview/WeekViewEvent.java b/library/src/main/java/com/alamkanak/weekview/WeekViewEvent.java deleted file mode 100644 index 28faa59aa..000000000 --- a/library/src/main/java/com/alamkanak/weekview/WeekViewEvent.java +++ /dev/null @@ -1,343 +0,0 @@ -package com.alamkanak.weekview; - -import android.graphics.Shader; -import android.support.annotation.ColorInt; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -import static com.alamkanak.weekview.WeekViewUtil.isSameDay; - -/** - * Created by Raquib-ul-Alam Kanak on 7/21/2014. - * Website: http://april-shower.com - */ -public class WeekViewEvent { - private String mId; - private Calendar mStartTime; - private Calendar mEndTime; - private String mName; - private String mLocation; - private - @ColorInt - int mColor; - private boolean mAllDay; - private Shader mShader; - - public WeekViewEvent() { - - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event as String. - * @param name Name of the event. - * @param startYear Year when the event starts. - * @param startMonth Month when the event starts. - * @param startDay Day when the event starts. - * @param startHour Hour (in 24-hour format) when the event starts. - * @param startMinute Minute when the event starts. - * @param endYear Year when the event ends. - * @param endMonth Month when the event ends. - * @param endDay Day when the event ends. - * @param endHour Hour (in 24-hour format) when the event ends. - * @param endMinute Minute when the event ends. - */ - public WeekViewEvent(String id, String name, int startYear, int startMonth, int startDay, int startHour, int startMinute, int endYear, int endMonth, int endDay, int endHour, int endMinute) { - this.mId = id; - - this.mStartTime = Calendar.getInstance(); - this.mStartTime.set(Calendar.YEAR, startYear); - this.mStartTime.set(Calendar.MONTH, startMonth - 1); - this.mStartTime.set(Calendar.DAY_OF_MONTH, startDay); - this.mStartTime.set(Calendar.HOUR_OF_DAY, startHour); - this.mStartTime.set(Calendar.MINUTE, startMinute); - - this.mEndTime = Calendar.getInstance(); - this.mEndTime.set(Calendar.YEAR, endYear); - this.mEndTime.set(Calendar.MONTH, endMonth - 1); - this.mEndTime.set(Calendar.DAY_OF_MONTH, endDay); - this.mEndTime.set(Calendar.HOUR_OF_DAY, endHour); - this.mEndTime.set(Calendar.MINUTE, endMinute); - - this.mName = name; - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event. - * @param name Name of the event. - * @param startYear Year when the event starts. - * @param startMonth Month when the event starts. - * @param startDay Day when the event starts. - * @param startHour Hour (in 24-hour format) when the event starts. - * @param startMinute Minute when the event starts. - * @param endYear Year when the event ends. - * @param endMonth Month when the event ends. - * @param endDay Day when the event ends. - * @param endHour Hour (in 24-hour format) when the event ends. - * @param endMinute Minute when the event ends. - */ - @Deprecated - public WeekViewEvent(long id, String name, int startYear, int startMonth, int startDay, int startHour, int startMinute, int endYear, int endMonth, int endDay, int endHour, int endMinute) { - this(String.valueOf(id), name, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute); - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event as String. - * @param name Name of the event. - * @param location The location of the event. - * @param startTime The time when the event starts. - * @param endTime The time when the event ends. - * @param allDay Is the event an all day event. - * @param shader the Shader of the event rectangle - */ - public WeekViewEvent(String id, String name, String location, Calendar startTime, Calendar endTime, boolean allDay, Shader shader) { - this.mId = id; - this.mName = name; - this.mLocation = location; - this.mStartTime = startTime; - this.mEndTime = endTime; - this.mAllDay = allDay; - this.mShader = shader; - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event. - * @param name Name of the event. - * @param location The location of the event. - * @param startTime The time when the event starts. - * @param endTime The time when the event ends. - * @param allDay Is the event an all day event. - * @param shader the Shader of the event rectangle - */ - @Deprecated - public WeekViewEvent(long id, String name, String location, Calendar startTime, Calendar endTime, boolean allDay, Shader shader) { - this(String.valueOf(id), name, location, startTime, endTime, allDay, shader); - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event as String. - * @param name Name of the event. - * @param location The location of the event. - * @param startTime The time when the event starts. - * @param endTime The time when the event ends. - * @param allDay Is the event an all day event - */ - public WeekViewEvent(String id, String name, String location, Calendar startTime, Calendar endTime, boolean allDay) { - this(id, name, location, startTime, endTime, allDay, null); - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event. - * @param name Name of the event. - * @param location The location of the event. - * @param startTime The time when the event starts. - * @param endTime The time when the event ends. - * @param allDay Is the event an all day event - */ - @Deprecated - public WeekViewEvent(long id, String name, String location, Calendar startTime, Calendar endTime, boolean allDay) { - this(id, name, location, startTime, endTime, allDay, null); - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event as String. - * @param name Name of the event. - * @param location The location of the event. - * @param startTime The time when the event starts. - * @param endTime The time when the event ends. - */ - public WeekViewEvent(String id, String name, String location, Calendar startTime, Calendar endTime) { - this(id, name, location, startTime, endTime, false); - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event. - * @param name Name of the event. - * @param location The location of the event. - * @param startTime The time when the event starts. - * @param endTime The time when the event ends. - */ - @Deprecated - public WeekViewEvent(long id, String name, String location, Calendar startTime, Calendar endTime) { - this(id, name, location, startTime, endTime, false); - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event specified as String. - * @param name Name of the event. - * @param startTime The time when the event starts. - * @param endTime The time when the event ends. - */ - public WeekViewEvent(String id, String name, Calendar startTime, Calendar endTime) { - this(id, name, null, startTime, endTime); - } - - /** - * Initializes the event for week view. - * - * @param id The id of the event. - * @param name Name of the event. - * @param startTime The time when the event starts. - * @param endTime The time when the event ends. - */ - @Deprecated - public WeekViewEvent(long id, String name, Calendar startTime, Calendar endTime) { - this(id, name, null, startTime, endTime); - } - - public Calendar getStartTime() { - return mStartTime; - } - - public void setStartTime(Calendar startTime) { - this.mStartTime = startTime; - } - - public Calendar getEndTime() { - return mEndTime; - } - - public void setEndTime(Calendar endTime) { - this.mEndTime = endTime; - } - - public String getName() { - return mName; - } - - public void setName(String name) { - this.mName = name; - } - - public String getLocation() { - return mLocation; - } - - public void setLocation(String location) { - this.mLocation = location; - } - - public - @ColorInt - int getColor() { - return mColor; - } - - public void setColor(int color) { - this.mColor = color; - } - - public boolean isAllDay() { - return mAllDay; - } - - public void setAllDay(boolean allDay) { - this.mAllDay = allDay; - } - - public Shader getShader() { - return mShader; - } - - public void setShader(Shader shader) { - mShader = shader; - } - - public String getIdentifier() { - return mId; - } - - @Deprecated - public long getId() { - return Long.parseLong(mId); - } - - public void setIdentifier(String id) { - this.mId = id; - } - - @Deprecated - public void setId(long id) { - this.mId = String.valueOf(id); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - WeekViewEvent that = (WeekViewEvent) o; - - return mId.equals(that.mId); - } - - @Override - public int hashCode() { - return mId.hashCode(); - } - - public List splitWeekViewEvents() { - //This function splits the WeekViewEvent in WeekViewEvents by day - List events = new ArrayList(); - // The first millisecond of the next day is still the same day. (no need to split events for this). - Calendar endTime = (Calendar) this.getEndTime().clone(); - endTime.add(Calendar.MILLISECOND, -1); - if (!isSameDay(this.getStartTime(), endTime)) { - endTime = (Calendar) this.getStartTime().clone(); - endTime.set(Calendar.HOUR_OF_DAY, 23); - endTime.set(Calendar.MINUTE, 59); - WeekViewEvent event1 = new WeekViewEvent(this.getIdentifier(), this.getName(), this.getLocation(), this.getStartTime(), endTime, this.isAllDay()); - event1.setColor(this.getColor()); - events.add(event1); - - // Add other days. - Calendar otherDay = (Calendar) this.getStartTime().clone(); - otherDay.add(Calendar.DATE, 1); - while (!isSameDay(otherDay, this.getEndTime())) { - Calendar overDay = (Calendar) otherDay.clone(); - overDay.set(Calendar.HOUR_OF_DAY, 0); - overDay.set(Calendar.MINUTE, 0); - Calendar endOfOverDay = (Calendar) overDay.clone(); - endOfOverDay.set(Calendar.HOUR_OF_DAY, 23); - endOfOverDay.set(Calendar.MINUTE, 59); - WeekViewEvent eventMore = new WeekViewEvent(this.getIdentifier(), this.getName(), null, overDay, endOfOverDay, this.isAllDay()); - eventMore.setColor(this.getColor()); - events.add(eventMore); - - // Add next day. - otherDay.add(Calendar.DATE, 1); - } - - // Add last day. - Calendar startTime = (Calendar) this.getEndTime().clone(); - startTime.set(Calendar.HOUR_OF_DAY, 0); - startTime.set(Calendar.MINUTE, 0); - WeekViewEvent event2 = new WeekViewEvent(this.getIdentifier(), this.getName(), this.getLocation(), startTime, this.getEndTime(), this.isAllDay()); - event2.setColor(this.getColor()); - events.add(event2); - } else { - events.add(this); - } - - return events; - } -} diff --git a/library/src/main/java/com/alamkanak/weekview/WeekViewEvent.kt b/library/src/main/java/com/alamkanak/weekview/WeekViewEvent.kt new file mode 100644 index 000000000..c1ccd75b4 --- /dev/null +++ b/library/src/main/java/com/alamkanak/weekview/WeekViewEvent.kt @@ -0,0 +1,206 @@ +package com.alamkanak.weekview + +import android.graphics.Shader +import android.support.annotation.ColorInt +import com.alamkanak.weekview.WeekViewUtil.isSameDay +import java.util.* + +/** + * Created by Raquib-ul-Alam Kanak on 7/21/2014. + * Website: http://april-shower.com + */ +class WeekViewEvent { + var identifier: String? = null + var startTime: Calendar? = null + var endTime: Calendar? = null + var name: String? = null + var location: String? = null + @ColorInt + @get:ColorInt + var color: Int = 0 + var isAllDay: Boolean = false + var shader: Shader? = null + + var id: Long + @Deprecated("") + get() = java.lang.Long.parseLong(identifier) + @Deprecated("") + set(id) { + this.identifier = id.toString() + } + + constructor() + + /** + * Initializes the event for week view. + * + * @param id The id of the event as String. + * @param name Name of the event. + * @param startYear Year when the event starts. + * @param startMonth Month when the event starts. + * @param startDay Day when the event starts. + * @param startHour Hour (in 24-hour format) when the event starts. + * @param startMinute Minute when the event starts. + * @param endYear Year when the event ends. + * @param endMonth Month when the event ends. + * @param endDay Day when the event ends. + * @param endHour Hour (in 24-hour format) when the event ends. + * @param endMinute Minute when the event ends. + */ + constructor(id: String, name: String, startYear: Int, startMonth: Int, startDay: Int, startHour: Int, startMinute: Int, endYear: Int, endMonth: Int, endDay: Int, endHour: Int, endMinute: Int) { + this.identifier = id + + this.startTime = Calendar.getInstance() + this.startTime!!.set(Calendar.YEAR, startYear) + this.startTime!!.set(Calendar.MONTH, startMonth - 1) + this.startTime!!.set(Calendar.DAY_OF_MONTH, startDay) + this.startTime!!.set(Calendar.HOUR_OF_DAY, startHour) + this.startTime!!.set(Calendar.MINUTE, startMinute) + + this.endTime = Calendar.getInstance() + this.endTime!!.set(Calendar.YEAR, endYear) + this.endTime!!.set(Calendar.MONTH, endMonth - 1) + this.endTime!!.set(Calendar.DAY_OF_MONTH, endDay) + this.endTime!!.set(Calendar.HOUR_OF_DAY, endHour) + this.endTime!!.set(Calendar.MINUTE, endMinute) + + this.name = name + } + + /** + * Initializes the event for week view. + * + * @param id The id of the event. + * @param name Name of the event. + * @param startYear Year when the event starts. + * @param startMonth Month when the event starts. + * @param startDay Day when the event starts. + * @param startHour Hour (in 24-hour format) when the event starts. + * @param startMinute Minute when the event starts. + * @param endYear Year when the event ends. + * @param endMonth Month when the event ends. + * @param endDay Day when the event ends. + * @param endHour Hour (in 24-hour format) when the event ends. + * @param endMinute Minute when the event ends. + */ + @Deprecated("") + constructor(id: Long, name: String, startYear: Int, startMonth: Int, startDay: Int, startHour: Int, startMinute: Int, endYear: Int, endMonth: Int, endDay: Int, endHour: Int, endMinute: Int) : this(id.toString(), name, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute) { + } + + /** + * Initializes the event for week view. + * + * @param id The id of the event as String. + * @param name Name of the event. + * @param location The location of the event. + * @param startTime The time when the event starts. + * @param endTime The time when the event ends. + * @param allDay Is the event an all day event. + * @param shader the Shader of the event rectangle + */ + @JvmOverloads constructor(id: String, name: String?, location: String?, startTime: Calendar, endTime: Calendar, allDay: Boolean = false, shader: Shader? = null) { + this.identifier = id + this.name = name + this.location = location + this.startTime = startTime + this.endTime = endTime + this.isAllDay = allDay + this.shader = shader + } + + /** + * Initializes the event for week view. + * + * @param id The id of the event. + * @param name Name of the event. + * @param location The location of the event. + * @param startTime The time when the event starts. + * @param endTime The time when the event ends. + * @param allDay Is the event an all day event. + * @param shader the Shader of the event rectangle + */ + @Deprecated("") + @JvmOverloads constructor(id: Long, name: String, location: String?, startTime: Calendar, endTime: Calendar, allDay: Boolean = false, shader: Shader? = null) : this(id.toString(), name, location, startTime, endTime, allDay, shader) { + } + + /** + * Initializes the event for week view. + * + * @param id The id of the event specified as String. + * @param name Name of the event. + * @param startTime The time when the event starts. + * @param endTime The time when the event ends. + */ + constructor(id: String, name: String, startTime: Calendar, endTime: Calendar) : this(id, name, null, startTime, endTime) {} + + /** + * Initializes the event for week view. + * + * @param id The id of the event. + * @param name Name of the event. + * @param startTime The time when the event starts. + * @param endTime The time when the event ends. + */ + @Deprecated("") + constructor(id: Long, name: String, startTime: Calendar, endTime: Calendar) : this(id, name, null, startTime, endTime) { + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || javaClass != other.javaClass) return false + + val that = other as WeekViewEvent? + + return identifier == that!!.identifier + } + + override fun hashCode(): Int { + return identifier!!.hashCode() + } + + fun splitWeekViewEvents(): List { + //This function splits the WeekViewEvent in WeekViewEvents by day + val events = ArrayList() + // The first millisecond of the next day is still the same day. (no need to split events for this). + var endTime = this.endTime!!.clone() as Calendar + endTime.add(Calendar.MILLISECOND, -1) + if (!isSameDay(this.startTime!!, endTime)) { + endTime = this.startTime!!.clone() as Calendar + endTime.set(Calendar.HOUR_OF_DAY, 23) + endTime.set(Calendar.MINUTE, 59) + val event1 = WeekViewEvent(this.identifier!!, this.name, this.location, this.startTime!!, endTime, this.isAllDay) + event1.color = this.color + events.add(event1) + + // Add other days. + val otherDay = this.startTime!!.clone() as Calendar + otherDay.add(Calendar.DATE, 1) + while (!isSameDay(otherDay, this.endTime!!)) { + val overDay = otherDay.clone() as Calendar + overDay.set(Calendar.HOUR_OF_DAY, 0) + overDay.set(Calendar.MINUTE, 0) + val endOfOverDay = overDay.clone() as Calendar + endOfOverDay.set(Calendar.HOUR_OF_DAY, 23) + endOfOverDay.set(Calendar.MINUTE, 59) + val eventMore = WeekViewEvent(this.identifier!!, this.name, null, overDay, endOfOverDay, this.isAllDay) + eventMore.color = this.color + events.add(eventMore) + + // Add next day. + otherDay.add(Calendar.DATE, 1) + } + + // Add last day. + val startTime = this.endTime!!.clone() as Calendar + startTime.set(Calendar.HOUR_OF_DAY, 0) + startTime.set(Calendar.MINUTE, 0) + val event2 = WeekViewEvent(this.identifier!!, this.name, this.location, startTime, this.endTime!!, this.isAllDay) + event2.color = this.color + events.add(event2) + } else { + events.add(this) + } + + return events + } +} diff --git a/library/src/main/java/com/alamkanak/weekview/WeekViewLoader.java b/library/src/main/java/com/alamkanak/weekview/WeekViewLoader.kt similarity index 69% rename from library/src/main/java/com/alamkanak/weekview/WeekViewLoader.java rename to library/src/main/java/com/alamkanak/weekview/WeekViewLoader.kt index 3b8d6a5b8..cdeb37ded 100644 --- a/library/src/main/java/com/alamkanak/weekview/WeekViewLoader.java +++ b/library/src/main/java/com/alamkanak/weekview/WeekViewLoader.kt @@ -1,19 +1,19 @@ -package com.alamkanak.weekview; +package com.alamkanak.weekview -import java.util.Calendar; -import java.util.List; +import java.util.* -public interface WeekViewLoader { +interface WeekViewLoader { /** * Convert a date into a double that will be used to reference when you're loading data. - *

+ * + * * All periods that have the same integer part, define one period. Dates that are later in time * should have a greater return value. * * @param instance the date * @return The period index in which the date falls (floating point number). */ - double toWeekViewPeriodIndex(Calendar instance); + fun toWeekViewPeriodIndex(instance: Calendar): Double /** * Load the events within the period @@ -21,5 +21,5 @@ public interface WeekViewLoader { * @param periodIndex the period to load * @return A list with the events of this period */ - List onLoad(int periodIndex); + fun onLoad(periodIndex: Int): List? } diff --git a/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.java b/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.kt similarity index 56% rename from library/src/main/java/com/alamkanak/weekview/WeekViewUtil.java rename to library/src/main/java/com/alamkanak/weekview/WeekViewUtil.kt index 9a33c7221..3c2cea487 100644 --- a/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.java +++ b/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.kt @@ -1,11 +1,11 @@ -package com.alamkanak.weekview; +package com.alamkanak.weekview -import java.util.Calendar; +import java.util.* /** * Created by jesse on 6/02/2016. */ -public class WeekViewUtil { +object WeekViewUtil { ///////////////////////////////////////////////////////////////// @@ -21,8 +21,9 @@ public class WeekViewUtil { * @param dateTwo The second date. * * @return Whether the dates are on the same day. */ - public static boolean isSameDay(Calendar dateOne, Calendar dateTwo) { - return dateOne.get(Calendar.YEAR) == dateTwo.get(Calendar.YEAR) && dateOne.get(Calendar.DAY_OF_YEAR) == dateTwo.get(Calendar.DAY_OF_YEAR); + @JvmStatic + fun isSameDay(dateOne: Calendar, dateTwo: Calendar): Boolean { + return dateOne.get(Calendar.YEAR) == dateTwo.get(Calendar.YEAR) && dateOne.get(Calendar.DAY_OF_YEAR) == dateTwo.get(Calendar.DAY_OF_YEAR) } /** @@ -30,13 +31,14 @@ public static boolean isSameDay(Calendar dateOne, Calendar dateTwo) { * * @return the calendar instance */ - public static Calendar today() { - Calendar today = Calendar.getInstance(); - today.set(Calendar.HOUR_OF_DAY, 0); - today.set(Calendar.MINUTE, 0); - today.set(Calendar.SECOND, 0); - today.set(Calendar.MILLISECOND, 0); - return today; + @JvmStatic + fun today(): Calendar { + val today = Calendar.getInstance() + today.set(Calendar.HOUR_OF_DAY, 0) + today.set(Calendar.MINUTE, 0) + today.set(Calendar.SECOND, 0) + today.set(Calendar.MILLISECOND, 0) + return today } /** @@ -46,12 +48,12 @@ public static Calendar today() { * @param dateTwo The second day. * @return Whether the dates are on the same day and hour. */ - public static boolean isSameDayAndHour(Calendar dateOne, Calendar dateTwo) { + @JvmStatic + fun isSameDayAndHour(dateOne: Calendar, dateTwo: Calendar?): Boolean { - if (dateTwo != null) { - return isSameDay(dateOne, dateTwo) && dateOne.get(Calendar.HOUR_OF_DAY) == dateTwo.get(Calendar.HOUR_OF_DAY); - } - return false; + return if (dateTwo != null) { + isSameDay(dateOne, dateTwo) && dateOne.get(Calendar.HOUR_OF_DAY) == dateTwo.get(Calendar.HOUR_OF_DAY) + } else false } /** @@ -61,9 +63,9 @@ public static boolean isSameDayAndHour(Calendar dateOne, Calendar dateTwo) { * @param dateTwo the second date * @return the amount of days between dateTwo and dateOne */ - public static int daysBetween(Calendar dateOne, Calendar dateTwo) { - return (int) (((dateTwo.getTimeInMillis() + dateTwo.getTimeZone().getOffset(dateTwo.getTimeInMillis())) / (1000 * 60 * 60 * 24)) - - ((dateOne.getTimeInMillis() + dateOne.getTimeZone().getOffset(dateOne.getTimeInMillis())) / (1000 * 60 * 60 * 24))); + @JvmStatic + fun daysBetween(dateOne: Calendar, dateTwo: Calendar): Int { + return ((dateTwo.timeInMillis + dateTwo.timeZone.getOffset(dateTwo.timeInMillis)) / (1000 * 60 * 60 * 24) - (dateOne.timeInMillis + dateOne.timeZone.getOffset(dateOne.timeInMillis)) / (1000 * 60 * 60 * 24)).toInt() } /* @@ -71,8 +73,9 @@ public static int daysBetween(Calendar dateOne, Calendar dateTwo) { * @param date * @return amount of minutes in day before time */ - public static int getPassedMinutesInDay(Calendar date) { - return getPassedMinutesInDay(date.get(Calendar.HOUR_OF_DAY), date.get(Calendar.MINUTE)); + @JvmStatic + fun getPassedMinutesInDay(date: Calendar): Int { + return getPassedMinutesInDay(date.get(Calendar.HOUR_OF_DAY), date.get(Calendar.MINUTE)) } /** @@ -82,7 +85,8 @@ public static int getPassedMinutesInDay(Calendar date) { * @param minute * @return amount of minutes in the given hours and minutes */ - public static int getPassedMinutesInDay(int hour, int minute) { - return hour * 60 + minute; + @JvmStatic + fun getPassedMinutesInDay(hour: Int, minute: Int): Int { + return hour * 60 + minute } } diff --git a/sample/build.gradle b/sample/build.gradle index c7734ab7b..c61601c10 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,4 +1,6 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 27 @@ -23,4 +25,8 @@ dependencies { compile project(':library') compile 'com.android.support:appcompat-v7:27.1.1' compile 'com.squareup.retrofit:retrofit:1.9.0' + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} +repositories { + mavenCentral() } diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 775a8b5f8..12e77bf3b 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,28 +1,24 @@ - + - + + android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" + android:theme="@style/AppTheme" tools:ignore="AllowBackup,GoogleAppIndexingWarning"> + android:name=".MainActivity" android:label="@string/app_name"> - - + + + android:name=".BasicActivity" android:label="@string/title_activity_basic"/> + android:name=".AsynchronousActivity" android:label="@string/title_activity_asynchronous"/> + diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/AsynchronousActivity.java b/sample/src/main/java/com/alamkanak/weekview/sample/AsynchronousActivity.java deleted file mode 100644 index ea0bf4dc6..000000000 --- a/sample/src/main/java/com/alamkanak/weekview/sample/AsynchronousActivity.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.alamkanak.weekview.sample; - -import android.widget.Toast; - -import com.alamkanak.weekview.WeekViewEvent; -import com.alamkanak.weekview.sample.apiclient.Event; -import com.alamkanak.weekview.sample.apiclient.MyJsonService; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -import retrofit.Callback; -import retrofit.RestAdapter; -import retrofit.RetrofitError; -import retrofit.client.Response; - -/** - * An example of how events can be fetched from network and be displayed on the week view. - * Created by Raquib-ul-Alam Kanak on 1/3/2014. - * Website: http://alamkanak.github.io - */ -public class AsynchronousActivity extends BaseActivity implements Callback> { - - private List events = new ArrayList(); - boolean calledNetwork = false; - - @Override - public List onMonthChange(int newYear, int newMonth) { - - // Download events from network if it hasn't been done already. To understand how events are - // downloaded using retrofit, visit http://square.github.io/retrofit - if (!calledNetwork) { - RestAdapter retrofit = new RestAdapter.Builder() - .setEndpoint("https://api.myjson.com/bins") - .build(); - MyJsonService service = retrofit.create(MyJsonService.class); - service.listEvents(this); - calledNetwork = true; - } - - // Return only the events that matches newYear and newMonth. - List matchedEvents = new ArrayList(); - for (WeekViewEvent event : events) { - if (eventMatches(event, newYear, newMonth)) { - matchedEvents.add(event); - } - } - return matchedEvents; - } - - /** - * Checks if an event falls into a specific year and month. - * - * @param event The event to check for. - * @param year The year. - * @param month The month. - * @return True if the event matches the year and month. - */ - private boolean eventMatches(WeekViewEvent event, int year, int month) { - return (event.getStartTime().get(Calendar.YEAR) == year && event.getStartTime().get(Calendar.MONTH) == month - 1) || (event.getEndTime().get(Calendar.YEAR) == year && event.getEndTime().get(Calendar.MONTH) == month - 1); - } - - @Override - public void success(List events, Response response) { - this.events.clear(); - for (Event event : events) { - this.events.add(event.toWeekViewEvent()); - } - getWeekView().notifyDatasetChanged(); - } - - @Override - public void failure(RetrofitError error) { - error.printStackTrace(); - Toast.makeText(this, R.string.async_error, Toast.LENGTH_SHORT).show(); - } -} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/AsynchronousActivity.kt b/sample/src/main/java/com/alamkanak/weekview/sample/AsynchronousActivity.kt new file mode 100644 index 000000000..2adc1f776 --- /dev/null +++ b/sample/src/main/java/com/alamkanak/weekview/sample/AsynchronousActivity.kt @@ -0,0 +1,70 @@ +package com.alamkanak.weekview.sample + +import android.widget.Toast +import com.alamkanak.weekview.WeekViewEvent +import com.alamkanak.weekview.sample.apiclient.Event +import com.alamkanak.weekview.sample.apiclient.MyJsonService +import retrofit.Callback +import retrofit.RestAdapter +import retrofit.RetrofitError +import retrofit.client.Response +import java.util.* + +/** + * An example of how events can be fetched from network and be displayed on the week view. + * Created by Raquib-ul-Alam Kanak on 1/3/2014. + * Website: http://alamkanak.github.io + */ +class AsynchronousActivity : BaseActivity(), Callback> { + + private val events = ArrayList() + internal var calledNetwork = false + + override fun onMonthChange(newYear: Int, newMonth: Int): List? { + + // Download events from network if it hasn't been done already. To understand how events are + // downloaded using retrofit, visit http://square.github.io/retrofit + if (!calledNetwork) { + val retrofit = RestAdapter.Builder() + .setEndpoint("https://api.myjson.com/bins") + .build() + val service = retrofit.create(MyJsonService::class.java) + service.listEvents(this) + calledNetwork = true + } + + // Return only the events that matches newYear and newMonth. + val matchedEvents = ArrayList() + for (event in events) { + if (eventMatches(event, newYear, newMonth)) { + matchedEvents.add(event) + } + } + return matchedEvents + } + + /** + * Checks if an event falls into a specific year and month. + * + * @param event The event to check for. + * @param year The year. + * @param month The month. + * @return True if the event matches the year and month. + */ + private fun eventMatches(event: WeekViewEvent, year: Int, month: Int): Boolean { + return event.startTime!!.get(Calendar.YEAR) == year && event.startTime!!.get(Calendar.MONTH) == month - 1 || event.endTime!!.get(Calendar.YEAR) == year && event.endTime!!.get(Calendar.MONTH) == month - 1 + } + + override fun success(events: List, response: Response) { + this.events.clear() + for (event in events) { + this.events.add(event.toWeekViewEvent()) + } + weekView.notifyDatasetChanged() + } + + override fun failure(error: RetrofitError) { + error.printStackTrace() + Toast.makeText(this, R.string.async_error, Toast.LENGTH_SHORT).show() + } +} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.java b/sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.java deleted file mode 100644 index 58970a47e..000000000 --- a/sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.java +++ /dev/null @@ -1,258 +0,0 @@ -package com.alamkanak.weekview.sample; - -import android.content.ClipData; -import android.graphics.RectF; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.RequiresApi; -import android.support.v7.app.AppCompatActivity; -import android.util.TypedValue; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.TextView; -import android.widget.Toast; - -import com.alamkanak.weekview.DateTimeInterpreter; -import com.alamkanak.weekview.MonthLoader; -import com.alamkanak.weekview.WeekView; -import com.alamkanak.weekview.WeekViewEvent; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.List; -import java.util.Locale; - -/** - * This is a base activity which contains week view and all the codes necessary to initialize the - * week view. - * Created by Raquib-ul-Alam Kanak on 1/3/2014. - * Website: http://alamkanak.github.io - */ -public abstract class BaseActivity extends AppCompatActivity implements WeekView.EventClickListener, MonthLoader.MonthChangeListener, WeekView.EventLongPressListener, WeekView.EmptyViewLongPressListener, WeekView.EmptyViewClickListener, WeekView.AddEventClickListener, WeekView.DropListener { - private static final int TYPE_DAY_VIEW = 1; - private static final int TYPE_THREE_DAY_VIEW = 2; - private static final int TYPE_WEEK_VIEW = 3; - private int mWeekViewType = TYPE_THREE_DAY_VIEW; - protected WeekView mWeekView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_base); - - TextView draggableView = (TextView) findViewById(R.id.draggable_view); - draggableView.setOnLongClickListener(new DragTapListener()); - - - // Get a reference for the week view in the layout. - mWeekView = (WeekView) findViewById(R.id.weekView); - - // Show a toast message about the touched event. - mWeekView.setOnEventClickListener(this); - - // The week view has infinite scrolling horizontally. We have to provide the events of a - // month every time the month changes on the week view. - mWeekView.setMonthChangeListener(this); - - // Set long press listener for events. - mWeekView.setEventLongPressListener(this); - - // Set long press listener for empty view - mWeekView.setEmptyViewLongPressListener(this); - - // Set EmptyView Click Listener - mWeekView.setEmptyViewClickListener(this); - - // Set AddEvent Click Listener - mWeekView.setAddEventClickListener(this); - - // Set Drag and Drop Listener - mWeekView.setDropListener(this); - - // Set minDate - /*Calendar minDate = Calendar.getInstance(); - minDate.set(Calendar.DAY_OF_MONTH, 1); - minDate.add(Calendar.MONTH, 1); - mWeekView.setMinDate(minDate); - - // Set maxDate - Calendar maxDate = Calendar.getInstance(); - maxDate.add(Calendar.MONTH, 1); - maxDate.set(Calendar.DAY_OF_MONTH, 10); - mWeekView.setMaxDate(maxDate); - - Calendar calendar = (Calendar) maxDate.clone(); - calendar.add(Calendar.DATE, -2); - mWeekView.goToDate(calendar);*/ - - //mWeekView.setAutoLimitTime(true); - //mWeekView.setLimitTime(4, 16); - - //mWeekView.setMinTime(10); - //mWeekView.setMaxTime(20); - - // Set up a date time interpreter to interpret how the date and time will be formatted in - // the week view. This is optional. - setupDateTimeInterpreter(false); - } - - @Override - protected void onResume() { - super.onResume(); - /*mWeekView.setShowDistinctPastFutureColor(true); - mWeekView.setShowDistinctWeekendColor(true); - mWeekView.setFutureBackgroundColor(Color.rgb(24,85,96)); - mWeekView.setFutureWeekendBackgroundColor(Color.rgb(255,0,0)); - mWeekView.setPastBackgroundColor(Color.rgb(85,189,200)); - mWeekView.setPastWeekendBackgroundColor(Color.argb(50, 0,255,0)); - */ - } - - private final class DragTapListener implements View.OnLongClickListener { - @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) - @Override - public boolean onLongClick(View v) { - ClipData data = ClipData.newPlainText("", ""); - View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v); - v.startDrag(data, shadowBuilder, v, 0); - return true; - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - setupDateTimeInterpreter(id == R.id.action_week_view); - switch (id) { - case R.id.action_today: - mWeekView.goToToday(); - return true; - case R.id.action_day_view: - if (mWeekViewType != TYPE_DAY_VIEW) { - item.setChecked(!item.isChecked()); - mWeekViewType = TYPE_DAY_VIEW; - mWeekView.setNumberOfVisibleDays(1); - - // Lets change some dimensions to best fit the view. - mWeekView.setColumnGap((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics())); - mWeekView.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics())); - mWeekView.setEventTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics())); - } - return true; - case R.id.action_three_day_view: - if (mWeekViewType != TYPE_THREE_DAY_VIEW) { - item.setChecked(!item.isChecked()); - mWeekViewType = TYPE_THREE_DAY_VIEW; - mWeekView.setNumberOfVisibleDays(3); - - // Lets change some dimensions to best fit the view. - mWeekView.setColumnGap((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics())); - mWeekView.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics())); - mWeekView.setEventTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics())); - } - return true; - case R.id.action_week_view: - if (mWeekViewType != TYPE_WEEK_VIEW) { - item.setChecked(!item.isChecked()); - mWeekViewType = TYPE_WEEK_VIEW; - mWeekView.setNumberOfVisibleDays(7); - - // Lets change some dimensions to best fit the view. - mWeekView.setColumnGap((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics())); - mWeekView.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, getResources().getDisplayMetrics())); - mWeekView.setEventTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, getResources().getDisplayMetrics())); - } - return true; - } - - return super.onOptionsItemSelected(item); - } - - /** - * Set up a date time interpreter which will show short date values when in week view and long - * date values otherwise. - * - * @param shortDate True if the date values should be short. - */ - private void setupDateTimeInterpreter(final boolean shortDate) { - mWeekView.setDateTimeInterpreter(new DateTimeInterpreter() { - @Override - public String interpretDate(Calendar date) { - SimpleDateFormat weekdayNameFormat = new SimpleDateFormat("EEE", Locale.getDefault()); - String weekday = weekdayNameFormat.format(date.getTime()); - SimpleDateFormat format = new SimpleDateFormat(" M/d", Locale.getDefault()); - - // All android api level do not have a standard way of getting the first letter of - // the week day name. Hence we get the first char programmatically. - // Details: http://stackoverflow.com/questions/16959502/get-one-letter-abbreviation-of-week-day-of-a-date-in-java#answer-16959657 - if (shortDate) - weekday = String.valueOf(weekday.charAt(0)); - return weekday.toUpperCase() + format.format(date.getTime()); - } - - @Override - public String interpretTime(int hour, int minutes) { - String strMinutes = String.format("%02d", minutes); - if (hour > 11) { - return (hour - 12) + ":" + strMinutes + " PM"; - } else { - if (hour == 0) { - return "12:" + strMinutes + " AM"; - } else { - return hour + ":" + strMinutes + " AM"; - } - } - } - }); - } - - protected String getEventTitle(Calendar time) { - return String.format("Event of %02d:%02d %s/%d", time.get(Calendar.HOUR_OF_DAY), time.get(Calendar.MINUTE), time.get(Calendar.MONTH) + 1, time.get(Calendar.DAY_OF_MONTH)); - } - - @Override - public void onEventClick(WeekViewEvent event, RectF eventRect) { - Toast.makeText(this, "Clicked " + event.getName(), Toast.LENGTH_SHORT).show(); - } - - @Override - public void onEventLongPress(WeekViewEvent event, RectF eventRect) { - Toast.makeText(this, "Long pressed event: " + event.getName(), Toast.LENGTH_SHORT).show(); - } - - @Override - public void onEmptyViewLongPress(Calendar time) { - Toast.makeText(this, "Empty view long pressed: " + getEventTitle(time), Toast.LENGTH_SHORT).show(); - } - - public WeekView getWeekView() { - return mWeekView; - } - - @Override - public void onEmptyViewClicked(Calendar date) { - Toast.makeText(this, "Empty view" + " clicked: " + getEventTitle(date), Toast.LENGTH_SHORT).show(); - } - - @Override - public List onMonthChange(int newYear, int newMonth) { - return null; - } - - @Override - public void onAddEventClicked(Calendar startTime, Calendar endTime) { - Toast.makeText(this, "Add event clicked.", Toast.LENGTH_SHORT).show(); - } - - @Override - public void onDrop(View view, Calendar date) { - Toast.makeText(this, "View dropped to " + date.toString(), Toast.LENGTH_SHORT).show(); - } -} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.kt b/sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.kt new file mode 100644 index 000000000..eeebd8330 --- /dev/null +++ b/sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.kt @@ -0,0 +1,252 @@ +package com.alamkanak.weekview.sample + +import android.content.ClipData +import android.graphics.RectF +import android.os.Build +import android.os.Bundle +import android.support.annotation.RequiresApi +import android.support.v7.app.AppCompatActivity +import android.text.format.DateFormat +import android.text.format.DateUtils +import android.util.TypedValue +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.TextView +import android.widget.Toast +import com.alamkanak.weekview.DateTimeInterpreter +import com.alamkanak.weekview.MonthLoader +import com.alamkanak.weekview.WeekView +import com.alamkanak.weekview.WeekViewEvent +import java.text.SimpleDateFormat +import java.util.* + +/** + * This is a base activity which contains week view and all the codes necessary to initialize the + * week view. + * Created by Raquib-ul-Alam Kanak on 1/3/2014. + * Website: http://alamkanak.github.io + */ +abstract class BaseActivity : AppCompatActivity(), WeekView.EventClickListener, MonthLoader.MonthChangeListener, WeekView.EventLongPressListener, WeekView.EmptyViewLongPressListener, WeekView.EmptyViewClickListener, WeekView.AddEventClickListener, WeekView.DropListener { + private var mWeekViewType = TYPE_THREE_DAY_VIEW + lateinit var weekView: WeekView + protected set + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_base) + + val draggableView = findViewById(R.id.draggable_view) as TextView + draggableView.setOnLongClickListener(DragTapListener()) + + + // Get a reference for the week view in the layout. + weekView = findViewById(R.id.weekView) as WeekView + + // Show a toast message about the touched event. + weekView.setOnEventClickListener(this) + + // The week view has infinite scrolling horizontally. We have to provide the events of a + // month every time the month changes on the week view. + weekView.monthChangeListener = this + + // Set long press listener for events. + weekView.eventLongPressListener = this + + // Set long press listener for empty view + weekView.emptyViewLongPressListener = this + + // Set EmptyView Click Listener + weekView.emptyViewClickListener = this + + // Set AddEvent Click Listener + weekView.addEventClickListener = this + + // Set Drag and Drop Listener + weekView.setDropListener(this) + + // Set minDate + /*Calendar minDate = Calendar.getInstance(); + minDate.set(Calendar.DAY_OF_MONTH, 1); + minDate.add(Calendar.MONTH, 1); + mWeekView.setMinDate(minDate); + + // Set maxDate + Calendar maxDate = Calendar.getInstance(); + maxDate.add(Calendar.MONTH, 1); + maxDate.set(Calendar.DAY_OF_MONTH, 10); + mWeekView.setMaxDate(maxDate); + + Calendar calendar = (Calendar) maxDate.clone(); + calendar.add(Calendar.DATE, -2); + mWeekView.goToDate(calendar);*/ + + //mWeekView.setAutoLimitTime(true); + //mWeekView.setLimitTime(4, 16); + + //mWeekView.setMinTime(10); + //mWeekView.setMaxTime(20); + + // Set up a date time interpreter to interpret how the date and time will be formatted in + // the week view. This is optional. + setupDateTimeInterpreter(false) + } + + override fun onResume() { + super.onResume() + /*mWeekView.setShowDistinctPastFutureColor(true); + mWeekView.setShowDistinctWeekendColor(true); + mWeekView.setFutureBackgroundColor(Color.rgb(24,85,96)); + mWeekView.setFutureWeekendBackgroundColor(Color.rgb(255,0,0)); + mWeekView.setPastBackgroundColor(Color.rgb(85,189,200)); + mWeekView.setPastWeekendBackgroundColor(Color.argb(50, 0,255,0)); + */ + } + + private inner class DragTapListener : View.OnLongClickListener { + override fun onLongClick(v: View): Boolean { + val data = ClipData.newPlainText("", "") + val shadowBuilder = View.DragShadowBuilder(v) + v.startDrag(data, shadowBuilder, v, 0) + return true + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.main, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val id = item.itemId + setupDateTimeInterpreter(id == R.id.action_week_view) + when (id) { + R.id.action_today -> { + weekView.goToToday() + return true + } + R.id.action_day_view -> { + if (!item.isChecked) { + item.isChecked = true + setDayViewType(TYPE_DAY_VIEW) + } + return true + } + R.id.action_three_day_view -> { + if (!item.isChecked) { + item.isChecked = true + setDayViewType(TYPE_THREE_DAY_VIEW) + } + return true + } + R.id.action_week_view -> { + if (!item.isChecked) { + item.isChecked = true + setDayViewType(TYPE_WEEK_VIEW) + } + return true + } + } + return super.onOptionsItemSelected(item) + } + + fun setDayViewType(dayViewType: Int) { + when (dayViewType) { + TYPE_DAY_VIEW -> { + mWeekViewType = TYPE_DAY_VIEW + weekView.numberOfVisibleDays = 1 + // Lets change some dimensions to best fit the view. + weekView.columnGap = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics).toInt() + weekView.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics).toInt() + weekView.eventTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics).toInt() + } + TYPE_THREE_DAY_VIEW -> { + mWeekViewType = TYPE_THREE_DAY_VIEW + weekView.numberOfVisibleDays = 3 + // Lets change some dimensions to best fit the view. + weekView.columnGap = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics).toInt() + weekView.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics).toInt() + weekView.eventTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics).toInt() + } + TYPE_WEEK_VIEW -> { + mWeekViewType = TYPE_WEEK_VIEW + weekView.numberOfVisibleDays = 7 + // Lets change some dimensions to best fit the view. + weekView.columnGap = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics).toInt() + weekView.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10f, resources.displayMetrics).toInt() + weekView.eventTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10f, resources.displayMetrics).toInt() + } + } + } + + /** + * Set up a date time interpreter which will show short date values when in week view and long + * date values otherwise. + * + * @param shortDate True if the date values should be short. + */ + private fun setupDateTimeInterpreter(shortDate: Boolean) { + val calendar = Calendar.getInstance() + calendar.set(Calendar.MINUTE, 0) + calendar.set(Calendar.SECOND, 0) + calendar.set(Calendar.MILLISECOND, 0) + val dateFormat = DateFormat.getTimeFormat(this@BaseActivity) + ?: SimpleDateFormat("HH:mm", Locale.getDefault()) + val format = SimpleDateFormat(" M/d", Locale.getDefault()) + weekView.dateTimeInterpreter = object : DateTimeInterpreter { + override fun interpretTime(hour: Int, minutes: Int): String { + calendar.set(Calendar.HOUR_OF_DAY, hour) + calendar.set(Calendar.MINUTE, minutes) + return dateFormat.format(calendar.time) + } + + override fun interpretDate(date: Calendar): String { + var weekday = DateUtils.getDayOfWeekString(date.get(Calendar.DAY_OF_WEEK), DateUtils.LENGTH_SHORT) + if (shortDate) { + val dayOfWeekString = DateUtils.getDayOfWeekString(date.get(Calendar.DAY_OF_WEEK), DateUtils.LENGTH_SHORTEST) + weekday = dayOfWeekString + } + return weekday + format.format(date.time) + } + + } + } + + protected fun getEventTitle(time: Calendar): String { + return String.format("Event of %02d:%02d %s/%d", time.get(Calendar.HOUR_OF_DAY), time.get(Calendar.MINUTE), time.get(Calendar.MONTH) + 1, time.get(Calendar.DAY_OF_MONTH)) + } + + override fun onEventClick(event: WeekViewEvent, eventRect: RectF) { + Toast.makeText(this, "Clicked " + event.name, Toast.LENGTH_SHORT).show() + } + + override fun onEventLongPress(event: WeekViewEvent, eventRect: RectF) { + Toast.makeText(this, "Long pressed event: " + event.name, Toast.LENGTH_SHORT).show() + } + + override fun onEmptyViewLongPress(time: Calendar) { + Toast.makeText(this, "Empty view long pressed: " + getEventTitle(time), Toast.LENGTH_SHORT).show() + } + + override fun onEmptyViewClicked(date: Calendar) { + Toast.makeText(this, "Empty view" + " clicked: " + getEventTitle(date), Toast.LENGTH_SHORT).show() + } + + override fun onMonthChange(newYear: Int, newMonth: Int): List? { + return null + } + + override fun onAddEventClicked(startTime: Calendar, endTime: Calendar) { + Toast.makeText(this, "Add event clicked.", Toast.LENGTH_SHORT).show() + } + + override fun onDrop(view: View, date: Calendar) { + Toast.makeText(this, "View dropped to " + date.toString(), Toast.LENGTH_SHORT).show() + } + + companion object { + val TYPE_DAY_VIEW = 1 + val TYPE_THREE_DAY_VIEW = 2 + val TYPE_WEEK_VIEW = 3 + } +} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/BasicActivity.java b/sample/src/main/java/com/alamkanak/weekview/sample/BasicActivity.java deleted file mode 100644 index d0473cc27..000000000 --- a/sample/src/main/java/com/alamkanak/weekview/sample/BasicActivity.java +++ /dev/null @@ -1,195 +0,0 @@ -package com.alamkanak.weekview.sample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; - -import com.alamkanak.weekview.TextColorPicker; -import com.alamkanak.weekview.WeekViewEvent; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -/** - * A basic example of how to use week view library. - * Created by Raquib-ul-Alam Kanak on 1/3/2014. - * Website: http://alamkanak.github.io - */ -public class BasicActivity extends BaseActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Typeface customTypeface = Typeface.createFromAsset(this.getAssets(), "fonts/Raleway/Raleway-Medium.ttf"); - mWeekView.setTypeface(customTypeface); - mWeekView.setTextColorPicker(new TextColorPicker() { - @Override - public int getTextColor(WeekViewEvent event) { - int color = event.getColor(); - double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255; - return a < 0.2 ? Color.BLACK : Color.WHITE; - } - }); - } - - @Override - public List onMonthChange(int newYear, int newMonth) { - // Populate the week view with some events. - List events = new ArrayList(); - - Calendar startTime = Calendar.getInstance(); - startTime.set(Calendar.HOUR_OF_DAY, 3); - startTime.set(Calendar.MINUTE, 0); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - Calendar endTime = (Calendar) startTime.clone(); - endTime.add(Calendar.HOUR, 1); - endTime.set(Calendar.MONTH, newMonth - 1); - WeekViewEvent event = new WeekViewEvent("First", getEventTitle(startTime), startTime, endTime); - event.setColor(getResources().getColor(R.color.event_color_01)); - events.add(event); - - startTime = Calendar.getInstance(); - startTime.set(Calendar.HOUR_OF_DAY, 3); - startTime.set(Calendar.MINUTE, 30); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.set(Calendar.HOUR_OF_DAY, 4); - endTime.set(Calendar.MINUTE, 30); - endTime.set(Calendar.MONTH, newMonth - 1); - event = new WeekViewEvent("Second", getEventTitle(startTime), startTime, endTime); - event.setColor(getResources().getColor(R.color.event_color_05)); - events.add(event); - - startTime = Calendar.getInstance(); - startTime.set(Calendar.HOUR_OF_DAY, 4); - startTime.set(Calendar.MINUTE, 20); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.set(Calendar.HOUR_OF_DAY, 5); - endTime.set(Calendar.MINUTE, 0); - event = new WeekViewEvent(10, getEventTitle(startTime), startTime, endTime); - event.setColor(getResources().getColor(R.color.event_color_03)); - events.add(event); - - startTime = Calendar.getInstance(); - startTime.set(Calendar.HOUR_OF_DAY, 5); - startTime.set(Calendar.MINUTE, 30); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.add(Calendar.HOUR_OF_DAY, 2); - endTime.set(Calendar.MONTH, newMonth - 1); - event = new WeekViewEvent(2, getEventTitle(startTime), startTime, endTime); - event.setColor(getResources().getColor(R.color.event_color_02)); - events.add(event); - - startTime = Calendar.getInstance(); - startTime.set(Calendar.HOUR_OF_DAY, 5); - startTime.set(Calendar.MINUTE, 0); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - startTime.add(Calendar.DATE, 1); - endTime = (Calendar) startTime.clone(); - endTime.add(Calendar.HOUR_OF_DAY, 3); - endTime.set(Calendar.MONTH, newMonth - 1); - event = new WeekViewEvent(3, getEventTitle(startTime), startTime, endTime); - event.setColor(getResources().getColor(R.color.event_color_03)); - events.add(event); - - startTime = Calendar.getInstance(); - startTime.set(Calendar.DAY_OF_MONTH, 15); - startTime.set(Calendar.HOUR_OF_DAY, 3); - startTime.set(Calendar.MINUTE, 0); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.add(Calendar.HOUR_OF_DAY, 3); - event = new WeekViewEvent(4, getEventTitle(startTime), startTime, endTime); - event.setColor(getResources().getColor(R.color.event_color_04)); - events.add(event); - - startTime = Calendar.getInstance(); - startTime.set(Calendar.DAY_OF_MONTH, 1); - startTime.set(Calendar.HOUR_OF_DAY, 3); - startTime.set(Calendar.MINUTE, 0); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.add(Calendar.HOUR_OF_DAY, 3); - event = new WeekViewEvent(5, getEventTitle(startTime), startTime, endTime); - event.setColor(getResources().getColor(R.color.event_color_01)); - events.add(event); - - startTime = Calendar.getInstance(); - startTime.set(Calendar.DAY_OF_MONTH, startTime.getActualMaximum(Calendar.DAY_OF_MONTH)); - startTime.set(Calendar.HOUR_OF_DAY, 15); - startTime.set(Calendar.MINUTE, 0); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.add(Calendar.HOUR_OF_DAY, 3); - event = new WeekViewEvent(5, getEventTitle(startTime), startTime, endTime); - event.setColor(getResources().getColor(R.color.event_color_02)); - events.add(event); - - //AllDay event - startTime = Calendar.getInstance(); - startTime.set(Calendar.HOUR_OF_DAY, 0); - startTime.set(Calendar.MINUTE, 0); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.add(Calendar.HOUR_OF_DAY, 23); - event = new WeekViewEvent(7, getEventTitle(startTime), null, startTime, endTime, true); - event.setColor(getResources().getColor(R.color.event_color_04)); - events.add(event); - events.add(event); - - startTime = Calendar.getInstance(); - startTime.set(Calendar.DAY_OF_MONTH, 8); - startTime.set(Calendar.HOUR_OF_DAY, 2); - startTime.set(Calendar.MINUTE, 0); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.set(Calendar.DAY_OF_MONTH, 10); - endTime.set(Calendar.HOUR_OF_DAY, 23); - event = new WeekViewEvent(8, getEventTitle(startTime), null, startTime, endTime, true); - event.setColor(getResources().getColor(R.color.event_color_03)); - events.add(event); - - // All day event until 00:00 next day - startTime = Calendar.getInstance(); - startTime.set(Calendar.DAY_OF_MONTH, 10); - startTime.set(Calendar.HOUR_OF_DAY, 0); - startTime.set(Calendar.MINUTE, 0); - startTime.set(Calendar.SECOND, 0); - startTime.set(Calendar.MILLISECOND, 0); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.set(Calendar.DAY_OF_MONTH, 11); - event = new WeekViewEvent(8, getEventTitle(startTime), null, startTime, endTime, true); - event.setColor(getResources().getColor(R.color.event_color_01)); - - startTime = Calendar.getInstance(); - startTime.set(Calendar.HOUR_OF_DAY, 18); - startTime.set(Calendar.MINUTE, 30); - startTime.set(Calendar.MONTH, newMonth - 1); - startTime.set(Calendar.YEAR, newYear); - endTime = (Calendar) startTime.clone(); - endTime.set(Calendar.HOUR_OF_DAY, 19); - endTime.set(Calendar.MINUTE, 30); - endTime.set(Calendar.MONTH, newMonth - 1); - event = new WeekViewEvent(22, getEventTitle(startTime), startTime, endTime); - event.setColor(getResources().getColor(R.color.event_color_02)); - events.add(event); - - return events; - } -} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/BasicActivity.kt b/sample/src/main/java/com/alamkanak/weekview/sample/BasicActivity.kt new file mode 100644 index 000000000..8f0fb28fa --- /dev/null +++ b/sample/src/main/java/com/alamkanak/weekview/sample/BasicActivity.kt @@ -0,0 +1,188 @@ +package com.alamkanak.weekview.sample + +import android.graphics.Color +import android.graphics.Typeface +import android.os.Bundle +import com.alamkanak.weekview.TextColorPicker +import com.alamkanak.weekview.WeekViewEvent +import java.util.* + +/** + * A basic example of how to use week view library. + * Created by Raquib-ul-Alam Kanak on 1/3/2014. + * Website: http://alamkanak.github.io + */ +open class BasicActivity : BaseActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val customTypeface = Typeface.createFromAsset(this.assets, "fonts/Raleway/Raleway-Medium.ttf") + weekView.setTypeface(customTypeface) + weekView.textColorPicker = object : TextColorPicker { + override fun getTextColor(event: WeekViewEvent): Int { + val color = event.color + val a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255 + return if (a < 0.2) Color.BLACK else Color.WHITE + } + } + } + + override fun onMonthChange(newYear: Int, newMonth: Int): List? { + // Populate the week view with some events. + val events = ArrayList() + + var startTime = Calendar.getInstance() + startTime.set(Calendar.HOUR_OF_DAY, 3) + startTime.set(Calendar.MINUTE, 0) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + var endTime = startTime.clone() as Calendar + endTime.add(Calendar.HOUR, 1) + endTime.set(Calendar.MONTH, newMonth - 1) + var event = WeekViewEvent("First", getEventTitle(startTime), startTime, endTime) + event.color = resources.getColor(R.color.event_color_01) + events.add(event) + + startTime = Calendar.getInstance() + startTime.set(Calendar.HOUR_OF_DAY, 3) + startTime.set(Calendar.MINUTE, 30) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.set(Calendar.HOUR_OF_DAY, 4) + endTime.set(Calendar.MINUTE, 30) + endTime.set(Calendar.MONTH, newMonth - 1) + event = WeekViewEvent("Second", getEventTitle(startTime), startTime, endTime) + event.color = resources.getColor(R.color.event_color_05) + events.add(event) + + startTime = Calendar.getInstance() + startTime.set(Calendar.HOUR_OF_DAY, 4) + startTime.set(Calendar.MINUTE, 20) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.set(Calendar.HOUR_OF_DAY, 5) + endTime.set(Calendar.MINUTE, 0) + event = WeekViewEvent(10, getEventTitle(startTime), startTime, endTime) + event.color = resources.getColor(R.color.event_color_03) + events.add(event) + + startTime = Calendar.getInstance() + startTime.set(Calendar.HOUR_OF_DAY, 5) + startTime.set(Calendar.MINUTE, 30) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.add(Calendar.HOUR_OF_DAY, 2) + endTime.set(Calendar.MONTH, newMonth - 1) + event = WeekViewEvent(2, getEventTitle(startTime), startTime, endTime) + event.color = resources.getColor(R.color.event_color_02) + events.add(event) + + startTime = Calendar.getInstance() + startTime.set(Calendar.HOUR_OF_DAY, 5) + startTime.set(Calendar.MINUTE, 0) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + startTime.add(Calendar.DATE, 1) + endTime = startTime.clone() as Calendar + endTime.add(Calendar.HOUR_OF_DAY, 3) + endTime.set(Calendar.MONTH, newMonth - 1) + event = WeekViewEvent(3, getEventTitle(startTime), startTime, endTime) + event.color = resources.getColor(R.color.event_color_03) + events.add(event) + + startTime = Calendar.getInstance() + startTime.set(Calendar.DAY_OF_MONTH, 15) + startTime.set(Calendar.HOUR_OF_DAY, 3) + startTime.set(Calendar.MINUTE, 0) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.add(Calendar.HOUR_OF_DAY, 3) + event = WeekViewEvent(4, getEventTitle(startTime), startTime, endTime) + event.color = resources.getColor(R.color.event_color_04) + events.add(event) + + startTime = Calendar.getInstance() + startTime.set(Calendar.DAY_OF_MONTH, 1) + startTime.set(Calendar.HOUR_OF_DAY, 3) + startTime.set(Calendar.MINUTE, 0) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.add(Calendar.HOUR_OF_DAY, 3) + event = WeekViewEvent(5, getEventTitle(startTime), startTime, endTime) + event.color = resources.getColor(R.color.event_color_01) + events.add(event) + + startTime = Calendar.getInstance() + startTime.set(Calendar.DAY_OF_MONTH, startTime.getActualMaximum(Calendar.DAY_OF_MONTH)) + startTime.set(Calendar.HOUR_OF_DAY, 15) + startTime.set(Calendar.MINUTE, 0) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.add(Calendar.HOUR_OF_DAY, 3) + event = WeekViewEvent(5, getEventTitle(startTime), startTime, endTime) + event.color = resources.getColor(R.color.event_color_02) + events.add(event) + + //AllDay event + startTime = Calendar.getInstance() + startTime.set(Calendar.HOUR_OF_DAY, 0) + startTime.set(Calendar.MINUTE, 0) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.add(Calendar.HOUR_OF_DAY, 23) + event = WeekViewEvent(7, getEventTitle(startTime), null, startTime, endTime, true) + event.color = resources.getColor(R.color.event_color_04) + events.add(event) + events.add(event) + + startTime = Calendar.getInstance() + startTime.set(Calendar.DAY_OF_MONTH, 8) + startTime.set(Calendar.HOUR_OF_DAY, 2) + startTime.set(Calendar.MINUTE, 0) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.set(Calendar.DAY_OF_MONTH, 10) + endTime.set(Calendar.HOUR_OF_DAY, 23) + event = WeekViewEvent(8, getEventTitle(startTime), null, startTime, endTime, true) + event.color = resources.getColor(R.color.event_color_03) + events.add(event) + + // All day event until 00:00 next day + startTime = Calendar.getInstance() + startTime.set(Calendar.DAY_OF_MONTH, 10) + startTime.set(Calendar.HOUR_OF_DAY, 0) + startTime.set(Calendar.MINUTE, 0) + startTime.set(Calendar.SECOND, 0) + startTime.set(Calendar.MILLISECOND, 0) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.set(Calendar.DAY_OF_MONTH, 11) + event = WeekViewEvent(8, getEventTitle(startTime), null, startTime, endTime, true) + event.color = resources.getColor(R.color.event_color_01) + + startTime = Calendar.getInstance() + startTime.set(Calendar.HOUR_OF_DAY, 18) + startTime.set(Calendar.MINUTE, 30) + startTime.set(Calendar.MONTH, newMonth - 1) + startTime.set(Calendar.YEAR, newYear) + endTime = startTime.clone() as Calendar + endTime.set(Calendar.HOUR_OF_DAY, 19) + endTime.set(Calendar.MINUTE, 30) + endTime.set(Calendar.MONTH, newMonth - 1) + event = WeekViewEvent(22, getEventTitle(startTime), startTime, endTime) + event.color = resources.getColor(R.color.event_color_02) + events.add(event) + + return events + } +} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/MainActivity.java b/sample/src/main/java/com/alamkanak/weekview/sample/MainActivity.java deleted file mode 100644 index dc20a1477..000000000 --- a/sample/src/main/java/com/alamkanak/weekview/sample/MainActivity.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.alamkanak.weekview.sample; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.view.View; - -/** - * The launcher activity of the sample app. It contains the links to visit all the example screens. - * Created by Raquib-ul-Alam Kanak on 7/21/2014. - * Website: http://alamkanak.github.io - */ -public class MainActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - findViewById(R.id.buttonBasic).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(MainActivity.this, BasicActivity.class); - startActivity(intent); - } - }); - - findViewById(R.id.buttonAsynchronous).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(MainActivity.this, AsynchronousActivity.class); - startActivity(intent); - } - }); - } -} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/MainActivity.kt b/sample/src/main/java/com/alamkanak/weekview/sample/MainActivity.kt new file mode 100644 index 000000000..967374af3 --- /dev/null +++ b/sample/src/main/java/com/alamkanak/weekview/sample/MainActivity.kt @@ -0,0 +1,32 @@ +package com.alamkanak.weekview.sample + +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.view.View + +/** + * The launcher activity of the sample app. It contains the links to visit all the example screens. + * Created by Raquib-ul-Alam Kanak on 7/21/2014. + * Website: http://alamkanak.github.io + */ +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + findViewById(R.id.buttonBasic).setOnClickListener { + val intent = Intent(this@MainActivity, BasicActivity::class.java) + startActivity(intent) + } + + findViewById(R.id.buttonAsynchronous).setOnClickListener { + val intent = Intent(this@MainActivity, AsynchronousActivity::class.java) + startActivity(intent) + } + findViewById(R.id.buttonWholeViewSnap).setOnClickListener { + startActivity(Intent(this@MainActivity, WholeViewSnappingActivity::class.java)) + } + } +} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/WholeViewSnappingActivity.kt b/sample/src/main/java/com/alamkanak/weekview/sample/WholeViewSnappingActivity.kt new file mode 100644 index 000000000..892c969fb --- /dev/null +++ b/sample/src/main/java/com/alamkanak/weekview/sample/WholeViewSnappingActivity.kt @@ -0,0 +1,38 @@ +package com.alamkanak.weekview.sample + +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import kotlinx.android.synthetic.main.activity_base.* +import java.util.* + +/** + * A basic example of how to use week view library. + * Created by Raquib-ul-Alam Kanak on 1/3/2014. + * Website: http://alamkanak.github.io + */ +class WholeViewSnappingActivity : BasicActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + weekView.isShowNowLine = true +// weekView.setAutoLimitTime(true) + weekView.setMinTime(0) + weekView.setMaxTime(24) + weekView.isScrollNumberOfVisibleDays = true + setDayViewType(TYPE_WEEK_VIEW) + val cal = Calendar.getInstance() + cal.set(Calendar.DAY_OF_WEEK, cal.firstDayOfWeek) + weekView.goToDate(cal) + draggable_view.visibility = View.GONE + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == R.id.action_today) { + val cal = Calendar.getInstance() + cal.set(Calendar.DAY_OF_WEEK, cal.firstDayOfWeek) + weekView.goToDate(cal) + return true + } + return super.onOptionsItemSelected(item) + } +} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/Event.java b/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/Event.java deleted file mode 100644 index 406409e17..000000000 --- a/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/Event.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.alamkanak.weekview.sample.apiclient; - -import android.annotation.SuppressLint; -import android.graphics.Color; - -import com.alamkanak.weekview.WeekViewEvent; -import com.google.gson.annotations.Expose; -import com.google.gson.annotations.SerializedName; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; - -/** - * An event model that was built for automatic serialization from json to object. - * Created by Raquib-ul-Alam Kanak on 1/3/16. - * Website: http://alamkanak.github.io - */ -public class Event { - - @Expose - @SerializedName("name") - private String mName; - @Expose - @SerializedName("dayOfMonth") - private int mDayOfMonth; - @Expose - @SerializedName("startTime") - private String mStartTime; - @Expose - @SerializedName("endTime") - private String mEndTime; - @Expose - @SerializedName("color") - private String mColor; - - public String getName() { - return mName; - } - - public void setName(String name) { - this.mName = name; - } - - public int getDayOfMonth() { - return mDayOfMonth; - } - - public void setDayOfMonth(int dayOfMonth) { - this.mDayOfMonth = dayOfMonth; - } - - public String getStartTime() { - return mStartTime; - } - - public void setStartTime(String startTime) { - this.mStartTime = startTime; - } - - public String getEndTime() { - return mEndTime; - } - - public void setEndTime(String endTime) { - this.mEndTime = endTime; - } - - public String getColor() { - return mColor; - } - - public void setColor(String color) { - this.mColor = color; - } - - @SuppressLint("SimpleDateFormat") - public WeekViewEvent toWeekViewEvent() { - - // Parse time. - SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); - Date start = new Date(); - Date end = new Date(); - try { - start = sdf.parse(getStartTime()); - } catch (ParseException e) { - e.printStackTrace(); - } - try { - end = sdf.parse(getEndTime()); - } catch (ParseException e) { - e.printStackTrace(); - } - - // Initialize start and end time. - Calendar now = Calendar.getInstance(); - Calendar startTime = (Calendar) now.clone(); - startTime.setTimeInMillis(start.getTime()); - startTime.set(Calendar.YEAR, now.get(Calendar.YEAR)); - startTime.set(Calendar.MONTH, now.get(Calendar.MONTH)); - startTime.set(Calendar.DAY_OF_MONTH, getDayOfMonth()); - Calendar endTime = (Calendar) startTime.clone(); - endTime.setTimeInMillis(end.getTime()); - endTime.set(Calendar.YEAR, startTime.get(Calendar.YEAR)); - endTime.set(Calendar.MONTH, startTime.get(Calendar.MONTH)); - endTime.set(Calendar.DAY_OF_MONTH, startTime.get(Calendar.DAY_OF_MONTH)); - - // Create an week view event. - WeekViewEvent weekViewEvent = new WeekViewEvent(); - weekViewEvent.setIdentifier(getName()); - weekViewEvent.setName(getName()); - weekViewEvent.setStartTime(startTime); - weekViewEvent.setEndTime(endTime); - weekViewEvent.setColor(Color.parseColor(getColor())); - - return weekViewEvent; - } -} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/Event.kt b/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/Event.kt new file mode 100644 index 000000000..4de253a30 --- /dev/null +++ b/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/Event.kt @@ -0,0 +1,77 @@ +package com.alamkanak.weekview.sample.apiclient + +import android.annotation.SuppressLint +import android.graphics.Color +import com.alamkanak.weekview.WeekViewEvent +import com.google.gson.annotations.Expose +import com.google.gson.annotations.SerializedName +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.* + +/** + * An event model that was built for automatic serialization from json to object. + * Created by Raquib-ul-Alam Kanak on 1/3/16. + * Website: http://alamkanak.github.io + */ +class Event { + + @Expose + @SerializedName("name") + var name: String? = null + @Expose + @SerializedName("dayOfMonth") + var dayOfMonth: Int = 0 + @Expose + @SerializedName("startTime") + var startTime: String? = null + @Expose + @SerializedName("endTime") + var endTime: String? = null + @Expose + @SerializedName("color") + var color: String? = null + + @SuppressLint("SimpleDateFormat") + fun toWeekViewEvent(): WeekViewEvent { + + // Parse time. + val sdf = SimpleDateFormat("HH:mm") + var start = Date() + var end = Date() + try { + start = sdf.parse(startTime) + } catch (e: ParseException) { + e.printStackTrace() + } + + try { + end = sdf.parse(endTime) + } catch (e: ParseException) { + e.printStackTrace() + } + + // Initialize start and end time. + val now = Calendar.getInstance() + val startTime = now.clone() as Calendar + startTime.timeInMillis = start.time + startTime.set(Calendar.YEAR, now.get(Calendar.YEAR)) + startTime.set(Calendar.MONTH, now.get(Calendar.MONTH)) + startTime.set(Calendar.DAY_OF_MONTH, dayOfMonth) + val endTime = startTime.clone() as Calendar + endTime.timeInMillis = end.time + endTime.set(Calendar.YEAR, startTime.get(Calendar.YEAR)) + endTime.set(Calendar.MONTH, startTime.get(Calendar.MONTH)) + endTime.set(Calendar.DAY_OF_MONTH, startTime.get(Calendar.DAY_OF_MONTH)) + + // Create an week view event. + val weekViewEvent = WeekViewEvent() + weekViewEvent.identifier = name + weekViewEvent.name = name + weekViewEvent.startTime = startTime + weekViewEvent.endTime = endTime + weekViewEvent.color = Color.parseColor(color) + + return weekViewEvent + } +} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/MyJsonService.java b/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/MyJsonService.java deleted file mode 100644 index cebe4c8c3..000000000 --- a/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/MyJsonService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.alamkanak.weekview.sample.apiclient; - -import java.util.List; - -import retrofit.Callback; -import retrofit.http.GET; - -/** - * Created by Raquib-ul-Alam Kanak on 1/3/16. - * Website: http://alamkanak.github.io - */ -public interface MyJsonService { - - @GET("/1kpjf") - void listEvents(Callback> eventsCallback); - -} diff --git a/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/MyJsonService.kt b/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/MyJsonService.kt new file mode 100644 index 000000000..8458ea0b5 --- /dev/null +++ b/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/MyJsonService.kt @@ -0,0 +1,15 @@ +package com.alamkanak.weekview.sample.apiclient + +import retrofit.Callback +import retrofit.http.GET + +/** + * Created by Raquib-ul-Alam Kanak on 1/3/16. + * Website: http://alamkanak.github.io + */ +interface MyJsonService { + + @GET("/1kpjf") + fun listEvents(eventsCallback: Callback>) + +} diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index f8e348f0b..f5aec3c72 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -1,23 +1,17 @@ - +