diff --git a/app/build.gradle b/app/build.gradle index dbe87d5d..dfa230e5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -82,6 +82,8 @@ android { } } + + dependencies { implementation ("org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}") diff --git a/app/src/main/java/com/monstarlab/arch/data/Repository.kt b/app/src/main/java/com/monstarlab/arch/data/Repository.kt index d3fda1ad..3475c65e 100644 --- a/app/src/main/java/com/monstarlab/arch/data/Repository.kt +++ b/app/src/main/java/com/monstarlab/arch/data/Repository.kt @@ -3,4 +3,4 @@ package com.monstarlab.arch.data /** * Empty for now, but could allow us to add cache helpers etc */ -abstract class Repository constructor() +abstract class Repository diff --git a/app/src/main/java/com/monstarlab/arch/data/SingleSharedPreferenceDataStore.kt b/app/src/main/java/com/monstarlab/arch/data/SingleSharedPreferenceDataStore.kt index 32bf7639..c368e0e0 100644 --- a/app/src/main/java/com/monstarlab/arch/data/SingleSharedPreferenceDataStore.kt +++ b/app/src/main/java/com/monstarlab/arch/data/SingleSharedPreferenceDataStore.kt @@ -2,7 +2,10 @@ package com.monstarlab.arch.data import android.content.SharedPreferences import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json +import timber.log.Timber +import java.io.IOException abstract class SingleSharedPreferenceDataStore constructor( private val sharedPreferences: SharedPreferences, @@ -16,7 +19,7 @@ abstract class SingleSharedPreferenceDataStore constructor( val json = sharedPreferences.getString(key, "") ?: "" val entries = Json.decodeFromString(serializer, json) entries - } catch (e: Exception) { + } catch (e: SerializationException) { null } } @@ -25,7 +28,8 @@ abstract class SingleSharedPreferenceDataStore constructor( try { val json = Json.encodeToString(serializer, item) sharedPreferences.edit().putString(key, json).apply() - } catch (e: Exception) { + } catch (e: SerializationException) { + Timber.e(e) } } diff --git a/app/src/main/java/com/monstarlab/arch/extensions/FlowExtensions.kt b/app/src/main/java/com/monstarlab/arch/extensions/FlowExtensions.kt index 92fe65be..7e6c8d17 100644 --- a/app/src/main/java/com/monstarlab/arch/extensions/FlowExtensions.kt +++ b/app/src/main/java/com/monstarlab/arch/extensions/FlowExtensions.kt @@ -58,8 +58,9 @@ import kotlinx.coroutines.launch * @return [Flow] that only emits items from `this` upstream flow when the [lifecycle] is at * least in the [minActiveState]. */ + @OptIn(ExperimentalCoroutinesApi::class) -public fun Flow.flowWithLifecycle( +fun Flow.flowWithLifecycle( lifecycle: Lifecycle, minActiveState: Lifecycle.State = Lifecycle.State.STARTED ): Flow = callbackFlow { @@ -71,7 +72,11 @@ public fun Flow.flowWithLifecycle( close() } -fun CoroutineScope.combineFlows(flow1: Flow, flow2: Flow, collectBlock: (suspend (T1, T2) -> Unit)) { +fun CoroutineScope.combineFlows( + flow1: Flow, + flow2: Flow, + collectBlock: (suspend (T1, T2) -> Unit) +) { launch { flow1.combine(flow2) { v1, v2 -> collectBlock.invoke(v1, v2) @@ -81,7 +86,12 @@ fun CoroutineScope.combineFlows(flow1: Flow, flow2: Flow, colle } } -fun CoroutineScope.combineFlows(flow1: Flow, flow2: Flow, flow3: Flow, collectBlock: (suspend (T1, T2, T3) -> Unit)) { +fun CoroutineScope.combineFlows( + flow1: Flow, + flow2: Flow, + flow3: Flow, + collectBlock: (suspend (T1, T2, T3) -> Unit) +) { launch { combine(flow1, flow2, flow3) { v1, v2, v3 -> collectBlock.invoke(v1, v2, v3) @@ -91,7 +101,13 @@ fun CoroutineScope.combineFlows(flow1: Flow, flow2: Flow, f } } -fun CoroutineScope.combineFlows(flow1: Flow, flow2: Flow, flow3: Flow, flow4: Flow, collectBlock: (suspend (T1, T2, T3, T4) -> Unit)) { +fun CoroutineScope.combineFlows( + flow1: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + collectBlock: (suspend (T1, T2, T3, T4) -> Unit) +) { launch { combine(flow1, flow2, flow3, flow4) { v1, v2, v3, v4 -> collectBlock.invoke(v1, v2, v3, v4) diff --git a/app/src/main/java/com/monstarlab/arch/extensions/FragmentViewBindingDelegate.kt b/app/src/main/java/com/monstarlab/arch/extensions/FragmentViewBindingDelegate.kt index 90f42dce..096fe3ba 100644 --- a/app/src/main/java/com/monstarlab/arch/extensions/FragmentViewBindingDelegate.kt +++ b/app/src/main/java/com/monstarlab/arch/extensions/FragmentViewBindingDelegate.kt @@ -11,9 +11,11 @@ class FragmentViewBindingDelegate( private val fragment: Fragment, private val viewBinder: (View) -> T, private val disposeEvents: T.() -> Unit = {} -) : ReadOnlyProperty, LifecycleObserver { +) : ReadOnlyProperty, DefaultLifecycleObserver { - private inline fun Fragment.observeLifecycleOwnerThroughLifecycleCreation(crossinline viewOwner: LifecycleOwner.() -> Unit) { + private inline fun Fragment.observeLifecycleOwnerThroughLifecycleCreation( + crossinline viewOwner: LifecycleOwner.() -> Unit + ) { lifecycle.addObserver(object : DefaultLifecycleObserver { override fun onCreate(owner: LifecycleOwner) { viewLifecycleOwnerLiveData.observe( @@ -28,8 +30,12 @@ class FragmentViewBindingDelegate( private var fragmentBinding: T? = null - @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) - fun disposeBinding() { + override fun onDestroy(owner: LifecycleOwner) { + disposeBinding() + super.onDestroy(owner) + } + + private fun disposeBinding() { fragmentBinding?.disposeEvents() fragmentBinding = null } diff --git a/app/src/main/java/com/monstarlab/arch/extensions/RepeatOnLifecycle.kt b/app/src/main/java/com/monstarlab/arch/extensions/RepeatOnLifecycle.kt index fde84274..526496ac 100644 --- a/app/src/main/java/com/monstarlab/arch/extensions/RepeatOnLifecycle.kt +++ b/app/src/main/java/com/monstarlab/arch/extensions/RepeatOnLifecycle.kt @@ -87,6 +87,7 @@ public fun LifecycleOwner.addRepeatingJob( * again. * @param block The block to run when the lifecycle is at least in [state] state. */ +@Suppress("LongMethod") public suspend fun Lifecycle.repeatOnLifecycle( state: Lifecycle.State, block: suspend CoroutineScope.() -> Unit @@ -138,4 +139,4 @@ public suspend fun Lifecycle.repeatOnLifecycle( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/monstarlab/arch/extensions/UseCaseExtensions.kt b/app/src/main/java/com/monstarlab/arch/extensions/UseCaseResult.kt similarity index 66% rename from app/src/main/java/com/monstarlab/arch/extensions/UseCaseExtensions.kt rename to app/src/main/java/com/monstarlab/arch/extensions/UseCaseResult.kt index 0592b728..d2c8af66 100644 --- a/app/src/main/java/com/monstarlab/arch/extensions/UseCaseExtensions.kt +++ b/app/src/main/java/com/monstarlab/arch/extensions/UseCaseResult.kt @@ -23,6 +23,7 @@ suspend inline fun safeUseCase( UseCaseResult.Error(e.toError()) } +@Suppress("TooGenericExceptionCaught") inline fun useCaseFlow( crossinline block: suspend () -> T, ): Flow> = flow { @@ -36,25 +37,28 @@ inline fun useCaseFlow( } } -fun observableFlow(block: suspend FlowCollector.() -> Unit): Flow> = flow(block) - .catch { exception -> - Timber.e(exception) - UseCaseResult.Error(exception.toError()) - } - .map { - UseCaseResult.Success(it) - } +fun observableFlow(block: suspend FlowCollector.() -> Unit): Flow> = + flow(block) + .catch { exception -> + Timber.e(exception) + UseCaseResult.Error(exception.toError()) + } + .map { + UseCaseResult.Success(it) + } -fun Flow>.onSuccess(action: suspend (T) -> Unit): Flow> = transform { result -> - if (result is UseCaseResult.Success) { - action(result.value) +fun Flow>.onSuccess(action: suspend (T) -> Unit): Flow> = + transform { result -> + if (result is UseCaseResult.Success) { + action(result.value) + } + return@transform emit(result) } - return@transform emit(result) -} -fun Flow>.onError(action: suspend (ErrorModel) -> Unit): Flow> = transform { result -> - if (result is UseCaseResult.Error) { - action(result.error) +fun Flow>.onError(action: suspend (ErrorModel) -> Unit): Flow> = + transform { result -> + if (result is UseCaseResult.Error) { + action(result.error) + } + return@transform emit(result) } - return@transform emit(result) -} diff --git a/app/src/main/java/com/monstarlab/core/data/network/dtos/ResourceDto.kt b/app/src/main/java/com/monstarlab/core/data/network/dtos/ResourceDto.kt index 282ef9ed..7816755b 100644 --- a/app/src/main/java/com/monstarlab/core/data/network/dtos/ResourceDto.kt +++ b/app/src/main/java/com/monstarlab/core/data/network/dtos/ResourceDto.kt @@ -1,5 +1,6 @@ package com.monstarlab.core.data.network.dtos +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable @@ -8,5 +9,6 @@ data class ResourceDto( val name: String, val year: Int, val color: String, - val pantone_value: String + @SerialName("pantone_value") + val pantoneValue: String ) diff --git a/app/src/main/java/com/monstarlab/core/data/network/dtos/ResourceMapping.kt b/app/src/main/java/com/monstarlab/core/data/network/dtos/ResourceMapping.kt index 854596bb..72d41dad 100644 --- a/app/src/main/java/com/monstarlab/core/data/network/dtos/ResourceMapping.kt +++ b/app/src/main/java/com/monstarlab/core/data/network/dtos/ResourceMapping.kt @@ -8,6 +8,6 @@ fun ResourceDto.toEntity(): Resource { name = name, year = year, color = color, - pantoneValue = pantone_value + pantoneValue = pantoneValue ) } diff --git a/app/src/main/java/com/monstarlab/core/data/network/dtos/UserDto.kt b/app/src/main/java/com/monstarlab/core/data/network/dtos/UserDto.kt index 7fb6ec4c..2a9e18c9 100644 --- a/app/src/main/java/com/monstarlab/core/data/network/dtos/UserDto.kt +++ b/app/src/main/java/com/monstarlab/core/data/network/dtos/UserDto.kt @@ -1,12 +1,15 @@ package com.monstarlab.core.data.network.dtos +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class UserDto( val id: Int, val email: String, - val first_name: String, - val last_name: String, + @SerialName("first_name") + val firstName: String, + @SerialName("last_name") + val lastName: String, val avatar: String ) diff --git a/app/src/main/java/com/monstarlab/core/data/network/dtos/UserMapping.kt b/app/src/main/java/com/monstarlab/core/data/network/dtos/UserMapping.kt index 3ace5075..b255a911 100644 --- a/app/src/main/java/com/monstarlab/core/data/network/dtos/UserMapping.kt +++ b/app/src/main/java/com/monstarlab/core/data/network/dtos/UserMapping.kt @@ -5,8 +5,8 @@ import com.monstarlab.core.domain.model.User fun UserDto.toUser(): User { return User( email = email, - firstName = first_name, - lastName = last_name, + firstName = firstName, + lastName = lastName, avatar = avatar ) } diff --git a/app/src/main/java/com/monstarlab/core/data/network/responses/ResourcesResponse.kt b/app/src/main/java/com/monstarlab/core/data/network/responses/ResourcesResponse.kt index 747ea2e0..9c6ee395 100644 --- a/app/src/main/java/com/monstarlab/core/data/network/responses/ResourcesResponse.kt +++ b/app/src/main/java/com/monstarlab/core/data/network/responses/ResourcesResponse.kt @@ -1,13 +1,16 @@ package com.monstarlab.core.data.network.responses import com.monstarlab.core.data.network.dtos.ResourceDto +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class ResourcesResponse( val page: Int, - val per_page: Int, + @SerialName("per_page") + val perPage: Int, val total: Int, - val total_pages: Int, + @SerialName("total_pages") + val totalPages: Int, val data: List ) diff --git a/app/src/main/java/com/monstarlab/core/domain/error/ErrorMapping.kt b/app/src/main/java/com/monstarlab/core/domain/error/ErrorMapping.kt index 572f9e06..6ced3348 100644 --- a/app/src/main/java/com/monstarlab/core/domain/error/ErrorMapping.kt +++ b/app/src/main/java/com/monstarlab/core/domain/error/ErrorMapping.kt @@ -6,6 +6,7 @@ import java.io.IOException import java.net.SocketTimeoutException import java.net.UnknownHostException +@Suppress("MagicNumber") fun Response.toError(): ErrorModel.Http { return when { code() == 400 -> ErrorModel.Http.BadRequest diff --git a/app/src/main/java/com/monstarlab/core/domain/mock/MockFlows.kt b/app/src/main/java/com/monstarlab/core/domain/mock/MockFlows.kt deleted file mode 100644 index d1e17840..00000000 --- a/app/src/main/java/com/monstarlab/core/domain/mock/MockFlows.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.monstarlab.core.domain.mock - -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow - -object MockFlows { - - fun mockString(): Flow = flow { - delay(2000) - throw RuntimeException("lol") - emit("Hello world from Flow") - } - - fun mockFlag(): Flow = flow { - delay(1000) - emit(false) - delay(2000) - emit(true) - } -} diff --git a/app/src/main/java/com/monstarlab/core/domain/mock/MockSuspends.kt b/app/src/main/java/com/monstarlab/core/domain/mock/MockSuspends.kt deleted file mode 100644 index 584784b3..00000000 --- a/app/src/main/java/com/monstarlab/core/domain/mock/MockSuspends.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.monstarlab.core.domain.mock - -import kotlinx.coroutines.delay - -object MockSuspends { - - suspend fun fetchString(): String { - delay(5000) - return "Hello world from suspend" - } -} diff --git a/app/src/main/java/com/monstarlab/core/sharedui/errorhandling/ViewErrorController.kt b/app/src/main/java/com/monstarlab/core/sharedui/errorhandling/ViewErrorController.kt index 0e498ad1..43124774 100644 --- a/app/src/main/java/com/monstarlab/core/sharedui/errorhandling/ViewErrorController.kt +++ b/app/src/main/java/com/monstarlab/core/sharedui/errorhandling/ViewErrorController.kt @@ -7,7 +7,11 @@ import com.google.android.material.snackbar.Snackbar import com.monstarlab.core.domain.error.ErrorModel import javax.inject.Inject -fun Fragment.showErrorDialog(error: ViewError, cancelable: Boolean = true, dismissAction: (() -> Unit)? = null) { +fun Fragment.showErrorDialog( + error: ViewError, + cancelable: Boolean = true, + dismissAction: (() -> Unit)? = null +) { val builder = AlertDialog.Builder(requireContext()) builder.setTitle(error.title) builder.setMessage(error.message) @@ -26,7 +30,12 @@ fun Fragment.showErrorDialog(error: ViewError, cancelable: Boolean = true, dismi } } -fun Fragment.showErrorSnackbar(view: View, error: ViewError, showAction: Boolean = false, dismissAction: (() -> Unit)? = null) { +fun Fragment.showErrorSnackbar( + view: View, + error: ViewError, + showAction: Boolean = false, + dismissAction: (() -> Unit)? = null, +) { val showLength = if (showAction) Snackbar.LENGTH_INDEFINITE else Snackbar.LENGTH_LONG val snackbar = Snackbar.make(view, error.message, showLength) if (showAction) { @@ -41,6 +50,7 @@ fun Fragment.showErrorSnackbar(view: View, error: ViewError, showAction: Boolean } } +@Suppress("LongMethod") fun ErrorModel.mapToViewError(): ViewError { return when (this) { is ErrorModel.Http.Forbidden, diff --git a/app/src/main/java/com/monstarlab/features/login/LoginFragment.kt b/app/src/main/java/com/monstarlab/features/login/LoginFragment.kt index 43429260..6aa5941e 100644 --- a/app/src/main/java/com/monstarlab/features/login/LoginFragment.kt +++ b/app/src/main/java/com/monstarlab/features/login/LoginFragment.kt @@ -10,11 +10,7 @@ import androidx.lifecycle.viewErrorFlow import androidx.navigation.fragment.findNavController import androidx.transition.TransitionManager import com.monstarlab.R -import com.monstarlab.arch.extensions.collectFlow -import com.monstarlab.arch.extensions.onClick -import com.monstarlab.arch.extensions.snackErrorFlow -import com.monstarlab.arch.extensions.viewBinding -import com.monstarlab.arch.extensions.visibilityFlow +import com.monstarlab.arch.extensions.* import com.monstarlab.databinding.FragmentLoginBinding import dagger.hilt.android.AndroidEntryPoint @@ -26,26 +22,32 @@ class LoginFragment : Fragment(R.layout.fragment_login) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + bindEvent() + collectFlows() + } + private fun bindEvent() { binding.loginButton.onClick { viewModel.login( binding.loginEmailEditText.text.toString(), binding.loginPasswordEditText.text.toString() ) } + } + private fun collectFlows() { collectFlow(viewModel.loginResultFlow) { findNavController().navigate(R.id.resourceFragment) } - snackErrorFlow(viewModel.viewErrorFlow, view) - visibilityFlow(viewModel.loadingFlow, binding.loginProgressBar) - collectFlow(viewModel.loadingFlow) { loading -> TransitionManager.beginDelayedTransition(binding.root) binding.loginEmailEditText.isEnabled = !loading binding.loginPasswordEditText.isEnabled = !loading binding.loginButton.isVisible = !loading } + + snackErrorFlow(viewModel.viewErrorFlow, binding.root) + visibilityFlow(viewModel.loadingFlow, binding.loginProgressBar) } } diff --git a/app/src/main/java/com/monstarlab/features/main/setupNStack.kt b/app/src/main/java/com/monstarlab/features/main/setupNStack.kt index 70810ca8..705acb85 100644 --- a/app/src/main/java/com/monstarlab/features/main/setupNStack.kt +++ b/app/src/main/java/com/monstarlab/features/main/setupNStack.kt @@ -94,9 +94,9 @@ fun MainActivity.showUpdateDialog(appUpdate: AppUpdate) { fun MainActivity.showChangelogDialog(appUpdate: AppUpdate) { AlertDialog.Builder(this) - .setTitle(appUpdate.update?.translate?.title ?: return) - .setMessage(appUpdate.update?.translate?.message ?: return) - .setNegativeButton(appUpdate.update?.translate?.negativeButton ?: return) { dialog, _ -> + .setTitle(appUpdate.update?.translate?.title ?: "") + .setMessage(appUpdate.update?.translate?.message ?: "") + .setNegativeButton(appUpdate.update?.translate?.negativeButton ?: "") { dialog, _ -> dialog.dismiss() } .show() diff --git a/app/src/main/java/com/monstarlab/features/resources/ResourceAdapter.kt b/app/src/main/java/com/monstarlab/features/resources/ResourceAdapter.kt index c8018aef..8cae87cb 100644 --- a/app/src/main/java/com/monstarlab/features/resources/ResourceAdapter.kt +++ b/app/src/main/java/com/monstarlab/features/resources/ResourceAdapter.kt @@ -16,7 +16,8 @@ class ResourceAdapter : RecyclerView.Adapter } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ResourceViewHolder { - val itemBinding = ItemResourceBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val itemBinding = + ItemResourceBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ResourceViewHolder(itemBinding) } @@ -28,7 +29,8 @@ class ResourceAdapter : RecyclerView.Adapter return resources.size } - inner class ResourceViewHolder(private val itemBinding: ItemResourceBinding) : RecyclerView.ViewHolder(itemBinding.root) { + inner class ResourceViewHolder(private val itemBinding: ItemResourceBinding) : + RecyclerView.ViewHolder(itemBinding.root) { fun bind(resource: Resource) { itemBinding.resourceTitleTextView.text = resource.name } diff --git a/app/src/main/java/com/monstarlab/injection/modules/RestModule.kt b/app/src/main/java/com/monstarlab/injection/modules/RestModule.kt index 47f0fe33..73bd6cbe 100644 --- a/app/src/main/java/com/monstarlab/injection/modules/RestModule.kt +++ b/app/src/main/java/com/monstarlab/injection/modules/RestModule.kt @@ -7,6 +7,7 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient @@ -36,6 +37,11 @@ class RestModule { return clientBuilder.build() } + private val json = Json { + ignoreUnknownKeys = true + } + + @ExperimentalSerializationApi @Provides @Singleton fun provideRetrofit(client: OkHttpClient): Retrofit { @@ -43,9 +49,7 @@ class RestModule { .client(client) .baseUrl(BuildConfig.API_URL) .addConverterFactory( - Json { - ignoreUnknownKeys = true - }.asConverterFactory("application/json".toMediaType()) + json.asConverterFactory("application/json".toMediaType()) ) .build() } diff --git a/build.gradle b/build.gradle index ca5b03dc..a7baaf4e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext.versions = [ - kotlin_plugin : '1.6.0', + kotlin_plugin : '1.6.10', kotlin : '1.4.10', appcompat : '1.4.0', coroutines : '1.5.2', @@ -43,10 +43,10 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.3' + classpath 'com.android.tools.build:gradle:7.0.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin_plugin}" classpath "dk.nodes.nstack:translation:${versions.nstack_gradle_plugin}" classpath "com.google.dagger:hilt-android-gradle-plugin:${versions.hilt}" @@ -57,13 +57,16 @@ buildscript { plugins { id "com.diffplug.spotless" version "5.9.0" + id 'io.gitlab.arturbosch.detekt' version "1.19.0" } allprojects { repositories { google() - jcenter() + mavenCentral() } + + apply from: "$rootDir/detekt.gradle" } subprojects { diff --git a/detekt-config.yml b/detekt-config.yml new file mode 100644 index 00000000..0f4cae7c --- /dev/null +++ b/detekt-config.yml @@ -0,0 +1,346 @@ +build: + maxIssues: 10 + weights: + complexity: 2 + formatting: 1 + LongParameterList: 1 + comments: 1 + +processors: + active: true + exclude: + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ClassCountProcessor' + # - 'PackageCountProcessor' + # - 'KtFileCountProcessor' + +console-reports: + active: true + exclude: + # - 'ProjectStatisticsReport' + # - 'ComplexityReport' + # - 'NotificationReport' + # - 'FindingsReport' + # - 'BuildFailureReport' + +output-reports: + active: true + exclude: + # - 'PlainOutputReport' + # - 'XmlOutputReport' + +comments: + active: true + CommentOverPrivateFunction: + active: false + CommentOverPrivateProperty: + active: false + EndOfSentenceFormat: + active: false + endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!]$) + UndocumentedPublicClass: + active: false + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + UndocumentedPublicFunction: + active: false + +complexity: + active: true + ComplexCondition: + active: true + threshold: 3 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + ComplexMethod: + active: true + threshold: 10 + LabeledExpression: + active: false + LargeClass: + active: true + threshold: 150 + LongMethod: + active: true + threshold: 20 + LongParameterList: + active: true + functionThreshold: 10 + constructorThreshold: 10 + ignoreDefaultParameters: false + MethodOverloading: + active: false + threshold: 5 + NestedBlockDepth: + active: true + threshold: 3 + StringLiteralDuplication: + active: false + threshold: 2 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: true + thresholdInFiles: 10 + thresholdInClasses: 10 + thresholdInInterfaces: 10 + thresholdInObjects: 10 + thresholdInEnums: 10 + +empty-blocks: + active: true + EmptyCatchBlock: + active: true + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: false + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: false + methodNames: 'toString,hashCode,equals,finalize' + InstanceOfCheckForException: + active: false + NotImplementedDeclaration: + active: false + PrintStackTrace: + active: false + RethrowCaughtException: + active: false + ReturnFromFinally: + active: false + SwallowedException: + active: false + ThrowingExceptionFromFinally: + active: false + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: false + exceptions: 'IllegalArgumentException,IllegalStateException,IOException' + ThrowingNewInstanceOfSameException: + active: false + TooGenericExceptionCaught: + active: true + exceptionNames: + - ArrayIndexOutOfBoundsException + - Error + - Exception + - IllegalMonitorStateException + - NullPointerException + - IndexOutOfBoundsException + - RuntimeException + - Throwable + TooGenericExceptionThrown: + active: true + exceptionNames: + - Error + - Exception + - NullPointerException + - Throwable + - RuntimeException + +naming: + active: true + ClassNaming: + active: true + classPattern: '[A-Z$][a-zA-Z0-9$]*' + EnumNaming: + active: true + enumEntryPattern: '^[A-Z$][a-zA-Z_$]*$' + ForbiddenClassName: + active: false + forbiddenName: '' + FunctionMaxLength: + active: false + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + minimumFunctionNameLength: 3 + FunctionNaming: + active: true + functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' + MatchingDeclarationName: + active: true + MemberNameEqualsClassName: + active: false + ignoreOverridden: true + ObjectPropertyNaming: + active: true + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + packagePattern: '^[a-z]+(\.[a-z][a-z0-9]*)*$' + TopLevelPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[a-z][A-Za-z\d]*' + privatePropertyPattern: '(_)?[a-z][A-Za-z0-9]*' + VariableMaxLength: + active: false + maximumVariableNameLength: 64 + VariableMinLength: + active: false + minimumVariableNameLength: 1 + VariableNaming: + active: true + variablePattern: '(_)?[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + +performance: + active: true + ForEachOnRange: + active: true + SpreadOperator: + active: true + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + DuplicateCaseInWhenExpression: + active: true + EqualsAlwaysReturnsTrueOrFalse: + active: false + EqualsWithHashCodeExist: + active: true + ExplicitGarbageCollectionCall: + active: true + InvalidRange: + active: false + IteratorHasNextCallsNextMethod: + active: false + IteratorNotThrowingNoSuchElementException: + active: false + LateinitUsage: + active: false + excludeAnnotatedProperties: "" + ignoreOnClassesPattern: "" + UnconditionalJumpStatementInLoop: + active: false + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: false + UnsafeCast: + active: false + UselessPostfixExpression: + active: false + WrongEqualsTypeParameter: + active: false + +style: + active: true + CollapsibleIfStatements: + active: false + DataClassContainsFunctions: + active: false + conversionFunctionPrefix: 'to' + EqualsNullCall: + active: false + ExpressionBodySyntax: + active: false + ForbiddenComment: + active: true + values: 'FIXME:,STOPSHIP:' + ForbiddenImport: + active: false + imports: '' + FunctionOnlyReturningConstant: + active: false + ignoreOverridableFunction: true + excludedFunctions: 'describeContents' + LoopWithTooManyJumpStatements: + active: false + maxJumpCount: 1 + MagicNumber: + active: true + ignoreNumbers: '-1,0,1,2' + ignoreHashCodeFunction: false + ignorePropertyDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: false + MaxLineLength: + active: true + maxLineLength: 120 + excludePackageStatements: false + excludeImportStatements: false + ModifierOrder: + active: true + NestedClassesVisibility: + active: false + NewLineAtEndOfFile: + active: false + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: false + OptionalWhenBraces: + active: false + ProtectedMemberInFinalClass: + active: false + RedundantVisibilityModifierRule: + active: false + ReturnCount: + active: true + max: 2 + excludedFunctions: "equals" + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: false + SpacingBetweenPackageAndImports: + active: false + ThrowsCount: + active: true + max: 2 + UnnecessaryAbstractClass: + active: false + UnnecessaryInheritance: + active: false + UnnecessaryParentheses: + active: false + UntilInsteadOfRangeTo: + active: false + UnusedImports: + active: false + UseDataClass: + active: false + excludeAnnotatedClasses: "" + UtilityClassWithPublicConstructor: + active: false + WildcardImport: + active: false diff --git a/detekt.gradle b/detekt.gradle new file mode 100644 index 00000000..a9a42e51 --- /dev/null +++ b/detekt.gradle @@ -0,0 +1,7 @@ +apply plugin: 'io.gitlab.arturbosch.detekt' + +detekt { + buildUponDefaultConfig = true // preconfigure defaults + allRules = false // activate all available (even unstable) rules. + config = files("$rootDir/detekt-config.yml") // point to your custom config defining rules to run, overwriting default behavior +}