I'm using Realm in my project and I was trying to update the realm object through RxJava. Realm object is successfully stored but the realm object is not updated.Actually i am performing all Read operations on UI thread and all WRITE operations on background thread using RxJava. Below is my code to perform all Realm's read and write operations.
class SuggestedFriendsController internal constructor(realm : Realm) {
private var realm: Realm? = null
init {
this.realm = realm
}
fun addSuggestedFriends(requestPojo: RequestPojo?) {
realm?.executeTransaction { realm ->
val realmFriends = RealmSuggestedFriends()
realmFriends.friendEmail = requestPojo?.email
realmFriends.friendImage = requestPojo?.image
realmFriends.friendName = requestPojo?.name
realmFriends.friendStatus = requestPojo?.status
realmFriends.friendThumbImage = requestPojo?.thumb_image
realmFriends.friendUid = requestPojo?.uid
realmFriends.requestSent = "No"
realm.copyToRealmOrUpdate(realmFriends)
}
}
fun getRequestStateSync(uid: String?): String? {
var res = Realm.getDefaultInstance().use { realm ->
realm?.where(RealmSuggestedFriends::class.java)
?.equalTo("friendUid", uid)
?.findFirst()
}
return res?.requestSent
}
fun getAllAsync() = Observable.create<List<RealmSuggestedFriends>> { emitter ->
Realm.getDefaultInstance().use { realm ->
val results = realm?.where(RealmSuggestedFriends::class.java)
?.findAll()
emitter.onNext(realm?.copyFromRealm(results)!!)
}
}.flatMapIterable { list -> list }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
fun updateSuggestedFriendAsync(uid: String?, request_type: String?) =
Single.create<String>{emitter ->
Realm.getDefaultInstance().use {realm ->
realm?.executeTransaction{ realm ->
var suggested = realm.where(RealmSuggestedFriends::class.java)
.equalTo("friendUid", uid)
.findFirst()!!
suggested.requestSent = request_type
realm.insertOrUpdate(suggested)
}
emitter.onSuccess("success")
}
}.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
}
Below is my activity code. Here the variable suggestedFriendController?.getRequestStateSync(friend_uid) value is remain unchanged even its value is changed in the database.
class SFriend : AppCompatActivity(){
private var image : NewCircularImagview ?= null
private var name : AppCompatTextView ?= null
private var addFriend : AppCompatButton ?= null
private var imageUrl : String ?= null
private var toolbar : Toolbar ?= null
private var toolbar_text : TextView ?= null
private var progressBar : ProgressBar ?= null
private var friend_uid : String ?= null
private var databaseReference: DatabaseReference? = null
private var uid: String? = null
private var compositeDisposable : CompositeDisposable ?= null
private var realm : Realm ?= null
private var suggestedFriendController : SuggestedFriendsController ?= null
private var request_state : String ?= null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.suggested_friend_profile)
initialize()
friend_uid = intent.getStringExtra("friend_uid")
imageUrl = intent.getStringExtra("imageurl")
name?.text = intent.getStringExtra("name")
request_state = suggestedFriendController?.getRequestStateSync(friend_uid)
if (request_state?.equals("No")!!){
addFriend?.setText("Send Request")
}else{
addFriend?.text = "Cancel Request"
}
}
private fun initialize() {
compositeDisposable = CompositeDisposable()
databaseReference = FirebaseDatabase.getInstance().reference
databaseReference?.keepSynced(true)
uid = FirebaseAuth.getInstance().currentUser?.uid
progressBar = findViewById(R.id.progress_bar)
image = findViewById(R.id.profile_pic)
name = findViewById(R.id.name)
addFriend = findViewById(R.id.addFriend)
toolbar = findViewById(R.id.toolbar)
toolbar_text = toolbar?.findViewById(R.id.toolbar_text1)
toolbar_text?.visibility = View.VISIBLE
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
getSupportActionBar()?.setHomeButtonEnabled(true)
realm = Realm.getDefaultInstance()
suggestedFriendController = realm?.let { r-> SuggestedFriendsController(r) }
}
override fun onStart() {
super.onStart()
progressBar?.visibility = View.VISIBLE
Picasso.with(this).load(imageUrl!!).into(image,object :Callback{
override fun onSuccess() {
progressBar?.visibility = View.GONE
}
override fun onError() {
showMessage("image loading failed")
}
})
addFriend?.setOnClickListener {
addFriend?.isEnabled = false
if (request_state?.equals("No")!!) {
sendRequest(friend_uid)
}else{
cancelRequest(friend_uid)
}
}
}
private fun cancelRequest(friend_uid: String?) {
Single.create<String>{ subscriber ->
databaseReference
?.child("friend_requests")
?.child(uid)
?.child(friend_uid)
?.removeValue()
?.addOnCompleteListener { task ->
if (task.isSuccessful) {
if (!subscriber.isDisposed) {
subscriber.onSuccess("success")
}
}else{
subscriber.onError(Throwable("error in cancel request"))
}
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap { Single.create<String>{ subscriber ->
databaseReference
?.child("friend_requests")
?.child(friend_uid)
?.child(uid)
?.removeValue()
?.addOnCompleteListener { task ->
if (task.isSuccessful){
if (!subscriber.isDisposed){
subscriber.onSuccess("success")
}
}else{
subscriber.onError(Throwable("error in cancel request"))
}
}
}.subscribeOn(Schedulers.io())
}
.subscribe({
t1 -> showMessage(t1)
addFriend?.text = "Send Request"
addFriend?.isEnabled = true
suggestedFriendController?.updateSuggestedFriend(friend_uid!!,"No")
},{
t2 -> showMessage(t2.message)
})
}
private fun showMessage(message : String?) {
Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
}
private fun sendRequest(friend_uid : String?){
var d = Single.create<String>{ subscriber ->
databaseReference
?.child("friend_requests")
?.child(uid)
?.child(friend_uid)
?.child("request_type")
?.setValue("sent")
?.addOnCompleteListener { task ->
if (task.isSuccessful){
if (!subscriber.isDisposed){
subscriber.onSuccess("success")
}
}else{
subscriber.onError(Throwable("request failed"))
}
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap { Single.create<String> { subscriber ->
databaseReference
?.child("friend_requests")
?.child(friend_uid)
?.child(uid)
?.child("request_type")
?.setValue("received")
?.addOnCompleteListener { task ->
if (task.isSuccessful){
if (!subscriber.isDisposed){
subscriber.onSuccess("sent")
}
}else{
subscriber.onError(Throwable("request failed"))
}
}
}.subscribeOn(Schedulers.io())
}.flatMap {
suggestedFriendController?.updateSuggestedFriendAsync(friend_uid,"Yes")
}.subscribe({
t1: String? -> showMessage(t1)
addFriend?.text = "Delete Request"
addFriend?.isEnabled = true
},{
t2 -> showMessage(t2.message)
}
)
compositeDisposable?.add(d)
}
override fun onBackPressed() {
super.onBackPressed()
finish()
}
override fun onStop() {
super.onStop()
compositeDisposable?.clear()
}
override fun onDestroy() {
super.onDestroy()
realm?.close()
}
}
Below code is my Realm model.
open class RealmSuggestedFriends : RealmObject(){
#Required
#PrimaryKey
var friendUid : String ?= null
#Required
var friendName : String ?= null
#Required
var friendEmail : String ?= null
#Required
var friendStatus : String ?= null
#Required
var friendImage : String ?= null
#Required
var friendThumbImage : String ?= null
#Required
var requestSent : String ?= null
}
I need your suggestions to make it work. Help me
Don't get the String, get the object. Also I wouldn't pass a Realm instance to a constructor of something like that.
class SuggestedFriendsController {
fun addSuggestedFriends(requestPojo: RequestPojo) {
Realm.getDefaultInstance().use { r ->
{
r.executeTransaction { realm ->
realm.insertOrUpdate(RealmSuggestedFriends().apply {
friendEmail = requestPojo?.email
friendImage = requestPojo?.image
friendName = requestPojo?.name
friendStatus = requestPojo?.status
friendThumbImage = requestPojo?.thumb_image
friendUid = requestPojo?.uid
requestSent = "No"
})
}
}
}
fun getSuggestedFriend(realm: Realm, uid: String): RealmSuggestedFriends? =
realm.where<RealmSuggestedFriends>().equalTo("friendUid", uid).findFirst()
fun getAllAsync() = Single.create<List<RealmSuggestedFriends>> { emitter ->
Realm.getDefaultInstance().use { realm ->
with(realm) {
emitter.onNext(copyFromRealm(where<RealmSuggestedFriends>().findAll())
}
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
fun updateSuggestedFriendAsync(uid: String, requestType: String) =
Single.create<String> { emitter ->
Realm.getDefaultInstance().use { r ->
r.executeTransaction { realm ->
var suggested = realm.where<RealmSuggestedFriends>()
.equalTo("friendUid", uid)
.findFirst()!!
suggested.requestSent = requestType
realm.insertOrUpdate(suggested)
}
emitter.onSuccess("success")
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
class SFriend : AppCompatActivity() {
private lateinit var image: NewCircularImagview
private lateinit var name: AppCompatTextView
private lateinit var addFriend: AppCompatButton
private lateinit var imageUrl: String
private lateinit var toolbar: Toolbar
private lateinit var toolbar_text: TextView
private lateinit var progressBar: ProgressBar
private lateinit var friend_uid: String
private lateinit var databaseReference: DatabaseReference
private lateinit var uid: String
private lateinit var compositeDisposable: CompositeDisposable
private lateinit var realm: Realm
private lateinit var suggestedFriendController: SuggestedFriendsController
private var suggestedFriend: RealmSuggestedFriends
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.suggested_friend_profile)
initialize()
friend_uid = intent.getStringExtra("friend_uid")
imageUrl = intent.getStringExtra("imageurl")
name.text = intent.getStringExtra("name")
suggestedFriend = suggestedFriendController.getRequestStateSync(realm, friend_uid)
addFriend.text = when {
"No" == suggestedFriend?.requestSent -> "Send Request"
else ->
= "Cancel Request"
}
suggestedFriend.addChangeListener(RealmChangeListener {
if(it.isValid()) {
addFriend.text = when {
"No" == it.requestSent -> "Send Request"
else -> "Cancel Request"
}
}
})
}
private fun initialize() {
compositeDisposable = CompositeDisposable()
databaseReference = FirebaseDatabase.getInstance().reference
databaseReference.keepSynced(true)
uid = FirebaseAuth.getInstance().currentUser?.uid
progressBar = findViewById(R.id.progress_bar)
image = findViewById(R.id.profile_pic)
name = findViewById(R.id.name)
addFriend = findViewById(R.id.addFriend)
toolbar = findViewById(R.id.toolbar)
toolbar_text = toolbar?.findViewById(R.id.toolbar_text1)
toolbar_text.visibility = View.VISIBLE
setSupportActionBar(toolbar)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
supportActionBar!!.setHomeButtonEnabled(true)
realm = Realm.getDefaultInstance()
suggestedFriendController = SuggestedFriendsController()
}
override fun onStart() {
super.onStart()
progressBar.visibility = View.VISIBLE
Picasso.with(this).load(imageUrl).into(image, object : Callback {
override fun onSuccess() {
progressBar.visibility = View.GONE
}
override fun onError() {
showMessage("image loading failed")
}
})
addFriend.setOnClickListener {
addFriend.isEnabled = false
if ("No" == suggestedFriend?.requestSent) {
sendRequest(friend_uid)
} else {
cancelRequest(friend_uid)
}
}
}
private fun cancelRequest(friend_uid: String?) {
Single.create<String> { subscriber ->
databaseReference
.child("friend_requests")
.child(uid)
.child(friend_uid)
.removeValue()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
if (!subscriber.isDisposed) {
subscriber.onSuccess("success")
}
} else {
subscriber.onError(Throwable("error in cancel request"))
}
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap {
Single.create<String> { subscriber ->
databaseReference
.child("friend_requests")
.child(friend_uid)
.child(uid)
.removeValue()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
if (!subscriber.isDisposed) {
subscriber.onSuccess("success")
}
} else {
subscriber.onError(Throwable("error in cancel request"))
}
}
}.subscribeOn(Schedulers.io())
}
.subscribe({ t1 ->
showMessage(t1)
addFriend.text = "Send Request"
addFriend.isEnabled = true
suggestedFriendController.updateSuggestedFriend(friend_uid!!, "No")
}, { t2 ->
showMessage(t2.message)
})
}
private fun showMessage(message: String?) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
private fun sendRequest(friend_uid: String?) {
var d = Single.create<String> { subscriber ->
databaseReference
.child("friend_requests")
.child(uid)
.child(friend_uid)
.child("request_type")
.setValue("sent")
.addOnCompleteListener { task ->
if (task.isSuccessful) {
if (!subscriber.isDisposed) {
subscriber.onSuccess("success")
}
} else {
subscriber.onError(Throwable("request failed"))
}
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap {
Single.create<String> { subscriber ->
databaseReference
.child("friend_requests")
.child(friend_uid)
.child(uid)
.child("request_type")
.setValue("received")
.addOnCompleteListener { task ->
if (task.isSuccessful) {
if (!subscriber.isDisposed) {
subscriber.onSuccess("sent")
}
} else {
subscriber.onError(Throwable("request failed"))
}
}
}.subscribeOn(Schedulers.io())
}.doOnNext {
suggestedFriendController.updateSuggestedFriendAsync(friend_uid, "Yes")
}.subscribe({ t1: String? ->
realm.refresh()
showMessage(t1)
addFriend.text = "Delete Request"
addFriend.isEnabled = true
}, { t2 ->
showMessage(t2.message)
})
compositeDisposable.add(d)
}
override fun onBackPressed() {
super.onBackPressed()
finish()
}
override fun onStop() {
super.onStop()
compositeDisposable.clear()
}
override fun onDestroy() {
super.onDestroy()
suggestedFriend.removeAllChangeListeners()
realm.close()
}
}
}
Related
I am almost new to android testing and following the official docs and Udacity course for learning purposes.
Coming to the issue I want to check when the task is completed or incompleted to be displayed properly or not, for this I wrote a few tests. Here I got the exception that toast can not be displayed on a thread that has not called Looper.prepare.
When I comment out the toast msg live data updating line of code then all tests work fine and pass successfully. I am new to android testing and searched out a lot but did not get any info to solve this issue. Any help would be much appreciated. A little bit of explanation will be much more helpful if provided.
Below is my test class source code along with ViewModel, FakeRepository, and fragment source code.
Test Class.
#ExperimentalCoroutinesApi
#MediumTest
#RunWith(AndroidJUnit4::class)
class TaskDetailFragmentTest {
#get:Rule
var mainCoroutineRule = MainCoroutineRule()
#get:Rule
val rule = InstantTaskExecutorRule()
private lateinit var tasksRepository: FakeTasksRepository
#Before
fun setUp() {
tasksRepository = FakeTasksRepository()
ServiceLocator.taskRepositories = tasksRepository
}
#Test
fun addNewTask_addNewTaskToDatabase() = mainCoroutineRule.runBlockingTest {
val newTask = Task(id = "1", userId = 0, title = "Hello AndroidX World",false)
tasksRepository.addTasks(newTask)
val task = tasksRepository.getTask(newTask.id)
assertEquals(newTask.id,(task as Result.Success).data.id)
}
#Test
fun activeTaskDetails_DisplayedInUi() = mainCoroutineRule.runBlockingTest {
val newTask = Task(id = "2", userId = 0, title = "Hello AndroidX World",false)
tasksRepository.addTasks(newTask)
val bundle = TaskDetailFragmentArgs(newTask.id).toBundle()
launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.Theme_ToDoWithTDD)
onView(withId(R.id.title_text)).check(matches(isDisplayed()))
onView(withId(R.id.title_text)).check(matches(withText("Hello AndroidX World")))
onView(withId(R.id.complete_checkbox)).check(matches(isDisplayed()))
onView(withId(R.id.complete_checkbox)).check(matches(isNotChecked()))
}
#Test
fun completedTaskDetails_DisplayedInUI() = mainCoroutineRule.runBlockingTest {
val newTask = Task(id = "2", userId = 0, title = "Hello AndroidX World",true)
tasksRepository.addTasks(newTask)
val bundle = TaskDetailFragmentArgs(newTask.id).toBundle()
launchFragmentInContainer <TaskDetailFragment>(bundle,R.style.Theme_ToDoWithTDD)
onView(withId(R.id.title_text)).check(matches(isDisplayed()))
onView(withId(R.id.title_text)).check(matches(withText("Hello AndroidX World")))
onView(withId(R.id.complete_checkbox)).check(matches(isDisplayed()))
onView(withId(R.id.complete_checkbox)).check(matches(isChecked()))
}
#After
fun tearDown() = mainCoroutineRule.runBlockingTest {
ServiceLocator.resetRepository()
}
}
FakeRepository class.
class FakeTasksRepository: TasksRepository {
var tasksServiceData: LinkedHashMap<String,Task> = LinkedHashMap()
private val observableTasks: MutableLiveData<Result<List<Task>>> = MutableLiveData()
private var shouldReturnError: Boolean = false
fun setReturnError(value: Boolean) {
shouldReturnError = value
}
override fun observeTasks(): LiveData<Result<List<Task>>> {
return observableTasks
}
override fun observeTask(taskId: String): LiveData<Result<Task>> {
runBlocking { fetchAllToDoTasks() }
return observableTasks.map { tasks ->
when(tasks) {
is Result.Loading -> Result.Loading
is Result.Error -> Result.Error(tasks.exception)
is Result.Success -> {
val task = tasks.data.firstOrNull() { it.id == taskId }
?: return#map Result.Error(Exception("Not found"))
Result.Success(task)
}
}
}
}
override suspend fun completeTask(id: String) {
tasksServiceData[id]?.completed = true
}
override suspend fun completeTask(task: Task) {
val compTask = task.copy(completed = true)
tasksServiceData[task.id] = compTask
fetchAllToDoTasks()
}
override suspend fun activateTask(id: String) {
tasksServiceData[id]?.completed = false
}
override suspend fun activateTask(task: Task) {
val activeTask = task.copy(completed = false)
tasksServiceData[task.id] = activeTask
fetchAllToDoTasks()
}
override suspend fun getTask(taskId: String): Result<Task> {
if (shouldReturnError) return Result.Error(Exception("Test Exception"))
tasksServiceData[taskId]?.let {
return Result.Success(it)
}
return Result.Error(Exception("Could not find task"))
}
override suspend fun getTasks(): Result<List<Task>> {
return Result.Success(tasksServiceData.values.toList())
}
override suspend fun saveTask(task: Task) {
tasksServiceData[task.id] = task
}
override suspend fun clearAllCompletedTasks() {
tasksServiceData = tasksServiceData.filterValues {
!it.completed
} as LinkedHashMap<String, Task>
}
override suspend fun deleteAllTasks() {
tasksServiceData.clear()
fetchAllToDoTasks()
}
override suspend fun deleteTask(taskId: String) {
tasksServiceData.remove(taskId)
fetchAllToDoTasks()
}
override suspend fun fetchAllToDoTasks(): Result<List<Task>> {
if(shouldReturnError) {
return Result.Error(Exception("Could not find task"))
}
val tasks = Result.Success(tasksServiceData.values.toList())
observableTasks.value = tasks
return tasks
}
override suspend fun updateLocalDataStore(list: List<Task>) {
TODO("Not yet implemented")
}
fun addTasks(vararg tasks: Task) {
tasks.forEach {
tasksServiceData[it.id] = it
}
runBlocking {
fetchAllToDoTasks()
}
}
}
Fragment class.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.loadTaskById(args.taskId)
setUpToast(this,viewModel.toastText)
viewModel.editTaskEvent.observe(viewLifecycleOwner, {
it?.let {
val action = TaskDetailFragmentDirections
.actionTaskDetailFragmentToAddEditFragment(
args.taskId,
resources.getString(R.string.edit_task)
)
findNavController().navigate(action)
}
})
binding.editTaskFab.setOnClickListener {
viewModel.editTask()
}
}
ViewModel class.
class TaskDetailViewModel(
private val tasksRepository: TasksRepository
) : ViewModel() {
private val TAG = "TaskDetailViewModel"
private val _taskId: MutableLiveData<String> = MutableLiveData()
private val _task = _taskId.switchMap {
tasksRepository.observeTask(it).map { res ->
Log.d("Test","res with value ${res.toString()}")
isolateTask(res)
}
}
val task: LiveData<Task?> = _task
private val _toastText = MutableLiveData<Int?>()
val toastText: LiveData<Int?> = _toastText
private val _dataLoading = MutableLiveData<Boolean>()
val dataLoading: LiveData<Boolean> = _dataLoading
private val _editTaskEvent = MutableLiveData<Unit?>(null)
val editTaskEvent: LiveData<Unit?> = _editTaskEvent
fun loadTaskById(taskId: String) {
if(dataLoading.value == true || _taskId.value == taskId) return
_taskId.value = taskId
Log.d("Test","loading task with id $taskId")
}
fun editTask(){
_editTaskEvent.value = Unit
}
fun setCompleted(completed: Boolean) = viewModelScope.launch {
val task = _task.value ?: return#launch
if(completed) {
tasksRepository.completeTask(task.id)
_toastText.value = R.string.task_marked_complete
}
else {
tasksRepository.activateTask(task.id)
_toastText.value = R.string.task_marked_active
}
}
private fun isolateTask(result: Result<Task?>): Task? {
return if(result is Result.Success) {
result.data
} else {
_toastText.value = R.string.loading_tasks_error
null
}
}
#Suppress("UNCHECKED_CAST")
class TasksDetailViewModelFactory(
private val tasksRepository: TasksRepository
): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return (TaskDetailViewModel(
tasksRepository
) as T)
}
}
}
In this method in ViewModel when I comment out the below line of code all tests passed.
_toastText.value = R.string.loading_tasks_error
private fun isolateTask(result: Result<Task?>): Task? {
return if(result is Result.Success) {
result.data
} else {
_toastText.value = R.string.loading_tasks_error // Comment out this line then all test passed.
null
}
}
This question already has answers here:
getContactsFromFirebase() method return an empty list
(1 answer)
Setting Singleton property value in Firebase Listener
(3 answers)
Why does my function that calls an API or launches a coroutine return an empty or null value?
(4 answers)
Closed 1 year ago.
I receive null snapshot in only this two method
private fun getUserName() {
databaseReference=FirebaseDatabase.getInstance("https://tailoring-e7e0c-default-rtdb.asia-southeast1.firebasedatabase.app/").getReference("Users")
databaseReference.child(uAuth.currentUser?.uid.toString()).addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
var userList :User
userList = snapshot.getValue(User::class.java)!!
userName=userList?.cid.toString()
Toast.makeText(this#ProductDetail,"Username detected",Toast.LENGTH_LONG).show()
}
override fun onCancelled(error: DatabaseError) {
}
})
}
private fun getProductPic(prodImageURL:String) {
storageReference= FirebaseStorage.getInstance().reference.child(prodImageURL)
val localFile = File.createTempFile("tempImage","jpg")
storageReference.getFile(localFile).addOnSuccessListener {
val bitMap= BitmapFactory.decodeFile(localFile.absolutePath)
viewBinding.picViewProd.setImageBitmap(bitMap)
}
}
class ProductDetail : AppCompatActivity() {
private lateinit var databaseReference: DatabaseReference
private lateinit var uAuth:FirebaseAuth
private lateinit var storageReference: StorageReference
private lateinit var viewBinding:ActivityProductDetailBinding
private lateinit var adapter:commentAdapter
private lateinit var tc:String
private lateinit var commentList:ArrayList<commentContain>
private lateinit var thisProductID:String
private lateinit var dateTage :String
private var userName :String=""
private var tailorUID :String=""
private var tailorName :String=""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityProductDetailBinding.inflate(layoutInflater)
val view = viewBinding.root
setContentView(view)
tc=getIntent().getStringExtra("tc").toString()
thisProductID = getIntent().getStringExtra("prodNo").toString()
uAuth = FirebaseAuth.getInstance()
//Comment adapter and recycler view code
commentList= ArrayList()
getTailorName()
getComment()
adapter= commentAdapter(this,commentList)
viewBinding.commentList.layoutManager=LinearLayoutManager(this)
viewBinding.commentList.adapter=adapter
getTimeTag()
val currentUser = uAuth.currentUser?.uid
val currentUserURL = "Users/$currentUser.jpg"
getProductData()
if(tc=="tailor"){
viewBinding.btnOrderProduct.isVisible=false
}
viewBinding.btnOrderProduct.setOnClickListener {
addOrder()
}
viewBinding.picViewTailor.setOnClickListener {
val intent = Intent(this, TailorProfile::class.java)
intent.putExtra("tc",tc)
intent.putExtra("tuid",tailorUID)
startActivity(intent)
}
viewBinding.sendButton.setOnClickListener{
getUserName()
val comment = viewBinding.messageBox.text.toString()
addComment(thisProductID,userName,comment,dateTage,currentUserURL)
}
}
private fun getUserName() {
databaseReference=FirebaseDatabase.getInstance("https://tailoring-e7e0c-default-rtdb.asia-southeast1.firebasedatabase.app/").getReference("Users")
databaseReference.child(uAuth.currentUser?.uid.toString()).addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
var userList :User
userList = snapshot.getValue(User::class.java)!!
userName=userList?.cid.toString()
Toast.makeText(this#ProductDetail,"Username detected",Toast.LENGTH_LONG).show()
}
override fun onCancelled(error: DatabaseError) {
}
})
}
private fun getProductData() {
databaseReference=FirebaseDatabase.getInstance("https://tailoring-e7e0c-default-rtdb.asia-southeast1.firebasedatabase.app/").reference
databaseReference.child("Product").child(thisProductID).addValueEventListener(object: ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val productData = snapshot.getValue<Product>()
viewBinding.tvViewPrice.setText(productData?.price.toString())
viewBinding.tvViewClothspants.setText(productData?.clothsPants.toString())
viewBinding.tvViewFabric.setText(productData?.fabric.toString())
viewBinding.tvViewHeight.setText(productData?.height.toString())
viewBinding.tvViewHipBust.setText(productData?.hipBust.toString())
viewBinding.tvViewSleeve.setText(productData?.sleeve.toString())
viewBinding.tvViewStyleName.setText(productData?.styleName.toString())
viewBinding.tvViewWaist.setText(productData?.waist.toString())
tailorUID = productData?.tailorID.toString()
getTailorPic(productData?.tailorID.toString())
getProductPic(productData?.imageURL.toString())
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(this#ProductDetail,"Some Things wrong in the database", Toast.LENGTH_SHORT).show()
}
}
)
}
private fun getProductPic(prodImageURL:String) {
storageReference= FirebaseStorage.getInstance().reference.child(prodImageURL)
val localFile = File.createTempFile("tempImage","jpg")
storageReference.getFile(localFile).addOnSuccessListener {
val bitMap= BitmapFactory.decodeFile(localFile.absolutePath)
viewBinding.picViewProd.setImageBitmap(bitMap)
}
}
private fun getTailorName() {
databaseReference=FirebaseDatabase.getInstance("https://tailoring-e7e0c-default-rtdb.asia-southeast1.firebasedatabase.app/").getReference("Users")
databaseReference.child(tailorUID).addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val userList = snapshot.getValue(User::class.java)!!
tailorName=userList.cid.toString()
viewBinding.tvPViewTailorName.setText(tailorName)
Toast.makeText(this#ProductDetail,"Username detected",Toast.LENGTH_LONG).show()
}
override fun onCancelled(error: DatabaseError) {
}
})
}
private fun getTailorPic(tailorUID:String){
storageReference= FirebaseStorage.getInstance().reference.child("Users/$tailorUID.jpg")
val localFile = File.createTempFile("tempImage","jpg")
storageReference.getFile(localFile).addOnSuccessListener {
val bitMap= BitmapFactory.decodeFile(localFile.absolutePath)
viewBinding.picViewTailor.setImageBitmap(bitMap)
}
}
private fun getComment() {
databaseReference=FirebaseDatabase.getInstance().getReference()
databaseReference.child("Comment").addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
commentList.clear()
for(postSnapshot in snapshot.children){
val currentComment = postSnapshot.getValue(commentContain::class.java)
if(currentComment?.productID==thisProductID){
commentList.add(currentComment!!)
}
}
adapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
}
})
}
private fun addOrder() {
val intent = Intent(this, NewCustomOrder::class.java)
intent.putExtra("tc",tc)
intent.putExtra("prodNo",thisProductID)
startActivity(intent)
}
private fun addComment(productID:String,senderID:String?,comment:String,dateTage:String,currentUserURL:String){
val newComment = commentContain(uAuth.currentUser?.uid,senderID,comment,dateTage,currentUserURL,productID)
databaseReference=FirebaseDatabase.getInstance().reference
databaseReference.child("Comment").child(commentList.size.toString()).setValue(newComment).addOnFailureListener{
Toast.makeText(this,"Some thing wrong for real time database", Toast.LENGTH_SHORT).show()
}.addOnSuccessListener {
Toast.makeText(this,"Comment is added", Toast.LENGTH_SHORT).show()
}
}
private fun getTimeTag() {
val formatter = SimpleDateFormat("yyyy_MM_dd", Locale.getDefault())
val now = Date()
dateTage=formatter.format(now).toString()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
super.onCreateOptionsMenu(menu)
getMenuInflater().inflate(R.menu.all_menu,menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
super.onOptionsItemSelected(item)
if(item.itemId==R.id.toProfile){
val intent = Intent(this, TailorProfile::class.java)
intent.putExtra("tc",tc)
intent.putExtra("tuid",uAuth.currentUser?.uid)
finish()
startActivity(intent)
}
else if(item.itemId == R.id.toHome){
val intent = Intent(this, ProductList::class.java)
intent.putExtra("tc",tc)
finish()
startActivity(intent)
}else if(item.itemId == R.id.logout){
val intent = Intent(this, Ground::class.java)
uAuth.signOut()
finish()
startActivity(intent)
}
return true
}
}
The only problem is the getUserName and getTailorName return null at all,which is cause by the snapshot return a null value. I have tried all the method of get data from firebase realtime database, but all of them meets same problem. The weird thing is only the two function meet that problem. Any one can help me please?
below is the picture output
enter image description here
I have the following data class that holds the child values of a specific reference from the Firebase Realtime Database:
data class Users(
val uid: String = "",
var firstName: String = "",
var lastName: String = "",
)
This is my ViewModel:
class UsersViewModel : ViewModel() {
private val uid = Firebase.auth.currentUser!!.uid
private val USERS_REF: DatabaseReference = FirebaseDatabase.getInstance().getReference("/users/$uid")
private val liveData: FirebaseQueryLiveData = FirebaseQueryLiveData(USERS_REF)
private val usersLiveData: MediatorLiveData<Users> = MediatorLiveData()
init {
usersLiveData.addSource(liveData, object : Observer<DataSnapshot> {
override fun onChanged(dataSnapshot: DataSnapshot?) {
if (dataSnapshot != null) {
usersLiveData.postValue(dataSnapshot.getValue(Users::class.java))
} else {
usersLiveData.value = null
}
}
})
}
#NonNull
fun getUsersLiveData() : LiveData<Users> {
return usersLiveData
}
}
This is my extended LiveData:
class FirebaseQueryLiveData(ref: DatabaseReference) : LiveData<DataSnapshot>() {
private val query: Query = ref
private val listener: MyValueEventListener = MyValueEventListener()
private var listenerRemovePending = false
private val removeListener = object : Runnable {
override fun run() {
query.removeEventListener(listener)
listenerRemovePending = false
}
}
override fun onActive() {
super.onActive()
if (listenerRemovePending) {
Handler(Looper.getMainLooper()).removeCallbacks(removeListener)
} else {
query.addValueEventListener(listener)
}
listenerRemovePending = false
}
override fun onInactive() {
super.onInactive()
Handler(Looper.getMainLooper()).postDelayed(removeListener, 2000)
query.removeEventListener(listener)
}
private inner class MyValueEventListener : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
value = snapshot
}
override fun onCancelled(error: DatabaseError) {
return
}
}
}
For reading the values from my database in my Activity or Fragment, this is what I've done:
val usersViewModel = ViewModelProvider(this).get(UsersViewModel::class.java)
val usersLiveData = usersViewModel.getUsersLiveData()
usersLiveData.observe(this, object : Observer<Users> {
override fun onChanged(users: Users?) {
if (users != null) {
firstNameTextView.text = users.firstName
lastNameTextView.text = users.lastName
}
}
})
This all works, but my question is how do I write to a specific child for my database in my Activity or Fragment? For example, let's say I want to only modify the lastName child? How do I do that? I don't have any references to FirebaseDatabase in my activities and fragments because the ViewModel and LiveData ensure that I don't need them anymore.
I already did all the competent of the paging library tried to send all data to the UI following this artricle but no luck on getting the data here is my code
When I debugged I found the following:
when it's loading the network state and pagedUserList get notified for the first time and when the request DONE only the network state get notified and the list doesn't get notified with the list itself and I don't know why? any clue?
MainActivity
private fun initAdapter() {
newsListAdapter = NewsListAdapter { mainViewModel.retry() }
binder.rvUsers.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
binder.rvUsers.adapter = newsListAdapter
mainViewModel.getPagedList().observe(this, Observer {
newsListAdapter.submitList(it)
})
}
private fun initState() {
binder.txtError.setOnClickListener { mainViewModel.retry() }
mainViewModel.getState().observe(this, Observer { state ->
binder.progressBar.visibility =
if (mainViewModel.listIsEmpty() && state == State.LOADING) View.VISIBLE else View.GONE
binder.txtError.visibility =
if (mainViewModel.listIsEmpty() && state == State.ERROR) View.VISIBLE else View.GONE
if (!mainViewModel.listIsEmpty()) {
newsListAdapter.setState(state ?: State.DONE)
}
})
}
ViewModel
private var pagedUserList: LiveData<PagedList<User>>
private val compositeDisposable = CompositeDisposable()
private val pageSize = 10
private val usersDataSourceFactory: UsersDataSourceFactory
init {
usersDataSourceFactory = UsersDataSourceFactory(usersService, compositeDisposable)
val config = PagedList.Config.Builder()
.setPageSize(pageSize)
.setInitialLoadSizeHint(pageSize * 3)
.setEnablePlaceholders(false)
.build()
pagedUserList = LivePagedListBuilder<Int, User>(usersDataSourceFactory, config)
.build();
}
fun getState(): LiveData<State> = Transformations.switchMap<UsersDataSource,
State>(usersDataSourceFactory.getUserLiveData(), UsersDataSource::state)
fun retry() {
usersDataSourceFactory.getUserLiveData().value?.retry()
}
fun listIsEmpty(): Boolean {
return pagedUserList.value?.isEmpty() ?: true
}
fun getPagedList(): LiveData<PagedList<User>> {
return pagedUserList
}
DataSourceFactory
class UsersDataSourceFactory(
private val usersService: UsersService,
private val compositeDisposable: CompositeDisposable
) : DataSource.Factory<Int, User>() {
private val usersDataSourceLiveData = MutableLiveData<UsersDataSource>()
override fun create(): DataSource<Int, User> {
val usersDataSource = UsersDataSource(usersService, compositeDisposable)
usersDataSourceLiveData.postValue(usersDataSource)
return usersDataSource
}
fun getUserLiveData() = usersDataSourceLiveData
}
DataSource
class UsersDataSource(
private val usersService: UsersService,
private val compositeDisposable: CompositeDisposable
) : PageKeyedDataSource<Int, User>() {
var state: MutableLiveData<State> = MutableLiveData()
private var retryCompletable: Completable? = null
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Int, User>
) {
updateState(State.LOADING)
compositeDisposable.add(
usersService.getUsers(0, 20)
.subscribe({ response: List<User> ->
updateState(State.DONE)
val nextPageKey = response[response.size - 1].id
callback.onResult(
response, null, nextPageKey
)
}, { t: Throwable? ->
updateState(State.ERROR)
setRetry(Action { loadInitial(params, callback) })
})
)
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, User>) {
updateState(State.LOADING)
compositeDisposable.add(
usersService.getUsers(params.key + 1, 20)
.subscribe({ response: List<User> ->
updateState(State.DONE)
if (!response.isEmpty()) {
val nextPageKey = response[response.size - 1].id
callback.onResult(response, nextPageKey)
}else {
updateState(State.ERROR)
setRetry(Action { loadAfter(params, callback) })
}
}, { t: Throwable? ->
updateState(State.ERROR)
setRetry(Action { loadAfter(params, callback) })
})
)
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, User>) {
}
private fun updateState(state: State) {
this.state.postValue(state)
}
fun retry() {
if (retryCompletable != null) {
compositeDisposable.add(
retryCompletable!!
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
)
}
}
private fun setRetry(action: Action?) {
retryCompletable = if (action == null) null else Completable.fromAction(action)
}
}
enum class State {
DONE, LOADING, ERROR
}
I am making a messenger app on Kotlin and encountered this problem,
2020-11-18 14:20: 41.163 18185-18185/com.example.vinco E/zzf: Failed to get reCAPTCHA token - calling backend without app verification
I'll leave the class codes below
class EnterPhoneNumberFragment : Fragment(R.layout.fragment_enter_phone_number) {
private lateinit var mPhoneNumber:String
private lateinit var mCallback:PhoneAuthProvider.OnVerificationStateChangedCallbacks
override fun onStart() {
super.onStart()
AUTH = FirebaseAuth.getInstance()
initFirebase() ////////////////////////////////////////////////////////////////////////////////
mCallback = object :PhoneAuthProvider.OnVerificationStateChangedCallbacks(){
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
AUTH.signInWithCredential(credential)
.addOnCompleteListener { task ->
if(task.isSuccessful){
showToast("Добро пожаловать")
(activity as RegisterActivity).replaceActivity(MainActivity())
}else showToast(task.exception?.message.toString())
}
}
override fun onVerificationFailed(p0: FirebaseException) {
// showToast(p0.message.toString())
}
override fun onCodeSent(id: String, token: PhoneAuthProvider.ForceResendingToken) {
replaceFragment(EnterCodeFragment(mPhoneNumber,id))
}
}
register_btn_next.setOnClickListener{ sendCode()}
}
private fun sendCode(){
if (register_input_phone_number.text.toString().isEmpty()){
showToast(getString(R.string.register_toast_enter_phone))
}else{
authUser()
}
}
private fun authUser() {
mPhoneNumber = register_input_phone_number.text.toString()
PhoneAuthProvider.verifyPhoneNumber(PhoneAuthOptions.newBuilder(FirebaseAuth.getInstance())
.setPhoneNumber(mPhoneNumber)
.setTimeout(60L,
TimeUnit.SECONDS)
.setActivity(activity as RegisterActivity)
.setCallbacks(mCallback)
.build()
)
}
}
class EnterCodeFragment(private val phoneNumber: String, val id: String) : Fragment(R.layout.fragment_enter_code) {
override fun onStart() {
super.onStart()
(activity as RegisterActivity).title = phoneNumber
register_input_code.addTextChangedListener ( AppTextWatcher{
initFirebase()
val string = register_input_code.text.toString()
if(string.length==6) {
enterCode()
}
} )
}
private fun enterCode(){
val code = register_input_code.text.toString()
val credential:PhoneAuthCredential = PhoneAuthProvider.getCredential(id, code)
AUTH.signInWithCredential(credential).addOnCompleteListener { task ->
if(task.isSuccessful){
val uid:String = AUTH.currentUser?.uid.toString()
val dateMap = mutableMapOf<String, Any>()
dateMap[CHILD_ID] = uid
dateMap[CHILD_PHONE] = phoneNumber
dateMap[CHILD_USERNAME] = uid
REF_DATABASE_ROOT.child(NODE_USERS).child(uid).updateChildren(dateMap)
.addOnCompleteListener{ task2 ->
if (task2.isSuccessful) {
showToast("Добро пожаловать")
(activity as RegisterActivity).replaceActivity(MainActivity())
}else showToast(task2.exception?.message.toString())
}
}
}
}
}