Skip to content

Commit e10ca9a

Browse files
PM-19099: Centralize app metadata (#4847)
1 parent 3fca61a commit e10ca9a

File tree

5 files changed

+108
-75
lines changed

5 files changed

+108
-75
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,62 @@
11
package com.x8bit.bitwarden.data.platform.util
22

3+
import android.os.Build
34
import com.x8bit.bitwarden.BuildConfig
45

56
/**
67
* A boolean property that indicates whether the current build flavor is "fdroid".
78
*/
89
val isFdroid: Boolean
910
get() = BuildConfig.FLAVOR == "fdroid"
11+
12+
/**
13+
* A string that represents a displayable app version.
14+
*/
15+
val versionData: String
16+
get() = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
17+
18+
/**
19+
* A string that represents device data.
20+
*/
21+
val deviceData: String get() = "$deviceBrandModel $osInfo $buildInfo"
22+
23+
/**
24+
* A string representing the CI information if available.
25+
*/
26+
val ciBuildInfo: String? get() = BuildConfig.CI_INFO.takeUnless { it.isBlank() }
27+
28+
/**
29+
* A string representing the build flavor or blank if it is the standard configuration.
30+
*/
31+
private val buildFlavorName: String
32+
get() = when (BuildConfig.FLAVOR) {
33+
"standard" -> ""
34+
else -> "-${BuildConfig.FLAVOR}"
35+
}
36+
37+
/**
38+
* A string representing the build type.
39+
*/
40+
private val buildTypeName: String
41+
get() = when (BuildConfig.BUILD_TYPE) {
42+
"debug" -> "dev"
43+
"release" -> "prod"
44+
else -> BuildConfig.BUILD_TYPE
45+
}
46+
47+
/**
48+
* A string representing the device brand and model.
49+
*/
50+
private val deviceBrandModel: String get() = "\uD83D\uDCF1 ${Build.BRAND} ${Build.MODEL}"
51+
52+
/**
53+
* A string representing the operating system information.
54+
*/
55+
private val osInfo: String get() = "\uD83E\uDD16 ${Build.VERSION.RELEASE}@${Build.VERSION.SDK_INT}"
56+
57+
/**
58+
* A string representing the build information.
59+
*/
60+
private val buildInfo: String
61+
get() = "\uD83D\uDCE6 $buildTypeName" +
62+
buildFlavorName.takeUnless { it.isBlank() }?.let { " $it" }.orEmpty()

app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/about/AboutViewModel.kt

+16-49
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package com.x8bit.bitwarden.ui.platform.feature.settings.about
22

3-
import android.os.Build
43
import android.os.Parcelable
54
import androidx.lifecycle.SavedStateHandle
65
import androidx.lifecycle.viewModelScope
7-
import com.x8bit.bitwarden.BuildConfig
86
import com.x8bit.bitwarden.R
97
import com.x8bit.bitwarden.data.platform.manager.LogsManager
108
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
119
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
1210
import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault
11+
import com.x8bit.bitwarden.data.platform.util.ciBuildInfo
12+
import com.x8bit.bitwarden.data.platform.util.deviceData
1313
import com.x8bit.bitwarden.data.platform.util.isFdroid
14+
import com.x8bit.bitwarden.data.platform.util.versionData
1415
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
1516
import com.x8bit.bitwarden.ui.platform.base.util.Text
1617
import com.x8bit.bitwarden.ui.platform.base.util.asText
@@ -37,10 +38,15 @@ class AboutViewModel @Inject constructor(
3738
private val logsManager: LogsManager,
3839
private val environmentRepository: EnvironmentRepository,
3940
) : BaseViewModel<AboutState, AboutEvent, AboutAction>(
40-
initialState = savedStateHandle[KEY_STATE] ?: createInitialState(
41-
clock = clock,
42-
isCrashLoggingEnabled = logsManager.isEnabled,
43-
),
41+
initialState = savedStateHandle[KEY_STATE]
42+
?: AboutState(
43+
version = R.string.version.asText().concat(": $versionData".asText()),
44+
deviceData = deviceData.asText(),
45+
ciData = ciBuildInfo?.let { "\n$it" }.orEmpty().asText(),
46+
isSubmitCrashLogsEnabled = logsManager.isEnabled,
47+
shouldShowCrashLogsButton = !isFdroid,
48+
copyrightInfo = "© Bitwarden Inc. 2015-${Year.now(clock).value}".asText(),
49+
),
4450
) {
4551
init {
4652
stateFlow
@@ -92,34 +98,13 @@ class AboutViewModel @Inject constructor(
9298
}
9399

94100
private fun handleVersionClick() {
95-
val buildFlavour = when (BuildConfig.FLAVOR) {
96-
"standard" -> ""
97-
else -> "-${BuildConfig.FLAVOR}"
98-
}
99-
100-
val buildVariant = when (BuildConfig.BUILD_TYPE) {
101-
"debug" -> "dev"
102-
"release" -> "prod"
103-
else -> BuildConfig.BUILD_TYPE
104-
}
105-
106-
val deviceBrandModel = "\uD83D\uDCF1 ${Build.BRAND} ${Build.MODEL}"
107-
val osInfo = "\uD83E\uDD16 ${Build.VERSION.RELEASE}@${Build.VERSION.SDK_INT}"
108-
val buildInfo = "\uD83D\uDCE6 $buildVariant$buildFlavour"
109-
val ciBuildInfoString = BuildConfig.CI_INFO
110-
111101
clipboardManager.setText(
112102
text = state.copyrightInfo
113103
.concat("\n\n".asText())
114104
.concat(state.version)
115105
.concat("\n".asText())
116-
.concat("$deviceBrandModel $osInfo $buildInfo".asText())
117-
.concat(
118-
"\n$ciBuildInfoString"
119-
.takeUnless { ciBuildInfoString.isEmpty() }
120-
.orEmpty()
121-
.asText(),
122-
),
106+
.concat(state.deviceData)
107+
.concat(state.ciData),
123108
)
124109
}
125110

@@ -130,26 +115,6 @@ class AboutViewModel @Inject constructor(
130115
),
131116
)
132117
}
133-
134-
@Suppress("UndocumentedPublicClass")
135-
companion object {
136-
/**
137-
* Create initial state for the About View model.
138-
*/
139-
fun createInitialState(clock: Clock, isCrashLoggingEnabled: Boolean): AboutState {
140-
val currentYear = Year.now(clock).value
141-
val copyrightInfo = "© Bitwarden Inc. 2015-$currentYear".asText()
142-
143-
return AboutState(
144-
version = R.string.version
145-
.asText()
146-
.concat(": ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})".asText()),
147-
isSubmitCrashLogsEnabled = isCrashLoggingEnabled,
148-
shouldShowCrashLogsButton = !isFdroid,
149-
copyrightInfo = copyrightInfo,
150-
)
151-
}
152-
}
153118
}
154119

155120
/**
@@ -158,6 +123,8 @@ class AboutViewModel @Inject constructor(
158123
@Parcelize
159124
data class AboutState(
160125
val version: Text,
126+
val deviceData: Text,
127+
val ciData: Text,
161128
val isSubmitCrashLogsEnabled: Boolean,
162129
val shouldShowCrashLogsButton: Boolean,
163130
val copyrightInfo: Text,

app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/about/AboutScreenTest.kt

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class AboutScreenTest : BaseComposeTest() {
3939
private val mutableStateFlow = MutableStateFlow(
4040
AboutState(
4141
version = "Version: 1.0.0 (1)".asText(),
42+
deviceData = "device_data".asText(),
43+
ciData = "ci_data".asText(),
4244
isSubmitCrashLogsEnabled = false,
4345
copyrightInfo = "".asText(),
4446
shouldShowCrashLogsButton = true,

app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/about/AboutViewModelTest.kt

+10-26
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package com.x8bit.bitwarden.ui.platform.feature.settings.about
22

3-
import android.os.Build
43
import androidx.lifecycle.SavedStateHandle
54
import app.cash.turbine.test
6-
import com.x8bit.bitwarden.BuildConfig
75
import com.x8bit.bitwarden.data.platform.manager.LogsManager
86
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
97
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
@@ -110,31 +108,17 @@ class AboutViewModelTest : BaseViewModelTest() {
110108

111109
@Test
112110
fun `on VersionClick should call setText on the ClipboardManager with specific Text`() {
113-
val versionName = BuildConfig.VERSION_NAME
114-
val versionCode = BuildConfig.VERSION_CODE
115-
116-
val deviceBrandModel = "\uD83D\uDCF1 ${Build.BRAND} ${Build.MODEL}"
117-
val osInfo = "\uD83E\uDD16 ${Build.VERSION.RELEASE}@${Build.VERSION.SDK_INT}"
118-
val buildInfo = "\uD83D\uDCE6 dev"
119-
val ciInfo = BuildConfig.CI_INFO
120-
121-
val expectedText = "© Bitwarden Inc. 2015-"
122-
.asText()
123-
.concat(Year.now(fixedClock).value.toString().asText())
111+
val state = DEFAULT_ABOUT_STATE
112+
val expectedText = state.copyrightInfo
124113
.concat("\n\n".asText())
125-
.concat("Version: $versionName ($versionCode)".asText())
114+
.concat(state.version)
126115
.concat("\n".asText())
127-
.concat("$deviceBrandModel $osInfo $buildInfo".asText())
128-
.concat(
129-
"\n$ciInfo"
130-
.takeUnless { ciInfo.isEmpty() }
131-
.orEmpty()
132-
.asText(),
133-
)
116+
.concat(state.deviceData)
117+
.concat(state.ciData)
134118

135119
every { clipboardManager.setText(expectedText, true, null) } just runs
136120

137-
val viewModel = createViewModel(DEFAULT_ABOUT_STATE)
121+
val viewModel = createViewModel(state)
138122
viewModel.trySendAction(AboutAction.VersionClick)
139123

140124
verify(exactly = 1) {
@@ -175,10 +159,10 @@ private val fixedClock = Clock.fixed(
175159
ZoneId.systemDefault(),
176160
)
177161
private val DEFAULT_ABOUT_STATE: AboutState = AboutState(
178-
version = "Version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})".asText(),
162+
version = "Version: <version_name> (<version_code>)".asText(),
163+
deviceData = "<device_data>".asText(),
164+
ciData = "\n<ci_info>".asText(),
179165
isSubmitCrashLogsEnabled = false,
180-
copyrightInfo = "© Bitwarden Inc. 2015-"
181-
.asText()
182-
.concat(Year.now(fixedClock).value.toString().asText()),
166+
copyrightInfo = "© Bitwarden Inc. 2015-${Year.now(fixedClock).value}".asText(),
183167
shouldShowCrashLogsButton = true,
184168
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.x8bit.bitwarden.ui.platform.util
2+
3+
import android.os.Build
4+
import com.x8bit.bitwarden.BuildConfig
5+
import com.x8bit.bitwarden.data.platform.util.deviceData
6+
import com.x8bit.bitwarden.data.platform.util.versionData
7+
import org.junit.jupiter.api.Assertions.assertEquals
8+
import org.junit.jupiter.api.Test
9+
10+
class BuildConfigTest {
11+
@Test
12+
fun `deviceData should be formatted correctly`() {
13+
val deviceBrandModel = "\uD83D\uDCF1 ${Build.BRAND} ${Build.MODEL}"
14+
val osInfo = "\uD83E\uDD16 ${Build.VERSION.RELEASE}@${Build.VERSION.SDK_INT}"
15+
val buildInfo = "\uD83D\uDCE6 dev"
16+
17+
assertEquals("$deviceBrandModel $osInfo $buildInfo", deviceData)
18+
}
19+
20+
@Test
21+
fun `versionData should be formatted correctly`() {
22+
val versionName = BuildConfig.VERSION_NAME
23+
val versionCode = BuildConfig.VERSION_CODE
24+
25+
assertEquals("$versionName ($versionCode)", versionData)
26+
}
27+
}

0 commit comments

Comments
 (0)