This is my first time posting. I have made a simple counting app. I can connect and add data to the database, but i just cannot figure out how to view the data in the COLUMN_COUNT field. I have created a retrieveCount function, but cannot figure out how to call it on the HistoryAdapter page without making errors.
I have pasted the 4 files with code below.
MainActivity.kt
package com.example.thesupremecounter
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import java.text.SimpleDateFormat
import java.util.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val addButton = findViewById<LinearLayout>(R.id.addButton)
val reduceButton = findViewById<LinearLayout>(R.id.reduceButton)
val resetButton = findViewById<LinearLayout>(R.id.resetButton)
val myTextView = findViewById<TextView>(R.id.textView)
val saveButton = findViewById<Button>(R.id.saveButton)
val historyButton = findViewById<Button>(R.id.historyButton)
var timeClicked = 0
addButton.setOnClickListener {
timeClicked += 1
myTextView.text = timeClicked.toString()
// Toast.makeText(this#MainActivity, "You clicked me.", Toast.LENGTH_SHORT).show()
}
reduceButton.setOnClickListener {
timeClicked -= 1
if (timeClicked < 0) {
timeClicked = 0
} else {
myTextView.text = timeClicked.toString()
// Toast.makeText(this#MainActivity, "You clicked me.", Toast.LENGTH_SHORT).show()
}
}
resetButton.setOnClickListener {
timeClicked = 0
myTextView.text = timeClicked.toString()
// Toast.makeText(this#MainActivity, "You clicked me.", Toast.LENGTH_SHORT).show()
}
historyButton.setOnClickListener {
val intent = Intent(this, HistoryActivity::class.java)
startActivity(intent)
}
saveButton.setOnClickListener {
val count = timeClicked.toString()
val c = Calendar.getInstance() // Calender Current Instance
val dateTime = c.time // Current Date and Time of the system.
val sdf = SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.getDefault()) // Date Formatter
val date = sdf.format(dateTime) // dateTime is formatted in the given format.
val dbHandler = SqliteOpenHelper(this, null)
dbHandler.addDate(count, date) // Add date function is called.
Toast.makeText(this#MainActivity, count + date, Toast.LENGTH_SHORT).show()
}
}
}
HistoryActivity.kt
package com.example.thesupremecounter
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
class HistoryActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_history)
val toolbarPageTwo = findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbar_history_activity)
setSupportActionBar(toolbarPageTwo)
supportActionBar?.setDisplayHomeAsUpEnabled(true) //set back button
supportActionBar?.title = "HISTORY" // Setting an title in the action bar.
toolbarPageTwo.setNavigationOnClickListener {
onBackPressed()
}
getAllCompletedDates()
}
private fun getAllCompletedDates() {
val dbHandler = SqliteOpenHelper(this, null)
val allCompletedDatesList = dbHandler.getAllCompletedDatesList()
val tvHistory = findViewById<TextView>(R.id.tvHistory)
val rvHistory = findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.rvHistory)
val tvNoDataAvailable = findViewById<TextView>(R.id.tvNoDataAvailable)
if (allCompletedDatesList.size > 0) {
tvHistory.visibility = View.VISIBLE
rvHistory.visibility = View.VISIBLE
tvNoDataAvailable.visibility = View.GONE
rvHistory.layoutManager = LinearLayoutManager(this)
val historyAdapter = HistoryAdapter(this, allCompletedDatesList)
rvHistory.adapter = historyAdapter
} else {
tvHistory.visibility = View.GONE
rvHistory.visibility = View.GONE
tvNoDataAvailable.visibility = View.VISIBLE
}
}
}
SqliteOpenHelper.kt
package com.example.thesupremecounter
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
class SqliteOpenHelper(
context: Context,
factory: SQLiteDatabase.CursorFactory?
) :
SQLiteOpenHelper(
context, DATABASE_NAME,
factory, DATABASE_VERSION
) {
override fun onCreate(db: SQLiteDatabase) {
val CREATE_HISTORY_TABLE = ("CREATE TABLE " + TABLE_HISTORY +
"(" + COLUMN_ID + " INTEGER PRIMARY KEY,"
+ COLUMN_COUNT + " TEXT,"
+ COLUMN_COMPLETED_DATE + " TEXT" + ")")
db.execSQL(CREATE_HISTORY_TABLE)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY) // It drops the existing history table
onCreate(db) // Calls the onCreate function so all the updated table will be created.
}
fun addDate(count: String, date: String ) {
val values = "INSERT INTO history " +
"( 'count', 'completed_date')" +
" VALUES " +
"( '$count', '$date')"
val db = this.writableDatabase
db.execSQL(values)
db.close()
}
fun retrieveCount(): ArrayList<String> {
val countList = ArrayList<String>() // ArrayList is initialized
val db = this.readableDatabase
val cursor = db.rawQuery("SELECT * FROM $TABLE_HISTORY", null)
while (cursor.moveToNext())
{countList.add(cursor.getString(cursor.getColumnIndex(COLUMN_COUNT)))
}
cursor.close()
return countList
}
fun getAllCompletedDatesList(): ArrayList<String> {
val list = ArrayList<String>() // ArrayList is initialized
val db = this.readableDatabase
val cursor = db.rawQuery("SELECT * FROM $TABLE_HISTORY", null)
while (cursor.moveToNext())
{list.add(cursor.getString(cursor.getColumnIndex(COLUMN_COMPLETED_DATE)))
}
cursor.close()
return list
}
companion object {
const val DATABASE_VERSION = 1 // This DATABASE Version
const val DATABASE_NAME = "TheCountDatabase9.db" // Name of the DATABASE
const val TABLE_HISTORY = "history" // Table Name
const val COLUMN_ID = "_id" // Column Id
const val COLUMN_COUNT = "count" // Count
const val COLUMN_COMPLETED_DATE = "completed_date" // Column for Completed Date
}
}
HistoryAdapter.kt
package com.example.thesupremecounter
import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_history_row.view.*
class HistoryAdapter(val context: Context, val items: ArrayList<String>) :
RecyclerView.Adapter<HistoryAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(context).inflate(
R.layout.item_history_row,
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val date: String = items[position]
holder.tvPosition.text = (position + 1).toString()
holder.tvCount.text = "unsure what code to write here to display count values from COLUMN_COUNT + also not sure how to call the retrieve count function"
holder.tvItem.text = date
if (position % 2 == 0) {
holder.llHistoryItemMain.setBackgroundColor(
Color.parseColor("#EBEBEB")
)
} else {
holder.llHistoryItemMain.setBackgroundColor(
Color.parseColor("#FFFFFF")
)
}
}
override fun getItemCount(): Int {
return items.size
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val llHistoryItemMain = view.ll_history_item_main!!
val tvItem = view.tvItem!!
val tvPosition = view.tvPosition!!
val tvCount = view.tvCount!!
}
}
Any help will be greatly appreciated.
Thanks
A recyclerview is designed to display the contents of the list that is passed to it (items in your case).
so :-
holder.tvCount.text = "unsure what code to write here to display count values from COLUMN_COUNT + also not sure how to call the retrieve count function"
Should be the count in items, however items is just the date. You need items to be a little more complex i.e. a list of more than just the one String.
So create a class such as HistoryRow with members for each of the components e.g.
data class HistoryRow(
val id: String,
val count: String,
val date: String
)
in the working example below I just added this as a sub class of SqliteOpenHelper
The instead create a List in your getAllCompletedDatesList() you create a List and pass this to your Adapter.
Then in the onBindViewHolder method/function you then assign the values from the respective elements.
The following is a working example based upon the above.
First SqliteOpenHelper :-
class SqliteOpenHelper(
context: Context,
factory: SQLiteDatabase.CursorFactory?
) :
SQLiteOpenHelper(
context, DATABASE_NAME,
factory, DATABASE_VERSION
) {
override fun onCreate(db: SQLiteDatabase) {
val CREATE_HISTORY_TABLE = ("CREATE TABLE " + TABLE_HISTORY +
"(" + COLUMN_ID + " INTEGER PRIMARY KEY,"
+ COLUMN_COUNT + " TEXT,"
+ COLUMN_COMPLETED_DATE + " TEXT" + ")")
db.execSQL(CREATE_HISTORY_TABLE)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY) // It drops the existing history table
onCreate(db) // Calls the onCreate function so all the updated table will be created.
}
fun addDate(count: String, date: String ) {
val values = "INSERT INTO history " +
"( 'count', 'completed_date')" +
" VALUES " +
"( '$count', '$date')"
val db = this.writableDatabase
db.execSQL(values)
db.close()
}
fun retrieveCount(): ArrayList<String> {
val countList = ArrayList<String>() // ArrayList is initialized
val db = this.readableDatabase
val cursor = db.rawQuery("SELECT * FROM $TABLE_HISTORY", null)
while (cursor.moveToNext())
{countList.add(cursor.getString(cursor.getColumnIndex(COLUMN_COUNT)))
}
cursor.close()
return countList
}
fun getAllCompletedDatesList(): ArrayList<HistoryRow> {
val list = ArrayList<HistoryRow>() // ArrayList is initialized
val db = this.readableDatabase
val cursor = db.rawQuery("SELECT * FROM $TABLE_HISTORY", null)
while (cursor.moveToNext())
list.add(
HistoryRow(
cursor.getString(cursor.getColumnIndex(COLUMN_ID)),
cursor.getString(cursor.getColumnIndex(COLUMN_COUNT)),
cursor.getString(cursor.getColumnIndex(COLUMN_COMPLETED_DATE))
)
)
cursor.close()
return list
}
companion object {
const val DATABASE_VERSION = 1 // This DATABASE Version
const val DATABASE_NAME = "TheCountDatabase9.db" // Name of the DATABASE
const val TABLE_HISTORY = "history" // Table Name
const val COLUMN_ID = "_id" // Column Id
const val COLUMN_COUNT = "count" // Count
const val COLUMN_COMPLETED_DATE = "completed_date" // Column for Completed Date
}
data class HistoryRow(
val id: String,
val count: String,
val date: String
)
}
HistoryRow class added (could be elsewhere but added here for brevity)
Note the changes to the getAllCompletedDatesList() method/function to build and return an ArrayList<HistoryRow> instead of the ArrayList<String>.
The other changes required are for the HistoryAdapter which was changed to :-
class HistoryAdapter(val context: Context, val items: ArrayList<SqliteOpenHelper.HistoryRow>) :
RecyclerView.Adapter<HistoryAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(context).inflate(
R.layout.item_history_row,
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tvPosition.setText((position + 1).toString())
holder.tvCount.setText(items[position].count)
holder.tvItem.setText(items[position].date)
if (position % 2 == 0) {
holder.llHistoryItemMain.setBackgroundColor(
Color.parseColor("#EBEBEB")
)
} else {
holder.llHistoryItemMain.setBackgroundColor(
Color.parseColor("#FFFFFF")
)
}
}
override fun getItemCount(): Int {
return items.size
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val llHistoryItemMain = view.findViewById<LinearLayout>(R.id.ll_history_item_main)
val tvItem = view.findViewById<TextView>(R.id.tvItem)
val tvPosition = view.findViewById<TextView>(R.id.tvPosition)
val tvCount = view.findViewById<TextView>(R.id.tvCount)
}
}
items changed from ArrayList to ArrayList
onBindViewHolder assigns values from the elements of items
I'm not sure why I had to change the ViewHolder to use view.FindViewById but I had to.
As I had to create the layouts I have commented out setting the toolbar so as not to have to go to the pains of creating appropriate layouts. Obviously the layouts may differ. However when run the following results were achieved:-
When first run :-
When History is clicked (no data as yet, as expected) :-
Going back to Main and clicking SAVE 3 times and then to History :-
i.e. the 3 rows are now displayed with the respective data (albeit the same data)
Related
i want to update the value of my textview that is in the PosActivity when the button in MyAdapter is click ( to increase/decrease the quantity and to delete the card from recycler view). and i can't find anything on how to do it.
here is what i tried.
in MyAdapter :
package com.mycodlabs.pos.ui.sale.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.mycodlabs.pos.R
import com.mycodlabs.pos.domain.inventory.ProductModel
import kotlinx.android.synthetic.main.pos_item_card.view.*
import java.math.BigDecimal
class MyAdapter(mUx: Context, var selectedItems: ArrayList<ProductModel>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
val mUx = mUx
// var quantity = 1
var totalPrice = BigDecimal.ZERO
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var pclose = itemView.removeItempos
var pname = itemView.pos_name
var pprice = itemView.pos_price
var pqty = itemView.pos_qty
var pminus = itemView.cart_minus_img
var pplus = itemView.cart_plus_img
// val pimage = itemView.pos_image
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.pos_item_card, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val pos: ProductModel = selectedItems[position]
holder.pname.text = pos.name
holder.pprice.text = pos.unitPrice.toString()
holder.pqty.text = pos.quantity.toString()
// holder.pimage.= pos.image
holder.pclose.setOnClickListener {
val username = pos.name
var price = pos.unitPrice
totalPrice -= price
selectedItems.removeAt(position)
notifyDataSetChanged()
notifyItemRangeChanged(position, selectedItems.size)
// Toast.makeText(mUx, "User $username Deleted", Toast.LENGTH_SHORT).show()
}
holder.pminus.setOnClickListener {
if(pos.quantity == 1 ){
// Toast.makeText(mUx,"Can't go any lower", Toast.LENGTH_SHORT).show()
}else {
pos.quantity -= 1
notifyItemChanged(position)
}
}
holder.pplus.setOnClickListener {
pos.quantity += 1
notifyItemChanged(position)
}
}
fun grandTotal(items: ArrayList<ProductModel>): BigDecimal {
totalPrice = BigDecimal.ZERO
for (i in items.indices) {
totalPrice += items[i].unitPrice.multiply(items[i].quantity.toBigDecimal())
}
return totalPrice
}
fun clearData() {
selectedItems.clear()
notifyDataSetChanged()
}
override fun getItemCount() = selectedItems.size
}
and in the Activity:
package com.mycodlabs.pos.ui
import android.os.Bundle
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.mycodlabs.pos.R
import com.mycodlabs.pos.db.AndroidDatabase
import com.mycodlabs.pos.db.DatabaseTables
import com.mycodlabs.pos.db.inventory.InventoryDbo
import com.mycodlabs.pos.db.sale.SalesLinesDao
import com.mycodlabs.pos.domain.inventory.ProductModel
import com.mycodlabs.pos.ui.sale.adapter.MyAdapter
import kotlinx.android.synthetic.main.activity_pos.*
import kotlinx.android.synthetic.main.adapter_available_promotions.*
import kotlinx.android.synthetic.main.dialog_paymentsuccession.view.*
import kotlinx.android.synthetic.main.dialog_saleedit.*
import kotlinx.android.synthetic.main.layout_addcategory.*
import kotlinx.android.synthetic.main.layout_sale.*
import kotlinx.android.synthetic.main.listview_stock.*
import kotlinx.android.synthetic.main.pos_bottom_sheet.*
import kotlinx.android.synthetic.main.pos_item_card.*
class PosActivity : AppCompatActivity() {
private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
private lateinit var productNames: ArrayList<String>
private lateinit var recyclerView: RecyclerView
private lateinit var db: SalesLinesDao
//// Create an empty list to store the selected items
var selectedItems = ArrayList<ProductModel>()
//// Create an adapter for the RecyclerView
val adapter = MyAdapter(this,selectedItems)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pos)
// for Back button
back.setOnClickListener {
finish()
}
check_out_Pos.setOnClickListener{
// val pos = ProductModel()
//
// db = SalesLinesDbo(this)
//
// pos.name = pos_name.text.toString()
//
// db.addLineItem(pos.id, LineItemModel())
adapter.clearData()
}
// val autoCompleteTextView = findViewById<AutoCompleteTextView>(R.id.searchBoxPos)
// autoCompleteTextView.threshold = 0
// val suggestions =InventoryDbo.getInstance(applicationContext).allProduct
// var NameArray=suggestions.toList().filter { t -> t.name.contains(autoCompleteTextView)}.toList().map { m->m.name }
// val arrayAdapter = ArrayAdapter(this, android.R.layout.simple_expandable_list_item_2 ,NameArray)
// autoCompleteTextView.setAdapter(arrayAdapter)
// Auto Complete Textview filtering from Product table colm "productName"
val autoCompleteTextView = findViewById<AutoCompleteTextView>(R.id.searchBoxPos)
val productNames = ArrayList<String>()
val dbHelper = AndroidDatabase(this)
val db = dbHelper.readableDatabase
val cursor = db.rawQuery(
"SELECT DISTINCT ${InventoryDbo.colm_productName} FROM ${DatabaseTables.TABLE_PRODUCT} WHERE ${InventoryDbo.colm_productName} like '%%'",
null
)
if (cursor.moveToFirst()) {
do {
productNames.add(cursor.getString(cursor.getColumnIndex(InventoryDbo.colm_productName)))
} while (cursor.moveToNext())
}
cursor.close()
db.close()
val adapterr = ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line, productNames)
autoCompleteTextView.setAdapter(adapterr)
// //// Auto Complete suggestion item display in recyclerview on select
// // Create the RecyclerView
val recyclerView = findViewById<RecyclerView>(R.id.sale_List_Pos)
//// Create a layout manager for the RecyclerView
val layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = adapter
// Set an item click listener for the AutoCompleteTextView
searchBoxPos.setOnItemClickListener { _, _, position, _ ->
// Get the selected product name and product from the list
val selectedItem = autoCompleteTextView.adapter.getItem(position).toString()
// val selectedProductName = productNames[position]
val selectedProduct = InventoryDbo.getInstance(applicationContext).getPosProductByName(selectedItem).first()
//InventoryDbo.getProductByName(selectedProductName)
// Add the selected product to the selected items list
selectedItems.add(selectedProduct)
// Notify the adapter that the data has changed
adapter.notifyDataSetChanged()
// Clear the focus and text from the AutoCompleteTextView
searchBoxPos.clearFocus()
searchBoxPos.setText("")
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
//for the bottomsheet
bottomSheetBehavior = BottomSheetBehavior.from<LinearLayout>(std_btm_sht)
bottomSheetBehavior.setBottomSheetCallback(object :
BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, state: Int) {
print(state)
when (state) {
BottomSheetBehavior.STATE_HIDDEN -> {
}
BottomSheetBehavior.STATE_EXPANDED -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
BottomSheetBehavior.STATE_COLLAPSED -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
BottomSheetBehavior.STATE_DRAGGING -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
BottomSheetBehavior.STATE_SETTLING -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
BottomSheetBehavior.STATE_HALF_EXPANDED -> {
total_items_Pos.text = adapter.grandTotal(selectedItems).toString()
}
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
})
}
}
i hope you can help me solve this because it was bugging me all day and i couldn't find anything about it
I'll post the changes for the plus button, you can then repeat it for the others. Mind that this is just an example, as I don't know in what way you'd like to update the text or what's the actual name of your TextView.
class MyAdapter(
mUx: Context,
var selectedItems: ArrayList<ProductModel>,
val plusLambda: (String) -> Unit // <------
) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val pos: ProductModel = selectedItems[position]
...
holder.pplus.setOnClickListener {
pos.quantity += 1
plusLambda("The new quantity is: ${ pos.quantity }") // <------
notifyItemChanged(position)
}
...
class PosActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pos)
...
val adapterr = ArrayAdapter(this,
android.R.layout.simple_dropdown_item_1line,
productNames) {
someTextView.text = it // <------
}
autoCompleteTextView.setAdapter(adapterr)
...
Of course, you can use just one lambda for all buttons if all you want to do is to change the text of the TextView.
I need help with this project, i use BLE and connect it to smartphone and i can get the RSSI value by "ScanResult" in the ScannerFragment but its a static value in the recyclerview, i dont know what to do to make the RSSI value change in real time as i move my phone away/ closer to the BLE device, so it would jump for example from -50dBm to -60dBm as i move the smartphone away from the BLE device
ScannerFragment
import android.Manifest
import android.app.AlertDialog
import android.bluetooth.*
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.support.annotation.RequiresApi
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import java.util.*
import kotlin.collections.HashSet
class ScannerFragment : Fragment() {
private lateinit var startButton: Button
private lateinit var stopButton: Button
private lateinit var multilateration: TextView
private lateinit var recyclerView: RecyclerView
private lateinit var linearLayoutManager: LinearLayoutManager
private var btManager: BluetoothManager? = null
private var btAdapter: BluetoothAdapter? = null
private var btScanner: BluetoothLeScanner? = null
var beaconSet: HashSet<Beacon> = HashSet()
var beaconAdapter: BeaconsAdapter? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_scanner, container, false)
initViews(view)
setUpBluetoothManager()
return view
}
companion object {
private const val REQUEST_ENABLE_BT = 1
private const val PERMISSION_REQUEST_COARSE_LOCATION = 1
}
private fun initViews(view: View) {
startButton = view.findViewById(R.id.startButton)
stopButton = view.findViewById(R.id.stopButton)
recyclerView = view.findViewById(R.id.recyclerView)
startButton.setOnClickListener { onStartScannerButtonClick() }
stopButton.setOnClickListener { onStopScannerButtonClick() }
linearLayoutManager = LinearLayoutManager(context)
recyclerView.layoutManager = linearLayoutManager
beaconAdapter = BeaconsAdapter(beaconSet.toList())
recyclerView.adapter = beaconAdapter
multilateration = view.findViewById(R.id.multilateration)
}
private fun onStartScannerButtonClick() {
startButton.setBackgroundColor(Color.GREEN)
startButton.visibility = View.GONE
stopButton.visibility = View.VISIBLE
beaconSet.clear()
btScanner?.startScan(leScanCallback)
}
private fun onStopScannerButtonClick() {
stopButton.setBackgroundColor(Color.RED)
stopButton.visibility = View.GONE
startButton.visibility = View.VISIBLE
//beaconAdapter?.beaconList?.clear()
beaconSet.clear()
btScanner?.stopScan(leScanCallback)
}
private fun setUpBluetoothManager() {
btManager = activity?.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
btAdapter = btManager!!.adapter
btScanner = btAdapter?.bluetoothLeScanner
if (btAdapter != null && !btAdapter!!.isEnabled) {
val enableIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableIntent, REQUEST_ENABLE_BT)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkForLocationPermission()
}
}
#RequiresApi(Build.VERSION_CODES.M)
private fun checkForLocationPermission() {
// Make sure we have access coarse location enabled, if not, prompt the user to enable it
if (requireActivity().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
val builder = AlertDialog.Builder(activity)
builder.setTitle("This app needs location access")
builder.setMessage("Please grant location access so this app can detect the BLE.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener {
requestPermissions(
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
PERMISSION_REQUEST_COARSE_LOCATION
)
}
builder.show()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>, grantResults: IntArray
) {
when (requestCode) {
PERMISSION_REQUEST_COARSE_LOCATION -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
println("coarse location permission granted")
} else {
val builder = AlertDialog.Builder(activity)
builder.setTitle("Functionality limited")
builder.setMessage("Since location access has not been granted, this app will not be able to discover BLE beacons")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener { }
builder.show()
}
return
}
}
}
private val leScanCallback: ScanCallback = object : ScanCallback() {
#RequiresApi(Build.VERSION_CODES.O)
override fun onScanResult(callbackType: Int, result: ScanResult) {
val scanRecord = result.scanRecord
val beacon = Beacon(result.rssi)
beacon.manufacturer = result.device.name
beacon.macAddress = result.device.address
beacon.rssi = result.rssi
if (scanRecord != null) {
val iBeaconManufactureData = scanRecord.getManufacturerSpecificData(0X004c)
if (iBeaconManufactureData != null && iBeaconManufactureData.size >= 23) {
val iBeaconUUID = Utility.toHexString(iBeaconManufactureData.copyOfRange(2, 18))
val major = Integer.parseInt(
Utility.toHexString(
iBeaconManufactureData.copyOfRange(
18,
20
)
), 16
)
val minor = Integer.parseInt(
Utility.toHexString(
iBeaconManufactureData.copyOfRange(
20,
22
)
), 16
)
beacon.type = Beacon.BeaconType.iBeacon
beacon.name = beacon.manufacturer
beacon.uuid = iBeaconUUID
beacon.major = major
beacon.txPower = result.txPower
beacon.minor = minor
Log.e("BeaconID", "iBeaconUUID:$iBeaconUUID major:$major minor:$minor")
}
if(beacon.name == "Beacon_1" || beacon.name == "Beacon_2" || beacon.name == "Beacon_3" || beacon.name == "Beacon_4" ) {
beaconSet.add(beacon)
}
(recyclerView.adapter as BeaconsAdapter).updateData(beaconSet.toList())
}
}
override fun onScanFailed(errorCode: Int) {
Log.e("Failed", errorCode.toString())
}
}
}
BeaconsAdapter
import android.os.Build
import android.support.annotation.RequiresApi
import android.support.v4.content.ContextCompat
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import java.util.*
class BeaconsAdapter(beacons: List<Beacon>) :
RecyclerView.Adapter<BeaconsAdapter.BeaconHolder>() {
var beaconList: MutableList<Beacon> = beacons.toMutableList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BeaconHolder {
val inflater = LayoutInflater.from(parent.context)
return BeaconHolder(inflater, parent)
}
override fun onBindViewHolder(holder: BeaconHolder, pos: Int) {
val beacon: Beacon = beaconList[pos]
holder.bind(beacon)
}
override fun getItemCount() = beaconList.size
#RequiresApi(Build.VERSION_CODES.N)
fun updateData(data: List<Beacon>) {
beaconList.clear()
beaconList.addAll(data)
notifyDataSetChanged()
}
class BeaconHolder(inflater: LayoutInflater, parent: ViewGroup) :
RecyclerView.ViewHolder(inflater.inflate(R.layout.scan_result_items, parent, false)) {
private var image: ImageView? = null
private var mac: TextView? = null
private var name: TextView? = null
private var instanceMajorMinor: TextView? = null
private var namespaceUUID: TextView? = null
private var beaconRSSI: TextView? = null
private val context = parent.context
init {
image = itemView.findViewById(R.id.beacon_image)
mac = itemView.findViewById(R.id.beacon_mac)
name = itemView.findViewById(R.id.beacon_name)
instanceMajorMinor = itemView.findViewById(R.id.beacon_instance_major_minor)
beaconRSSI = itemView.findViewById(R.id.beacon_rssi)
}
fun bind(beacon: Beacon) {
mac?.text = String.format(
context.getString(R.string.mac),
beacon.macAddress
)
beaconRSSI?.text = String.format(
context.getString(R.string.rssi),
beacon.rssi
)
if (beacon.type == Beacon.BeaconType.iBeacon) {
namespaceUUID?.text = String.format(context.getString(R.string.uuid), beacon.uuid)
name?.text = String.format(context.getString(R.string.name), beacon.name)
//namespace
instanceMajorMinor?.text = String.format(
context.getString(R.string.major_minor),
beacon.major,
beacon.minor
)
image?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ibeacon))
instanceMajorMinor?.visibility = View.VISIBLE
namespaceUUID?.visibility = View.VISIBLE
}
}
}
}
Beacon
class Beacon(rssi: Int?) {
enum class BeaconType {
iBeacon
}
var macAddress : String? = null
var manufacturer: String? = null
var type: BeaconType = BeaconType.iBeacon
var uuid: String? = null
var name: String? = null
var major: Int? = null
var minor: Int? = null
var txPower: Int? = null
var rssi = rssi
override fun equals(other: Any?): Boolean{
if(this === other) return true
if(other !is Beacon) return false
if(macAddress != other.macAddress) return false
return true
}
override fun hashCode(): Int {
return macAddress?.hashCode() ?:0
}
}
I get null on object reference. I have a default value set(not sure if this has an effect). My DataState is in a Arraylist which is shown in a Recyclerview. Datastate is changed when a button is clicked and reflect on a textview.
My datestate class:
enum class DataState {
Unselected,
Success,
Failure
}
My Arraylist:
class Tripsheetlist (var videos: ArrayList<DataModel>)
class DataModel(
var Tripsheet: Int,
var WOrder: Int,
var DElNote: Int,
var name: String,
var Weight: Int,
var tvdone: String,
var Drivers: String,
var state: DataState = DataState.Unselected
)
My Adapter:
if ( rowPos != 0){
val modal = tripsheetlist.videos[rowPos -1]
holder.txttvdone.apply {
setBackgroundResource(when (modal.state) {
DataState.Unselected -> android.R.color.transparent
DataState.Success -> R.color.green
DataState.Failure -> R.color.orange
})
text = when (modal.state) {
DataState.Unselected -> ""
DataState.Success -> "✓"
DataState.Failure -> "x"
//this is where I add code to export data through api maybe add it in the datastate set where it is success and Failure
}
}
holder.apply {
txtbutton1.setOnClickListener {
Log.e("Clicked", "Successful delivery")
//this is where I add code to export data through api
modal.state = DataState.Success
notifyDataSetChanged()
}
txtbutton2.setOnClickListener {
Log.e("Clicked", "Exception on delivery")
modal.state = DataState.Failure
notifyDataSetChanged()
}
}}
I get the error on the line setBackgroundResource(when (modal.state) {
Error :
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.dispatch.tripsheet, PID: 12935
java.lang.NullPointerException: Attempt to invoke virtual method 'int com.dispatch.tripsheet.DataState.ordinal()' on a null object reference
at com.dispatch.tripsheet.TableViewAdapter.onBindViewHolder(TableViewAdapter.kt:162)
at com.dispatch.tripsheet.TableViewAdapter.onBindViewHolder(TableViewAdapter.kt:12)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7254)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7337)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6194)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6460)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6300)
Below will be my full adapter and Main class.
Adapter:
package com.dispatch.tripsheet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.table_list_item.view.*
class TableViewAdapter(
var tripsheetlist: Tripsheetlist,
val driver: String,
var tvHeader: TextView,
val spnDriver: Spinner,
var adapter: ArrayAdapter<String>,
) : RecyclerView.Adapter<TableViewAdapter.RowViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RowViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.table_list_item, parent, false)
var i: Int = 0
var totT: Int = 1
var a: Int = 0
var b: Int = 0
var c: Int = 0
var l: Int = tripsheetlist.videos.size -1
while (i <= l){
if (driver == tripsheetlist.videos[i].Drivers) {
if (a == 0){
a = tripsheetlist.videos[i].Tripsheet}
totT = totT
if(a != tripsheetlist.videos[i].Tripsheet) {
if (b == 0){
b = tripsheetlist.videos[i].Tripsheet}
if ( totT != 3){
totT = 2}
if(b != tripsheetlist.videos[i].Tripsheet) {
c = tripsheetlist.videos[i].Tripsheet
totT = 3
}
}
}
i++
}
when (totT)
{
1 -> tvHeader.setText("Vulcan Steel Trip Sheet: " +a)
2-> tvHeader.setText("Vulcan Steel Trip Sheet: " + a + " & " + b )
3-> tvHeader.setText("Vulcan Steel Trip Sheet: " + a + " & " + b + " & " + c)
else -> tvHeader.setText("Vulcan Steel Trip Sheets")
}
var h =0
// val driverList = tripsheetlist?.videos.distinctBy { it.Drivers }
// while (h <= driverList.size){
// adapter.add(driverList[h].Drivers.toString())
// h++
// }
//used for testing until we have driver data
while (h <= 4){
adapter.add("driverList[h].Drivers.toString()"+h)
h++
}
spnDriver.adapter = adapter
return RowViewHolder(itemView) }
private fun updateData(data: ArrayList<DataModel>) {
tripsheetlist.videos = data
notifyDataSetChanged()
}
override fun getItemCount(): Int { return tripsheetlist.videos.size + 1 // one more to add header row
}
override fun onBindViewHolder(holder: RowViewHolder, position: Int) {
val rowPos = holder.adapterPosition
if (rowPos == 0) {
holder.itemView.apply {
setHeaderBg(txtWOrder)
setHeaderBg(txtDElNote)
setHeaderBg(txtCompany)
setHeaderBg(txtWeight)
setHeaderBg(txttvdone)
txtWOrder.text = "WOrder"
txtDElNote.text = "DElNote"
txtCompany.text = "Compan"
txtWeight.text = "Weight"
txttvdone.text = "?"
}
} else {
val modal = tripsheetlist.videos[rowPos -1]
holder.itemView.apply {
setContentBg(txtWOrder)
setContentBg(txtDElNote)
setContentBg(txtCompany)
setContentBg(txtWeight)
txtWOrder.text = modal.WOrder.toString()
txtDElNote.text = modal.DElNote.toString()
txtCompany.text = modal.name //company
txtWeight.text = modal.Weight.toString()
// used when we have driver data
// if (modal.Drivers == driver) {
// txtWOrder.text = modal.WOrder.toString()
// txtDElNote.text = modal.DElNote.toString()
// txtCompany.text = modal.name
// txtWeight.text = modal.Weight.toString()
//
// }
}
}
//used for testing to look in concsole for responeses - all return null
// println(tripsheetlist.videos[rowPos ].name) //name needs to be chagned to company
// println("where we need to see the state")
// println("-1")
// println(tripsheetlist.videos[rowPos ].state)
// println("0")
// println(tripsheetlist.videos[0].state)
// println("+1")
// println(tripsheetlist.videos[1 ].state)
var p = tripsheetlist.videos[1].state
println("state")
println(p)
if ( rowPos != 0){
val modal = tripsheetlist.videos[rowPos -1]
holder.txttvdone.apply {
setBackgroundResource(when (modal.state) {
DataState.Unselected -> android.R.color.transparent
DataState.Success -> R.color.green
DataState.Failure -> R.color.orange
})
text = when (modal.state) {
DataState.Unselected -> ""
DataState.Success -> "✓"
DataState.Failure -> "x"
//this is where I add code to export data through api maybe add it in the datastate set where it is success and Failure
}
}
holder.apply {
txtbutton1.setOnClickListener {
Log.e("Clicked", "Successful delivery")
//this is where I add code to export data through api
modal.state = DataState.Success
notifyDataSetChanged()
}
txtbutton2.setOnClickListener {
Log.e("Clicked", "Exception on delivery")
modal.state = DataState.Failure
notifyDataSetChanged()
}
}}
}
class RowViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val txttvdone:TextView = itemView.findViewById<TextView>(R.id.txttvdone)
val txtbutton1:Button = itemView.findViewById<Button>(R.id.txtbutton1)
val txtbutton2:Button = itemView.findViewById<Button>(R.id.txtbutton2)
}
private fun setHeaderBg(view: View) {
view.setBackgroundResource(R.drawable.table_header_cell_bg)
}
private fun setContentBg(view: View) {
view.setBackgroundResource(R.drawable.table_content_cell_bg)
}
}
Main class:
package com.dispatch.tripsheet
import android.os.Bundle
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.gson.GsonBuilder
import kotlinx.android.synthetic.main.activity_main.*
import okhttp3.*
import java.io.IOException
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerViewTripsheetlist.layoutManager = LinearLayoutManager(this)
val spnDriver: Spinner = findViewById(R.id.spnDriver)
var adapter = ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
fetchJson( adapter, spnDriver)
}
private fun fetchJson(adapter: ArrayAdapter<String>, spnDriver: Spinner){
println("Attempting to Fetch JSON")
val url = "https://api.letsbuildthatapp.com/youtube/home_feed"
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object: Callback {
override fun onFailure(call: Call, e: IOException) {
println("Failed to execute request") }
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
println(body)
val gson = GsonBuilder().create()
val tripsheetlist = gson.fromJson(body, Tripsheetlist::class.java)
//remove the following later
tripsheetlist.videos.apply {
add(DataModel(190617, 182832, 1, "100",20, "Delivery not done",""))
add(DataModel(190616, 182833, 2, "100",10, "Exceptions",""))
add(DataModel(190616, 182832, 3, "100",100, "50%",""))
}
weightsum(tvTotalweight, tripsheetlist)
totaldelNotes(tvTotaldelv,tripsheetlist)
var driver : String = Spinner(tripsheetlist, adapter)
runOnUiThread {
recyclerViewTripsheetlist.adapter = TableViewAdapter(tripsheetlist, driver, tvHeader, spnDriver, adapter)
}
}
})
}
private fun Spinner(tripsheetlist: Tripsheetlist, adapter: ArrayAdapter<String>): String {
var driver : String = ""
var item : String = ""
var rowTot : Int = tripsheetlist?.videos.size
spnDriver.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
var rowPos : Int = 0
while (rowPos != rowTot){
//check which one of the following works
// item = tripsheetlist.videos[p2].Drivers.toString()
item = p2.toString()
rowPos = rowTot
}
Toast.makeText(this#MainActivity, "Driver $item selected", Toast.LENGTH_SHORT).show()
driver = item
}
override fun onNothingSelected(p0: AdapterView<*>?) {
driver = ""
//empty
}
}
return driver
}
private fun totaldelNotes(tvTotaldelv: TextView, tripsheetlist: Tripsheetlist) {
var totnotes :Int = tripsheetlist.videos.size
tvTotaldelv.setText("Total Delivery Notes: " +totnotes) }
private fun weightsum(tvTotalweight: TextView, tripsheetlist: Tripsheetlist) {
var totweight: Int = 0
var sum: Int = 0
for (i in 0 until tripsheetlist.videos.size) {
sum += tripsheetlist.videos[i].Weight }
totweight = sum
tvTotalweight.setText("Total Weight: " + totweight + "kg")
}
//not used
fun limitDropDownHeight(spinner: Spinner){
val popup = Spinner::class.java.getDeclaredField( "mPopup")
popup.isAccessible = true
val popupWindow = popup.get(spinner)as ListPopupWindow
popupWindow.height = (200 * resources.displayMetrics.density).toInt()
}
}
This is my RecyclerView Adapter Class. Clicking an item from the list UI should change of the particular item like background black to white. After clicking an item I am changing the status of the item 0 to 1 from the UI class, based on that status UI should change of the item. But inside diffUtil areContentTheSame() retures true.
class TimeSlotsAdapter(val adapterItemOnClick: (TimeSlots) -> Unit):RecyclerView.Adapter<TimeSlotsAdapter.TimeSlotsViewHolder>() {
private lateinit var binding: RvTimeSlotsBinding
private var timeSlots: List<TimeSlots> = ArrayList<TimeSlots>()
inner class TimeSlotsViewHolder : RecyclerView.ViewHolder {
constructor(rvBinding: RvTimeSlotsBinding) : super(rvBinding.root) {
}
fun setItem(timeSlots: TimeSlots) {
binding.timeSlotView.setOnClickListener { adapterItemOnClick(timeSlots) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimeSlotsViewHolder {
binding =
RvTimeSlotsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return TimeSlotsViewHolder(binding)
}
override fun onBindViewHolder(holder: TimeSlotsViewHolder, position: Int) {
timeSlots[position]?.let { currentSlot ->
with(holder) {
if (currentSlot.status == "1") {
binding.timeSlotView.background =
ContextCompat.getDrawable(
binding.timeSlotView.context,
R.drawable.time_slot_item_bg
)
binding.timeSlotTv.setTextColor(
ContextCompat
.getColor(binding.timeSlotTv.context, R.color.black)
)
binding.timeSlotTv.text = "1 " + currentSlot.time
Timber.e("${currentSlot.time} ${currentSlot.status}")
} else {
binding.timeSlotView.background =
ContextCompat.getDrawable(
binding.timeSlotView.context,
R.drawable.black_bg
)
binding.timeSlotTv.setTextColor(
ContextCompat
.getColor(binding.timeSlotTv.context, R.color.white)
)
binding.timeSlotTv.text = "0 " + currentSlot.time
Timber.e("${currentSlot.time} ${currentSlot.status}")
}
setItem(currentSlot)
}
}
}
override fun getItemCount(): Int = timeSlots.size
fun submitSlots(timeSlotList: List<TimeSlots>) {
val oldList = timeSlots
val diffResult: DiffUtil.DiffResult = DiffUtil.calculateDiff(
TimeSlotsDiffCallBack(
oldList,
timeSlotList
)
)
timeSlots = timeSlotList
diffResult.dispatchUpdatesTo(this)
}
class TimeSlotsDiffCallBack(
var oldTimeSlotsList: List<TimeSlots>,
var newTimeSlotsList: List<TimeSlots>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldTimeSlotsList.size
override fun getNewListSize(): Int = newTimeSlotsList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return (oldTimeSlotsList[oldItemPosition].time
== newTimeSlotsList[newItemPosition].time)
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldTimeSlots = oldTimeSlotsList[oldItemPosition].status
val newTimeSlots = newTimeSlotsList[newItemPosition].status
Timber.e("old: $oldTimeSlots new: $newTimeSlots")
return oldTimeSlots == newTimeSlots
}
}
}
This is the UI class for submitting adapter list. Below the doClickTimeSlot() I am updating the status of the items.
class TimeSlotBottomSheetFragment : BottomSheetDialogFragment(){
private val obj: List<TimeSlots> = mutableListOf<TimeSlots>(
TimeSlots("09:30", "0"),
TimeSlots("10:30", "0"),
TimeSlots("11:30", "0"),
TimeSlots("12:30", "0"),
TimeSlots("14:30", "0"),
TimeSlots("15:30", "0"),
TimeSlots("16:30", "0"),
TimeSlots("17:30", "0"),
TimeSlots("18:30", "0"),
TimeSlots("19:30", "0"),
TimeSlots("20:30", "0"),
TimeSlots("21:30", "0")
)
private fun doClickTimeSlot(timeSlots: TimeSlots) {
val newList: ArrayList<TimeSlots> = ArrayList<TimeSlots>()
newList.addAll(obj)
for (i in newList.indices) {
if (newList[i].time == timeSlots.time) {
newList[i].status = "1"
Timber.e("" + newList[i].time + " " + newList[i].status)
} else {
newList[i].status = "0"
Timber.e("" + newList[i].time + " " + newList[i].status)
}
}
adapter.submitSlots(newList)
}
}
I am having difficulties retrieving the information correctly from Firebase Firestore for my Recycler Adapter. I am not sure what I might be doing wrong but I used a Document Reference to get the required field but now it seems to just copy the same thing over and over, I want it to display each created users profile and display it on my RecyclerAdapter but am not sure what I should do and have tried different methods but get a
"No setter/field error" on my Model Class "Users".
This is my Firebase Schema
This is what it is outputting
This is what I have my code as so far
[Update]
This is what I have imported
import Models.User
import android.content.Intent
import android.content.res.Configuration
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.firebase.ui.firestore.FirestoreRecyclerAdapter
import com.firebase.ui.firestore.FirestoreRecyclerOptions
import com.google.android.material.navigation.NavigationView
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.*
import com.squareup.picasso.Picasso
import de.hdodenhof.circleimageview.CircleImageView
import kotlinx.android.synthetic.main.all_nearby_users.*
import kotlinx.android.synthetic.main.toolbar_layout.*
Oncreate
auth = FirebaseAuth.getInstance()
val customUserId = auth.currentUser!!.uid
val db = FirebaseFirestore.getInstance()
val userRef = db.collection("sUsers").document(customUserId)
val userQuery = db.collection("sUsers").orderBy("Full Name", Query.Direction.DESCENDING).limit(10)
//User List Layout
all_users_nearby_list.layoutManager = LinearLayoutManager(this)
//Firestore
val firestoreRecyclerOptions: FirestoreRecyclerOptions<Users> = FirestoreRecyclerOptions.Builder<Users>()
.setQuery(userQuery, Users::class.java)
.build()
adapter = UserFirestoreRecyclerAdapter(firestoreRecyclerOptions)
all_users_nearby_list.adapter = adapter
Firestore Recycler Adapter
private inner class UserFirestoreRecyclerAdapter internal constructor
(firestoreRecyclerOptions: FirestoreRecyclerOptions<Users>): FirestoreRecyclerAdapter<Users, UserViewHolder>(firestoreRecyclerOptions) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val userView = LayoutInflater.from(parent.context)
.inflate(R.layout.display_users_profile, parent, false)
return UserViewHolder(userView)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int, model: Users) {
holder.setFullname(model.fullname)
holder.setProfileimage(model.profileImage)
}
}
UserViewHolder
private inner class UserViewHolder internal constructor (private val pView: View) : RecyclerView.ViewHolder(pView) {
internal fun setFullname(fullname: String) {
val username = pView.findViewById<TextView>(R.id.usernameTextView)
val db = FirebaseFirestore.getInstance()
val docRef = db.collection("sUsers").document(auth.currentUser!!.uid)
docRef.get()
.addOnSuccessListener { document ->
if (document != null) {
Log.d("HomeActivity", "DocumentSnapshot data: ${document.data}")
username.text = document.getString("Full Name")
} else {
Log.d("HomeActivity", "No such document")
}
}
.addOnFailureListener { exception ->
Log.d("HomeActivity", "get failed with ", exception)
}
username.text = fullname
Log.d("HomeActivity", "Current Data: " + fullname)
}
internal fun setProfileimage(profileImage: String) {
val userProfileImage = pView.findViewById<CircleImageView>(R.id.profileUserImage)
Picasso.get().load(profileImage).into(userProfileImage)
}
}
Model Class
package Models
class Users(
var fullname: String= "",
var profileImage: String= "",
var uid: String? = "",
var haircut: Boolean? = null,
var waxing: Boolean? = null,
var nails: Boolean? = null,
var profileRatingBar: Float? = 1.0f
)
My onStart and onStop
override fun onStart() {
super.onStart()
adapter!!.startListening()
}
override fun onStop() {
super.onStop()
if (adapter != null) {
adapter!!.stopListening()
}
}
This is how I would write your RecyclerView. Key points:
Don't make a 2nd FireStore query inside the ViewHolder
Your Firestore schema must exactly match your model
Use lifecycle owner instead of onStart/onStop
Firebase UI doesn't capture the uid; so do this manually (see apply)
ViewHolder must "hold" the views as fields (to avoid calling find every time)
Model represents 1 object, so I name it "User" not "Users"
Set layoutManager in XML to reduce boilerplate in onCreate
Layout XML
<androidx.recyclerview.widget.RecyclerView
...
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="5"
tools:listitem="#layout/display_users_profile"
... />
Activity onCreate
val query = FirebaseFirestore.getInstance()
.collection("sUsers") // Why not "users" ?
.orderBy("fullname", Query.Direction.DESCENDING)
.limit(10)
val options = FirestoreRecyclerOptions.Builder<User>()
.setLifeCycleOwner(this)
.setQuery(query) { it.toObject(User::class.java)!!.apply { uid = it.id } }
.build()
all_users_nearby_list.adapter = UserFirestoreRecyclerAdapter(options)
Adapter
internal class UserFirestoreRecyclerAdapter(options: FirestoreRecyclerOptions<User>) :
FirestoreRecyclerAdapter<User, UserViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
LayoutInflater.from(parent.context)
.inflate(R.layout.display_users_profile, parent, false)
.let { UserViewHolder(it) }
override fun onBindViewHolder(holder: UserViewHolder, position: Int, model: Users) =
holder.bind(model)
}
ViewHolder
internal class UserViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
// Hold view refs
private val usernameTextView: TextView = itemView.userNameTextView
private val profileUserImage: ImageView = itemView.profileUserImage
internal fun bind(model: User) {
model.apply {
usernameTextView.text = fullname
Picasso.get().load(profileImage).into(profileUserImage)
}
}
}
Model
// Set sensible defaults here (or null if no sensible default)
data class User(
var uid: String = "",
var fullname: String= "",
var profileImage: String= "",
var haircut: Boolean = false,
var waxing: Boolean = false,
var nails: Boolean = false,
var profileRatingBar: Float? = null
)