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,215 @@
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()
val clipLabel = stringResource(R.string.device_info_title)

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(clipLabel, 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,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import org.wordpress.android.support.SupportWebViewActivity
import org.wordpress.android.ui.accounts.DeviceInfoActivity
import org.wordpress.android.ui.accounts.applicationpassword.ApplicationPasswordsListActivity
import org.wordpress.android.ui.blaze.blazecampaigns.BlazeCampaignParentActivity
import org.wordpress.android.ui.bloggingprompts.promptslist.BloggingPromptsListActivity
Expand Down Expand Up @@ -40,6 +41,7 @@ import org.wordpress.android.ui.reader.ReaderSubsActivity
import org.wordpress.android.ui.selfhostedusers.SelfHostedUsersActivity
import org.wordpress.android.ui.sitemonitor.SiteMonitorParentActivity
import org.wordpress.android.ui.subscribers.SubscribersActivity
import org.wordpress.android.ui.prefs.AppPrefs
import org.wordpress.android.ui.taxonomies.TermsDataViewActivity

/**
Expand All @@ -50,6 +52,13 @@ open class BaseAppCompatActivity : AppCompatActivity() {
@Override
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Disable animations for e-ink devices
if (AppPrefs.isEinkModeEnabled()) {
window.setWindowAnimations(0)
disableActivityTransition()
}

// apply insets for Android 15+ edge-to-edge
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) &&
!isExcludedActivity(this)
Expand All @@ -58,6 +67,23 @@ open class BaseAppCompatActivity : AppCompatActivity() {
}
}

override fun finish() {
super.finish()
if (AppPrefs.isEinkModeEnabled()) {
disableActivityTransition()
}
}

@Suppress("DEPRECATION")
private fun disableActivityTransition() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, 0, 0)
overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, 0, 0)
} else {
overridePendingTransition(0, 0)
}
}

@RequiresApi(Build.VERSION_CODES.R)
private fun applyInsetOffsets() {
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, insets ->
Expand Down Expand Up @@ -90,6 +116,7 @@ private val excludedActivities = listOf(
BlazeCampaignParentActivity::class.java.name,
BloggingPromptsListActivity::class.java.name,
DebugSharedPreferenceFlagsActivity::class.java.name,
DeviceInfoActivity::class.java.name,
DomainManagementActivity::class.java.name,
EditJetpackSocialShareMessageActivity::class.java.name,
ExperimentalFeaturesActivity::class.java.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.play.core.install.model.AppUpdateType;
Expand Down Expand Up @@ -135,6 +136,7 @@
import org.wordpress.android.util.AniUtils;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;
import org.wordpress.android.util.EinkDeviceDetector;
import org.wordpress.android.util.AuthenticationDialogUtils;
import org.wordpress.android.util.BuildConfigWrapper;
import org.wordpress.android.util.DeviceUtils;
Expand Down Expand Up @@ -410,6 +412,8 @@ && getIntent().getExtras().getBoolean(ARG_CONTINUE_JETPACK_CONNECT, false)) {
checkTrackAnalyticsEvent();
}

showEinkPromptIfNeeded();

// Ensure deep linking activities are enabled.They may have been disabled elsewhere and failed to get re-enabled
enableDeepLinkingComponentsIfNeeded();

Expand Down Expand Up @@ -549,6 +553,39 @@ private void showBloggingPromptsOnboarding() {
);
}

private void showEinkPromptIfNeeded() {
if (AppPrefs.isEinkAutoDetectDone()
|| !EinkDeviceDetector.INSTANCE.isEinkDevice()) {
return;
}
AnalyticsTracker.track(Stat.EINK_PROMPT_SHOWN);
new MaterialAlertDialogBuilder(this)
.setTitle(R.string.eink_prompt_title)
.setMessage(R.string.eink_prompt_message)
.setCancelable(false)
.setPositiveButton(R.string.eink_prompt_enable, (dialog, which) -> {
AppPrefs.setEinkAutoDetectDone(true);
AppPrefs.setEinkModeEnabled(true);
AnalyticsTracker.track(Stat.EINK_PROMPT_ACCEPTED);
Toast.makeText(
this,
R.string.eink_enabled_message,
Toast.LENGTH_LONG
).show();
recreate();
})
.setNegativeButton(R.string.eink_prompt_no_thanks, (dialog, which) -> {
AppPrefs.setEinkAutoDetectDone(true);
AnalyticsTracker.track(Stat.EINK_PROMPT_DISMISSED);
Toast.makeText(
this,
R.string.eink_declined_message,
Toast.LENGTH_LONG
).show();
})
.show();
}

private void checkDismissNotification() {
final Intent intent = getIntent();
if (intent != null && intent.hasExtra(ARG_DISMISS_NOTIFICATION)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ public enum UndeletablePrefKey implements PrefKey {
// These preferences persist across logout/login cycles.
IS_TRACK_NETWORK_REQUESTS_ENABLED,
TRACK_NETWORK_REQUESTS_RETENTION_PERIOD,

// Indicates if e-ink auto-detection has already run on this device
EINK_AUTO_DETECT_DONE,
}

static SharedPreferences prefs() {
Expand Down Expand Up @@ -1817,4 +1820,20 @@ public static int getTrackNetworkRequestsRetentionPeriod() {
public static void setTrackNetworkRequestsRetentionPeriod(int period) {
setInt(UndeletablePrefKey.TRACK_NETWORK_REQUESTS_RETENTION_PERIOD, period);
}

public static boolean isEinkModeEnabled() {
return getExperimentalFeatureConfig("eink_mode");
}

public static void setEinkModeEnabled(boolean enabled) {
setExperimentalFeatureConfig(enabled, "eink_mode");
}

public static boolean isEinkAutoDetectDone() {
return getBoolean(UndeletablePrefKey.EINK_AUTO_DETECT_DONE, false);
}

public static void setEinkAutoDetectDone(boolean done) {
setBoolean(UndeletablePrefKey.EINK_AUTO_DETECT_DONE, done);
}
}
Loading
Loading