Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions WordPress/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@
android:label="@string/help_buttons_screen_title"
android:theme="@style/WordPress.NoActionBar" />

<activity
android:name=".ui.accounts.DeviceInfoActivity"
android:label="@string/device_info_title"
android:theme="@style/WordPress.NoActionBar" />

<activity
android:name=".ui.main.feedbackform.FeedbackFormActivity"
android:label="@string/feedback_form_title"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package org.wordpress.android.ui.accounts

import android.content.ClipData
import android.content.ClipboardManager
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import dagger.hilt.android.AndroidEntryPoint
import org.wordpress.android.R
import org.wordpress.android.WordPress
import org.wordpress.android.ui.compose.theme.AppThemeM3
import org.wordpress.android.ui.main.BaseAppCompatActivity
import org.wordpress.android.util.EinkDeviceDetector
import org.wordpress.android.util.extensions.setContent

@AndroidEntryPoint
class DeviceInfoActivity : BaseAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
AppThemeM3 {
DeviceInfoScreen(
onNavigateBack = onBackPressedDispatcher::onBackPressed
)
}
}
}
}

private data class DeviceInfoSection(
val title: String,
val entries: List<Pair<String, String>>,
)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun DeviceInfoScreen(onNavigateBack: () -> Unit) {
val context = LocalContext.current
val sections = buildDeviceInfoSections()

Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = stringResource(R.string.device_info_title)
)
},
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
stringResource(R.string.back)
)
}
},
)
},
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.verticalScroll(rememberScrollState())
) {
sections.forEach { section ->
SectionHeader(title = section.title)
section.entries.forEach { (label, value) ->
DeviceInfoRow(label = label, value = value)
}
}
Button(
onClick = {
val text = sections.joinToString("\n\n") { s ->
s.title + "\n" + s.entries.joinToString(
"\n"
) { "${it.first}: ${it.second}" }
}
val clipboard = context.getSystemService(
ClipboardManager::class.java
)
clipboard.setPrimaryClip(
ClipData.newPlainText(
context.getString(
R.string.device_info_title
),
Comment on lines +107 to +109

Check failure

Code scanning / Android Lint

Querying resource properties using LocalContext.current Error

Querying resource values using LocalContext.current
text
)
)
Toast.makeText(
context,
R.string.device_info_copied,
Toast.LENGTH_SHORT
).show()
},
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
text = stringResource(
R.string.copy_to_clipboard
)
)
}
}
}
}

@Composable
private fun SectionHeader(title: String) {
Text(
text = title,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(
start = 16.dp, end = 16.dp, top = 16.dp, bottom = 4.dp
),
)
}

@Composable
private fun buildDeviceInfoSections(): List<DeviceInfoSection> {
val einkValue = if (EinkDeviceDetector.isEinkDevice()) {
stringResource(R.string.yes)
} else {
stringResource(R.string.no)
}
return listOf(
DeviceInfoSection(
title = stringResource(R.string.device_info_section_application),
entries = listOf(
stringResource(R.string.device_info_app_version)
to WordPress.versionName,
),
),
DeviceInfoSection(
title = stringResource(R.string.device_info_section_device),
entries = listOf(
stringResource(R.string.device_info_manufacturer)
to Build.MANUFACTURER,
stringResource(R.string.device_info_brand)
to Build.BRAND,
stringResource(R.string.device_info_model)
to Build.MODEL,
stringResource(R.string.device_info_device)
to Build.DEVICE,
stringResource(R.string.device_info_product)
to Build.PRODUCT,
stringResource(R.string.device_info_eink_detected)
to einkValue,
),
),
DeviceInfoSection(
title = stringResource(R.string.device_info_section_android),
entries = listOf(
stringResource(R.string.device_info_android_version)
to Build.VERSION.RELEASE,
stringResource(R.string.device_info_sdk_level)
to Build.VERSION.SDK_INT.toString(),
),
),
)
}

@Composable
private fun DeviceInfoRow(label: String, value: String) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)
) {
Text(
text = label,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
text = value,
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.Normal,
)
}
HorizontalDivider()
}

@Preview(showBackground = true)
@Composable
private fun DeviceInfoRowPreview() {
AppThemeM3 {
DeviceInfoRow(
label = "App version",
value = "24.5-rc-1"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ class HelpActivity : BaseAppCompatActivity() {
logsButton.setOnClickListener { v ->
startActivity(Intent(v.context, AppLogViewerActivity::class.java))
}
deviceInfoButton.setOnClickListener { v ->
startActivity(Intent(v.context, DeviceInfoActivity::class.java))
}

if (originFromExtras == Origin.JETPACK_MIGRATION_HELP) {
configureForJetpackMigrationHelp()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalInspectionMode
import org.wordpress.android.BuildConfig
import org.wordpress.android.ui.prefs.AppPrefs

private val localColors = staticCompositionLocalOf { extraPaletteJPLight }

@Composable
fun AppThemeM3(
isDarkTheme: Boolean = isSystemInDarkTheme(),
isJetpackApp: Boolean = BuildConfig.IS_JETPACK_APP,
isEinkMode: Boolean = if (LocalInspectionMode.current) false else AppPrefs.isEinkModeEnabled(),

Check warning on line 25 in WordPress/src/main/java/org/wordpress/android/ui/compose/theme/AppThemeM3.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unnecessary Boolean literal.

See more on https://sonarcloud.io/project/issues?id=wordpress-mobile_WordPress-Android&issues=AZzFsEPPh-FmWewhhGSC&open=AZzFsEPPh-FmWewhhGSC&pullRequest=22663
content: @Composable () -> Unit
) {
AppThemeM3WithoutBackground(isDarkTheme, isJetpackApp) {
AppThemeM3WithoutBackground(isDarkTheme, isJetpackApp, isEinkMode) {
ContentInSurfaceM3(content)
}
}
Expand All @@ -31,35 +34,46 @@
fun AppThemeM3WithoutBackground(
isDarkTheme: Boolean = isSystemInDarkTheme(),
isJetpackApp: Boolean = BuildConfig.IS_JETPACK_APP,
isEinkMode: Boolean = if (LocalInspectionMode.current) false else AppPrefs.isEinkModeEnabled(),

Check warning on line 37 in WordPress/src/main/java/org/wordpress/android/ui/compose/theme/AppThemeM3.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unnecessary Boolean literal.

See more on https://sonarcloud.io/project/issues?id=wordpress-mobile_WordPress-Android&issues=AZzFsEPPh-FmWewhhGSD&open=AZzFsEPPh-FmWewhhGSD&pullRequest=22663
content: @Composable () -> Unit
) {
val effectiveIsDark = if (isEinkMode) false else isDarkTheme

Check warning on line 40 in WordPress/src/main/java/org/wordpress/android/ui/compose/theme/AppThemeM3.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unnecessary Boolean literal.

See more on https://sonarcloud.io/project/issues?id=wordpress-mobile_WordPress-Android&issues=AZzFsEPPh-FmWewhhGSE&open=AZzFsEPPh-FmWewhhGSE&pullRequest=22663
val extraColors = getExtraColors(
isDarkTheme = isDarkTheme,
isJetpackApp = isJetpackApp
isDarkTheme = effectiveIsDark,
isJetpackApp = isJetpackApp,
isEinkMode = isEinkMode,
)
CompositionLocalProvider(localColors provides extraColors) {
MaterialTheme(
colorScheme = getColorScheme(
isDarkTheme = isDarkTheme,
isJetpackApp = isJetpackApp
isDarkTheme = effectiveIsDark,
isJetpackApp = isJetpackApp,
isEinkMode = isEinkMode,
),
content = content
)
}
}

/**
* This theme should *only* be used in the context of the Editor (e.g. Post Settings).
* This theme should *only* be used in the context of the Editor
* (e.g. Post Settings).
* More info: https://github.com/wordpress-mobile/gutenberg-mobile/issues/4889
*/
@Composable
fun AppThemeM3Editor(
isDarkTheme: Boolean = isSystemInDarkTheme(),
isJetpackApp: Boolean = BuildConfig.IS_JETPACK_APP,
isEinkMode: Boolean = if (LocalInspectionMode.current) false else AppPrefs.isEinkModeEnabled(),

Check warning on line 67 in WordPress/src/main/java/org/wordpress/android/ui/compose/theme/AppThemeM3.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unnecessary Boolean literal.

See more on https://sonarcloud.io/project/issues?id=wordpress-mobile_WordPress-Android&issues=AZzFsEPPh-FmWewhhGSF&open=AZzFsEPPh-FmWewhhGSF&pullRequest=22663
content: @Composable () -> Unit
) {
val effectiveIsDark = if (isEinkMode) false else isDarkTheme

Check warning on line 70 in WordPress/src/main/java/org/wordpress/android/ui/compose/theme/AppThemeM3.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unnecessary Boolean literal.

See more on https://sonarcloud.io/project/issues?id=wordpress-mobile_WordPress-Android&issues=AZzFsEPPh-FmWewhhGSG&open=AZzFsEPPh-FmWewhhGSG&pullRequest=22663
androidx.compose.material3.MaterialTheme(
colorScheme = getColorScheme(isDarkTheme = isDarkTheme, isJetpackApp = isJetpackApp),
colorScheme = getColorScheme(
isDarkTheme = effectiveIsDark,
isJetpackApp = isJetpackApp,
isEinkMode = isEinkMode,
),
content = content
)
}
Expand All @@ -70,8 +84,11 @@
@Suppress("SameParameterValue")
private fun getColorScheme(
isDarkTheme: Boolean,
isJetpackApp: Boolean
isJetpackApp: Boolean,
isEinkMode: Boolean = false,
): ColorScheme {
if (isEinkMode) return colorSchemeEink

return if (isJetpackApp) {
if (isDarkTheme) {
colorSchemeJPDark
Expand All @@ -85,6 +102,31 @@
}
}

// E-ink displays are grayscale with limited shade range (typically
// 16 levels). Error color is the same as onSurface because there
// aren't enough distinct shades to differentiate them — error
// states rely on icons, borders, and layout rather than color alone.
private val colorSchemeEink = lightColorScheme(
primary = AppColor.Black,
secondary = AppColor.Gray50,
background = AppColor.White,
surface = AppColor.White,
error = AppColor.Black,
onPrimary = AppColor.White,
onSecondary = AppColor.White,
onBackground = AppColor.Black,
onSurface = AppColor.Black,
onError = AppColor.White,
primaryContainer = AppColor.Gray10,
onPrimaryContainer = AppColor.Black,
secondaryContainer = AppColor.Gray10,
onSecondaryContainer = AppColor.Black,
outline = AppColor.Gray50,
outlineVariant = AppColor.Gray30,
surfaceVariant = AppColor.Gray10,
onSurfaceVariant = AppColor.Black,
)

private val colorSchemeJPLight = lightColorScheme(
primary = AppColor.JetpackGreen50,
secondary = AppColor.JetpackGreen30,
Expand Down Expand Up @@ -142,8 +184,11 @@
@Suppress("SameParameterValue")
private fun getExtraColors(
isDarkTheme: Boolean,
isJetpackApp: Boolean
isJetpackApp: Boolean,
isEinkMode: Boolean = false,
): ExtraColors {
if (isEinkMode) return extraPaletteEink

return if (isJetpackApp) {
if (isDarkTheme) {
extraPaletteJPDark
Expand All @@ -157,6 +202,13 @@
}
}

private val extraPaletteEink = ExtraColors(
success = AppColor.Black,
warning = AppColor.Gray50,
neutral = AppColor.Gray50,
ghost = AppColor.Black,
)

private val extraPaletteJPLight = ExtraColors(
success = AppColor.JetpackGreen50,
warning = AppColor.Orange50,
Expand Down
Loading