Skip to main content
Common modules provide reusable utilities, extensions, and base classes shared across the application. They are divided into platform-agnostic (common) and Android-specific (commonAndroid) modules.

Module Overview

Common

Platform-agnostic utilities and extensions

Common Android

Android-specific UI components and utilities

Common Module

Module: common Package: es.mobiledev.common Provides platform-agnostic utilities, extensions, and common constants.

String Extensions

File: common/src/main/java/es/mobiledev/common/extensions/StringExtensions.kt Extension functions for safe string parsing.
fun String.toSafeInt(): Int?
fun String.toSafeLong(): Long?
fun String.toLocalDate(): LocalDate?

toSafeInt()

toSafeInt
extension function
Safely converts a string to an integer, returning null on failureReturns: Int? - Parsed integer or null if parsing fails
Usage:
val age = "25".toSafeInt() // Returns 25
val invalid = "abc".toSafeInt() // Returns null

toSafeLong()

toSafeLong
extension function
Safely converts a string to a long, returning null on failureReturns: Long? - Parsed long or null if parsing fails
Usage:
val timestamp = "1640995200000".toSafeLong() // Returns 1640995200000L
val invalid = "xyz".toSafeLong() // Returns null

toLocalDate()

toLocalDate
extension function
Parses a string to LocalDate using the app’s date pattern formatReturns: LocalDate? - Parsed date or null if parsing fails
Usage:
val date = "2024-03-15".toLocalDate()
if (date != null) {
    println("Year: ${date.year}")
}
These extensions use try-catch blocks internally, making null checks cleaner than explicit exception handling.

Date Extensions

Package: es.mobiledev.common.extensions Extension functions for date manipulation.
fun LocalDate.toFormattedString(): String
fun Long.toLocalDate(): LocalDate

AppDispatchers

File: common/src/main/java/es/mobiledev/common/util/AppDispatchers.kt:5 Wrapper class for coroutine dispatchers, enabling testability.
class AppDispatchers(
    val main: CoroutineDispatcher,
    val default: CoroutineDispatcher,
    val io: CoroutineDispatcher,
)
main
CoroutineDispatcher
required
Dispatcher for UI operations (Main thread)
default
CoroutineDispatcher
required
Dispatcher for CPU-intensive work
io
CoroutineDispatcher
required
Dispatcher for IO operations (network, database)
Usage:
class MyRepository(
    private val dispatchers: AppDispatchers
) {
    suspend fun loadData() = withContext(dispatchers.io) {
        // Perform IO operation
    }
}
Using AppDispatchers instead of Dispatchers directly allows injecting test dispatchers in unit tests.

CommonConstants

const val DATE_PATTERN_FORMAT = "yyyy-MM-dd"
const val DATE_TIME_PATTERN_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"

CommonAndroid Module

Module: commonAndroid Package: es.mobiledev.commonandroid Provides Android-specific base classes, UI components, and utilities.

Base Classes

BaseViewModel

File: commonAndroid/src/main/java/es/mobiledev/commonandroid/ui/base/BaseViewModel.kt:17 Base ViewModel class with built-in state management.
abstract class BaseViewModel<T> : ViewModel() {
    protected abstract val uiState: MutableStateFlow<UiState<T>>

    fun getUiState(): StateFlow<UiState<T>> = uiState.asStateFlow()

    fun MutableStateFlow<UiState<T>>.updateState(block: (T) -> T)
    fun MutableStateFlow<UiState<T>>.loadingState()
    fun MutableStateFlow<UiState<T>>.successState(block: (T) -> T)
}
T
generic type
required
Type parameter for the UI state data
Methods:
getUiState
function
Returns the UI state as a read-only StateFlowReturns: StateFlow<UiState<T>>
updateState
extension function
Updates the data within the current UI state
block
(T) -> T
required
Lambda that transforms current state to new state
loadingState
extension function
Sets the UI state to loading
successState
extension function
Sets the UI state to success and updates the data
block
(T) -> T
required
Lambda that transforms current state to new state
Usage Example:
class HomeViewModel(
    private val getArticles: GetArticlesUseCase
) : BaseViewModel<HomeUiData>() {
    override val uiState = MutableStateFlow(
        UiState(data = HomeUiData())
    )

    fun loadArticles() {
        viewModelScope.launch {
            uiState.loadingState()
            try {
                getArticles(limit = 20, offset = 0)
                    .collect { response ->
                        uiState.successState { currentData ->
                            currentData.copy(
                                articles = response.results
                            )
                        }
                    }
            } catch (e: Exception) {
                // Handle error
            }
        }
    }
}

UiState

Wrapper class for UI state with loading indicator.
data class UiState<T>(
    val data: T,
    val isLoading: Boolean = false
)
data
T
required
The actual UI data
isLoading
Boolean
default:"false"
Loading indicator flag

BaseScreen

File: commonAndroid/src/main/java/es/mobiledev/commonandroid/ui/base/BaseScreen.kt:39 Base composable for screens with built-in loading state.
@Composable
fun BaseScreen(
    modifier: Modifier = Modifier,
    isLoading: Boolean = false,
    topBar: @Composable () -> Unit = EmptyComposable,
    bottomBar: @Composable () -> Unit = EmptyComposable,
    content: @Composable (PaddingValues) -> Unit = {},
)
modifier
Modifier
default:"Modifier"
Modifier to be applied to the screen
isLoading
Boolean
default:"false"
Whether to show loading indicator
topBar
@Composable () -> Unit
default:"EmptyComposable"
Top app bar content
bottomBar
@Composable () -> Unit
default:"EmptyComposable"
Bottom bar content
content
@Composable (PaddingValues) -> Unit
default:"{}"
Screen content receiving padding values
Features:
  • Automatic loading indicator display
  • Scaffold-based layout
  • Padding management for top/bottom bars
Usage:
@Composable
fun MyScreen() {
    val viewModel: MyViewModel = hiltViewModel()
    val uiState by viewModel.getUiState().collectAsStateWithLifecycle()

    BaseScreen(
        isLoading = uiState.isLoading,
        topBar = { TopAppBar(title = { Text("My Screen") }) }
    ) { paddingValues ->
        Column(modifier = Modifier.padding(paddingValues)) {
            // Screen content
        }
    }
}

UI Components

ArticleItem

Composable for displaying article list items.
@Composable
fun ArticleItem(
    article: ArticleBo,
    isFavorite: Boolean,
    onArticleClick: (Long) -> Unit,
    onFavoriteClick: (ArticleBo, Boolean) -> Unit,
    modifier: Modifier = Modifier
)

CPTButton

Custom styled button component.
@Composable
fun CPTButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true
)

Utilities

ComposableUtils

val EmptyComposable: @Composable () -> Unit = {}

IntentUtils

Helper functions for Android intents.
fun Context.openUrl(url: String)
fun Context.shareText(text: String)

TimeUtils

Time formatting and manipulation utilities.
fun Long.toFormattedTime(): String
fun getCurrentTimeMillis(): Long

Architecture Benefits

Platform Agnostic
  • No Android dependencies
  • Pure Kotlin code
  • Reusable across platforms
  • Easy to unit test
// Can be used in any Kotlin project
val id = "123".toSafeLong()

Dependency Graph

The separation between common and commonAndroid follows the principle of least dependency. Pure business logic depends only on common, while UI code uses commonAndroid.

Module Dependencies

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core"
}

Build docs developers (and LLMs) love