I try to create a notification overlay inside my application that can show notifications about certain application wise important events.
I decided to use a RecyclerView
which will be drown directly on WindowManager
.
This works fine for showing initial items, however the the items don't get updated.
Below is my implementation. So when I call start
the not1
and not2
are shown, but when removeNotification
function get called, the notification is actually being removed and a correct list is being submitted to the adapter, but the view on screen does not update.
However if I add windowManager.updateViewLayout(recyclerView, layoutParams)
after submitList
inside removeNotification
, everything seems to work as expected, but this time I am loosing RV animations..
As this is the first time I work with WindowManager directly, I am quite confused. Can someone help me to figure out what's going on and how can I achieve what I want to, if only that's possible.
class NotificationOverlay(private val context: Context) {
private val windowManager: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private val layoutParams = WindowManager.LayoutParams().apply {
gravity = android.view.Gravity.TOP or android.view.Gravity.CENTER_HORIZONTAL
width = WindowManager.LayoutParams.MATCH_PARENT
height = WindowManager.LayoutParams.WRAP_CONTENT
format = PixelFormat.TRANSLUCENT
dimAmount = 0.5f
flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND
type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
}
private val notifications = mutableListOf<NotificationItem>().also {
it.addAll(listOf(
NotificationItem(title = "not 1", message = "first notification"),
NotificationItem(title = "not 2", message = "second notification")
))
}
private val notificationsAdapter = NotificationAdapter(::removeNotification)
private val recyclerView = RecyclerView(context).apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
adapter = notificationsAdapter
}
private fun removeNotification(notification: NotificationItem){
notifications.remove(notification)
notificationsAdapter.submitList(notifications)
if(notificationsAdapter.currentList.isEmpty()){
windowManager.removeView(recyclerView)
}
}
fun show(){
windowManager.addView(recyclerView, layoutParams)
notificationsAdapter.submitList(notifications)
}
}
Edited
Well, I found out that the issue is not in WindowManager but rather in DiffUtils, but cannot understand what's wrong with it, as it is very simple one, and I implemented such DiffUtils a lot of times, anyways, I'll post the code here if you could have any idea on why this does not work:
class NotificationAdapter(private val onCloseClicked: (NotificationItem) -> Unit):
ListAdapter<NotificationItem, NotificationAdapter.NotificationViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationViewHolder {
val binding = ItemNotificationOverlayBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return NotificationViewHolder(binding, onCloseClicked)
}
override fun onBindViewHolder(holder: NotificationViewHolder, position: Int) {
holder.bind(currentList[position])
}
class NotificationViewHolder(private val itemBinding: ItemNotificationOverlayBinding, private val onCloseClicked: (NotificationItem) -> Unit): RecyclerView.ViewHolder(itemBinding.root) {
fun bind(item: NotificationItem) {
itemBinding.title.text = item.title
itemBinding.message.text = item.message
itemBinding.close.setOnClickListener {
onCloseClicked.invoke(item)
}
}
}
class DiffCallback : DiffUtil.ItemCallback<NotificationItem>() {
override fun areItemsTheSame(oldItem: NotificationItem, newItem: NotificationItem) =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: NotificationItem, newItem: NotificationItem) =
oldItem == newItem
}
}
What can every be wrong in such a simple construction? I am going crazy already and want to throw away this DiffUtils and implement the old school notifyItemRemoved
Edited 2
So the answer offered by @IamFr0ssT fixed the issue. I dig a bit deeper to see why this happens and the reason is in androidx.recyclerview.widget.AsyncListDiffer
class in main submitList
function. It is doing the following check there:
if (newList == mList) {
// nothing to do (Note - still had to inc generation, since may have ongoing work)
if (commitCallback != null) {
commitCallback.run();
}
return;
}
so my diff was never being even tried to be calculated as I was submitting the same reference of the list.
what the additional toList()
function suggested by @IamFr0ssT did, is created a new instance of the list thus making the differ to calculate my diff. If you go deeper inside toList()
function, it eventually creates a new instance of an ArrayList
based on provided list ...return ArrayList(this)
So well, this issue had nothing to do with WindowManager
just the DiffUtil