diff --git a/README.md b/README.md
index abdd676f4..33d3b718c 100644
--- a/README.md
+++ b/README.md
@@ -1,60 +1,13 @@
-[Join our slack](https://join.slack.com/t/androidweekview/shared_invite/enQtMzEyMDE3NzU3NTM3LWQyZGRhNjRlMTUzNzNlNjNlM2M0OTMyMDhjMzE1NDMzOGQzYzhjNzI2YjZhZWM3MzJiY2I1YmY2NGEwOTlkNTY)
+A fork of this repository:
+https://github.com/Quachero/Android-Week-View
+Which is a fork of this:
+https://github.com/Quivr/Android-Week-View
+Which is a fork of this:
+https://github.com/alamkanak/Android-Week-View
+All because all of those aren't functional anymore...
-Android Week View
-=================
+I've added so many things compared to them. You can read about them in a pull request I've made:
+https://github.com/Quivr/Android-Week-View/pull/97
+https://github.com/Quachero/Android-Week-View/pull/1
-**Android Week View** is an android library to display calendars (week view or day view) within the app. It supports custom styling.
-
-[Why this fork? (features + community & contributing)](https://github.com/Quivr/Android-Week-View/issues/45)
-
-![](images/screen-shot.png)
-
-Features
-------------
-
-* Week view calendar
-* Day view calendar
-* Custom styling
-* Vertical scrolling and zooming
-* Infinite horizontal scrolling
-* Possibility to set min and max date
-* Possibility to set range of visible hours
-* All day events at the top
-* Live preview of custom styling in xml preview window
-
-Who uses it
----------------
-
-* [Quivr](https://quivr.be/en/)
-* [Series Addict](https://play.google.com/store/apps/details?id=com.alamkanak.seriesaddict)
-* [Unicaen Timetable](https://play.google.com/store/apps/details?id=fr.skyost.timetable)
-* Using the library? Just make an issue
-
-Getting Started
----------------
-
-See the [wiki](https://github.com/Quivr/Android-Week-View/wiki)
-
-Sample
-----------
-
-There is also a [sample app](https://github.com/quivr/Android-Week-View/tree/master/sample) to get you started.
-
-
-License
-----------
-
- Copyright 2014 Raquib-ul-Alam
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/build.gradle b/build.gradle
index bd1e1fd48..5b646abf7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext.kotlin_version = '1.3.21'
repositories {
jcenter()
+ google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.3'
+ classpath 'com.android.tools.build:gradle:3.3.1'
+ 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
@@ -18,6 +21,7 @@ allprojects {
group = GROUP
repositories {
+ google()
jcenter()
}
}
diff --git a/gradle.properties b/gradle.properties
index 998877f4f..9adbf9d4e 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -9,4 +9,6 @@ POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=quivr
-POM_DEVELOPER_NAME=Quivr
\ No newline at end of file
+POM_DEVELOPER_NAME=Quivr
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 66b2b75d1..2cb0ce4da 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
+#Fri Feb 08 08:07:15 IST 2019
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-5.1.1-all.zip
diff --git a/library/build.gradle b/library/build.gradle
index a9291dd32..191f15056 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -1,16 +1,16 @@
apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
repositories {
mavenCentral()
}
android {
- compileSdkVersion 25
- buildToolsVersion '25.0.2'
+ compileSdkVersion 28
defaultConfig {
- minSdkVersion 9
- targetSdkVersion 25
+ minSdkVersion 14
+ targetSdkVersion 28
}
}
@@ -18,8 +18,9 @@ configurations {
javadocDeps
}
dependencies {
- compile 'com.android.support:appcompat-v7:25.1.0'
- javadocDeps 'com.android.support:appcompat-v7:25.1.0'
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ javadocDeps 'androidx.appcompat:appcompat:1.0.2'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
-apply from: 'gradle-mvn-push.gradle'
\ No newline at end of file
+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/project.properties b/library/project.properties
index 7568f54a0..efe3b01bc 100644
--- a/library/project.properties
+++ b/library/project.properties
@@ -1 +1,2 @@
-android.library=true
\ No newline at end of file
+# suppress inspection "UnusedProperty" for whole file
+android.library=true
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..c805d8ac3
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/DateTimeInterpreter.kt
@@ -0,0 +1,11 @@
+package com.alamkanak.weekview
+
+import java.util.*
+
+interface DateTimeInterpreter {
+ fun getFormattedWeekDayTitle(date: Calendar): String
+
+ fun getFormattedTimeOfDay(hour: Int, minutes: Int): String
+
+
+}
diff --git a/library/src/main/java/com/alamkanak/weekview/DrawPerformanceTester.kt b/library/src/main/java/com/alamkanak/weekview/DrawPerformanceTester.kt
new file mode 100644
index 000000000..1a8cc4b08
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/DrawPerformanceTester.kt
@@ -0,0 +1,27 @@
+package com.alamkanak.weekview
+
+import android.util.Log
+
+class DrawPerformanceTester(val measureDrawTime: Boolean = true) {
+ var drawSamplesCount = 0L
+ var drawTotalTime = 0L
+
+ private var startTime: Long = 0L
+
+ fun startMeasure() {
+ if (!measureDrawTime)
+ return
+ startTime = System.currentTimeMillis()
+ }
+
+ fun endMeasure() {
+ if (!measureDrawTime)
+ return
+ val endTime = System.currentTimeMillis()
+ val totalTime = endTime - startTime
+ ++drawSamplesCount
+ drawTotalTime += totalTime
+ val drawAverageTime = drawTotalTime.toFloat() / drawSamplesCount.toFloat()
+ Log.d("AppLog", "currentTime:$totalTime average:$drawAverageTime")
+ }
+}
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 extends WeekViewEvent> 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 extends WeekViewEvent> 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..adf9b6139
--- /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): MutableList? {
+ 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): MutableList?
+ }
+}
diff --git a/library/src/main/java/com/alamkanak/weekview/PrefetchingWeekViewLoader.kt b/library/src/main/java/com/alamkanak/weekview/PrefetchingWeekViewLoader.kt
new file mode 100644
index 000000000..3bafb444d
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/PrefetchingWeekViewLoader.kt
@@ -0,0 +1,45 @@
+package com.alamkanak.weekview
+
+import androidx.annotation.IntRange
+import java.util.*
+
+/**
+ *
PrefetchingWeekViewLoader
+ * This class provides prefetching data loading behaviour.
+ * By setting a specific period of N, data is retrieved for the current period,
+ * the next N periods and the previous N periods.
+ */
+/**
+ * @param weekViewLoader An instance of the WeekViewLoader class
+ * @param prefetchingPeriod The amount of periods to be fetched before and after the
+ * current period. Must be 1 or greater.
+ */
+class PrefetchingWeekViewLoader(val weekViewLoader: WeekViewLoader, @IntRange(from = 1L) val prefetchingPeriod: Int = 1) : WeekViewLoader {
+
+ init {
+ if (prefetchingPeriod < 1)
+ throw IllegalArgumentException("Must specify prefetching period of at least 1!")
+ }
+
+ override fun onLoad(periodIndex: Int): MutableList? {
+ // fetch the current period
+ var loadedEvents = weekViewLoader.onLoad(periodIndex)
+ val events = ArrayList()
+ if (loadedEvents != null)
+ events.addAll(loadedEvents)
+ // fetch periods before/after
+ for (i in 1..this.prefetchingPeriod) {
+ loadedEvents = weekViewLoader.onLoad(periodIndex - i)
+ if (loadedEvents != null)
+ events.addAll(loadedEvents)
+ loadedEvents = weekViewLoader.onLoad(periodIndex + i)
+ if (loadedEvents != null)
+ events.addAll(loadedEvents)
+ }
+ // return list of all events together
+ return events
+ }
+
+ override fun toWeekViewPeriodIndex(instance: Calendar) = weekViewLoader.toWeekViewPeriodIndex(instance)
+
+}
diff --git a/library/src/main/java/com/alamkanak/weekview/SimpleDate.kt b/library/src/main/java/com/alamkanak/weekview/SimpleDate.kt
new file mode 100644
index 000000000..7571e9c34
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/SimpleDate.kt
@@ -0,0 +1,9 @@
+package com.alamkanak.weekview
+
+import java.util.*
+
+data class SimpleDate(val year: Int, val month: Int, val dayOfMonth: Int) {
+ override fun toString(): String = "$year-$month-$dayOfMonth"
+
+ constructor(cal: Calendar) : this(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH))
+}
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..bc1d7b6f0
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/TextColorPicker.kt
@@ -0,0 +1,10 @@
+package com.alamkanak.weekview
+
+import androidx.annotation.ColorInt
+
+interface TextColorPicker {
+
+ @ColorInt
+ fun getTextColor(event: WeekViewEvent): Int
+
+}
diff --git a/library/src/main/java/com/alamkanak/weekview/TimeChangedBroadcastReceiver.kt b/library/src/main/java/com/alamkanak/weekview/TimeChangedBroadcastReceiver.kt
new file mode 100644
index 000000000..56b1754a9
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/TimeChangedBroadcastReceiver.kt
@@ -0,0 +1,38 @@
+package com.alamkanak.weekview
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import java.util.*
+
+/**a broadcast receiver that tells you when the time has changed (minute, date, configuration of time/date...), based on: https://stackoverflow.com/a/48782963/878126 */
+abstract class TimeChangedBroadcastReceiver : BroadcastReceiver() {
+ private var curCal = Calendar.getInstance()
+
+ abstract fun onTimeChanged()
+
+ @Suppress("MemberVisibilityCanBePrivate")
+ fun register(context: Context, cal: Calendar) {
+ curCal = cal
+ val filter = IntentFilter()
+ filter.addAction(Intent.ACTION_TIME_CHANGED)
+ filter.addAction(Intent.ACTION_DATE_CHANGED)
+ filter.addAction(Intent.ACTION_TIME_TICK)
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
+ context.registerReceiver(this, filter)
+ val newDate = Calendar.getInstance()
+ if (!WeekViewUtil.isSameDayAndHourAndMinute(newDate, curCal)) {
+ curCal = newDate
+ onTimeChanged()
+ }
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ val newTime = Calendar.getInstance()
+ if (!WeekViewUtil.isSameDayAndHourAndMinute(newTime, curCal)) {
+ curCal = newTime
+ onTimeChanged()
+ }
+ }
+}
diff --git a/library/src/main/java/com/alamkanak/weekview/WeekDaySubtitleInterpreter.kt b/library/src/main/java/com/alamkanak/weekview/WeekDaySubtitleInterpreter.kt
new file mode 100644
index 000000000..0add99ecd
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/WeekDaySubtitleInterpreter.kt
@@ -0,0 +1,7 @@
+package com.alamkanak.weekview
+
+import java.util.*
+
+interface WeekDaySubtitleInterpreter {
+ fun getFormattedWeekDaySubtitle(date: Calendar): String
+}
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 f6685e020..000000000
--- a/library/src/main/java/com/alamkanak/weekview/WeekView.java
+++ /dev/null
@@ -1,2854 +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;
-
- // 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.MONDAY;
- 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;
-
- // 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) {
- 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 ((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:
- 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);
- } 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 extends WeekViewEvent> 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 extends WeekViewEvent> 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;
- }
-
- /////////////////////////////////////////////////////////////////
- //
- // Functions related to scrolling.
- //
- /////////////////////////////////////////////////////////////////
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- 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);
-
- 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);
- }
-
- 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;
- }
-
-
- @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..eb20f0aae
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/WeekView.kt
@@ -0,0 +1,2529 @@
+package com.alamkanak.weekview
+
+import android.content.Context
+import android.graphics.*
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import androidx.annotation.ColorInt
+import androidx.core.view.GestureDetectorCompat
+import androidx.core.view.ViewCompat
+import androidx.interpolator.view.animation.FastOutLinearInInterpolator
+import androidx.appcompat.content.res.AppCompatResources
+import android.text.*
+import android.text.format.DateFormat
+import android.text.style.StyleSpan
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.*
+import android.widget.OverScroller
+import com.alamkanak.weekview.MonthLoader.MonthChangeListener
+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.*
+
+class WeekView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
+ //region fields and properties
+ val drawPerformanceTester = DrawPerformanceTester(false)
+ private var mHomeDate: Calendar? = null
+ /**
+ * the earliest day that can be displayed. null if no minimum date is set.
+ */
+ var minDate: Calendar? = null
+ set(value) {
+ if (value === field)
+ return
+ if (value != null) {
+ value.set(Calendar.HOUR_OF_DAY, 0)
+ value.set(Calendar.MINUTE, 0)
+ value.set(Calendar.SECOND, 0)
+ value.set(Calendar.MILLISECOND, 0)
+ if (maxDate != null && value.after(maxDate)) {
+ throw IllegalArgumentException("minDate cannot be later than maxDate")
+ }
+ if (field != null && WeekViewUtil.isSameDay(field!!, value))
+ return
+ }
+ field = value
+ resetHomeDate()
+ mCurrentOrigin.x = 0f
+ invalidate()
+ }
+ /**
+ * the latest day that can be displayed. null if no maximum date is set.
+ */
+ var maxDate: Calendar? = null
+ set(value) {
+ if (field === value)
+ return
+ if (value != null) {
+ value.set(Calendar.HOUR_OF_DAY, 0)
+ value.set(Calendar.MINUTE, 0)
+ value.set(Calendar.SECOND, 0)
+ value.set(Calendar.MILLISECOND, 0)
+ if (minDate != null && value.before(minDate)) {
+ throw IllegalArgumentException("maxDate has to be after minDate")
+ }
+ if (field != null && WeekViewUtil.isSameDay(field!!, value))
+ return
+ }
+ field = value
+ resetHomeDate()
+ mCurrentOrigin.x = 0f
+ invalidate()
+ }
+ private val timeTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+ private val mHeaderWeekDayTitleTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+ private val mHeaderWeekDaySubtitleTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+ private val mHeaderWeekDayTitleTodayTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+ private val mHeaderWeekDaySubtitleTodayTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+ private val mEventTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG or Paint.LINEAR_TEXT_FLAG)
+ private val sideTitleTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+ private val sideSubtitleTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+ private val allDaySideTitleTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+ private val mEmptyEventPaint = Paint()
+ private val mHeaderBackgroundPaint: Paint = Paint()
+ private val mDayBackgroundPaint: Paint = Paint()
+ private val mHourSeparatorPaint = Paint()
+ private val mTodayColumnBackgroundPaint: Paint = Paint()
+ private val mFutureBackgroundPaint: Paint = Paint()
+ private val mPastBackgroundPaint = Paint()
+ private val mFutureWeekendBackgroundPaint = Paint()
+ private val mPastWeekendBackgroundPaint = Paint()
+ private val mNowLinePaint = Paint()
+ private val mEventBackgroundPaint = Paint()
+ private val mNewEventBackgroundPaint = Paint()
+ private var containsAllDayEvent: Boolean = false
+ private var timeTextWidth: Float = 0f
+ private var timeTextHeight: Float = 0f
+ var headerWeekDayTitleTextHeight: Float = 0f
+ private set
+ private var headerHeight: Float = 0f
+ var headerWeekDaySubtitleTextHeight: Float = 0f
+ private set
+ private var mGestureDetector: GestureDetectorCompat? = null
+ private var mScroller: OverScroller? = null
+ private val mCurrentOrigin = PointF(0f, 0f)
+ private var mCurrentScrollDirection = Direction.NONE
+ private var widthPerDay: Float = 0f
+ private var mHeaderColumnWidth: Float = 0f
+ private var eventRects: MutableList? = null
+ private val mEvents = ArrayList()
+ private var mFetchedPeriod = -1 // the middle period the calendar has fetched.
+ private var mRefreshEvents = false
+ private var mCurrentFlingDirection = Direction.NONE
+ private val scaleDetector = ScaleGestureDetector(context, WeekViewGestureListener())
+ private var mIsZooming: Boolean = false
+ /**
+ * the first visible day in the week view.
+ */
+ var firstVisibleDay: Calendar? = null
+ private set
+
+ private var mMinimumFlingVelocity = 0
+ private var mScaledTouchSlop = 0
+ private var mNewEventRect: EventRect? = null
+ var textColorPicker: TextColorPicker? = null
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+ private var mSizeOfWeekView: Float = 0f
+ private var mDistanceDone = 0f
+ private var mDistanceMin: Float = 0f
+ private var mOffsetValueToSecureScreen = 9
+ private var mStartOriginForScroll = 0f
+ 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 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 125f, resources.displayMetrics).toInt()
+ 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
+ /**
+ * the scrolling speed factor in horizontal direction.
+ */
+ var xScrollingSpeed = 1f
+ private var mScrollToDay: Calendar? = null
+ private var mScrollToHour = -1.0
+ /**
+ * corner radius for event rect (in px)
+ */
+ var eventCornerRadius = 0.0f
+ /**
+ * whether the week view should fling horizontally.
+ */
+ var isHorizontalFlingEnabled = true
+ /**
+ * whether the week view should fling vertically.
+ */
+ var isVerticalFlingEnabled = true
+ /**
+ * the height of AllDay-events.
+ */
+ var allDayEventHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40f, resources.displayMetrics).toInt()
+ /**
+ * 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.
+ */
+ var isZoomFocusPointEnabled = true
+ var scrollDuration = 250
+ var timeColumnResolution = 60
+ var typeface: Typeface? = Typeface.DEFAULT_BOLD
+ set(value) {
+ if (value == field)
+ return
+ if (value != null) {
+ field = value
+ init()
+ }
+ }
+ var allDaySideTitleText: String? = null
+ set(value) {
+ if (value == field)
+ return
+ field = value
+ invalidate()
+ }
+
+ private var mMinTime = 0
+ private var mMaxTime = 24
+
+ /**
+ * auto calculate limit time on events in visible days.
+ */
+ var autoLimitTime = false
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var minOverlappingMinutes = 0
+
+ // Listeners.
+ var eventClickListener: EventClickListener? = null
+ var eventLongPressListener: EventLongPressListener? = null
+ /**
+ * 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.
+ */
+ val weekViewLoader: WeekViewLoader = PrefetchingWeekViewLoader(MonthLoader(object : MonthChangeListener {
+ override fun onMonthChange(newYear: Int, newMonth: Int): MutableList? {
+ return monthChangeListener?.onMonthChange(newYear, newMonth)
+ }
+ }))
+ var emptyViewClickListener: EmptyViewClickListener? = null
+ var emptyViewLongPressListener: EmptyViewLongPressListener? = null
+ var scrollListener: ScrollListener? = null
+ var addEventClickListener: AddEventClickListener? = null
+ var dropListener: DropListener? = null
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ setOnDragListener(if (value != null) DragListener() else null)
+ }
+
+ var enableDrawHeaderBackgroundOnlyOnWeekDays = false
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+ var sideTitleText: String? = null
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+ var sideSubtitleText: String? = null
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+ var untitledEventText: String? = null
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ private val mGestureListener = object : GestureDetector.SimpleOnGestureListener() {
+ override fun onDown(e: MotionEvent): Boolean {
+ 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.
+ mCurrentScrollDirection = if (Math.abs(distanceX) > Math.abs(distanceY)) {
+ if (distanceX > 0) {
+ Direction.LEFT
+ } else {
+ Direction.RIGHT
+ }
+ } else {
+ 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
+
+ mDistanceDone = if (e2.x < 0) {
+ e2.x - e1.x
+ } else {
+ e1.x - e2.x
+ }
+
+ when {
+ mCurrentOrigin.x - distanceX * xScrollingSpeed > maxX -> mCurrentOrigin.x = maxX
+ 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
+ when {
+ mCurrentOrigin.y - distanceY > maxY -> mCurrentOrigin.y = maxY
+ 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 (!isScrollNumberOfVisibleDays) {
+ 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 (eventRects != null && eventClickListener != null)
+ for (eventRect in eventRects!!) {
+ if (newEventIdentifier != eventRect.event.id && 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 > headerHeight + weekDaysHeaderRowTotalPadding + spaceBelowAllDayEvents) {
+ 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 = hourHeight * getPassedMinutesInDay(selectedTime) / 60 + eventsTop
+ val bottom = hourHeight * getPassedMinutesInDay(endTime) / 60 + eventsTop
+ // Calculate left and right.
+ val left = widthPerDay * WeekViewUtil.daysBetween(firstVisibleDay!!, selectedTime)
+ val right = left + widthPerDay
+ // 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 = newEventColor
+ mNewEventRect = EventRect(newEvent, newEvent, dayRectF)
+ tempEvents.add(newEvent)
+ clearEvents()
+ cacheAndSortEvents(tempEvents)
+ computePositionOfEvents(eventRects!!)
+ invalidate()
+ }
+ }
+ }
+ }
+ return super.onSingleTapConfirmed(e)
+ }
+
+ override fun onLongPress(e: MotionEvent) {
+ super.onLongPress(e)
+ if (eventLongPressListener != null && eventRects != null) {
+ for (event in eventRects!!) {
+ 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 > headerHeight + weekDaysHeaderRowTotalPadding + spaceBelowAllDayEvents) {
+ 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() = -(((hourHeight * (mMaxTime - mMinTime)).toFloat()
+ + headerHeight
+ + weekDaysHeaderRowTotalPadding
+ + spaceBelowAllDayEvents
+ + timeTextHeight / 2) - height)
+
+ private val yMaxLimit: Float
+ get() = 0f
+
+ private val xMinLimit: Float
+ get() {
+ return if (maxDate == null) {
+ 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)
+ }
+
+ getXOriginForDate(date)
+ }
+ }
+
+ private val xMaxLimit: Float
+ get() = if (minDate == null) {
+ Integer.MAX_VALUE.toFloat()
+ } else {
+ getXOriginForDate(minDate!!)
+ }
+
+ private val minHourOffset: Int
+ get() = hourHeight * mMinTime
+
+ private
+ val eventsTop: Float
+ get() = mCurrentOrigin.y + headerHeight + weekDaysHeaderRowTotalPadding + timeTextHeight / 2 - minHourOffset
+
+ private val leftDaysWithGaps: Int
+ get() = (-Math.ceil((mCurrentOrigin.x / (widthPerDay + columnGap)).toDouble())).toInt()
+
+ private val xStartPixel: Float
+ get() = mCurrentOrigin.x + (widthPerDay + columnGap) * leftDaysWithGaps +
+ mHeaderColumnWidth
+
+ var monthChangeListener: MonthChangeListener? = null
+
+ /**
+ * the interpreter which provides the text to show in the header column and the header row.
+ */
+ var dateTimeInterpreter: DateTimeInterpreter = object : DateTimeInterpreter {
+ val calendar = Calendar.getInstance()
+ val timeFormat = DateFormat.getTimeFormat(context)
+ ?: SimpleDateFormat("HH:mm", Locale.getDefault())
+ val shortDateFormat = WeekViewUtil.getWeekdayWithNumericDayAndMonthFormat(context, true)
+ val normalDateFormat = WeekViewUtil.getWeekdayWithNumericDayAndMonthFormat(context, false)
+
+ init {
+ calendar.set(Calendar.MINUTE, 0)
+ calendar.set(Calendar.SECOND, 0)
+ calendar.set(Calendar.MILLISECOND, 0)
+ }
+
+ override fun getFormattedTimeOfDay(hour: Int, minutes: Int): String {
+ calendar.set(Calendar.HOUR_OF_DAY, hour)
+ calendar.set(Calendar.MINUTE, minutes)
+ return timeFormat.format(calendar.time)
+ }
+
+ override fun getFormattedWeekDayTitle(date: Calendar): String {
+ val shortDate = dayNameLength == LENGTH_SHORT
+ return if (shortDate) shortDateFormat.format(date.time) else normalDateFormat.format(date.time)
+ }
+ }
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ timeFormatterCache.clear()
+ initTextTimeWidth()
+ }
+
+ var weekDaySubtitleInterpreter: WeekDaySubtitleInterpreter? = null
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ /**
+ * the real number of visible days
+ * If the amount of days between max date and min date is smaller, that value is returned
+ */
+ val realNumberOfVisibleDays: Int
+ get() = if (minDate == null || maxDate == null) numberOfVisibleDays else Math.min(numberOfVisibleDays, daysBetween(minDate!!, maxDate!!) + 1)
+
+ /**
+ * the number of visible days in a week.
+ */
+ var numberOfVisibleDays: Int = 3
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ resetHomeDate()
+ mCurrentOrigin.x = 0f
+ mCurrentOrigin.y = 0f
+ invalidate()
+ }
+
+ var hourHeight: Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50f, resources.displayMetrics).toInt()
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var columnGap: Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt()
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ /**
+ * 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.
+ *
+ * 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 = Calendar.getInstance().firstDayOfWeek
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var textSize: Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12.0f, context.resources.displayMetrics)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ timeTextPaint.textSize = value
+ allDaySideTitleTextPaint.textSize = value
+ invalidate()
+ }
+
+ var headerWeekDayTitleTextSize: Float = textSize
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mHeaderWeekDayTitleTextPaint.textSize = value
+ mHeaderWeekDayTitleTodayTextPaint.textSize = value
+ sideTitleTextPaint.textSize = value
+ invalidate()
+ }
+
+ var headerWeekDaySubtitleTextSize: Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 22.0f, context.resources.displayMetrics)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mHeaderWeekDaySubtitleTextPaint.textSize = value
+ mHeaderWeekDaySubtitleTodayTextPaint.textSize = value
+ sideSubtitleTextPaint.textSize = value
+ invalidate()
+ }
+
+ var headerColumnPadding: Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, resources.displayMetrics)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mHeaderColumnWidth = timeTextWidth + headerColumnPadding * 2.0f
+ invalidate()
+ }
+
+ @ColorInt
+ var headerColumnTextColor: Int = Color.BLACK
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mHeaderWeekDayTitleTextPaint.color = value
+ mHeaderWeekDaySubtitleTextPaint.color = value
+ timeTextPaint.color = value
+ sideTitleTextPaint.color = value
+ sideSubtitleTextPaint.color = value
+ allDaySideTitleTextPaint.color = value
+ invalidate()
+ }
+
+ @ColorInt
+ var allDaySideTitleTextColor = headerColumnTextColor
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ allDaySideTitleTextPaint.color = value
+ invalidate()
+ }
+
+ @ColorInt
+ var headerRowBackgroundColor: Int = Color.WHITE
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mHeaderBackgroundPaint.color = value
+ invalidate()
+ }
+
+ @ColorInt
+ var dayBackgroundColor: Int = Color.rgb(245, 245, 245)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mDayBackgroundPaint.color = value
+ invalidate()
+ }
+
+ @ColorInt
+ var hourSeparatorColor: Int = Color.rgb(230, 230, 230)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mHourSeparatorPaint.color = value
+ invalidate()
+ }
+
+ @ColorInt
+ var todayColumnBackgroundColor: Int = Color.rgb(239, 247, 254)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mTodayColumnBackgroundPaint.color = value
+ invalidate()
+ }
+
+ var hourSeparatorHeight: Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, resources.displayMetrics).toInt()
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mHourSeparatorPaint.strokeWidth = value.toFloat()
+ invalidate()
+ }
+
+ @ColorInt
+ var todayHeaderTextColor: Int = Color.rgb(39, 137, 228)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mHeaderWeekDayTitleTodayTextPaint.color = value
+ mHeaderWeekDaySubtitleTodayTextPaint.color = value
+ invalidate()
+ }
+
+ var eventTextSize: Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12.0f, context.resources.displayMetrics)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mEventTextPaint.textSize = value
+ invalidate()
+ }
+
+ @ColorInt
+ var eventTextColor: Int = Color.BLACK
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ mEventTextPaint.color = value
+ invalidate()
+ }
+
+ var eventPadding: Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4f, resources.displayMetrics).toInt()
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ @ColorInt
+ var defaultEventColor: Int = 0
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ @ColorInt
+ var newEventColor: Int = 0
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ /**
+ * 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.
+ */
+ var dayNameLength: Int = LENGTH_LONG
+ @Deprecated("")
+ set(value) {
+ if (value != LENGTH_LONG && value != LENGTH_SHORT)
+ throw IllegalArgumentException("length parameter must be either LENGTH_LONG or LENGTH_SHORT")
+ field = value
+ }
+
+ /**
+ * the gap between overlapping events.
+ */
+ var overlappingEventGap: Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, resources.displayMetrics)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var weekDayHeaderRowPaddingTop = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6f, resources.displayMetrics)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var weekDayHeaderRowPaddingBottom = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6f, resources.displayMetrics)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var weekDaysHeaderRowTotalPadding: Float = weekDayHeaderRowPaddingTop + weekDayHeaderRowPaddingBottom
+ get() = weekDayHeaderRowPaddingTop + weekDayHeaderRowPaddingBottom
+
+ var spaceBetweenWeekDaysAndAllDayEvents = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3f, resources.displayMetrics).toInt()
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var spaceBelowAllDayEvents = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3f, resources.displayMetrics).toInt()
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var spaceBetweenHeaderWeekDayTitleAndSubtitle = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6f, context.resources.displayMetrics).toInt()
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ 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`.
+ */
+ var isShowDistinctWeekendColor: Boolean = false
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ 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`.
+ */
+ var isShowDistinctPastFutureColor: Boolean = false
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ /**
+ * whether "now" line should be displayed. "Now" line is defined by the attributes
+ * `nowLineColor` and `nowLineThickness`.
+ */
+ var isShowNowLine: Boolean = false
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ /**
+ * the "now" line color.
+ */
+ @ColorInt
+ var nowLineColor: Int = Color.rgb(102, 102, 102)
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var nowLineThickness: Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics).toInt()
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ /*
+ * 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.
+ */
+ var zoomFocusPoint: Float = 0f
+ set(value) {
+ if (0 > value || value > 1)
+ throw IllegalStateException("The zoom focus point percentage has to be between 0 and 1")
+ field = value
+ }
+
+ @ColorInt
+ var pastBackgroundColor: Int = Color.rgb(227, 227, 227)
+ set(value) {
+ field = value
+ mPastBackgroundPaint.color = value
+ }
+
+ @ColorInt
+ var futureBackgroundColor: Int = Color.rgb(245, 245, 245)
+ set(value) {
+ field = value
+ mFutureBackgroundPaint.color = value
+ }
+
+ @ColorInt
+ var pastWeekendBackgroundColor: Int = 0
+ set(value) {
+ field = value
+ this.mPastWeekendBackgroundPaint.color = value
+ }
+
+ @ColorInt
+ var futureWeekendBackgroundColor: Int = 0
+ set(value) {
+ field = value
+ this.mFutureWeekendBackgroundPaint.color = value
+ }
+
+ var isScrollNumberOfVisibleDays: Boolean = false
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ /**
+ * Get the first hour that is visible on the screen.
+ *
+ * @return The first hour that is visible.
+ */
+ val firstVisibleHour: Double
+ get() = (-mCurrentOrigin.y / hourHeight).toDouble()
+
+ var isUsingCheckersStyle: Boolean = false
+ set(value) {
+ if (field == value)
+ return
+ field = value
+ invalidate()
+ }
+
+ var isSubtitleHeaderEnabled: Boolean
+ get() = weekDaySubtitleInterpreter != null
+ private set(value) {}
+
+ private val timeChangedBroadcastReceiver: TimeChangedBroadcastReceiver
+ private var today: Calendar = WeekViewUtil.today()
+ private val containsAllDayEventCache = HashMap, Boolean>()
+ private val weekDayTitleFormatterCache = HashMap()
+ private val weekDaySubtitleFormatterCache = HashMap()
+ private val timeFormatterCache = HashMap, String>()
+
+ //endregion fields and properties
+
+ private enum class Direction {
+ NONE, LEFT, RIGHT, VERTICAL
+ }
+
+ init {
+ timeChangedBroadcastReceiver = object : TimeChangedBroadcastReceiver() {
+ override fun onTimeChanged() {
+ this@WeekView.today = WeekViewUtil.today()
+ invalidate()
+ }
+ }
+ timeChangedBroadcastReceiver.register(context, Calendar.getInstance())
+ 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
+ }
+ }
+ // Get the attribute values (if any).
+ val a = context.theme.obtainStyledAttributes(attrs, R.styleable.WeekView, 0, 0)
+ try {
+ firstDayOfWeek = a.getInteger(R.styleable.WeekView_firstDayOfWeek, firstDayOfWeek)
+ hourHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourHeight, hourHeight)
+ minHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_minHourHeight, minHourHeight)
+ mEffectiveMinHourHeight = minHourHeight
+ maxHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_maxHourHeight, maxHourHeight)
+ textSize = a.getDimension(R.styleable.WeekView_textSize, textSize)
+ headerColumnPadding = a.getDimension(R.styleable.WeekView_headerColumnPadding, headerColumnPadding)
+ columnGap = a.getDimensionPixelSize(R.styleable.WeekView_columnGap, columnGap)
+ headerColumnTextColor = a.getColor(R.styleable.WeekView_headerColumnTextColor, headerColumnTextColor)
+ numberOfVisibleDays = a.getInteger(R.styleable.WeekView_noOfVisibleDays, numberOfVisibleDays)
+ isShowFirstDayOfWeekFirst = a.getBoolean(R.styleable.WeekView_showFirstDayOfWeekFirst, isShowFirstDayOfWeekFirst)
+ weekDayHeaderRowPaddingTop = a.getDimension(R.styleable.WeekView_weekDayHeaderRowPaddingTop, weekDayHeaderRowPaddingTop)
+ weekDayHeaderRowPaddingBottom = a.getDimension(R.styleable.WeekView_weekDayHeaderRowPaddingBottom, weekDayHeaderRowPaddingBottom)
+ headerRowBackgroundColor = a.getColor(R.styleable.WeekView_headerRowBackgroundColor, headerRowBackgroundColor)
+ dayBackgroundColor = a.getColor(R.styleable.WeekView_dayBackgroundColor, dayBackgroundColor)
+ futureBackgroundColor = a.getColor(R.styleable.WeekView_futureBackgroundColor, futureBackgroundColor)
+ pastBackgroundColor = a.getColor(R.styleable.WeekView_pastBackgroundColor, pastBackgroundColor)
+ // If not set, use the same color as in the week
+ futureWeekendBackgroundColor = a.getColor(R.styleable.WeekView_futureWeekendBackgroundColor, futureBackgroundColor)
+ pastWeekendBackgroundColor = a.getColor(R.styleable.WeekView_pastWeekendBackgroundColor, pastBackgroundColor)
+ nowLineColor = a.getColor(R.styleable.WeekView_nowLineColor, nowLineColor)
+ nowLineThickness = a.getDimensionPixelSize(R.styleable.WeekView_nowLineThickness, nowLineThickness)
+ hourSeparatorColor = a.getColor(R.styleable.WeekView_hourSeparatorColor, hourSeparatorColor)
+ todayColumnBackgroundColor = a.getColor(R.styleable.WeekView_todayColumnBackgroundColor, todayColumnBackgroundColor)
+ hourSeparatorHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourSeparatorHeight, hourSeparatorHeight)
+ todayHeaderTextColor = a.getColor(R.styleable.WeekView_todayHeaderTextColor, todayHeaderTextColor)
+ eventTextSize = a.getDimension(R.styleable.WeekView_eventTextSize, eventTextSize)
+ eventTextColor = a.getColor(R.styleable.WeekView_eventTextColor, eventTextColor)
+ newEventColor = a.getColor(R.styleable.WeekView_newEventColor, newEventColor)
+ newEventIconDrawable = a.getDrawable(R.styleable.WeekView_newEventIconResource)
+ // For backward compatibility : Set "mNewEventIdentifier" if the attribute is "WeekView_newEventId" of type int
+ newEventIdentifier = a.getString(R.styleable.WeekView_newEventIdentifier) ?: newEventIdentifier
+ newEventLengthInMinutes = a.getInt(R.styleable.WeekView_newEventLengthInMinutes, newEventLengthInMinutes)
+ newEventTimeResolutionInMinutes = a.getInt(R.styleable.WeekView_newEventTimeResolutionInMinutes, newEventTimeResolutionInMinutes)
+ eventPadding = a.getDimensionPixelSize(R.styleable.WeekView_eventPadding, eventPadding)
+ dayNameLength = a.getInteger(R.styleable.WeekView_dayNameLength, dayNameLength)
+ overlappingEventGap = a.getDimension(R.styleable.WeekView_overlappingEventGap, overlappingEventGap)
+ spaceBetweenWeekDaysAndAllDayEvents = a.getDimensionPixelSize(R.styleable.WeekView_spaceBetweenWeekDaysAndAllDayEvents, spaceBetweenWeekDaysAndAllDayEvents)
+ xScrollingSpeed = a.getFloat(R.styleable.WeekView_xScrollingSpeed, xScrollingSpeed)
+ eventCornerRadius = a.getDimension(R.styleable.WeekView_eventCornerRadius, eventCornerRadius)
+ isShowDistinctPastFutureColor = a.getBoolean(R.styleable.WeekView_showDistinctPastFutureColor, isShowDistinctPastFutureColor)
+ isShowDistinctWeekendColor = a.getBoolean(R.styleable.WeekView_showDistinctWeekendColor, isShowDistinctWeekendColor)
+ isShowNowLine = a.getBoolean(R.styleable.WeekView_showNowLine, isShowNowLine)
+ isHorizontalFlingEnabled = a.getBoolean(R.styleable.WeekView_horizontalFlingEnabled, isHorizontalFlingEnabled)
+ isVerticalFlingEnabled = a.getBoolean(R.styleable.WeekView_verticalFlingEnabled, isVerticalFlingEnabled)
+ allDayEventHeight = a.getDimensionPixelSize(R.styleable.WeekView_allDayEventHeight, allDayEventHeight)
+ zoomFocusPoint = a.getFraction(R.styleable.WeekView_zoomFocusPoint, 1, 1, zoomFocusPoint)
+ isZoomFocusPointEnabled = a.getBoolean(R.styleable.WeekView_zoomFocusPointEnabled, isZoomFocusPointEnabled)
+ scrollDuration = a.getInt(R.styleable.WeekView_scrollDuration, scrollDuration)
+ timeColumnResolution = a.getInt(R.styleable.WeekView_timeColumnResolution, timeColumnResolution)
+ autoLimitTime = a.getBoolean(R.styleable.WeekView_autoLimitTime, autoLimitTime)
+ mMinTime = a.getInt(R.styleable.WeekView_minTime, mMinTime)
+ mMaxTime = a.getInt(R.styleable.WeekView_maxTime, mMaxTime)
+ minOverlappingMinutes = a.getInt(R.styleable.WeekView_minOverlappingMinutes, minOverlappingMinutes)
+ isScrollNumberOfVisibleDays = a.getBoolean(R.styleable.WeekView_isScrollNumberOfVisibleDays, isScrollNumberOfVisibleDays)
+ enableDrawHeaderBackgroundOnlyOnWeekDays = a.getBoolean(R.styleable.WeekView_enableDrawHeaderBackgroundOnlyOnWeekDays, enableDrawHeaderBackgroundOnlyOnWeekDays)
+ isUsingCheckersStyle = a.getBoolean(R.styleable.WeekView_isUsingCheckersStyle, isUsingCheckersStyle)
+ headerWeekDayTitleTextSize = a.getDimension(R.styleable.WeekView_headerWeekDayTitleTextSize, headerWeekDayTitleTextSize)
+ headerWeekDaySubtitleTextSize = a.getDimension(R.styleable.WeekView_headerWeekDaySubtitleTextSize, headerWeekDaySubtitleTextSize)
+ spaceBetweenHeaderWeekDayTitleAndSubtitle = a.getDimensionPixelSize(R.styleable.WeekView_spaceBetweenHeaderWeekDayTitleAndSubtitle, spaceBetweenHeaderWeekDayTitleAndSubtitle)
+ untitledEventText = a.getString(R.styleable.WeekView_untitledEventText) ?: untitledEventText
+ } finally {
+ a.recycle()
+ }
+ //some one time initializations
+ mHeaderWeekDayTitleTextPaint.textAlign = Paint.Align.CENTER
+ mHeaderWeekDaySubtitleTextPaint.textAlign = Paint.Align.CENTER
+ sideTitleTextPaint.textAlign = Paint.Align.CENTER
+ sideSubtitleTextPaint.textAlign = Paint.Align.CENTER
+ allDaySideTitleTextPaint.textAlign = Paint.Align.CENTER
+ timeTextPaint.textAlign = Paint.Align.RIGHT
+ mEventTextPaint.style = Paint.Style.FILL
+ mHourSeparatorPaint.style = Paint.Style.STROKE
+ mHeaderWeekDayTitleTodayTextPaint.textAlign = Paint.Align.CENTER
+ mHeaderWeekDaySubtitleTodayTextPaint.textAlign = Paint.Align.CENTER
+ init()
+ }
+
+ private fun init() {
+ resetHomeDate()
+
+ // Scrolling initialization.
+ mGestureDetector = GestureDetectorCompat(context, mGestureListener)
+ mScroller = OverScroller(context, FastOutLinearInInterpolator())
+
+ mMinimumFlingVelocity = ViewConfiguration.get(context).scaledMinimumFlingVelocity
+ mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
+
+ // Measure settings for time column.
+ timeTextPaint.textSize = textSize
+ timeTextPaint.color = headerColumnTextColor
+ timeTextPaint.typeface = typeface
+
+ val rect = Rect()
+ val exampleTime = if (timeColumnResolution % 60 != 0) "00:00 PM" else "00 PM"
+ timeTextPaint.getTextBounds(exampleTime, 0, exampleTime.length, rect)
+ timeTextHeight = rect.height().toFloat()
+ initTextTimeWidth()
+
+ //handle sideTitleTextPaint
+ sideTitleTextPaint.color = headerColumnTextColor
+ sideTitleTextPaint.typeface = typeface
+ sideTitleTextPaint.textSize = headerWeekDayTitleTextSize
+ // handle sideSubtitleTextPaint
+ sideSubtitleTextPaint.textSize = headerWeekDaySubtitleTextSize
+ sideSubtitleTextPaint.color = headerColumnTextColor
+ sideSubtitleTextPaint.typeface = typeface
+
+ //handle allDaySideTitleTextPaint
+ allDaySideTitleTextPaint.textSize = textSize
+ allDaySideTitleTextPaint.color = allDaySideTitleTextColor
+ allDaySideTitleTextPaint.typeface = typeface
+
+ // Measure settings for header row.
+ //TODO measure the text that will actually be used, based on the locale and dates. Important because various characters might look different.
+ val sampleText = "ABCDEFGHIKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz0123456789"
+ mHeaderWeekDayTitleTextPaint.color = headerColumnTextColor
+ mHeaderWeekDayTitleTextPaint.textSize = headerWeekDayTitleTextSize
+ mHeaderWeekDayTitleTextPaint.typeface = typeface
+ mHeaderWeekDayTitleTextPaint.getTextBounds(sampleText, 0, sampleText.length, rect)
+ headerWeekDayTitleTextHeight = rect.height().toFloat()
+
+ //measure settings for header subtitle
+ mHeaderWeekDaySubtitleTextPaint.color = headerColumnTextColor
+ mHeaderWeekDaySubtitleTextPaint.textSize = headerWeekDaySubtitleTextSize
+ mHeaderWeekDaySubtitleTextPaint.typeface = typeface
+ mHeaderWeekDaySubtitleTextPaint.getTextBounds(sampleText, 0, sampleText.length, rect)
+ headerWeekDaySubtitleTextHeight = rect.height().toFloat()
+
+ // Prepare header background paint.
+ mHeaderBackgroundPaint.color = headerRowBackgroundColor
+
+ // Prepare day background color paint.
+ mDayBackgroundPaint.color = dayBackgroundColor
+ mFutureBackgroundPaint.color = futureBackgroundColor
+ mPastBackgroundPaint.color = pastBackgroundColor
+ mFutureWeekendBackgroundPaint.color = futureWeekendBackgroundColor
+ mPastWeekendBackgroundPaint.color = pastWeekendBackgroundColor
+
+ // Prepare hour separator color paint.
+ mHourSeparatorPaint.strokeWidth = hourSeparatorHeight.toFloat()
+ mHourSeparatorPaint.color = hourSeparatorColor
+
+ // Prepare the "now" line color paint
+ mNowLinePaint.strokeWidth = nowLineThickness.toFloat()
+ mNowLinePaint.color = nowLineColor
+
+ // Prepare today background color paint.
+ mTodayColumnBackgroundPaint.color = todayColumnBackgroundColor
+
+ // Prepare today header text color paint.
+
+ mHeaderWeekDayTitleTodayTextPaint.textSize = headerWeekDayTitleTextSize
+ mHeaderWeekDayTitleTodayTextPaint.typeface = typeface
+ mHeaderWeekDayTitleTodayTextPaint.color = todayHeaderTextColor
+
+ mHeaderWeekDaySubtitleTodayTextPaint.textSize = headerWeekDaySubtitleTextSize
+ mHeaderWeekDaySubtitleTodayTextPaint.typeface = typeface
+ mHeaderWeekDaySubtitleTodayTextPaint.color = todayHeaderTextColor
+
+ // Prepare event background color.
+ mEventBackgroundPaint.color = Color.rgb(174, 208, 238)
+ // Prepare empty event background color.
+ mNewEventBackgroundPaint.color = Color.rgb(60, 147, 217)
+
+ // Prepare event text size and color.
+ mEventTextPaint.color = eventTextColor
+ mEventTextPaint.textSize = eventTextSize
+ mEventTextPaint.typeface = typeface
+
+
+ // Set default event color.
+ defaultEventColor = 0xff9fc6e7.toInt()
+ // Set default empty event color.
+ newEventColor = 0xff3c93d9.toInt()
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ context.unregisterReceiver(timeChangedBroadcastReceiver)
+ }
+
+ 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) * (widthPerDay + columnGap)
+ }
+
+ // 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() {
+ timeTextWidth = 0f
+ for (i in 0 until numberOfPeriods) {
+ // Measure time string and get max width.
+ val time = getFormattedTime(i, i % 2 * 30)
+ timeTextWidth = Math.max(timeTextWidth, timeTextPaint.measureText(time))
+ }
+ mHeaderColumnWidth = timeTextWidth + headerColumnPadding * 2.0f
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ drawPerformanceTester.startMeasure()
+ // Draw the header row.
+ drawHeaderRowAndEvents(canvas)
+ // Draw the time column and all the axes/separators.
+ drawTimeColumnAndAxes(canvas)
+ drawPerformanceTester.endMeasure()
+ }
+
+ private fun calculateHeaderHeight() {
+ //Make sure the header is the right size (depends on AllDay events)
+ if (eventRects != null && eventRects!!.size > 0) {
+ val cacheKey = Pair(SimpleDate(firstVisibleDay!!), realNumberOfVisibleDays)
+ val cachedResult = containsAllDayEventCache[cacheKey]
+ if (cachedResult != null)
+ containsAllDayEvent = cachedResult
+ else {
+ containsAllDayEvent = false
+ val day = firstVisibleDay!!.clone() as Calendar
+ outerLoop@
+ for (dayNumber in 0 until realNumberOfVisibleDays) {
+ for (i in eventRects!!.indices) {
+ val event = eventRects!![i].event
+ if (isSameDay(event.startTime, day) && event.isAllDay) {
+ containsAllDayEvent = true
+ break@outerLoop
+ }
+ }
+ day.add(Calendar.DATE, 1)
+ }
+ containsAllDayEventCache[cacheKey] = containsAllDayEvent
+ }
+ }
+ headerHeight = if (containsAllDayEvent) {
+ headerWeekDayTitleTextHeight + (allDayEventHeight + spaceBelowAllDayEvents + spaceBetweenWeekDaysAndAllDayEvents)
+ } else {
+ headerWeekDayTitleTextHeight
+ }
+ if (isSubtitleHeaderEnabled)
+ headerHeight += headerWeekDaySubtitleTextHeight + spaceBetweenHeaderWeekDayTitleAndSubtitle
+ }
+
+ private fun drawTimeColumnAndAxes(canvas: Canvas) {
+ canvas.save()
+ // Clip to paint in left column only.
+ canvas.clipRect(0.0f, headerHeight + weekDaysHeaderRowTotalPadding, mHeaderColumnWidth, height.toFloat())
+
+ 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.0f / timeColumnResolution
+ timeSpacing = hourHeight / 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 = headerHeight + weekDaysHeaderRowTotalPadding + mCurrentOrigin.y + timeSpacing * i + spaceBelowAllDayEvents
+ // Get the time to be displayed, as a String.
+ val time = getFormattedTime(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 (top < height)
+ canvas.drawText(time, timeTextWidth + headerColumnPadding, top + timeTextHeight, timeTextPaint)
+ }
+ canvas.restore()
+ }
+
+ private fun drawHeaderRowAndEvents(canvas: Canvas) {
+ // Calculate the available width for each day.
+ widthPerDay = (width.toFloat() - mHeaderColumnWidth - (columnGap.toFloat() * (realNumberOfVisibleDays.toFloat() - 1.0f))) / realNumberOfVisibleDays.toFloat()
+ calculateHeaderHeight() //Make sure the header is the right size (depends on AllDay events)
+
+ if (mAreDimensionsInvalid) {
+ mEffectiveMinHourHeight = Math.max(minHourHeight, ((height.toFloat() - headerHeight - weekDaysHeaderRowTotalPadding - spaceBelowAllDayEvents) / (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) != firstDayOfWeek && isShowFirstDayOfWeekFirst) {
+ val difference = mHomeDate!!.get(Calendar.DAY_OF_WEEK) - firstDayOfWeek
+ mCurrentOrigin.x += (widthPerDay + columnGap) * 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
+
+ hourHeight = mNewHourHeight
+ mNewHourHeight = -1
+ }
+
+ // If the new mCurrentOrigin.y is invalid, make it valid.
+ mCurrentOrigin.y = Math.min(0f, Math.max(mCurrentOrigin.y,
+ height.toFloat() - (hourHeight * (mMaxTime - mMinTime)).toFloat() - headerHeight - weekDaysHeaderRowTotalPadding - spaceBelowAllDayEvents - timeTextHeight / 2))
+
+ val leftDaysWithGaps = leftDaysWithGaps
+ // Consider scroll offset.
+ val startFromPixel = xStartPixel
+ var startPixel = startFromPixel
+
+ // Prepare to iterate for each hour to draw the hour lines.
+ var lineCount = ((height.toFloat() - headerHeight - weekDaysHeaderRowTotalPadding - spaceBelowAllDayEvents) / hourHeight).toInt() + 1
+ lineCount *= (realNumberOfVisibleDays + 1)
+
+ val hourLines = FloatArray(lineCount * 4)
+
+ // Clear the cache for event rectangles.
+ if (eventRects != null)
+ for (eventRect in eventRects!!)
+ eventRect.rectF = null
+
+ canvas.save()
+
+ // Clip to paint events only.
+ canvas.clipRect(mHeaderColumnWidth, headerHeight + weekDaysHeaderRowTotalPadding + spaceBelowAllDayEvents + timeTextHeight / 2, width.toFloat(), height.toFloat())
+
+ // Iterate through each day.
+
+ val oldFirstVisibleDay = firstVisibleDay
+ firstVisibleDay = mHomeDate!!.clone() as Calendar
+ firstVisibleDay!!.add(Calendar.DATE, -Math.round(mCurrentOrigin.x / (widthPerDay + columnGap)))
+
+ if (oldFirstVisibleDay == null || !WeekViewUtil.isSameDay(firstVisibleDay!!, oldFirstVisibleDay)) {
+ scrollListener?.onFirstVisibleDayChanged(firstVisibleDay!!, oldFirstVisibleDay)
+ }
+
+ if (autoLimitTime) {
+ val days = ArrayList()
+ for (dayNumber in leftDaysWithGaps + 1..leftDaysWithGaps + realNumberOfVisibleDays) {
+ val day = mHomeDate!!.clone() as Calendar
+ day.add(Calendar.DATE, dayNumber - 1)
+ days.add(day)
+ }
+ limitEventTime(days)
+ }
+ run {
+ val day = mHomeDate!!.clone() as Calendar
+ day.add(Calendar.DATE, leftDaysWithGaps)
+
+ for (dayNumber in leftDaysWithGaps + 1..leftDaysWithGaps + realNumberOfVisibleDays + 1) {
+ // Check if the day is today.
+ val isToday = isSameDay(day, today)
+
+ // Don't draw days which are outside requested range
+ if (!dateIsValid(day))
+ continue
+
+ // Get more events if necessary.
+
+ // mFetchedPeriod: currently fetched period index
+ // mWeekViewLoader.toWeekViewPeriodIndex(day): index for the day we want to display
+ // fetchIndex = 1.0: end of period in the future reached
+ // fetchIndex = 0.0: end of period in the past reached
+ val fetchIndex = this.weekViewLoader.toWeekViewPeriodIndex(day) - mFetchedPeriod
+
+ // if we are using the PrefetchingWeekViewLoader class, we need to adjust the bounds
+ // so that we wait to fetch new data until we really need it
+ var upperBound = 1.0
+ var lowerBound = 0.0
+
+ if (this.weekViewLoader is PrefetchingWeekViewLoader) {
+ // the offset causes the onMonthChangeListener to be trigger when half of the
+ // last fetched period is passed
+
+ // example:
+ // if the prefetching period = 1, we load the current period, the next and the previous
+ // when half of the next/previous period is passed, the listener is triggered to fetch new data
+ val boundOffset = this.weekViewLoader.prefetchingPeriod - 0.5
+
+ upperBound = 1.0 + boundOffset
+ lowerBound = 0.0 - boundOffset
+ }
+
+ if ((eventRects == null || mRefreshEvents ||
+ dayNumber == leftDaysWithGaps + 1 && mFetchedPeriod != this.weekViewLoader.toWeekViewPeriodIndex(day).toInt() &&
+ (fetchIndex >= upperBound || fetchIndex <= lowerBound))) {
+ getMoreEvents(day)
+ mRefreshEvents = false
+ }
+
+ // Draw background color for each day.
+ val start = if (startPixel < mHeaderColumnWidth) mHeaderColumnWidth else startPixel
+ if (widthPerDay + startPixel - start > 0) {
+ if (isShowDistinctPastFutureColor) {
+ val isWeekend = day.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || day.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY
+ val pastPaint = if (isWeekend && isShowDistinctWeekendColor) mPastWeekendBackgroundPaint else mPastBackgroundPaint
+ val futurePaint = if (isWeekend && isShowDistinctWeekendColor) mFutureWeekendBackgroundPaint else mFutureBackgroundPaint
+ val startY = headerHeight + weekDaysHeaderRowTotalPadding + timeTextHeight / 2 + spaceBelowAllDayEvents + mCurrentOrigin.y
+ when {
+ isToday -> {
+ val now = Calendar.getInstance()
+ val beforeNow = (now.get(Calendar.HOUR_OF_DAY) - mMinTime + now.get(Calendar.MINUTE) / 60f) * hourHeight
+ canvas.drawRect(start, startY, startPixel + widthPerDay, startY + beforeNow, pastPaint)
+ canvas.drawRect(start, startY + beforeNow, startPixel + widthPerDay, height.toFloat(), futurePaint)
+ }
+ day.before(today) -> canvas.drawRect(start, startY, startPixel + widthPerDay, height.toFloat(), pastPaint)
+ else -> canvas.drawRect(start, startY, startPixel + widthPerDay, height.toFloat(), futurePaint)
+ }
+ } else {
+ val cellBackgroundPaint = if (isToday) mTodayColumnBackgroundPaint else mDayBackgroundPaint
+ if (cellBackgroundPaint.color != 0)
+ canvas.drawRect(start, headerHeight + weekDaysHeaderRowTotalPadding + timeTextHeight / 2 + spaceBelowAllDayEvents, startPixel + widthPerDay, height.toFloat(), cellBackgroundPaint)
+ }
+ }
+
+ // Prepare the separator lines for hours.
+ var i = 0
+ for (hourNumber in mMinTime until mMaxTime) {
+ val top = headerHeight + weekDaysHeaderRowTotalPadding + mCurrentOrigin.y + (hourHeight * (hourNumber - mMinTime)).toFloat() + timeTextHeight / 2
+ if (top > headerHeight + weekDaysHeaderRowTotalPadding + timeTextHeight / 2 + spaceBelowAllDayEvents - hourSeparatorHeight && top < height && startPixel + widthPerDay - start > 0) {
+ hourLines[i * 4] = start
+ hourLines[i * 4 + 1] = top
+ hourLines[i * 4 + 2] = startPixel + widthPerDay + if (isUsingCheckersStyle) columnGap else 0
+ hourLines[i * 4 + 3] = top
+ i++
+ }
+ }
+ // Draw the lines for hours.
+ canvas.drawLines(hourLines, mHourSeparatorPaint)
+
+ // Draw line between days (before current one)
+ if (isUsingCheckersStyle) {
+ val x = if (dayNumber == leftDaysWithGaps + 1) start else start - columnGap / 2
+ canvas.drawLine(x, headerHeight, x, height.toFloat(), mHourSeparatorPaint)
+ }
+
+ // Draw the events.
+ drawEvents(day, startPixel, canvas)
+
+ // Draw the line at the current time.
+ if (isShowNowLine && isToday) {
+ val startY = headerHeight + weekDaysHeaderRowTotalPadding + timeTextHeight / 2 + spaceBelowAllDayEvents + mCurrentOrigin.y
+ val now = Calendar.getInstance()
+ val beforeNow = (now.get(Calendar.HOUR_OF_DAY) - mMinTime + now.get(Calendar.MINUTE) / 60f) * hourHeight
+ val top = startY + beforeNow
+ canvas.drawLine(start, top, startPixel + widthPerDay, top, mNowLinePaint)
+ }
+
+ // In the next iteration, start from the next day.
+ startPixel += widthPerDay + columnGap
+ day.add(Calendar.DATE, 1)
+ }
+ }
+
+ canvas.restore()
+
+ // Hide everything in the first cell (top left corner).
+ canvas.save()
+ canvas.clipRect(0f, 0f, mHeaderColumnWidth, headerHeight + weekDaysHeaderRowTotalPadding)
+ val headerTitleAndSubtitleTextHeight = headerWeekDayTitleTextHeight + (if (isSubtitleHeaderEnabled) headerWeekDaySubtitleTextHeight + spaceBetweenHeaderWeekDayTitleAndSubtitle else 0.0f)
+ if (enableDrawHeaderBackgroundOnlyOnWeekDays)
+ canvas.drawRect(0f, 0f, mHeaderColumnWidth, headerTitleAndSubtitleTextHeight + weekDaysHeaderRowTotalPadding, mHeaderBackgroundPaint)
+ else
+ canvas.drawRect(canvas.clipBounds, mHeaderBackgroundPaint)
+
+ // draw text on the left of the week days
+ when {
+ //TODO set left column size based on possible text of sideTitle and sideSubtitle, or auto-resize text according to available space
+ !TextUtils.isEmpty(sideTitleText) && TextUtils.isEmpty(sideSubtitleText) ->
+ canvas.drawText(sideTitleText, mHeaderColumnWidth / 2, (headerTitleAndSubtitleTextHeight + headerWeekDayTitleTextHeight) / 2.0f + weekDayHeaderRowPaddingTop, sideTitleTextPaint)
+ !TextUtils.isEmpty(sideTitleText) && !TextUtils.isEmpty(sideSubtitleText) -> {
+ canvas.drawText(sideTitleText, mHeaderColumnWidth / 2, headerWeekDayTitleTextHeight + weekDayHeaderRowPaddingTop, sideTitleTextPaint)
+ canvas.drawText(sideSubtitleText, mHeaderColumnWidth / 2, headerTitleAndSubtitleTextHeight + weekDayHeaderRowPaddingTop, sideSubtitleTextPaint)
+ }
+ TextUtils.isEmpty(sideTitleText) && !TextUtils.isEmpty(sideSubtitleText) ->
+ canvas.drawText(sideSubtitleText, mHeaderColumnWidth / 2, (headerTitleAndSubtitleTextHeight + sideSubtitleTextPaint.textSize) / 2.0f + weekDayHeaderRowPaddingTop, sideSubtitleTextPaint)
+ }
+
+ canvas.restore()
+ // Clip to paint header row only.
+ canvas.save()
+ canvas.clipRect(mHeaderColumnWidth, 0f, width.toFloat(), headerHeight + weekDaysHeaderRowTotalPadding)
+
+
+ // Draw the header background.
+ if (enableDrawHeaderBackgroundOnlyOnWeekDays)
+ canvas.drawRect(0f, 0f, width.toFloat(), headerTitleAndSubtitleTextHeight + weekDaysHeaderRowTotalPadding, mHeaderBackgroundPaint)
+ else
+ canvas.drawRect(0f, 0f, width.toFloat(), headerHeight + weekDaysHeaderRowTotalPadding, mHeaderBackgroundPaint)
+
+ canvas.restore()
+ canvas.save()
+
+ canvas.clipRect(mHeaderColumnWidth, 0f, width.toFloat(), headerHeight + weekDaysHeaderRowTotalPadding - spaceBelowAllDayEvents)
+
+ // Draw the header row texts.
+ run {
+ val day = mHomeDate!!.clone() as Calendar
+ startPixel = startFromPixel
+ day.add(Calendar.DATE, leftDaysWithGaps)
+ for (dayNumber in leftDaysWithGaps + 1..leftDaysWithGaps + realNumberOfVisibleDays + 1) {
+ // Check if the day is today.
+ val isToday = isSameDay(day, today)
+ // Don't draw days which are outside requested range
+ if (!dateIsValid(day)) {
+ day.add(Calendar.DAY_OF_YEAR, 1)
+ continue
+ }
+ // Draw the day labels title
+ val dayLabel = getFormattedWeekDayTitle(day)
+ canvas.drawText(dayLabel, startPixel + widthPerDay / 2, headerWeekDayTitleTextHeight + weekDayHeaderRowPaddingTop, if (isToday) mHeaderWeekDayTitleTodayTextPaint else mHeaderWeekDayTitleTextPaint)
+
+ //draw day subtitle
+ if (isSubtitleHeaderEnabled) {
+ val subtitleText = getFormattedWeekDaySubtitle(day)
+ canvas.drawText(subtitleText, startPixel + widthPerDay / 2, headerTitleAndSubtitleTextHeight + weekDayHeaderRowPaddingTop,
+ if (isToday) mHeaderWeekDaySubtitleTodayTextPaint else mHeaderWeekDaySubtitleTextPaint)
+ }
+ if (containsAllDayEvent)
+ drawAllDayEvents(day, startPixel, canvas)
+ startPixel += widthPerDay + columnGap
+ day.add(Calendar.DAY_OF_YEAR, 1)
+ }
+ }
+ canvas.restore()
+ //draw text on the left of the all-day events
+ if (containsAllDayEvent && !TextUtils.isEmpty(allDaySideTitleText)) {
+ canvas.save()
+ val weekDaysHeight = headerTitleAndSubtitleTextHeight + weekDaysHeaderRowTotalPadding
+ val top = weekDaysHeight + spaceBetweenWeekDaysAndAllDayEvents + timeTextHeight / 2
+ val bottom = top + allDayEventHeight
+ canvas.clipRect(0f, 0f, top, bottom)
+ canvas.drawText(allDaySideTitleText, mHeaderColumnWidth / 2, (top + bottom) / 2, allDaySideTitleTextPaint)
+ canvas.restore()
+ }
+ }
+
+ private fun getFormattedTime(hour: Int, minutes: Int): String {
+ val cacheKey = Pair(hour, minutes)
+ val cachedResult = timeFormatterCache[cacheKey]
+ if (cachedResult != null)
+ return cachedResult
+ val result = dateTimeInterpreter.getFormattedTimeOfDay(hour, minutes)
+ timeFormatterCache[cacheKey] = result
+ return result
+ }
+
+ private fun getFormattedWeekDayTitle(cal: Calendar): String {
+ val cacheKey = SimpleDate(cal)
+ val cachedResult = weekDayTitleFormatterCache[cacheKey]
+ if (cachedResult != null)
+ return cachedResult
+ val result = dateTimeInterpreter.getFormattedWeekDayTitle(cal)
+ weekDayTitleFormatterCache[cacheKey] = result
+ return result
+ }
+
+ private fun getFormattedWeekDaySubtitle(cal: Calendar): String {
+ val cacheKey = SimpleDate(cal)
+ val cachedResult = weekDaySubtitleFormatterCache[cacheKey]
+ if (cachedResult != null)
+ return cachedResult
+ val result = weekDaySubtitleInterpreter!!.getFormattedWeekDaySubtitle(cal)
+ weekDaySubtitleFormatterCache[cacheKey] = result
+ return result
+ }
+
+ /**
+ * 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 (widthPerDay + startPixel - start > 0 && x > start && x < startPixel + widthPerDay) {
+ val day = mHomeDate!!.clone() as Calendar
+ day.add(Calendar.DATE, dayNumber - 1)
+ val pixelsFromZero = (y - mCurrentOrigin.y - headerHeight
+ - weekDaysHeaderRowTotalPadding - timeTextHeight / 2 - spaceBelowAllDayEvents)
+ val hour = (pixelsFromZero / hourHeight).toInt()
+ val minute = (60 * (pixelsFromZero - hour * hourHeight) / hourHeight).toInt()
+ day.add(Calendar.HOUR_OF_DAY, hour + mMinTime)
+ day.set(Calendar.MINUTE, minute)
+ return day
+ }
+ startPixel += widthPerDay + columnGap
+ }
+ return null
+ }
+
+ /**
+ * limit current time of event by update mMinTime & mMaxTime
+ * find smallest of start time & latest of end time
+ */
+ private fun limitEventTime(dates: MutableList) {
+ if (eventRects != null && eventRects!!.size > 0) {
+ var startTime: Calendar? = null
+ var endTime: Calendar? = null
+
+ for (eventRect in eventRects!!) {
+ 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 (eventRects == null || eventRects!!.isEmpty())
+ return
+ for (eventRect in eventRects!!) {
+ if (isSameDay(eventRect.event.startTime, date) && !eventRect.event.isAllDay) {
+ val top = hourHeight * eventRect.top / 60 + eventsTop
+ val bottom = hourHeight * eventRect.bottom / 60 + eventsTop
+
+ // Calculate left and right.
+ var left = startFromPixel + eventRect.left * widthPerDay
+ if (left < startFromPixel)
+ left += overlappingEventGap
+ var right = left + eventRect.width * widthPerDay
+ if (right < startFromPixel + widthPerDay)
+ right -= overlappingEventGap
+
+ // Draw the event and the event name on top of it.
+ if (left < right && left < width && top < height && right > mHeaderColumnWidth &&
+ bottom > headerHeight + weekDaysHeaderRowTotalPadding + timeTextHeight / 2 + spaceBelowAllDayEvents) {
+ eventRect.rectF = RectF(left, top, right, bottom)
+ mEventBackgroundPaint.color = if (eventRect.event.color == 0) defaultEventColor else eventRect.event.color
+ mEventBackgroundPaint.shader = eventRect.event.shader
+ canvas.drawRoundRect(eventRect.rectF!!, eventCornerRadius, eventCornerRadius, mEventBackgroundPaint)
+ var topToUse = top
+ if (eventRect.event.startTime.get(Calendar.HOUR_OF_DAY) < mMinTime)
+ topToUse = hourHeight * getPassedMinutesInDay(mMinTime, 0) / 60 + eventsTop
+
+ if (newEventIdentifier != eventRect.event.id)
+ drawEventTitle(eventRect.event, eventRect.rectF!!, canvas, topToUse, left)
+ else
+ drawEmptyImage(eventRect.event, eventRect.rectF!!, canvas, topToUse, left)
+
+ } else
+ eventRect.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 (eventRects == null || eventRects!!.isEmpty())
+ return
+ val headerTitleAndSubtitleTextHeight = headerWeekDayTitleTextHeight + (if (isSubtitleHeaderEnabled) headerWeekDaySubtitleTextHeight + spaceBetweenHeaderWeekDayTitleAndSubtitle else 0.0f)
+ for (eventRect in eventRects!!) {
+ if (isSameDay(eventRect.event.startTime, date) && eventRect.event.isAllDay) {
+ // Calculate top.
+ val weekDaysHeight = headerTitleAndSubtitleTextHeight + weekDaysHeaderRowTotalPadding
+ val top = weekDaysHeight + spaceBetweenWeekDaysAndAllDayEvents + timeTextHeight / 2
+ // Calculate bottom.
+ val bottom = top + eventRect.bottom
+ // Calculate left and right.
+ var left = startFromPixel + eventRect.left * widthPerDay
+ if (left < startFromPixel)
+ left += overlappingEventGap
+ var right = left + eventRect.width * widthPerDay
+ if (right < startFromPixel + widthPerDay)
+ right -= overlappingEventGap
+ // Draw the event and the event name on top of it.
+ if (left < right && left < width && top < height && right > mHeaderColumnWidth && bottom > 0) {
+ eventRect.rectF = RectF(left, top, right, bottom)
+ mEventBackgroundPaint.color = if (eventRect.event.color == 0) defaultEventColor else eventRect.event.color
+ mEventBackgroundPaint.shader = eventRect.event.shader
+ canvas.drawRoundRect(eventRect.rectF!!, eventCornerRadius, eventCornerRadius, mEventBackgroundPaint)
+ drawEventTitle(eventRect.event, eventRect.rectF!!, canvas, top, left)
+ } else
+ eventRect.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 - (eventPadding * 2).toFloat() < 0) return
+ if (rect.bottom - rect.top - (eventPadding * 2).toFloat() < 0) return
+
+ // Prepare the name of the event.
+ val bob = SpannableStringBuilder()
+ if (!TextUtils.isEmpty(event.name) || !TextUtils.isEmpty(untitledEventText)) {
+ if (!TextUtils.isEmpty(event.name))
+ bob.append(event.name)
+ else bob.append(untitledEventText)
+ bob.setSpan(StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length, 0)
+ }
+ // Prepare the location of the event.
+ if (!TextUtils.isEmpty(event.location)) {
+ if (bob.isNotEmpty())
+ bob.append(' ')
+ bob.append(event.location)
+ }
+
+ val availableHeight = (rect.bottom - originalTop - (eventPadding * 2).toFloat()).toInt()
+ val availableWidth = (rect.right - originalLeft - (eventPadding * 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, 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.id)
+ textLayout = StaticLayout(TextUtils.ellipsize(bob, mEventTextPaint, (availableLineCount * availableWidth).toFloat(), TextUtils.TruncateAt.END), mEventTextPaint, (rect.right - originalLeft - (eventPadding * 2).toFloat()).toInt(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f, false)
+
+ // Reduce line count.
+ availableLineCount--
+
+ // Repeat until text is short enough.
+ } while (textLayout.height > availableHeight)
+
+ // Draw text.
+ canvas.save()
+ canvas.translate(originalLeft + eventPadding, originalTop + eventPadding)
+ 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 = AppCompatResources.getDrawable(context, 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, mEmptyEventPaint)
+ }
+
+ /**
+ * 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".
+ */
+ /**
+ * 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.
+ */
+ private inner class EventRect(var event: WeekViewEvent, var originalEvent: WeekViewEvent, var rectF: RectF?) {
+ var left: Float = 0f
+ var width: Float = 0f
+ var top: Float = 0f
+ var bottom: Float = 0f
+ override fun toString(): String {
+ return "EventRect(left=$left, width=$width, top=$top, bottom=$bottom, rectF=$rectF, event=$event, 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 fun getMoreEvents(day: Calendar) {
+ clearOptimizationsCaches()
+ // Get more events if the month is changed.
+ if (eventRects == null)
+ eventRects = ArrayList()
+ // If a refresh was requested then reset some variables.
+ if (mRefreshEvents) {
+ this.clearEvents()
+ mFetchedPeriod = -1
+ }
+ val periodToFetch = this.weekViewLoader.toWeekViewPeriodIndex(day).toInt()
+ if (!isInEditMode && (mFetchedPeriod < 0 || mFetchedPeriod != periodToFetch || mRefreshEvents)) {
+ val newEvents = this.weekViewLoader.onLoad(periodToFetch)
+ // Clear events.
+ this.clearEvents()
+ cacheAndSortEvents(newEvents)
+ calculateHeaderHeight()
+ mFetchedPeriod = periodToFetch
+ }
+ // Prepare to calculate positions of each events.
+ val tempEvents = eventRects
+ eventRects = 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() {
+ clearOptimizationsCaches()
+ eventRects!!.clear()
+ mEvents.clear()
+ }
+
+ private fun clearOptimizationsCaches() {
+ containsAllDayEventCache.clear()
+ timeFormatterCache.clear()
+ weekDayTitleFormatterCache.clear()
+ weekDaySubtitleFormatterCache.clear()
+ }
+
+ /**
+ * Cache the event for smooth scrolling functionality.
+ *
+ * @param event The event to cache.
+ */
+ private fun cacheEvent(event: WeekViewEvent) {
+ if (!event.isAllDay && event.startTime >= event.endTime)
+ return
+ val splitEvents = event.splitWeekViewEvents()
+ for (splitEvent in splitEvents) {
+ eventRects!!.add(EventRect(splitEvent, event, null))
+ }
+ mEvents.add(event)
+ }
+
+ /**
+ * Cache and sort events.
+ *
+ * @param events The events to be cached and sorted.
+ */
+ private fun cacheAndSortEvents(events: MutableList?) {
+ if (events != null)
+ for (event in events)
+ cacheEvent(event)
+ sortEventRects(eventRects)
+ }
+
+ /**
+ * Sorts the events in ascending order.
+ *
+ * @param eventRects The events to be sorted.
+ */
+ private fun sortEventRects(eventRects: MutableList?) {
+ eventRects?.sortWith(Comparator { 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: MutableList) {
+ // 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: MutableList) {
+ // 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()
+ }
+ eventRects!!.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 {
+ if (event1.isAllDay != event2.isAllDay)
+ return false
+ val start1 = event1.startTime.timeInMillis
+ val start2 = event2.startTime.timeInMillis
+ val end1 = event1.endTime.timeInMillis
+ val end2 = event2.endTime.timeInMillis
+ if (event1.isAllDay)
+ return !(start1 > end2 || end1 < start2)
+ 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.
+//
+/////////////////////////////////////////////////////////////////
+
+ private fun recalculateHourHeight() {
+ val height = ((height - (headerHeight + weekDaysHeaderRowTotalPadding + timeTextHeight / 2 + spaceBelowAllDayEvents)) / (this.mMaxTime - this.mMinTime)).toInt()
+ if (height > hourHeight) {
+ 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) {
+ when {
+ endHour <= startHour -> throw IllegalArgumentException("endHour must larger startHour.")
+ startHour < 0 -> throw IllegalArgumentException("startHour must be at least 0.")
+ endHour > 24 -> throw IllegalArgumentException("endHour can't be higher than 24.")
+ else -> {
+ 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()
+ }
+
+/////////////////////////////////////////////////////////////////
+//
+// Functions related to scrolling.
+//
+/////////////////////////////////////////////////////////////////
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+
+ mSizeOfWeekView = (widthPerDay + columnGap) * numberOfVisibleDays
+ mDistanceMin = mSizeOfWeekView / mOffsetValueToSecureScreen
+
+ scaleDetector.onTouchEvent(event)
+ val value = 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 value
+ }
+
+ private fun goToNearestOrigin() {
+ var leftDays = (mCurrentOrigin.x / (widthPerDay + columnGap)).toDouble()
+
+ val beforeScroll = mStartOriginForScroll
+ var isPassed = false
+
+ if (mDistanceDone > mDistanceMin || mDistanceDone < -mDistanceMin || !isScrollNumberOfVisibleDays) {
+
+ when {
+ !isScrollNumberOfVisibleDays && mCurrentFlingDirection != Direction.NONE -> // snap to nearest day
+ leftDays = Math.round(leftDays).toDouble()
+ mCurrentScrollDirection == Direction.LEFT -> {
+ // snap to last day
+ leftDays = Math.floor(leftDays)
+ mStartOriginForScroll -= mSizeOfWeekView
+ isPassed = true
+ }
+ 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 (isScrollNumberOfVisibleDays) {
+ 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 * (widthPerDay + columnGap)).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) / widthPerDay * 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.
+//
+/////////////////////////////////////////////////////////////////
+ fun getLastVisibleDay(): Calendar? {
+ if (firstVisibleDay == null)
+ return null
+ val result = firstVisibleDay!!.clone() as Calendar
+ result.add(Calendar.DATE, realNumberOfVisibleDays - 1)
+ return result
+ }
+
+ /**
+ * 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) * (widthPerDay + columnGap)
+ mStartOriginForScroll = mCurrentOrigin.x
+ 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 = hourHeight * (mMaxTime - mMinTime)
+ else if (hour > mMinTime)
+ verticalOffset = (hourHeight * hour).toInt()
+
+ if (verticalOffset > (hourHeight * (mMaxTime - mMinTime) - height).toFloat() + headerHeight + weekDaysHeaderRowTotalPadding + spaceBelowAllDayEvents)
+ verticalOffset = ((hourHeight * (mMaxTime - mMinTime) - height).toFloat() + headerHeight + weekDaysHeaderRowTotalPadding + spaceBelowAllDayEvents).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 !(maxDate != null && day.after(maxDate))
+ }
+
+//region 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 {
+ 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 = 0f
+
+ override fun onScaleEnd(detector: ScaleGestureDetector) {
+ mIsZooming = false
+ }
+
+ override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
+ mIsZooming = true
+ goToNearestOrigin()
+
+ // Calculate focused point for scale action
+ mFocusedPointY = if (isZoomFocusPointEnabled) {
+ // Use fractional focus, percentage of height
+ (height.toFloat() - headerHeight - weekDaysHeaderRowTotalPadding - spaceBelowAllDayEvents) * zoomFocusPoint
+ } else {
+ // Grab focus
+ detector.focusY
+ }
+
+ return true
+ }
+
+ override fun onScale(detector: ScaleGestureDetector): Boolean {
+ val scale = detector.scaleFactor
+
+ mNewHourHeight = Math.round(hourHeight * 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 -> {
+ val headerTitleAndSubtitleTextHeight = headerWeekDayTitleTextHeight + (if (isSubtitleHeaderEnabled) headerWeekDaySubtitleTextHeight + spaceBetweenHeaderWeekDayTitleAndSubtitle else 0.0f)
+ if (e.x > mHeaderColumnWidth && e.y > headerTitleAndSubtitleTextHeight + weekDaysHeaderRowTotalPadding + spaceBelowAllDayEvents) {
+ val selectedTime = getTimeFromPoint(e.x, e.y)
+ if (selectedTime != null) {
+ dropListener!!.onDrop(v, selectedTime)
+ }
+ }
+ }
+ }
+ return true
+ }
+ }
+
+//endregion interfaces
+
+ 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..e10632643
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/WeekViewEvent.kt
@@ -0,0 +1,131 @@
+package com.alamkanak.weekview
+
+import android.graphics.Shader
+import androidx.annotation.ColorInt
+import com.alamkanak.weekview.WeekViewUtil.isSameDay
+import java.util.*
+
+open class WeekViewEvent {
+ val id: String?
+ val startTime: Calendar
+ val endTime: Calendar
+ var name: String? = null
+ var location: String? = null
+ @ColorInt
+ @get:ColorInt
+ var color: Int = 0
+ val isAllDay: Boolean
+ var shader: Shader? = null
+
+ /**CTOR for a single, all day event*/
+ constructor(id: String?, name: String?, location: String? = null, allDayTime: Calendar, shader: Shader? = null) : this(id, name, location, allDayTime, allDayTime, true, 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.
+ * @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.id = id
+ this.name = name
+ this.location = location
+ this.isAllDay = allDay
+ if (!allDay) {
+ this.startTime = startTime
+ this.endTime = endTime
+ } else {
+ WeekViewUtil.resetTime(startTime)
+ this.startTime = startTime
+ if (!WeekViewUtil.isSameDay(startTime, endTime)) {
+ WeekViewUtil.resetTime(endTime)
+ this.endTime = endTime
+ } else
+ this.endTime = startTime
+ }
+ this.shader = 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)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is WeekViewEvent) return false
+ return id == other.id
+ }
+
+ override fun hashCode(): Int {
+ return id?.hashCode() ?: 0
+ }
+
+ fun splitWeekViewEvents(): MutableList {
+ //This function splits the WeekViewEvent in WeekViewEvents by day
+ if (isSameDay(this.startTime, this.endTime)) {
+ val events = ArrayList(1)
+ events.add(this)
+ return events
+ }
+ 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)
+ endTime = this.startTime.clone() as Calendar
+ endTime.set(Calendar.HOUR_OF_DAY, 23)
+ endTime.set(Calendar.MINUTE, 59)
+ val event1 = WeekViewEvent(this.id, this.name, this.location, this.startTime, endTime, this.isAllDay)
+ event1.color = this.color
+ events.add(event1)
+ // Add other days.
+ if (!isSameDay(this.startTime, this.endTime)) {
+ 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.id, 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.id, this.name, this.location, startTime, this.endTime, this.isAllDay)
+ event2.color = this.color
+ events.add(event2)
+ }
+ return events
+ }
+
+ override fun toString(): String {
+ val colorStr = "#${Integer.toHexString(color)}"
+ val startTimeStr = WeekViewUtil.calendarToString(startTime, !isAllDay)
+ if (isAllDay) {
+ if (WeekViewUtil.isSameDay(startTime, endTime))
+ return "allDayEvent(id=$id, time=$startTimeStr..${WeekViewUtil.calendarToString(startTime, false)}, name=$name, location=$location, color=$colorStr ,shader=$shader)"
+ return "allDayEvent(id=$id, time=$startTimeStr, name=$name, location=$location, color=$colorStr ,shader=$shader)"
+ }
+ val endTimeStr = WeekViewUtil.calendarToString(endTime, true)
+ return "normalEvent(id=$id, startTime=$colorStr, endTime=$endTimeStr, name=$name, location=$location, color=$colorStr , shader=$shader)"
+ }
+}
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..4e6c5b34d 100644
--- a/library/src/main/java/com/alamkanak/weekview/WeekViewLoader.java
+++ b/library/src/main/java/com/alamkanak/weekview/WeekViewLoader.kt
@@ -1,19 +1,20 @@
-package com.alamkanak.weekview;
+package com.alamkanak.weekview
-import java.util.Calendar;
-import java.util.List;
+import java.util.*
+
+interface WeekViewLoader {
-public 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 +22,5 @@ public interface WeekViewLoader {
* @param periodIndex the period to load
* @return A list with the events of this period
*/
- List extends WeekViewEvent> onLoad(int periodIndex);
+ fun onLoad(periodIndex: Int): MutableList?
}
diff --git a/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.java b/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.java
deleted file mode 100644
index 9a33c7221..000000000
--- a/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.alamkanak.weekview;
-
-import java.util.Calendar;
-
-/**
- * Created by jesse on 6/02/2016.
- */
-public class WeekViewUtil {
-
-
- /////////////////////////////////////////////////////////////////
- //
- // Helper methods.
- //
- /////////////////////////////////////////////////////////////////
-
- /**
- * Checks if two dates are on the same day.
- *
- * @param dateOne The first date.
- * @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);
- }
-
- /**
- * Returns a calendar instance at the start of today
- *
- * @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;
- }
-
- /**
- * Checks if two dates are on the same day and hour.
- *
- * @param dateOne The first day.
- * @param dateTwo The second day.
- * @return Whether the dates are on the same day and hour.
- */
- public static boolean isSameDayAndHour(Calendar dateOne, Calendar dateTwo) {
-
- if (dateTwo != null) {
- return isSameDay(dateOne, dateTwo) && dateOne.get(Calendar.HOUR_OF_DAY) == dateTwo.get(Calendar.HOUR_OF_DAY);
- }
- return false;
- }
-
- /**
- * Returns the amount of days between the second date and the first date
- *
- * @param dateOne the first date
- * @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)));
- }
-
- /*
- * Returns the amount of minutes passed in the day before the time in the given date
- * @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));
- }
-
- /**
- * Returns the amount of minutes in the given hours and minutes
- *
- * @param hour
- * @param minute
- * @return amount of minutes in the given hours and minutes
- */
- public static int getPassedMinutesInDay(int hour, int minute) {
- return hour * 60 + minute;
- }
-}
diff --git a/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.kt b/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.kt
new file mode 100644
index 000000000..2d83a424a
--- /dev/null
+++ b/library/src/main/java/com/alamkanak/weekview/WeekViewUtil.kt
@@ -0,0 +1,148 @@
+package com.alamkanak.weekview
+
+import android.content.Context
+import android.os.Build
+import android.text.format.DateFormat
+import java.text.SimpleDateFormat
+import java.util.*
+
+object WeekViewUtil {
+ /////////////////////////////////////////////////////////////////
+ //
+ // Helper methods.
+ //
+ /////////////////////////////////////////////////////////////////
+
+ /**
+ * Checks if two dates are on the same day.
+ *
+ * @param dateOne The first date.
+ * @param dateTwo The second date. *
+ * @return Whether the dates are on the same day.
+ */
+ @JvmStatic
+ fun isSameDay(dateOne: Calendar, dateTwo: Calendar): Boolean {
+ if (dateOne === dateTwo)
+ return true
+ return dateOne.get(Calendar.YEAR) == dateTwo.get(Calendar.YEAR) && dateOne.get(Calendar.DAY_OF_YEAR) == dateTwo.get(Calendar.DAY_OF_YEAR)
+ }
+
+ /**
+ * Returns a calendar instance at the start of today
+ *
+ * @return the calendar instance
+ */
+ @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
+ }
+
+ @JvmStatic
+ fun isSameDayAndHourAndMinute(dateOne: Calendar, dateTwo: Calendar): Boolean {
+ return isSameDay(dateOne, dateTwo) && dateOne.get(Calendar.HOUR_OF_DAY) == dateTwo.get(Calendar.HOUR_OF_DAY)
+ && dateOne.get(Calendar.MINUTE) == dateTwo.get(Calendar.MINUTE)
+ }
+
+ /**
+ * Returns the amount of days between the second date and the first date
+ *
+ * @param dateOne the first date
+ * @param dateTwo the second date
+ * @return the amount of days between dateTwo and dateOne
+ */
+ @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()
+ }
+
+ /*
+ * Returns the amount of minutes passed in the day before the time in the given date
+ * @param date
+ * @return amount of minutes in day before time
+ */
+ @JvmStatic
+ fun getPassedMinutesInDay(date: Calendar): Int {
+ return getPassedMinutesInDay(date.get(Calendar.HOUR_OF_DAY), date.get(Calendar.MINUTE))
+ }
+
+ /**
+ * Returns the amount of minutes in the given hours and minutes
+ *
+ * @param hour
+ * @param minute
+ * @return amount of minutes in the given hours and minutes
+ */
+ @JvmStatic
+ fun getPassedMinutesInDay(hour: Int, minute: Int): Int {
+ return hour * 60 + minute
+ }
+
+ /**returns a date format of dayOfWeek+day&month, based on the current locale.
+ * This is important, as the format is different in many countries. Especially the numeric part that can be different : "d/M", "M/d", "d-M", "M-d" ,...*/
+ @JvmStatic
+ fun getWeekdayWithNumericDayAndMonthFormat(context: Context, shortDate: Boolean): java.text.DateFormat {
+ val weekDayFormat = if (shortDate) "EEEEE" else "EEE"
+ val defaultDateFormatPattern = "$weekDayFormat d/M"
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ val locale = Locale.getDefault()
+ var bestDateTimePattern = DateFormat.getBestDateTimePattern(locale, defaultDateFormatPattern)
+ //workaround fix for this issue: https://issuetracker.google.com/issues/79311044
+ //TODO if there is a better API that doesn't require this workaround, use it. Be sure to check vs all locales, as done here: https://issuetracker.google.com/issues/37044127
+ bestDateTimePattern = bestDateTimePattern.replace("d+".toRegex(), "d").replace("M+".toRegex(), "M")
+ bestDateTimePattern = bestDateTimePattern.replace("E+".toRegex(), weekDayFormat)
+ return SimpleDateFormat(bestDateTimePattern, locale)
+ }
+ try {
+ val dateFormatOrder = DateFormat.getDateFormatOrder(context)
+ if (dateFormatOrder.isEmpty())
+ return SimpleDateFormat(defaultDateFormatPattern, Locale.getDefault())
+ val sb = StringBuilder()
+ for (i in dateFormatOrder.indices) {
+ val c = dateFormatOrder[i]
+ if (Character.toLowerCase(c) == 'y')
+ continue
+ if (sb.isNotEmpty())
+ sb.append('/')
+ when (Character.toLowerCase(c)) {
+ 'm' -> sb.append("M")
+ 'd' -> sb.append("d")
+ }
+ }
+ val dateFormatString = sb.toString()
+ return SimpleDateFormat("$weekDayFormat $dateFormatString", Locale.getDefault())
+ } catch (e: Exception) {
+ return SimpleDateFormat(defaultDateFormatPattern, Locale.getDefault())
+ }
+ }
+
+ @JvmStatic
+ fun calendarToString(cal: Calendar?, includeTime: Boolean = true): String {
+ if (cal == null)
+ return ""
+ val sb = StringBuilder()
+ with(cal) {
+ sb.append(get(Calendar.YEAR).toString()).append('-').append((get(Calendar.MONTH) + 1).toString())
+ .append('-').append(get(Calendar.DAY_OF_MONTH).toString())
+ if (includeTime)
+ sb.append(" ").append(get(Calendar.HOUR_OF_DAY).toString()).append(':').append(get(Calendar.MINUTE).toString()).append(':')
+ .append(get(Calendar.SECOND).toString()).append('.').append(get(Calendar.MILLISECOND).toString())
+ }
+ return sb.toString()
+ }
+
+ @JvmStatic
+ fun resetTime(cal: Calendar) {
+ with(cal)
+ {
+ set(java.util.Calendar.HOUR_OF_DAY, 0)
+ set(java.util.Calendar.SECOND, 0)
+ set(java.util.Calendar.MINUTE, 0)
+ set(java.util.Calendar.MILLISECOND, 0)
+ }
+ }
+}
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
index 516f90227..2656c95f0 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -1,69 +1,74 @@
-
-
-
-
+
+
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/build.gradle b/sample/build.gradle
index 23e0f47cc..38b5fdb01 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -1,13 +1,14 @@
apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
android {
- compileSdkVersion 25
- buildToolsVersion '25.0.2'
+ compileSdkVersion 28
defaultConfig {
applicationId "com.alamkanak.weekview"
minSdkVersion 14
- targetSdkVersion 25
+ targetSdkVersion 28
versionCode 1
versionName "1.0"
}
@@ -17,11 +18,25 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ repositories {
+ maven { url 'https://jitpack.io' }
+ }
}
+
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile project(':library')
- compile 'com.android.support:appcompat-v7:25.1.0'
- compile 'com.squareup.retrofit:retrofit:1.9.0'
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'com.squareup.retrofit:retrofit:1.9.0'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation project(':library')
+ implementation 'androidx.core:core-ktx:1.0.1'
+
+// implementation 'com.github.AndroidDeveloperLB:Android-Week-View:ef94b8d256'
+// implementation 'com.github.AndroidDeveloperLB:Android-Week-View:develop-SNAPSHOT'
+// implementation 'com.github.AndroidDeveloperLB:Android-Week-View:develop-v2.3.0-gef94b8d-26'
+
+}
+repositories {
+ mavenCentral()
}
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index 775a8b5f8..c7659a868 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -1,28 +1,29 @@
-
+
-
+
+ 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=".AsynchronousActivity" android:label="@string/title_activity_asynchronous"/>
+
+ android:name=".WholeViewSnappingActivity" android:label="@string/title_activity_whole_view_snap"
+ android:screenOrientation="reverseLandscape" android:theme="@style/AppTheme.FullScreen"/>
diff --git a/sample/src/main/assets/fonts/Raleway/Raleway-Black.ttf b/sample/src/main/assets/fonts/Raleway/Raleway-Black.ttf
deleted file mode 100644
index 6805f4f58..000000000
Binary files a/sample/src/main/assets/fonts/Raleway/Raleway-Black.ttf and /dev/null differ
diff --git a/sample/src/main/assets/fonts/Raleway/Raleway-Medium.ttf b/sample/src/main/assets/fonts/Raleway/Raleway-Medium.ttf
deleted file mode 100644
index 7a71a6ff0..000000000
Binary files a/sample/src/main/assets/fonts/Raleway/Raleway-Medium.ttf and /dev/null differ
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 extends WeekViewEvent> 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..a387be577
--- /dev/null
+++ b/sample/src/main/java/com/alamkanak/weekview/sample/AsynchronousActivity.kt
@@ -0,0 +1,70 @@
+package com.alamkanak.weekview.sample
+
+import android.os.Bundle
+import android.widget.Toast
+import com.alamkanak.weekview.WeekViewEvent
+import com.alamkanak.weekview.sample.apiclient.Event
+import com.alamkanak.weekview.sample.apiclient.MyJsonService
+import kotlinx.android.synthetic.main.activity_base.*
+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.
+ */
+class AsynchronousActivity : BaseActivity(), Callback> {
+
+ private val events = ArrayList()
+ private var calledNetwork = false
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ weekView.setLimitTime(0, 24)
+ }
+
+ override fun onMonthChange(newYear: Int, newMonth: Int): MutableList? {
+
+ // 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: MutableList, response: Response) {
+ 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 extends WeekViewEvent> 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..693ddeaf3
--- /dev/null
+++ b/sample/src/main/java/com/alamkanak/weekview/sample/BaseActivity.kt
@@ -0,0 +1,248 @@
+package com.alamkanak.weekview.sample
+
+import android.content.ClipData
+import android.graphics.RectF
+import android.os.Bundle
+import android.util.TypedValue
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import com.alamkanak.weekview.*
+import kotlinx.android.synthetic.main.activity_base.*
+import java.text.DateFormat
+import java.text.SimpleDateFormat
+import java.util.*
+
+//import android.text.format.DateFormat
+/**
+ * This is a base activity which contains week view and all the codes necessary to initialize the
+ * week view.
+ */
+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
+ private lateinit var shortDateFormat: DateFormat
+ private lateinit var timeFormat: DateFormat
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ shortDateFormat = WeekViewUtil.getWeekdayWithNumericDayAndMonthFormat(this, true)
+ timeFormat = android.text.format.DateFormat.getTimeFormat(this) ?: SimpleDateFormat("HH:mm", Locale.getDefault())
+ setContentView(R.layout.activity_base)
+
+ draggable_view.setOnLongClickListener(DragTapListener())
+
+ // Get a reference for the week view in the layout.
+
+ // Show a toast message about the touched event.
+ weekView.eventClickListener = 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.dropListener = 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
+ 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) {
+ setupDateTimeInterpreter(dayViewType == TYPE_WEEK_VIEW)
+
+ 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)
+ weekView.eventTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics)
+ }
+ 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)
+ weekView.eventTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics)
+ }
+ 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)
+ weekView.eventTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10f, resources.displayMetrics)
+ }
+ }
+ }
+
+ /**
+ * 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.
+ */
+ protected open fun setupDateTimeInterpreter(shortDate: Boolean) {
+ val calendar = Calendar.getInstance().apply {
+ set(Calendar.MINUTE, 0)
+ set(Calendar.SECOND, 0)
+ set(Calendar.MILLISECOND, 0)
+ }
+ val normalDateFormat = WeekViewUtil.getWeekdayWithNumericDayAndMonthFormat(this@BaseActivity, false)
+ weekView.dateTimeInterpreter = object : DateTimeInterpreter {
+ override fun getFormattedTimeOfDay(hour: Int, minutes: Int): String {
+ calendar.set(Calendar.HOUR_OF_DAY, hour)
+ calendar.set(Calendar.MINUTE, minutes)
+ return timeFormat.format(calendar.time)
+ }
+
+ override fun getFormattedWeekDayTitle(date: Calendar): String {
+ return if (shortDate) shortDateFormat.format(date.time) else normalDateFormat.format(date.time)
+ }
+ }
+ }
+
+ protected fun getEventTitle(startCal: Calendar, endCal: Calendar? = null, allDay: Boolean = false): String {
+ val startDate = startCal.time
+ val endDate = endCal?.time
+ return when {
+ allDay -> {
+ if (endCal == null || WeekViewUtil.isSameDay(startCal, endCal))
+ shortDateFormat.format(startDate)
+ else "${shortDateFormat.format(startDate)}..${shortDateFormat.format(endDate)}"
+ }
+ endCal == null -> "${shortDateFormat.format(startDate)} ${timeFormat.format(startDate)}"
+ WeekViewUtil.isSameDay(startCal, endCal) -> "${shortDateFormat.format(startDate)} ${timeFormat.format(startDate)}..${timeFormat.format(endDate)}"
+ else -> "${shortDateFormat.format(startDate)} ${timeFormat.format(startDate)}..${shortDateFormat.format(endDate)} ${timeFormat.format(endDate)}"
+ }
+ }
+
+ 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): MutableList? {
+ 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 {
+ const val TYPE_DAY_VIEW = 1
+ const val TYPE_THREE_DAY_VIEW = 2
+ const 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 extends WeekViewEvent> 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..7d66593d7
--- /dev/null
+++ b/sample/src/main/java/com/alamkanak/weekview/sample/BasicActivity.kt
@@ -0,0 +1,189 @@
+package com.alamkanak.weekview.sample
+
+import android.os.Bundle
+import androidx.core.content.res.ResourcesCompat
+import com.alamkanak.weekview.WeekViewEvent
+import kotlinx.android.synthetic.main.activity_base.*
+import java.util.*
+
+/**
+ * A basic example of how to use week view library.
+ */
+open class BasicActivity : BaseActivity() {
+ var uniqueId: Long = 0
+ fun getUniqueId(): String = uniqueId++.toString()
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ weekView.typeface = ResourcesCompat.getFont(this, R.font.lato)
+ }
+
+ override fun onMonthChange(newYear: Int, newMonth: Int): MutableList? {
+ // 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, endTime), startTime, endTime)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_01, null)
+ events.add(event)
+
+ startTime = Calendar.getInstance()
+ startTime.set(Calendar.MINUTE, 0)
+ startTime.set(Calendar.MONTH, newMonth - 1)
+ startTime.set(Calendar.YEAR, newYear)
+ endTime = startTime.clone() as Calendar
+ endTime.set(Calendar.HOUR_OF_DAY, startTime.get(Calendar.HOUR_OF_DAY) + 1)
+ endTime.set(Calendar.MINUTE, 0)
+ endTime.set(Calendar.MONTH, newMonth - 1)
+ event = WeekViewEvent("cur", "cur", startTime, endTime)
+ event.color = 0xffff0000.toInt()
+ 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, endTime), startTime, endTime)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_05, null)
+ 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(getUniqueId(), getEventTitle(startTime, endTime), startTime, endTime)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_03, null)
+ 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(getUniqueId(), getEventTitle(startTime, endTime), startTime, endTime)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_02, null)
+ 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)
+ event = WeekViewEvent(getUniqueId(), getEventTitle(startTime, endTime), startTime, endTime)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_03, null)
+ 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(getUniqueId(), getEventTitle(startTime, endTime), startTime, endTime)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_04, null)
+ 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(getUniqueId(), getEventTitle(startTime, endTime), startTime, endTime)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_01, null)
+ 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(getUniqueId(), getEventTitle(startTime, endTime), startTime, endTime)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_02, null)
+ events.add(event)
+
+ //AllDay event
+ //single day
+ 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)
+ event = WeekViewEvent(getUniqueId(), getEventTitle(startTime, allDay = true), null, startTime)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_04, null)
+ 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(getUniqueId(), getEventTitle(startTime, endTime, true), null, startTime, endTime, true)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_03, null)
+ 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(getUniqueId(), getEventTitle(startTime, endTime, true), null, startTime, endTime, true)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_01, null)
+
+ 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(getUniqueId(), getEventTitle(startTime, endTime, true), null, startTime, endTime, true)
+ event.color = ResourcesCompat.getColor(resources, R.color.event_color_02, null)
+ 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..060c8c073
--- /dev/null
+++ b/sample/src/main/java/com/alamkanak/weekview/sample/MainActivity.kt
@@ -0,0 +1,52 @@
+package com.alamkanak.weekview.sample
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuItem
+import androidx.appcompat.app.AppCompatActivity
+import kotlinx.android.synthetic.main.activity_main.*
+
+/**
+ * The launcher activity of the sample app. It contains the links to visit all the example screens.
+ */
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ buttonBasic.setOnClickListener {
+ startActivity(Intent(this@MainActivity, BasicActivity::class.java))
+ }
+ buttonAsynchronous.setOnClickListener {
+ startActivity(Intent(this@MainActivity, AsynchronousActivity::class.java))
+ }
+ buttonWholeViewSnap.setOnClickListener {
+ startActivity(Intent(this@MainActivity, WholeViewSnappingActivity::class.java))
+ }
+// startActivity(Intent(this@MainActivity, WholeViewSnappingActivity::class.java))
+// finish()
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.main_activity, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ var url: String? = null
+ when (item.itemId) {
+ R.id.menuItem_all_my_apps -> url = "https://play.google.com/store/apps/developer?id=AndroidDeveloperLB"
+ R.id.menuItem_all_my_repositories -> url = "https://github.com/AndroidDeveloperLB"
+ R.id.menuItem_current_repository_website -> url = "https://github.com/AndroidDeveloperLB/Android-Week-View"
+ }
+ if (url == null)
+ return true
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ startActivity(intent)
+ return true
+ }
+}
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..435372193
--- /dev/null
+++ b/sample/src/main/java/com/alamkanak/weekview/sample/WholeViewSnappingActivity.kt
@@ -0,0 +1,109 @@
+package com.alamkanak.weekview.sample
+
+import android.graphics.RectF
+import android.os.Bundle
+import android.text.format.DateFormat
+import android.util.TypedValue
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import com.alamkanak.weekview.DateTimeInterpreter
+import com.alamkanak.weekview.WeekDaySubtitleInterpreter
+import com.alamkanak.weekview.WeekView
+import com.alamkanak.weekview.WeekViewEvent
+import kotlinx.android.synthetic.main.activity_base.*
+import java.text.SimpleDateFormat
+import java.util.*
+
+
+/**
+ * Activity to demonstrate snapping of the whole view, for example week-by-week.
+ */
+class WholeViewSnappingActivity : BasicActivity() {
+ val locale = Locale.getDefault()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ weekView.isShowNowLine = true
+// weekView.setAutoLimitTime(true)
+ weekView.setLimitTime(0, 24)
+ weekView.isUsingCheckersStyle = true
+ weekView.columnGap = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, resources.displayMetrics).toInt()
+ weekView.hourSeparatorHeight = weekView.columnGap
+ weekView.isScrollNumberOfVisibleDays = true
+ weekView.dropListener = null
+ weekView.allDaySideTitleText = getString(R.string.all_day)
+ setDayViewType(TYPE_WEEK_VIEW)
+ val cal = Calendar.getInstance()
+ val currentHour = cal.get(Calendar.HOUR_OF_DAY) + cal.get(Calendar.MINUTE) / 60.0
+ weekView.goToHour(Math.max(currentHour - 1, 0.0))
+ cal.set(Calendar.DAY_OF_WEEK, cal.firstDayOfWeek)
+ weekView.goToDate(cal)
+ weekView.scrollListener = object : WeekView.ScrollListener {
+ val monthFormatter = SimpleDateFormat("MMM", locale)
+ val yearFormatter = SimpleDateFormat("yyyy", locale)
+
+ override fun onFirstVisibleDayChanged(newFirstVisibleDay: Calendar, oldFirstVisibleDay: Calendar?) {
+ //we show just the month here, so no need to update it every time
+ if (oldFirstVisibleDay == null || oldFirstVisibleDay.get(Calendar.MONTH) != newFirstVisibleDay.get(Calendar.MONTH)) {
+ val date = newFirstVisibleDay.time
+ if (cal.get(Calendar.YEAR) == newFirstVisibleDay.get(Calendar.YEAR)) {
+ weekView.sideSubtitleText = ""
+ weekView.sideTitleText = monthFormatter.format(date)
+ } else {
+ weekView.sideTitleText = monthFormatter.format(date)
+ weekView.sideSubtitleText = yearFormatter.format(date)
+ }
+ }
+ }
+ }
+ draggable_view.visibility = View.GONE
+ weekView.weekDaySubtitleInterpreter = object : WeekDaySubtitleInterpreter {
+ val dateFormatTitle = SimpleDateFormat("d", locale)
+
+ override fun getFormattedWeekDaySubtitle(date: Calendar): String = dateFormatTitle.format(date.time)
+ }
+ weekView.eventClickListener = object : WeekView.EventClickListener {
+ override fun onEventClick(event: WeekViewEvent, eventRect: RectF) {
+ }
+ }
+ }
+
+ override fun setupDateTimeInterpreter(shortDate: Boolean) {
+ val calendar = Calendar.getInstance().apply {
+ set(Calendar.MINUTE, 0)
+ set(Calendar.SECOND, 0)
+ set(Calendar.MILLISECOND, 0)
+ }
+ val timeFormat = DateFormat.getTimeFormat(this)
+ ?: SimpleDateFormat("HH:mm", locale)
+ val dateFormatTitle = SimpleDateFormat("EEE", locale)
+ weekView.dateTimeInterpreter = object : DateTimeInterpreter {
+ override fun getFormattedTimeOfDay(hour: Int, minutes: Int): String {
+ calendar.set(Calendar.HOUR_OF_DAY, hour)
+ calendar.set(Calendar.MINUTE, minutes)
+ return timeFormat.format(calendar.time)
+ }
+
+ override fun getFormattedWeekDayTitle(date: Calendar): String = dateFormatTitle.format(date.time)
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == R.id.action_today) {
+ val cal = Calendar.getInstance().apply { set(Calendar.DAY_OF_WEEK, firstDayOfWeek) }
+ weekView.goToDate(cal)
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ val result = super.onCreateOptionsMenu(menu)
+ menu.findItem(R.id.action_day_view).isChecked = false
+ menu.findItem(R.id.action_three_day_view).isChecked = false
+ menu.findItem(R.id.action_week_view).isChecked = true
+ return result
+ }
+}
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..38bdbb62c
--- /dev/null
+++ b/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/Event.kt
@@ -0,0 +1,75 @@
+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.
+ */
+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 = Calendar.getInstance()
+ startTime.timeInMillis = start.time
+ with(startTime) {
+ set(Calendar.YEAR, now.get(Calendar.YEAR))
+ set(Calendar.MONTH, now.get(Calendar.MONTH))
+ set(Calendar.DAY_OF_MONTH, dayOfMonth)
+ }
+ val endTime = startTime.clone() as Calendar
+ endTime.timeInMillis = end.time
+ with(endTime) {
+ set(Calendar.YEAR, startTime.get(Calendar.YEAR))
+ set(Calendar.MONTH, startTime.get(Calendar.MONTH))
+ set(Calendar.DAY_OF_MONTH, startTime.get(Calendar.DAY_OF_MONTH))
+ }
+
+ // Create an week view event.
+ val weekViewEvent = WeekViewEvent(name, name, null, startTime, 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..9c51ff677
--- /dev/null
+++ b/sample/src/main/java/com/alamkanak/weekview/sample/apiclient/MyJsonService.kt
@@ -0,0 +1,11 @@
+package com.alamkanak.weekview.sample.apiclient
+
+import retrofit.Callback
+import retrofit.http.GET
+
+interface MyJsonService {
+
+ @GET("/1kpjf")
+ fun listEvents(eventsCallback: Callback>)
+
+}
diff --git a/sample/src/main/res/font/lato.ttf b/sample/src/main/res/font/lato.ttf
new file mode 100644
index 000000000..04ea8efb1
Binary files /dev/null and b/sample/src/main/res/font/lato.ttf differ
diff --git a/sample/src/main/res/layout/activity_base.xml b/sample/src/main/res/layout/activity_base.xml
index 910d93e3b..1b8288b4f 100644
--- a/sample/src/main/res/layout/activity_base.xml
+++ b/sample/src/main/res/layout/activity_base.xml
@@ -1,43 +1,19 @@
-
+
+ android:id="@+id/weekView" android:layout_width="match_parent" android:layout_height="match_parent"
+ app:autoLimitTime="false" app:dayBackgroundColor="#05000000" app:headerColumnTextColor="@color/toolbar_text"
+ app:headerRowBackgroundColor="@color/toolbar" app:maxTime="16" app:minOverlappingMinutes="5" app:minTime="4"
+ app:newEventTimeResolutionInMinutes="15" app:noOfVisibleDays="3" app:timeColumnResolution="60"
+ app:todayColumnBackgroundColor="#1848adff" app:todayHeaderTextColor="@color/accent"
+ app:untitledEventText="@string/untitled_event"/>
+ android:id="@+id/draggable_view" android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:background="#ffff0000" android:padding="3dp" android:text="@string/drag_me"
+ android:textColor="@android:color/white"/>
diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml
index f8e348f0b..a39762d25 100644
--- a/sample/src/main/res/layout/activity_main.xml
+++ b/sample/src/main/res/layout/activity_main.xml
@@ -1,23 +1,17 @@
-
+
+ android:id="@+id/buttonBasic" style="@style/Button" android:layout_width="match_parent"
+ android:layout_height="wrap_content" android:text="@string/title_activity_basic"/>
+ android:id="@+id/buttonAsynchronous" style="@style/Button" android:layout_width="match_parent"
+ android:layout_height="wrap_content" android:text="@string/title_activity_asynchronous"/>
+
diff --git a/sample/src/main/res/menu/main_activity.xml b/sample/src/main/res/menu/main_activity.xml
new file mode 100644
index 000000000..f771358a3
--- /dev/null
+++ b/sample/src/main/res/menu/main_activity.xml
@@ -0,0 +1,19 @@
+
+
diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
index 28e4f67e4..9ae67662a 100644
--- a/sample/src/main/res/values/strings.xml
+++ b/sample/src/main/res/values/strings.xml
@@ -9,4 +9,7 @@
Asynchronous EventsCould not download events from the internetDrag Me
+ Whole view snap
+ All day
+ Untitled event
diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml
index d6953b187..2187839b8 100644
--- a/sample/src/main/res/values/styles.xml
+++ b/sample/src/main/res/values/styles.xml
@@ -9,6 +9,14 @@
@color/toolbar_text@style/ThemeOverlay.AppCompat.Light@color/accent
+ @android:color/white
+
+
+