# Android Code Style Guide
Before reading this checkout this sources:
- [Official Kotlin style guide](https://kotlinlang.org/docs/reference/coding-conventions.html)
- [Android Architecture](https://hackmd.io/s/BJg82eA7N)
- Clean Code
- [Representing State](https://www.youtube.com/watch?v=-lVVfxsRjcY)
**Download & import:** [Kotlin Code Style for Android Studio](https://drive.google.com/file/d/1E-Z-4LoM9ycjDMSlnNvzRmGpnEG1DzsC/view?usp=sharing)
## Code organization
Code organized by packages as stated in [architecture guide](https://hackmd.io/s/BJg82eA7N).
### Class layout
Class structure has following order:
- initializer
- companion object
- fields declaration
- methods declaration
- nested classes declaration
#### Initializers
##### Arguments
If constructor arguments doesn't fit in one line or it has class scoped arguments each argument should be states on a new line.
```kotlin=
// Good
class A(
private val name: String,
private val title: String
)
// Good
class Derived(count: Int) : Base(count)
// Bad
class Adapter(private val data: List<String>) {
...
}
```
##### JvmOverloads
If class extends from `View` class it will be good to use `JvmOverloads` annotation.
```kotlin=
class CustomView
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View
```
#### Companion object
For regular classes `companion object` should be at the top of class (exceptions -- model classes, dagger modules). Companion object follows the same structure rules as a regular classes.
```kotlin=
class A {
companion object {
const val NAME = "name"
fun newInstance(): A =
A()
}
}
```
#### Fields declaration
In first place should be declared fields that will be injected.
```kotlin=
class A {
@Inject
internal lateinit var fieldA: FieldA
@Inject
internal lateinit var fiedlB: FieldB
private val b = B()
}
```
#### Methods declaration
##### Order
Methods declaration ordering has following rules:
- methods should follow order in superclass
- lifecycle methods should follow call order
- methods should be ordered by mention
```kotlin=
// Good
class A : Activity {
override fun onStart() {
super.onStart()
attach()
}
override fun onStop() {
detach()
super.onStop()
}
private fun attach() { ... }
private fun detach() { ... }
}
// Bad
class B : Activity {
private fun detach() { ... }
override fun onStop() {
detach()
super.onStop()
}
private fun attach() { ... }
override fun onStart() {
super.onStart()
attach()
}
}
```
##### Types
It's preferred for all methods to have explicitly specified type.
```kotlin=
class A(c: C) {
// Good
fun getName(): String =
resolveCalcs()
// Good
private fun resolveCalcs(): String =
c.resolveCalcs()
// Very bad
fun getName() = resolveCalcs()
// Bad
private fun resolveCalcs() = c.resolveCalcs()
}
```
##### Expression body
Expression body is preferred when it is possible to use it.
```kotlin=
// Good
fun getResponse(): Response =
api.getResponse()
// Bad
fun getResponse(): Response {
return api.getResponse()
}
```
#### Nested classes
Nested classes should be at the end of parent class (view state is exception). `inner` keyword should be avoided if it possible.
```kotlin=
class A {
fun f() { ... }
class B {
...
}
}
```
Nested classes shouldn't be referenced without parent.
```kotlin=
class Video {
class Url
}
// Good
fun openVideoUrl(videoUrl: Video.Url) { ... }
// Bad
fun openVideoUrl(url: Url) { ... }
```
### Model classes
Model classes follows the general rules for classes.
#### Serialization
If model class supposed to be used in network call it should have `@SerializedName` on each valuable field.
```kotlin=
class Video(
@SerializedName("id")
val id: Long,
@SerializedName("url")
val url: String
)
```
Also developer should keep in mind that Gson ingonres Kotlin default values.
#### Data classes
Data classes should be used only on demand. So if there is a class that is response wrapper and it won't be used or created manually there is no reason to use `data class` keyword.
```kotlin=
// Good
class Response(
@SerializedName("name")
val name: String?
)
// Bad
data class Response(
@SerializedName("name")
val name: String?
)
```
### Extensions
Global extensions should be placed in file with name: original class name + `Extensions` suffix on appropriate code layer. Also global extension should solve "global" problems.
```kotlin=
// Good
fun LongArray?.isNullOrEmpty(): Boolean =
this == null || this.isEmpty()
// Bad
val String.USER_AGENT: String
get() = "Android ${BuildConfig.APPLICATION_ID}"
// Bad
fun List<String>.getFullName(): String =
this[0] + " " + this[1]
```
## MVP
MVP pattern implemented on top of Android Architecture components that make it possible to preserve view state on configuration changes.
### View contract
View state is usually represented by `State` object with addition one shoot methods. If some atom cannot be represented by state it should be expressed as one shoot action.
View `State` object is implemented with `sealed class` with required payload.
```kotlin=
// Good
interface CourseView {
sealed class State {
object NoConnection : State()
object Loading : State()
class CourseLoaded(val course: Course) : State()
}
fun setState(state: State)
// one shoot method as it doesn't describe state
fun showSectionScreen(section: Section)
}
// Bad
interface CourseView {
sealed class State {
object NoConnection : State()
object Loading : State()
class CourseLoaded(val course: Course) : State()
// doesn't describe state
class ShowSectionScreen(val section: Section) : State()
}
}
```
## Dependency injection
Dependency injection works on top of Dagger.
### Modules
It is good to split modules by logic. Predefined modules names are:
- FeatureDataModule -- for `domain` + `data` + `remote` + `cache` layers
- FeatureRoutingModule -- for routing
- FeatureModule -- for `presentation` + `view` layer
### Inject annotation
We prefer `@Inject` annotation on constructor instead of manual constructor call in module.
```kotlin=
// Good
class InjecteeImpl
@Inject
constructor(
private val injectee1: Injectee1
) : Injectee
abstract class FeatureModule {
@Binds
internal abstract fun bindInjectee(
injectee: InjecteeImpl
): Injectee
}
```
```kotlin=
// Bad
class InjecteeImpl(
private val injectee1: Injectee1
) : Injectee
abstract class FeatureModule {
@Provides
internal fun provideInjectee(injectee1: Injectee1): Injectee =
InjecteeImpl(injectee1)
}
```
### Modificators
We prefer `private val` modificator for constructor injected properties and `internal lateinit var` for fields. Also it is prefered to use `internal` in dagger modules and components.
```kotlin=
// Good
class InjecteeImpl
@Inject
constructor(
private val injectee1: Injectee1
)
class Injected {
@Inject
internal lateinit var injectee1: Injectee1
}
abstract class FeatureModule {
@Binds
internal abstract fun bindInjectee(
injectee: InjecteeImpl
): Injectee
}
```
```kotlin=
// Bad
class InjecteeImpl
@Inject
constructor(
var injectee1: Injectee1
)
class Injected {
@Inject
var injectee1: Injectee1? = null
}
abstract class FeatureModule {
@Binds
abstract fun bindInjectee(
injectee: InjecteeImpl
): Injectee
}
```
## Common practices
### Guards
Prefer to use guards in methods instead of nested if. Guards should be on top of method.
```kotlin=
sealed class State {
object Loading : State()
class Success(val payload: String?) : State()
}
// ok
fun changeState(currentState: State?) {
val payload = (currentState as? State.Success)
?.payload
?: return
...
}
// Bad
fun changeState(currentState: State?) {
if (currentState != null) {
if (currentState is State.Success) {
if (currentState.payload != null) {
...
}
}
}
}
// Not ok
fun changeState(currentState: State?) {
if (currentState == null
|| currentState !is State.Success
|| currentState.payload == null
) {
return
}
...
}
```
### Horizontal alignment
Horizontal alignment is allowed only for named arguments.
```kotlin=
// moderate
val someNewValue =
Value(
id = 24,
title = "Title",
description = "Description",
cover = "Cover",
summary = "Summary",
workload = "Workload",
intro = "Intro",
language = "Language"
)
// bad
val name : String = "name"
val title : String = "title"
val description: String = "description"
```
### Constraint Layout
**Do not** use `ConstraintLayout` for every layout. It's better to use simpler layout if it possible.
### Support & AndroidX
Support & AndroidX views is preferred over default ones.
## Resources
### Naming
#### Non layout files
Prefixes:
- `bg_` prefix for all background or foreground resources
- `ic_` prefix for all icons
- `color_` prefix for color resources
Suffixes:
- `_selector` suffix for selectors
- `_ripple` suffix for ripple variants resources (only for projects with minSdk < 21)
- suffixes for states
Prefixes & suffixes can be combined.
#### Layout files
Prefixes:
- `activity_` for activity layout
- `fragment_` for fragment layout
- `layout_` for `include` layouts
- `item_` for recycler view items
- `view_` for custom view
#### Common
All resources names after prefixes described above should also contain feature name.
Examples:
- `activity_restaurants_map.xml`
- `layout_restaurant_header.xml`
```xml
<string name="restaurant_header_title">Restaurant</string>
<string name="restaurant_header_action_book">Book table</string>
```
### Clickable views
All clickable views should have visual response on clicks. By default you can use `?selectableItemBackground` or `?selectableItemBackgroundBorderless` for square and round views. Or you should create your own background drawable with ripple effect for this view if it necessary.