Creating a custom Pulsate Inbox
The PuslateSDK also allows Developers to directly access the Pulsate Feed API to get the Pulsate Feed Models and build them in a custom way.
To get the Feed Models just call this method "void getFeed(int page, IPulsateValueListener listener)". This method takes two arguments - "page" which specifies which page of the Feed do you want to get and "listener" which is the listener to which we send the Feed Models and any errors if they occur. The models that are returned are - PulsateInboxItemCard and PulsateInboxItemTalk. Having all these models you can build a custom Feed.
val pulsateManager = PulsateFactory.getInstance()
pulsateManager.getFeed(1, object : IPulsateValueListener<PulsateFeedState> {
override fun onSuccess(feedState: PulsateFeedState) {
mAdapter.setCustomFeed(feedState.mInboxItems)
}
override fun onError(e: Throwable) {
e.printStackTrace()
}
})
Showing the Feed in a custom way also means that item clicks also need to be handled in a custom way. When handling on click events you must pass those clicks also to the PulsateSDK for it to be able to open the proper destination (deeplink, url, back of card, feed, reply) and also to send open event and custom events. To pass the clicks use the "void handleFeedClick(PulsateInboxItem pulsateInboxItem, int destination)" method.
mBinding!!.cardView.setOnClickListener {
val pulsateManager = PulsateFactory.getInstance()
pulsateManager.handleFeedClick(itemCard, PulsateUtils.PulsateCardDestination.VIEW_MORE);
}
Example:
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/custom_feed_recycler"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
app:cardBackgroundColor="@color/colorAccent"
app:cardCornerRadius="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/card_view_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/card_view_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/card_view_body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:text="TextView"
app:layout_constraintBottom_toTopOf="@+id/card_view_reply"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/card_view_title" />
<Button
android:id="@+id/card_view_reply"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Reply"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/card_view_view_more"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/card_view_view_more"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="View More"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/card_view_reply" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
class CustomFeedFragment : Fragment() {
private var mBinding: FragmentCustomFeedBinding? = null
private var mAdapter: CustomFeedRecyclerAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAdapter = CustomFeedRecyclerAdapter()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mBinding = FragmentCustomFeedBinding.inflate(inflater, container, false)
setupRecyclerView()
return mBinding!!.root
}
override fun onStart() {
super.onStart()
val pulsateManager = getInstance()
pulsateManager.getFeed(1, object : IPulsateValueListener<PulsateFeedState> {
override fun onSuccess(feedState: PulsateFeedState) {
mAdapter?.setCustomFeed(feedState.mFeedItems)
}
override fun onError(e: Throwable) {
e.printStackTrace()
}
})
}
private fun setupRecyclerView() {
mBinding!!.customFeedRecycler.layoutManager = GridLayoutManager(context, 1)
mBinding!!.customFeedRecycler.adapter = mAdapter
}
}
class CustomFeedRecyclerAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder?>() {
private val mInboxItems: MutableList<PulsateFeedItem> = ArrayList()
fun setCustomFeed(newPulsateInboxItems: List<PulsateFeedItem>?) {
mInboxItems.clear()
mInboxItems.addAll(newPulsateInboxItems!!)
notifyDataSetChanged()
}
override fun getItemViewType(position: Int): Int {
if (mInboxItems[position] is PulsateFeedItemCard) {
return INBOX_ITEM_CARD
} else if (mInboxItems[position] is PulsateFeedItemTalk) {
return INBOX_ITEM_TALK
}
return INBOX_ITEM_NONE
}
override fun getItemCount(): Int {
return mInboxItems.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return when (viewType) {
INBOX_ITEM_CARD -> {
val itemBinding: FragmentCustomFeedItemBinding =
FragmentCustomFeedItemBinding.inflate(layoutInflater, parent, false)
FragmentCustomFeedItemCardViewHolder(itemBinding)
}
INBOX_ITEM_TALK -> {
val itemBinding: FragmentCustomFeedItemBinding =
FragmentCustomFeedItemBinding.inflate(layoutInflater, parent, false)
FragmentCustomFeedItemTalkViewHolder(itemBinding)
}
else -> {
val itemBinding: FragmentCustomFeedItemBinding =
FragmentCustomFeedItemBinding.inflate(layoutInflater, parent, false)
FragmentCustomFeedItemTalkViewHolder(itemBinding)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder.itemViewType) {
INBOX_ITEM_CARD -> {
val itemCard: PulsateFeedItemCard = mInboxItems[position] as PulsateFeedItemCard
(holder as FragmentCustomFeedItemCardViewHolder).bind(itemCard)
}
INBOX_ITEM_TALK -> {
val itemTalk: PulsateFeedItemTalk = mInboxItems[position] as PulsateFeedItemTalk
(holder as FragmentCustomFeedItemTalkViewHolder).bind(itemTalk)
}
}
}
private class FragmentCustomFeedItemCardViewHolder(binding: FragmentCustomFeedItemBinding) :
RecyclerView.ViewHolder(binding.root) {
val mBinding: FragmentCustomFeedItemBinding
init {
mBinding = binding
}
fun bind(itemCard: PulsateFeedItemCard) {
mBinding.cardViewReply.setOnClickListener { view ->
val pulsateManager = getInstance()
pulsateManager.handleFeedClick(itemCard, PulsateUtils.PulsateCardDestination.REPLY)
}
mBinding.cardViewViewMore.setOnClickListener { view ->
val pulsateManager = getInstance()
pulsateManager.handleFeedClick(
itemCard,
PulsateUtils.PulsateCardDestination.VIEW_MORE
)
}
for (i in 0 until itemCard.front.size) {
val frontElement: PulsateCardFrontBack = itemCard.front[i]
if (frontElement.type == "headline") {
val headlineAttrs = frontElement.attrs[0] as PulsateCardHeadlineAttrs
mBinding.cardViewTitle.text = headlineAttrs.text
} else if (frontElement.type == "text") {
val textAttrs = frontElement.attrs[0] as PulsateCardHeadlineAttrs
mBinding.cardViewBody.text = textAttrs.text
}
}
}
}
private class FragmentCustomFeedItemTalkViewHolder(binding: FragmentCustomFeedItemBinding) :
RecyclerView.ViewHolder(binding.root) {
val mBinding: FragmentCustomFeedItemBinding
init {
mBinding = binding
}
fun bind(itemTalk: PulsateFeedItemTalk) {
mBinding.cardViewReply.setOnClickListener { view ->
val pulsateManager = getInstance()
pulsateManager.handleFeedClick(itemTalk, PulsateUtils.PulsateCardDestination.REPLY)
}
val lastMessage: PulsateCardLastMessage = itemTalk.lastMessages[0]
mBinding.cardViewTitle.text = lastMessage.sender
mBinding.cardViewBody.text = lastMessage.content
}
}
companion object {
const val INBOX_ITEM_NONE = 0
const val INBOX_ITEM_CARD = 1
const val INBOX_ITEM_TALK = 2
}
}