Skip to content

Instantly share code, notes, and snippets.

@Judas
Last active May 13, 2022 16:28
Show Gist options
  • Save Judas/d0a34a50f1b6b8d542d77af5db9d9787 to your computer and use it in GitHub Desktop.
Save Judas/d0a34a50f1b6b8d542d77af5db9d9787 to your computer and use it in GitHub Desktop.
iAdvize Android SDK - Chat button overlay management
class ActivityLifecycleController constructor(listener: CurrentActivityUpdatedListener) :
Application.ActivityLifecycleCallbacks {
interface CurrentActivityUpdatedListener {
fun onCurrentActivityUpdated()
}
/**
* Current activity displayed in screen.
*/
var currentActivityReference: WeakReference<Activity>? = null
set(value) {
field = value
listeners.iterator().forEach { it.onCurrentActivityUpdated() }
}
private var activityRefCount = 0
private var isActivityChangingConfigurations = false
var startWatching = false
val listeners = mutableListOf<CurrentActivityUpdatedListener>()
init {
listeners.add(listener)
}
/**
* Called right after the activity has been started.
*/
override fun onActivityStarted(activity: Activity) {
Logger.log(Logger.Level.VERBOSE, "onActivityStarted $activityRefCount")
currentActivityReference = WeakReference(activity)
activityRefCount++
}
/**
* Called right before the activity is stopped.
*/
override fun onActivityStopped(activity: Activity) {
Logger.log(Logger.Level.VERBOSE, "onActivityStopped $currentActivityReference")
currentActivityReference?.let { act ->
act.get()?.let {
if (it.javaClass.name == activity.javaClass.name) {
currentActivityReference = null
}
}
isActivityChangingConfigurations = activity.isChangingConfigurations
activityRefCount--
}
}
/**
* Called right after the activity has been created.
*/
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
/**
* Called right after the activity has been destroyed.
*/
override fun onActivityDestroyed(activity: Activity) {}
/**
* Called right after the activity has been resumed (come to the foreground).
*/
override fun onActivityResumed(activity: Activity) {}
/**
* Called when the activity is paused (but still awake).
*/
override fun onActivityPaused(activity: Activity) {}
/**
* Called when the activity state is saved.
*/
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
}
val activityLifecycleController = ActivityLifecycleController(this::updateChatButton)
val overlayController = OverlayController()
override fun updateChatButton() {
activityLifecycleController.currentActivityReference?.get()?.let {
val sdkActivated = IAdvizeSDK.activationStatus == IAdvizeSDK.ActivationStatus.ACTIVATED
val chatboxOpened = IAdvizeSDK.chatboxController.isChatboxPresented()
val ruleAvailable = IAdvizeSDK.targetingController.isActiveTargetingRuleAvailable()
val hasOngoingConv = IAdvizeSDK.conversationController.ongoingConversation() != null
if (sdkActivated && !chatboxOpened && (hasOngoingConv || ruleAvailable)) {
overlayController.showChatButton(it) { _ -> presentChatbox(it) }
} else {
overlayController.hideChatButton(it)
}
}
}
class OverlayController() {
private val layoutResId = R.layout.your_custom_button
// Array of activities where chat button should not be displayed
private val exceptionsArray = arrayOf(
AttachmentActivity::class.java,
ChatboxActivity::class.java,
ImageAttachmentViewerActivity::class.java,
VideoLobbyActivity::class.java,
VideoActivity::class.java
)
/**
* Show the chat button on the current activity.
*
* @param activity: Current activity where to show button.
*/
internal fun showChatButton(
activity: Activity,
clickListener: View.OnClickListener
) {
// Add an exception for activities where chat button is useless
if (exceptionsArray.contains(activity::class.java)) return
if (!hasChatButton(activity)) {
Logger.log(Logger.Level.VERBOSE, "Showing default chat button.")
val button = RelativeLayout.inflate(activity, layoutResId, null)
button.layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, BOTTOM or LEFT)
button.setOnClickListener { clickListener.onClick(it) }
getRootView(activity).addView(button)
}
}
/**
* Hide the chat button on the current activity.
*
* @param activity: Current activity where to hide button.
*/
internal fun hideChatButton(activity: Activity) {
getChatButton(activity)?.let {
Logger.log(Logger.Level.VERBOSE, "Hiding default chat button.")
getRootView(activity).removeView(it)
}
}
/**
* Check if the current activity has the chat button in the hierarchy.
*
* @param activity: Current activity to check.
* @return: `true` if the activity has the chat button.
*/
internal fun hasChatButton(activity: Activity): Boolean = getChatButton(activity) != null
/**
* Get the root view of the activity
*
* @param activity: Current activity to get the root view.
* @return: The ViewGroup at the top of the hierarchy of the activity.
*/
internal fun getRootView(activity: Activity): FrameLayout =
activity.findViewById(android.R.id.content)
/**
* Get the chat button in the given activity.
*
* @param activity: Current activity to get the chat button.
* @return: `null` if the activity has no chat button,
* the button view if the activity has it.
*/
fun getChatButton(activity: Activity): View? =
getRootView(activity).findViewById<RelativeLayout>(layoutResId)
}
@Judas
Copy link
Author

Judas commented Sep 15, 2021

The ActivityLifecycleController is in charge of observing the activities update in your app and store the current one.
The OverlayController adds the button to the given activity, directly inside the root view, above the current layout.

The ChatButtonController snippet shows how to orchestrate these two.

@Judas
Copy link
Author

Judas commented Sep 15, 2021

When developing an hybrid application (webview based), the ActivityLifecycleController must be started before the main webview activity is started otherwise the controller won't be able to trigger the button display.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment