Search code examples
androidkotlinuser-interfaceandroid-fragmentsviewmodel

Updating UI fragment from Activity


I am currently developing an Android NFC application. This application contains a NavigationDrawer in which I can access 3 different fragments which each correspond to 3 different NFC features.

My goal is that when the onNewIntent method is called, so when the NFC tag is detected, I update the UI with the information in the tag.

At first, the update of the UI is done synchronously but the idea is to have something asynchronous by using coroutines in the future.

The problem is simply that the UI does not update when the onNewIntent function is called, can you help me?

MainActivity:

private val memoryViewModel: MemoryViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    checkNFC()
    mNfcAdapter = NfcAdapter.getDefaultAdapter(this)
    setNfcIntent()

    configureToolbar()

    val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
    val navView: NavigationView = findViewById(R.id.nav_view)
    val navController = findNavController(R.id.nav_host_fragment)
    // Passing each menu ID as a set of Ids because each
    // menu should be considered as top level destinations.
    appBarConfiguration = AppBarConfiguration(setOf(
            R.id.nav_memory, R.id.nav_tag, R.id.nav_product), drawerLayout)
    setupActionBarWithNavController(navController, appBarConfiguration)
    navView.setupWithNavController(navController)
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    // Inflate the menu; this adds items to the action bar if it is present.
    menuInflater.inflate(R.menu.main, menu)
    return true
}

override fun onSupportNavigateUp(): Boolean {
    val navController = findNavController(R.id.nav_host_fragment)
    return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}

private fun configureToolbar() {
    val toolbar: Toolbar = findViewById(R.id.toolbar)
    setSupportActionBar(toolbar)
}

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
    Log.d(TAG, "Card ID: " + Tools.byteArrayToHex(tag!!.id))
    val techList = tag!!.techList

    updateUI()

    //Check that the discovered tag is a vicinity tag
    if (techList[0] == "android.nfc.tech.NfcV") {
        val tagUid = tag!!.id
        nfcvTag = NfcV.get(tag)

        //ISO/IEC 15693 tags can be operated in two modes:
        // Select mode and Addressed mode.
        //To work in the select mode it is needed to send a SELECT
        // command at the beginning of communic.
        //In the address mode, the tag UID is sent within each command.
        //This application works in SELECT MODE.
        val select_command: ByteArray = RFCommands.cmd_select
        System.arraycopy(tagUid, 0, select_command, 2, 8)
        if (nfcvTag != null) {
            try {
                nfcvTag!!.connect()
                val select_respo: ByteArray = nfcvTag!!.transceive(select_command)
                Log.d(TAG, "Select response: " +
                        Tools.byteArrayToHex(select_respo))
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }
}

private fun updateUI() {
    memoryViewModel.setManufacturer(nfcManufacturer.getValue(tag!!.id[6].toInt()))
}

MemoryFragment:

class MemoryFragment : Fragment() {

    private lateinit var memoryViewModel: MemoryViewModel

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        memoryViewModel =
                ViewModelProvider(this).get(MemoryViewModel::class.java)
        val root = inflater.inflate(R.layout.fragment_memory, container, false)

        val icManuf: TextView = root.findViewById(R.id.ic_manufacturer_value)
        memoryViewModel.icManufacturer.observe(viewLifecycleOwner, Observer {
            icManuf.text = it
        })
        return root
    }
}

MemoryViewModel:

class MemoryViewModel : ViewModel() {
    // The current IC Manufacturer
    private val _icManufacturer = MutableLiveData<String>()

    val icManufacturer: LiveData<String>
        get() = _icManufacturer

    init {
        _icManufacturer.value = ""
    }

    fun setManufacturer(value: String) {
        _icManufacturer.value = value
    }
}

The 3 different fragments are created in the mobile_nagivation.xml file:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/nav_memory">

    <fragment
        android:id="@+id/nav_memory"
        android:name="com.bodet.bodettag.ui.memory.MemoryFragment"
        android:label="@string/menu_memory"
        tools:layout="@layout/fragment_memory" />

    <fragment
        android:id="@+id/nav_tag"
        android:name="com.bodet.bodettag.ui.tag.TagFragment"
        android:label="@string/menu_tag_settings"
        tools:layout="@layout/fragment_tag" />

    <fragment
        android:id="@+id/nav_product"
        android:name="com.bodet.bodettag.ui.product.ProductFragment"
        android:label="@string/menu_product_settings"
        tools:layout="@layout/fragment_product" />
</navigation>

The activity_main_drawer.xml file contains items that have the same IDs as the fragments:

<item android:title="@string/menu_nfc">
    <menu>
        <group
            android:id="@+id/menu_top"
            android:checkableBehavior="single">
            <item
                android:id="@+id/nav_memory"
                android:icon="@drawable/ic_baseline_contactless_24"
                android:title="@string/menu_memory" />
            <item
                android:id="@+id/nav_tag"
                android:icon="@drawable/ic_baseline_memory_24"
                android:title="@string/menu_tag_settings" />
            <item
                android:id="@+id/nav_product"
                android:icon="@drawable/ic_icon_visio_x7_hov"
                android:title="@string/menu_product_settings" />
        </group>
    </menu>
</item>

Solution

  • If you want to update all fragments simultanousely use a Shared ViewModel

    check the doc: https://developer.android.com/topic/libraries/architecture/viewmodel#sharing

    In your case do:

    private val memoryViewModel: MemoryViewModel by activityViewModels()