0

My activity layout is as follows

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:id="@+id/fragment_container"
    android:layout_height="match_parent">


        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <com.google.android.material.tabs.TabLayout
                android:id="@+id/inbox_tab"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="@style/TabLayoutStyle"
                />
        </androidx.viewpager2.widget.ViewPager2>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

The adapter is given below

class InboxAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {

    override fun getItemCount(): Int = 2

    override fun createFragment(position: Int): Fragment =
        InboxFragment().apply {
 
            arguments = Bundle().apply {
               // Our object is just an integer :-P
               putInt(InboxFragment.CURRENT_TAB, position)
            }
        }
    
}

The fragment layout consists of a single recycler view which is created, and intialised in onCreateView method.

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="LinearLayoutManager"/>

My activity makes a call to API endpoint to get two sets of lists representing a combination of read, and unread notifications, each of which is populated in the recycler view of the particular fragment.

The activity stores the value in two sets of live data instances and passes the data between the two fragments like the tutorial suggests. This is working well.

ISSUE

I have to send a PUT request to the backend in which the request body consists of list of unread notifications for the fragment which is current in the foreground.

  1. when the user switches tabs
  2. when the user leaves the screen

For the first option, I can do something like this in my fragment as follows:

override fun onPause() {
    // Aggregates the list of unread notifications, and submits a PUT request.
    // Updates the view model live data backing list as well to mark as read.
    // View Model is initialised as per the tutorial

    inboxViewModel.markNotificationsAsRead(arguments?.getInt(CURRENT_TAB, -1) ?: 0)
    super.onPause()
}

This fails for the 2nd use case when the user leaves the screen, thereby destroying the activity. In this case, both the fragments would be paused before their view is destroyed. This results in both the unread notifications in both the list to be marked as read.

Consider the use case in which the user is on the first fragment (which is loaded by default), then leaves the inbox screen to navigate to another screen which managed by another activity, then I want just the unread notifications displayed in the first fragment to be marked as read.

Since the 2nd fragment was not loaded, the unread notifications that would have been displayed in the 2nd fragment should not be marked as read.

How can I achieve this?

Kartik
  • 2,541
  • 2
  • 37
  • 59
  • Why don't you mark them as read as soon as the user taps on the notification? – Praveen Nov 18 '21 at 09:32
  • We do it for clicks as well, but in this specific case we are looking to mark as read when the user leaves the screen. – Kartik Nov 18 '21 at 17:22
  • Just to clarify this is not a push notifications but an inbox like screen for something like emails. – Kartik Nov 18 '21 at 22:14

1 Answers1

0

Mark the message as read when fragment be visible. So you don't need to mark whether the message was read or not when the Activity is destroyed.

Or adding a public field to fragment to tag whether fragment has shown. Then according this tag to call inboxViewModel.markNotificationsAsRead.

How to determine if the fragment is displayed

------------------------------------20210-11-12------------------------
Demo code snippet

interface ICheckFragment {
    fun markShown()

    fun hasShown(): Boolean
}

// in your adapter
xxxAdapter: FragmentPagerAdapter(fm) {
    private val fragments = mutableListOf<Fragment>()
    init {
        val fragment = PlaceholderFragment.newInstance(1)
        val fragment2 = PlaceholderFragment.newInstance(2)
        fragments.add(fragment)
        fragments.add(fragment2)
    }
    override fun getItem(position: Int): Fragment {
        return fragments[position]
    }

}

// in your activity code
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            // Please notice that this callback not be called at first when viewpager shown. 
            // You need mark fragment at position 0 shown manually 
            override fun onTabSelected(tab: TabLayout.Tab?) {
                val position = tab?.position ?: 0
                val fragment = sectionsPagerAdapter.getItem(position)
                if (fragment is ICheckFragment) {
                    fragment.markShown()
                }
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) = Unit

            override fun onTabReselected(tab: TabLayout.Tab?) = Unit

        })
val fragment0 = sectionsPagerAdapter.getItem(0)
                if (fragment is ICheckFragment) {
                    fragment.markShown()
                }

// in your fragment
class xxxFragment: Fragment(), ICheckFragment {
    ...
    private var hasShown = false
    override fun markShown() {
        hasShown = true
    }

    override fun hasShown(): Boolean {
        return hasShown
    }

    override fun onPause() {
        super.onPause()
        // do your logic
        if (hasShown) {

        } else {

        }
    }
}

leimenghao
  • 226
  • 1
  • 10
  • Two reasons: 1. there would be no way to distinguish between newer, and older notifications. 2. The proposed solution has been deprecated in AndroidX fragments. https://developer.android.com/reference/androidx/fragment/app/Fragment.html#setUserVisibleHint(boolean) – Kartik Nov 18 '21 at 17:22
  • @Kartik How about monitoring TabLayout's onTabSelected event, then set if Fragment is displayed – leimenghao Nov 19 '21 at 01:57
  • Would you mind pasting a code snippet, – Kartik Nov 19 '21 at 21:10
  • @Kartik I updated my answer with demo code snippet. – leimenghao Nov 22 '21 at 02:15
  • @leimenghao can you check this [issue](https://stackoverflow.com/a/69813808/11560810) – Kotlin Learner Jan 11 '22 at 17:15