0

We do not like the Toast but we included it in our DBHelper Class to know if the DB was created
YES the Logcat pointed at the Toast and the other involved Activities
First mistake not commenting out the Toast

Here is the ERROR

Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference

Here is the call made to the DBHelper

        btnSaveItemData.setOnClickListener {
        if (etItemData.text.toString().equals("")) {
            message("ENTER Item")
            etItemData.requestFocus()
            return@setOnClickListener
        }

        if (etItemFK.text.toString().equals("") || etItemFK.text.toString().toInt() == 0) {
            message("ENTER foreign key")
            etItemFK.requestFocus()
            return@setOnClickListener
        }

        val dbManager = DBHelper(this)
        val values = ContentValues()
        values.put("item", etItemData.text.toString())
        values.put("fkI",Integer.parseInt(etItemFK.text.toString()))
        if (idI == 0) {
            val mID = dbManager.insertITEM(values)

            if (mID > 0) {
                tvMsg.setTextColor(Color.RED)
                message("ADDED Item successfully")
                //Timer().schedule(800){
                    nextACTIVITY()
                //}
            } else {
                message("Failed to add Item")
            }
        }
    }

And here is the complete DBHelper with TOAST

class DBHelper(context: Context): SQLiteOpenHelper(context,DBHelper.DB_NAME,null,DBHelper.DB_VERSION) {

override fun onCreate(db: SQLiteDatabase?) {

    val CREATE_TABLE_DEPT = "CREATE TABLE ${PARENT_TABLE} ($colidD INTEGER PRIMARY KEY,$colDept TEXT,$colPFK INTEGER);"
    val CREATE_TABLE_ITEM = "CREATE TABLE ${CHILD_TABLE} ($colidI INTEGER PRIMARY KEY,$colItem TEXT,$colCFK INTEGER);"
    db!!.execSQL(CREATE_TABLE_DEPT)
    db.execSQL(CREATE_TABLE_ITEM)

    Toast.makeText(this.context,"database is created",Toast.LENGTH_LONG).show()
}

override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
    val DROP_TABLE_DEPT = "DROP TABLE IF EXISTS $PARENT_TABLE"
    val DROP_TABLE_ITEM = "DROP TABLE IF EXISTS $CHILD_TABLE"
    db!!.execSQL(DROP_TABLE_DEPT)
    db.execSQL(DROP_TABLE_ITEM)
    onCreate(db)
}

fun queryDEPT(): List<ModelParent> {
    val db = this.writableDatabase
    val parentList = ArrayList<ModelParent>()
    val selectQuery = "SELECT  * FROM ${PARENT_TABLE}"
    val cursor = db.rawQuery(selectQuery, null)
    if (cursor != null) {
        if (cursor.moveToFirst()) {
            do {
                val contact = ModelParent()
                contact.idD = Integer.parseInt(cursor.getString(cursor.getColumnIndex(colidD)))
                contact.dept = cursor.getString(cursor.getColumnIndex(colDept))
                contact.fkD = Integer.parseInt(cursor.getString(cursor.getColumnIndex(colPFK)))
                parentList.add(contact)
            } while (cursor.moveToNext())
        }
    }
    cursor.close()
    return parentList
}

fun queryITEM(): List<ModelChild> {
    val db = this.writableDatabase
    val childList = ArrayList<ModelChild>()
    val selectQuery = "SELECT  * FROM ${CHILD_TABLE}"
    val cursor = db.rawQuery(selectQuery, null)
    if (cursor != null) {
        if (cursor.moveToFirst()) {
            do {
                val contact = ModelChild()
                contact.idI = Integer.parseInt(cursor.getString(cursor.getColumnIndex(colidI)))
                contact.item = cursor.getString(cursor.getColumnIndex(colItem))
                contact.fkI = Integer.parseInt(cursor.getString(cursor.getColumnIndex(colCFK)))
                childList.add(contact)
            } while (cursor.moveToNext())
        }
    }
    cursor.close()
    return childList
}

fun insertDEPT(values: ContentValues): Long {
    val db = this.writableDatabase
    val idD = db.insert(PARENT_TABLE, null, values)
    return idD
}

fun updateDEPT(values: ContentValues, selection: String, selectionargs: Array<String>):Int{
    val db = this.writableDatabase
    val dept = db.update(PARENT_TABLE,values,selection,selectionargs)
    return dept
}

fun insertITEM(values: ContentValues): Long {
    val db = this.writableDatabase
    val idI = db.insert(CHILD_TABLE, null, values)
    return idI
}

fun updateITEM(values: ContentValues, selection: String, selectionargs: Array<String>): Int {
    val db = this.writableDatabase
    val count = db.update(CHILD_TABLE, values, selection, selectionargs)
    return count
}

fun deleteDEPT(productname: String): Boolean {
    var result = false
    val query = "SELECT * FROM $PARENT_TABLE WHERE $colDept= \"$productname\""
    val db = this.writableDatabase
    val cursor = db.rawQuery(query, null)

    if (cursor.moveToFirst()) {
        val id = Integer.parseInt(cursor.getString(0))
        db.delete(PARENT_TABLE, "$colidD = ?", arrayOf(id.toString()))
        cursor.close()
        result = true
    }
    db.close()
    return result
}

fun deleteITEM(productname: String): Boolean {
    var result = false
    val query = "SELECT * FROM $CHILD_TABLE WHERE $colItem= \"$productname\""
    val db = this.writableDatabase
    val cursor = db.rawQuery(query, null)

    if (cursor.moveToFirst()) {
        val id = Integer.parseInt(cursor.getString(0))
        db.delete(CHILD_TABLE, "$colidI = ?", arrayOf(id.toString()))
        cursor.close()
        result = true
    }
    db.close()
    return result
}

var context: Context? = null

companion object {
    private val DB_VERSION = 1
    private val DB_NAME = "Kids.db"
    private val PARENT_TABLE = "Parent"
    private val colidD = "idD"
    private val colDept = "Dept"
    private val colPFK = "fkD"
    private val CHILD_TABLE = "Child"
    private val colidI = "idI"
    private val colItem = "Item"
    private val colCFK = "fkI"
}

}

After the DBHelper writes the Data to the Database the nextActivity makes a call back to the DBHelper to queryDEPT and will display the data with the ViewParentActivity

class ViewParentActivity : AppCompatActivity() {

private var RecyclerAdapter: ParentAdapter? = null
private var recyclerView: RecyclerView? = null
private val db = DBHelper(this)
private var parentList:List<ModelParent> = ArrayList()
private var linearLayoutManager: LinearLayoutManager? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_view_parent)
    supportActionBar?.setDisplayHomeAsUpEnabled(true)

    initViews()

}// end onCreate


override fun onResume() {
    super.onResume()
    initDB()
}

// This is ONLY called when Activity is in onResume state
private fun initDB() {
    parentList = db.queryDEPT()
    if(parentList.isEmpty()){
        title = "No Records in DB"
    }else{
        title = "Parent List"
    }

    RecyclerAdapter = ParentAdapter(parentList = parentList, context = applicationContext)
    (recyclerView as RecyclerView).adapter = RecyclerAdapter
}

private fun initViews() {

    recyclerView = this.findViewById(R.id.rvParentView)
    // val etDeptFK can not be reasigned ?
    //etDeptFK = this.findViewById(R.id.etDeptFK)
    RecyclerAdapter = ParentAdapter(parentList = parentList, context = applicationContext)
    linearLayoutManager = LinearLayoutManager(applicationContext)
    (recyclerView as RecyclerView).layoutManager = linearLayoutManager!!
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.menu_main, menu)
    return super.onCreateOptionsMenu(menu)
}

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    if (item != null) {
        when (item.itemId) {
            R.id.addNote -> {
                val intent = Intent(this, EnterParentActivity::class.java)
                intent.putExtra("FROM","N")// ADD NEW NOTE
                startActivity(intent)
            }
        }
        // CODE below manages HOME Button
        val id = item.itemId
        if (id == android.R.id.home) {
            val intent = Intent(this, EnterParentActivity::class.java)
            intent.putExtra("FROM","N")// ADD NEW NOTE
            startActivity(intent)
        }
    }
    return super.onOptionsItemSelected(item)
}

Because we are curious now
The question is how to place a Toast or some other form of notification in a non Activity Class?

Vector
  • 3,066
  • 5
  • 27
  • 54
  • Are you sure the error comes from the Toast? I use Toast in my DBHelper without any problems. – leonardkraemer Oct 02 '18 at 18:49
  • @leonardkraemer Well I comment the Toast out and it seemed to work Will do more testing If you like edit your ans with a snipit of your code – Vector Oct 02 '18 at 19:58
  • @leonardkraemer Just tested with Toast commented out and No ERRORS – Vector Oct 02 '18 at 20:07
  • Can you provide a [MVCE](https://stackoverflow.com/help/mcve) please? I can not reproduce the issue. – leonardkraemer Oct 02 '18 at 20:47
  • @leonardkraemer Here is most of the code that is involved in the ERROR I did not include the ModelParent been testing code and it works great WITHOUT the Toast We appreciate you dedication to this question sooner or later someone will want to place a Toast or Snackbar in a non Activity Class Once we found the Error we almost did not ask the question – Vector Oct 02 '18 at 21:20
  • your `context` is `null`. you initialize it here: `var context: Context? = null`. Delete that line and use my solution. Please read https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it – leonardkraemer Oct 02 '18 at 21:24
  • @leonardkraemer My bad I missed the construct private val context:Context the first time I looked at your answer got too focused on the Toast Thanks for the advice and persistence Now that I have two tables with fk how to show the data in a recyclerview or do a UNION and create a new Table THANKS – Vector Oct 02 '18 at 21:37

3 Answers3

5

I suspect that this.context might be the culprit. You can not access the context of the SQLiteOpenHelper this way. You can use the following code:

//declare the context as private val property in the constructor see https://kotlinlang.org/docs/reference/classes.html
class DBHelper(private val context: Context):SQLiteOpenHelper(context,DBHelper.DB_NAME,null,DBHelper.DB_VERSION) {

    override fun onCreate(db: SQLiteDatabase?) {

    val CREATE_TABLE_DEPT = "CREATE TABLE ${DEPT_TABLE} ($colidD INTEGER PRIMARY KEY, $colDept TEXT);"
    val CREATE_TABLE_ITEM = "CREATE TABLE ${ITEM_TABLE} ($colidI INTEGER PRIMARY KEY, $colItem TEXT);"
    db!!.execSQL(CREATE_TABLE_DEPT)
    db.execSQL(CREATE_TABLE_ITEM)

    //use the context from the constructor
    Toast.makeText(context, " database is created", Toast.LENGTH_LONG).show()
}

It creates a new reference to context via the val codeword in the constructor which is in turn used for the Toast

leonardkraemer
  • 6,573
  • 1
  • 31
  • 54
  • We tried that and same ERROR I like James_Duh answer but the code is messy this way but YES it works – Vector Oct 02 '18 at 19:15
0

This is why they invented inner class this code shows the nasty old Toast

inner class DatabaseHelper : SQLiteOpenHelper {

    var context: Context? = null

    constructor(context: Context) : super(context, dbName, null, dbVersion) {
        this.context = context
    }

    override fun onCreate(db: SQLiteDatabase?) {
        db!!.execSQL(CREATE_TABLE_SQL)
        Toast.makeText(this.context, " database is created", Toast.LENGTH_LONG).show()
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        db!!.execSQL("Drop table IF EXISTS " + dbTable)
    }
}
James_Duh
  • 1,321
  • 11
  • 31
  • 2
    This won't fix the issue, since the OP's toast isn't the problem. Also, if you have an inner class, there's no reason to have a `context` global variable. – TheWanderer Oct 02 '18 at 18:51
  • @TheWanderer With out the global variable context the other fun will not work I am not crazy about the construct you need to use for this code but it does work Suggest you test this code before you down vote just a suggestion – Vector Oct 02 '18 at 19:20
  • @Grendel the whole point of an inner class is that you can access the references of the parent class. A global `context` variable isn't needed because you can simply do `this@WhateverYourActivityClassIs`. – TheWanderer Oct 02 '18 at 19:22
  • @TheWanderer We rewrote the app to include the inner class construct and tried to use this@DBHandler lots and lots of RED We have not used inner class enough to have a good grasp of how and when to use it but in this case it makes for messy code even though it works your thoughts are appreciated – Vector Oct 02 '18 at 19:35
0

If you are using Toast in Kotlin and insdie RecyclerView.Adapter, you have to call Toast like this and use instance prefix:

   class MyViewHolder (view : View) : RecyclerView.ViewHolder(view) { 

      init {
          view.setOnClickListener {
              Toast.makeText(view.context, "Your Text", Toast.LENGTH_SHORT).show()
          }
      }
}

where view is instance of View class, and context is parameter of Adapter class which implements RecyclerView

Shaahin
  • 1,195
  • 3
  • 14
  • 22