diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..8fd47e85 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +name: Build + +on: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 17 + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Build the sample app to verify it works + run: ./gradlew sample:assemble diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..58e56004 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,39 @@ +name: Publish + +on: + push: + branches: + - master + tags: + - v* + +jobs: + deploy: + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 17 + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Build the sample app to verify it works + run: ./gradlew sample:assemble + + - name: Deploy to Sonatype + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + run: ./gradlew publish --no-parallel + env: + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} + ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.GPG_KEY_ID }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 480991b7..d74a2876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,26 @@ - # ChangeLog +## Version 1.7.0 + +* New: Bindings list: + * Google "material" library bindings: + * `corbind-material`: + * `HideBottomViewOnScrollBehavior`: + * `bottomViewScrollStateChanges` + * `SearchBar`: + * `navigationClicks` + * `SearchView`: + * `transitionStateChanges` + * `transitionStateChangeEvents` + * `SideSheetBehavior`: + * `sideSheetSlides` + * `sideSheetStateChanges` +* Update: Kotlin modules dependency to v1.8.0. +* Update: Material components dependency to v1.8.0. +* Update: Minor update of other libraries. + + ## Version 1.6.0 * New: Fragment module @@ -222,7 +241,7 @@ single selection flag. * `Snackbar`: * `shown` * `SwipeDismissBehavior`: - * 'dragStateChanges` + * `dragStateChanges` * Fix: Fixed sources jars generation (#6). diff --git a/LICENSE b/LICENSE index 6ca9be21..d6456956 100755 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -186,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [2019] [Vladimir Raupov] + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index eb0bc72f..bf11e5c4 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,69 @@ [![Corbind](logo.svg)](https://ldralighieri.github.io/Corbind) [![Maven Central](https://img.shields.io/maven-central/v/ru.ldralighieri.corbind/corbind.svg)](https://search.maven.org/search?q=g:ru.ldralighieri.corbind) -[![Kotlin Version](https://img.shields.io/badge/Kotlin-v1.7.20-blue.svg)](https://kotlinlang.org) +[![Kotlin Version](https://img.shields.io/badge/Kotlin-v1.8.0-blue.svg?logo=kotlin)](https://kotlinlang.org) [![Kotlin Coroutines Version](https://img.shields.io/badge/Coroutines-v1.6.4-blue.svg)](https://kotlinlang.org/docs/reference/coroutines-overview.html) [![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a1c9a1b1d1ce4ca7a201ab93492bf6e0)](https://app.codacy.com/gh/LDRAlighieri/Corbind) [![API](https://img.shields.io/badge/API-14%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=14) -[![Google Dev Library](https://img.shields.io/badge/Featured%20in%20devlibrary.withgoogle.com-Corbind-blue)](https://devlibrary.withgoogle.com/products/android/repos/LDRAlighieri-Corbind) +[![Google Dev Library](https://img.shields.io/badge/Google_DevLibrary-Corbind-blue)](https://devlibrary.withgoogle.com/products/android/repos/LDRAlighieri-Corbind) [![Android Weekly](https://androidweekly.net/issues/issue-377/badge)](https://androidweekly.net/issues/issue-377)
-Kotlin Coroutines binding APIs for Android UI widgets from the platform and support libraries. **Supports Flow, ReceiveChannel and Actor**. +⚡ Kotlin Coroutines binding APIs for Android UI widgets from the platform and support libraries. **Supports Flow, ReceiveChannel and Actor**. ## Description -This library is for Android applications only. Help you to transform Android UI events into cold [Flow][flow], hot [ReceiveChannel][channel] or just perform an action through an [Actor][actor]. +This library is for Android applications only. Help you to transform Android UI events into cold [Flow][flow], hot [ReceiveChannel][channel] or just perform an action through an [Actor][actor]. Please consider giving this repository a star ⭐ if you like the project. -## Download +## Using in your projects Platform bindings: -```groovy -implementation 'ru.ldralighieri.corbind:corbind:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind:1.7.0") +} ``` AndroidX library bindings: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-activity:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-appcompat:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-core:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-drawerlayout:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-fragment:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-leanback:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-lifecycle:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-navigation:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-recyclerview:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-slidingpanelayout:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-swiperefreshlayout:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-viewpager:1.6.0' -implementation 'ru.ldralighieri.corbind:corbind-viewpager2:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-activity:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-appcompat:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-core:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-drawerlayout:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-fragment:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-leanback:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-lifecycle:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-navigation:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-recyclerview:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-slidingpanelayout:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-swiperefreshlayout:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-viewpager:1.7.0") + implementation("ru.ldralighieri.corbind:corbind-viewpager2:1.7.0") +} ``` Google 'material' library bindings: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-material:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-material:1.7.0") +} ``` Snapshot build: -```groovy +```kotlin repositories { - maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } + maven("https://oss.sonatype.org/content/repositories/snapshots/") } dependencies { - implementation 'ru.ldralighieri.corbind:{module}:1.6.1-SNAPSHOT' + implementation("ru.ldralighieri.corbind:{module}:1.8.0-SNAPSHOT") } ``` @@ -65,21 +71,21 @@ dependencies { ## List of extensions You can find a list of extensions in the description of each module: -[corbind] -[corbind-activity] -[corbind-appcompat] -[corbind-core] -[corbind-drawerlayout] -[corbind-fragment] -[corbind-leanback] -[corbind-lifecycle] -[corbind-material] -[corbind-navigation] -[corbind-recyclerview] -[corbind-slidingpanelayout] -[corbind-swiperefreshlayout] -[corbind-viewpager] -[corbind-viewpager2] +* [corbind] +* [corbind-activity] +* [corbind-appcompat] +* [corbind-core] +* [corbind-drawerlayout] +* [corbind-fragment] +* [corbind-leanback] +* [corbind-lifecycle] +* [corbind-material] +* [corbind-navigation] +* [corbind-recyclerview] +* [corbind-slidingpanelayout] +* [corbind-swiperefreshlayout] +* [corbind-viewpager] +* [corbind-viewpager2] ## How to use it? @@ -146,7 +152,7 @@ If I forgot something or you have any ideas what can be added or corrected, plea ## License ``` -Copyright 2019-2022 Vladimir Raupov +Copyright 2019-2023 Vladimir Raupov Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 827cebb7..f35e67ca 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -21,8 +21,8 @@ plugins { group = "ru.ldralighieri.corbind.buildlogic" java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } dependencies { diff --git a/build-logic/convention/src/main/kotlin/DokkaConventionPlugin.kt b/build-logic/convention/src/main/kotlin/DokkaConventionPlugin.kt index 3cf2812c..286d602a 100644 --- a/build-logic/convention/src/main/kotlin/DokkaConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/DokkaConventionPlugin.kt @@ -29,7 +29,7 @@ class DokkaConventionPlugin : Plugin { tasks.withType().configureEach { dokkaSourceSets.named("main") { - jdkVersion.set(JavaVersion.VERSION_11.majorVersion.toInt()) + jdkVersion.set(JavaVersion.VERSION_17.majorVersion.toInt()) skipDeprecated.set(false) reportUndocumented.set(false) diff --git a/build-logic/convention/src/main/kotlin/SpotlessConventionPlugin.kt b/build-logic/convention/src/main/kotlin/SpotlessConventionPlugin.kt index eb9e368a..6aa7ab38 100644 --- a/build-logic/convention/src/main/kotlin/SpotlessConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/SpotlessConventionPlugin.kt @@ -32,7 +32,10 @@ class SpotlessConventionPlugin : Plugin { kotlin { target("**/*.kt") targetExclude("**/build/**/*.kt") + ktlint(libs.findVersion("ktlint").get().toString()) + .editorConfigOverride(mapOf("disabled_rules" to "filename")) + licenseHeaderFile(rootProject.file("spotless/copyright.kt")) } } diff --git a/build-logic/convention/src/main/kotlin/ru/ldralighieri/corbind/Project+configureKotlinAndroid.kt b/build-logic/convention/src/main/kotlin/ru/ldralighieri/corbind/Project+configureKotlinAndroid.kt index b5c61ab3..233aad71 100644 --- a/build-logic/convention/src/main/kotlin/ru/ldralighieri/corbind/Project+configureKotlinAndroid.kt +++ b/build-logic/convention/src/main/kotlin/ru/ldralighieri/corbind/Project+configureKotlinAndroid.kt @@ -39,12 +39,12 @@ internal fun Project.configureKotlinAndroid( } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget = JavaVersion.VERSION_17.toString() // Treat all Kotlin warnings as errors allWarningsAsErrors = true diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties new file mode 100644 index 00000000..510f596d --- /dev/null +++ b/build-logic/gradle.properties @@ -0,0 +1,4 @@ +# Gradle +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configureondemand=true diff --git a/build-logic/gradle/wrapper/gradle-wrapper.jar b/build-logic/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f6b961fd Binary files /dev/null and b/build-logic/gradle/wrapper/gradle-wrapper.jar differ diff --git a/build-logic/gradle/wrapper/gradle-wrapper.properties b/build-logic/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..ae03e316 --- /dev/null +++ b/build-logic/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Dec 04 14:00:15 VLAT 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index 641ddefd..5140168e 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -30,4 +30,5 @@ dependencyResolutionManagement { } } +rootProject.name = "build-logic" include(":convention") diff --git a/build.gradle.kts b/build.gradle.kts index 8f8794b7..1b2437e0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,13 +17,12 @@ import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask import io.gitlab.arturbosch.detekt.Detekt -buildscript { - dependencies { - classpath(libs.bundles.plugins) - } -} - plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.android.library) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.spotless) apply false + alias(libs.plugins.maven.publish) apply false alias(libs.plugins.detekt) alias(libs.plugins.gver) } @@ -55,7 +54,7 @@ detekt { } tasks.withType().configureEach { - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget = JavaVersion.VERSION_17.toString() reports { html.required.set(true) xml.required.set(false) @@ -66,7 +65,7 @@ tasks.withType().configureEach { // Dependency updates fun isNonStable(version: String): Boolean { - val stableKeyword = listOf("RELEASE", "FINAL").any { version.toUpperCase().contains(it) } + val stableKeyword = listOf("RELEASE", "FINAL").any { version.contains(it) } val regex = "^[0-9,.v-]+(-r)?$".toRegex() val isStable = stableKeyword || regex.matches(version) return isStable.not() diff --git a/corbind-activity/README.md b/corbind-activity/README.md index 13ff9f28..a0829eed 100644 --- a/corbind-activity/README.md +++ b/corbind-activity/README.md @@ -3,8 +3,10 @@ To add androidx activity bindings, import `corbind-activity` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-activity:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-activity:1.7.0") +} ``` ## List of extensions @@ -17,7 +19,7 @@ Component | Extension | Description ## Simple examples ```kotlin -onBackPressedDispatcher.backPresses(lifecycleOwner = this) +onBackPressedDispatcher.backPresses(lifecycleOwner = this) // Flow .onEach { /* handle onBackPressed event */ } .flowWithLifecycle(lifecycle) .launchIn(lifecycleScope) // lifecycle-runtime-ktx diff --git a/corbind-appcompat/README.md b/corbind-appcompat/README.md index b0ac9c6d..7a3e03d9 100644 --- a/corbind-appcompat/README.md +++ b/corbind-appcompat/README.md @@ -3,8 +3,10 @@ To add androidx appcompat bindings, import `corbind-appcompat` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-appcompat:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-appcompat:1.7.0") +} ``` ## List of extensions diff --git a/corbind-core/README.md b/corbind-core/README.md index adf4ec6a..edf80145 100644 --- a/corbind-core/README.md +++ b/corbind-core/README.md @@ -3,8 +3,10 @@ To add androidx core bindings, import `corbind-core` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-core:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-core:1.7.0") +} ``` ## List of extensions diff --git a/corbind-drawerlayout/README.md b/corbind-drawerlayout/README.md index 349a25e6..540eeba2 100644 --- a/corbind-drawerlayout/README.md +++ b/corbind-drawerlayout/README.md @@ -3,8 +3,10 @@ To add androidx drawerlayout bindings, import `corbind-drawerlayout` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-drawerlayout:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-drawerlayout:1.7.0") +} ``` ## List of extensions diff --git a/corbind-fragment/README.md b/corbind-fragment/README.md index 0f386da3..68a9068b 100644 --- a/corbind-fragment/README.md +++ b/corbind-fragment/README.md @@ -3,8 +3,10 @@ To add androidx fragment bindings, import `corbind-fragment` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-fragment:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-fragment:1.7.0") +} ``` ## List of extensions @@ -21,7 +23,7 @@ lifecycleScope.launchWhenStarted { parentFragmentManager.resultEvents( requestKey = FRAGMENT_REQUEST_KEY, lifecycleOwner = this@CurrentFragment - ) + ) // Flow .onEach { event -> /* handle result event */ } .launchIn(this@launchWhenStarted) // lifecycle-runtime-ktx } diff --git a/corbind-leanback/README.md b/corbind-leanback/README.md index b82a5723..8709c732 100644 --- a/corbind-leanback/README.md +++ b/corbind-leanback/README.md @@ -3,8 +3,10 @@ To add androidx leanback bindings, import `corbind-leanback` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-leanback:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-leanback:1.7.0") +} ``` ## List of extensions diff --git a/corbind-lifecycle/README.md b/corbind-lifecycle/README.md index dc226fde..a8015184 100644 --- a/corbind-lifecycle/README.md +++ b/corbind-lifecycle/README.md @@ -3,8 +3,10 @@ To add androidx lifecycle bindings, import `corbind-lifecycle` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-lifecycle:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-lifecycle:1.7.0") +} ``` ## List of extensions @@ -17,7 +19,7 @@ Component | Extension | Description ## Simple examples ```kotlin -lifecycle.events() +lifecycle.events() // Flow .filter { it == Lifecycle.Event.ON_RESUME } .onEach { /* handle lifecycle onResume event */ } .flowWithLifecycle(lifecycle) diff --git a/corbind-material/README.md b/corbind-material/README.md index 354dda15..e4361840 100644 --- a/corbind-material/README.md +++ b/corbind-material/README.md @@ -3,8 +3,10 @@ To add material bindings, import `corbind-material` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-material:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-material:1.7.0") +} ``` ## List of extensions @@ -16,6 +18,7 @@ Component | Extension | Description   | `stateChanges` | Called when the bottom sheet changes its state. **Chip** | `closeIconClicks` | Called when the chip’s close icon is clicked. **ChipGroup** | `checkedChanges` | Called when the checked chips are changed. +**View**
(HideBottomViewOnScrollBehavior) | `bottomViewScrollStateChanges` | Called when the bottom view changes its scrolled state. **MaterialButton** | `checkedChanges` | Called when the checked state of a MaterialButton has changed. **MaterialButtonToggleGroup** | `buttonCheckedChangeEvents` | Called when a `MaterialButton` in this group is checked or unchecked (only *not* in single selection mode).   | `buttonCheckedChanges` | Called when a `MaterialButton` in this group is checked (only in single selection mode). @@ -34,6 +37,11 @@ Component | Extension | Description **RangeSlider** | `touches` | Called when a range slider's touch event is being started/stopped.   | `valuesChanges` | Called a range slider's value is changed. This is called for all existing values to check all the current values use.   | `valuesChangeEvents` | A more advanced version of the `valuesChanges`. +**SearchBar** | `navigationClicks` | Called whenever the user clicks the navigation button at the start of the searchbar. +**SearchView** | `transitionStateChanges` | Called when the given `SearchView's` transition state has changed. +  | `transitionStateChangeEvents` | A more advanced version of the `transitionStateChanges`. +**View**
(SideSheetBehavior) | `sideSheetSlides` | Called when the side sheet is being dragged. +  | `sideSheetStateChanges` | Called when the side sheet changes its state. **Slider** | `touches` | Called when a slider's touch event is being started/stopped.   | `valueChanges` | Called a slider's value is changed.   | `valueChangeEvents` | A more advanced version of the `valueChanges`. diff --git a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/BottomSheetBehaviorSlides.kt b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/BottomSheetBehaviorSlides.kt index 7ad7465a..b6ad9fe6 100644 --- a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/BottomSheetBehaviorSlides.kt +++ b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/BottomSheetBehaviorSlides.kt @@ -18,7 +18,6 @@ package ru.ldralighieri.corbind.material import android.view.View import androidx.annotation.CheckResult -import androidx.coordinatorlayout.widget.CoordinatorLayout import com.google.android.material.bottomsheet.BottomSheetBehavior import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -48,7 +47,7 @@ fun View.slides( for (offset in channel) action(offset) } - val behavior = getBehavior(this@slides) + val behavior = getBottomSheetBehavior() val callback = callback(scope, events::trySend) behavior.addBottomSheetCallback(callback) events.invokeOnClose { behavior.removeBottomSheetCallback(callback) } @@ -88,7 +87,7 @@ fun View.slides( scope: CoroutineScope, capacity: Int = Channel.RENDEZVOUS ): ReceiveChannel = corbindReceiveChannel(capacity) { - val behavior = getBehavior(this@slides) + val behavior = getBottomSheetBehavior() val callback = callback(scope, ::trySend) behavior.addBottomSheetCallback(callback) invokeOnClose { behavior.removeBottomSheetCallback(callback) } @@ -106,21 +105,14 @@ fun View.slides( * .launchIn(lifecycleScope) // lifecycle-runtime-ktx * ``` */ +@CheckResult fun View.slides(): Flow = channelFlow { - val behavior = getBehavior(this@slides) + val behavior = getBottomSheetBehavior() val callback = callback(this, ::trySend) behavior.addBottomSheetCallback(callback) awaitClose { behavior.removeBottomSheetCallback(callback) } } -@CheckResult -private fun getBehavior(view: View): BottomSheetBehavior<*> { - val params = view.layoutParams as? CoordinatorLayout.LayoutParams - ?: throw IllegalArgumentException("The view is not in a Coordinator Layout.") - return params.behavior as BottomSheetBehavior<*>? - ?: throw IllegalStateException("There's no behavior set on this view.") -} - @CheckResult private fun callback( scope: CoroutineScope, diff --git a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/BottomSheetBehaviorStateChanges.kt b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/BottomSheetBehaviorStateChanges.kt index 101484da..3d103195 100644 --- a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/BottomSheetBehaviorStateChanges.kt +++ b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/BottomSheetBehaviorStateChanges.kt @@ -49,7 +49,7 @@ fun View.stateChanges( for (state in channel) action(state) } - val behavior = getBehavior(this@stateChanges) + val behavior = getBottomSheetBehavior() events.trySend(behavior.state) val callback = callback(scope, events::trySend) behavior.addBottomSheetCallback(callback) @@ -92,7 +92,7 @@ fun View.stateChanges( scope: CoroutineScope, capacity: Int = Channel.RENDEZVOUS ): ReceiveChannel = corbindReceiveChannel(capacity) { - val behavior = getBehavior(this@stateChanges) + val behavior = getBottomSheetBehavior() trySend(behavior.state) val callback = callback(scope, ::trySend) behavior.addBottomSheetCallback(callback) @@ -121,19 +121,19 @@ fun View.stateChanges( * .launchIn(lifecycleScope) // lifecycle-runtime-ktx * ``` */ +@CheckResult fun View.stateChanges(): InitialValueFlow = channelFlow { - val behavior = getBehavior(this@stateChanges) + val behavior = getBottomSheetBehavior() val callback = callback(this, ::trySend) behavior.addBottomSheetCallback(callback) awaitClose { behavior.removeBottomSheetCallback(callback) } -}.asInitialValueFlow(getBehavior(this@stateChanges).state) +}.asInitialValueFlow(getBottomSheetBehavior().state) -@CheckResult -private fun getBehavior(view: View): BottomSheetBehavior<*> { - val params = view.layoutParams as? CoordinatorLayout.LayoutParams +internal fun View.getBottomSheetBehavior(): BottomSheetBehavior<*> { + val params = layoutParams as? CoordinatorLayout.LayoutParams ?: throw IllegalArgumentException("The view is not in a Coordinator Layout.") return params.behavior as BottomSheetBehavior<*>? - ?: throw IllegalStateException("There's no behavior set on this view.") + ?: throw IllegalStateException("There's no BottomSheetBehavior set on this view.") } @CheckResult @@ -142,9 +142,9 @@ private fun callback( emitter: (Int) -> Unit ) = object : BottomSheetBehavior.BottomSheetCallback() { - override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit - override fun onStateChanged(bottomSheet: View, newState: Int) { if (scope.isActive) { emitter(newState) } } + + override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit } diff --git a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/HideBottomViewOnScrollBehaviorScrollStateChanges.kt b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/HideBottomViewOnScrollBehaviorScrollStateChanges.kt new file mode 100644 index 00000000..6babd45a --- /dev/null +++ b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/HideBottomViewOnScrollBehaviorScrollStateChanges.kt @@ -0,0 +1,137 @@ +/* + * Copyright 2022 Vladimir Raupov + * + * 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. + */ + +package ru.ldralighieri.corbind.material + +import android.view.View +import androidx.annotation.CheckResult +import androidx.coordinatorlayout.widget.CoordinatorLayout +import com.google.android.material.behavior.HideBottomViewOnScrollBehavior +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.actor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.isActive +import ru.ldralighieri.corbind.internal.corbindReceiveChannel + +/** + * Perform an action on the bottom view scroll state change events from [View] on + * [HideBottomViewOnScrollBehavior]. + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +fun View.bottomViewScrollStateChanges( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS, + action: suspend (Int) -> Unit +) { + val events = scope.actor(Dispatchers.Main.immediate, capacity) { + for (state in channel) action(state) + } + + val behavior = getBehavior() + val listener = listener(scope, events::trySend) + behavior.addOnScrollStateChangedListener(listener) + events.invokeOnClose { behavior.removeOnScrollStateChangedListener(listener) } +} + +/** + * Perform an action on the bottom view scroll state change events from [View] on + * [HideBottomViewOnScrollBehavior], inside new [CoroutineScope]. + * + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +suspend fun View.bottomViewScrollStateChanges( + capacity: Int = Channel.RENDEZVOUS, + action: suspend (Int) -> Unit +) = coroutineScope { + bottomViewScrollStateChanges(this, capacity, action) +} + +/** + * Create a channel which emits the bottom view scroll state change events from [View] on + * [HideBottomViewOnScrollBehavior]. + * + * *Note:* A value will be emitted immediately. + * + * Examples: + * + * ``` + * launch { + * bottomView.bottomViewScrollStateChanges(scope) + * .consumeEach { /* handle bottom view scroll state change */ } + * } + * ``` + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + */ +@CheckResult +fun View.bottomViewScrollStateChanges( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS +): ReceiveChannel = corbindReceiveChannel(capacity) { + val behavior = getBehavior() + val listener = listener(scope, ::trySend) + behavior.addOnScrollStateChangedListener(listener) + invokeOnClose { behavior.removeOnScrollStateChangedListener(listener) } +} + +/** + * Create a flow which emits the bottom view scroll state change events from [View] on + * [HideBottomViewOnScrollBehavior]. + * + * *Note:* A value will be emitted immediately. + * + * Examples: + * + * ``` + * bottomView.bottomViewScrollStateChanges() + * .onEach { /* handle bottom view scroll state change */ } + * .flowWithLifecycle(lifecycle) + * .launchIn(lifecycleScope) // lifecycle-runtime-ktx + * ``` + */ +@CheckResult +fun View.bottomViewScrollStateChanges(): Flow = channelFlow { + val behavior = getBehavior() + val listener = listener(this, ::trySend) + behavior.addOnScrollStateChangedListener(listener) + awaitClose { behavior.removeOnScrollStateChangedListener(listener) } +} + +private fun View.getBehavior(): HideBottomViewOnScrollBehavior<*> { + val params = layoutParams as? CoordinatorLayout.LayoutParams + ?: throw IllegalArgumentException("The view is not in a Coordinator Layout.") + return params.behavior as HideBottomViewOnScrollBehavior<*>? + ?: throw IllegalStateException("There's no HideBottomViewOnScrollBehavior set on this view.") +} + +@CheckResult +private fun listener( + scope: CoroutineScope, + emitter: (Int) -> Unit +) = HideBottomViewOnScrollBehavior.OnScrollStateChangedListener { _, newState -> + if (scope.isActive) { emitter(newState) } +} diff --git a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SearchBarNavigationClicks.kt b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SearchBarNavigationClicks.kt new file mode 100644 index 00000000..05b15148 --- /dev/null +++ b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SearchBarNavigationClicks.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2023 Vladimir Raupov + * + * 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. + */ + +package ru.ldralighieri.corbind.material + +import android.os.Build +import android.view.View +import androidx.annotation.CheckResult +import androidx.annotation.RequiresApi +import com.google.android.material.search.SearchBar +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.actor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.isActive +import ru.ldralighieri.corbind.internal.corbindReceiveChannel + +/** + * Perform an action on [SearchBar] navigation click events. + * + * *Warning:* The created actor uses [SearchBar.setNavigationOnClickListener]. Only one actor can + * be used at a time. + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +fun SearchBar.navigationClicks( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS, + action: suspend () -> Unit +) { + val events = scope.actor(Dispatchers.Main.immediate, capacity) { + for (ignored in channel) action() + } + + setNavigationOnClickListener(listener(scope, events::trySend)) + events.invokeOnClose { setNavigationOnClickListener(null) } +} + +/** + * Perform an action on [SearchBar] navigation click events, inside new [CoroutineScope]. + * + * *Warning:* The created actor uses [SearchBar.setNavigationOnClickListener]. Only one actor can + * be used at a time. + * + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +suspend fun SearchBar.navigationClicks( + capacity: Int = Channel.RENDEZVOUS, + action: suspend () -> Unit +) = coroutineScope { + navigationClicks(this, capacity, action) +} + +/** + * Create a channel which emits on [SearchBar] navigation click events. + * + * *Warning:* The created channel uses [SearchBar.setNavigationOnClickListener]. Only one channel + * can be used at a time. + * + * Example: + * + * ``` + * launch { + * searchbar.navigationClicks(scope) + * .consumeEach { /* handle navigation click */ } + * } + * ``` + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + */ +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +@CheckResult +fun SearchBar.navigationClicks( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS +): ReceiveChannel = corbindReceiveChannel(capacity) { + setNavigationOnClickListener(listener(scope, ::trySend)) + invokeOnClose { setNavigationOnClickListener(null) } +} + +/** + * Create a flow which emits on [SearchBar] navigation click events. + * + * *Warning:* The created flow uses [SearchBar.setNavigationOnClickListener]. Only one flow can be + * used at a time. + * + * Example: + * + * ``` + * searchbar.navigationClicks() + * .onEach { /* handle navigation click */ } + * .flowWithLifecycle(lifecycle) + * .launchIn(lifecycleScope) // lifecycle-runtime-ktx + * ``` + */ +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +@CheckResult +fun SearchBar.navigationClicks(): Flow = channelFlow { + setNavigationOnClickListener(listener(this, ::trySend)) + awaitClose { setNavigationOnClickListener(null) } +} + +@CheckResult +private fun listener( + scope: CoroutineScope, + emitter: (Unit) -> Unit +) = View.OnClickListener { + if (scope.isActive) { emitter(Unit) } +} diff --git a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SearchViewTransitionStateChangeEvents.kt b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SearchViewTransitionStateChangeEvents.kt new file mode 100644 index 00000000..43db0012 --- /dev/null +++ b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SearchViewTransitionStateChangeEvents.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2022 Vladimir Raupov + * + * 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. + */ + +package ru.ldralighieri.corbind.material + +import androidx.annotation.CheckResult +import com.google.android.material.search.SearchView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.actor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.isActive +import ru.ldralighieri.corbind.internal.corbindReceiveChannel + +data class SearchViewTransitionStateChangeEvent( + val view: SearchView, + val previousState: SearchView.TransitionState, + val newState: SearchView.TransitionState +) + +/** + * Perform an action on the + * [transition state change event][SearchViewTransitionStateChangeEvent] on [SearchView]. + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +fun SearchView.transitionStateChangeEvents( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS, + action: suspend (SearchViewTransitionStateChangeEvent) -> Unit +) { + val events = scope.actor(Dispatchers.Main.immediate, capacity) { + for (event in channel) action(event) + } + + val listener = listener(scope, events::trySend) + addTransitionListener(listener) + events.invokeOnClose { removeTransitionListener(listener) } +} + +/** + * Perform an action on the + * [transition state change event][SearchViewTransitionStateChangeEvent] on [SearchView], inside new + * [CoroutineScope]. + * + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +suspend fun SearchView.transitionStateChangeEvents( + capacity: Int = Channel.RENDEZVOUS, + action: suspend (SearchViewTransitionStateChangeEvent) -> Unit +) = coroutineScope { + transitionStateChangeEvents(this, capacity, action) +} + +/** + * Create a channel which emits the + * [transition state change event][SearchViewTransitionStateChangeEvent] on [SearchView]. + * + * Example: + * + * ``` + * launch { + * searchView.transitionStateChangeEvents(scope) + * .consumeEach { /* handle transition state change even */ } + * } + * ``` + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + */ +@CheckResult +fun SearchView.transitionStateChangeEvents( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS +): ReceiveChannel = corbindReceiveChannel(capacity) { + val listener = listener(scope, ::trySend) + addTransitionListener(listener) + invokeOnClose { removeTransitionListener(listener) } +} + +/** + * Create a flow which emits the + * [transition state change event][SearchViewTransitionStateChangeEvent] on [SearchView]. + * + * Example: + * + * ``` + * searchView.transitionStateChangeEvents() + * .onEach { /* handle transition state change event */ } + * .flowWithLifecycle(lifecycle) + * .launchIn(lifecycleScope) // lifecycle-runtime-ktx + * ``` + */ +@CheckResult +fun SearchView.transitionStateChangeEvents(): Flow = + channelFlow { + val listener = listener(this, ::trySend) + addTransitionListener(listener) + awaitClose { removeTransitionListener(listener) } + } + +@CheckResult +private fun listener( + scope: CoroutineScope, + emitter: (SearchViewTransitionStateChangeEvent) -> Unit +) = SearchView.TransitionListener { searchView, previousState, newState -> + if (scope.isActive) { + emitter(SearchViewTransitionStateChangeEvent(searchView, previousState, newState)) + } +} diff --git a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SearchViewTransitionStateChanges.kt b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SearchViewTransitionStateChanges.kt new file mode 100644 index 00000000..c1ac958c --- /dev/null +++ b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SearchViewTransitionStateChanges.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2022 Vladimir Raupov + * + * 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. + */ + +package ru.ldralighieri.corbind.material + +import androidx.annotation.CheckResult +import com.google.android.material.search.SearchView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.actor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.isActive +import ru.ldralighieri.corbind.internal.corbindReceiveChannel + +/** + * Perform an action on the transition state change events on [SearchView]. + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +fun SearchView.transitionStateChanges( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS, + action: suspend (SearchView.TransitionState) -> Unit +) { + val events = scope.actor(Dispatchers.Main.immediate, capacity) { + for (state in channel) action(state) + } + + val listener = listener(scope, events::trySend) + addTransitionListener(listener) + events.invokeOnClose { removeTransitionListener(listener) } +} + +/** + * Perform an action on the transition state change events on [SearchView], inside new + * [CoroutineScope]. + * + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +suspend fun SearchView.transitionStateChanges( + capacity: Int = Channel.RENDEZVOUS, + action: suspend (SearchView.TransitionState) -> Unit +) = coroutineScope { + transitionStateChanges(this, capacity, action) +} + +/** + * Create a channel which emits the transition state change events on [SearchView]. + * + * Example: + * + * ``` + * launch { + * searchView.transitionStateChanges(scope) + * .consumeEach { /* handle transition state change even */ } + * } + * ``` + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + */ +@CheckResult +fun SearchView.transitionStateChanges( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS +): ReceiveChannel = corbindReceiveChannel(capacity) { + val listener = listener(scope, ::trySend) + addTransitionListener(listener) + invokeOnClose { removeTransitionListener(listener) } +} + +/** + * Create a flow which emits the transition state change events on [SearchView]. + * + * Example: + * + * ``` + * searchView.transitionStateChanges() + * .onEach { /* handle transition state change event */ } + * .flowWithLifecycle(lifecycle) + * .launchIn(lifecycleScope) // lifecycle-runtime-ktx + * ``` + */ +@CheckResult +fun SearchView.transitionStateChanges(): Flow = channelFlow { + val listener = listener(this, ::trySend) + addTransitionListener(listener) + awaitClose { removeTransitionListener(listener) } +} + +@CheckResult +private fun listener( + scope: CoroutineScope, + emitter: (SearchView.TransitionState) -> Unit +) = SearchView.TransitionListener { _, _, newState -> + if (scope.isActive) { emitter(newState) } +} diff --git a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SideSheetBehaviorSlides.kt b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SideSheetBehaviorSlides.kt new file mode 100644 index 00000000..8fc5b9b4 --- /dev/null +++ b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SideSheetBehaviorSlides.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2022 Vladimir Raupov + * + * 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. + */ + +package ru.ldralighieri.corbind.material + +import android.view.View +import androidx.annotation.CheckResult +import com.google.android.material.sidesheet.SideSheetBehavior +import com.google.android.material.sidesheet.SideSheetCallback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.actor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.isActive +import ru.ldralighieri.corbind.internal.corbindReceiveChannel + +/** + * Perform an action on the slide offset events from [View] on [SideSheetBehavior]. + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +fun View.sideSheetSlides( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS, + action: suspend (Float) -> Unit +) { + val events = scope.actor(Dispatchers.Main.immediate, capacity) { + for (offset in channel) action(offset) + } + + val behavior = getSideSheetBehavior() + val callback = callback(scope, events::trySend) + behavior.addCallback(callback) + events.invokeOnClose { behavior.removeCallback(callback) } +} + +/** + * Perform an action on the slide offset events from [View] on [SideSheetBehavior], inside new + * [CoroutineScope]. + * + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +suspend fun View.sideSheetSlides( + capacity: Int = Channel.RENDEZVOUS, + action: suspend (Float) -> Unit +) = coroutineScope { + sideSheetSlides(this, capacity, action) +} + +/** + * Create a channel which emits the slide offset events from [View] on [SideSheetBehavior]. + * + * Example: + * + * ``` + * launch { + * sideSheetBehavior.sideSheetSlides(scope) + * .consumeEach { /* handle slide offset */ } + * } + * ``` + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + */ +@CheckResult +fun View.sideSheetSlides( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS +): ReceiveChannel = corbindReceiveChannel(capacity) { + val behavior = getSideSheetBehavior() + val callback = callback(scope, ::trySend) + behavior.addCallback(callback) + invokeOnClose { behavior.removeCallback(callback) } +} + +/** + * Create a flow which emits the slide offset events from [View] on [SideSheetBehavior]. + * + * Example: + * + * ``` + * sideSheetBehavior.sideSheetSlides() + * .onEach { /* handle slide offset */ } + * .flowWithLifecycle(lifecycle) + * .launchIn(lifecycleScope) // lifecycle-runtime-ktx + * ``` + */ +@CheckResult +fun View.sideSheetSlides(): Flow = channelFlow { + val behavior = getSideSheetBehavior() + val callback = callback(this, ::trySend) + behavior.addCallback(callback) + awaitClose { behavior.removeCallback(callback) } +} + +@CheckResult +private fun callback( + scope: CoroutineScope, + emitter: (Float) -> Unit +) = object : SideSheetCallback() { + + override fun onSlide(sheet: View, slideOffset: Float) { + if (scope.isActive) { emitter(slideOffset) } + } + + override fun onStateChanged(sheet: View, newState: Int) = Unit +} diff --git a/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SideSheetBehaviorStateChanges.kt b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SideSheetBehaviorStateChanges.kt new file mode 100644 index 00000000..2fd18084 --- /dev/null +++ b/corbind-material/src/main/kotlin/ru/ldralighieri/corbind/material/SideSheetBehaviorStateChanges.kt @@ -0,0 +1,151 @@ +/* + * Copyright 2022 Vladimir Raupov + * + * 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. + */ + +package ru.ldralighieri.corbind.material + +import android.view.View +import androidx.annotation.CheckResult +import androidx.coordinatorlayout.widget.CoordinatorLayout +import com.google.android.material.sidesheet.SideSheetBehavior +import com.google.android.material.sidesheet.SideSheetCallback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.actor +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.isActive +import ru.ldralighieri.corbind.internal.InitialValueFlow +import ru.ldralighieri.corbind.internal.asInitialValueFlow +import ru.ldralighieri.corbind.internal.corbindReceiveChannel + +/** + * Perform an action on the state change events from [View] on [SideSheetBehavior]. + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +fun View.sideSheetStateChanges( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS, + action: suspend (Int) -> Unit +) { + val events = scope.actor(Dispatchers.Main.immediate, capacity) { + for (state in channel) action(state) + } + + val behavior = getSideSheetBehavior() + events.trySend(behavior.state) + val callback = callback(scope, events::trySend) + behavior.addCallback(callback) + events.invokeOnClose { behavior.removeCallback(callback) } +} + +/** + * Perform an action on the state change events from [View] on [SideSheetBehavior], inside new + * [CoroutineScope]. + * + * @param capacity Capacity of the channel's buffer (no buffer by default) + * @param action An action to perform + */ +suspend fun View.sideSheetStateChanges( + capacity: Int = Channel.RENDEZVOUS, + action: suspend (Int) -> Unit +) = coroutineScope { + sideSheetStateChanges(this, capacity, action) +} + +/** + * Create a channel which emits the state change events from [View] on [SideSheetBehavior]. + * + * *Note:* A value will be emitted immediately. + * + * Example: + * + * ``` + * launch { + * sideSheetBehavior.sideSheetStateChanges(scope) + * .consumeEach { /* handle state change */ } + * } + * ``` + * + * @param scope Root coroutine scope + * @param capacity Capacity of the channel's buffer (no buffer by default) + */ +@CheckResult +fun View.sideSheetStateChanges( + scope: CoroutineScope, + capacity: Int = Channel.RENDEZVOUS +): ReceiveChannel = corbindReceiveChannel(capacity) { + val behavior = getSideSheetBehavior() + trySend(behavior.state) + val callback = callback(scope, ::trySend) + behavior.addCallback(callback) + invokeOnClose { behavior.removeCallback(callback) } +} + +/** + * Create a flow which emits the state change events from [View] on [SideSheetBehavior]. + * + * *Note:* A value will be emitted immediately. + * + * Examples: + * + * ``` + * // handle initial value + * sideSheetBehavior.sideSheetStateChanges() + * .onEach { /* handle state change */ } + * .flowWithLifecycle(lifecycle) + * .launchIn(lifecycleScope) // lifecycle-runtime-ktx + * + * // drop initial value + * sideSheetBehavior.sideSheetStateChanges() + * .dropInitialValue() + * .onEach { /* handle state change */ } + * .flowWithLifecycle(lifecycle) + * .launchIn(lifecycleScope) // lifecycle-runtime-ktx + * ``` + */ +@CheckResult +fun View.sideSheetStateChanges(): InitialValueFlow = channelFlow { + val behavior = getSideSheetBehavior() + val callback = callback(this, ::trySend) + behavior.addCallback(callback) + awaitClose { behavior.removeCallback(callback) } +}.asInitialValueFlow(getSideSheetBehavior().state) + +internal fun View.getSideSheetBehavior(): SideSheetBehavior<*> { + val params = layoutParams as? CoordinatorLayout.LayoutParams + ?: throw IllegalArgumentException("The view is not in a Coordinator Layout.") + return params.behavior as SideSheetBehavior<*>? + ?: throw IllegalStateException("There's no SideSheetBehavior set on this view.") +} + +@CheckResult +private fun callback( + scope: CoroutineScope, + emitter: (Int) -> Unit +) = object : SideSheetCallback() { + + override fun onStateChanged(sheet: View, newState: Int) { + if (scope.isActive) { emitter(newState) } + } + + override fun onSlide(sheet: View, slideOffset: Float) = Unit +} diff --git a/corbind-navigation/README.md b/corbind-navigation/README.md index 667cb151..44b81d33 100644 --- a/corbind-navigation/README.md +++ b/corbind-navigation/README.md @@ -3,8 +3,10 @@ To add androidx navigation bindings, import `corbind-navigation` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-navigation:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-navigation:1.7.0") +} ``` ## List of extensions diff --git a/corbind-recyclerview/README.md b/corbind-recyclerview/README.md index f591e400..2e06b89f 100644 --- a/corbind-recyclerview/README.md +++ b/corbind-recyclerview/README.md @@ -3,8 +3,10 @@ To add androidx recyclerview bindings, import `corbind-recyclerview` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-recyclerview:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-recyclerview:1.7.0") +} ``` ## List of extensions diff --git a/corbind-slidingpanelayout/README.md b/corbind-slidingpanelayout/README.md index 3834fee5..3e21e000 100644 --- a/corbind-slidingpanelayout/README.md +++ b/corbind-slidingpanelayout/README.md @@ -3,8 +3,10 @@ To add androidx slidingpanelayout bindings, import `corbind-slidingpanelayout` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-slidingpanelayout:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-slidingpanelayout:1.7.0") +} ``` ## List of extensions diff --git a/corbind-swiperefreshlayout/README.md b/corbind-swiperefreshlayout/README.md index e3e51a87..9904d7b8 100644 --- a/corbind-swiperefreshlayout/README.md +++ b/corbind-swiperefreshlayout/README.md @@ -3,8 +3,10 @@ To add androidx swiperefreshlayout bindings, import `corbind-swiperefreshlayout` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-swiperefreshlayout:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-swiperefreshlayout:1.7.0") +} ``` ## List of extensions diff --git a/corbind-viewpager/README.md b/corbind-viewpager/README.md index 26c38e9e..d6e4e61a 100644 --- a/corbind-viewpager/README.md +++ b/corbind-viewpager/README.md @@ -3,8 +3,10 @@ To add androidx viewpager bindings, import `corbind-viewpager` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-viewpager:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-viewpager:1.7.0") +} ``` ## List of extensions diff --git a/corbind-viewpager/src/main/kotlin/ru/ldralighieri/corbind/viewpager/ViewPagerPageScrollEvents.kt b/corbind-viewpager/src/main/kotlin/ru/ldralighieri/corbind/viewpager/ViewPagerPageScrollEvents.kt index e9eb368f..c5aacf72 100644 --- a/corbind-viewpager/src/main/kotlin/ru/ldralighieri/corbind/viewpager/ViewPagerPageScrollEvents.kt +++ b/corbind-viewpager/src/main/kotlin/ru/ldralighieri/corbind/viewpager/ViewPagerPageScrollEvents.kt @@ -126,8 +126,10 @@ private fun listener( override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { if (scope.isActive) { val event = ViewPagerPageScrollEvent( - viewPager, position, positionOffset, - positionOffsetPixels + viewPager = viewPager, + position = position, + positionOffset = positionOffset, + positionOffsetPixels = positionOffsetPixels ) emitter(event) } diff --git a/corbind-viewpager2/README.md b/corbind-viewpager2/README.md index dd391817..1a4f11a1 100644 --- a/corbind-viewpager2/README.md +++ b/corbind-viewpager2/README.md @@ -3,8 +3,10 @@ To add androidx viewpager2 bindings, import `corbind-viewpager2` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind-viewpager2:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind-viewpager2:1.7.0") +} ``` ## List of extensions diff --git a/corbind-viewpager2/src/main/kotlin/ru/ldralighieri/corbind/viewpager2/ViewPager2PageScrollEvents.kt b/corbind-viewpager2/src/main/kotlin/ru/ldralighieri/corbind/viewpager2/ViewPager2PageScrollEvents.kt index bb93abe4..c96b0b2b 100644 --- a/corbind-viewpager2/src/main/kotlin/ru/ldralighieri/corbind/viewpager2/ViewPager2PageScrollEvents.kt +++ b/corbind-viewpager2/src/main/kotlin/ru/ldralighieri/corbind/viewpager2/ViewPager2PageScrollEvents.kt @@ -126,8 +126,10 @@ private fun callback( override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { if (scope.isActive) { val event = ViewPager2PageScrollEvent( - viewPager, position, positionOffset, - positionOffsetPixels + viewPager = viewPager, + position = position, + positionOffset = positionOffset, + positionOffsetPixels = positionOffsetPixels ) emitter(event) } diff --git a/corbind/README.md b/corbind/README.md index 4ac57e11..21442c01 100644 --- a/corbind/README.md +++ b/corbind/README.md @@ -3,8 +3,10 @@ To add platform bindings, import `corbind` module: -```groovy -implementation 'ru.ldralighieri.corbind:corbind:1.6.0' +```kotlin +dependencies { + implementation("ru.ldralighieri.corbind:corbind:1.7.0") +} ``` ## List of extensions diff --git a/corbind/build.gradle.kts b/corbind/build.gradle.kts index bb097f0c..e0d7bbd7 100644 --- a/corbind/build.gradle.kts +++ b/corbind/build.gradle.kts @@ -25,7 +25,6 @@ android { } dependencies { - api(libs.kotlin.stdlib) api(libs.kotlin.coroutines.android) api(libs.androidx.annotation) } diff --git a/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/AbsListViewScrollEvents.kt b/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/AbsListViewScrollEvents.kt index 818118f9..bfa463fa 100644 --- a/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/AbsListViewScrollEvents.kt +++ b/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/AbsListViewScrollEvents.kt @@ -138,8 +138,11 @@ private fun listener( currentScrollState = scrollState if (scope.isActive) { val event = AbsListViewScrollEvent( - absListView, scrollState, - absListView.firstVisiblePosition, absListView.childCount, absListView.count + view = absListView, + scrollState = scrollState, + firstVisibleItem = absListView.firstVisiblePosition, + visibleItemCount = absListView.childCount, + totalItemCount = absListView.count ) emitter(event) } @@ -153,8 +156,11 @@ private fun listener( ) { if (scope.isActive) { val event = AbsListViewScrollEvent( - absListView, currentScrollState, firstVisibleItem, - visibleItemCount, totalItemCount + view = absListView, + scrollState = currentScrollState, + firstVisibleItem = firstVisibleItem, + visibleItemCount = visibleItemCount, + totalItemCount = totalItemCount ) emitter(event) } diff --git a/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/AdapterViewSelectionEvents.kt b/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/AdapterViewSelectionEvents.kt index 6f99fdec..78b67adf 100644 --- a/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/AdapterViewSelectionEvents.kt +++ b/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/AdapterViewSelectionEvents.kt @@ -182,8 +182,10 @@ private fun initialValue(adapterView: AdapterView): AdapterView AdapterViewNothingSelectionEvent(adapterView) } else { AdapterViewItemSelectionEvent( - adapterView, adapterView.selectedView, - adapterView.selectedItemPosition, adapterView.selectedItemId + view = adapterView, + selectedView = adapterView.selectedView, + position = adapterView.selectedItemPosition, + id = adapterView.selectedItemId ) } } diff --git a/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/PopupMenuItemClicks.kt b/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/PopupMenuItemClicks.kt index 63c98041..42434283 100644 --- a/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/PopupMenuItemClicks.kt +++ b/corbind/src/main/kotlin/ru/ldralighieri/corbind/widget/PopupMenuItemClicks.kt @@ -123,7 +123,6 @@ private fun listener( scope: CoroutineScope, emitter: (MenuItem) -> Unit ) = PopupMenu.OnMenuItemClickListener { - if (scope.isActive) { emitter(it) return@OnMenuItemClickListener true diff --git a/gradle.properties b/gradle.properties index 168e36b8..aedfbe09 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,20 +1,10 @@ +# Gradle # https://github.com/Kotlin/dokka/issues/1405 org.gradle.jvmargs=-XX:MaxMetaspaceSize=3G - -# AndroidX -android.useAndroidX=true -compileSdk=33 -targetSdk=33 -minSdk=14 - - # Maven -SONATYPE_HOST=DEFAULT -RELEASE_SIGNING_ENABLED=true - GROUP=ru.ldralighieri.corbind -VERSION_NAME=1.6.1-SNAPSHOT +VERSION_NAME=1.7.0 POM_DESCRIPTION=Kotlin Coroutines binding APIs for Android UI widgets from the platform and support libraries. @@ -30,3 +20,14 @@ POM_LICENCE_DIST=repo POM_DEVELOPER_ID=ldralighieri POM_DEVELOPER_NAME=Vladimir Raupov POM_DEVELOPER_URL=https://github.com/LDRAlighieri/ + +SONATYPE_HOST=DEFAULT +RELEASE_SIGNING_ENABLED=true +SONATYPE_AUTOMATIC_RELEASE=true + +# Android +android.useAndroidX=true +buildTools=33.0.1 +compileSdk=33 +targetSdk=33 +minSdk=14 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 329ca624..8c995c42 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,76 +1,77 @@ [versions] -spotless = "6.8.0" -gver = "0.43.0" -detekt = "1.21.0" +# Plugins +agp = "7.4.0" dokka = "1.7.20" -maven-publish = "0.22.0" +spotless = "6.13.0" +mavenPublish = "0.23.2" +detekt = "1.22.0" +gver = "0.44.0" -plugin-android = "7.3.1" +# Kotlin +kotlin = "1.8.0" +kotlinCoroutines = "1.6.4" -kotlin = "1.7.20" -kotlin-coroutines = "1.6.4" +# Androidx +androidxCore = "1.9.0" +androidxAnnotation = "1.5.0" +androidxAppcompat = "1.6.0" +androidxDrawerlayout = "1.1.1" +androidxLeanback = "1.0.0" +androidxNavigation = "2.5.3" +androidxRecyclerview = "1.2.1" +androidxSlidingpanelayout = "1.2.0" +androidxSwiperefreshlayout = "1.1.0" +androidxViewpager = "1.0.0" +androidxViewpager2 = "1.0.0" +androidxLifecycle = "2.5.1" +androidxActivity = "1.6.1" +androidxFragment = "1.5.5" +androidxConstraintlayout = "2.1.4" -androidx-core = "1.9.0" -androidx-annotation = "1.5.0" -androidx-appcompat = "1.5.1" -androidx-drawerlayout = "1.1.1" -androidx-leanback = "1.0.0" -androidx-navigation = "2.5.2" -androidx-recyclerview = "1.2.1" -androidx-slidingpanelayout = "1.2.0" -androidx-swiperefreshlayout = "1.1.0" -androidx-viewpager = "1.0.0" -androidx-viewpager2 = "1.0.0" -androidx-lifecycle = "2.5.1" -androidx-activity = "1.6.0" -androidx-fragment = "1.5.3" -androidx-constraintlayout = "2.1.4" - -material = "1.7.0" +# Google +material = "1.8.0" +# Lint ktlint = "0.46.1" -[plugins] -spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } -gver = { id = "com.github.ben-manes.versions", version.ref = "gver" } -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } -dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } +[libraries] +# Kotlin +kotlin-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinCoroutines" } +# Androidx +androidx-core = { module = "androidx.core:core", version.ref = "androidxCore" } +androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidxAnnotation" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidxAppcompat" } +androidx-drawerlayout = { module = "androidx.drawerlayout:drawerlayout", version.ref = "androidxDrawerlayout" } +androidx-leanback = { module = "androidx.leanback:leanback", version.ref = "androidxLeanback" } +androidx-navigation = { module = "androidx.navigation:navigation-runtime", version.ref = "androidxNavigation" } +androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "androidxRecyclerview" } +androidx-slidingpanelayout = { module = "androidx.slidingpanelayout:slidingpanelayout", version.ref = "androidxSlidingpanelayout" } +androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidxSwiperefreshlayout" } +androidx-viewpager = { module = "androidx.viewpager:viewpager", version.ref = "androidxViewpager" } +androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "androidxViewpager2" } +androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" } +androidx-activity = { module = "androidx.activity:activity", version.ref = "androidxActivity" } +androidx-fragment = { module = "androidx.fragment:fragment", version.ref = "androidxFragment" } +androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidxConstraintlayout" } -[libraries] -android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "plugin-android" } +# Google +material = { module = "com.google.android.material:material", version.ref = "material" } + + +# build-logic +android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "agp" } kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } dokka-gradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } -publish-gradlePlugin = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "maven-publish" } spotless-gradlePlugin = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } -kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" } -kotlin-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlin-coroutines" } - -androidx-core = { module = "androidx.core:core", version.ref = "androidx-core" } -androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" } -androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } -androidx-drawerlayout = { module = "androidx.drawerlayout:drawerlayout", version.ref = "androidx-drawerlayout" } -androidx-leanback = { module = "androidx.leanback:leanback", version.ref = "androidx-leanback" } -androidx-navigation = { module = "androidx.navigation:navigation-runtime", version.ref = "androidx-navigation" } -androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "androidx-recyclerview" } -androidx-slidingpanelayout = { module = "androidx.slidingpanelayout:slidingpanelayout", version.ref = "androidx-slidingpanelayout" } -androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidx-swiperefreshlayout" } -androidx-viewpager = { module = "androidx.viewpager:viewpager", version.ref = "androidx-viewpager" } -androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "androidx-viewpager2" } -androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" } -androidx-activity = { module = "androidx.activity:activity", version.ref = "androidx-activity" } -androidx-fragment = { module = "androidx.fragment:fragment", version.ref = "androidx-fragment" } -androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" } - -material = { module = "com.google.android.material:material", version.ref = "material" } - -[bundles] -plugins = [ - "android-gradlePlugin", - "kotlin-gradlePlugin", - "dokka-gradlePlugin", - "publish-gradlePlugin" -] +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } +maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +gver = { id = "com.github.ben-manes.versions", version.ref = "gver" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cbff8992..f1ff01ff 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -18,4 +18,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 44032297..07e0fbee 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -20,12 +20,15 @@ plugins { } android { + namespace = "ru.ldralighieri.corbind.sample" + + val buildTools: String by project val compileSdk: String by project val minSdk: String by project val targetSdk: String by project @Suppress("LocalVariableName") val VERSION_NAME: String by project - namespace = "ru.ldralighieri.corbind.sample" + buildToolsVersion = buildTools this.compileSdk = compileSdk.toInt() defaultConfig { @@ -39,24 +42,24 @@ android { } buildTypes { - debug { + val debug by getting { isDebuggable = true isMinifyEnabled = false isShrinkResources = false - proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } release { isDebuggable = false isMinifyEnabled = true isShrinkResources = true + signingConfig = debug.signingConfig proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } packagingOptions { diff --git a/sample/src/main/res/values-v29/themes.xml b/sample/src/main/res/values-v29/themes.xml index 808d87fb..95e3cec7 100644 --- a/sample/src/main/res/values-v29/themes.xml +++ b/sample/src/main/res/values-v29/themes.xml @@ -14,12 +14,12 @@ ~ limitations under the License. --> - + -