diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 313dc83..b0aed3c 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -18,17 +18,14 @@ permissions: jobs: build: - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 - uses: actions/setup-java@v3 + - name: Set up JDK 18 + - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: 17 distribution: 'temurin' - - name: Build with Gradle - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 + - uses: gradle/gradle-build-action@v2 with: arguments: build diff --git a/app/build.gradle b/app/build.gradle index 60424eb..d6abcc9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "com.mabn.calendar" minSdk 26 - targetSdk 32 + targetSdk 34 versionCode 1 versionName "1.0" @@ -36,26 +36,27 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion "1.3.1" + kotlinCompilerExtensionVersion "1.5.4" } packagingOptions { resources { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + namespace 'com.mabn.calendar' } dependencies { implementation(project(path:":library")) - implementation 'androidx.core:core-ktx:1.9.0' + implementation 'androidx.core:core-ktx:1.12.0' implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.material:material:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' - implementation 'androidx.activity:activity-compose:1.5.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' + implementation 'androidx.activity:activity-compose:1.8.1' testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" diff --git a/app/src/androidTest/java/com/mabn/calendar/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/mabn/calendar/ExampleInstrumentedTest.kt deleted file mode 100644 index eebed5c..0000000 --- a/app/src/androidTest/java/com/mabn/calendar/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.mabn.calendar - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.mabn.calendar", appContext.packageName) - } -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 779e021..a7a0469 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> Unit) { val colors = if (darkTheme) { - DarkColorPalette + _darkColorPalette } else { - LightColorPalette + _lightColorPalette } MaterialTheme( diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml similarity index 100% rename from app/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to app/src/main/res/drawable/ic_launcher_foreground.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to app/src/main/res/mipmap-anydpi/ic_launcher.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to app/src/main/res/mipmap-anydpi/ic_launcher_round.xml diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 31a3ae3..1e661d2 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,7 +1,4 @@ #FF000000 - #FFFFFFFF - #FFF2F5DE - #FF86E7B8 \ No newline at end of file diff --git a/app/src/test/java/com/mabn/calendar/ExampleUnitTest.kt b/app/src/test/java/com/mabn/calendar/ExampleUnitTest.kt deleted file mode 100644 index 2656a18..0000000 --- a/app/src/test/java/com/mabn/calendar/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.mabn.calendar - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 1e1b63b..03fbf37 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,15 @@ buildscript { ext { - compose_version = '1.2.1' + compose_version = '1.5.4' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.2.1' apply false - id 'com.android.library' version '7.2.1' apply false - id 'org.jetbrains.kotlin.android' version '1.7.10' apply false + id 'com.android.application' version '8.1.2' apply false + id 'com.android.library' version '8.1.2' apply false + id 'org.jetbrains.kotlin.android' version '1.9.20' apply false id 'org.jetbrains.kotlinx.kover' version "0.6.0" } -task clean(type: Delete) { +tasks.register('clean', Delete) { delete rootProject.buildDir } diff --git a/gradle.properties b/gradle.properties index cd0519b..022338b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,6 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b269c8d..f1ecd19 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Sep 19 11:07:20 CEST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 4f906e0..d71de43 100755 --- a/gradlew +++ b/gradlew @@ -27,20 +27,20 @@ PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then +ls=`ls -ld "$PRG"` +link=`expr "$ls" : '.*-> \(.*\)$'` +if expr "$link" : '/.*' > /dev/null; then PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done +else +PRG=`dirname "$PRG"`"/$link" +fi + done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" + APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null -APP_NAME="Gradle" + APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @@ -66,111 +66,111 @@ msys=false darwin=false nonstop=false case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; + CYGWIN* ) +cygwin=true +;; +Darwin* ) +darwin=true +;; +MINGW* ) +msys=true +;; +NONSTOP* ) +nonstop=true +;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables +if [ -x "$JAVA_HOME/jre/sh/java" ] ; then +# IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then +else +JAVACMD="$JAVA_HOME/bin/java" +fi +if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi + location of your Java installation." +fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +JAVACMD="java" +which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." + location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi + MAX_FD_LIMIT=`ulimit -H -n` +if [ $? -eq 0 ] ; then +if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" +fi ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" fi +else +warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" +fi + fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin or MSYS, switch paths to Windows format before running java if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` +CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + +JAVACMD=`cygpath --unix "$JAVACMD"` + +# We build the pattern for arguments to be converted via cygpath +ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` +SEP="" +for dir in $ROOTDIRSRAW ; do +ROOTDIRS="$ROOTDIRS$SEP$dir" +SEP="|" +done + OURCYGPATTERN="(^($ROOTDIRS))" +# Add a user-defined pattern to the cygpath arguments +if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi +# Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 +for arg in "$@" ; do +CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` +CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + +if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` +else +eval `echo args$i`="\"$arg\"" +fi + i=`expr $i + 1` +done +case $i in +0) set -- ;; +1) set -- "$args0" ;; +2) set -- "$args0" "$args1" ;; +3) set -- "$args0" "$args1" "$args2" ;; +4) set -- "$args0" "$args1" "$args2" "$args3" ;; +5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; +6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; +7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; +8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; +9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +esac + fi # Escape application args save () { diff --git a/library/build.gradle b/library/build.gradle index 3fbbb0d..8d59ccf 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -4,12 +4,11 @@ plugins { } android { - compileSdk 33 + compileSdk 34 defaultConfig { minSdk 26 - targetSdk 32 - + targetSdk 34 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" } @@ -28,32 +27,30 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } composeOptions { - kotlinCompilerExtensionVersion "1.3.1" + kotlinCompilerExtensionVersion "1.5.4" } kotlinOptions { jvmTarget = '1.8' } + namespace 'com.mabn.calendarlibrary' } dependencies { - - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.appcompat:appcompat:1.5.1' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2' implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.material:material:$compose_version" implementation "androidx.compose.runtime:runtime-livedata:$compose_version" implementation "com.google.accompanist:accompanist-pager:0.23.1" implementation "com.google.accompanist:accompanist-flowlayout:0.26.4-beta" - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2" + debugImplementation "androidx.compose.ui:ui-test-manifest:$rootProject.compose_version" + testImplementation "junit:junit:4.13.2" + testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0" + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm:1.6.4" + androidTestImplementation "androidx.test.ext:junit:1.1.5" + androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1" androidTestImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0" - // lifecycle - implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1" - - - // Compose testing dependencies androidTestImplementation "androidx.compose.ui:ui-test-junit4:$rootProject.compose_version" - debugImplementation "androidx.compose.ui:ui-test-manifest:$rootProject.compose_version" } \ No newline at end of file diff --git a/library/src/androidTest/java/com/mabn/calendarlibrary/DayViewTest.kt b/library/src/androidTest/java/com/mabn/calendarlibrary/DayViewTest.kt index 884b35b..05f763a 100644 --- a/library/src/androidTest/java/com/mabn/calendarlibrary/DayViewTest.kt +++ b/library/src/androidTest/java/com/mabn/calendarlibrary/DayViewTest.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.test.* import androidx.compose.ui.test.junit4.createComposeRule import com.mabn.calendarlibrary.component.DayView +import com.mabn.calendarlibrary.core.calendarDefaultTheme import com.mabn.calendarlibrary.utils.getBackgroundColor import org.junit.* import org.junit.runners.MethodSorters @@ -26,8 +27,8 @@ internal class DayViewTest { composeTestRule.setContent { Surface(Modifier.background(Color.White)){ Row { - DayView(todayDate, onDayClick = {}) - DayView(tomorrowDate, onDayClick = {}) + DayView(todayDate, onDayClick = {}, theme = calendarDefaultTheme) + DayView(tomorrowDate, onDayClick = {}, theme = calendarDefaultTheme) } } } diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml index e2e82ab..44008a4 100644 --- a/library/src/main/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/library/src/main/java/com/mabn/calendarlibrary/CalendarViewModel.kt b/library/src/main/java/com/mabn/calendarlibrary/CalendarViewModel.kt index 3685b78..58edbd8 100644 --- a/library/src/main/java/com/mabn/calendarlibrary/CalendarViewModel.kt +++ b/library/src/main/java/com/mabn/calendarlibrary/CalendarViewModel.kt @@ -3,9 +3,10 @@ package com.mabn.calendarlibrary import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.mabn.calendarlibrary.core.CalendarIntent +import com.mabn.calendarlibrary.core.DateTimeConstants import com.mabn.calendarlibrary.core.Period +import com.mabn.calendarlibrary.core.RelativePosition import com.mabn.calendarlibrary.utils.* -import com.mabn.calendarlibrary.utils.getMonthStartDate import com.mabn.calendarlibrary.utils.getNextDates import com.mabn.calendarlibrary.utils.getRemainingDatesInWeek import com.mabn.calendarlibrary.utils.getWeekStartDate @@ -16,6 +17,7 @@ import java.time.LocalDate import java.time.YearMonth class CalendarViewModel : ViewModel() { + private val _visibleDates = MutableStateFlow( calculateCollapsedCalendarDays( @@ -30,12 +32,16 @@ class CalendarViewModel : ViewModel() { val currentMonth: StateFlow get() = calendarExpanded.zip(visibleDates) { isExpanded, dates -> if (isExpanded) { - dates[1][dates[1].size / 2].yearMonth() + dates[RelativePosition.CURRENT.ordinal][dates[RelativePosition.CURRENT.ordinal].size / 2].yearMonth() } else { - if (dates[1].count { it.month == dates[1][0].month } > 3) - dates[1][0].yearMonth() - else - dates[1][dates[1].size - 1].yearMonth() + if (dates[RelativePosition.CURRENT.ordinal].count { it.month == dates[RelativePosition.CURRENT.ordinal].first().month } > RelativePosition.values().size + ) { + dates[RelativePosition.CURRENT.ordinal].first() + .yearMonth() + } else { + dates[RelativePosition.CURRENT.ordinal].last() + .yearMonth() + } } }.stateIn(viewModelScope, SharingStarted.Eagerly, LocalDate.now().yearMonth()) @@ -48,11 +54,14 @@ class CalendarViewModel : ViewModel() { when (intent) { CalendarIntent.ExpandCalendar -> { calculateCalendarDates( - startDate = currentMonth.value.minusMonths(1).atDay(1), + startDate = currentMonth.value + .minusMonths(1) + .atDay(1), period = Period.MONTH ) _calendarExpanded.value = true } + CalendarIntent.CollapseCalendar -> { calculateCalendarDates( startDate = calculateCollapsedCalendarVisibleStartDay() @@ -62,9 +71,11 @@ class CalendarViewModel : ViewModel() { ) _calendarExpanded.value = false } + is CalendarIntent.LoadNextDates -> { calculateCalendarDates(intent.startDate, intent.period) } + is CalendarIntent.SelectDate -> { viewModelScope.launch { _selectedDate.emit(intent.date) @@ -88,7 +99,8 @@ class CalendarViewModel : ViewModel() { } private fun calculateCollapsedCalendarVisibleStartDay(): LocalDate { - val halfOfMonth = visibleDates.value[1][visibleDates.value[1].size / 2] + val halfOfMonth = + visibleDates.value[RelativePosition.CURRENT.ordinal][visibleDates.value[RelativePosition.CURRENT.ordinal].size / 2] val visibleMonth = YearMonth.of(halfOfMonth.year, halfOfMonth.month) return if (selectedDate.value.month == visibleMonth.month && selectedDate.value.year == visibleMonth.year) selectedDate.value @@ -96,14 +108,14 @@ class CalendarViewModel : ViewModel() { } private fun calculateCollapsedCalendarDays(startDate: LocalDate): Array> { - val dates = startDate.getNextDates(21) - return Array(3) { - dates.slice(it * 7 until (it + 1) * 7) + val dates = startDate.getNextDates(RelativePosition.values().size * DateTimeConstants.DAYS_IN_WEEK) + return Array(RelativePosition.values().size) { + dates.slice(it * DateTimeConstants.DAYS_IN_WEEK until (it + 1) * DateTimeConstants.DAYS_IN_WEEK) } } private fun calculateExpandedCalendarDays(startDate: LocalDate): Array> { - val array = Array(3) { monthIndex -> + val array = Array(RelativePosition.values().size) { monthIndex -> val monthFirstDate = startDate.plusMonths(monthIndex.toLong()) val monthLastDate = monthFirstDate.plusMonths(1).minusDays(1) val weekBeginningDate = monthFirstDate.getWeekStartDate() diff --git a/library/src/main/java/com/mabn/calendarlibrary/ExpandableCalendar.kt b/library/src/main/java/com/mabn/calendarlibrary/ExpandableCalendar.kt index 1b5f160..5d9c489 100644 --- a/library/src/main/java/com/mabn/calendarlibrary/ExpandableCalendar.kt +++ b/library/src/main/java/com/mabn/calendarlibrary/ExpandableCalendar.kt @@ -2,26 +2,28 @@ package com.mabn.calendarlibrary import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import java.time.YearMonth import androidx.lifecycle.viewmodel.compose.viewModel -import com.mabn.calendarlibrary.core.CalendarIntent -import com.mabn.calendarlibrary.core.Period -import com.mabn.calendarlibrary.utils.getWeekStartDate import com.mabn.calendarlibrary.component.InlineCalendar import com.mabn.calendarlibrary.component.MonthText import com.mabn.calendarlibrary.component.MonthViewCalendar import com.mabn.calendarlibrary.component.ToggleExpandCalendarButton +import com.mabn.calendarlibrary.core.CalendarIntent import com.mabn.calendarlibrary.core.CalendarTheme +import com.mabn.calendarlibrary.core.Period import com.mabn.calendarlibrary.core.calendarDefaultTheme -import com.mabn.calendarlibrary.utils.yearMonth +import com.mabn.calendarlibrary.utils.getWeekStartDate import java.time.LocalDate +import java.time.YearMonth @Composable fun ExpandableCalendar( diff --git a/library/src/main/java/com/mabn/calendarlibrary/component/CalendarPager.kt b/library/src/main/java/com/mabn/calendarlibrary/component/CalendarPager.kt index 3653832..e1e31db 100644 --- a/library/src/main/java/com/mabn/calendarlibrary/component/CalendarPager.kt +++ b/library/src/main/java/com/mabn/calendarlibrary/component/CalendarPager.kt @@ -8,6 +8,7 @@ import androidx.compose.ui.Alignment import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.rememberPagerState +import com.mabn.calendarlibrary.core.RelativePosition import java.time.LocalDate @OptIn(ExperimentalPagerApi::class) @@ -19,22 +20,22 @@ internal fun CalendarPager( content: @Composable (currentPage: Int) -> Unit ) { val initialized = remember { mutableStateOf(false) } - val pagerState = rememberPagerState(initialPage = 1) + val pagerState = rememberPagerState(initialPage = RelativePosition.CURRENT.ordinal) LaunchedEffect(pagerState.currentPage) { - if (pagerState.currentPage == 2) { - loadNextDates(loadedDates[1][0]) - pagerState.scrollToPage(1) + if (pagerState.currentPage == RelativePosition.NEXT.ordinal) { + loadNextDates(loadedDates[RelativePosition.CURRENT.ordinal].first()) + pagerState.scrollToPage(RelativePosition.CURRENT.ordinal) } - if (pagerState.currentPage == 0 && initialized.value) { - loadPrevDates(loadedDates[0][0]) - pagerState.scrollToPage(1) + if (pagerState.currentPage == RelativePosition.PREVIOUS.ordinal && initialized.value) { + loadPrevDates(loadedDates[RelativePosition.PREVIOUS.ordinal].first()) + pagerState.scrollToPage(RelativePosition.CURRENT.ordinal) } } LaunchedEffect(Unit) { initialized.value = true } HorizontalPager( - count = 3, + count = RelativePosition.values().size, state = pagerState, verticalAlignment = Alignment.Top ) { currentPage -> diff --git a/library/src/main/java/com/mabn/calendarlibrary/component/InlineCalendar.kt b/library/src/main/java/com/mabn/calendarlibrary/component/InlineCalendar.kt index dfeb6c9..588e5d6 100644 --- a/library/src/main/java/com/mabn/calendarlibrary/component/InlineCalendar.kt +++ b/library/src/main/java/com/mabn/calendarlibrary/component/InlineCalendar.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.unit.dp import com.mabn.calendarlibrary.core.CalendarTheme +import com.mabn.calendarlibrary.core.DateTimeConstants import com.mabn.calendarlibrary.utils.dayViewModifier import java.time.LocalDate @@ -22,7 +23,7 @@ internal fun InlineCalendar( loadPrevWeek: (endWeekDate: LocalDate) -> Unit, onDayClick: (LocalDate) -> Unit ) { - val itemWidth = LocalConfiguration.current.screenWidthDp / 7 + val itemWidth = LocalConfiguration.current.screenWidthDp / DateTimeConstants.DAYS_IN_WEEK CalendarPager( loadedDates = loadedDates, loadNextDates = loadNextWeek, diff --git a/library/src/main/java/com/mabn/calendarlibrary/component/MonthViewCalendar.kt b/library/src/main/java/com/mabn/calendarlibrary/component/MonthViewCalendar.kt index 048716e..22073d8 100644 --- a/library/src/main/java/com/mabn/calendarlibrary/component/MonthViewCalendar.kt +++ b/library/src/main/java/com/mabn/calendarlibrary/component/MonthViewCalendar.kt @@ -1,17 +1,14 @@ package com.mabn.calendarlibrary.component import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.unit.dp -import com.google.accompanist.flowlayout.FlowColumn import com.google.accompanist.flowlayout.FlowRow import com.mabn.calendarlibrary.core.CalendarTheme +import com.mabn.calendarlibrary.core.DateTimeConstants import com.mabn.calendarlibrary.utils.dayViewModifier import java.time.LocalDate import java.time.YearMonth @@ -25,11 +22,11 @@ internal fun MonthViewCalendar( loadDatesForMonth: (YearMonth) -> Unit, onDayClick: (LocalDate) -> Unit ) { - val itemWidth = LocalConfiguration.current.screenWidthDp / 7 + val itemWidth = LocalConfiguration.current.screenWidthDp / DateTimeConstants.DAYS_IN_WEEK CalendarPager( loadedDates = loadedDates, loadNextDates = { loadDatesForMonth(currentMonth) }, - loadPrevDates = { loadDatesForMonth(currentMonth.minusMonths(2)) } // why 2 + loadPrevDates = { loadDatesForMonth(currentMonth.minusMonths(2)) } ) { currentPage -> FlowRow(Modifier.height(355.dp)) { loadedDates[currentPage].forEachIndexed { index, date -> @@ -44,7 +41,7 @@ internal fun MonthViewCalendar( theme = theme, isSelected = selectedDate == date, onDayClick = { onDayClick(date) }, - weekDayLabel = index < 7, + weekDayLabel = index < DateTimeConstants.DAYS_IN_WEEK, modifier = Modifier.dayViewModifier(date, currentMonth, monthView = true) ) } diff --git a/library/src/main/java/com/mabn/calendarlibrary/core/CalendarIntent.kt b/library/src/main/java/com/mabn/calendarlibrary/core/CalendarIntent.kt index 4b073c5..151b463 100644 --- a/library/src/main/java/com/mabn/calendarlibrary/core/CalendarIntent.kt +++ b/library/src/main/java/com/mabn/calendarlibrary/core/CalendarIntent.kt @@ -11,6 +11,6 @@ sealed class CalendarIntent { class SelectDate(val date: LocalDate) : CalendarIntent() - object ExpandCalendar : CalendarIntent() - object CollapseCalendar : CalendarIntent() + data object ExpandCalendar : CalendarIntent() + data object CollapseCalendar : CalendarIntent() } diff --git a/library/src/main/java/com/mabn/calendarlibrary/core/DateTimeConstants.kt b/library/src/main/java/com/mabn/calendarlibrary/core/DateTimeConstants.kt new file mode 100644 index 0000000..620fa93 --- /dev/null +++ b/library/src/main/java/com/mabn/calendarlibrary/core/DateTimeConstants.kt @@ -0,0 +1,7 @@ +package com.mabn.calendarlibrary.core + +class DateTimeConstants() { + companion object { + const val DAYS_IN_WEEK = 7 + } +} diff --git a/library/src/main/java/com/mabn/calendarlibrary/core/TimePeriodType.kt b/library/src/main/java/com/mabn/calendarlibrary/core/TimePeriodType.kt new file mode 100644 index 0000000..f59c89f --- /dev/null +++ b/library/src/main/java/com/mabn/calendarlibrary/core/TimePeriodType.kt @@ -0,0 +1,7 @@ +package com.mabn.calendarlibrary.core + +enum class RelativePosition { + PREVIOUS, + CURRENT, + NEXT +} \ No newline at end of file diff --git a/library/src/main/java/com/mabn/calendarlibrary/utils/LocalDateExtension.kt b/library/src/main/java/com/mabn/calendarlibrary/utils/LocalDateExtension.kt index 05748b8..fa1e2c9 100644 --- a/library/src/main/java/com/mabn/calendarlibrary/utils/LocalDateExtension.kt +++ b/library/src/main/java/com/mabn/calendarlibrary/utils/LocalDateExtension.kt @@ -26,13 +26,6 @@ internal fun LocalDate.getWeekStartDate(weekStartDay: DayOfWeek = DayOfWeek.MOND return date } -/** - * @return first date of the month - */ -internal fun LocalDate.getMonthStartDate(): LocalDate { - return LocalDate.of(this.year, this.month, 1) -} - /** * @return list of dates remaining in the week */ diff --git a/library/src/test/java/com/mabn/calendarlibrary/CalendarViewModelTest.kt b/library/src/test/java/com/mabn/calendarlibrary/CalendarViewModelTest.kt new file mode 100644 index 0000000..7c4c2a2 --- /dev/null +++ b/library/src/test/java/com/mabn/calendarlibrary/CalendarViewModelTest.kt @@ -0,0 +1,85 @@ +package com.mabn.calendarlibrary + +import com.mabn.calendarlibrary.core.CalendarIntent +import com.mabn.calendarlibrary.core.Period +import com.mabn.calendarlibrary.core.RelativePosition +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertTrue +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestDispatcher +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.Before +import org.junit.Test +import org.mockito.InjectMocks +import org.mockito.MockitoAnnotations +import java.time.LocalDate + +class CalendarViewModelTest { + + @InjectMocks + private lateinit var calendarViewModel: CalendarViewModel + + @OptIn(ExperimentalCoroutinesApi::class) + private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher() + + + @Before + @OptIn(ExperimentalCoroutinesApi::class) + fun setup() { + MockitoAnnotations.openMocks(this) + Dispatchers.setMain(testDispatcher) + } + + @Test + fun `should expand calendar`() { + calendarViewModel.onIntent(CalendarIntent.ExpandCalendar) + assertTrue(calendarViewModel.calendarExpanded.value) + } + + @Test + fun `should collapse calendar`() { + calendarViewModel.onIntent(CalendarIntent.CollapseCalendar) + assertFalse(calendarViewModel.calendarExpanded.value) + } + + @Test + fun `should select date`() { + val date = LocalDate.now() + calendarViewModel.onIntent(CalendarIntent.SelectDate(date)) + assertEquals(date, calendarViewModel.selectedDate.value) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `should load next week`() { + val beginningOfCurrentWeek = LocalDate.now() + .minusDays(LocalDate.now().dayOfWeek.value.toLong() - 1) + val beginningOfNextWeek = beginningOfCurrentWeek + .plusWeeks(1) + val endOfNextWeek = beginningOfCurrentWeek + .plusWeeks(2) + .minusDays(1) + runTest { + calendarViewModel.onIntent( + CalendarIntent.LoadNextDates( + beginningOfCurrentWeek, + Period.WEEK + ) + ) + advanceUntilIdle() + assertEquals( + beginningOfNextWeek, + calendarViewModel.visibleDates.value[RelativePosition.CURRENT.ordinal].first() + ) + assertEquals( + endOfNextWeek, + calendarViewModel.visibleDates.value[RelativePosition.CURRENT.ordinal].last() + ) + } + } +} \ No newline at end of file diff --git a/library/src/test/java/com/mabn/calendarlibrary/LocalDateExtensionTest.kt b/library/src/test/java/com/mabn/calendarlibrary/LocalDateExtensionTest.kt index 01e8327..bf6247d 100644 --- a/library/src/test/java/com/mabn/calendarlibrary/LocalDateExtensionTest.kt +++ b/library/src/test/java/com/mabn/calendarlibrary/LocalDateExtensionTest.kt @@ -9,30 +9,30 @@ import java.time.DayOfWeek import java.time.LocalDate -internal class LocalDateExtensionTest{ +internal class LocalDateExtensionTest { @Test - fun `get remaining dates in month`(){ + fun `should get remaining dates in month`() { val date = LocalDate.of(2022, 8, 29) val remainingDays = date.getRemainingDatesInMonth() - Assert.assertEquals(3,remainingDays.size) + Assert.assertEquals(3, remainingDays.size) Assert.assertEquals(LocalDate.of(2022, 8, 29), remainingDays[0]) - Assert.assertEquals( LocalDate.of(2022, 8,31),remainingDays[2]) + Assert.assertEquals(LocalDate.of(2022, 8, 31), remainingDays[2]) } @Test - fun `get remaining dates in week`(){ - val date = LocalDate.of(2022,9,30) + fun `should get remaining dates in week`() { + val date = LocalDate.of(2022, 9, 30) val remainingDays = date.getRemainingDatesInWeek() Assert.assertEquals(2, remainingDays.size) Assert.assertEquals(LocalDate.of(2022, 10, 1), remainingDays[0]) - Assert.assertEquals( LocalDate.of(2022, 10,2),remainingDays[1]) + Assert.assertEquals(LocalDate.of(2022, 10, 2), remainingDays[1]) } @Test - fun `get date for the first day of the week (default monday)`() { + fun `should get date for the first day of the week (default monday)`() { val date = LocalDate.of(2022, 9, 21) - Assert.assertEquals(date.dayOfWeek, DayOfWeek.WEDNESDAY) val startWeekDate = date.getWeekStartDate() + Assert.assertEquals(date.dayOfWeek, DayOfWeek.WEDNESDAY) Assert.assertEquals(startWeekDate.dayOfWeek, DayOfWeek.MONDAY) Assert.assertEquals(startWeekDate.dayOfMonth, 19) Assert.assertEquals(startWeekDate.monthValue, 9) @@ -40,10 +40,10 @@ internal class LocalDateExtensionTest{ } @Test - fun `get date for the first day of the week which is sunday`() { + fun `should get date for the first day of the week which is sunday`() { val date = LocalDate.of(2022, 9, 21) - Assert.assertEquals(date.dayOfWeek, DayOfWeek.WEDNESDAY) val startWeekDate = date.getWeekStartDate(DayOfWeek.SUNDAY) + Assert.assertEquals(date.dayOfWeek, DayOfWeek.WEDNESDAY) Assert.assertEquals(startWeekDate.dayOfWeek, DayOfWeek.SUNDAY) Assert.assertEquals(startWeekDate.dayOfMonth, 18) Assert.assertEquals(startWeekDate.monthValue, 9)