RecylerView Data not showing - android

I am new to Kotlin, I have this Kotlin code, the loaded data is not displayed at first but after navigating to another activity and coming back the data appears.
In online I have checked they said to add notifyOnChnaged but I am unable to implement it, If you know how to add notifyOnChanged or any other solution please help me out.
class SecretsActivity : AppCompatActivity() {
lateinit var bind : ActivitySecretsBinding
override fun onCreate(savedInstanceState: Bundle?) {
val secretsList = firebaseSecretsData()
super.onCreate(savedInstanceState)
bind = ActivitySecretsBinding.inflate(layoutInflater)
setContentView(bind.root)
bind.secretsRecyclerView.adapter = SecretsAdapter(secretsList)
bind.secretsRecyclerView.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
bind.secretsRecyclerView.setHasFixedSize(true)
bind.secretsRecyclerView.adapter = SecretsAdapter(secretsList)
bind.ButtonWrite.setOnClickListener {
val intent = Intent(this, WriteActivity::class.java)
startActivity(intent)
overridePendingTransition(R.transition.fadein, R.transition.fadeout)
finish()
}
}
override fun onBackPressed() {
super.onBackPressed()
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
overridePendingTransition(R.transition.fadein, R.transition.fadeout)
finish()
}
private fun firebaseSecretsData() : List<SecretsModel>{
val list = ArrayList<SecretsModel>()
val dbref = FirebaseDatabase.getInstance().getReference("Secrets")
dbref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot.exists()){
for(i in snapshot.children){
val item = SecretsModel(i.value.toString())
list+=item
}
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
return list
}
}

//Initialize this first
val myAdapter = SecretsAdapter(secretsList)
.
.
.
.
.
private fun firebaseSecretsData() : List<SecretsModel>{
val list = ArrayList<SecretsModel>()
val dbref =
FirebaseDatabase.getInstance().getReference("Secrets")
dbref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot.exists()){
for(i in snapshot.children){
val item = SecretsModel(i.value.toString())
list+=item
}
//Add this code
myAdapter.notifyDataSetChanged()
}
}
.
.
.
.
.
Moreover, you should use DiffUtil if possible for performance reasons.

Related

How to read asynchronous data from real-time database using android Kotlin?

Here is my code to read asynchronous data from a real-time database using android Kotlin:
class suDetails : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_su_details)
su_image.setOnClickListener {
readData(object : MyCallback {
override fun onCallback(imageUrl: String?) {
if (imageUrl != null) {
val imageViewer = Intent(baseContext, suDetails::class.java)
imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
imageViewer.putExtra("su_image", imageUrl)
startActivity(imageViewer)
}
}
})
}
}
fun readData(myCallback: MyCallback) {
val su_resource =intent
val su_res = su_resource.getStringExtra("su_userid")
val suRef = FirebaseDatabase.getInstance().getReference().child("Users").child(su_res!!)
suRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if(dataSnapshot.exists()){
su_layout.visibility = View.VISIBLE
val userData = dataSnapshot.getValue(profile_model::class.java)
val imageUrl = userData!!.getImageUrl()
Picasso.get().load(imageUrl).placeholder(R.drawable.ic_baseline_image_200).into(su_image)
su_name.text = userData.getnameOfsu()
Toast.makeText(baseContext, imageUrl, Toast.LENGTH_LONG).show()
myCallback.onCallback(imageUrl)
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
interface MyCallback {
fun onCallback(value: String?)
}
}
I have referred to other questions to read asynchronous data from a real-time database but when I tried the solution I am not able to show any data in my ImageView and textView. I am getting only the blank screen.
The New code after the answer of Tyler V:
class suDetails : AppCompatActivity() {
private var currentImageUrl: String = ""
private var su_res: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_su_details)
su_res = intent.getStringExtra("su_userid").toString()
// get views
val su_name = findViewById<TextView>(R.id.su_name)
val su_image = findViewById<ImageView>(R.id.su_image)
// onClick launches another activity - if the image
// hasn't loaded yet nothing happens
su_image.setOnClickListener { viewCurrentImage() }
// start the async loading right away - once it is loaded the
// su_layout view will be visible and the view data
// will be populated. It might be good to show a progress bar
// while it's loading
readData()
}
fun readData() {
println("LOG: called readData")
Toast.makeText(baseContext, su_res, Toast.LENGTH_LONG).show()
println("LOG: getting data for ${su_res}")
val suRef = FirebaseDatabase.getInstance()
.getReference()
.child("Users")
.child(su_res)
suRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
println("LOG: data snapshot exists")
su_layout.visibility = View.VISIBLE
val userData = dataSnapshot.getValue(profile_model::class.java)
currentImageUrl = userData?.getImageUrl() ?: ""
su_name.text = userData?.getnameOfsu() ?: ""
println("LOG: Got user data ${currentImageUrl}")
if (currentImageUrl.isNotEmpty()) {
Picasso.get()
.load(currentImageUrl)
.placeholder(R.drawable.ic_baseline_image_200)
.into(su_image)
}
} else {
println("LOG: user not found in database")
}
}
override fun onCancelled(error: DatabaseError) {
println("LOG: cancelled")
}
})
}
private fun viewCurrentImage() {
if (currentImageUrl.isEmpty()) return
Toast.makeText(baseContext, currentImageUrl, Toast.LENGTH_LONG).show()
val imageViewer = Intent(baseContext, ImageViewer::class.java)
imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
imageViewer.putExtra("su_image", currentImageUrl)
startActivity(imageViewer)
}
}
The top answer to this related question shows you how to make callbacks, but that doesn't really answer the question of how to use the async data, and isn't really helpful or relevant to this type of problem.
I don't see anything specifically wrong with your callback - but it silently swallows a number of possible error cases (e.g. if the user doesn't exist). The example below has some print statements that should help determine better what is happening.
A cleaner approach than the extra callback interface is to make a separate method to handle the async result. Here is a cleaned up example of how that might look - with some pseudo-code where parts of your example were missing. To help debug, you should get in the habit of using log or print statements if you don't understand what parts of the code are running, or if something doesn't look the way you expect it to.
private var currentImageUrl: String = ""
private var userId: String = ""
private lateinit var su_name: TextView
private lateinit var su_image : ImageView
private lateinit var su_layout : ConstraintLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_su_details)
// get views
su_name = findViewById<TextView>(R.id.su_name)
su_image = findViewById<ImageView>(R.id.su_image)
su_layout = findViewById<ConstraintLayout>(R.id.su_layout)
su_layout.visibility = View.INVISIBLE
// get user id from intent
userId = intent.getStringExtra("su_userid").orEmpty()
// TODO: Handle what to do if userId is empty here!
if( userId.isEmpty() ) {
finish()
}
// onClick launches another activity - if the image
// hasn't loaded yet nothing happens
su_image.setOnClickListener { viewCurrentImage() }
// start the async loading right away - once it is loaded the
// su_layout view will be visible and the view data
// will be populated. It might be good to show a progress bar
// while it's loading
startLoading()
}
private fun startLoading() {
println("LOG: getting data for ${userId}")
val suRef = FirebaseDatabase.getInstance()
.getReference()
.child("Users")
.child(userId)
suRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if(dataSnapshot.exists()) {
println("LOG: data snapshot exists")
val userData = dataSnapshot.getValue(profile_model::class.java)
showData(userData)
}
else {
println("LOG: user not found in database")
}
}
override fun onCancelled(error: DatabaseError) {
println("LOG: cancelled")
}
})
}
private fun showData(userData: profile_model?) {
su_layout.visibility = View.VISIBLE
currentImageUrl = userData?.getImageUrl() ?: ""
su_name.text = userData?.getnameOfsu() ?: "Error"
println("LOG: Got user data ${currentImageUrl}")
if( currentImageUrl.isNotEmpty() ) {
Picasso.get()
.load(currentImageUrl)
.placeholder(R.drawable.ic_baseline_image_200)
.into(su_image)
}
}
private fun viewCurrentImage() {
if( currentImageUrl.isEmpty() ) return
val imageViewer = Intent(this, suDetails::class.java)
imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
imageViewer.putExtra("su_image", currentImageUrl)
startActivity(imageViewer)
}

Filtering ArrayList Determining a Firebase RealTime-Database Reference Child

I'm trying to filter three RecyclerViews on an activity based on a property that children have in one of my RTDB references have. On my ServiceActivity, I have the three RecyclerViews and I need help in knowing what I need to do to perform the filters. Do I need to create an empty ArrayList for each category property name and then perform the filter function on each? Or how would I perform this? Thank you!
What I Have
A RTDB Reference Child
ServiceActivity.kt
lateinit var autoJobServicesAdapter: JobServicesAdapter
lateinit var homeJobServicesAdapter: JobServicesAdapter
lateinit var personalJobServicesAdapter: JobServicesAdapter
val jobServices = ArrayList<JobService>()
val jobServicesDatabaseRef = FirebaseDatabase.getInstance().reference.child(REF_JOB_SERVICES)
val autoServices = ArrayList<JobService>()
val homeServices = ArrayList<JobService>()
val personalServices = ArrayList<JobService>()
private fun setupAutoRecyclerView() {
autoJobServicesAdapter = JobServicesAdapter(jobServices)
val autoRecyclerView = findViewById<RecyclerView>(R.id.autoServicesRecyclerView)
autoRecyclerView.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
adapter = autoJobServicesAdapter
setHasFixedSize(true)
}
jobServicesDatabaseRef.orderByChild("jobName").addValueEventListener(object: ValueEventListener {
#SuppressLint("NotifyDataSetChanged")
override fun onDataChange(snapshot: DataSnapshot) {
jobServices.clear()
for (snap in snapshot.children) {
val jobService = JobService(snap.child("category").getValue(String::class.java)!! ,
snap.child("jobName").getValue(String::class.java)!! , snap.child("jobImageUrl").getValue(String::class.java)!! ,
snap.child("jobServiceImageUrl").getValue(String::class.java)!!)
jobServices.add(jobService)
}
autoJobServicesAdapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
Log.d("ServiceActivity", "LoadPost:onCancelled", error.toException())
}
})
}
private fun setupHomeRecyclerView() {
homeJobServicesAdapter = JobServicesAdapter(jobServices)
val homeRecyclerView = findViewById<RecyclerView>(R.id.homeServicesRecyclerView)
homeRecyclerView.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
adapter = homeJobServicesAdapter
setHasFixedSize(true)
}
jobServicesDatabaseRef.orderByChild("jobName").addValueEventListener(object: ValueEventListener {
#SuppressLint("NotifyDataSetChanged")
override fun onDataChange(snapshot: DataSnapshot) {
jobServices.clear()
for (snap in snapshot.children) {
val jobService = JobService(snap.child("category").getValue(String::class.java)!! ,
snap.child("jobName").getValue(String::class.java)!! , snap.child("jobImageUrl").getValue(String::class.java)!! ,
snap.child("jobServiceImageUrl").getValue(String::class.java)!!)
jobServices.add(jobService)
}
homeJobServicesAdapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
Log.d("ServiceActivity", "LoadPost:onCancelled", error.toException())
}
})
}
private fun setupPersonalRecyclerView() {
personalJobServicesAdapter = JobServicesAdapter(jobServices)
val personalRecyclerView = findViewById<RecyclerView>(R.id.personalServicesRecyclerView)
personalRecyclerView.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
adapter = personalJobServicesAdapter
setHasFixedSize(true)
}
jobServicesDatabaseRef.orderByChild("jobName").addValueEventListener(object: ValueEventListener {
#SuppressLint("NotifyDataSetChanged")
override fun onDataChange(snapshot: DataSnapshot) {
jobServices.clear()
for (snap in snapshot.children) {
val jobService = JobService(snap.child("category").getValue(String::class.java)!! ,
snap.child("jobName").getValue(String::class.java)!! , snap.child("jobImageUrl").getValue(String::class.java)!! ,
snap.child("jobServiceImageUrl").getValue(String::class.java)!!)
jobServices.add(jobService)
}
personalJobServicesAdapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
Log.d("ServiceActivity", "LoadPost:onCancelled", error.toException())
}
})
}
You're loading the exact same data from Firebase three times. While the client is smart enough to deduplicate these queries for you, it's still better to only process the same data once.
How about creating the three views, list, and adapters all at once, and then loading the data for each with this single snippet? Something like this:
private fun loadDataForRecyclerViews() {
jobServicesDatabaseRef.orderByChild("jobName").addValueEventListener(object: ValueEventListener {
#SuppressLint("NotifyDataSetChanged")
override fun onDataChange(snapshot: DataSnapshot) {
jobServices.clear()
for (snap in snapshot.children) {
// 👇 Load the category and the job
val category = category
val jobService = (category ,
snap.child("jobName").getValue(String::class.java)!! , snap.child("jobImageUrl").getValue(String::class.java)!! ,
snap.child("jobServiceImageUrl").getValue(String::class.java)!!)
// 👇 Put the job in the correct list based on its category
if (category == "auto") {
autoJobServices.add(jobService)
}
else if (category == "home") {
homeJobServices.add(jobService)
}
else if (category == "personal") {
personalJobServices.add(jobService)
}
}
// 👇 Refresh all views
autoJobServicesAdapter.notifyDataSetChanged()
homeJobServicesAdapter.notifyDataSetChanged()
personalJobServicesAdapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
Log.d("ServiceActivity", "LoadJobs", error.toException())
}
})
}
Note: I did not compile this code, but merely provide it as a starting point. If you get a compiler error, please try to fix it on your own before commenting about it.

Not Able to get the usersList [duplicate]

This question already has answers here:
How to return DataSnapshot value as a result of a method?
(6 answers)
getContactsFromFirebase() method return an empty list
(1 answer)
Setting Singleton property value in Firebase Listener
(3 answers)
Closed 1 year ago.
Hello I am trying to implement a RecyclerView of users that are in my RealtimeDatabase, but this code dosen't work and I dont know why. Here is the code for Activity.
class NewMessageActivity : AppCompatActivity() {
private val TAG = "NewMessageActivityTag/////////////////////////////"
private lateinit var binding : ActivityNewMessageBinding
private lateinit var realtimeDatabase : FirebaseDatabase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityNewMessageBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.title = "New Message"
realtimeDatabase = FirebaseDatabase.getInstance("https://firechat-931d2-default-rtdb.asia-southeast1.firebasedatabase.app/")
val users = fetchUsers()
Log.d(TAG,"vkgjhbiuglkjhglioiuhghubgDone2 $users")
binding.rvNewMessageActivity.adapter = UsersAdpater(this,users)
binding.rvNewMessageActivity.layoutManager = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)
}
private fun fetchUsers() : ArrayList<UserInfo> {
val ref = realtimeDatabase.getReference("/users")
val userList = ArrayList<UserInfo>()
Log.d(TAG,"pidsnigfngadsnfpoin")
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot.exists()) {
Log.d(TAG,"$snapshot")
for(child in snapshot.children){
val tempchild = child.getValue(UserInfo::class.java)
Log.d(TAG,"$tempchild")
userList.add(tempchild ?: UserInfo("default_UID","default_email","default_name","default_picurl"))
}
}
}
override fun onCancelled(error: DatabaseError) {
Log.d(TAG,"$error ${error.message}")
}
})
Log.d(TAG,"$userList")
// if(userList.isEmpty()) {
// for(i in 1..10) {
// userList.add(UserInfo("default_UID$i","default_email$i","default_name$i","https://firebasestorage.googleapis.com/v0/b/firechat-931d2.appspot.com/o/ProfileImages%2FL5yOgELQtmXdUcnxf7S6Iqs1OsN2.profileImage?alt=media&token=3573195b-a5bb-499f-a79b-7191d5ad2655"))
// }
// }
return userList
}
}
Now I'll add the data class code
1
Now I'll add the adapter
class UsersAdpater(val context : Context, val gotData : ArrayList<UserInfo>) : RecyclerView.Adapter<UsersAdpater.UserViewHolder>() {
private var data : ArrayList<UserInfo> = gotData
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_user_newmessasgesactivity,parent,false)
return UserViewHolder(view)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.username.text = data[position].name
holder.useremail.text = data[position].email
Glide.with(context).load(data[position].profilePicUrl).into(holder.userImage).onLoadFailed(ContextCompat.getDrawable(context,R.drawable.profilepicnormall))
holder.entireLayout.setOnClickListener {
//TODO("IMPLEMENT OPENING A NEW CHAT WITH THIS USER")
Log.d("NewMessagesActivity","${data[position].toString()}")
}
}
override fun getItemCount(): Int {
return data.size
}
private fun updateList(userList : ArrayList<UserInfo>) {
data = userList
}
inner class UserViewHolder(private val itemView : View) : RecyclerView.ViewHolder(itemView) {
val username = itemView.findViewById<TextView>(R.id.item_rvnma_username)
val useremail = itemView.findViewById<TextView>(R.id.item_rvnma_email)
val userImage = itemView.findViewById<ImageView>(R.id.item_rvnma_image)
val entireLayout = itemView
}
}
// In the Log
2 As you can see the the function fetchUsers() has already returned an empty list and the data is logged after that. Why so ? And also why is it returning an empty list when it logs data for every user?
Is it because the fetching of users takes time? Do I need to use Coroutines?

How to get list data on Firebase android and processing data on oncreate function in Android

I have a question, I've tried several methods but they haven't worked; I'm hoping that everyone will assist me.
I'd want to obtain a list of data from Firebase (Figure 1). However, when I use it in the onCreate function (Figure 2), it becomes much slower, and I am unable to obtain the log data (Figure 3).
Such, how do I obtain the list data and alter it so that I can hide, reveal, or deactivate a feature?
Everyone, thank you for taking the time to read this.
My code here:
class FirebaseDatabaseManager
class FirebaseDatabaseManager() : ChildEventListener {
private lateinit var reference: DatabaseReference
private var productIds: MutableList<String> = ArrayList()
fun initDatabaseReference(key: String): FirebaseDatabaseManager {
reference = FirebaseDatabase.getInstance().reference.child(key)
return this
}
fun pushData(value: String) {
reference.push().setValue(value, completionListener)
}
private var completionListener =
DatabaseReference.CompletionListener { error, ref ->
if (error == null) {
Logger.d("completionListener", "onComplete pushed.!")
}
}
fun loadListData() {
reference.addChildEventListener(this)
}
fun getListData(): MutableList<String> {
return productIds
}
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
val value = snapshot.value.toString()
productIds.add(value)
Logger.d("FirebaseDatabaseManager_Main",
"onChildAdded: productIds.size ------ ${productIds.size}")
}
override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {
}
override fun onChildRemoved(snapshot: DataSnapshot) {
val value = snapshot.value.toString()
productIds.remove(value)
}
override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {
}
override fun onCancelled(error: DatabaseError) {
}
}
class MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var productIds: MutableList<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
productIds = ArrayList()
FirebaseDatabaseManager().initDatabaseReference(keyId).loadListData()
productIds = FirebaseDatabaseManager().getListData()
Logger.d("MainActivity", "productIds ------ $productIds")
}
}
This is log when run app:

On Groupie Recycler view every 7 rows it repeats the same uid of firebase database

I dont know what to do in this case because every 7 rows the image drawable is favorite even when it shouldn't be.
class RecipesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recipes)
getRecipes()
}
companion object{
val REC_KEY = "REC_KEY"
}
var adapter = GroupAdapter<GroupieViewHolder>()
private fun getRecipes(){
val ref = FirebaseDatabase.getInstance().getReference("/recipes")
val adapter = GroupAdapter<GroupieViewHolder>()
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach{
val reci = it.getValue(Recipes::class.java)
if(reci != null) {
adapter.add(RecipesItem(reci))
}
}
adapter.setOnItemClickListener{ item, view ->
val recItem = item as RecipesItem
val intent = Intent(view.context, RecipeDetail::class.java)
intent.putExtra(REC_KEY, recItem.recipes)
startActivity(intent)
}
recycler_recipes.adapter = adapter
}
override fun onCancelled(p0: DatabaseError) {
}
})
}
}
This class is to put the items in the views. I need them to be, but when I check for favourite or not there's the problem.
class RecipesItem(val recipes: Recipes): Item<GroupieViewHolder>(){
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.itemView.name_recycler.text = recipes.reci_name
viewHolder.itemView.type_recipes.text = recipes.reci_diet
viewHolder.itemView.timeToPrepare.text = recipes.reci_time.plus(" min")
//checkiffav
val user = FirebaseAuth.getInstance().currentUser!!.uid
val fav = FirebaseDatabase.getInstance().getReference("/users/$user/fav_recipes/${recipes.id}")
fav.addListenerForSingleValueEvent(object: ValueEventListener{
override fun onDataChange(p0: DataSnapshot){
if(p0.exists()){
viewHolder.itemView.favorite.setImageResource(R.drawable.favorite)
}
}
override fun onCancelled(p0: DatabaseError) {
}
})
}
override fun getLayout(): Int {
return R.layout.row_recipes
}
}
Basically I have 20 recipes and I have a favorite system, but the recycler view only gets the id of the recipes on every 7 rows, then it repeats the same id's! I'm really new to kotlin and firebase and I dont know how to solve. On every 7 rows the recipe appears has fav because the id that the firebase reference is getting is the same as the first one.
Answered on Groupie issue tracker: https://github.com/lisawray/groupie/issues/320#issuecomment-581915693

Categories

Resources