mirror of
https://github.com/usetrmnl/trmnl-android.git
synced 2026-04-29 13:35:26 -07:00
[REFACTOR] Moved fake API config from DevConfig to BuildConfig
Fixes #16
This commit is contained in:
@@ -34,3 +34,4 @@ google-services.json
|
||||
.DS_Store
|
||||
app/debug/
|
||||
app/release/
|
||||
*.salive
|
||||
|
||||
+8
-11
@@ -1,5 +1,3 @@
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
@@ -21,15 +19,6 @@ android {
|
||||
targetSdk = 35
|
||||
versionCode = 2
|
||||
versionName = "1.1.0"
|
||||
|
||||
// Read key or other properties from local.properties
|
||||
val localProperties =
|
||||
project.rootProject.file("local.properties").takeIf { it.exists() }?.inputStream()?.use {
|
||||
Properties().apply { load(it) }
|
||||
}
|
||||
val apiKey = localProperties?.getProperty("SERVICE_API_KEY") ?: "MISSING-KEY"
|
||||
buildConfigField("String", "SERVICE_API_KEY", "\"$apiKey\"")
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -57,6 +46,10 @@ android {
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// Always force `USE_FAKE_API` to `false` for release builds
|
||||
// See https://github.com/usetrmnl/trmnl-android/issues/16
|
||||
buildConfigField("Boolean", "USE_FAKE_API", "false")
|
||||
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
@@ -66,6 +59,10 @@ android {
|
||||
}
|
||||
|
||||
debug {
|
||||
// Allow developers to configure this value for debug builds
|
||||
// Use fake API response for local development and testing purposes.
|
||||
buildConfigField("Boolean", "USE_FAKE_API", "true")
|
||||
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package ink.trmnl.android.data
|
||||
|
||||
/**
|
||||
* Development configuration for the TRMNL app.
|
||||
*/
|
||||
object DevConfig {
|
||||
/**
|
||||
* Fake API response for local development and testing purposes.
|
||||
* In debug builds, we use fake API responses.
|
||||
*/
|
||||
const val FAKE_API_RESPONSE = true
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package ink.trmnl.android.data
|
||||
|
||||
import ink.trmnl.android.BuildConfig
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -12,10 +13,12 @@ class RepositoryConfigProvider
|
||||
/**
|
||||
* Indicates if the app should use fake data instead of real API responses.
|
||||
*
|
||||
* @return Boolean value from [DevConfig.FAKE_API_RESPONSE]
|
||||
* @return Boolean value from [BuildConfig.USE_FAKE_API]
|
||||
*/
|
||||
val shouldUseFakeData: Boolean
|
||||
get() {
|
||||
return DevConfig.FAKE_API_RESPONSE
|
||||
// To change this value, update the `buildConfigField` in the app's build.gradle file
|
||||
// Or, change the value here for local development. Do not commit this change.
|
||||
return BuildConfig.USE_FAKE_API
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package ink.trmnl.android.data
|
||||
|
||||
import com.slack.eithernet.successOrNull
|
||||
import com.squareup.anvil.annotations.optional.SingleIn
|
||||
import ink.trmnl.android.data.DevConfig.FAKE_API_RESPONSE
|
||||
import ink.trmnl.android.BuildConfig.USE_FAKE_API
|
||||
import ink.trmnl.android.di.AppScope
|
||||
import ink.trmnl.android.network.TrmnlApiService
|
||||
import ink.trmnl.android.util.HTTP_200
|
||||
@@ -14,11 +14,11 @@ import javax.inject.Inject
|
||||
/**
|
||||
* Repository class responsible for fetching and mapping display data.
|
||||
*
|
||||
* ⚠️ NOTE: [FAKE_API_RESPONSE] is set to `true` in debug builds, meaning it will
|
||||
* ⚠️ NOTE: [USE_FAKE_API] is set to `true` in debug builds, meaning it will
|
||||
* use mock data and avoid network calls. In release builds, it is set to `false`
|
||||
* to enable real API calls.
|
||||
*
|
||||
* You can override this behavior by updating [DevConfig.FAKE_API_RESPONSE] for local development.
|
||||
* You can override this behavior by updating [RepositoryConfigProvider.shouldUseFakeData] for local development.
|
||||
*/
|
||||
@SingleIn(AppScope::class)
|
||||
class TrmnlDisplayRepository
|
||||
@@ -118,7 +118,7 @@ class TrmnlDisplayRepository
|
||||
/**
|
||||
* Generates fake display info for debugging purposes without wasting an API request.
|
||||
*
|
||||
* ℹ️ This is only used when [FAKE_API_RESPONSE] is set to `true`.
|
||||
* ℹ️ This is only used when [RepositoryConfigProvider.shouldUseFakeData] is returns `true`.
|
||||
*/
|
||||
private suspend fun fakeTrmnlDisplayInfo(apiUsed: String): TrmnlDisplayInfo {
|
||||
Timber.d("DEBUG: Using mock data for display info")
|
||||
|
||||
@@ -8,6 +8,9 @@ import timber.log.Timber
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
/**
|
||||
* Serializer for [TrmnlRefreshLogs] using Moshi.
|
||||
*/
|
||||
object TrmnlRefreshLogSerializer : Serializer<TrmnlRefreshLogs> {
|
||||
private val moshi = Moshi.Builder().build()
|
||||
private val adapter = moshi.adapter(TrmnlRefreshLogs::class.java)
|
||||
@@ -27,12 +30,12 @@ object TrmnlRefreshLogSerializer : Serializer<TrmnlRefreshLogs> {
|
||||
}
|
||||
|
||||
override suspend fun writeTo(
|
||||
t: TrmnlRefreshLogs,
|
||||
refreshLogs: TrmnlRefreshLogs,
|
||||
output: OutputStream,
|
||||
) {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val jsonString = adapter.toJson(t)
|
||||
val jsonString = adapter.toJson(refreshLogs)
|
||||
output.write(jsonString.toByteArray())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error writing activity logs")
|
||||
|
||||
@@ -20,7 +20,6 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.DateRange
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.outlined.Warning
|
||||
import androidx.compose.material3.Button
|
||||
@@ -82,7 +81,7 @@ import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import ink.trmnl.android.R
|
||||
import ink.trmnl.android.data.AppConfig.DEFAULT_REFRESH_INTERVAL_SEC
|
||||
import ink.trmnl.android.data.DevConfig
|
||||
import ink.trmnl.android.data.RepositoryConfigProvider
|
||||
import ink.trmnl.android.data.TrmnlDisplayRepository
|
||||
import ink.trmnl.android.data.TrmnlTokenDataStore
|
||||
import ink.trmnl.android.di.AppScope
|
||||
@@ -123,6 +122,7 @@ data class AppSettingsScreen(
|
||||
) : Screen {
|
||||
data class State(
|
||||
val accessToken: String,
|
||||
val usesFakeApiData: Boolean,
|
||||
val isLoading: Boolean = false,
|
||||
val validationResult: ValidationResult? = null,
|
||||
val nextRefreshJobInfo: NextImageRefreshDisplayInfo? = null,
|
||||
@@ -186,12 +186,14 @@ class AppSettingsPresenter
|
||||
private val trmnlTokenDataStore: TrmnlTokenDataStore,
|
||||
private val trmnlWorkScheduler: TrmnlWorkScheduler,
|
||||
private val trmnlImageUpdateManager: TrmnlImageUpdateManager,
|
||||
private val repositoryConfigProvider: RepositoryConfigProvider,
|
||||
) : Presenter<AppSettingsScreen.State> {
|
||||
@Composable
|
||||
override fun present(): AppSettingsScreen.State {
|
||||
var accessToken by remember { mutableStateOf("") }
|
||||
var isLoading by remember { mutableStateOf(false) }
|
||||
var validationResult by remember { mutableStateOf<ValidationResult?>(null) }
|
||||
val usesFakeApiData by remember { mutableStateOf(repositoryConfigProvider.shouldUseFakeData) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
@@ -212,6 +214,7 @@ class AppSettingsPresenter
|
||||
|
||||
return AppSettingsScreen.State(
|
||||
accessToken = accessToken,
|
||||
usesFakeApiData = usesFakeApiData,
|
||||
isLoading = isLoading,
|
||||
validationResult = validationResult,
|
||||
nextRefreshJobInfo = nextRefreshInfo,
|
||||
@@ -352,8 +355,7 @@ fun AppSettingsContent(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
if (DevConfig.FAKE_API_RESPONSE) {
|
||||
// Show fake API banner when DevConfig.FAKE_API_RESPONSE is `true`
|
||||
if (state.usesFakeApiData) {
|
||||
FakeApiInfoBanner(
|
||||
modifier =
|
||||
Modifier
|
||||
@@ -655,7 +657,7 @@ private fun FakeApiInfoBanner(modifier: Modifier = Modifier) {
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Set `DevConfig.FAKE_API_RESPONSE` to `false` to use real API.",
|
||||
text = "Set `BuildConfig.USE_FAKE_API` to `false` using build.gradle to use real API.",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
@@ -727,6 +729,7 @@ private fun PreviewAppSettingsContentInitial() {
|
||||
state =
|
||||
AppSettingsScreen.State(
|
||||
accessToken = "",
|
||||
usesFakeApiData = true,
|
||||
isLoading = false,
|
||||
validationResult = null,
|
||||
nextRefreshJobInfo = null,
|
||||
@@ -744,6 +747,7 @@ private fun PreviewAppSettingsContentLoading() {
|
||||
state =
|
||||
AppSettingsScreen.State(
|
||||
accessToken = "some-token",
|
||||
usesFakeApiData = false,
|
||||
isLoading = true,
|
||||
validationResult = null,
|
||||
nextRefreshJobInfo = null,
|
||||
@@ -761,6 +765,7 @@ private fun PreviewAppSettingsContentSuccess() {
|
||||
state =
|
||||
AppSettingsScreen.State(
|
||||
accessToken = "valid-token-123",
|
||||
usesFakeApiData = false,
|
||||
isLoading = false,
|
||||
validationResult =
|
||||
ValidationResult.Success(
|
||||
@@ -782,6 +787,7 @@ private fun PreviewAppSettingsContentFailure() {
|
||||
state =
|
||||
AppSettingsScreen.State(
|
||||
accessToken = "invalid-token",
|
||||
usesFakeApiData = false,
|
||||
isLoading = false,
|
||||
validationResult =
|
||||
ValidationResult.Failure(
|
||||
@@ -806,6 +812,7 @@ private fun PreviewAppSettingsContentWithWork() {
|
||||
state =
|
||||
AppSettingsScreen.State(
|
||||
accessToken = "valid-token-123",
|
||||
usesFakeApiData = false,
|
||||
isLoading = false,
|
||||
validationResult = null, // Can also be Success state
|
||||
nextRefreshJobInfo =
|
||||
@@ -833,6 +840,7 @@ private fun PreviewWorkScheduleStatusCardScheduled() {
|
||||
state =
|
||||
AppSettingsScreen.State(
|
||||
accessToken = "some-token",
|
||||
usesFakeApiData = false,
|
||||
nextRefreshJobInfo =
|
||||
NextImageRefreshDisplayInfo(
|
||||
workerState = WorkInfo.State.ENQUEUED,
|
||||
@@ -854,6 +862,7 @@ private fun PreviewWorkScheduleStatusCardNoWork() {
|
||||
state =
|
||||
AppSettingsScreen.State(
|
||||
accessToken = "some-token",
|
||||
usesFakeApiData = false,
|
||||
nextRefreshJobInfo = null,
|
||||
eventSink = {},
|
||||
),
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package ink.trmnl.android.data
|
||||
|
||||
/**
|
||||
* Development configuration for the TRMNL app.
|
||||
*/
|
||||
object DevConfig {
|
||||
/**
|
||||
* Fake API response for local development and testing purposes.
|
||||
* In release builds, we use real API responses.
|
||||
*/
|
||||
const val FAKE_API_RESPONSE = false
|
||||
}
|
||||
Reference in New Issue
Block a user