├── sectionsDecorator ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ ├── values │ │ │ └── dimens.xml │ │ └── layout │ │ │ └── section_header.xml │ │ └── java │ │ └── co │ │ └── netguru │ │ └── sectionsDecorator │ │ ├── SectionAdapterInterface.kt │ │ ├── Extentions.kt │ │ ├── VerticalPainter.kt │ │ ├── HorizontalPainter.kt │ │ ├── Painter.kt │ │ └── SectionDecorator.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── app ├── src │ ├── test │ │ └── resources │ │ │ └── mockito-extensions │ │ │ └── org.mockito.plugins.MockMaker │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── drawable │ │ │ │ ├── splash_background.xml │ │ │ │ ├── ic_panorama_horizontal_black_24dp.xml │ │ │ │ └── ic_panorama_vertical_black_24dp.xml │ │ │ ├── layout │ │ │ │ ├── list_item_vertical.xml │ │ │ │ ├── list_item_horizontal.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── fragment_vertical.xml │ │ │ │ └── fragment_horizontal.xml │ │ │ └── menu │ │ │ │ └── bottom_navigation_main.xml │ │ ├── ic_launcher-web.png │ │ ├── kotlin │ │ │ └── co │ │ │ │ └── netguru │ │ │ │ └── android │ │ │ │ └── sectionsDecoratorDemo │ │ │ │ ├── application │ │ │ │ ├── scope │ │ │ │ │ ├── AppScope.kt │ │ │ │ │ ├── ActivityScope.kt │ │ │ │ │ └── FragmentScope.kt │ │ │ │ ├── ApplicationModule.kt │ │ │ │ ├── ApplicationComponent.kt │ │ │ │ ├── App.kt │ │ │ │ └── RxJavaErrorHandler.kt │ │ │ │ ├── common │ │ │ │ ├── extensions │ │ │ │ │ ├── SharedPreferencesExtensions.kt │ │ │ │ │ ├── ActivityExtentions.kt │ │ │ │ │ ├── ContextExtensions.kt │ │ │ │ │ └── RxExtensions.kt │ │ │ │ └── Optional.kt │ │ │ │ └── feature │ │ │ │ ├── splash │ │ │ │ └── SplashActivity.kt │ │ │ │ └── demo │ │ │ │ ├── HorizontalFragment.kt │ │ │ │ ├── VerticalFragment.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ └── Adapter.kt │ │ └── AndroidManifest.xml │ ├── release │ │ └── kotlin │ │ │ └── co │ │ │ └── netguru │ │ │ └── android │ │ │ └── sectionsDecoratorDemo │ │ │ └── application │ │ │ ├── RxJavaErrorHandlerImpl.kt │ │ │ └── DebugMetricsHelper.kt │ └── debug │ │ └── kotlin │ │ └── co │ │ └── netguru │ │ └── android │ │ └── sectionsDecoratorDemo │ │ └── application │ │ ├── RxJavaErrorHandlerImpl.kt │ │ └── DebugMetricsHelper.kt ├── proguard-rules-test.pro ├── proguard-rules.pro └── build.gradle ├── images ├── example1.gif └── example2.gif ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── vcs.xml ├── codeStyles │ └── Project.xml └── misc.xml ├── LICENSE ├── codecov.yml ├── README.md ├── gradlew.bat ├── .gitignore ├── gradlew └── default-detekt-config.yml /sectionsDecorator/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':sectionsDecorator' 2 | -------------------------------------------------------------------------------- /app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /images/example1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/images/example1.gif -------------------------------------------------------------------------------- /images/example2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/images/example2.gif -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sectionsDecorator/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/sections-decorator-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/application/scope/AppScope.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application.scope 2 | 3 | import javax.inject.Scope 4 | 5 | @Scope 6 | annotation class AppScope 7 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/application/scope/ActivityScope.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application.scope 2 | 3 | import javax.inject.Scope 4 | 5 | @Scope 6 | annotation class ActivityScope 7 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/application/scope/FragmentScope.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application.scope 2 | 3 | import javax.inject.Scope 4 | 5 | @Scope 6 | annotation class FragmentScope 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sections decorator 4 | Vertical 5 | Horizontal 6 | 7 | -------------------------------------------------------------------------------- /sectionsDecorator/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24dp 4 | 24dp 5 | 1dp 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 11 10:02:25 CEST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /app/proguard-rules-test.pro: -------------------------------------------------------------------------------- 1 | # Additional proguard rules for instrumentation testing 2 | 3 | -keep class rx.plugins.** { *; } 4 | -keep class org.junit.** { *; } 5 | -keep class co.netguru.android.testcommons.** { *; } 6 | -keep class android.support.test.espresso.** { *; } 7 | -dontwarn org.hamcrest.** 8 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sectionsDecorator/src/main/java/co/netguru/sectionsDecorator/SectionAdapterInterface.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.sectionsDecorator 2 | 3 | interface SectionsAdapterInterface { 4 | fun getSectionsCount() : Int 5 | fun getSectionTitleAt(sectionIndex: Int): String 6 | fun getItemCountForSection(sectionIndex: Int): Int 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #d81b60 6 | #FFFFFF 7 | #5FCF6F 8 | 9 | -------------------------------------------------------------------------------- /sectionsDecorator/src/main/res/layout/section_header.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/common/extensions/SharedPreferencesExtensions.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.common.extensions 2 | 3 | import android.content.SharedPreferences 4 | 5 | inline fun SharedPreferences.edit(action: SharedPreferences.Editor.() -> Unit) { 6 | val editor = edit() 7 | editor.action() 8 | editor.apply() 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/application/ApplicationModule.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application 2 | 3 | import co.netguru.android.sectionsDecoratorDemo.application.scope.AppScope 4 | import dagger.Module 5 | import dagger.Provides 6 | 7 | @Module 8 | class ApplicationModule { 9 | 10 | @AppScope 11 | @Provides 12 | fun rxJavaErrorHandler(): RxJavaErrorHandler = RxJavaErrorHandlerImpl() 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Netguru 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/feature/splash/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.feature.splash 2 | 3 | import android.os.Bundle 4 | import android.support.v7.app.AppCompatActivity 5 | import co.netguru.android.sectionsDecoratorDemo.common.extensions.startActivity 6 | import co.netguru.android.sectionsDecoratorDemo.feature.demo.DemoActivity 7 | 8 | class SplashActivity : AppCompatActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | startActivity() 12 | finish() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item_vertical.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/common/extensions/ActivityExtentions.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.common.extensions 2 | 3 | import android.support.annotation.IdRes 4 | import android.support.v4.app.Fragment 5 | import android.support.v4.app.FragmentManager 6 | 7 | fun FragmentManager.replaceFragment( 8 | @IdRes container: Int, 9 | tag: String, 10 | createFragment: () -> Fragment 11 | ) { 12 | val foundFragment = findFragmentByTag(tag) ?: createFragment() 13 | val transaction = beginTransaction() 14 | transaction.replace(container, foundFragment) 15 | transaction.commit() 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item_horizontal.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/application/ApplicationComponent.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application 2 | 3 | import co.netguru.android.sectionsDecoratorDemo.application.scope.AppScope 4 | import dagger.Component 5 | import dagger.android.AndroidInjector 6 | import dagger.android.support.AndroidSupportInjectionModule 7 | 8 | @AppScope 9 | @Component( 10 | modules = [ 11 | AndroidSupportInjectionModule::class, 12 | ApplicationModule::class 13 | ] 14 | ) 15 | internal interface ApplicationComponent : AndroidInjector { 16 | @Component.Builder 17 | abstract class Builder : AndroidInjector.Builder() 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_navigation_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/release/kotlin/co/netguru/android/sectionsDecoratorDemo/application/RxJavaErrorHandlerImpl.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application 2 | 3 | import io.reactivex.exceptions.UndeliverableException 4 | import timber.log.Timber 5 | 6 | class RxJavaErrorHandlerImpl : RxJavaErrorHandler() { 7 | 8 | override fun handleUndeliverableException(undeliverableException: UndeliverableException) { 9 | //TODO - decide whether this should be logged and passed or not to used crash reporter 10 | //often occurring might be indication of some problem in library or our codebase but definitely shouldn't crash the app 11 | //Crashlytics.logException(undeliverableException) 12 | Timber.e(undeliverableException) 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/release/kotlin/co/netguru/android/sectionsDecoratorDemo/application/DebugMetricsHelper.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application 2 | 3 | import android.content.Context 4 | import co.netguru.android.sectionsDecoratorDemo.application.scope.AppScope 5 | import javax.inject.Inject 6 | 7 | /** 8 | * Helper class that initializes a set of debugging tools 9 | * for the debug build type and register crash manager for release type. 10 | * ## Debug type tools: 11 | * - AndroidDevMetrics 12 | * - Stetho 13 | * - StrictMode 14 | * - LeakCanary 15 | * - Timber 16 | * 17 | * ## Release type tools: 18 | * - CrashManager 19 | */ 20 | @AppScope 21 | class DebugMetricsHelper @Inject constructor() { 22 | 23 | internal fun init(context: Context) = Unit 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/debug/kotlin/co/netguru/android/sectionsDecoratorDemo/application/RxJavaErrorHandlerImpl.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application 2 | 3 | import io.reactivex.exceptions.UndeliverableException 4 | 5 | class RxJavaErrorHandlerImpl : RxJavaErrorHandler() { 6 | 7 | override fun handleUndeliverableException(undeliverableException: UndeliverableException) { 8 | /** 9 | * Crash the app while in debug as undeliverable exception can sometimes be indication of 10 | * bug's that could have been prevented. Check if disposables/cancelables are set properly 11 | * on emitter or backup with tryOnError from emitter if that's not possible. 12 | */ 13 | undeliverableException.printStackTrace() 14 | uncaught(undeliverableException) 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/application/App.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application 2 | 3 | import dagger.android.AndroidInjector 4 | import dagger.android.support.DaggerApplication 5 | import io.reactivex.plugins.RxJavaPlugins 6 | import javax.inject.Inject 7 | 8 | class App : DaggerApplication() { 9 | 10 | @Inject 11 | lateinit var debugMetricsHelper: DebugMetricsHelper 12 | 13 | @Inject 14 | lateinit var rxJavaErrorHandler: RxJavaErrorHandler 15 | 16 | override fun onCreate() { 17 | super.onCreate() 18 | debugMetricsHelper.init(this) 19 | RxJavaPlugins.setErrorHandler(rxJavaErrorHandler) 20 | } 21 | 22 | override fun applicationInjector(): AndroidInjector = 23 | DaggerApplicationComponent.builder().create(this) 24 | } 25 | -------------------------------------------------------------------------------- /sectionsDecorator/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_panorama_horizontal_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_panorama_vertical_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_vertical.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /sectionsDecorator/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'com.github.dcendents.android-maven' 5 | 6 | group = 'com.github.netguru' 7 | 8 | android { 9 | compileSdkVersion 27 10 | 11 | defaultConfig { 12 | minSdkVersion 17 13 | targetSdkVersion 27 14 | versionCode 1 15 | versionName "0.1.1" 16 | consumerProguardFiles 'proguard-rules.pro' 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | // TODO replace with https://issuetracker.google.com/issues/72050365 once released. 21 | libraryVariants.all { 22 | it.generateBuildConfig.enabled = false 23 | } 24 | } 25 | 26 | androidExtensions { 27 | experimental = true 28 | } 29 | 30 | dependencies { 31 | implementation libs.recyclerView 32 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" 33 | } 34 | repositories { 35 | mavenCentral() 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_horizontal.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | ci: 3 | - bitrise 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "0...100" 9 | 10 | status: 11 | project: 12 | unit: 13 | target: auto 14 | threshold: 1% 15 | flag: unit 16 | flags: 17 | - unit 18 | 19 | instrumentation: 20 | target: auto 21 | threshold: 1% 22 | flag: instrumentation 23 | flags: 24 | - instrumentation 25 | patch: 26 | unit: 27 | flag: unit 28 | flags: 29 | - unit 30 | 31 | instrumentation: 32 | flag: instrumentation 33 | flags: 34 | - instrumentation 35 | 36 | changes: 37 | unit: 38 | flag: unit 39 | flags: 40 | - unit 41 | 42 | instrumentation: 43 | flag: instrumentation 44 | flags: 45 | - instrumentation 46 | 47 | ignore: 48 | - mock/* 49 | - production/* 50 | - androidTest/* 51 | - androidTestMock/* 52 | - androidTestProduction/* 53 | 54 | comment: 55 | layout: "header, diff, changes, sunburst, uncovered" 56 | behavior: default 57 | flags: 58 | - unit 59 | - instrumentation -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/common/extensions/ContextExtensions.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.common.extensions 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.support.annotation.AttrRes 7 | import android.support.annotation.ColorRes 8 | import android.support.annotation.DrawableRes 9 | import android.support.v4.content.ContextCompat 10 | import android.util.TypedValue 11 | 12 | inline fun Context.startActivity() { 13 | val intent = Intent(this, T::class.java) 14 | startActivity(intent) 15 | } 16 | 17 | fun Context.getColorCompat(@ColorRes color: Int) = ContextCompat.getColor(this, color) 18 | 19 | fun Context.getDrawableCompat(@DrawableRes drawable: Int) = 20 | ContextCompat.getDrawable(this, drawable) 21 | 22 | fun Context.getAttributeColor(@AttrRes attrColor: Int): Int { 23 | val typedValue = TypedValue() 24 | theme.resolveAttribute(attrColor, typedValue, true) 25 | return typedValue.data 26 | } 27 | 28 | fun Context.getAttributeDrawable(@AttrRes attrDrawableRes: Int): Int { 29 | val typedValue = TypedValue() 30 | theme.resolveAttribute(attrDrawableRes, typedValue, true) 31 | return typedValue.resourceId 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/feature/demo/HorizontalFragment.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.feature.demo 2 | 3 | import android.os.Bundle 4 | import android.support.v4.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.LinearLayout 9 | import co.netguru.android.sectionsDecoratorDemo.R 10 | import co.netguru.sectionsDecorator.SectionDecorator 11 | import kotlinx.android.synthetic.main.fragment_horizontal.* 12 | 13 | class HorizontalFragment : Fragment(){ 14 | companion object { 15 | val TAG = HorizontalFragment::class.java.simpleName!! 16 | fun createFragment() = HorizontalFragment() 17 | } 18 | 19 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 20 | return inflater.inflate(R.layout.fragment_horizontal, container, false) 21 | } 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | recyclerView.adapter = MyAdapter(LinearLayout.HORIZONTAL) 26 | recyclerView.addItemDecoration(SectionDecorator(context!!).apply { 27 | setLineColor(R.color.green) 28 | setLineWidth(15f) 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/feature/demo/VerticalFragment.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.feature.demo 2 | 3 | import android.os.Bundle 4 | import android.support.v4.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.LinearLayout 9 | import co.netguru.android.sectionsDecoratorDemo.R 10 | import co.netguru.sectionsDecorator.SectionDecorator 11 | import kotlinx.android.synthetic.main.fragment_horizontal.* 12 | 13 | class VerticalFragment : Fragment() { 14 | 15 | companion object { 16 | val TAG = VerticalFragment::class.java.simpleName!! 17 | fun createFragment() = VerticalFragment() 18 | } 19 | 20 | override fun onCreateView( 21 | inflater: LayoutInflater, 22 | container: ViewGroup?, 23 | savedInstanceState: Bundle? 24 | ): View? { 25 | return inflater.inflate(R.layout.fragment_vertical, container, false) 26 | } 27 | 28 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 29 | super.onViewCreated(view, savedInstanceState) 30 | recyclerView.adapter = MyAdapter(LinearLayout.VERTICAL) 31 | recyclerView.addItemDecoration(SectionDecorator(context!!).apply { 32 | setLineColor(R.color.green) 33 | setLineWidth(15f) 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/application/RxJavaErrorHandler.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application 2 | 3 | import io.reactivex.exceptions.UndeliverableException 4 | import io.reactivex.functions.Consumer 5 | 6 | /** 7 | * [RxJava2 error handling](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling) 8 | */ 9 | abstract class RxJavaErrorHandler : Consumer { 10 | 11 | override fun accept(throwable: Throwable) = when (throwable) { 12 | is UndeliverableException -> { 13 | //we log such exceptions but avoid app crash for release as we can't do much in such case. 14 | handleUndeliverableException(throwable) 15 | } 16 | else -> { 17 | //we crash the app else - this is a bug 18 | throwable.printStackTrace() 19 | uncaught(throwable) 20 | } 21 | } 22 | 23 | /** 24 | * Something thrown error after stream finished. 25 | * If this happens often it can be indication of some problem in library or our codebase. 26 | * Make sure that source sets disposable/cancellable while creating stream. 27 | * You can also use tryOnError if the wrapped data source doesn't provide good way to cancel emissions. 28 | */ 29 | abstract fun handleUndeliverableException(undeliverableException: UndeliverableException) 30 | 31 | protected fun uncaught(throwable: Throwable) { 32 | val currentThread = Thread.currentThread() 33 | val handler = currentThread.uncaughtExceptionHandler 34 | handler.uncaughtException(currentThread, throwable) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sectionsDecorator/src/main/java/co/netguru/sectionsDecorator/Extentions.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.sectionsDecorator 2 | 3 | import android.content.Context 4 | import android.support.annotation.ColorRes 5 | import android.support.v4.content.ContextCompat 6 | import android.view.View 7 | import android.view.ViewGroup 8 | 9 | fun ViewGroup.asSequence(): Sequence = object : Sequence { 10 | 11 | override fun iterator(): Iterator = object : Iterator { 12 | private var nextValue: View? = null 13 | private var done = false 14 | private var position: Int = 0 15 | 16 | override fun hasNext(): Boolean { 17 | if (nextValue == null && !done) { 18 | nextValue = getChildAt(position) 19 | position++ 20 | if (nextValue == null) done = true 21 | } 22 | return nextValue != null 23 | } 24 | 25 | override fun next(): View { 26 | if (!hasNext()) { 27 | throw NoSuchElementException() 28 | } 29 | val answer = nextValue 30 | nextValue = null 31 | return answer!! 32 | } 33 | } 34 | } 35 | 36 | fun Context.getColorCompat(@ColorRes color: Int) = ContextCompat.getColor(this, color) 37 | 38 | fun Map>.addToValueList(key: R, element: T): Map> { 39 | val mutable = this.toMutableMap() 40 | mutable[key] = (this[key] ?: emptyList()) + element 41 | return mutable 42 | } 43 | 44 | inline fun nullCheck2(item1: A?, item2: B?, f: (A, B) -> Unit) { 45 | if (item1 != null && item2 != null) { 46 | f(item1, item2) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/common/extensions/RxExtensions.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.common.extensions 2 | 3 | import io.reactivex.* 4 | 5 | import io.reactivex.android.schedulers.AndroidSchedulers 6 | import io.reactivex.schedulers.Schedulers 7 | 8 | fun Completable.applyIoSchedulers() = this.subscribeOn(Schedulers.io()) 9 | .observeOn(AndroidSchedulers.mainThread()) 10 | 11 | fun Completable.applyComputationSchedulers() = this.subscribeOn(Schedulers.computation()) 12 | .observeOn(AndroidSchedulers.mainThread()) 13 | 14 | fun Maybe.applyIoSchedulers() = this.subscribeOn(Schedulers.io()) 15 | .observeOn(AndroidSchedulers.mainThread()) 16 | 17 | fun Maybe.applyComputationSchedulers() = this.subscribeOn(Schedulers.computation()) 18 | .observeOn(AndroidSchedulers.mainThread()) 19 | 20 | fun Single.applyIoSchedulers() = this.subscribeOn(Schedulers.io()) 21 | .observeOn(AndroidSchedulers.mainThread()) 22 | 23 | fun Single.applyComputationSchedulers() = this.subscribeOn(Schedulers.computation()) 24 | .observeOn(AndroidSchedulers.mainThread()) 25 | 26 | fun Observable.applyIoSchedulers() = this.subscribeOn(Schedulers.io()) 27 | .observeOn(AndroidSchedulers.mainThread()) 28 | 29 | fun Observable.applyComputationSchedulers() = this.subscribeOn(Schedulers.computation()) 30 | .observeOn(AndroidSchedulers.mainThread()) 31 | 32 | fun Flowable.applyIoSchedulers(): Flowable = this.subscribeOn(Schedulers.io()) 33 | .observeOn(AndroidSchedulers.mainThread()) 34 | 35 | fun Flowable.applyComputationSchedulers(): Flowable = 36 | this.subscribeOn(Schedulers.computation()) 37 | .observeOn(AndroidSchedulers.mainThread()) 38 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/feature/demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.feature.demo 2 | 3 | 4 | import android.os.Bundle 5 | import android.support.v7.app.AppCompatActivity 6 | import co.netguru.android.sectionsDecoratorDemo.R 7 | import co.netguru.android.sectionsDecoratorDemo.common.extensions.replaceFragment 8 | import co.netguru.android.sectionsDecoratorDemo.feature.demo.HorizontalFragment.Companion.createFragment 9 | import kotlinx.android.synthetic.main.activity_main.* 10 | 11 | class DemoActivity : AppCompatActivity() { 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | setContentView(R.layout.activity_main) 16 | 17 | supportFragmentManager.replaceFragment( 18 | R.id.content, 19 | HorizontalFragment.TAG, 20 | HorizontalFragment.Companion::createFragment 21 | ) 22 | 23 | bottomNavigation.setOnNavigationItemSelectedListener { 24 | when (it.itemId) { 25 | R.id.vertical -> { 26 | supportFragmentManager.replaceFragment( 27 | R.id.content, 28 | VerticalFragment.TAG, 29 | VerticalFragment.Companion::createFragment 30 | ) 31 | } 32 | R.id.horizontal -> { 33 | supportFragmentManager.replaceFragment( 34 | R.id.content, 35 | HorizontalFragment.TAG, 36 | HorizontalFragment.Companion::createFragment 37 | ) 38 | } 39 | } 40 | return@setOnNavigationItemSelectedListener true 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/debug/kotlin/co/netguru/android/sectionsDecoratorDemo/application/DebugMetricsHelper.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.application 2 | 3 | import android.content.Context 4 | import co.netguru.android.sectionsDecoratorDemo.application.scope.AppScope 5 | import com.facebook.stetho.Stetho 6 | import com.github.moduth.blockcanary.BlockCanary 7 | import com.github.moduth.blockcanary.BlockCanaryContext 8 | import com.squareup.leakcanary.LeakCanary 9 | import timber.log.Timber 10 | import javax.inject.Inject 11 | 12 | /** 13 | * Helper class that initializes a set of debugging tools 14 | * for the debug build type and register crash manager for release type. 15 | * ## Debug type tools: 16 | * - AndroidDevMetrics 17 | * - Stetho 18 | * - StrictMode 19 | * - LeakCanary 20 | * - Timber 21 | * 22 | * ## Release type tools: 23 | * - CrashManager 24 | */ 25 | @AppScope 26 | class DebugMetricsHelper @Inject constructor() { 27 | 28 | internal fun init(context: Context) { 29 | // LeakCanary 30 | if (LeakCanary.isInAnalyzerProcess(context.applicationContext as App)) { 31 | // This process is dedicated to LeakCanary for heap analysis. 32 | // You should not init your app in this process. 33 | return 34 | } 35 | LeakCanary.install(context.applicationContext as App) 36 | 37 | // Stetho 38 | Stetho.initialize( 39 | Stetho.newInitializerBuilder(context) 40 | .enableDumpapp(Stetho.defaultDumperPluginsProvider(context)) 41 | .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(context)) 42 | .build() 43 | ) 44 | 45 | //Timber 46 | Timber.plant(Timber.DebugTree()) 47 | 48 | //BlockCanary 49 | BlockCanary.install(context, BlockCanaryContext()).start() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/common/Optional.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.common 2 | 3 | import io.reactivex.Maybe 4 | import io.reactivex.Observable 5 | import io.reactivex.Single 6 | import io.reactivex.rxkotlin.ofType 7 | 8 | /** 9 | * Optional implementation intended to be used together with Rx2 which doesn't accept nulls. 10 | */ 11 | sealed class Optional { 12 | 13 | fun toNullable(): T? = when (this) { 14 | is Some -> value 15 | is None -> null 16 | } 17 | 18 | data class Some(val value: T) : Optional() 19 | object None : Optional() 20 | 21 | /** 22 | * If a value is present, apply the provided mapping function to it, 23 | * and if the result is non-null, return an [Optional.Some] describing the 24 | * result. Otherwise return an empty [Optional.None]. 25 | */ 26 | inline fun map(f: (T) -> U?): Optional = when (this) { 27 | is Optional.Some -> f(value).toOptional() 28 | Optional.None -> None 29 | } 30 | 31 | /** 32 | * If a value is present, apply the provided [Optional]-bearing 33 | * mapping function to it, return that result, otherwise return an empty 34 | * [Optional.None]. This method is similar to [Optional.map], 35 | * but the provided mapper is one whose result is already an [Optional]. 36 | */ 37 | inline fun flatMap(f: (T) -> Optional): Optional = when (this) { 38 | is Optional.Some -> f(value) 39 | Optional.None -> None 40 | } 41 | } 42 | 43 | fun T?.toOptional(): Optional = if (this == null) Optional.None else Optional.Some( 44 | this 45 | ) 46 | 47 | fun Observable>.filterOptionalNone(): Observable = 48 | this.ofType>() 49 | .map { it.toNullable() } 50 | 51 | fun Single>.filterOptionalNone(): Maybe = 52 | this.filter { it is Optional.Some } 53 | .map { it.toNullable() } 54 | 55 | fun Maybe>.filterOptionalNone(): Maybe = 56 | this.filter { it is Optional.Some } 57 | .map { it.toNullable() } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sections decorator 2 | 3 | ## About 4 | Sections decorator is RecyclerView decorator that decorates multi section recycler with vertical / horizontal line and section title. 5 | Current section title is always visible. It might be useful in todo list or calendar like applications. 6 | ![example1](images/example1.gif) ![example2](images/example2.gif) 7 | 8 | ## Usage 9 | 10 | 1. Add jitpack.io to your build.gradle 11 | ```groovy 12 | repositories { 13 | mavenCentral() 14 | maven { url 'https://jitpack.io' } 15 | } 16 | ``` 17 | 2. Add library dependency 18 | ```groovy 19 | implementation 'com.github.netguru:sections-decorator-android:0.1.1' 20 | ``` 21 | 22 | 3. implement SectionsAdapterInterface in your RecyclerView adapter 23 | - see example in [Sample app](app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/feature/demo/Adapter.kt) 24 | 25 | 4. add decorator to your RecyclerView 26 | 27 | kotlin: 28 | ```kotlin 29 | recyclerView.addItemDecoration(SectionDecorator(context).apply { 30 | setLineColor(R.color.green) 31 | setLineWidth(15f) 32 | }) 33 | ``` 34 | 35 | java: 36 | ```java 37 | SectionDecorator decorator = new SectionDecorator(getContext()); 38 | decorator.setLineColor(R.color.green) 39 | decorator.setLineWidth(15f) 40 | recyclerView.addItemDecoration(decorator); 41 | ``` 42 | 5. Customize 43 | 44 | ## Customization 45 | you can customize line color and width through `setLineColor(color: Int)` and `setLineWidth(width: Float)`. 46 | To customize text appearience you can create layout with only TextView in it and pass it to decorator using 47 | `setHeaderView(layoutRes: Int)` 48 | 49 | ## License 50 | ``` 51 | Copyright 2018 Netguru 52 | 53 | Licensed under the Apache License, Version 2.0 (the "License"); 54 | you may not use this file except in compliance with the License. 55 | You may obtain a copy of the License at 56 | 57 | http://www.apache.org/licenses/LICENSE-2.0 58 | 59 | Unless required by applicable law or agreed to in writing, software 60 | distributed under the License is distributed on an "AS IS" BASIS, 61 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 62 | See the License for the specific language governing permissions and 63 | limitations under the License. 64 | ``` 65 | -------------------------------------------------------------------------------- /sectionsDecorator/src/main/java/co/netguru/sectionsDecorator/VerticalPainter.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.sectionsDecorator 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.Paint 5 | import android.graphics.Rect 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | import kotlin.math.max 10 | import kotlin.math.min 11 | 12 | class VerticalPainter( 13 | fixLayoutSize: (View, ViewGroup) -> Unit, 14 | headerToLineOffset: Float, 15 | linePaint: Paint 16 | ) : Painter(fixLayoutSize, headerToLineOffset, linePaint) { 17 | 18 | 19 | override fun getLineStart(sectionIndex: Int, first: View): Float { 20 | val topMargin = 21 | (first.layoutParams as ViewGroup.MarginLayoutParams).topMargin.toFloat() 22 | return if (sectionIndex == 0) { 23 | topMargin 24 | } else { 25 | max(first.top.toFloat(), topMargin) 26 | } 27 | } 28 | 29 | override fun getLineEnd( 30 | sectionIndex: Int, 31 | sectionSize: Int, 32 | canvas: Canvas, 33 | last: View 34 | ): Float { 35 | val bottomMargin = 36 | (last.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin.toFloat() 37 | 38 | return if (sectionIndex == sectionSize - 1) { 39 | canvas.height.toFloat() - bottomMargin 40 | } else { 41 | min(last.bottom.toFloat(), canvas.height.toFloat() - bottomMargin) 42 | } 43 | } 44 | 45 | override fun getXStart(sectionIndex: Int, first: View) = headerToLineOffset 46 | override fun getYStart(sectionIndex: Int, first: View) = getLineStart(sectionIndex, first) 47 | 48 | override fun getXEnd(sectionIndex: Int, sectionSize: Int, canvas: Canvas, last: View) = 49 | headerToLineOffset 50 | 51 | override fun getYEnd(sectionIndex: Int, sectionSize: Int, canvas: Canvas, last: View) = 52 | getLineEnd(sectionIndex, sectionSize, canvas, last) 53 | 54 | override fun drawHeader( 55 | canvas: Canvas, 56 | textView: TextView, 57 | startPosition: Float, 58 | headerWidth: Int 59 | ) { 60 | canvas.save() 61 | canvas.translate(0f, startPosition + headerWidth) 62 | canvas.rotate(-90f) 63 | textView.draw(canvas) 64 | canvas.restore() 65 | } 66 | 67 | override fun getOutRect(outRect: Rect) { 68 | outRect.left = headerToLineOffset.toInt() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /sectionsDecorator/src/main/java/co/netguru/sectionsDecorator/HorizontalPainter.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.sectionsDecorator 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.Paint 5 | import android.graphics.Rect 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | import kotlin.math.max 10 | import kotlin.math.min 11 | 12 | class HorizontalPainter( 13 | fixLayoutSize: (View, ViewGroup) -> Unit, 14 | headerToLineOffset: Float, 15 | linePaint: Paint 16 | ) : Painter(fixLayoutSize, headerToLineOffset, linePaint) { 17 | 18 | 19 | override fun getLineStart(sectionIndex: Int, first: View): Float { 20 | val leftMargin = 21 | (first.layoutParams as ViewGroup.MarginLayoutParams).leftMargin.toFloat() 22 | 23 | return if (sectionIndex == 0) { 24 | leftMargin 25 | } else { 26 | max(first.left.toFloat(), leftMargin) 27 | } 28 | } 29 | 30 | override fun getLineEnd( 31 | sectionIndex: Int, 32 | sectionSize: Int, 33 | canvas: Canvas, 34 | last: View 35 | ): Float { 36 | val rightMargin = 37 | (last.layoutParams as ViewGroup.MarginLayoutParams).rightMargin.toFloat() 38 | return if (sectionIndex == sectionSize - 1) { 39 | canvas.width.toFloat() - rightMargin 40 | } else { 41 | min(last.right.toFloat(), canvas.width.toFloat() - rightMargin) 42 | } 43 | 44 | } 45 | 46 | 47 | override fun getXStart(sectionIndex: Int, first: View) = getLineStart(sectionIndex, first) 48 | 49 | override fun getXEnd( 50 | sectionIndex: Int, 51 | sectionSize: Int, 52 | canvas: Canvas, 53 | last: View 54 | ) = getLineEnd(sectionIndex, sectionSize, canvas, last) 55 | 56 | override fun getYStart(sectionIndex: Int, first: View) = headerToLineOffset 57 | 58 | override fun getYEnd( 59 | sectionIndex: Int, 60 | sectionSize: Int, 61 | canvas: Canvas, 62 | last: View 63 | ) = headerToLineOffset 64 | 65 | override fun getOutRect(outRect: Rect) { 66 | outRect.top = headerToLineOffset.toInt() 67 | } 68 | 69 | override fun drawHeader( 70 | canvas: Canvas, 71 | textView: TextView, 72 | startPosition: Float, 73 | headerWidth: Int 74 | ) { 75 | canvas.save() 76 | canvas.translate(startPosition, 0f) 77 | textView.draw(canvas) 78 | canvas.restore() 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/kotlin/co/netguru/android/sectionsDecoratorDemo/feature/demo/Adapter.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.android.sectionsDecoratorDemo.feature.demo 2 | 3 | import android.graphics.Color 4 | import android.support.v7.widget.RecyclerView 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.LinearLayout 9 | import co.netguru.android.sectionsDecoratorDemo.R 10 | import co.netguru.sectionsDecorator.SectionsAdapterInterface 11 | import kotlinx.android.synthetic.main.list_item_horizontal.view.* 12 | 13 | class MyAdapter(val orientation: Int) : RecyclerView.Adapter(), 14 | SectionsAdapterInterface { 15 | 16 | private val items = linkedMapOf( 17 | Pair("Section 1", listOf("one 1", "two 1", "three 1", "four 1", "five 1", "six 1", "seven 1", "eight 1", "nine 1", "ten 1")), 18 | Pair("Middle", listOf("middle")), 19 | Pair("Section 2", listOf("one 2", "two 2", "three 2", "four 2", "five 2", "six 2", "seven 2", "eight 2", "nine 2", "ten 2")) 20 | ) 21 | 22 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { 23 | val layout = if (orientation == LinearLayout.HORIZONTAL) { 24 | R.layout.list_item_horizontal 25 | } else { 26 | R.layout.list_item_vertical 27 | } 28 | return MyViewHolder(LayoutInflater.from(parent.context).inflate(layout, parent, false)) 29 | } 30 | 31 | override fun getItemCount(): Int { 32 | return items.values.flatten().size 33 | } 34 | 35 | override fun onBindViewHolder(holder: MyViewHolder, position: Int) { 36 | val sectionNameOfItem = items.entries.flatMap { entry -> entry.value.map { Pair(entry.key, it) } }[position].first 37 | holder.bind( 38 | items.values.flatten()[position], 39 | items.keys.asSequence().toList().indexOf(sectionNameOfItem) 40 | ) 41 | } 42 | 43 | override fun getSectionsCount(): Int { 44 | return items.keys.size 45 | } 46 | 47 | override fun getSectionTitleAt(sectionIndex: Int): String { 48 | return items.keys.toList()[sectionIndex] 49 | } 50 | 51 | override fun getItemCountForSection(sectionIndex: Int): Int { 52 | return (items[items.keys.toList()[sectionIndex]] ?: emptyList()).size 53 | } 54 | } 55 | 56 | class MyViewHolder(val view: View): RecyclerView.ViewHolder(view){ 57 | private val textView = view.item_text!! 58 | 59 | fun bind(text: String, section: Int){ 60 | if (section.rem(2) == 0) { 61 | view.setBackgroundColor(Color.CYAN) 62 | } else { 63 | view.setBackgroundColor(Color.LTGRAY) 64 | } 65 | textView.text = text 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Android 2 | 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Application secrets 26 | secret.properties 27 | 28 | Proguard folder generated by Eclipse 29 | #proguard/ 30 | 31 | # Log Files 32 | *.log 33 | 34 | # Android Studio Navigation editor temp files 35 | .navigation/ 36 | 37 | # Android Studio captures folder 38 | captures/ 39 | 40 | # Keystore files 41 | *.jks 42 | 43 | ### JetBrains 44 | 45 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 46 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 47 | 48 | # IDEA/Android Studio ignores: 49 | *.iml 50 | *.ipr 51 | *.iws 52 | /.idea/* 53 | # IDEA/Android Studio Ignore exceptions 54 | !/.idea/vcs.xml 55 | !/.idea/fileTemplates/ 56 | !/.idea/inspectionProfiles/ 57 | !/.idea/scopes/ 58 | !/.idea/codeStyleSettings.xml 59 | !/.idea/encodings.xml 60 | !/.idea/copyright/ 61 | !/.idea/compiler.xml 62 | !/.idea/codeStyles 63 | 64 | # Project files backups generated on failures 65 | projectFilesBackup*/ 66 | 67 | ## Plugin-specific files: 68 | 69 | # IntelliJ 70 | /out/ 71 | 72 | # mpeltonen/sbt-idea plugin 73 | .idea_modules/ 74 | 75 | # JIRA plugin 76 | atlassian-ide-plugin.xml 77 | 78 | # Crashlytics plugin (for Android Studio and IntelliJ) 79 | com_crashlytics_export_strings.xml 80 | crashlytics.properties 81 | crashlytics-build.properties 82 | fabric.properties 83 | 84 | # Firebase 85 | google-services.json 86 | 87 | ### Temporary files 88 | 89 | ## Vim 90 | [._]*.s[a-w][a-z] 91 | [._]s[a-w][a-z] 92 | Session.vim 93 | .netrwhist 94 | tags 95 | 96 | ## OS X 97 | *.DS_Store 98 | .AppleDouble 99 | .LSOverride 100 | # Icon must end with two \r 101 | Icon 102 | # Thumbnails 103 | ._* 104 | # Files that might appear in the root of a volume 105 | .DocumentRevisions-V100 106 | .fseventsd 107 | .Spotlight-V100 108 | .TemporaryItems 109 | .Trashes 110 | .VolumeIcon.icns 111 | .com.apple.timemachine.donotpresent 112 | # Directories potentially created on remote AFP share 113 | .AppleDB 114 | .AppleDesktop 115 | Network Trash Folder 116 | Temporary Items 117 | .apdisk 118 | 119 | ## Linux 120 | *~ 121 | 122 | # temporary files which can be created if a process still has a handle open of a deleted file 123 | .fuse_hidden* 124 | 125 | # KDE directory preferences 126 | .directory 127 | 128 | # Linux trash folder which might appear on any partition or disk 129 | .Trash-* 130 | -------------------------------------------------------------------------------- /sectionsDecorator/src/main/java/co/netguru/sectionsDecorator/Painter.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.sectionsDecorator 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.Paint 5 | import android.graphics.Rect 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.TextView 9 | 10 | abstract class Painter( 11 | internal val fixLayoutSize: (View, ViewGroup) -> Unit, 12 | internal val headerToLineOffset: Float, 13 | private val linePaint: Paint 14 | ) { 15 | abstract fun getOutRect(outRect: Rect) 16 | 17 | fun paint( 18 | canvas: Canvas, 19 | sectionIndex: Int, 20 | sectionTitle: String, 21 | sectionsVisibleElements: List, 22 | headerView: TextView, 23 | sectionSize: Int, 24 | parent: ViewGroup 25 | ) { 26 | 27 | val lineStart = getLineStart(sectionIndex, sectionsVisibleElements.first()) 28 | val lineEnd = getLineEnd(sectionIndex, sectionSize, canvas, sectionsVisibleElements.last()) 29 | 30 | if (lineEnd > lineStart) { 31 | canvas.drawLine( 32 | getXStart(sectionIndex, sectionsVisibleElements.first()), 33 | getYStart(sectionIndex, sectionsVisibleElements.first()), 34 | getXEnd(sectionIndex, sectionSize, canvas, sectionsVisibleElements.last()), 35 | getYEnd(sectionIndex, sectionSize, canvas, sectionsVisibleElements.last()), 36 | linePaint 37 | ) 38 | } 39 | 40 | headerView.apply { 41 | text = sectionTitle 42 | fixLayoutSize(this, parent) 43 | 44 | val headerWidth = with(this) { 45 | width + paddingStart + paddingEnd + 46 | kotlin.with(layoutParams as ViewGroup.MarginLayoutParams) { 47 | marginStart + marginEnd 48 | } 49 | } 50 | 51 | val startPosition = if (lineEnd - lineStart < headerWidth && sectionIndex == 0) { 52 | lineEnd - headerWidth 53 | } else { 54 | lineStart 55 | } 56 | 57 | drawHeader(canvas, this, startPosition, headerWidth) 58 | } 59 | } 60 | 61 | internal abstract fun getLineStart(sectionIndex: Int, first: View): Float 62 | internal abstract fun getLineEnd( 63 | sectionIndex: Int, 64 | sectionSize: Int, 65 | canvas: Canvas, 66 | last: View 67 | ): Float 68 | 69 | internal abstract fun getXStart(sectionIndex: Int, first: View): Float 70 | internal abstract fun getXEnd( 71 | sectionIndex: Int, 72 | sectionSize: Int, 73 | canvas: Canvas, 74 | last: View 75 | ): Float 76 | 77 | internal abstract fun getYStart(sectionIndex: Int, first: View): Float 78 | internal abstract fun getYEnd( 79 | sectionIndex: Int, 80 | sectionSize: Int, 81 | canvas: Canvas, 82 | last: View 83 | ): Float 84 | 85 | internal abstract fun drawHeader( 86 | canvas: Canvas, 87 | textView: TextView, 88 | startPosition: Float, 89 | headerWidth: Int 90 | ) 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/maciek/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # base option from *App Dev Note* 20 | -optimizationpasses 5 21 | -dontusemixedcaseclassnames 22 | -dontskipnonpubliclibraryclasses 23 | -dontskipnonpubliclibraryclassmembers 24 | -dontpreverify 25 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 26 | -keepattributes LineNumberTable,SourceFile,Signature,*Annotation*,Exceptions,InnerClasses 27 | 28 | # Keep native methods 29 | -keepclassmembers class * { 30 | native ; 31 | } 32 | 33 | # remove log call 34 | -assumenosideeffects class android.util.Log { 35 | public static *** d(...); 36 | } 37 | -assumenosideeffects class timber.log.Timber { 38 | public static *** d(...); 39 | } 40 | 41 | # Models! 42 | # TODO 07.09.2017 Rule should be adjusted to current project - all models used with GSON should keep their members name 43 | # TODO 07.09.2017 or all their members should be annotated with @SerializedName(). 44 | -keepclassmembernames class co.netguru.android.template.data.**.model.** { *; } 45 | 46 | # app compat-v7 47 | -keep class android.support.v7.widget.SearchView { *; } 48 | 49 | # FragmentArgs 50 | -keep class com.hannesdorfmann.fragmentargs.** { *; } 51 | 52 | # Gson 53 | -keep class sun.misc.Unsafe { *; } 54 | 55 | # retrofit 56 | -keepclasseswithmembers class * { 57 | @retrofit2.http.* ; 58 | } 59 | -dontnote retrofit2.Platform 60 | -dontnote retrofit2.Platform$IOS$MainThreadExecutor 61 | -dontwarn retrofit2.Platform$Java8 62 | -keepattributes Signature 63 | -keepattributes Exceptions 64 | 65 | # dagger 66 | -keepclassmembers,allowobfuscation class * { 67 | @javax.inject.* *; 68 | @dagger.* *; 69 | (); 70 | } 71 | -keep class javax.inject.** { *; } 72 | -keep class **$$ModuleAdapter 73 | -keep class **$$InjectAdapter 74 | -keep class **$$StaticInjection 75 | -keep class dagger.** { *; } 76 | -dontwarn dagger.internal.codegen.** 77 | 78 | # stetho 79 | -dontwarn org.apache.http.** 80 | -keep class com.facebook.stetho.dumpapp.** { *; } 81 | -keep class com.facebook.stetho.server.** { *; } 82 | -dontwarn com.facebook.stetho.dumpapp.** 83 | -dontwarn com.facebook.stetho.server.** 84 | 85 | # leak canary 86 | -keep class org.eclipse.mat.** { *; } 87 | -keep class com.squareup.leakcanary.** { *; } 88 | -dontwarn android.app.Notification 89 | 90 | # fabric 91 | -dontwarn com.crashlytics.android.** 92 | 93 | # glide 94 | -keep public class * implements com.bumptech.glide.module.GlideModule 95 | -keep public class * extends com.bumptech.glide.AppGlideModule 96 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { 97 | **[] $VALUES; 98 | public *; 99 | } 100 | 101 | -dontwarn okhttp3.** 102 | -dontwarn okio.** 103 | -dontwarn javax.annotation.** 104 | 105 | #dagger 106 | -dontwarn com.google.errorprone.annotations.* -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'kotlin-kapt' 5 | apply plugin: 'com.frogermcs.androiddevmetrics' 6 | apply plugin: 'com.getkeepsafe.dexcount' 7 | apply plugin: 'com.github.ben-manes.versions' 8 | apply plugin: 'org.sonarqube' 9 | apply plugin: 'com.vanniktech.android.junit.jacoco' 10 | apply from: '../buildsystem/sonarqube.gradle' 11 | 12 | android { 13 | compileSdkVersion 27 14 | 15 | defaultConfig { 16 | applicationId "co.netguru.android.sectionsDecorator" 17 | minSdkVersion 18 18 | targetSdkVersion 27 19 | versionCode isBitrise ? Integer.parseInt(bitrise.io.buildNumber) : 1 20 | versionName '0.1.1' 21 | 22 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 23 | } 24 | 25 | buildTypes { 26 | debug { 27 | versionNameSuffix "-DEBUG" 28 | applicationIdSuffix ".debug" 29 | testCoverageEnabled true 30 | debuggable true 31 | minifyEnabled true 32 | shrinkResources false 33 | useProguard false 34 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 35 | 'proguard-rules.pro', 'proguard-rules-test.pro' 36 | } 37 | release { 38 | debuggable false 39 | minifyEnabled true 40 | shrinkResources true 41 | zipAlignEnabled true 42 | useProguard true 43 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 44 | 'proguard-rules.pro' 45 | } 46 | } 47 | 48 | // Always show the result of every unit test, even if it passes. 49 | testOptions.unitTests.all { 50 | testLogging { 51 | events 'passed', 'skipped', 'failed', 'standardOut', 'standardError' 52 | } 53 | } 54 | 55 | packagingOptions { 56 | exclude 'META-INF/services/javax.annotation.processing.Processor' 57 | exclude 'META-INF/LICENSE.txt' 58 | exclude 'META-INF/NOTICE.txt' 59 | } 60 | 61 | sourceSets.all { 62 | it.java.srcDir "src/$it.name/kotlin" 63 | } 64 | 65 | junitJacoco { 66 | jacocoVersion='0.7.9' 67 | excludes = ['android/databinding/**/*.class', 68 | '**/android/databinding/*Binding.class', 69 | '**/BR.*', 70 | '**/R.class', 71 | '**/R$*.class', 72 | '**/BuildConfig.*', 73 | '**/Manifest*.*', 74 | '**/*$ViewInjector*.*', 75 | '**/*$ViewBinder*.*', 76 | '**/*_MembersInjector.class', 77 | '**/Dagger*Component.class', 78 | '**/Dagger*Component$Builder.class', 79 | '**/*Module_*Factory.class', 80 | '**/AutoValue_*.*', 81 | '**/*JavascriptBridge.class', 82 | '**/Lambda$*.class', 83 | '**/Lambda.class', 84 | '**/*Lambda.class', 85 | '**/*Lambda*.class'] 86 | } 87 | 88 | lintOptions { 89 | abortOnError false 90 | } 91 | } 92 | 93 | configurations { 94 | dependencyUpdates.resolutionStrategy = { 95 | componentSelection { rules -> 96 | rules.all { ComponentSelection selection -> 97 | boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm'].any { qualifier -> 98 | selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/ 99 | } 100 | if (rejected) { 101 | selection.reject('Release candidate') 102 | } 103 | } 104 | } 105 | } 106 | } 107 | 108 | dependencies { 109 | 110 | implementation project(":sectionsDecorator") 111 | 112 | //Kotlin 113 | implementation libs.kotlin 114 | 115 | // Anko Commons 116 | implementation libs.ankoCommons 117 | implementation libs.ankoCommonsSupportV7 118 | implementation libs.ankoCommonsSupportV4 119 | 120 | // Support 121 | implementation libs.appCompat 122 | implementation libs.recyclerView 123 | implementation libs.design 124 | 125 | // MVP 126 | implementation(libs.mosby) { 127 | exclude module: 'appcompat-v7' 128 | exclude module: 'support-annotations' 129 | } 130 | 131 | // Dagger 132 | implementation libs.dagger 133 | implementation libs.daggerAndroid 134 | implementation libs.daggerAndroidSupport 135 | kapt proc.dagger 136 | kapt proc.daggerAndroid 137 | compileOnly proc.javaxAnnotation 138 | 139 | //RxJava 140 | implementation libs.rxJava2 141 | implementation (libs.rxKotlin2) { 142 | exclude module: 'rxjava' 143 | exclude module: 'kotlin-stdlib' 144 | } 145 | implementation(libs.rxAndroid2) { 146 | exclude module: 'rxjava' 147 | } 148 | 149 | // OkHttp + Retrofit 150 | implementation libs.okHttp 151 | implementation libs.okHttpLoggingInterceptor 152 | implementation libs.retrofit 153 | implementation libs.retrofitGsonConverter 154 | implementation libs.retrofitRxJava2Adapter 155 | 156 | // PaperParcel 157 | implementation libs.paperParcel 158 | kapt proc.paperParcel 159 | 160 | // Timber 161 | implementation libs.timber 162 | 163 | // Stetho 164 | debugImplementation libs.stetho 165 | 166 | // LeakCanary 167 | debugImplementation libs.leakCanary 168 | 169 | // BlockCanary 170 | debugImplementation libs.blockCanary 171 | 172 | // Unit testing 173 | testImplementation test.junit 174 | testImplementation test.mockito 175 | testImplementation test.mockitoKotlin 176 | } 177 | -------------------------------------------------------------------------------- /sectionsDecorator/src/main/java/co/netguru/sectionsDecorator/SectionDecorator.kt: -------------------------------------------------------------------------------- 1 | package co.netguru.sectionsDecorator 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import android.graphics.Paint 6 | import android.graphics.Rect 7 | import android.support.annotation.ColorRes 8 | import android.support.annotation.LayoutRes 9 | import android.support.v7.widget.LinearLayoutManager 10 | import android.support.v7.widget.RecyclerView 11 | import android.view.LayoutInflater 12 | import android.view.View 13 | import android.view.ViewGroup 14 | import android.widget.LinearLayout 15 | import android.widget.TextView 16 | 17 | 18 | class SectionDecorator(private val context: Context) : RecyclerView.ItemDecoration() { 19 | 20 | private val headerVerticalOffset by lazy { 21 | context.resources.getDimensionPixelSize(R.dimen.header_vertical_offset).toFloat() 22 | } 23 | private val headerToLineOffset by lazy { 24 | context.resources.getDimensionPixelSize(R.dimen.header_to_line_offset).toFloat() 25 | } 26 | private val linePaint = Paint() 27 | 28 | private var headerView: TextView? = null 29 | private var headerLayoutId: Int = R.layout.section_header 30 | 31 | private var painter: Painter? = null 32 | 33 | init { 34 | linePaint.color = context.getColorCompat(android.R.color.black) 35 | linePaint.strokeWidth = 36 | context.resources.getDimensionPixelSize(R.dimen.divider_size).toFloat() 37 | } 38 | 39 | fun setLineColor(@ColorRes color: Int) { 40 | linePaint.color = context.getColorCompat(color) 41 | } 42 | 43 | fun setLineWidth(width: Float) { 44 | linePaint.strokeWidth = width 45 | } 46 | 47 | fun setHeaderView(@LayoutRes layout: Int) { 48 | headerView = null 49 | headerLayoutId = layout 50 | } 51 | 52 | override fun getItemOffsets( 53 | outRect: Rect, 54 | view: View, 55 | parent: RecyclerView, 56 | state: RecyclerView.State? 57 | ) { 58 | super.getItemOffsets(outRect, view, parent, state) 59 | createPainter(parent) 60 | 61 | painter?.getOutRect(outRect) 62 | 63 | } 64 | 65 | override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State?) { 66 | super.onDrawOver(canvas, parent, state) 67 | val adapter = getSectionsAdapter(parent) 68 | 69 | createPainter(parent) 70 | 71 | if (headerView == null) createHeaderView(parent) 72 | 73 | val sectionsList = parent.asSequence() 74 | .map { 75 | Pair(adapter.getSectionTitleForPosition(parent.getChildAdapterPosition(it)), it) 76 | }.fold(mapOf>()) { acc, data -> 77 | acc.addToValueList(data.first, data.second) 78 | }.toList() 79 | 80 | nullCheck2(painter, headerView) { painter, headerView -> 81 | sectionsList.forEachIndexed { index, (sectionTitle, sectionsVisibleElements) -> 82 | painter.paint( 83 | canvas, 84 | index, 85 | sectionTitle, 86 | sectionsVisibleElements, 87 | headerView, 88 | sectionsList.size, 89 | parent 90 | ) 91 | } 92 | } 93 | } 94 | 95 | private fun getSectionsAdapter(parent: RecyclerView): SectionsAdapterInterface { 96 | return parent.adapter as SectionsAdapterInterface 97 | } 98 | 99 | private fun createHeaderView(parent: RecyclerView) { 100 | headerView = LayoutInflater.from(parent.context) 101 | .inflate(headerLayoutId, parent, false) as TextView 102 | fixLayoutSize(headerView!!, parent) 103 | } 104 | 105 | private fun createPainter(parent: RecyclerView) { 106 | if (painter == null) { 107 | if (parent.layoutManager !is LinearLayoutManager) { 108 | throw IllegalArgumentException("Section decorator only works with linear layout manager") 109 | } else { 110 | painter = 111 | if ((parent.layoutManager as LinearLayoutManager).orientation == LinearLayout.HORIZONTAL) { 112 | HorizontalPainter(::fixLayoutSize, headerToLineOffset, linePaint) 113 | } else { 114 | VerticalPainter(::fixLayoutSize, headerVerticalOffset, linePaint) 115 | } 116 | } 117 | } 118 | } 119 | 120 | private fun SectionsAdapterInterface.getSectionTitleForPosition(currentPosition: Int): String { 121 | var count = 0 122 | for (i in 0..getSectionsCount()) { 123 | count += getItemCountForSection(i) 124 | if (currentPosition < count) return getSectionTitleAt(i) 125 | } 126 | throw IndexOutOfBoundsException("try to get index=$currentPosition from items lenght=$count") 127 | } 128 | 129 | /** 130 | * Measures the headerTitle view to make sure its size is greater than 0 and will be drawn 131 | * [RecyclerView item decorations](https://yoda.entelect.co.za/view/9627/how-to-android-recyclerview-item-decorations) 132 | */ 133 | private fun fixLayoutSize(view: View, parent: ViewGroup) { 134 | val widthSpec = View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY) 135 | val heightSpec = 136 | View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.UNSPECIFIED) 137 | 138 | val childWidth = ViewGroup.getChildMeasureSpec( 139 | widthSpec, parent.paddingLeft + parent.paddingRight, view.layoutParams.width 140 | ) 141 | val childHeight = ViewGroup.getChildMeasureSpec( 142 | heightSpec, parent.paddingTop + parent.paddingBottom, view.layoutParams.height 143 | ) 144 | 145 | view.measure(childWidth, childHeight) 146 | view.layout(0, 0, view.measuredWidth, view.measuredHeight) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /default-detekt-config.yml: -------------------------------------------------------------------------------- 1 | autoCorrect: true 2 | failFast: false 3 | 4 | test-pattern: # Configure exclusions for test sources 5 | active: true 6 | patterns: # Test file regexes 7 | - '.*/test/.*' 8 | - '.*Test.kt' 9 | - '.*Spec.kt' 10 | exclude-rule-sets: 11 | - 'comments' 12 | exclude-rules: 13 | - 'NamingRules' 14 | - 'WildcardImport' 15 | - 'MagicNumber' 16 | - 'MaxLineLength' 17 | - 'LateinitUsage' 18 | - 'StringLiteralDuplication' 19 | - 'SpreadOperator' 20 | - 'TooManyFunctions' 21 | 22 | build: 23 | warningThreshold: 50 24 | failThreshold: 1000 25 | weights: 26 | complexity: 2 27 | formatting: 1 28 | LongParameterList: 1 29 | comments: 1 30 | 31 | processors: 32 | active: true 33 | exclude: 34 | # - 'FunctionCountProcessor' 35 | # - 'PropertyCountProcessor' 36 | # - 'ClassCountProcessor' 37 | # - 'PackageCountProcessor' 38 | # - 'KtFileCountProcessor' 39 | 40 | console-reports: 41 | active: true 42 | exclude: 43 | # - 'ProjectStatisticsReport' 44 | # - 'ComplexityReport' 45 | # - 'NotificationReport' 46 | # - 'FindingsReport' 47 | # - 'BuildFailureReport' 48 | 49 | output-reports: 50 | active: true 51 | exclude: 52 | # - 'PlainOutputReport' 53 | # - 'XmlOutputReport' 54 | 55 | comments: 56 | active: true 57 | CommentOverPrivateFunction: 58 | active: false 59 | CommentOverPrivateProperty: 60 | active: false 61 | UndocumentedPublicClass: 62 | active: false 63 | searchInNestedClass: true 64 | searchInInnerClass: true 65 | searchInInnerObject: true 66 | searchInInnerInterface: true 67 | UndocumentedPublicFunction: 68 | active: false 69 | 70 | complexity: 71 | active: true 72 | LongParameterList: 73 | active: true 74 | threshold: 5 75 | ignoreDefaultParameters: false 76 | LongMethod: 77 | active: true 78 | threshold: 20 79 | LargeClass: 80 | active: true 81 | threshold: 150 82 | ComplexInterface: 83 | active: true 84 | threshold: 10 85 | includeStaticDeclarations: false 86 | ComplexMethod: 87 | active: true 88 | threshold: 10 89 | StringLiteralDuplication: 90 | active: true 91 | threshold: 2 92 | ignoreAnnotation: true 93 | excludeStringsWithLessThan5Characters: true 94 | ignoreStringsRegex: '$^' 95 | MethodOverloading: 96 | active: true 97 | threshold: 5 98 | NestedBlockDepth: 99 | active: true 100 | threshold: 3 101 | TooManyFunctions: 102 | active: true 103 | thresholdInFiles: 10 104 | thresholdInClasses: 10 105 | thresholdInInterfaces: 10 106 | thresholdInObjects: 10 107 | thresholdInEnums: 10 108 | ComplexCondition: 109 | active: true 110 | threshold: 3 111 | LabeledExpression: 112 | active: true 113 | 114 | empty-blocks: 115 | active: true 116 | EmptyCatchBlock: 117 | active: true 118 | EmptyClassBlock: 119 | active: true 120 | EmptyDefaultConstructor: 121 | active: true 122 | EmptyDoWhileBlock: 123 | active: true 124 | EmptyElseBlock: 125 | active: true 126 | EmptyFinallyBlock: 127 | active: true 128 | EmptyForBlock: 129 | active: true 130 | EmptyFunctionBlock: 131 | active: true 132 | EmptyIfBlock: 133 | active: true 134 | EmptyInitBlock: 135 | active: true 136 | EmptyKtFile: 137 | active: true 138 | EmptySecondaryConstructor: 139 | active: true 140 | EmptyWhenBlock: 141 | active: true 142 | EmptyWhileBlock: 143 | active: true 144 | 145 | exceptions: 146 | active: true 147 | TooGenericExceptionCaught: 148 | active: true 149 | exceptions: 150 | - ArrayIndexOutOfBoundsException 151 | - Error 152 | - Exception 153 | - IllegalMonitorStateException 154 | - NullPointerException 155 | - IndexOutOfBoundsException 156 | - RuntimeException 157 | - Throwable 158 | ExceptionRaisedInUnexpectedLocation: 159 | active: true 160 | methodNames: 'toString,hashCode,equals,finalize' 161 | TooGenericExceptionThrown: 162 | active: true 163 | exceptions: 164 | - Error 165 | - Exception 166 | - NullPointerException 167 | - Throwable 168 | - RuntimeException 169 | NotImplementedDeclaration: 170 | active: true 171 | PrintStackTrace: 172 | active: true 173 | InstanceOfCheckForException: 174 | active: true 175 | ThrowingExceptionsWithoutMessageOrCause: 176 | active: true 177 | exceptions: 'IllegalArgumentException,IllegalStateException,IOException' 178 | ReturnFromFinally: 179 | active: true 180 | ThrowingExceptionFromFinally: 181 | active: true 182 | ThrowingExceptionInMain: 183 | active: false 184 | RethrowCaughtException: 185 | active: true 186 | ThrowingNewInstanceOfSameException: 187 | active: true 188 | SwallowedException: 189 | active: true 190 | 191 | performance: 192 | active: true 193 | ForEachOnRange: 194 | active: true 195 | SpreadOperator: 196 | active: true 197 | UnnecessaryTemporaryInstantiation: 198 | active: true 199 | 200 | potential-bugs: 201 | active: true 202 | DuplicateCaseInWhenExpression: 203 | active: true 204 | EqualsAlwaysReturnsTrueOrFalse: 205 | active: true 206 | EqualsWithHashCodeExist: 207 | active: true 208 | IteratorNotThrowingNoSuchElementException: 209 | active: true 210 | IteratorHasNextCallsNextMethod: 211 | active: true 212 | UselessPostfixExpression: 213 | active: true 214 | InvalidRange: 215 | active: true 216 | WrongEqualsTypeParameter: 217 | active: true 218 | ExplicitGarbageCollectionCall: 219 | active: true 220 | LateinitUsage: 221 | active: false 222 | excludeAnnotatedProperties: "" 223 | ignoreOnClassesPattern: "" 224 | UnconditionalJumpStatementInLoop: 225 | active: true 226 | UnreachableCode: 227 | active: true 228 | UnsafeCallOnNullableType: 229 | active: true 230 | UnsafeCast: 231 | active: true 232 | 233 | style: 234 | active: true 235 | CollapsibleIfStatements: 236 | active: true 237 | ReturnCount: 238 | active: true 239 | max: 2 240 | excludedFunctions: "equals" 241 | ThrowsCount: 242 | active: true 243 | max: 2 244 | NewLineAtEndOfFile: 245 | active: true 246 | WildcardImport: 247 | active: false 248 | excludeImports: 'java.util.*,kotlinx.android.synthetic.*' 249 | MaxLineLength: 250 | active: true 251 | maxLineLength: 120 252 | excludePackageStatements: false 253 | excludeImportStatements: false 254 | EqualsNullCall: 255 | active: true 256 | ForbiddenComment: 257 | active: true 258 | values: 'TODO:,FIXME:,STOPSHIP:' 259 | ForbiddenImport: 260 | active: false 261 | imports: '' 262 | FunctionOnlyReturningConstant: 263 | active: true 264 | ignoreOverridableFunction: true 265 | excludedFunctions: 'describeContents' 266 | SpacingBetweenPackageAndImports: 267 | active: true 268 | LoopWithTooManyJumpStatements: 269 | active: true 270 | maxJumpCount: 1 271 | MemberNameEqualsClassName: 272 | active: true 273 | ignoreOverriddenFunction: true 274 | VariableNaming: 275 | active: true 276 | variablePattern: '[a-z][A-Za-z0-9]*' 277 | privateVariablePattern: '[a-z][A-Za-z0-9]*' 278 | VariableMinLength: 279 | active: true 280 | minimumVariableNameLength: 1 281 | VariableMaxLength: 282 | active: false 283 | maximumVariableNameLength: 64 284 | TopLevelPropertyNaming: 285 | active: true 286 | constantPattern: '[A-Z][_A-Z0-9]*' 287 | propertyPattern: '[a-z][A-Za-z\d]*' 288 | privatePropertyPattern: '(_)?[a-z][A-Za-z0-9]*' 289 | ObjectPropertyNaming: 290 | active: true 291 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*' 292 | PackageNaming: 293 | active: true 294 | packagePattern: '^[a-z]+(\.[a-z][a-z0-9]*)*$' 295 | ClassNaming: 296 | active: true 297 | classPattern: '[A-Z$][a-zA-Z0-9$]*' 298 | EnumNaming: 299 | active: true 300 | enumEntryPattern: '^[A-Z$][a-zA-Z_$]*$' 301 | FunctionNaming: 302 | active: true 303 | functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' 304 | FunctionMaxLength: 305 | active: true 306 | maximumFunctionNameLength: 30 307 | FunctionMinLength: 308 | active: false 309 | minimumFunctionNameLength: 3 310 | ForbiddenClassName: 311 | active: false 312 | forbiddenName: '' 313 | SafeCast: 314 | active: true 315 | UnnecessaryAbstractClass: 316 | active: true 317 | UnnecessaryParentheses: 318 | active: true 319 | UnnecessaryInheritance: 320 | active: true 321 | UtilityClassWithPublicConstructor: 322 | active: true 323 | OptionalAbstractKeyword: 324 | active: true 325 | OptionalWhenBraces: 326 | active: true 327 | OptionalReturnKeyword: 328 | active: false 329 | OptionalUnit: 330 | active: true 331 | ProtectedMemberInFinalClass: 332 | active: true 333 | SerialVersionUIDInSerializableClass: 334 | active: true 335 | MagicNumber: 336 | active: true 337 | ignoreNumbers: '-1,0,1,2' 338 | ignoreHashCodeFunction: false 339 | ignorePropertyDeclaration: false 340 | ignoreConstantDeclaration: true 341 | ignoreCompanionObjectPropertyDeclaration: true 342 | ignoreAnnotation: false 343 | ignoreNamedArgument: true 344 | ignoreEnums: false 345 | ModifierOrder: 346 | active: true 347 | DataClassContainsFunctions: 348 | active: true 349 | conversionFunctionPrefix: 'to' 350 | UseDataClass: 351 | active: true 352 | UnusedImports: 353 | active: true 354 | ExpressionBodySyntax: 355 | active: true 356 | NestedClassesVisibility: 357 | active: true 358 | RedundantVisibilityModifierRule: 359 | active: true 360 | MatchingDeclarationName: 361 | active: true 362 | UntilInsteadOfRangeTo: 363 | active: true 364 | --------------------------------------------------------------------------------