Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2958676
session manager + pass tickled trigger + slots + request
vvoicehovics Mar 29, 2026
e899095
split InAppMessageView for Base to be reusable by OM
vvoicehovics Mar 29, 2026
69ea2a6
overlay messaging view variant
vvoicehovics Mar 29, 2026
d8d0a48
store type on message -- to clear right slot later + can be used in i…
vvoicehovics Mar 29, 2026
5441f91
wire OM view to manager + edge fixes
vvoicehovics Mar 30, 2026
61d11f0
close dialog as soon as error, dont rely on children
vvoicehovics Mar 30, 2026
0273ba2
track clicked and dismissed events
vvoicehovics Mar 30, 2026
5abfe81
bump version + changelog
vvoicehovics Mar 30, 2026
e449f36
OM is not a standalone feature + make inapp vs OM mutually exclusive
vvoicehovics Mar 30, 2026
d053b5d
fix warnings in base view -- we are minsdk 21 long ago
vvoicehovics Mar 30, 2026
e1ad284
apply security suggestion from wiz
vvoicehovics Mar 30, 2026
6898f3c
ui for OM in test app + rename hold->defer + explicitly handle 204
vvoicehovics Mar 31, 2026
ee7d769
events coming from IAR
vvoicehovics Apr 1, 2026
812224c
dispose view on hard crashes in IAR -- to prevent highly improbably s…
vvoicehovics Apr 1, 2026
a065692
proper push triggers for immediate messages
vvoicehovics Apr 2, 2026
59d91f5
fix typo + match OM messages endpoint changes + session length min 1h…
vvoicehovics Apr 6, 2026
9de3319
bugfix: OM enablement check
vvoicehovics Apr 6, 2026
50d9b27
wont use mapping in the end
vvoicehovics Apr 6, 2026
b62a511
format code
vvoicehovics Apr 6, 2026
002677e
status bar color funs
vvoicehovics Apr 7, 2026
60e47c9
delayed init vs sessions
vvoicehovics Apr 7, 2026
191e78c
Merge branch 'master' into 277632-overlay-messaging
vvoicehovics Apr 7, 2026
4df7e98
remove clicked event, it's given by IAR directly
vvoicehovics Apr 8, 2026
a038a5b
clean up message after view error
vvoicehovics Apr 12, 2026
3e7758e
Merge branch 'master' into 277632-overlay-messaging
vvoicehovics Apr 12, 2026
56192e0
align interceptor outcomes
vvoicehovics Apr 13, 2026
812c7ec
iar was released
vvoicehovics Apr 13, 2026
311278a
use public messages url
vvoicehovics Apr 15, 2026
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
14 changes: 8 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# Changelog

## 7.13.0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry i've stolen your version number with a new bugfix ! 😎


- Implementation for Overlay Messaging channel. Check optimove developer docs for more.

## 7.12.3

- Wraps `isLaunchActivity()` in try/catch and safely returns false on failure to prevent crashes.
- Wraps `isLaunchActivity()` in try/catch and safely returns false on failure to prevent crashes.

## 7.12.2

- Introduces a lastShownByInterceptorId field that tracks which message was last shown through the interceptor, preventing duplicate interception of the same head message.
- Introduces a lastShownByInterceptorId field that tracks which message was last shown through the interceptor, preventing duplicate interception of the same head message.

## 7.12.1

Expand All @@ -18,12 +22,10 @@

- Fixed dismissed in-app messages re-appearing on fast consecutive app foregrounds


## 7.11.1

- Fix: expiryDate is always absent in EmbeddedMessage due to inconsistent format given by v2 endpoint


## 7.11.0

- Updated delayed configuration to no longer require a redundant region parameter. Region is now inferred from credentials. Deprecated `OptimoveConfig.Builder(Region, FeatureSet)` — use `Builder(FeatureSet)` instead.
Expand All @@ -32,7 +34,6 @@

- Minor bug fixes for Embedded Messaging: correct field mapping


## 7.10.1

- Bumped GSON version number to fix a vulnerability issue
Expand All @@ -52,7 +53,8 @@

## 7.8.0

Add In-App Message Interceptor API `OptimoveInApp.getInstance().setInAppMessageInterceptor`, basic usage:
Add In-App Message Interceptor API `OptimoveInApp.getInstance().setInAppMessageInterceptor`, basic usage:

```java
OptimoveInApp.getInstance().setInAppMessageInterceptor((message, decision) -> {
// Example: decide based on your own logic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)

OptimoveInApp.getInstance().setDeepLinkHandler(object : InAppDeepLinkHandlerInterface {
override fun handle(context: android.content.Context, buttonPress: InAppDeepLinkHandlerInterface.InAppButtonPress) {
override fun handle(
context: android.content.Context,
buttonPress: InAppDeepLinkHandlerInterface.InAppButtonPress
) {
Log.d(TAG, "DeepLink handler invoked")
startActivity(Intent(this@MainActivity, MainActivity::class.java))
}
Expand All @@ -64,10 +67,18 @@ class MainActivity : AppCompatActivity() {
val config = Optimove.getConfig()
val showPreferenceCenter = config.isPreferenceCenterConfigured
val showEmbeddedMessaging = config.isEmbeddedMessagingConfigured
val showOverlayMessaging = config.isOverlayMessagingEnabled
val showDelayedConfig = config.usesDelayedConfiguration()

if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), WRITE_EXTERNAL_PERMISSION_REQUEST_CODE)
if (ContextCompat.checkSelfPermission(
this, Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
WRITE_EXTERNAL_PERMISSION_REQUEST_CODE
)
}

Optimove.getInstance().seeIntent(intent, savedInstanceState)
Expand All @@ -86,6 +97,7 @@ class MainActivity : AppCompatActivity() {
outputText = outputText,
showPreferenceCenter = showPreferenceCenter,
showEmbeddedMessaging = showEmbeddedMessaging,
showOverlayMessaging = showOverlayMessaging,
showDelayedConfig = showDelayedConfig,
credentialsSubmitted = credentialsSubmitted,
isInterceptingInApp = isInterceptingInApp,
Expand All @@ -104,6 +116,7 @@ class MainActivity : AppCompatActivity() {
onGetPreferences = ::getPreferences,
onSetPreferences = ::setPreferences,
onViewEmbeddedMessaging = ::viewEmbeddedMessaging,
onViewOverlayMessaging = ::viewOverlayMessaging,
onSetCredentials = ::setCredentials,
onInAppInterceptionClicked = ::onInAppInterceptionClicked,
onSendLocation = { lat, lng ->
Expand All @@ -118,12 +131,14 @@ class MainActivity : AppCompatActivity() {
onResetToken = {},
onOpenDeeplinkTest = ::openDeeplinkTest,
onRegisterPush = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(this@MainActivity, Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_DENIED &&
!shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(
this@MainActivity, Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_DENIED && !shouldShowRequestPermissionRationale(
Manifest.permission.POST_NOTIFICATIONS
)
) {
outputText = "Notification permission permanently denied. Opening settings..."
outputText =
"Notification permission permanently denied. Opening settings..."
startActivity(Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, this@MainActivity.packageName)
})
Expand All @@ -142,12 +157,9 @@ class MainActivity : AppCompatActivity() {
onDelayedInitToggle = { enabled ->
isDelayedInit = enabled
qaPrefs.edit().putBoolean(MyApplication.KEY_DELAYED_INIT, enabled).apply()
outputText = if (enabled)
"Delayed init enabled — restart app to apply"
else
"Immediate init enabled — restart app to apply"
}
)
outputText = if (enabled) "Delayed init enabled — restart app to apply"
else "Immediate init enabled — restart app to apply"
})
}
}
}
Expand Down Expand Up @@ -222,30 +234,27 @@ class MainActivity : AppCompatActivity() {
outputText = "Calling setUserId"
Optimove.getInstance().setUserId(userId)
}

userId.isEmpty() -> {
outputText = "Calling setUserEmail"
Optimove.getInstance().setUserEmail(userEmail)
}

else -> {
outputText = "Calling registerUser"
Optimove.getInstance().registerUser(userId, userEmail)
}
}
identityPrefs.edit()
.putString(KEY_USER_ID, userId)
.putString(KEY_USER_EMAIL, userEmail)
identityPrefs.edit().putString(KEY_USER_ID, userId).putString(KEY_USER_EMAIL, userEmail)
.apply()
}

private fun clearIdentity() {
identityPrefs.edit()
.remove(KEY_USER_ID)
.remove(KEY_USER_EMAIL)
.apply()
identityPrefs.edit().remove(KEY_USER_ID).remove(KEY_USER_EMAIL).apply()
persistedUserId = ""
persistedUserEmail = ""
outputText = "Identity cleared (saved values removed)"
}
}

private fun readInbox() {
val items = OptimoveInApp.getInstance().inboxItems
Expand All @@ -272,18 +281,21 @@ class MainActivity : AppCompatActivity() {
items.forEach { OptimoveInApp.getInstance().deleteMessageFromInbox(it) }
}


Comment thread
graciecooper marked this conversation as resolved.
private fun getPreferences() {
OptimovePreferenceCenter.getInstance().getPreferencesAsync { result, preferences ->
when (result) {
OptimovePreferenceCenter.ResultType.ERROR_USER_NOT_SET,
OptimovePreferenceCenter.ResultType.ERROR,
OptimovePreferenceCenter.ResultType.ERROR_CREDENTIALS_NOT_SET -> Log.d(PC_TAG, result.toString())
OptimovePreferenceCenter.ResultType.ERROR_USER_NOT_SET, OptimovePreferenceCenter.ResultType.ERROR, OptimovePreferenceCenter.ResultType.ERROR_CREDENTIALS_NOT_SET -> Log.d(
PC_TAG, result.toString()
)

OptimovePreferenceCenter.ResultType.SUCCESS -> preferences?.let { prefs ->
Log.d(PC_TAG, "configured: ${prefs.configuredChannels}")
prefs.customerPreferences.forEach { topic ->
Log.d(PC_TAG, "${topic.id} ${topic.name} ${topic.subscribedChannels}")
}
}

else -> Log.d(PC_TAG, "unknown res type")
}
}
Expand All @@ -292,9 +304,10 @@ class MainActivity : AppCompatActivity() {
private fun setPreferences() {
OptimovePreferenceCenter.getInstance().getPreferencesAsync { result, preferences ->
when (result) {
OptimovePreferenceCenter.ResultType.ERROR_USER_NOT_SET,
OptimovePreferenceCenter.ResultType.ERROR,
OptimovePreferenceCenter.ResultType.ERROR_CREDENTIALS_NOT_SET -> Log.d(PC_TAG, result.toString())
OptimovePreferenceCenter.ResultType.ERROR_USER_NOT_SET, OptimovePreferenceCenter.ResultType.ERROR, OptimovePreferenceCenter.ResultType.ERROR_CREDENTIALS_NOT_SET -> Log.d(
PC_TAG, result.toString()
)

OptimovePreferenceCenter.ResultType.SUCCESS -> preferences?.let { prefs ->
Log.d(PC_TAG, "loaded prefs for set: good")
val configuredChannels: List<Channel> = prefs.configuredChannels
Expand All @@ -303,10 +316,10 @@ class MainActivity : AppCompatActivity() {
PreferenceUpdate(topic.id, configuredChannels.subList(0, 1))
}
OptimovePreferenceCenter.getInstance().setCustomerPreferencesAsync(
{ setResult -> Log.d(PC_TAG, setResult.toString()) },
updates
{ setResult -> Log.d(PC_TAG, setResult.toString()) }, updates
)
}

else -> Log.d(PC_TAG, "unknown res type")
}
}
Expand All @@ -316,8 +329,16 @@ class MainActivity : AppCompatActivity() {
startActivity(Intent(this, EmbeddedMessagingActivity::class.java))
}

private fun viewOverlayMessaging() {
startActivity(Intent(this, OverlayMessagingActivity::class.java))
}

private fun openDeeplinkTest() {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(DeeplinkTargetActivity.DEEPLINK_TEST_URI)))
startActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(DeeplinkTargetActivity.DEEPLINK_TEST_URI)
)
)
}

private fun setCredentials(optimove: String?, optimobile: String?, prefCenter: String?) {
Expand Down Expand Up @@ -348,19 +369,18 @@ class MainActivity : AppCompatActivity() {
val options = arrayOf("Default (5000 ms)", "12,000 ms")
var selected = 0

AlertDialog.Builder(this)
.setTitle("Enable In-App Interception")
AlertDialog.Builder(this).setTitle("Enable In-App Interception")
.setSingleChoiceItems(options, 0) { _, which -> selected = which }
.setPositiveButton("Enable") { d, _ ->
val timeoutMs = if (selected == 0) 5000L else 12000L
enableInAppInterception(timeoutMs)
isInterceptingInApp = true

outputText = "In-App interception enabled ($timeoutMs ms timeout per message)"
Toast.makeText(this, "In-App interception enabled ($timeoutMs ms)", Toast.LENGTH_SHORT).show()

d.dismiss()
}
.setNegativeButton("Cancel", null)
.show()
}.setNegativeButton("Cancel", null).show()
}

private fun disableInAppInterception() {
Expand All @@ -373,10 +393,13 @@ class MainActivity : AppCompatActivity() {

private fun enableInAppInterception(timeoutMs: Long) {
OptimoveInApp.getInstance().setInAppMessageInterceptor(object : InAppMessageInterceptor {
override fun processMessage(messageData: JSONObject?, decision: InAppMessageInterceptorCallback) {
override fun processMessage(
messageData: JSONObject?, decision: InAppMessageInterceptorCallback
) {
runOnUiThread {
inAppDecisionDialog?.takeIf { it.isShowing }?.dismiss()
val dataText = messageData?.toString() ?: "No data provided"

inAppDecisionDialog = AlertDialog.Builder(this@MainActivity)
.setTitle("QA: In-App Message")
.setMessage("$dataText\nChoose Show, Postpone, or Suppress.")
Expand All @@ -394,6 +417,7 @@ class MainActivity : AppCompatActivity() {
}
.setOnCancelListener { decision.suppress() }
.create()

inAppDecisionDialog?.show()
}
}
Expand All @@ -405,8 +429,7 @@ class MainActivity : AppCompatActivity() {
private class SimpleCustomEvent : OptimoveEvent() {
override fun getName(): String = "Simple cUSTOM_Event "
override fun getParameters(): Map<String, Any> = mapOf(
"strinG_param" to " some_string ",
"number_param" to 42
"strinG_param" to " some_string ", "number_param" to 42
)
}

Expand Down
Loading
Loading