This document outlines the coding standards and style guidelines for the NFC Manager project. Following these guidelines ensures consistency, readability, and maintainability across the codebase.
NFC Manager follows modern Android development best practices with a focus on:
- Kotlin-first: 100% Kotlin codebase
- Clean Architecture: MVVM with Repository pattern
- Jetpack Compose: Modern declarative UI
- Nothing OS Design: Consistent visual language
- Readability: Code should be self-documenting
- Simplicity: Prefer simple solutions over complex ones
- Consistency: Follow established patterns throughout the project
- Maintainability: Write code that's easy to modify and extend
- Efficiency: Optimize for performance without sacrificing readability
- Memory: Be mindful of memory usage and leaks
- Battery: Consider battery impact of background operations
- Responsiveness: Keep UI responsive with proper threading
com.dxbmark.nfcmanager/
βββ data/ # Data layer
β βββ database/ # Room database
β β βββ entities/ # Database entities
β β βββ dao/ # Data access objects
β β βββ AppDatabase.kt # Database configuration
β βββ repository/ # Repository pattern
βββ services/ # Background services
βββ ui/ # UI layer
β βββ components/ # Reusable UI components
β βββ screens/ # Screen composables
β βββ theme/ # Theming and design system
βββ viewmodel/ # ViewModels (MVVM)
βββ utils/ # Utility classes
βββ di/ # Dependency injection
βββ MainActivity.kt # Main activity
- Classes: PascalCase (e.g.,
NFCRepository,MainViewModel) - Files: Match class name (e.g.,
NFCRepository.kt) - Resources: snake_case (e.g.,
activity_main.xml,ic_nfc_enabled.xml) - Constants: SCREAMING_SNAKE_CASE (e.g.,
MAX_RETRY_COUNT)
// β
Good: Proper spacing and indentation
class NFCRepository @Inject constructor(
private val nfcEventDao: NFCEventDao,
private val nfcSettingsDao: NFCSettingsDao
) {
fun getAllEvents(): Flow<List<NFCEventEntity>> =
nfcEventDao.getAllEvents()
suspend fun logEvent(
eventType: String,
message: String,
icon: String
): Long {
val event = NFCEventEntity(
timestamp = Date(),
eventType = eventType,
message = message,
icon = icon
)
return nfcEventDao.insertEvent(event)
}
}
// β Bad: Poor spacing and formatting
class NFCRepository @Inject constructor(private val nfcEventDao:NFCEventDao,private val nfcSettingsDao:NFCSettingsDao){
fun getAllEvents():Flow<List<NFCEventEntity>>=nfcEventDao.getAllEvents()
suspend fun logEvent(eventType:String,message:String,icon:String):Long{
val event=NFCEventEntity(timestamp=Date(),eventType=eventType,message=message,icon=icon)
return nfcEventDao.insertEvent(event)}}- Indentation: 4 spaces (no tabs)
- Line Length: 120 characters maximum
- Blank Lines: Use to separate logical sections
- Trailing Spaces: Remove all trailing whitespace
// β
Good: Proper indentation and spacing
class MainViewModel @Inject constructor(
application: Application,
private val repository: NFCRepository
) : AndroidViewModel(application) {
private val _uiState = MutableStateFlow(MainUiState())
val uiState: StateFlow<MainUiState> = _uiState.asStateFlow()
fun refreshNfcStatus() {
viewModelScope.launch {
// Implementation here
}
}
}// β
Good: Clear, descriptive names
class NFCMonitoringService : Service() {
companion object {
private const val TAG = "NFCMonitoringService"
const val ACTION_START_MONITORING = "ACTION_START_MONITORING"
}
private var isMonitoring = false
private lateinit var nfcAdapter: NfcAdapter
fun startNfcMonitoring() {
// Implementation
}
private fun updateNotificationContent(message: String) {
// Implementation
}
}
// β Bad: Unclear, abbreviated names
class NFCMonSvc : Service() {
companion object {
private const val T = "NFCMonSvc"
const val ASM = "ASM"
}
private var isMon = false
private lateinit var nfcAdp: NfcAdapter
fun strtMon() {
// Implementation
}
private fun updNotif(msg: String) {
// Implementation
}
}// β
Good: Clear function structure
suspend fun logEvent(
eventType: String,
message: String,
icon: String,
tagId: String? = null,
isImportant: Boolean = false
): Long {
return try {
val event = NFCEventEntity(
timestamp = Date(),
eventType = eventType,
message = message,
icon = icon,
tagId = tagId,
isImportant = isImportant
)
repository.insertEvent(event)
} catch (e: Exception) {
Log.e(TAG, "Failed to log event", e)
-1L
}
}
// β
Good: Single expression function
fun formatTime(seconds: Long): String = when {
seconds < 60 -> "${seconds}s"
seconds < 3600 -> "${seconds / 60}m ${seconds % 60}s"
else -> "${seconds / 3600}h ${(seconds % 3600) / 60}m"
}class ExampleClass {
// 1. Companion object
companion object {
private const val TAG = "ExampleClass"
}
// 2. Properties (private first, then public)
private val _state = MutableStateFlow(State())
val state: StateFlow<State> = _state.asStateFlow()
// 3. Init blocks
init {
// Initialization code
}
// 4. Public functions
fun publicFunction() {
// Implementation
}
// 5. Private functions
private fun privateFunction() {
// Implementation
}
// 6. Nested classes/interfaces
data class State(
val isLoading: Boolean = false
)
}// β
Good: Well-structured composable
@Composable
fun NFCStatusCard(
isEnabled: Boolean,
onToggle: () -> Unit,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = if (isEnabled) "ENABLED" else "DISABLED",
style = MaterialTheme.typography.headlineMedium,
color = if (isEnabled) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
}
)
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = onToggle,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(text = "TOGGLE NFC")
}
}
}
}
// β Bad: Poor structure and styling
@Composable
fun NFCCard(enabled: Boolean, toggle: () -> Unit) {
Card {
Column {
Text(if (enabled) "ON" else "OFF")
Button(onClick = toggle) { Text("Toggle") }
}
}
}// β
Good: Proper state hoisting
@Composable
fun SettingsScreen(
viewModel: SettingsViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsState()
val settings by viewModel.settings.collectAsState()
SettingsContent(
uiState = uiState,
settings = settings,
onToggleTheme = viewModel::setTheme,
onUpdateInterval = viewModel::updateReminderInterval
)
}
@Composable
private fun SettingsContent(
uiState: SettingsUiState,
settings: NFCSettingsEntity,
onToggleTheme: (Boolean) -> Unit,
onUpdateInterval: (Int) -> Unit
) {
// UI implementation
}
// β Bad: No state hoisting
@Composable
fun BadSettingsScreen() {
var isDarkMode by remember { mutableStateOf(true) }
var interval by remember { mutableStateOf(30) }
// Direct UI manipulation without proper state management
}// β
Good: Proper modifier chaining
@Composable
fun ExampleComposable(
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
.clip(RoundedCornerShape(8.dp))
.background(MaterialTheme.colorScheme.surface)
.clickable { /* action */ }
) {
// Content
}
}
// β Bad: Hardcoded modifiers, no parameter
@Composable
fun BadComposable() {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
// Content - not reusable
}
}// β
Good: Well-documented entity
@Entity(tableName = "nfc_events")
data class NFCEventEntity(
@PrimaryKey(autoGenerate = true)
val id: Long = 0,
val timestamp: Date,
@ColumnInfo(name = "event_type")
val eventType: String,
val message: String,
val icon: String,
@ColumnInfo(name = "tag_id")
val tagId: String? = null,
@ColumnInfo(name = "is_important")
val isImportant: Boolean = false
) {
companion object {
// Event type constants
const val TYPE_NFC_ENABLED = "NFC_ENABLED"
const val TYPE_NFC_DISABLED = "NFC_DISABLED"
const val TYPE_TAG_DETECTED = "TAG_DETECTED"
}
}// β
Good: Comprehensive DAO with clear queries
@Dao
interface NFCEventDao {
@Query("SELECT * FROM nfc_events ORDER BY timestamp DESC")
fun getAllEvents(): Flow<List<NFCEventEntity>>
@Query("""
SELECT * FROM nfc_events
WHERE DATE(timestamp) = DATE('now')
ORDER BY timestamp DESC
LIMIT 15
""")
fun getTodayEvents(): Flow<List<NFCEventEntity>>
@Query("SELECT COUNT(*) FROM nfc_events WHERE DATE(timestamp) = DATE('now')")
fun getTodayEventCount(): Flow<Int>
@Insert
suspend fun insertEvent(event: NFCEventEntity): Long
@Delete
suspend fun deleteEvent(event: NFCEventEntity)
@Query("DELETE FROM nfc_events WHERE timestamp < :cutoffDate")
suspend fun deleteOldEvents(cutoffDate: Date)
}// β
Good: Clean repository implementation
@Singleton
class NFCRepository @Inject constructor(
private val nfcEventDao: NFCEventDao,
private val nfcSettingsDao: NFCSettingsDao
) {
// Expose data as Flow for reactive UI
fun getAllEvents(): Flow<List<NFCEventEntity>> =
nfcEventDao.getAllEvents()
fun getTodayEventCount(): Flow<Int> =
nfcEventDao.getTodayEventCount()
// Suspend functions for one-shot operations
suspend fun logEvent(
eventType: String,
message: String,
icon: String,
isImportant: Boolean = false
): Long {
val event = NFCEventEntity(
timestamp = Date(),
eventType = eventType,
message = message,
icon = icon,
isImportant = isImportant
)
return nfcEventDao.insertEvent(event)
}
// Helper functions with clear names
suspend fun cleanupOldEvents(daysToKeep: Int = 30) {
val cutoffDate = Date(
System.currentTimeMillis() - (daysToKeep * 24 * 60 * 60 * 1000L)
)
nfcEventDao.deleteOldEvents(cutoffDate)
}
}// β
Good: Proper ViewModel implementation
@HiltViewModel
class MainViewModel @Inject constructor(
application: Application,
private val repository: NFCRepository
) : AndroidViewModel(application) {
// Private mutable state
private val _uiState = MutableStateFlow(MainUiState())
// Public read-only state
val uiState: StateFlow<MainUiState> = _uiState.asStateFlow()
// StateIn for data from repository
val todayEvents = repository.getTodayEvents()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)
// Public functions for UI actions
fun refreshNfcStatus() {
viewModelScope.launch {
try {
val isEnabled = withContext(Dispatchers.IO) {
NFCUtils.isNfcEnabled(getApplication())
}
_uiState.value = _uiState.value.copy(isNFCEnabled = isEnabled)
} catch (e: Exception) {
_uiState.value = _uiState.value.copy(
errorMessage = "Failed to check NFC status"
)
}
}
}
// Private helper functions
private fun updateError(message: String) {
_uiState.value = _uiState.value.copy(errorMessage = message)
}
}
// UI State data class
data class MainUiState(
val isLoading: Boolean = false,
val isNFCEnabled: Boolean = false,
val errorMessage: String? = null,
val lastActivity: Date = Date()
)// β
Good: Hilt module structure
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
@Singleton
fun provideAppDatabase(
@ApplicationContext context: Context
): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"nfc_manager_database"
)
.fallbackToDestructiveMigration()
.build()
}
@Provides
fun provideNfcEventDao(database: AppDatabase): NFCEventDao {
return database.nfcEventDao()
}
@Provides
fun provideNfcSettingsDao(database: AppDatabase): NFCSettingsDao {
return database.nfcSettingsDao()
}
}
// β
Good: Hilt annotations usage
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
lateinit var repository: NFCRepository
private val mainViewModel: MainViewModel by viewModels()
// Implementation
}// β
Good: Following Nothing OS design principles
object NothingColors {
val NothingRed = Color(0xFFEF4444)
val PureBlack = Color(0xFF000000)
val PureWhite = Color(0xFFFFFFFF)
val DarkSurface = Color(0xFF1F2937)
val DarkElevated = Color(0xFF1C1C1C)
}
object NothingTextStyles {
val HeaderTitle = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
letterSpacing = 2.sp
)
val NFCStatusLarge = TextStyle(
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
letterSpacing = 1.5.sp
)
val SmallCaps = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
letterSpacing = 1.sp
)
}
// β
Good: Consistent spacing
object NothingDimens {
val SpacingXXS = 2.dp
val SpacingXS = 4.dp
val SpacingS = 8.dp
val SpacingM = 16.dp
val SpacingL = 24.dp
val SpacingXL = 32.dp
val SpacingXXL = 48.dp
}// β
Good: Accessibility support
@Composable
fun AccessibleButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true
) {
Button(
onClick = onClick,
modifier = modifier
.semantics {
contentDescription = text
role = Role.Button
if (!enabled) {
disabled()
}
},
enabled = enabled
) {
Text(text = text)
}
}
// β
Good: Content descriptions for icons
Icon(
imageVector = Icons.Default.Nfc,
contentDescription = "NFC Status",
modifier = Modifier.semantics {
contentDescription = if (isEnabled) {
"NFC is currently enabled"
} else {
"NFC is currently disabled"
}
}
)// β
Good: Useful extension functions
fun Context.isNfcSupported(): Boolean {
return NfcAdapter.getDefaultAdapter(this) != null
}
fun Context.isNfcEnabled(): Boolean {
val adapter = NfcAdapter.getDefaultAdapter(this)
return adapter?.isEnabled == true
}
fun Date.formatRelativeTime(): String {
val seconds = (System.currentTimeMillis() - time) / 1000
return when {
seconds < 60 -> "now"
seconds < 3600 -> "${seconds / 60}m ago"
seconds < 86400 -> "${seconds / 3600}h ago"
else -> "${seconds / 86400}d ago"
}
}
// β
Good: Type-safe constants
object Constants {
const val DEFAULT_REMINDER_INTERVAL = 10
const val MAX_REMINDER_INTERVAL = 300
const val MIN_REMINDER_INTERVAL = 5
const val DATABASE_NAME = "nfc_manager_database"
const val PREFERENCES_NAME = "nfc_manager_prefs"
val SUPPORTED_INTERVALS = listOf(10, 30, 50)
}// β
Good: Comprehensive error handling
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
object Loading : Result<Nothing>()
}
// β
Good: Safe API calls
suspend fun <T> safeApiCall(
dispatcher: CoroutineDispatcher = Dispatchers.IO,
apiCall: suspend () -> T
): Result<T> {
return withContext(dispatcher) {
try {
Result.Success(apiCall.invoke())
} catch (throwable: Throwable) {
Result.Error(throwable)
}
}
}
// Usage
suspend fun loadEvents(): Result<List<NFCEventEntity>> {
return safeApiCall {
repository.getAllEvents().first()
}
}/**
* Repository for managing NFC events and settings.
*
* This repository provides a clean API for accessing NFC-related data
* and follows the repository pattern to abstract data sources.
*
* @param nfcEventDao DAO for NFC events
* @param nfcSettingsDao DAO for NFC settings
*/
@Singleton
class NFCRepository @Inject constructor(
private val nfcEventDao: NFCEventDao,
private val nfcSettingsDao: NFCSettingsDao
) {
/**
* Logs an NFC-related event to the database.
*
* @param eventType Type of event (e.g., "NFC_ENABLED", "TAG_DETECTED")
* @param message Human-readable message describing the event
* @param icon Icon identifier for UI display
* @param isImportant Whether this event should be highlighted
* @return The ID of the inserted event, or -1 if insertion failed
*/
suspend fun logEvent(
eventType: String,
message: String,
icon: String,
isImportant: Boolean = false
): Long {
// Implementation
}
}// β
Good: Explain complex logic
fun calculatePrivacyRisk(nfcEnabledDuration: Long): PrivacyRisk {
// NFC enabled for less than 30 seconds is considered low risk
if (nfcEnabledDuration < 30_000) {
return PrivacyRisk.LOW
}
// Between 30 seconds and 5 minutes is medium risk
if (nfcEnabledDuration < 300_000) {
return PrivacyRisk.MEDIUM
}
// More than 5 minutes is high risk due to potential unauthorized access
return PrivacyRisk.HIGH
}
// β Bad: Obvious comments
fun isNfcEnabled(): Boolean {
// Check if NFC is enabled
return nfcAdapter.isEnabled
}// β
Good: Comprehensive unit test
@Test
fun `logEvent should insert event and return valid ID`() = runTest {
// Given
val eventType = "TEST_EVENT"
val message = "Test message"
val icon = "TestIcon"
val expectedId = 123L
coEvery { nfcEventDao.insertEvent(any()) } returns expectedId
// When
val result = repository.logEvent(eventType, message, icon)
// Then
assertEquals(expectedId, result)
coVerify {
nfcEventDao.insertEvent(
match { event ->
event.eventType == eventType &&
event.message == message &&
event.icon == icon
}
)
}
}
// β
Good: Test naming convention
@Test
fun `getTodayEventCount should return correct count`() = runTest {
// Implementation
}
@Test
fun `cleanupOldEvents should delete events older than specified days`() = runTest {
// Implementation
}// β
Good: UI test with proper setup
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun `NFCStatusCard should display correct status text`() {
// Given
val isEnabled = true
// When
composeTestRule.setContent {
NFCStatusCard(
isEnabled = isEnabled,
onToggle = { }
)
}
// Then
composeTestRule
.onNodeWithText("ENABLED")
.assertIsDisplayed()
composeTestRule
.onNodeWithContentDescription("NFC Status")
.assertExists()
}<!-- β
Good: Organized string resources -->
<resources>
<!-- App Identity -->
<string name="app_name">NFC Manager</string>
<!-- Main Interface -->
<string name="home_title">NFC Manager</string>
<string name="nfc_monitoring">NFC Monitoring</string>
<string name="privacy_protection">Privacy Protection</string>
<!-- Settings -->
<string name="settings_title">Settings</string>
<string name="settings_theme_title">Theme</string>
<string name="settings_notifications_title">Notifications</string>
<!-- Status Messages -->
<string name="nfc_enabled">Enabled</string>
<string name="nfc_disabled">Disabled</string>
<string name="nfc_not_supported">Not Supported</string>
<!-- Error Messages -->
<string name="error_nfc_unavailable">NFC is not available on this device</string>
<string name="error_permission_denied">Permission denied</string>
</resources>// β
Good: Vector drawables for scalability
// res/drawable/ic_nfc_enabled.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,2L4,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>// β
Good: Secure data handling
class SecurePreferences @Inject constructor(
@ApplicationContext private val context: Context
) {
private val encryptedPrefs = EncryptedSharedPreferences.create(
"secure_prefs",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
fun storeSecureSetting(key: String, value: String) {
encryptedPrefs.edit()
.putString(key, value)
.apply()
}
}
// β
Good: Input validation
fun validateReminderInterval(interval: Int): Boolean {
return interval in MIN_REMINDER_INTERVAL..MAX_REMINDER_INTERVAL
}
// β
Good: Safe string operations
fun sanitizeUserInput(input: String): String {
return input.trim()
.take(MAX_INPUT_LENGTH)
.filter { it.isLetterOrDigit() || it.isWhitespace() }
}// β
Good: Proper lifecycle management
class NfcMonitoringService : Service() {
private val serviceJob = SupervisorJob()
private val serviceScope = CoroutineScope(Dispatchers.Main + serviceJob)
override fun onDestroy() {
super.onDestroy()
serviceJob.cancel() // Clean up coroutines
// Clean up resources
nfcAdapter = null
}
}
// β
Good: Efficient data loading
fun loadEventsWithPaging(): Flow<PagingData<NFCEventEntity>> {
return Pager(
config = PagingConfig(
pageSize = 20,
enablePlaceholders = false
),
pagingSourceFactory = { EventPagingSource(nfcEventDao) }
).flow
}// β
Good: Battery-conscious background work
class NfcMonitoringService : Service() {
private fun startMonitoring() {
// Use efficient monitoring intervals
val monitoringInterval = settings.monitoringInterval.coerceAtLeast(2000L)
serviceScope.launch {
while (isActive && isMonitoring) {
checkNfcStatus()
delay(monitoringInterval)
}
}
}
private fun checkNfcStatus() {
// Minimal work to check status
val isEnabled = nfcAdapter?.isEnabled == true
if (lastKnownStatus != isEnabled) {
lastKnownStatus = isEnabled
updateNotification()
}
}
}- Code follows Kotlin conventions
- Functions are properly documented
- Error handling is implemented
- Tests are written and passing
- No hardcoded strings (use resources)
- Accessibility considerations addressed
- Performance impact considered
- Security implications reviewed
- Follows MVVM pattern correctly
- Proper separation of concerns
- Repository pattern implemented correctly
- Dependency injection used appropriately
- State management follows best practices
- Follows Nothing OS design system
- Responsive on different screen sizes
- Accessibility features implemented
- Proper state hoisting
- Performance optimizations applied
// Use ktlint for automatic formatting
./gradlew ktlintFormat
// Check formatting
./gradlew ktlintCheck// Use detekt for code analysis
./gradlew detekt
// Custom detekt rules for project
detekt {
config = files("$projectDir/config/detekt.yml")
buildUponDefaultConfig = true
}#!/bin/sh
# pre-commit hook
./gradlew ktlintCheck
./gradlew detekt
./gradlew testDebugUnitTestFor questions about code style or to suggest improvements:
- Email: support@dxbmark.com
- Subject: "Code Style - NFC Manager"
- GitHub Issues: For style guide discussions
Built with β€οΈ by Tariq Said - Nothing OS Inspired Design
Technical Support & Contact: support@dxbmark.com
Licensed under the Apache License, Version 2.0
Copyright 2025 Tariq Said. All rights reserved.