Search code examples
androidkotlinnullpointerexceptionlayout-inflater

How to solve NullPointerException on LayoutInflaters?


I have been stuck on this issue for a bit of time now and I have looked through other questions on the subject. However, I do not fully understand their solutions and how it could be applied to my case.

Logcat

java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.realtimechat/com.example.realtimechat.chats.ChatActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.LayoutInflater android.view.Window.getLayoutInflater()' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3022)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3259)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1950)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7073)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.LayoutInflater android.view.Window.getLayoutInflater()' on a null object reference
        at android.app.Activity.getLayoutInflater(Activity.java:4435)
        at com.example.realtimechat.chats.ChatActivity.<init>(ChatActivity.kt:66)
        at java.lang.Class.newInstance(Native Method)
        at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:69)
        at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
        at android.app.Instrumentation.newActivity(Instrumentation.java:1215)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3010)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3259) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1950) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7073) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 

Chat Activity

package com.example.realtimechat.chats

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_PICK
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.provider.MediaStore.ACTION_IMAGE_CAPTURE
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.example.realtimechat.R
import com.example.realtimechat.common.Constants
import com.example.realtimechat.common.Extras
import com.example.realtimechat.common.NodeNames
import com.example.realtimechat.common.Utils
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.*
import com.google.firebase.storage.FirebaseStorage
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream


class ChatActivity : AppCompatActivity(), View.OnClickListener {

    private lateinit var etMessage: EditText
    private lateinit var ivSend: ImageView
    private lateinit var ivAttachment: ImageView

    private lateinit var mAuth: FirebaseAuth
    private lateinit var mRootRef: DatabaseReference

    private lateinit var currentUserId: String
    private lateinit var chatUserId: String

    private lateinit var rvMessages: RecyclerView
    private lateinit var srlMessages: SwipeRefreshLayout
    private lateinit var messagesAdapter: MessagesAdapter
    private lateinit var messagesList: MutableList<MessageModel>

    private var currentPage = 1
    companion object {
        private const val RECORD_PER_PAGE = 30
    }

    private val REQUEST_CODE_CAPTURE_IMAGE=102
    private val REQUEST_CODE_PICK_IMAGE=101
    private val REQUEST_CODE_PICK_VIDEO=103

    private lateinit var databaseReferenceMessages: DatabaseReference
    private lateinit var childEventListener: ChildEventListener

    private lateinit var bottomSheetDialog: BottomSheetDialog
    val view: View = layoutInflater.inflate(R.layout.chat_file_options, null)

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

        etMessage = findViewById(R.id.etMessage)
        ivSend = findViewById(R.id.ivSend)
        ivAttachment = findViewById(R.id.ivAttachment)

        ivSend.setOnClickListener(this)
        ivAttachment.setOnClickListener(this)

        mAuth = FirebaseAuth.getInstance()
        mRootRef = FirebaseDatabase.getInstance().reference

        currentUserId = mAuth.currentUser!!.uid

        if(intent.hasExtra(Extras.USER_KEY)){
            chatUserId = intent.getStringExtra(Extras.USER_KEY)!!
        }

        rvMessages = findViewById(R.id.rvMessages)
        srlMessages = findViewById(R.id.srlMessages)

        val layoutManager = LinearLayoutManager(applicationContext)
        layoutManager.orientation = RecyclerView.VERTICAL
        rvMessages.layoutManager = layoutManager
        rvMessages.setHasFixedSize(true)

        messagesList = ArrayList()
        messagesAdapter = MessagesAdapter(this, messagesList)
        rvMessages.adapter = messagesAdapter

        loadMessages()
        rvMessages.scrollToPosition(messagesList.size-1)

        srlMessages.setOnRefreshListener(object: SwipeRefreshLayout.OnRefreshListener{
            override fun onRefresh() {
                currentPage++
                loadMessages()
            }

        })

        bottomSheetDialog = BottomSheetDialog(this)
        view.findViewById<View>(R.id.llCamera).setOnClickListener(this)
        view.findViewById<View>(R.id.llGallery).setOnClickListener(this)
        view.findViewById<View>(R.id.llVideo).setOnClickListener(this)
        view.findViewById<View>(R.id.ivClose).setOnClickListener(this)
        bottomSheetDialog.setContentView(view)
    }

    //send messages
    private fun sendMessage(msg: String, msgType: String, pushId: String){
        try {
            if (msg != "") {
                val messageMap = HashMap<Any, Any>()
                messageMap[NodeNames.MESSAGE_ID] = pushId
                messageMap[NodeNames.MESSAGE] = msg
                messageMap[NodeNames.MESSAGE_TYPE] = msgType
                messageMap[NodeNames.MESSAGE_FROM] = currentUserId
                messageMap[NodeNames.MESSAGE_TIME] = ServerValue.TIMESTAMP

                val currentUserRef = NodeNames.MESSAGES + "/" + currentUserId + "/" + chatUserId
                val chatUserRef = NodeNames.MESSAGES + "/" + chatUserId + "/" + currentUserId

                val messageUserMap = mutableMapOf<String, Any>()
                messageUserMap["$currentUserRef/$pushId"] = messageMap
                messageUserMap["$chatUserRef/$pushId"] = messageMap

                etMessage.setText("")

                mRootRef.updateChildren(messageUserMap, object : DatabaseReference.CompletionListener {
                    override fun onComplete(databaseError: DatabaseError?, databaseReference: DatabaseReference) {
                        if (databaseError != null) {
                            Toast.makeText(applicationContext, "Failed to send message: ${databaseError.message}", Toast.LENGTH_SHORT).show()
                        }
                        run{
                            Toast.makeText(applicationContext, "Message sent successfully!", Toast.LENGTH_SHORT).show()
                        }
                    }
                })
            }
        } catch (e: Exception) {
            Toast.makeText(applicationContext, "Failed to send message: ${e.localizedMessage}", Toast.LENGTH_SHORT).show()
        }
    }

    private fun loadMessages(){
        messagesList.clear()
        databaseReferenceMessages = mRootRef.child(NodeNames.MESSAGES).child(currentUserId).child(chatUserId)

        val messageQuery: Query = databaseReferenceMessages.limitToLast(currentPage * RECORD_PER_PAGE)

        childEventListener = object: ChildEventListener{
            override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
                val message: MessageModel = snapshot.getValue(MessageModel::class.java)!!

                messagesList.add(message)
                messagesAdapter.notifyDataSetChanged()

                rvMessages.scrollToPosition(messagesList.size-1)
                srlMessages.isRefreshing = false
            }

            override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {
            }

            override fun onChildRemoved(snapshot: DataSnapshot) {
            }

            override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {
            }

            override fun onCancelled(error: DatabaseError) {
                srlMessages.isRefreshing = false
            }
        }

        messageQuery.addChildEventListener(childEventListener)
    }

    private fun uploadFile(uri: Uri, messageType: String){
        val databaseReference = mRootRef.child(NodeNames.MESSAGES).child(currentUserId).child(chatUserId).push()
        val pushId = databaseReference.key!!

        val folderName =
            if (messageType == Constants.MESSAGE_TYPE_VIDEO) Constants.MESSAGE_VIDEOS else Constants.MESSAGE_IMAGES
        val fileName: String =
            if (messageType == Constants.MESSAGE_TYPE_VIDEO) "$pushId.mp4" else "$pushId.jpg"

        val storageReference = FirebaseStorage.getInstance().reference
        val fileReference = storageReference.child(folderName).child(fileName)
        fileReference.putFile(uri)
    }

    private fun uploadBytes(bytes: ByteArrayOutputStream, messageType: String){
        val databaseReference = mRootRef.child(NodeNames.MESSAGES).child(currentUserId).child(chatUserId).push()
        val pushId = databaseReference.key!!

        val folderName =
            if (messageType == Constants.MESSAGE_TYPE_VIDEO) Constants.MESSAGE_VIDEOS else Constants.MESSAGE_IMAGES
        val fileName: String =
            if (messageType == Constants.MESSAGE_TYPE_VIDEO) "$pushId.mp4" else "$pushId.jpg"

        val storageReference = FirebaseStorage.getInstance().reference
        val fileReference = storageReference.child(folderName).child(fileName)
        fileReference.putBytes(bytes.toByteArray())
    }

    //if any objects are clicked
    override fun onClick(v: View?) {
        when(v!!.id){
            R.id.ivSend -> {
                val utility = Utils()
                if(utility.connectionAvailable(this)) {
                    val message = etMessage.text.toString().trim()

                    val messageType = NodeNames.MESSAGE_TYPE_TEXT

                    val userMessagePush =
                        mRootRef.child(NodeNames.MESSAGES).child(currentUserId).child(chatUserId)
                            .push()
                    val pushId = userMessagePush.key

                    sendMessage(message, messageType, pushId!!)
                }else{
                    Toast.makeText(applicationContext, "No Internet connection available", Toast.LENGTH_SHORT).show()
                }
            }
            R.id.ivAttachment -> {
                if(ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED){
                    if(bottomSheetDialog!=null){
                        bottomSheetDialog.show()
                    }
                }else{
                    ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), 1)
                }

                val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                if(inputMethodManager!=null){
                    inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
                }
            }
            R.id.llCamera -> {
                bottomSheetDialog.dismiss()
                val intentCamera = Intent(ACTION_IMAGE_CAPTURE)
                startActivityForResult(intentCamera, REQUEST_CODE_CAPTURE_IMAGE)
            }
            R.id.llGallery -> {
                bottomSheetDialog.dismiss()
                val intentImage = Intent(ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
                startActivityForResult(intentImage, REQUEST_CODE_PICK_IMAGE)
            }
            R.id.llVideo -> {
                bottomSheetDialog.dismiss()
                val intentVideo = Intent(ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
                startActivityForResult(intentVideo, REQUEST_CODE_PICK_VIDEO)
            }
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if(resultCode== Activity.RESULT_OK){
            if(requestCode==REQUEST_CODE_CAPTURE_IMAGE) {
                val bitMap = data!!.extras!!.get("data") as Bitmap

                val bytes = ByteArrayOutputStream()
                bitMap.compress(Bitmap.CompressFormat.JPEG, 100, bytes)
                uploadBytes(bytes, Constants.MESSAGE_TYPE_IMAGE)
            }else if(requestCode==REQUEST_CODE_PICK_IMAGE){
                val uri = data!!.data!!
                uploadFile(uri, Constants.MESSAGE_TYPE_IMAGE)
            }else if(requestCode==REQUEST_CODE_PICK_VIDEO){
                val uri = data!!.data!!
                uploadFile(uri, Constants.MESSAGE_TYPE_VIDEO)
            }
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if(requestCode==1){
            if(grantResults.size>1 && grantResults[0]==PackageManager.PERMISSION_GRANTED){
                if(bottomSheetDialog!=null){
                    bottomSheetDialog.show()
                }
            }else{
                Toast.makeText(this, "Permission needed", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

If anyone could explain (in an easy manner) how these types of problems could be approached and solved and how to solve it in my case, it would be greatly appreciated!


Solution

  • layoutInflater hasn't been instantiated yet. When doing anything with views, you pretty much have to wait until the onCreate method. So something like this:

    var view: View? = null
    // OR
    lateinit var view: View
    
    override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_chat)
    
       view = layoutInflater.inflate(R.layout.chat_file_options, null)
       ......
    }