1 Commits

Author SHA1 Message Date
tkrmagid
2287e1cf83 feat: app-level display name (v0.3.1) 2026-08-05 14:45:57 +09:00
7 changed files with 34 additions and 4 deletions

1
.gitignore vendored
View File

@@ -12,4 +12,3 @@
.cxx .cxx
app/build/ app/build/
build/ build/
.kotlin/

View File

@@ -24,8 +24,8 @@ android {
applicationId = "kr.tkrmagid.easyappliance" applicationId = "kr.tkrmagid.easyappliance"
minSdk = 24 minSdk = 24
targetSdk = 35 targetSdk = 35
versionCode = 3 versionCode = 4
versionName = "0.3.0" versionName = "0.3.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }

View File

@@ -17,6 +17,8 @@ private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(na
/** User/admin choices that must survive app restarts. */ /** User/admin choices that must survive app restarts. */
data class AppSettings( data class AppSettings(
/** App-level display name shown as the home header; null = default. */
val appName: String? = null,
val selectedDeviceId: String? = null, val selectedDeviceId: String? = null,
val showRemainingTime: Boolean = true, val showRemainingTime: Boolean = true,
val showReservation: Boolean = true, val showReservation: Boolean = true,
@@ -36,6 +38,7 @@ class SettingsRepository(private val context: Context) {
val settings: Flow<AppSettings> = context.dataStore.data.map { p -> val settings: Flow<AppSettings> = context.dataStore.data.map { p ->
AppSettings( AppSettings(
appName = p[KEY_APP_NAME]?.takeIf { it.isNotBlank() },
selectedDeviceId = p[KEY_SELECTED_DEVICE], selectedDeviceId = p[KEY_SELECTED_DEVICE],
showRemainingTime = p[KEY_SHOW_REMAINING] ?: true, showRemainingTime = p[KEY_SHOW_REMAINING] ?: true,
showReservation = p[KEY_SHOW_RESERVATION] ?: true, showReservation = p[KEY_SHOW_RESERVATION] ?: true,
@@ -47,6 +50,9 @@ class SettingsRepository(private val context: Context) {
) )
} }
suspend fun setAppName(name: String) =
edit { it[KEY_APP_NAME] = name.trim() }
suspend fun setSelectedDevice(deviceId: String) = suspend fun setSelectedDevice(deviceId: String) =
edit { it[KEY_SELECTED_DEVICE] = deviceId } edit { it[KEY_SELECTED_DEVICE] = deviceId }
@@ -93,6 +99,7 @@ class SettingsRepository(private val context: Context) {
else runCatching { Json.decodeFromString<Map<String, DeviceMacro>>(raw) }.getOrDefault(emptyMap()) else runCatching { Json.decodeFromString<Map<String, DeviceMacro>>(raw) }.getOrDefault(emptyMap())
private companion object { private companion object {
val KEY_APP_NAME = stringPreferencesKey("app_name")
val KEY_SELECTED_DEVICE = stringPreferencesKey("selected_device_id") val KEY_SELECTED_DEVICE = stringPreferencesKey("selected_device_id")
val KEY_SHOW_REMAINING = booleanPreferencesKey("show_remaining_time") val KEY_SHOW_REMAINING = booleanPreferencesKey("show_remaining_time")
val KEY_SHOW_RESERVATION = booleanPreferencesKey("show_reservation") val KEY_SHOW_RESERVATION = booleanPreferencesKey("show_reservation")

View File

@@ -49,6 +49,7 @@ import kr.tkrmagid.easyappliance.data.DeviceImages
import kr.tkrmagid.easyappliance.data.DeviceMacro import kr.tkrmagid.easyappliance.data.DeviceMacro
import kr.tkrmagid.easyappliance.data.ImageStorage import kr.tkrmagid.easyappliance.data.ImageStorage
import kr.tkrmagid.easyappliance.vm.HomeUiState import kr.tkrmagid.easyappliance.vm.HomeUiState
import kr.tkrmagid.easyappliance.vm.appDisplayName
import kr.tkrmagid.easyappliance.vm.displayLabelFor import kr.tkrmagid.easyappliance.vm.displayLabelFor
import kr.tkrmagid.easyappliance.vm.imageRefFor import kr.tkrmagid.easyappliance.vm.imageRefFor
import kr.tkrmagid.easyappliance.vm.macroFor import kr.tkrmagid.easyappliance.vm.macroFor
@@ -57,6 +58,7 @@ import kr.tkrmagid.easyappliance.vm.macroFor
fun AdminScreen( fun AdminScreen(
state: HomeUiState, state: HomeUiState,
onSelectDevice: (String) -> Unit, onSelectDevice: (String) -> Unit,
onSetAppName: (String) -> Unit,
onToggleRemaining: (Boolean) -> Unit, onToggleRemaining: (Boolean) -> Unit,
onToggleReservation: (Boolean) -> Unit, onToggleReservation: (Boolean) -> Unit,
onTogglePowerControls: (Boolean) -> Unit, onTogglePowerControls: (Boolean) -> Unit,
@@ -78,6 +80,13 @@ fun AdminScreen(
Text("관리자 설정", style = MaterialTheme.typography.headlineLarge) Text("관리자 설정", style = MaterialTheme.typography.headlineLarge)
Spacer(Modifier.height(24.dp)) Spacer(Modifier.height(24.dp))
SectionTitle("앱 이름 바꾸기")
NameEditor(
current = state.appDisplayName(),
onSave = onSetAppName,
)
Spacer(Modifier.height(28.dp))
SectionTitle("조작할 기기 선택") SectionTitle("조작할 기기 선택")
Card( Card(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -104,7 +113,7 @@ fun AdminScreen(
if (selected != null) { if (selected != null) {
Spacer(Modifier.height(28.dp)) Spacer(Modifier.height(28.dp))
SectionTitle("이름 바꾸기") SectionTitle("기기 이름 바꾸기")
NameEditor( NameEditor(
current = state.displayLabelFor(selected), current = state.displayLabelFor(selected),
onSave = { onSetLabel(selected.id, it) }, onSave = { onSetLabel(selected.id, it) },

View File

@@ -63,6 +63,7 @@ fun AppRoot() {
Screen.ADMIN -> AdminScreen( Screen.ADMIN -> AdminScreen(
state = state, state = state,
onSelectDevice = vm::selectDevice, onSelectDevice = vm::selectDevice,
onSetAppName = vm::setAppName,
onToggleRemaining = vm::setShowRemainingTime, onToggleRemaining = vm::setShowRemainingTime,
onToggleReservation = vm::setShowReservation, onToggleReservation = vm::setShowReservation,
onTogglePowerControls = vm::setShowPowerControls, onTogglePowerControls = vm::setShowPowerControls,

View File

@@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp
import kr.tkrmagid.easyappliance.data.DeviceType import kr.tkrmagid.easyappliance.data.DeviceType
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
import kr.tkrmagid.easyappliance.vm.HomeUiState import kr.tkrmagid.easyappliance.vm.HomeUiState
import kr.tkrmagid.easyappliance.vm.appDisplayName
import kr.tkrmagid.easyappliance.vm.displayLabelFor import kr.tkrmagid.easyappliance.vm.displayLabelFor
import kr.tkrmagid.easyappliance.vm.imageRefFor import kr.tkrmagid.easyappliance.vm.imageRefFor
import kr.tkrmagid.easyappliance.vm.macroFor import kr.tkrmagid.easyappliance.vm.macroFor
@@ -71,6 +72,13 @@ fun HomeScreen(
modifier = Modifier.widthIn(max = contentMaxWidth).fillMaxWidth(), modifier = Modifier.widthIn(max = contentMaxWidth).fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Text(
text = state.appDisplayName(),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.primary,
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(16.dp))
DeviceImage( DeviceImage(
imageRef = state.imageRefFor(device), imageRef = state.imageRefFor(device),
modifier = Modifier.size(if (narrow) 96.dp else 128.dp), modifier = Modifier.size(if (narrow) 96.dp else 128.dp),

View File

@@ -170,6 +170,7 @@ class AppViewModel(app: Application) : AndroidViewModel(app) {
viewModelScope.launch { settingsRepo.setDeviceMacro(deviceId, macro) } viewModelScope.launch { settingsRepo.setDeviceMacro(deviceId, macro) }
} }
fun setAppName(name: String) { viewModelScope.launch { settingsRepo.setAppName(name) } }
fun setShowRemainingTime(value: Boolean) { viewModelScope.launch { settingsRepo.setShowRemainingTime(value) } } fun setShowRemainingTime(value: Boolean) { viewModelScope.launch { settingsRepo.setShowRemainingTime(value) } }
fun setShowReservation(value: Boolean) { viewModelScope.launch { settingsRepo.setShowReservation(value) } } fun setShowReservation(value: Boolean) { viewModelScope.launch { settingsRepo.setShowReservation(value) } }
fun setShowPowerControls(value: Boolean) { viewModelScope.launch { settingsRepo.setShowPowerControls(value) } } fun setShowPowerControls(value: Boolean) { viewModelScope.launch { settingsRepo.setShowPowerControls(value) } }
@@ -188,3 +189,8 @@ fun HomeUiState.imageRefFor(device: Device): String =
/** The device's macro, or null if none configured. */ /** The device's macro, or null if none configured. */
fun HomeUiState.macroFor(device: Device): DeviceMacro? = settings.deviceMacros[device.id] fun HomeUiState.macroFor(device: Device): DeviceMacro? = settings.deviceMacros[device.id]
const val DEFAULT_APP_NAME = "쉬운 가전 리모컨"
/** App-level display name shown as the home header. */
fun HomeUiState.appDisplayName(): String = settings.appName ?: DEFAULT_APP_NAME