From 6a11c45d52f8be8903937d76d11ab3d518b0fd88 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 22 Oct 2026 16:07:17 -0400 Subject: [PATCH 1/4] task: load wp-env post types and posts in the Android demo The local WordPress site was never stored as an account, so the Android demo couldn't build an API client for it. The post type picker fell back to Post alone, and Browse was hidden. Like the iOS demo, it now builds an in-memory account from the wp-env credentials. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YNDBX7RNXn3NsfDuV1PkNV --- android/app/build.gradle.kts | 2 ++ .../example/gutenbergkit/EditorActivity.kt | 3 +-- .../gutenbergkit/GutenbergKitApplication.kt | 11 ++++++++ .../gutenbergkit/LocalWordPressCredentials.kt | 25 +++++++++++++++++-- .../example/gutenbergkit/PostsListActivity.kt | 3 +-- .../gutenbergkit/SitePreparationActivity.kt | 6 ++++- .../gutenbergkit/SitePreparationViewModel.kt | 5 ++-- 7 files changed, 45 insertions(+), 10 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 9964be38d..aa2596428 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -55,6 +55,8 @@ android { buildConfigField("String", "WP_ENV_SITE_URL", "\"${wpEnvCredentials["siteUrl"] ?: ""}\"") buildConfigField("String", "WP_ENV_SITE_API_ROOT", "\"${wpEnvCredentials["siteApiRoot"] ?: ""}\"") buildConfigField("String", "WP_ENV_AUTH_HEADER", "\"${wpEnvCredentials["authHeader"] ?: ""}\"") + buildConfigField("String", "WP_ENV_USERNAME", "\"${wpEnvCredentials["username"] ?: ""}\"") + buildConfigField("String", "WP_ENV_APP_PASSWORD", "\"${wpEnvCredentials["appPassword"] ?: ""}\"") } buildTypes { diff --git a/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt b/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt index 2acf0d33e..4ba16c951 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt @@ -464,8 +464,7 @@ private suspend fun persistPost( ) val app = context.applicationContext as GutenbergKitApplication - val account = app.accountRepository.all().firstOrNull { it.id() == accountId } - ?: error("Account not found") + val account = app.findAccount(accountId) ?: error("Account not found") val client = app.createApiClient(account) val endpointType = when (configuration.postType.postType) { diff --git a/android/app/src/main/java/com/example/gutenbergkit/GutenbergKitApplication.kt b/android/app/src/main/java/com/example/gutenbergkit/GutenbergKitApplication.kt index 51df489e2..dcc089d92 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/GutenbergKitApplication.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/GutenbergKitApplication.kt @@ -37,6 +37,17 @@ class GutenbergKitApplication : Application() { } } + /** + * Returns the account with the given [id], including the local WordPress + * site's in-memory account, which isn't stored. + */ + fun findAccount(id: ULong): Account? { + if (id == LocalWordPressCredentials.ACCOUNT_ID) { + return LocalWordPressCredentials.load()?.toAccount() + } + return accountRepository.all().firstOrNull { it.id() == id } + } + /** * Constructs a [WpApiClient] for the given [account]. Used by the demo app's posts * list and Save button to fetch/persist posts via the WordPress REST API. diff --git a/android/app/src/main/java/com/example/gutenbergkit/LocalWordPressCredentials.kt b/android/app/src/main/java/com/example/gutenbergkit/LocalWordPressCredentials.kt index 1c56cd32e..d80a89e87 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/LocalWordPressCredentials.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/LocalWordPressCredentials.kt @@ -1,11 +1,30 @@ package com.example.gutenbergkit +import uniffi.wp_mobile.Account + data class LocalWordPressCredentials( val siteUrl: String, val siteApiRoot: String, - val authHeader: String + val authHeader: String, + val username: String, + val appPassword: String ) { + /** + * An in-memory account for the local site, as the iOS demo builds, so the + * posts list and Save button can reach it without a stored account. + */ + fun toAccount(): Account = Account.SelfHostedSite( + id = ACCOUNT_ID, + domain = siteUrl, + username = username, + password = appPassword, + siteApiRoot = siteApiRoot + ) + companion object { + /** Never assigned to a stored account, whose IDs start at 1. */ + const val ACCOUNT_ID: ULong = 0u + /** * Loads credentials from BuildConfig fields populated at build time from * `.wp-env.credentials.json`. Remaps `localhost` to `10.0.2.2` so the @@ -18,7 +37,9 @@ data class LocalWordPressCredentials( return LocalWordPressCredentials( siteUrl = remapLocalhost(siteUrl), siteApiRoot = remapLocalhost(BuildConfig.WP_ENV_SITE_API_ROOT), - authHeader = BuildConfig.WP_ENV_AUTH_HEADER + authHeader = BuildConfig.WP_ENV_AUTH_HEADER, + username = BuildConfig.WP_ENV_USERNAME, + appPassword = BuildConfig.WP_ENV_APP_PASSWORD ) } diff --git a/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt b/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt index efc079b8a..fe69228a2 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt @@ -176,8 +176,7 @@ class PostsListViewModel( try { val app = application as GutenbergKitApplication - val account = app.accountRepository.all().firstOrNull { it.id() == accountId } - ?: error("Account not found") + val account = app.findAccount(accountId) ?: error("Account not found") val client = app.createApiClient(account) val endpointType = when (postType.postType) { diff --git a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt index 6ae8449a7..3e67e7810 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt @@ -155,7 +155,11 @@ class SitePreparationActivity : ComponentActivity() { SitePreparationViewModelFactory(application, configurationItem) )[SitePreparationViewModel::class.java] - val accountId = (configurationItem as? ConfigurationItem.ConfiguredEditor)?.accountId + val accountId = when (configurationItem) { + is ConfigurationItem.ConfiguredEditor -> configurationItem.accountId + is ConfigurationItem.LocalWordPress -> LocalWordPressCredentials.ACCOUNT_ID + is ConfigurationItem.BundledEditor -> null + } setContent { AppTheme { diff --git a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt index b71615e04..ee58051d2 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt @@ -76,7 +76,7 @@ class SitePreparationViewModel( try { loadConfiguration( ConfigurationItem.ConfiguredEditor( - accountId = 0u, + accountId = LocalWordPressCredentials.ACCOUNT_ID, name = "Local WordPress", siteUrl = credentials.siteUrl, siteApiRoot = credentials.siteApiRoot, @@ -337,8 +337,7 @@ class SitePreparationViewModel( config: ConfigurationItem.ConfiguredEditor ): List? { val app = getApplication() - val account = app.accountRepository.all().firstOrNull { it.id() == config.accountId } - ?: return null + val account = app.findAccount(config.accountId) ?: return null val client = app.createApiClient(account) val result = client.request { builder -> From e4c51f00ba5f44473132ceaf9800a860faa4860e Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Fri, 23 Oct 2026 11:04:57 -0400 Subject: [PATCH 2/4] task: derive the local wp-env editor config from its account Match the iOS demo, which computes the auth header from the credentials and reads the editor config from the in-memory account. Also stop a missing account ID from resolving to the local site's ID. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ae1XmRJfxLNoawZJQ2nYQ5 --- android/app/build.gradle.kts | 1 - .../example/gutenbergkit/LocalWordPressCredentials.kt | 2 -- .../example/gutenbergkit/SitePreparationActivity.kt | 2 +- .../example/gutenbergkit/SitePreparationViewModel.kt | 10 +++------- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index aa2596428..7da12899b 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -54,7 +54,6 @@ android { buildConfigField("String", "WP_ENV_SITE_URL", "\"${wpEnvCredentials["siteUrl"] ?: ""}\"") buildConfigField("String", "WP_ENV_SITE_API_ROOT", "\"${wpEnvCredentials["siteApiRoot"] ?: ""}\"") - buildConfigField("String", "WP_ENV_AUTH_HEADER", "\"${wpEnvCredentials["authHeader"] ?: ""}\"") buildConfigField("String", "WP_ENV_USERNAME", "\"${wpEnvCredentials["username"] ?: ""}\"") buildConfigField("String", "WP_ENV_APP_PASSWORD", "\"${wpEnvCredentials["appPassword"] ?: ""}\"") } diff --git a/android/app/src/main/java/com/example/gutenbergkit/LocalWordPressCredentials.kt b/android/app/src/main/java/com/example/gutenbergkit/LocalWordPressCredentials.kt index d80a89e87..3ff97ede0 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/LocalWordPressCredentials.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/LocalWordPressCredentials.kt @@ -5,7 +5,6 @@ import uniffi.wp_mobile.Account data class LocalWordPressCredentials( val siteUrl: String, val siteApiRoot: String, - val authHeader: String, val username: String, val appPassword: String ) { @@ -37,7 +36,6 @@ data class LocalWordPressCredentials( return LocalWordPressCredentials( siteUrl = remapLocalhost(siteUrl), siteApiRoot = remapLocalhost(BuildConfig.WP_ENV_SITE_API_ROOT), - authHeader = BuildConfig.WP_ENV_AUTH_HEADER, username = BuildConfig.WP_ENV_USERNAME, appPassword = BuildConfig.WP_ENV_APP_PASSWORD ) diff --git a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt index 3e67e7810..a63ec74d3 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationActivity.kt @@ -127,7 +127,7 @@ class SitePreparationActivity : ComponentActivity() { TYPE_LOCAL_WORDPRESS -> ConfigurationItem.LocalWordPress TYPE_CONFIGURED -> { ConfigurationItem.ConfiguredEditor( - accountId = json.optLong(KEY_ACCOUNT_ID, 0).toULong(), + accountId = json.getLong(KEY_ACCOUNT_ID).toULong(), name = json.getString(KEY_NAME), siteUrl = json.getString(KEY_SITE_URL), siteApiRoot = json.getString(KEY_SITE_API_ROOT), diff --git a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt index ee58051d2..c1a3bdca4 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/SitePreparationViewModel.kt @@ -75,13 +75,9 @@ class SitePreparationViewModel( ) try { loadConfiguration( - ConfigurationItem.ConfiguredEditor( - accountId = LocalWordPressCredentials.ACCOUNT_ID, - name = "Local WordPress", - siteUrl = credentials.siteUrl, - siteApiRoot = credentials.siteApiRoot, - authHeader = credentials.authHeader - ) + ConfigurationItem.ConfiguredEditor + .fromAccount(credentials.toAccount()) + .copy(name = "Local WordPress") ) } catch (e: java.net.ConnectException) { throw IllegalStateException( From 4fa11745bbc29addac59fdff546643629281937e Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Fri, 23 Oct 2026 11:30:54 -0400 Subject: [PATCH 3/4] task: route custom post types through their REST base in the Android demo The REST route uses the post type's rest_base, which can differ from its slug, so Browse and Save failed with rest_no_route for those types. Matches the iOS demo. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ae1XmRJfxLNoawZJQ2nYQ5 --- .../src/main/java/com/example/gutenbergkit/EditorActivity.kt | 2 +- .../src/main/java/com/example/gutenbergkit/PostsListActivity.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt b/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt index 4ba16c951..24960e3ea 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/EditorActivity.kt @@ -470,7 +470,7 @@ private suspend fun persistPost( val endpointType = when (configuration.postType.postType) { "page" -> PostEndpointType.Pages "post" -> PostEndpointType.Posts - else -> PostEndpointType.Custom(configuration.postType.postType) + else -> PostEndpointType.Custom(configuration.postType.restBase) } val params = PostUpdateParams( diff --git a/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt b/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt index fe69228a2..e4515b62c 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/PostsListActivity.kt @@ -182,7 +182,7 @@ class PostsListViewModel( val endpointType = when (postType.postType) { "page" -> PostEndpointType.Pages "post" -> PostEndpointType.Posts - else -> PostEndpointType.Custom(postType.postType) + else -> PostEndpointType.Custom(postType.restBase) } // Single page only — matches the iOS demo, which doesn't paginate either. From 2a8c560c96208e10d755025c49f5dcd5a424571b Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Fri, 23 Oct 2026 11:31:16 -0400 Subject: [PATCH 4/4] task: look up demo accounts by ID instead of loading them all `all()` decrypts every stored account to return one, and fails outright if any of them can't be decrypted. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ae1XmRJfxLNoawZJQ2nYQ5 --- .../java/com/example/gutenbergkit/GutenbergKitApplication.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/example/gutenbergkit/GutenbergKitApplication.kt b/android/app/src/main/java/com/example/gutenbergkit/GutenbergKitApplication.kt index dcc089d92..465322c52 100644 --- a/android/app/src/main/java/com/example/gutenbergkit/GutenbergKitApplication.kt +++ b/android/app/src/main/java/com/example/gutenbergkit/GutenbergKitApplication.kt @@ -45,7 +45,7 @@ class GutenbergKitApplication : Application() { if (id == LocalWordPressCredentials.ACCOUNT_ID) { return LocalWordPressCredentials.load()?.toAccount() } - return accountRepository.all().firstOrNull { it.id() == id } + return accountRepository.get(id) } /**