Migration Guide: 2.x → 3.x
Deprecated in 3.0.0Existing
OWUIFlowsandOWUIViewscode keeps working in 3.0.0 but produces deprecation warnings at compile time. Migrate toOWUIComponentsto clear the warnings.For the full Components API reference, see the Components page.
Version 3.0.0 introduces three additions to the OpenWeb Android SDK:
OWUIComponents— a unified API that replaces bothOWUIFlowsandOWUIViews- Jetpack Compose support —
PreConversationandConversationcomposables in the newspotim-composemodule - Per-element font customization via
OWCustomizationElements
Accessing the New Components API
Before (2.x):
val flows = OpenWeb.manager.ui.flows
val views = OpenWeb.manager.ui.viewsAfter (3.x):
val components = OpenWeb.manager.ui.componentsFlows.fragments → Components
| Before (2.x) | After (3.x) |
|---|---|
flows.fragments.getPreConversation(postId, …) | components.getPreConversation(postId, …) |
flows.fragments.getConversation(postId, …) | components.getConversation(postId, …) |
getPreConversation (fragment embed)
Before:
flows.fragments.getPreConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
additionalSettings = OWAdditionalSettings(),
flowActionsCallback = null,
callback = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) {
// handle error
}
}
)After:
components.getPreConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
conversationNavigation = OWConversationNavigationMode.ConversationFullScreen(),
additionalSettings = OWAdditionalSettings(),
actionCallbacks = null,
completion = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) {
// handle error
}
}
)getConversation (fragment embed)
Before:
flows.fragments.getConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
additionalSettings = OWAdditionalSettings(),
flowActionsCallback = null,
callback = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) {
// handle error
}
}
)After:
components.getConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
route = null,
additionalSettings = OWAdditionalSettings(),
actionCallbacks = null,
completion = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) {
// handle error
}
}
)Flows.intents → openConversation()
| Before (2.x) | After (3.x) |
|---|---|
flows.intents.getConversation(postId, …) | components.openConversation(postId, …) |
flows.intents.getCommentThread(postId, commentId, …) | components.openConversation(…, route = OWConversationRoute.OWCommentThreadRoute(commentId)) |
flows.intents.getCommentCreation(postId, …) | components.openConversation(…, route = OWConversationRoute.OWCommentCreationRoute(type)) |
Conversation intent
Before:
flows.intents.getConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
additionalSettings = OWAdditionalSettings(),
flowActionsCallback = null,
callback = object : SpotCallback<Intent>() {
override fun onSuccess(intent: Intent) { startActivity(intent) }
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)After:
components.openConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
additionalSettings = OWAdditionalSettings(),
actionCallbacks = null
)Comment thread intent
Before:
flows.intents.getCommentThread(
postId = "POST_ID",
commentId = "COMMENT_ID",
articleSettings = OWArticleSettings(),
additionalSettings = OWAdditionalSettings(),
flowActionsCallback = null,
callback = object : SpotCallback<Intent>() {
override fun onSuccess(intent: Intent) { startActivity(intent) }
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)After:
components.openConversation(
postId = "POST_ID",
route = OWConversationRoute.OWCommentThreadRoute(commentId = "COMMENT_ID"),
additionalSettings = OWAdditionalSettings(),
actionCallbacks = null
)Comment creation intent
Before:
flows.intents.getCommentCreation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
additionalSettings = OWAdditionalSettings(),
flowActionsCallback = null,
callback = object : SpotCallback<Intent>() {
override fun onSuccess(intent: Intent) { startActivity(intent) }
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)After:
components.openConversation(
postId = "POST_ID",
route = OWConversationRoute.OWCommentCreationRoute(type = OWCommentCreationType.Comment),
additionalSettings = OWAdditionalSettings(),
actionCallbacks = null
)Views → Components
| Before (2.x) | After (3.x) |
|---|---|
views.getPreConversation(postId, …) | components.getPreConversation(postId, …) |
views.getConversation(postId, …) | components.getConversation(postId, …) |
views.getCommentThread(commentId, postId, …) | components.getConversation(…, route = OWConversationRoute.OWCommentThreadRoute(commentId)) |
views.getCommentCreation(postId, …, commentCreationType, …) | components.getConversation(…, route = OWConversationRoute.OWCommentCreationRoute(type)) |
views.getReportReasons(…) | Removed — handled internally by the SDK |
getPreConversation
Before:
views.getPreConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
preConversationSettings = OWPreConversationSettings(),
viewActionsCallbacks = null,
callback = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)After:
components.getPreConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
conversationNavigation = OWConversationNavigationMode.ConversationFullScreen(),
additionalSettings = OWAdditionalSettings(),
actionCallbacks = null,
completion = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)getConversation
Before:
views.getConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
conversationSettings = OWConversationSettings(),
viewActionsCallbacks = null,
callback = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)After:
components.getConversation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
route = null,
additionalSettings = OWAdditionalSettings(),
actionCallbacks = null,
completion = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)getCommentThread
Before:
views.getCommentThread(
commentId = "COMMENT_ID",
postId = "POST_ID",
articleSettings = OWArticleSettings(),
commentThreadSettings = OWCommentThreadSettings(),
viewActionsCallbacks = null,
callback = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)After:
components.getConversation(
postId = "POST_ID",
route = OWConversationRoute.OWCommentThreadRoute(commentId = "COMMENT_ID"),
additionalSettings = OWAdditionalSettings(),
actionCallbacks = null,
completion = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)getCommentCreation
Before:
views.getCommentCreation(
postId = "POST_ID",
articleSettings = OWArticleSettings(),
commentCreationType = OWCommentCreationType.Comment,
commentCreationSettings = OWCommentCreationSettings(),
viewActionsCallbacks = null,
callback = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)After:
components.getConversation(
postId = "POST_ID",
route = OWConversationRoute.OWCommentCreationRoute(type = OWCommentCreationType.Comment),
additionalSettings = OWAdditionalSettings(),
actionCallbacks = null,
completion = object : SpotCallback<Fragment>() {
override fun onSuccess(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit()
}
override fun onFailure(exception: SpotException) { /* handle error */ }
}
)getReportReasons (removed)
views.getReportReasons(...) has no replacement. Report reason selection is now handled internally by the SDK and no longer requires a call site.
Callbacks
OWFlowActionsCallbacks (used in both flows.fragments and flows.intents) remains valid in 3.x via the actionCallbacks parameter on OWUIComponents methods. Rename your existing flowActionsCallback variable to actionCallbacks if desired — the interface is the same.
OWViewActionsCallbacks (used in views.*) is replaced by OWActionCallbacks in the Components API. Update your implementation to conform to OWActionCallbacks:
val actionCallbacks = object : OWActionCallbacks {
override fun callback(
type: OWFlowActionCallbackType,
source: OWFlowSourceType,
postId: OWPostId
) {
// same logic as before
}
}Customizations
3.0.0 introduces OWCustomizationElements, accessed via OpenWeb.manager.ui.customizations.elements. It replaces the OWTheme (customizedTheme), OWCommentActionsCustomizations (commentActions), and setCustomUIDelegate APIs.
Note: The 2.x customization APIs still compile in 3.0.0 — they emit deprecation warnings and are scheduled for removal in 4.0.0. Migrate before upgrading to 4.x. For the full element reference, see the Customization page.
Theme → Elements
Replace customizedTheme (OWTheme) property assignments with the matching elements.<element>.color.
Before (2.x):
OpenWeb.manager.ui.customizations.customizedTheme = OWTheme(
primaryBackgroundColor = UIColor(0xFFFFFFFF),
primaryTextColor = UIColor(0xFF000000),
brandColor = UIColor(0xFFFF5722)
)After (3.x):
with(OpenWeb.manager.ui.customizations.elements) {
background.color = UIColor(lightColor = Color.WHITE, darkColor = Color.BLACK)
commentBody.color = UIColor(lightColor = Color.BLACK, darkColor = Color.WHITE)
brand.color = UIColor(lightColor = Color.parseColor("#FF5722"), darkColor = Color.parseColor("#FF5722"))
}Full property mapping (from the SDK's own deprecation hints):
| OWTheme property (2.x) | elements equivalent (3.x) |
|---|---|
primaryBackgroundColor | background |
secondaryBackgroundColor | overlayBackground |
tertiaryBackgroundColor | cardBackground |
primaryTextColor | commentBody |
secondaryTextColor | subtitle |
tertiaryTextColor | detail |
primarySeparatorColor | sectionDivider |
secondarySeparatorColor | contentDivider |
tertiarySeparatorColor | divider |
primaryBorderColor | border |
secondaryBorderColor | Removed — no replacement |
loaderColor | loader |
brandColor | brand |
skeletonColor | skeletonGradientEdge |
skeletonShimmeringColor | skeletonGradientCenter |
voteUpSelectedColor | voteUpSelected |
voteUpUnselectedColor | voteUpUnselected |
voteDownSelectedColor | voteDownSelected |
voteDownUnselectedColor | voteDownUnselected |
statusBarColor | Removed — no replacement |
navigationBarColor | Removed — no replacement |
Comment Actions → elements.commentActions
The commentActions enum-based API is replaced by direct color and font control on elements.commentActions.
Before (2.x):
OpenWeb.manager.ui.customizations.commentActions = OWCommentActionsCustomizations(
commentActionsButtonsColor = CommentActionsButtonsColor.BRAND_COLOR,
commentActionsButtonsFont = CommentActionsButtonsFont.SEMI_BOLD
)After (3.x):
with(OpenWeb.manager.ui.customizations.elements.commentActions) {
color = UIColor(lightColor = Color.parseColor("#FF5722"), darkColor = Color.parseColor("#FF5722"))
fontWeight = OWFontWeight.SEMI_BOLD
}setCustomUIDelegate → per-element callbacks
Replace the single CustomUIDelegate with a customizeView callback on the specific element. Declarative color/fontFamily/fontWeight are applied before the callback runs, and the callback receives an OWCustomizationContext (isDarkModeEnabled, postId).
Before (2.x):
OpenWeb.manager.ui.customizations.setCustomUIDelegate(object : CustomUIDelegate {
override fun customizeView(
viewType: CustomizableViewType,
view: View,
isDarkModeEnabled: Boolean,
postId: OWPostId
) {
when (viewType) {
CustomizableViewType.NAVIGATION_TITLE_TEXT_VIEW ->
(view as TextView).textSize = 18f
CustomizableViewType.NAVIGATION_BACK_IMAGE_VIEW ->
(view as ImageView).setColorFilter(Color.BLACK)
else -> {}
}
}
})After (3.x):
with(OpenWeb.manager.ui.customizations.elements) {
navigationTitle.customizeView = { tv, ctx -> tv.textSize = 18f }
navigationBackIcon.customizeView = { imageView, ctx ->
imageView.setColorFilter(if (ctx.isDarkModeEnabled) Color.WHITE else Color.BLACK)
}
}CustomizableViewType value mapping:
| CustomizableViewType (2.x) | elements element (3.x) |
|---|---|
NAVIGATION_TITLE_TEXT_VIEW | navigationTitle |
LOGIN_PROMPT_TEXT_VIEW | loginPrompt |
COMMUNITY_QUESTION_TEXT_VIEW | communityQuestion |
COMMUNITY_GUIDELINES_TEXT_VIEW | communityGuidelines |
SAY_CONTROL_IN_PRE_CONVERSATION_TEXT_VIEW | sayControlPreConversation |
SAY_CONTROL_IN_CONVERSATION_TEXT_VIEW | sayControlConversation |
PRE_CONVERSATION_HEADER_TEXT_VIEW | preConversationHeaderText |
PRE_CONVERSATION_HEADER_COUNTER_TEXT_VIEW | preConversationHeaderCounter |
PRE_CONVERSATION_HEADER_USER_COUNT_TEXT_VIEW | preConversationHeaderUserCount |
CONVERSATION_USERNAME_TEXT_VIEW | commenterName |
CONVERSATION_COMMENT_COUNT_TEXT_VIEW | conversationCommentCount |
CONVERSATION_USER_COUNT_TEXT_VIEW | conversationUserCount |
CONVERSATION_SORT_TEXT_VIEW | conversationSortText |
READ_ONLY_TEXT_VIEW | readOnlyText |
EMPTY_STATE_READ_ONLY_TEXT_VIEW | emptyStateReadOnlyText |
NAVIGATION_BACK_IMAGE_VIEW | navigationBackIcon |
NAVIGATION_TOOLBAR_VIEW | navigationToolbar |
SHOW_COMMENTS_BUTTON | showCommentsButton |
COMMENT_CREATION_ACTION_BUTTON | commentCreationActionButton |
COMMENT_CREATION_ACTION_IMAGE_BUTTON | commentCreationActionImageButton |
CONVERSATION_FOOTER_VIEW | conversationFooter |
CONVERSATION_SORT_SPINNER_VIEW | conversationSortSpinner |
Fonts
Global font customization is unchanged — OpenWeb.manager.ui.customizations.fontFamily.styleResId = R.font.my_font still applies one font family across the SDK.
3.0.0 adds per-element fonts: set fontFamily and fontWeight (OWFontWeight) on individual elements via elements.<element>.
with(OpenWeb.manager.ui.customizations.elements) {
commentBody.fontFamily = "my_custom_font" // res/font/ name or a system family
commentBody.fontWeight = OWFontWeight.BOLD
}No migration is required for existing global-font code. See Custom Fonts for the full list of font-customizable elements and weight values.
Jetpack Compose
The new spotim-compose module provides PreConversation and Conversation composables for apps that use Jetpack Compose. This module is a new optional dependency — no migration is required if you are not using Compose. See the Jetpack Compose guide for setup instructions and full usage examples.
