diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 177b8ff..dbb431c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,4 +16,4 @@ jobs: with: java-version: 1.8 - name: Perform base checks - run: ./gradlew app:assembleDebug library:dokka \ No newline at end of file + run: ./gradlew app:assembleDebug library:publishToDirectory \ No newline at end of file diff --git a/README.md b/README.md index b607223..9a478e7 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A collection of modular elements for `RecyclerView` lists, alternative to [Google's Paging library](https://developer.android.com/topic/libraries/architecture/paging/), designed in Kotlin with these goals in mind: ```kotlin -implementation("com.otaliastudios:elements:0.3.7") +implementation("com.otaliastudios:elements:0.4.0") ``` Design features: diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2f914da..bb09db9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,12 +5,12 @@ plugins { } android { - setCompileSdkVersion(rootProject.extra["compileSdkVersion"] as Int) + setCompileSdkVersion(property("compileSdkVersion") as Int) defaultConfig { applicationId = "com.otaliastudios.elements.sample" - setMinSdkVersion(rootProject.extra["minSdkVersion"] as Int) - setTargetSdkVersion(rootProject.extra["targetSdkVersion"] as Int) + setMinSdkVersion(property("minSdkVersion") as Int) + setTargetSdkVersion(property("targetSdkVersion") as Int) versionCode = 1 versionName = "1.0" vectorDrawables.useSupportLibrary = true @@ -21,12 +21,12 @@ android { getByName("test").java.srcDir("src/test/kotlin") } - dataBinding.isEnabled = true + buildFeatures { + dataBinding = true + } } dependencies { - val kotlinVersion = property("kotlinVersion") as String implementation("androidx.appcompat:appcompat:1.1.0") - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion") implementation(project(":library")) } diff --git a/app/src/main/kotlin/com/otaliastudios/elements/sample/MainActivity.kt b/app/src/main/kotlin/com/otaliastudios/elements/sample/MainActivity.kt index 7a6c0d2..d56b34d 100644 --- a/app/src/main/kotlin/com/otaliastudios/elements/sample/MainActivity.kt +++ b/app/src/main/kotlin/com/otaliastudios/elements/sample/MainActivity.kt @@ -16,7 +16,6 @@ import com.otaliastudios.elements.sample.presenters.BottomPresenter import com.otaliastudios.elements.sample.presenters.PlaygroundPresenter import com.otaliastudios.elements.sample.presenters.TopMessagePresenter import com.otaliastudios.elements.sample.sources.* -import timber.log.Timber class MainActivity : AppCompatActivity() { @@ -24,7 +23,6 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) ElementsLogger.setLevel(ElementsLogger.VERBOSE) - Timber.plant(Timber.DebugTree()) val recycler = findViewById(R.id.recycler) val list = listOf( R.string.menu_paged, R.string.menu_paged_on_click, diff --git a/build.gradle.kts b/build.gradle.kts index 8f070dc..070f9ec 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,6 @@ buildscript { extra["minSdkVersion"] = 14 extra["compileSdkVersion"] = 29 extra["targetSdkVersion"] = 29 - extra["kotlinVersion"] = "1.3.61" repositories { mavenCentral() @@ -12,10 +11,9 @@ buildscript { } dependencies { - val kotlinVersion = property("kotlinVersion") as String - classpath("com.android.tools.build:gradle:3.6.1") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") - classpath("com.otaliastudios.tools:publisher:0.1.5") + classpath("com.android.tools.build:gradle:4.0.1") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0") + classpath("com.otaliastudios.tools:publisher:0.3.3") } } diff --git a/docs/.gitignore b/docs/.gitignore index d8a620e..928a70a 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -3,3 +3,4 @@ _pages *.sw? .sass-cache .jekyll-metadata +Gemfile.lock diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock deleted file mode 100644 index e26c1aa..0000000 --- a/docs/Gemfile.lock +++ /dev/null @@ -1,248 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - activesupport (4.2.10) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.11.1) - colorator (1.1.0) - commonmarker (0.17.13) - ruby-enum (~> 0.5) - concurrent-ruby (1.1.4) - dnsruby (1.61.2) - addressable (~> 2.5) - em-websocket (0.5.1) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0.6.0) - ethon (0.11.0) - ffi (>= 1.3.0) - eventmachine (1.2.7) - execjs (2.7.0) - faraday (0.15.4) - multipart-post (>= 1.2, < 3) - ffi (1.9.25) - forwardable-extended (2.6.0) - gemoji (3.0.0) - github-pages (193) - activesupport (= 4.2.10) - github-pages-health-check (= 1.8.1) - jekyll (= 3.7.4) - jekyll-avatar (= 0.6.0) - jekyll-coffeescript (= 1.1.1) - jekyll-commonmark-ghpages (= 0.1.5) - jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.11.0) - jekyll-gist (= 1.5.0) - jekyll-github-metadata (= 2.9.4) - jekyll-mentions (= 1.4.1) - jekyll-optional-front-matter (= 0.3.0) - jekyll-paginate (= 1.1.0) - jekyll-readme-index (= 0.2.0) - jekyll-redirect-from (= 0.14.0) - jekyll-relative-links (= 0.5.3) - jekyll-remote-theme (= 0.3.1) - jekyll-sass-converter (= 1.5.2) - jekyll-seo-tag (= 2.5.0) - jekyll-sitemap (= 1.2.0) - jekyll-swiss (= 0.4.0) - jekyll-theme-architect (= 0.1.1) - jekyll-theme-cayman (= 0.1.1) - jekyll-theme-dinky (= 0.1.1) - jekyll-theme-hacker (= 0.1.1) - jekyll-theme-leap-day (= 0.1.1) - jekyll-theme-merlot (= 0.1.1) - jekyll-theme-midnight (= 0.1.1) - jekyll-theme-minimal (= 0.1.1) - jekyll-theme-modernist (= 0.1.1) - jekyll-theme-primer (= 0.5.3) - jekyll-theme-slate (= 0.1.1) - jekyll-theme-tactile (= 0.1.1) - jekyll-theme-time-machine (= 0.1.1) - jekyll-titles-from-headings (= 0.5.1) - jemoji (= 0.10.1) - kramdown (= 1.17.0) - liquid (= 4.0.0) - listen (= 3.1.5) - mercenary (~> 0.3) - minima (= 2.5.0) - nokogiri (>= 1.8.2, < 2.0) - rouge (= 2.2.1) - terminal-table (~> 1.4) - github-pages-health-check (1.8.1) - addressable (~> 2.3) - dnsruby (~> 1.60) - octokit (~> 4.0) - public_suffix (~> 2.0) - typhoeus (~> 1.3) - html-pipeline (2.9.1) - activesupport (>= 2) - nokogiri (>= 1.4) - http_parser.rb (0.6.0) - i18n (0.9.5) - concurrent-ruby (~> 1.0) - jekyll (3.7.4) - addressable (~> 2.4) - colorator (~> 1.0) - em-websocket (~> 0.5) - i18n (~> 0.7) - jekyll-sass-converter (~> 1.0) - jekyll-watch (~> 2.0) - kramdown (~> 1.14) - liquid (~> 4.0) - mercenary (~> 0.3.3) - pathutil (~> 0.9) - rouge (>= 1.7, < 4) - safe_yaml (~> 1.0) - jekyll-avatar (0.6.0) - jekyll (~> 3.0) - jekyll-coffeescript (1.1.1) - coffee-script (~> 2.2) - coffee-script-source (~> 1.11.1) - jekyll-commonmark (1.2.0) - commonmarker (~> 0.14) - jekyll (>= 3.0, < 4.0) - jekyll-commonmark-ghpages (0.1.5) - commonmarker (~> 0.17.6) - jekyll-commonmark (~> 1) - rouge (~> 2) - jekyll-default-layout (0.1.4) - jekyll (~> 3.0) - jekyll-feed (0.11.0) - jekyll (~> 3.3) - jekyll-gist (1.5.0) - octokit (~> 4.2) - jekyll-github-metadata (2.9.4) - jekyll (~> 3.1) - octokit (~> 4.0, != 4.4.0) - jekyll-mentions (1.4.1) - html-pipeline (~> 2.3) - jekyll (~> 3.0) - jekyll-optional-front-matter (0.3.0) - jekyll (~> 3.0) - jekyll-paginate (1.1.0) - jekyll-readme-index (0.2.0) - jekyll (~> 3.0) - jekyll-redirect-from (0.14.0) - jekyll (~> 3.3) - jekyll-relative-links (0.5.3) - jekyll (~> 3.3) - jekyll-remote-theme (0.3.1) - jekyll (~> 3.5) - rubyzip (>= 1.2.1, < 3.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) - jekyll-seo-tag (2.5.0) - jekyll (~> 3.3) - jekyll-sitemap (1.2.0) - jekyll (~> 3.3) - jekyll-swiss (0.4.0) - jekyll-theme-architect (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-cayman (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-dinky (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-hacker (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-leap-day (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-merlot (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-midnight (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-minimal (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-modernist (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-primer (0.5.3) - jekyll (~> 3.5) - jekyll-github-metadata (~> 2.9) - jekyll-seo-tag (~> 2.0) - jekyll-theme-slate (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-tactile (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-time-machine (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-titles-from-headings (0.5.1) - jekyll (~> 3.3) - jekyll-watch (2.1.2) - listen (~> 3.0) - jemoji (0.10.1) - gemoji (~> 3.0) - html-pipeline (~> 2.2) - jekyll (~> 3.0) - kramdown (1.17.0) - liquid (4.0.0) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) - mercenary (0.3.6) - mini_portile2 (2.4.0) - minima (2.5.0) - jekyll (~> 3.5) - jekyll-feed (~> 0.9) - jekyll-seo-tag (~> 2.1) - minitest (5.11.3) - multipart-post (2.0.0) - nokogiri (1.10.8) - mini_portile2 (~> 2.4.0) - octokit (4.13.0) - sawyer (~> 0.8.0, >= 0.5.3) - pathutil (0.16.2) - forwardable-extended (~> 2.6) - public_suffix (2.0.5) - rb-fsevent (0.10.3) - rb-inotify (0.10.0) - ffi (~> 1.0) - rouge (2.2.1) - ruby-enum (0.7.2) - i18n - ruby_dep (1.5.0) - rubyzip (2.0.0) - safe_yaml (1.0.4) - sass (3.7.2) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - sawyer (0.8.1) - addressable (>= 2.3.5, < 2.6) - faraday (~> 0.8, < 1.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - thread_safe (0.3.6) - typhoeus (1.3.1) - ethon (>= 0.9.0) - tzinfo (1.2.5) - thread_safe (~> 0.1) - unicode-display_width (1.4.0) - -PLATFORMS - ruby - -DEPENDENCIES - github-pages - -BUNDLED WITH - 1.17.2 diff --git a/docs/_about/changelog.md b/docs/_about/changelog.md index cee82da..ace1298 100644 --- a/docs/_about/changelog.md +++ b/docs/_about/changelog.md @@ -9,10 +9,16 @@ New versions are released through GitHub, so the reference page is the [GitHub R > You can [support development](https://github.com/natario1/Elements/issues/4) through the GitHub Sponsors program. Companies can share a tiny part of their revenue and get private support hours in return. Thanks! +### v0.4.0 + +- Refreshed dependencies, Kotlin 1.4.0 [#10][10] + + + #### v0.3.7 First versioned release. [natario1]: https://github.com/natario1 -[38]: https://github.com/natario1/Elements/pull/38 +[10]: https://github.com/natario1/Elements/pull/10 diff --git a/docs/_config.yml b/docs/_config.yml index 1193dd8..9cc8069 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -12,7 +12,7 @@ google_analytics_id: 'UA-155077779-6' google_site_verification: '4x49i17ABIrSvUl52SeL0-t0341aTnWWaC62-FYCRT4' github: [metadata] # TODO What's this? github_repo: Elements -github_version: 0.3.7 +github_version: 0.4.0 github_branch: master baseurl: '/Elements' # Keep as an empty string if served up at the root collections: diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0ebb310..fd0c5a3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 65b9507..cb034a0 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -1,11 +1,11 @@ -import com.otaliastudios.tools.publisher.PublisherExtension.License -import com.otaliastudios.tools.publisher.PublisherExtension.Release +import com.otaliastudios.tools.publisher.common.License +import com.otaliastudios.tools.publisher.common.Release plugins { id("com.android.library") id("kotlin-android") id("kotlin-android-extensions") - id("maven-publisher-bintray") + id("com.otaliastudios.tools.publisher") } android { @@ -14,7 +14,7 @@ android { defaultConfig { setMinSdkVersion(property("minSdkVersion") as Int) setTargetSdkVersion(property("targetSdkVersion") as Int) - versionName = "0.3.7" + versionName = "0.4.0" } sourceSets { @@ -22,21 +22,23 @@ android { get("test").java.srcDirs("src/test/kotlin") } - dataBinding.isEnabled = true + buildFeatures { + dataBinding = true + } + + kotlinOptions { + // Until the explicitApi() works in the Kotlin block... + // https://youtrack.jetbrains.com/issue/KT-37652 + freeCompilerArgs += listOf("-Xexplicit-api=strict") + } } dependencies { - val kotlinVersion = property("kotlinVersion") as String - api("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion") api("androidx.recyclerview:recyclerview:1.1.0") api("androidx.lifecycle:lifecycle-livedata:2.2.0") - api("com.jakewharton.timber:timber:4.7.1") } publisher { - auth.user = "BINTRAY_USER" - auth.key = "BINTRAY_KEY" - auth.repo = "BINTRAY_REPO" project.artifact = "elements" project.description = "A modular approach to RecyclerView adapters with reusable, testable, independent, coordinated components." project.group = "com.otaliastudios" @@ -44,6 +46,14 @@ publisher { project.addLicense(License.APACHE_2_0) release.setSources(Release.SOURCES_AUTO) release.setDocs(Release.DOCS_AUTO) + bintray { + auth.user = "BINTRAY_USER" + auth.key = "BINTRAY_KEY" + auth.repo = "BINTRAY_REPO" + } + directory { + directory = "build/maven" + } } diff --git a/library/src/main/kotlin/com/otaliastudios/elements/Adapter.kt b/library/src/main/kotlin/com/otaliastudios/elements/Adapter.kt index 2efed20..1533ff0 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/Adapter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/Adapter.kt @@ -95,13 +95,13 @@ public final class Adapter private constructor( } } - companion object { + public companion object { /** * Constant for [Builder.autoScrollMode]. * This means that no autoscroll will be performed. */ - const val AUTOSCROLL_OFF = 0 + public const val AUTOSCROLL_OFF: Int = 0 /** * Constant for [Builder.autoScrollMode]. @@ -109,7 +109,7 @@ public final class Adapter private constructor( * the adapter will automatically scroll any attached RecyclerView * to position 0 so that new items are visible. */ - const val AUTOSCROLL_POSITION_0 = 1 + public const val AUTOSCROLL_POSITION_0: Int = 1 /** * Constant for [Builder.autoScrollMode]. @@ -117,13 +117,16 @@ public final class Adapter private constructor( * the adapter will automatically scroll any attached RecyclerView * to that position so that new items are visible. */ - const val AUTOSCROLL_POSITION_ANY = 2 + public const val AUTOSCROLL_POSITION_ANY: Int = 2 /** * Shorthand for creating a [Builder] for the given * lifecycle owner and page hint. */ - fun builder(lifecycleOwner: LifecycleOwner, pageSizeHint: Int = Int.MAX_VALUE) = Builder(lifecycleOwner, pageSizeHint) + public fun builder( + lifecycleOwner: LifecycleOwner, + pageSizeHint: Int = Int.MAX_VALUE + ): Builder = Builder(lifecycleOwner, pageSizeHint) private val TAG = Adapter::class.java.simpleName } @@ -133,8 +136,9 @@ public final class Adapter private constructor( * any reference while it observes sources and page state. * This is the same [Lifecycle] object that was passed to the builder. */ - override fun getLifecycle() = lifecycleOwner.lifecycle + override fun getLifecycle(): Lifecycle = lifecycleOwner.lifecycle + private val log = ElementsLogger("Adapter") private val autoScrollListeners = mutableSetOf() private val recyclerViews = mutableSetOf() private val typeMap: SparseArray> = SparseArray() @@ -347,7 +351,7 @@ public final class Adapter private constructor( * Returns the current item count, not considering any * transaction that is being computed. */ - override fun getItemCount() = pageManager.elementCount() + override fun getItemCount(): Int = pageManager.elementCount() /** * Returns the element type for the given position, @@ -373,9 +377,7 @@ public final class Adapter private constructor( * In the future we want to just do a no-op. */ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Presenter.Holder { - if (ElementsLogger.verbose()) { - ElementsLogger.v("onCreateViewHolder, type: $viewType") - } + log.v { "onCreateViewHolder, type: $viewType" } return presenterForType(viewType).createHolder(parent, viewType) } @@ -391,13 +393,13 @@ public final class Adapter private constructor( val query = pageManager.elementAt(position, true)!! val page = query.page val element = query.element as Element - if (ElementsLogger.verbose()) { - ElementsLogger.v("onBindViewHolder, type: ${holder.itemViewType}, " + + log.v { + "onBindViewHolder, type: ${holder.itemViewType}, " + "elementType: ${element.type}, " + "position: $position, " + "presenter: ${presenter.javaClass.simpleName}, " + "data: ${element.data?.javaClass?.simpleName}, " + - "source: ${element.source.javaClass.simpleName}") + "source: ${element.source.javaClass.simpleName}" } if (element.type != holder.itemViewType) { throw RuntimeException("Something is wrong here...") @@ -429,7 +431,7 @@ public final class Adapter private constructor( onPageCreated(page) return true } else { - ElementsLogger.w("requestPage was blocked by one of the sources. Current page: $current ${current?.number}") + log.w { "requestPage was blocked by one of the sources. Current page: $current ${current?.number}" } return false } } @@ -515,7 +517,7 @@ public final class Adapter private constructor( /** * Auto scroll is being performed. */ - fun onAutoScroll(adapter: Adapter, recycler: RecyclerView, position: Int) + public fun onAutoScroll(adapter: Adapter, recycler: RecyclerView, position: Int) } /** diff --git a/library/src/main/kotlin/com/otaliastudios/elements/AnimationType.kt b/library/src/main/kotlin/com/otaliastudios/elements/AnimationType.kt index c0f329c..4db4dd7 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/AnimationType.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/AnimationType.kt @@ -3,10 +3,10 @@ package com.otaliastudios.elements /** * The animation type. */ -enum class AnimationType { +public enum class AnimationType { ADD, REMOVE; - fun isAdd() = this == ADD + public fun isAdd(): Boolean = this == ADD - fun isRemove() = this == REMOVE + public fun isRemove(): Boolean = this == REMOVE } \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/Animator.kt b/library/src/main/kotlin/com/otaliastudios/elements/Animator.kt index b69dbbc..545d3f9 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/Animator.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/Animator.kt @@ -11,7 +11,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { - companion object { + private companion object { private val defaultInterpolator: TimeInterpolator by lazy { ValueAnimator().interpolator } } @@ -61,6 +61,8 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { var toX: Int = 0, var toY: Int = 0) + private val log = ElementsLogger("Animator") + // Pending operations. We will execute them in runPendingAnimations() (or after). private val pendingRemovals = mutableListOf() private val pendingAdditions = mutableListOf() @@ -92,17 +94,17 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { * Whether we are running. Depends on our lists. */ override fun isRunning(): Boolean { - return (!pendingAdditions.isEmpty() - || !pendingChanges.isEmpty() - || !pendingMoves.isEmpty() - || !pendingRemovals.isEmpty() - || !runningMoves.isEmpty() - || !runningRemovals.isEmpty() - || !runningAdditions.isEmpty() - || !runningChanges.isEmpty() - || !scheduledMoves.isEmpty() - || !scheduledAdditions.isEmpty() - || !scheduledChanges.isEmpty()) + return (pendingAdditions.isNotEmpty() + || pendingChanges.isNotEmpty() + || pendingMoves.isNotEmpty() + || pendingRemovals.isNotEmpty() + || runningMoves.isNotEmpty() + || runningRemovals.isNotEmpty() + || runningAdditions.isNotEmpty() + || runningChanges.isNotEmpty() + || scheduledMoves.isNotEmpty() + || scheduledAdditions.isNotEmpty() + || scheduledChanges.isNotEmpty()) } /** @@ -110,9 +112,7 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { * to understand whether the item should be animated out or not. */ override fun animateRemove(holder: RecyclerView.ViewHolder): Boolean { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator animateRemove called for ${holder.hashCode()}") - } + log.v { "animateRemove called for ${holder.hashCode()}" } holder as Presenter.Holder resetAnimation(holder) return if (holder.animates(AnimationType.REMOVE)) { @@ -131,9 +131,7 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { * to understand whether the item should be animated in or not. */ override fun animateAdd(holder: RecyclerView.ViewHolder): Boolean { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator animateAdd called for ${holder.hashCode()}") - } + log.v { "animateAdd called for ${holder.hashCode()}" } holder as Presenter.Holder resetAnimation(holder) return if (holder.animates(AnimationType.ADD)) { @@ -152,9 +150,7 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { @Suppress("NAME_SHADOWING") override fun animateMove(holder: RecyclerView.ViewHolder, fromX: Int, fromY: Int, toX: Int, toY: Int): Boolean { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator animateMove called for ${holder.hashCode()}") - } + log.v { "animateMove called for ${holder.hashCode()}" } holder as Presenter.Holder val view = holder.itemView val fromX = fromX + view.translationX.toInt() @@ -163,13 +159,11 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { val deltaX = toX - fromX val deltaY = toY - fromY if (deltaX == 0 && deltaY == 0) { - ElementsLogger.w("Animator animateMove: finished because delta is 0.") + log.i { "animateMove: finished because delta is 0." } dispatchMoveFinished(holder) return false } else { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator animateMove: adding move to pendingMoves. deltaX $deltaX deltaY $deltaY.") - } + log.v { "animateMove: adding move to pendingMoves. deltaX $deltaX deltaY $deltaY." } } if (deltaX != 0) view.translationX = (-deltaX).toFloat() if (deltaY != 0) view.translationY = (-deltaY).toFloat() @@ -182,22 +176,16 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { */ override fun animateChange(oldHolder0: RecyclerView.ViewHolder, newHolder0: RecyclerView.ViewHolder?, fromX: Int, fromY: Int, toX: Int, toY: Int): Boolean { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator animateChange called for ${oldHolder0.hashCode()} and ${newHolder0?.hashCode()}") - } + log.v { "animateChange called for ${oldHolder0.hashCode()} and ${newHolder0?.hashCode()}" } val oldHolder = oldHolder0 as Presenter.Holder val newHolder = newHolder0 as? Presenter.Holder if (oldHolder === newHolder) { // Don't know how to run change animations when the same view holder is re-used. // run a move animation to handle position changes. - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator animateChange: Same holders. xRange $fromX $toX yRange $fromY $toY") - } + log.v { "animateChange: Same holders. xRange $fromX $toX yRange $fromY $toY" } return animateMove(oldHolder, fromX, fromY, toX, toY) } else { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator animateChange: Different holders. xRange $fromX $toX yRange $fromY $toY") - } + log.v { "animateChange: Different holders. xRange $fromX $toX yRange $fromY $toY" } } val prevTranslationX = oldHolder.itemView.translationX val prevTranslationY = oldHolder.itemView.translationY @@ -238,26 +226,20 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { // First, execute remove animations. for (holder in pendingRemovals) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator runPendingAnimations: executing removals.") - } + log.v { "runPendingAnimations: executing removals." } executeRemoval(holder) } pendingRemovals.clear() // Next, execute move and change animations at the same time. if (hasMoves) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator runPendingAnimations: scheduling moves.") - } + log.v { "runPendingAnimations: scheduling moves." } val moves = mutableListOf() moves.addAll(pendingMoves) scheduledMoves.add(moves) pendingMoves.clear() val mover = Runnable { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator runPendingAnimations: executing moves.") - } + log.v { "runPendingAnimations: executing moves." } moves.forEach { executeMove(it) } moves.clear() scheduledMoves.remove(moves) @@ -270,17 +252,13 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { } } if (hasChanges) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator runPendingAnimations: scheduling changes.") - } + log.v { "runPendingAnimations: scheduling changes." } val changes = mutableListOf() changes.addAll(pendingChanges) scheduledChanges.add(changes) pendingChanges.clear() val changer = Runnable { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator runPendingAnimations: executing changes. delayed: $hasRemovals") - } + log.v { "runPendingAnimations: executing changes. delayed: $hasRemovals" } changes.forEach { executeChange(it) } changes.clear() scheduledChanges.remove(changes) @@ -295,17 +273,13 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { // Next, execute additions after everything else has finished. if (hasAdditions) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator runPendingAnimations: scheduling additions.") - } + log.v { "runPendingAnimations: scheduling additions." } val additions = mutableListOf() additions.addAll(pendingAdditions) scheduledAdditions.add(additions) pendingAdditions.clear() val adder = Runnable { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator runPendingAnimations: executing additions.") - } + log.v { "runPendingAnimations: executing additions." } additions.forEach { executeAddition(it) } additions.clear() scheduledAdditions.remove(additions) @@ -409,16 +383,12 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { oldViewAnim.translationY((changeInfo.toY - changeInfo.fromY).toFloat()) oldViewAnim.alpha(0f).setListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animator: Animator) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator executeChange: oldHolder: onAnimationStart") - } + log.v { "executeChange: oldHolder: onAnimationStart" } dispatchChangeStarting(oldHolder, true) } override fun onAnimationEnd(animator: Animator) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator executeChange: oldHolder: onAnimationEnd") - } + log.v { "executeChange: oldHolder: onAnimationEnd" } oldViewAnim.setListener(null) oldView.alpha = 1f oldView.translationX = 0f @@ -430,9 +400,7 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { override fun onAnimationCancel(animation: Animator?) { super.onAnimationCancel(animation) - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator executeChange: oldHolder: onAnimationCancel") - } + log.v { "executeChange: oldHolder: onAnimationCancel" } } }).start() } @@ -443,16 +411,12 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { newViewAnimation.translationX(0f).translationY(0f).setDuration(changeDuration) .alpha(1f).setListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animator: Animator) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator executeChange: newHolder: onAnimationStart") - } + log.v { "executeChange: newHolder: onAnimationStart" } dispatchChangeStarting(newHolder, false) } override fun onAnimationEnd(animator: Animator) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator executeChange: newHolder: onAnimationEnd") - } + log.v { "executeChange: newHolder: onAnimationEnd" } newViewAnimation.setListener(null) newView.alpha = 1f newView.translationX = 0f @@ -464,9 +428,7 @@ public class Animator(private val adapter: Adapter) : SimpleItemAnimator() { override fun onAnimationCancel(animation: Animator?) { super.onAnimationCancel(animation) - if (ElementsLogger.verbose()) { - ElementsLogger.v("Animator executeChange: newHolder: onAnimationCancel") - } + log.v { "executeChange: newHolder: onAnimationCancel" } } }).start() } diff --git a/library/src/main/kotlin/com/otaliastudios/elements/Element.kt b/library/src/main/kotlin/com/otaliastudios/elements/Element.kt index d0a94c6..29230a4 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/Element.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/Element.kt @@ -24,7 +24,7 @@ public class Element internal constructor( * Creates a new element with same characteristics, * but replaces its data. */ - fun clone(newData: T?): Element { + public fun clone(newData: T?): Element { return Element(source, type, newData, extra) } } \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/ElementAtResult.kt b/library/src/main/kotlin/com/otaliastudios/elements/ElementAtResult.kt index 0bd7639..4219ed5 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/ElementAtResult.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/ElementAtResult.kt @@ -3,17 +3,17 @@ package com.otaliastudios.elements /** * Results returned by [Adapter.elementAt]. */ -class ElementAtResult { +public class ElementAtResult { - lateinit var page: Page + public lateinit var page: Page internal set - lateinit var element: Element<*> + public lateinit var element: Element<*> internal set - var positionInPage: Int = 0 + public var positionInPage: Int = 0 internal set - var position: Int = 0 + public var position: Int = 0 internal set } \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/ElementsLogger.kt b/library/src/main/kotlin/com/otaliastudios/elements/ElementsLogger.kt index 0d6b628..c3194b4 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/ElementsLogger.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/ElementsLogger.kt @@ -1,62 +1,57 @@ package com.otaliastudios.elements import android.util.Log -import timber.log.Timber -object ElementsLogger { - const val VERBOSE = Log.VERBOSE - const val INFO = Log.INFO - const val WARN = Log.WARN - const val ERROR = Log.ERROR +/** + * Lazy logger used across the library. + * Use [setLevel] to configure the verbosity. + */ +public class ElementsLogger internal constructor(private val tag: String) { - private var level = ERROR + public companion object { + public const val VERBOSE: Int = Log.VERBOSE + public const val INFO: Int = Log.INFO + public const val WARN: Int = Log.WARN + public const val ERROR: Int = Log.ERROR - fun setLevel(level: Int) { - this.level = level - } + private var level = WARN - fun logs(level: Int): Boolean { - return this.level <= level + @JvmStatic + public fun setLevel(level: Int) { + this.level = level + } } - fun verbose() = logs(VERBOSE) - - fun infos() = logs(INFO) - - fun warns() = logs(WARN) - - fun errors() = logs(ERROR) - - internal fun w(message: String) { - if (warns()) Timber.w(message) + internal fun w(message: () -> String) { + if (level <= WARN) Log.w(tag, message()) } - internal fun w(throwable: Throwable, message: String) { - if (warns()) Timber.w(throwable, message) + internal fun w(throwable: Throwable, message: () -> String) { + if (level <= WARN) Log.w(tag, message(), throwable) } - internal fun e(message: String) { - if (errors()) Timber.e(message) + internal fun e(message: () -> String) { + if (level <= ERROR) Log.e(tag, message()) } - internal fun e(throwable: Throwable, message: String) { - if (errors()) Timber.e(throwable, message) + internal fun e(throwable: Throwable, message: () -> String) { + if (level <= ERROR) Log.e(tag, message(), throwable) } - internal fun v(message: String) { - if (verbose()) Timber.v(message) + internal fun i(message: () -> String) { + if (level <= INFO) Log.i(tag, message()) } - internal fun v(throwable: Throwable, message: String) { - if (verbose()) Timber.v(throwable, message) + internal fun i(throwable: Throwable, message: () -> String) { + if (level <= INFO) Log.i(tag, message(), throwable) } - internal fun i(message: String) { - if (infos()) Timber.i(message) + internal fun v(message: () -> String) { + if (level <= VERBOSE) Log.v(tag, message()) } - internal fun i(throwable: Throwable, message: String) { - if (infos()) Timber.i(throwable, message) + internal fun v(throwable: Throwable, message: () -> String) { + if (level <= VERBOSE) Log.v(tag, message(), throwable) } } \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/Page.kt b/library/src/main/kotlin/com/otaliastudios/elements/Page.kt index 5073cad..615e82f 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/Page.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/Page.kt @@ -61,6 +61,7 @@ import kotlin.collections.ArrayList */ public class Page internal constructor(pageManager: PageManager, internal val number: Int) { + private val log = ElementsLogger("Page($number)") private var pageManager: PageManager? = pageManager private val semaphore = Semaphore(1, true) private var elements = arrayListOf>() @@ -75,7 +76,7 @@ public class Page internal constructor(pageManager: PageManager, internal val nu /** * The current size, not considering any pending update. */ - public fun elementCount() = elements.size + public fun elementCount(): Int = elements.size internal fun elementAt(position: Int) = elements[position] @@ -95,13 +96,13 @@ public class Page internal constructor(pageManager: PageManager, internal val nu * Returns true if this is the first page, * false otherwise. */ - public fun isFirstPage() = previous() == null + public fun isFirstPage(): Boolean = previous() == null /** * Returns true if this is the last page, * false otherwise. */ - public fun isLastPage() = next() == null + public fun isLastPage(): Boolean = next() == null internal fun isInUpdate() = semaphore.availablePermits() == 0 @@ -135,11 +136,7 @@ public class Page internal constructor(pageManager: PageManager, internal val nu } private fun log(event: String, message: String) { - ElementsLogger.v("$event, page($number), " + - "el(${elements.size}), " + - "rawEl(${rawElements.size}), " + - "uiThread(${isUiThread()}), " + - "message($message)") + log.v { "$event: el=${elements.size} rawEl=${rawElements.size} ui=${isUiThread()} msg=$message" } } @@ -262,7 +259,7 @@ public class Page internal constructor(pageManager: PageManager, internal val nu * actually block the UI thread. */ @UiThread // API - fun replaceElement(element: Element<*>, replacement: Element<*>) { + public fun replaceElement(element: Element<*>, replacement: Element<*>) { executePageApi { val message = "replaceElement $element with $replacement" val list = startUpdate(message) @@ -285,7 +282,7 @@ public class Page internal constructor(pageManager: PageManager, internal val nu * actually block the UI thread. */ @UiThread // API - fun insertElements(position: Int, elements: Collection>) { + public fun insertElements(position: Int, elements: Collection>) { executePageApi { val message = "insertElements position $position size ${elements.size}" val list = startUpdate(message) @@ -307,7 +304,7 @@ public class Page internal constructor(pageManager: PageManager, internal val nu * actually block the UI thread. */ @UiThread // API - fun replaceElements(position: Int, vararg elements: Element<*>) { + public fun replaceElements(position: Int, vararg elements: Element<*>) { executePageApi { val message = "replaceElements position $position size ${elements.size}" val list = startUpdate(message) @@ -334,7 +331,7 @@ public class Page internal constructor(pageManager: PageManager, internal val nu * actually block the UI thread. */ @UiThread // API - fun removeElements(position: Int, count: Int) { + public fun removeElements(position: Int, count: Int) { executePageApi { val message = "removeElements position $position count $count" val list = startUpdate(message) @@ -360,7 +357,7 @@ public class Page internal constructor(pageManager: PageManager, internal val nu * actually block the UI thread. */ @UiThread // API - fun notifyItemChanged(position: Int) { + public fun notifyItemChanged(position: Int) { executePageApi { val message = "notifyItemChanged position $position" val list = startUpdate(message) @@ -381,7 +378,7 @@ public class Page internal constructor(pageManager: PageManager, internal val nu * actually block the UI thread. */ @UiThread // API - fun notifyItemChanged(element: Element<*>) { + public fun notifyItemChanged(element: Element<*>) { executePageApi { val message = "notifyItemChanged element $element" val list = startUpdate(message) diff --git a/library/src/main/kotlin/com/otaliastudios/elements/PageManager.kt b/library/src/main/kotlin/com/otaliastudios/elements/PageManager.kt index db1b413..f3f6e56 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/PageManager.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/PageManager.kt @@ -17,6 +17,7 @@ import java.util.concurrent.atomic.AtomicInteger */ internal class PageManager : Iterable { + private val log = ElementsLogger("PageManager") private val pages = mutableListOf() private var adapter: Adapter? = null @@ -207,9 +208,7 @@ internal class PageManager : Iterable { val page = op.page val newList = page.startUpdate("performSyncUpdate") val oldList = newList.toList() - if (ElementsLogger.verbose()) { - ElementsLogger.v("PageManager performSyncUpdate for update $debugUpdate. Executing. Source: ${op.source::class.java.simpleName}") - } + log.v { "performSyncUpdate for update $debugUpdate. Executing. Source: ${op.source::class.java.simpleName}" } performPageUpdate(adapter, op, newList) val callback = DiffCallback(adapter, oldList, newList) val result = DiffUtil.calculateDiff(callback, true) @@ -391,31 +390,25 @@ internal class PageManager : Iterable { * using the page as reference. */ private inner class DiffDispatcher(private val page: Page): ListUpdateCallback { + private val log = ElementsLogger("DiffDispatcher(${hashCode()})") + override fun onChanged(position: Int, count: Int, payload: Any?) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("DiffDispatcher ${hashCode()} onChanged, position: $position, count: $count") - } + log.v { "onChanged, position: $position, count: $count" } notifyPageItemRangeChanged(page, position, count, payload) } override fun onMoved(fromPosition: Int, toPosition: Int) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("DiffDispatcher ${hashCode()} onMoved, fromPosition: $fromPosition, toPosition: $toPosition") - } + log.v { "onMoved, fromPosition: $fromPosition, toPosition: $toPosition" } notifyPageItemMoved(page, fromPosition, toPosition) } override fun onInserted(position: Int, count: Int) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("DiffDispatcher ${hashCode()} onInserted, position: $position, count: $count") - } + log.v { "onInserted, position: $position, count: $count" } notifyPageItemRangeInserted(page, position, count) } override fun onRemoved(position: Int, count: Int) { - if (ElementsLogger.verbose()) { - ElementsLogger.v("DiffDispatcher ${hashCode()} onRemoved, position: $position, count: $count") - } + log.v { "onRemoved, position: $position, count: $count" } notifyPageItemRangeRemoved(page, position, count) } } diff --git a/library/src/main/kotlin/com/otaliastudios/elements/Pager.kt b/library/src/main/kotlin/com/otaliastudios/elements/Pager.kt index f2ad528..aaa66d4 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/Pager.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/Pager.kt @@ -1,12 +1,18 @@ package com.otaliastudios.elements -abstract class Pager { +public abstract class Pager { internal lateinit var adapter: Adapter - abstract fun onElementBound(page: Page, element: Element<*>, presenter: Presenter<*>, absolutePosition: Int, pagePosition: Int) + public abstract fun onElementBound( + page: Page, + element: Element<*>, + presenter: Presenter<*>, + absolutePosition: Int, + pagePosition: Int + ) - fun requestPage() { + public fun requestPage() { adapter.requestPage() } } \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/Presenter.kt b/library/src/main/kotlin/com/otaliastudios/elements/Presenter.kt index 128c7d4..994f2f2 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/Presenter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/Presenter.kt @@ -10,6 +10,7 @@ import android.view.View import android.view.ViewGroup import android.view.ViewPropertyAnimator import androidx.databinding.ViewDataBinding +import androidx.lifecycle.Lifecycle import com.otaliastudios.elements.extensions.* /** @@ -35,14 +36,14 @@ public abstract class Presenter( * Each [Presenter] implements the [LifecycleOwner] interface, * although this was not needed until now. */ - override fun getLifecycle() = owner.lifecycle + override fun getLifecycle(): Lifecycle = owner.lifecycle /** * Returns the adapter that this presenter was bound to. * For this reason, you shouldn't bind a presenter to more than * one adapter. */ - protected fun getAdapter() = adapter + protected fun getAdapter(): Adapter = adapter /** * Returns a legit layout inflater to be used when @@ -186,14 +187,14 @@ public abstract class Presenter( * Sets an object to be held by this Holder. Might be a view, * or any other object you need to attach. */ - public fun set(key: String, value: Any?) { map[key] = value } + public operator fun set(key: String, value: Any?) { map[key] = value } /** * Returns an object that was previously attached to this * holder using [set]. */ @Suppress("UNCHECKED_CAST") - public fun get(key: String): T = map[key] as T + public operator fun get(key: String): T = map[key] as T /** * Shorthand for itemViewType. @@ -202,48 +203,76 @@ public abstract class Presenter( } - companion object { + @Suppress("unused") + public companion object { /** * Creates a [SimplePresenter] with Kotlin-friendly syntax, * and restricted functionality. Extend the class for more freedom. */ - fun simple(context: Context, layoutRes: Int, elementType: Int, bind: ((View, T) -> Unit)? = null) = SimplePresenter(context, layoutRes, elementType, bind) + public fun simple( + context: Context, + layoutRes: Int, + elementType: Int, + bind: ((View, T) -> Unit)? = null + ): Presenter = SimplePresenter(context, layoutRes, elementType, bind) /** * Creates a [ErrorPresenter] with Kotlin-friendly syntax, * and restricted functionality. Extend the class for more freedom. */ - fun forErrorIndicator(context: Context, layoutRes: Int, bind: ((View, Exception) -> Unit)? = null) = ErrorPresenter(context, layoutRes, bind) + public fun forErrorIndicator( + context: Context, + layoutRes: Int, + bind: ((View, Exception) -> Unit)? = null + ): Presenter = ErrorPresenter(context, layoutRes, bind) /** * Creates a [EmptyPresenter] with Kotlin-friendly syntax, * and restricted functionality. Extend the class for more freedom. */ - fun forEmptyIndicator(context: Context, layoutRes: Int) = EmptyPresenter(context, layoutRes) + public fun forEmptyIndicator( + context: Context, + layoutRes: Int + ): Presenter = EmptyPresenter(context, layoutRes) /** * Creates a [LoadingPresenter] with Kotlin-friendly syntax, * and restricted functionality. Extend the class for more freedom. */ - fun forLoadingIndicator(context: Context, layoutRes: Int, bind: ((View) -> Unit)? = null) = LoadingPresenter(context, layoutRes, bind) + public fun forLoadingIndicator( + context: Context, + layoutRes: Int, + bind: ((View) -> Unit)? = null + ): Presenter = LoadingPresenter(context, layoutRes, bind) /** * Creates a [PaginationPresenter] with Kotlin-friendly syntax, * and restricted functionality. Extend the class for more freedom. */ - fun forPagination(context: Context, layoutRes: Int) = PaginationPresenter(context, layoutRes) + public fun forPagination( + context: Context, + layoutRes: Int + ): Presenter = PaginationPresenter(context, layoutRes) /** * Creates a [DividerPresenter] to display dividers. */ - fun forDividers(context: Context, layoutRes: Int) = DividerPresenter(context, layoutRes) + public fun forDividers( + context: Context, + layoutRes: Int + ): Presenter = DividerPresenter(context, layoutRes) /** * Creates a [DataBindingPresenter] with Kotlin-friendly syntax, * and restricted functionality. Extend the class for more freedom. */ - fun withDataBinding(context: Context, elementType: Int, factory: (LayoutInflater, ViewGroup) -> D, bind: (D, T) -> Unit) = object : DataBindingPresenter(context) { + public fun withDataBinding( + context: Context, + elementType: Int, + factory: (LayoutInflater, ViewGroup) -> D, + bind: (D, T) -> Unit + ): Presenter = object : DataBindingPresenter(context) { override val elementTypes = listOf(elementType) diff --git a/library/src/main/kotlin/com/otaliastudios/elements/Source.kt b/library/src/main/kotlin/com/otaliastudios/elements/Source.kt index 46d59cc..d3af64a 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/Source.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/Source.kt @@ -27,7 +27,7 @@ import com.otaliastudios.elements.extensions.DividerSource * * @param T the model type */ -abstract class Source { +public abstract class Source { private val map: MutableMap = mutableMapOf() private val keys: MutableMap> = mutableMapOf() @@ -267,7 +267,7 @@ abstract class Source { * have the same contents. If they haven't, a 'item changed' animation will be performed. * This defaults to standard equality. */ - public open fun areContentsTheSame(first: T, second: T) = first == second + public open fun areContentsTheSame(first: T, second: T): Boolean = first == second /** * Returns the change payload in the case the adapter identified a 'change' operation, @@ -328,7 +328,7 @@ abstract class Source { * @property values the list of elements provided by the source * @property error an error provided by the source */ - public class Result internal constructor(val values: List>, val error: Exception? = null) + public class Result internal constructor(public val values: List>, public val error: Exception? = null) private inner class ResultProvider(private val page: Page): MediatorLiveData>>() { @@ -373,27 +373,28 @@ abstract class Source { adapters.remove(adapter) } - companion object { + @Suppress("unused") + public companion object { /** * Creates a simple [ListSource] that displays a list in a single page. */ - fun fromList(list: List, elementType: Int = 0) = ListSource(list, elementType) + public fun fromList(list: List, elementType: Int = 0): Source = ListSource(list, elementType) /** * Creates a simple [LiveDataSource] that displays results from a [LiveData] * object in a single page. */ - fun fromLiveData(data: LiveData>, elementType: Int = 0) = LiveDataSource(data, elementType) + public fun fromLiveData(data: LiveData>, elementType: Int = 0): Source = LiveDataSource(data, elementType) /** * Creates a [PaginationSource]. */ - fun forPagination(dependency: Source<*>) = PaginationSource({ it == dependency }) + public fun forPagination(dependency: Source<*>): Source<*> = PaginationSource({ it == dependency }) /** * Creates a [DividerSource]. */ - fun forDividers(dependency: Source<*>) = DividerSource({ it == dependency }) + public fun forDividers(dependency: Source<*>): Source<*> = DividerSource({ it == dependency }) } } \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/DataBindingPresenter.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/DataBindingPresenter.kt index bdbb1ec..8751bd7 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/DataBindingPresenter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/DataBindingPresenter.kt @@ -19,16 +19,16 @@ import com.otaliastudios.elements.Presenter * @property context an activity context * @property onElementClick a click listener */ -abstract class DataBindingPresenter( +public abstract class DataBindingPresenter( context: Context, onElementClick: ((Page, Holder, Element) -> Unit)? = null ) : Presenter(context, onElementClick) { final override fun onCreate(parent: ViewGroup, elementType: Int): Holder { val binding = onCreateBinding(parent, elementType) - binding.setLifecycleOwner(this) + binding.lifecycleOwner = this val holder = Holder(binding.root) - holder.set("binding", binding) + holder["binding"] = binding lifecycle.addObserver(object: LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/DividerPresenter.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/DividerPresenter.kt index f5e990f..753a1c6 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/DividerPresenter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/DividerPresenter.kt @@ -14,12 +14,12 @@ import com.otaliastudios.elements.Presenter * A [DividerPresenter] deals with [DividerSource.ELEMENT_TYPE] elements that are * provided by a [DividerSource]. */ -open class DividerPresenter( +public open class DividerPresenter( context: Context, private val layout: Int -) : Presenter(context) { +) : Presenter(context) { - override val elementTypes = listOf(DividerSource.ELEMENT_TYPE) + override val elementTypes: List = listOf(DividerSource.ELEMENT_TYPE) override fun onCreate(parent: ViewGroup, elementType: Int): Holder { return Holder(getLayoutInflater().inflate(layout, parent, false)) diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/DividerSource.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/DividerSource.kt index d17bcb8..ec48902 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/DividerSource.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/DividerSource.kt @@ -10,21 +10,21 @@ import com.otaliastudios.elements.Source * * The divider is inserted before each item, except the first item of the first page. */ -class DividerSource(private val dependsOn: ((Source<*>) -> Boolean) = { true }) : Source() { +public class DividerSource(private val dependsOn: ((Source<*>) -> Boolean) = { true }) : Source() { - companion object { + public companion object { /** * The type for the element that we provide. */ - const val ELEMENT_TYPE = 1934812 + public const val ELEMENT_TYPE: Int = 1934812 } override fun dependsOn(source: Source<*>): Boolean { return dependsOn.invoke(source) } - override fun getElementType(data: Any) = ELEMENT_TYPE + override fun getElementType(data: Any): Int = ELEMENT_TYPE override fun areItemsTheSame(first: Any, second: Any): Boolean { return first == second diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/EmptyPresenter.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/EmptyPresenter.kt index 08a145d..66a4721 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/EmptyPresenter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/EmptyPresenter.kt @@ -12,12 +12,12 @@ import com.otaliastudios.elements.Presenter * * @property layout the layout resource to be inflated. */ -open class EmptyPresenter( +public open class EmptyPresenter( context: Context, private val layout: Int -) : Presenter(context) { +) : Presenter(context) { - override val elementTypes = listOf(MainSource.ELEMENT_TYPE_EMPTY) + override val elementTypes: List = listOf(MainSource.ELEMENT_TYPE_EMPTY) override fun onCreate(parent: ViewGroup, elementType: Int): Holder { return Holder(getLayoutInflater().inflate(layout, parent, false)) diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/ErrorPresenter.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/ErrorPresenter.kt index 2de66ef..7df74b1 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/ErrorPresenter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/ErrorPresenter.kt @@ -17,19 +17,19 @@ import com.otaliastudios.elements.Presenter * @property layout the layout resource to be inflated. * @property bind what to do when binding the error view. */ -open class ErrorPresenter( +public open class ErrorPresenter( context: Context, private val layout: Int, private val bind: ((View, Exception) -> Unit)? = null -) : Presenter(context) { +) : Presenter(context) { - override val elementTypes = listOf(MainSource.ELEMENT_TYPE_ERROR) + override val elementTypes: List = listOf(MainSource.ELEMENT_TYPE_ERROR) override fun onCreate(parent: ViewGroup, elementType: Int): Holder { return Holder(getLayoutInflater().inflate(layout, parent, false)) } - override fun onBind(page: Page, holder: Holder, element: Element) { + override fun onBind(page: Page, holder: Holder, element: Element) { super.onBind(page, holder, element) bind?.invoke(holder.itemView, element.extra as Exception) } diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/FooterSource.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/FooterSource.kt index 63c7b99..f768bca 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/FooterSource.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/FooterSource.kt @@ -18,7 +18,7 @@ import com.otaliastudios.elements.Source * @param Anchor the type for the dependency list * @param Footer the type for the footer list (most frequently, a String or Date) */ -abstract class FooterSource : Source>() { +public abstract class FooterSource : Source>() { /** * A [Data] object will be passed to presenters to have access to both the anchor @@ -48,7 +48,7 @@ abstract class FooterSource : Source) = ELEMENT_TYPE + override fun getElementType(data: Data): Int = ELEMENT_TYPE /** * Implement to extract footers and anchors from the list of dependency data. @@ -57,12 +57,12 @@ abstract class FooterSource : Source): List> - companion object { + public companion object { /** * The element type used by this source. * Can be changed by overriding [getElementType]. */ - public const val ELEMENT_TYPE = 127831783 + public const val ELEMENT_TYPE: Int = 127831783 } } \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/HeaderSource.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/HeaderSource.kt index f3a0851..2490320 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/HeaderSource.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/HeaderSource.kt @@ -19,7 +19,7 @@ import com.otaliastudios.elements.extensions.FooterSource.Data * @param Anchor the type for the dependency list * @param Header the type for the header list (most frequently, a String or Date) */ -abstract class HeaderSource() : Source>() { +public abstract class HeaderSource() : Source>() { /** * A [Data] object will be passed to presenters to have access to both the anchor @@ -28,7 +28,7 @@ abstract class HeaderSource() : Source(val anchor: Anchor, val header: Header) + public data class Data(val anchor: Anchor, val header: Header) override fun onPageOpened(page: Page, dependencies: List>) { onPageChanged(page, dependencies) @@ -49,7 +49,7 @@ abstract class HeaderSource() : Source) = ELEMENT_TYPE + override fun getElementType(data: Data): Int = ELEMENT_TYPE /** * Implement to extract headers and anchors from the list of dependency data. @@ -58,12 +58,12 @@ abstract class HeaderSource() : Source): List> - companion object { + public companion object { /** * The element type used by this source. * Can be changed by overriding [getElementType]. */ - public const val ELEMENT_TYPE = 127831782 + public const val ELEMENT_TYPE: Int = 127831782 } } \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/ListSource.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/ListSource.kt index 8906654..241c6c3 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/ListSource.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/ListSource.kt @@ -8,9 +8,9 @@ import com.otaliastudios.elements.Source * A source that already has results inside a list and will display them * in a single page. */ -open class ListSource(private val list: List, private val elementType: Int = 0) : Source() { +public open class ListSource(private val list: List, private val elementType: Int = 0) : Source() { - override fun dependsOn(source: Source<*>) = false + override fun dependsOn(source: Source<*>): Boolean = false override fun onPageOpened(page: Page, dependencies: List>) { if (page.isFirstPage()) { @@ -18,7 +18,7 @@ open class ListSource(private val list: List, private val elementType } } - override fun getElementType(data: T) = elementType + override fun getElementType(data: T): Int = elementType override fun areItemsTheSame(first: T, second: T): Boolean { return false diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/LiveDataSource.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/LiveDataSource.kt index f7b5fb1..f96d856 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/LiveDataSource.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/LiveDataSource.kt @@ -10,7 +10,7 @@ import com.otaliastudios.elements.Source * A source that provides results through a LiveData object and will display them * in a single page. */ -open class LiveDataSource(private val data: LiveData>, private val elementType: Int = 0) : Source() { +public open class LiveDataSource(private val data: LiveData>, private val elementType: Int = 0) : Source() { override fun onPageOpened(page: Page, dependencies: List>) { if (page.previous() == null) { @@ -18,9 +18,9 @@ open class LiveDataSource(private val data: LiveData>, private v } } - override fun dependsOn(source: Source<*>) = false + override fun dependsOn(source: Source<*>): Boolean = false - override fun getElementType(data: T) = elementType + override fun getElementType(data: T): Int = elementType override fun areItemsTheSame(first: T, second: T): Boolean { return first == second diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/LoadingPresenter.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/LoadingPresenter.kt index 04132fc..bbc09ab 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/LoadingPresenter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/LoadingPresenter.kt @@ -17,19 +17,19 @@ import com.otaliastudios.elements.Presenter * @property layout the layout resource to be inflated. * @property bind what to do when binding the loading view. */ -open class LoadingPresenter( +public open class LoadingPresenter( context: Context, private val layout: Int, private val bind: ((View) -> Unit)? = null -) : Presenter(context) { +) : Presenter(context) { - override val elementTypes = listOf(MainSource.ELEMENT_TYPE_LOADING) + override val elementTypes: List = listOf(MainSource.ELEMENT_TYPE_LOADING) override fun onCreate(parent: ViewGroup, elementType: Int): Holder { return Holder(getLayoutInflater().inflate(layout, parent, false)) } - override fun onBind(page: Page, holder: Holder, element: Element) { + override fun onBind(page: Page, holder: Holder, element: Element) { super.onBind(page, holder, element) bind?.invoke(holder.itemView) } diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/MainSource.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/MainSource.kt index a271564..e8883eb 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/MainSource.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/MainSource.kt @@ -19,30 +19,30 @@ import com.otaliastudios.elements.Source * @property errorIndicatorEnabled whether we should emit an error indicator when the source posts an error * @property emptyIndicatorEnabled whether we should emit an empty indicator when the source posts an empty list */ -abstract class MainSource( +public abstract class MainSource( protected var loadingIndicatorsEnabled: Boolean = true, protected var errorIndicatorEnabled: Boolean = true, protected var emptyIndicatorEnabled: Boolean = true ) : Source() { - companion object { + public companion object { /** * Constant for the loading element that is provided during * data fetching. */ - const val ELEMENT_TYPE_LOADING = 1936817 + public const val ELEMENT_TYPE_LOADING: Int = 1936817 /** * Constant for the empty element that is provided when the * list for the first page was empty. */ - const val ELEMENT_TYPE_EMPTY = 1936818 + public const val ELEMENT_TYPE_EMPTY: Int = 1936818 /** * Constant for the error element that is provided when the * list for the first page gave an Exception. */ - const val ELEMENT_TYPE_ERROR = 1936819 + public const val ELEMENT_TYPE_ERROR: Int = 1936819 } @CallSuper diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/PaginationPresenter.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/PaginationPresenter.kt index d96a90e..75447e7 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/PaginationPresenter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/PaginationPresenter.kt @@ -13,12 +13,12 @@ import com.otaliastudios.elements.Presenter * * This must be used with an unbound adapter, so we can manage the page creation ourselves. */ -open class PaginationPresenter( +public open class PaginationPresenter( context: Context, private val layout: Int -) : Presenter(context) { +) : Presenter(context) { - override val elementTypes = listOf(PaginationSource.ELEMENT_TYPE) + override val elementTypes: List = listOf(PaginationSource.ELEMENT_TYPE) override fun onCreate(parent: ViewGroup, elementType: Int): Holder { return Holder(getLayoutInflater().inflate(layout, parent, false)) diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/PaginationSource.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/PaginationSource.kt index bd4ce28..2278ac4 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/PaginationSource.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/PaginationSource.kt @@ -11,21 +11,21 @@ import com.otaliastudios.elements.Source * This is meant to be used with unbound adapters in conjunction with [PaginationPresenter]. * The provided element should be rendered as a 'Load more...' button. */ -class PaginationSource(private val dependsOn: ((Source<*>) -> Boolean) = { true }) : Source() { +public class PaginationSource(private val dependsOn: ((Source<*>) -> Boolean) = { true }) : Source() { - companion object { + public companion object { /** * The type for the element that we provide. */ - const val ELEMENT_TYPE = 1933811 + public const val ELEMENT_TYPE: Int = 1933811 } override fun dependsOn(source: Source<*>): Boolean { return dependsOn.invoke(source) } - override fun getElementType(data: Any) = ELEMENT_TYPE + override fun getElementType(data: Any): Int = ELEMENT_TYPE override fun areItemsTheSame(first: Any, second: Any): Boolean { return first == second diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/SimplePresenter.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/SimplePresenter.kt index f149f16..7621496 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/SimplePresenter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/SimplePresenter.kt @@ -16,14 +16,14 @@ import com.otaliastudios.elements.Presenter * * See also [Presenter.simple]. */ -open class SimplePresenter( +public open class SimplePresenter( context: Context, private val layout: Int, private val elementType: Int, private val bind: ((View, T) -> Unit)? ) : Presenter(context) { - override val elementTypes = listOf(elementType) + override val elementTypes: List = listOf(elementType) override fun onCreate(parent: ViewGroup, elementType: Int): Holder { return Holder(getLayoutInflater().inflate(layout, parent, false)) diff --git a/library/src/main/kotlin/com/otaliastudios/elements/extensions/KotlinExtensions.kt b/library/src/main/kotlin/com/otaliastudios/elements/extensions/extensions.kt similarity index 68% rename from library/src/main/kotlin/com/otaliastudios/elements/extensions/KotlinExtensions.kt rename to library/src/main/kotlin/com/otaliastudios/elements/extensions/extensions.kt index b4d004b..4e8d707 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/extensions/KotlinExtensions.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/extensions/extensions.kt @@ -8,18 +8,19 @@ import com.otaliastudios.elements.Presenter import com.otaliastudios.elements.Source +@Suppress("unused") public fun RecyclerView.setAdapter(lifecycleOwner: LifecycleOwner, block: (Adapter.Builder.() -> Unit)) { Adapter.Builder(lifecycleOwner).apply(block).into(this) } -operator fun Adapter.Builder.plus(pager: Pager): Adapter.Builder { +public operator fun Adapter.Builder.plus(pager: Pager): Adapter.Builder { return setPager(pager) } -operator fun Adapter.Builder.plus(source: Source<*>): Adapter.Builder { +public operator fun Adapter.Builder.plus(source: Source<*>): Adapter.Builder { return addSource(source) } -operator fun Adapter.Builder.plus(presenter: Presenter<*>): Adapter.Builder { +public operator fun Adapter.Builder.plus(presenter: Presenter<*>): Adapter.Builder { return addPresenter(presenter) } \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/pagers/NoPagesPager.kt b/library/src/main/kotlin/com/otaliastudios/elements/pagers/NoPagesPager.kt deleted file mode 100644 index 7924581..0000000 --- a/library/src/main/kotlin/com/otaliastudios/elements/pagers/NoPagesPager.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.otaliastudios.elements.pagers - -import com.otaliastudios.elements.Element -import com.otaliastudios.elements.Page -import com.otaliastudios.elements.Pager -import com.otaliastudios.elements.Presenter - -open class NoPagesPager(): Pager() { - - override fun onElementBound(page: Page, element: Element<*>, presenter: Presenter<*>, absolutePosition: Int, pagePosition: Int) {} -} \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/pagers/PageFractionPager.kt b/library/src/main/kotlin/com/otaliastudios/elements/pagers/PageFractionPager.kt deleted file mode 100644 index 21566bd..0000000 --- a/library/src/main/kotlin/com/otaliastudios/elements/pagers/PageFractionPager.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.otaliastudios.elements.pagers - -open class PageFractionPager( - val expectedPageSize: Int, - val fraction: Float -): PageSizePager((expectedPageSize * fraction).toInt()) \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/pagers/PageSizePager.kt b/library/src/main/kotlin/com/otaliastudios/elements/pagers/PageSizePager.kt deleted file mode 100644 index c5b5a51..0000000 --- a/library/src/main/kotlin/com/otaliastudios/elements/pagers/PageSizePager.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.otaliastudios.elements.pagers - -import com.otaliastudios.elements.Element -import com.otaliastudios.elements.Page -import com.otaliastudios.elements.Pager -import com.otaliastudios.elements.Presenter - -open class PageSizePager(val pageSize: Int): Pager() { - - override fun onElementBound(page: Page, element: Element<*>, presenter: Presenter<*>, absolutePosition: Int, pagePosition: Int) { - if (pagePosition == maxOf(pageSize - 1, 0) && page.isLastPage()) { - requestPage() - } - } -} \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/pagers/SourceResultsPager.kt b/library/src/main/kotlin/com/otaliastudios/elements/pagers/SourceResultsPager.kt deleted file mode 100644 index 3b8c5b1..0000000 --- a/library/src/main/kotlin/com/otaliastudios/elements/pagers/SourceResultsPager.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.otaliastudios.elements.pagers - -import com.otaliastudios.elements.* -import com.otaliastudios.elements.extensions.MainSource - -open class SourceResultsPager( - val fraction: Float = 0.8F, - val selector: (source: Source<*>, elementType: Int) -> Boolean = { source, elementType -> - source is MainSource - && elementType != MainSource.ELEMENT_TYPE_LOADING - && elementType != MainSource.ELEMENT_TYPE_EMPTY - && elementType != MainSource.ELEMENT_TYPE_ERROR - }): Pager() { - - override fun onElementBound(page: Page, element: Element<*>, presenter: Presenter<*>, absolutePosition: Int, pagePosition: Int) { - val source = element.source - if (page.isLastPage() && selector(source, element.type) && source.hasResultsForPage(page)) { - val sourceResults = source.getResultsForPage(page) - val sourcePosition = sourceResults.indexOfFirst { it === element } - ElementsLogger.v("SourceResultsPager: source matches and has ${sourceResults.size} results. " + - "The position of this element is $sourcePosition. (in page: $pagePosition, absolute: $absolutePosition, data: ${element.data})") - if (sourcePosition >= 0) { - val trigger = (sourceResults.size * fraction).toInt() - ElementsLogger.w("SourceResultsPager: comparing with ${trigger - 1}") - if (sourcePosition == maxOf(trigger - 1, 0)) { - requestPage() - } - } - } - } -} \ No newline at end of file diff --git a/library/src/main/kotlin/com/otaliastudios/elements/pagers/pagers.kt b/library/src/main/kotlin/com/otaliastudios/elements/pagers/pagers.kt new file mode 100644 index 0000000..3670850 --- /dev/null +++ b/library/src/main/kotlin/com/otaliastudios/elements/pagers/pagers.kt @@ -0,0 +1,67 @@ +@file:Suppress("unused") + +package com.otaliastudios.elements.pagers + +import com.otaliastudios.elements.* +import com.otaliastudios.elements.extensions.MainSource + +public open class NoPagesPager: Pager() { + override fun onElementBound( + page: Page, + element: Element<*>, + presenter: Presenter<*>, + absolutePosition: Int, + pagePosition: Int + ): Unit = Unit +} + +public open class PageFractionPager( + expectedPageSize: Int, + fraction: Float +): PageSizePager((expectedPageSize * fraction).toInt()) + +public open class PageSizePager(private val pageSize: Int): Pager() { + + override fun onElementBound( + page: Page, + element: Element<*>, + presenter: Presenter<*>, + absolutePosition: Int, + pagePosition: Int + ) { + if (pagePosition == maxOf(pageSize - 1, 0) && page.isLastPage()) { + requestPage() + } + } +} + +public open class SourceResultsPager( + private val fraction: Float = 0.8F, + private val selector: (source: Source<*>, elementType: Int) -> Boolean = { source, elementType -> + source is MainSource + && elementType != MainSource.ELEMENT_TYPE_LOADING + && elementType != MainSource.ELEMENT_TYPE_EMPTY + && elementType != MainSource.ELEMENT_TYPE_ERROR + } +): Pager() { + + private val log = ElementsLogger("SourceResultsPager") + + override fun onElementBound(page: Page, element: Element<*>, presenter: Presenter<*>, absolutePosition: Int, pagePosition: Int) { + val source = element.source + if (page.isLastPage() && selector(source, element.type) && source.hasResultsForPage(page)) { + val sourceResults = source.getResultsForPage(page) + val sourcePosition = sourceResults.indexOfFirst { it === element } + log.v { "source matches and has ${sourceResults.size} results. " + + "The position of this element is $sourcePosition. " + + "(in page: $pagePosition, absolute: $absolutePosition, data: ${element.data})" } + if (sourcePosition >= 0) { + val trigger = (sourceResults.size * fraction).toInt() + log.v { "comparing with ${trigger - 1}" } + if (sourcePosition == maxOf(trigger - 1, 0)) { + requestPage() + } + } + } + } +} \ No newline at end of file