Pass string from onLoadFinished method to another activity - java

I have created a content retriever which calls data from the content provider in another application, I can read the data successfully, but I want to pass the data from the onLoadFinished Method in my content retriever to an activity which should display the data on button press. But I am unsure of how to do it, I tried using a getter and setter, but it does not print anything.
Here are the rows in my content provider:
String[] columns = new String[]{"_id", "item"};
MatrixCursor matrixCursor = new MatrixCursor(columns);
matrixCursor.addRow(new Object[]{1, "FOUND"});
//Test is a string object that gets serialized to Json
matrixCursor.addRow(new Object[]{2, new Gson().toJson(test)});
matrixCursor.setNotificationUri(getContext().getContentResolver(), uri);
return matrixCursor;
I want to get the data from the 2nd row and this is what I have in my onLoadFinished in my Retriever class:
class Retriever : LoaderManager.LoaderCallbacks<Cursor>{
private var dataFromJson = ""
//getters and setters
fun setData(data: String){
this.dataFromJson = data
}
fun getData():String{
return dataFromJson
}
//other Loader method such as onCreateLoader, etc.
override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) {
try
{
val success: Boolean
if (data != null && data.getCount() > 0)
{
data.moveToFirst()
val dataString = data.getString(1)
//this works, I am able to find the "found" text at the first row
success = dataString.equals("FOUND", ignoreCase = true)
//move to 2nd row
data.moveToNext()
//get data from 2nd row does not work
val JSonData = data.getString(1)
setData(JSonData)
}
else
{
Timber.d( "onLoadFinished Unknown error occurred")
success = false
}
//custom interface listener
listener.onClassListener(success)
}
catch (e:Exception) {
Timber.d("Error")
}
}
}
And in my activity when I create a Retriever object and call the getData() method, it displays nothing.
class TestActivity{
private lateinit var dataRetriever: Retriever
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_activity)
//I have a textView called dataTxt
//displays nothing
dataTxt.text = dataRetriever.getData()
}
}
Any help or advice will be highly appreciated

This is because of the loading task is an async operation, when you call the getData() function, the value of dataFromJson is probably still an empty string.
To easily deal with this, you can declare an interface like this:
interface OnDataRetrieveListener{
fun onDataLoad(dataString: String)
}
and then create an instance of it inside the Retriever
private lateinit var listener: OnDataRetrieveListener
fun setListener(listener: OnDataRetrieveListener){
this.listener = listener
}
set the listener in your activity (if anonymously, it's like below):
dataRetriever.setListener(object: Retriever.Listener{
override fun onDataLoad(data: String) {
dataTxt.text = dataRetriever.getData()
}
})
callback the listener when the string is loaded in Retriever:
...
val jsonData = data.getString(1)
listener.onDataLoad(jsonData)
...
then the dataTxt will update

Related

How to retrieve Firestore data ID on each different Wallpaper?

I found a problem when (all) wallpaper is clicked, then the retrieved data is not the data it should be. Here's the application..
What the app looks like:
This is my firestore data with different name and secretariat in each wallpaper:
This is the code to retrieve data from the firestore into textViewContributor and textViewSekretariat located in FinalWallpaperActivity
lateinit var binding: ActivityInfoBinding
private var firestore = FirebaseFirestore.getInstance()
private lateinit var tvInputKontributor: TextView
private lateinit var tvInputSekretariat: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityInfoBinding.inflate(layoutInflater)
setContentView(binding.root)
tvInputKontributor = findViewById(R.id.inputKontributor)
tvInputSekretariat = findViewById(R.id.inputSekretariat)
firestore.collection("wallpaper")
.get()
.addOnSuccessListener { result ->
for (document in result) {
Log.d(TAG, "${document.id} => ${document.data}")
if (document != null) {
val name = document.data["name"].toString()
val sekretariat = document.data["sekretariat"].toString()
tvInputKontributor.text = name
tvInputSekretariat.text = sekretariat
}
}
}
.addOnFailureListener { exception ->
Log.d(TAG, "Error getting documents: ", exception)
}
This is the btnInfo declaration to open the InfoActivity
btnInfo.setOnClickListener {
val i = Intent(this, InfoActivity::class.java)
startActivity(i)
}
How to make each wallpaper have a different info description according to the clicked wallpaper? For my case all wallpapers have the same info description.. Whereas every contributor to the wallpaper should be different
you should be learning how to convert from json into array in kotlin ways and you should be working on Parcelable Data Class for eficiency data transfer.
Each pressed button/list item should passing a certain data object includes Contributor, Document ID,etc to a detail screen using Parcelable Data Class.

SearchView suggestions inside recyclerview (with API call onQueryTextChange)

I would like to display suggestions in recyclerView instead of embedded UI of the searchView (like on picture below)
Suggestions are loaded from API call on each text query change of my searchView.
Then, each time I receipt new suggestions, I'm executing an AsyncTask which populate the UI suggestions using Cursor
class FetchSearchUserTermSuggestionsTask(var searchview: SearchView) : AsyncTask<String?, Void?, Cursor>() {
override fun doInBackground(vararg params: String?): Cursor {
val cursor =
MatrixCursor(columnNamesArray)
var list : List<UserSuggestionDto>? = null
var call = RetrofitClient.userService.suggestionForTextSearch(0, 50, params[0]!!)
val response = call.execute()
if (response.isSuccessful) {
list = response.body()!!
} else {
//todo : throw exception
Log.e(EventAddFragment.TAG, response.message())
Log.e(EventAddFragment.TAG, response.errorBody().toString())
}
// parse your search terms into the MatrixCursor
if(list != null) {
for (index in list.indices) {
val term = list[index].fullname
val image = list[index].profilePic
val id = list[index].id
val row = arrayOf(index, id, image, term)
cursor.addRow(row)
}
}
return cursor
}
override fun onPostExecute(result: Cursor) {
searchview.suggestionsAdapter.changeCursor(result)
}
companion object {
private val columnNamesArray = arrayOf(BaseColumns._ID,
UserSuggestionDto.SUGGEST_COLUMN_ID,
UserSuggestionDto.SUGGEST_COLUMN_IMAGE,
UserSuggestionDto.SUGGEST_COLUMN_FULLNAME)
}
}
Finally, I prefer to populate a recyclerView in a dedicated fragment for research of user.
Is There a specific way to do it ?
My fears are :
RecyclerView is not designed to use Cursor and AsyncTask (Contrary to the searchView suggestions adapter)
So how to populate a recycler view from searchView on each text query change (by calling api each time) properly ?
Thanks in advance

How to use interfaces as listeners from and Dialoge in Android?

I don't know how I can use a interface in Android as an event listener. I defined an interface ProfilePictureTakenEvent with the method onProfilePictureTaken. In an dialog, which is opened by a button click on a fragment, the user can take an Image with the camera. I used the method startActivityForResult for it. The result (the image) of this action I get in the fragment (the overriden method onActivityResult).
Now my question: How can I show this picture in my dialog when I get the result in my fragment? I thought that I can use an event listener, but I don't know how and didn't find any Explanation.
Here my code:
UserFragment.tk
class UserFragment() : Fragment() {
private var userFragment: UserFragment? = null
private var profilePictureTakenEvent: ProfilePictureTakenEvent? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_user, container, false)
}
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?
) {
super.onViewCreated(view, savedInstanceState)
this.userFragment = this
val database = UserManagerDatabase(view.context)
val repository = UserRepository(database)
val factory = UserViewModelFactory(repository)
val viewModel = ViewModelProviders.of(this, factory).get(UserViewModel::class.java)
val adapter = UserAdapter(listOf(), viewModel)
rvUsers.layoutManager = LinearLayoutManager(view.context)
rvUsers.adapter = adapter
var emailAddresses: List<String> = emptyList()
viewModel.getAll().observe(viewLifecycleOwner, { userList ->
adapter.users = userList
emailAddresses = userList.map { user -> user.email }
adapter.notifyDataSetChanged()
})
viewModel.getEmailAddresses().observe(viewLifecycleOwner, {emailAddresses = it })
fBtnAddUser.setOnClickListener {
AddUserDialog(view.context,
object: UserCreateEvent {
override fun onAddButtonClicked(user: User) {
viewModel.insert(user)
}
}, emailAddresses, userFragment).show()
}
}
fun setProfilePictureTakenEvent(profilePictureTakenEvent: ProfilePictureTakenEvent) {
this.profilePictureTakenEvent = profilePictureTakenEvent
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == AddUserDialog.REQUEST_IMAGE_CAPTURE) {
this.profilePictureTakenEvent?.onProfilePictureTaken(data)
}
}
}
AddUserDialog.kt:
class AddUserDialog(
context: Context,
private var userCreateEvents: UserCreateEvent,
private var emails: List<String>,
private var parentFragment: UserFragment?
) : AppCompatDialog(context), ProfilePictureTakenEvent {
companion object {
const val REQUEST_IMAGE_CAPTURE = 1
}
private var imageData: Bitmap? = null
override fun onProfilePictureTaken(data: Intent?) {
this.ivProfilePicture.setImageBitmap(data?.extras?.get("data") as Bitmap)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.dialog_add_user)
setTitle(context.getString(R.string.title_add_user))
val editTextViews: Map<String, EditText> = mapOf(
"first_name" to dAddUserEdFirstName,
"last_name" to dAddUserEdLastName,
"email" to dAddUserEdEmail,
"password" to dAddUserEdPassword,
"password_confirmation" to dAddUserEdPasswordConfirmation
)
dAddUserBtnSave.setOnClickListener {
val firstName = dAddUserEdFirstName.text.toString()
val lastName = dAddUserEdLastName.text.toString()
val username = "${firstName.toLowerCase(Locale.GERMAN)}-${lastName.toLowerCase(Locale.GERMAN)}"
val email = dAddUserEdEmail.text.toString()
val password = dAddUserEdPassword.text.toString()
val passwordConfirmation = dAddUserEdPasswordConfirmation.text.toString()
var error = false
val emptyFields: StringBuilder = StringBuilder()
// Check if any EditText is empty
editTextViews.forEach {(key, editTextView) ->
val textEmpty: Boolean = editTextView.text.toString().isEmpty()
if (textEmpty) {
error = true
emptyFields.append("${context.getString(AppHelper.stringTranslation(key))}, ")
}
AppHelper.updateBackground(context, editTextView, if (textEmpty) R.drawable.input_field_error else R.drawable.input_field)
}
if (error) {
//AppHelper.showToastLong(context, context.getString(R.string.error_field_not_filled, AppHelper.commaStringToCorrectGrammatical(context, emptyFields.toString())))
return#setOnClickListener
}
// Check if E-Mail address is valid
if (AppHelper.invalidEmail(email)) {
AppHelper.showToastShort(context, R.string.error_invalid_email)
return#setOnClickListener
}
// Check if supplied E-Mail address is unique
if (emails.contains(email)) {
AppHelper.showToastShort(context, R.string.error_email_already_taken)
return#setOnClickListener
}
// Check if password and password confirmation match
if (password != passwordConfirmation) {
AppHelper.showToastShort(context, R.string.error_password_confirmation_not_matching)
return#setOnClickListener
}
//TODO: Password hashing
// Creating the User
val user = User(firstName, lastName, username, email, password)
userCreateEvents.onAddButtonClicked(user)
// Closing the dialog after calling the onClickEvent and data is valid
dismiss()
}
btnTakeProfilePicture.setOnClickListener {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
parentFragment?.startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
} catch (e: ActivityNotFoundException) {
AppHelper.showToastLong(context, "Fehler")
}
}
// Close dialog when cancel Button is clicked
dAddUserBtnCancel.setOnClickListener { cancel() }
}
}
ProfilePictureTakenEvent.kt:
interface ProfilePictureTakenEvent {
fun onProfilePictureTaken(data: Intent?)
}
As far I understand, your dialog is still shown after capture the image,
As are create the dialog from that fragment, one of the easy solution can be: hold the object in a global variable, can call a function to show that capture images.
Like:
val dialog = AddUserDialog(....)
inside
onActivityResult(..)
...
dialog.updateImage(data)
Thus actually you will not need any listeners to show the image at dialog.

Add a Intent in a class

I need to start a new activity to show an image in Fullscreen and my Intent is in a class but outside my main class.
class PhotoItem(val user: String, val send: String, val timestamp: Long, val country: String): Item<GroupieViewHolder>(){
override fun bind(viewHolder: GroupieViewHolder, position: kotlin.Int) {
viewHolder.itemView.textView10.text = user
viewHolder.itemView.textView13.text = timestamp.toString()
viewHolder.itemView.textView14.text = country
val uri = send
val targetImageView = viewHolder.itemView.selectphoto_imageview
val targetImageViewFullScreen = viewHolder.itemView.fullscreen
Picasso.get().load(uri).into(targetImageView)
viewHolder.itemView.setOnClickListener{v : View ->
v.getContext().startActivity(Intent(v.getContext(), FullscreenPhoto::class.java))
}
}
override fun getLayout(): kotlin.Int {
return R.layout.photo_from_row
}
}
So I found this line : v.getContext().startActivity(Intent(v.getContext(), FullscreenPhoto::class.java)) because I can't create a basic Intent : val intent = Intent(this,Home::class.java)startActivity(intent)
And I need to have the val uri for load the image into my Image view
thank you in advance.
I guess the issue is that startActivity is not available in PhotoItem
So change GroupieViewHolder class and add activity in constructor like this:
class GroupieViewHolder(val activity: Activity, /* other arguments */)
Now use that field in bind of PhotoItem as below:
override fun bind(viewHolder: GroupieViewHolder, position: kotlin.Int) {
// other codes.........
viewHolder.itemView.setOnClickListener{v : View ->
// using activity field for startActivity
viewHolder.activity.startActivity(Intent(v.getContext(), FullscreenPhoto::class.java))
}

RecyclerView throwing IndexOutOfBoundsException when trying to insert multiple items and notifying the adapter

I am trying to get unique numbers from call logs of a user and show them in a RecyclerView. By unique I mean if I have shown the number, or corresponding contact once in the list, I have to skip the item. And I want to use a Cursor for showing this to make the rendering quick.
To do this, I have implemented a RecyclerView Adapter which looks like this (Only showing the relevant code, kotlin)
class CallAdapter(val activity: Activity, var cursor: Cursor) : RecyclerView.Adapter<CallAdapter.ViewHolder>() {
val handler = Handler()
val contacts = ArrayList<Contact>()
init {
cursor.moveToFirst()
UniqueLogs(this).start()
}
companion object {
const val TAG = "CallAdapter"
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(activity)
val binding = CallLogItemBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun getItemCount(): Int {
return contacts.count()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val contact = contacts[position]
...
cursor.moveToNext()
}
class ViewHolder(val binding: CallLogItemBinding) : RecyclerView.ViewHolder(binding.root)
class UniqueLogs(val adapter: CallLogAdapterShobhit) : Thread() {
private var available = true
private val cursor = adapter.cursor
private val hash: HashMap<String, Int> = HashMap<String, Int>()
private val contacts = adapter.contacts
private var count = 0
override fun run() {
cursor.moveToFirst()
while (available && cursor.moveToNext()) {
var contact = adapter.getCurrentItemContact()
while (available && hash[contact.phoneNumber] == 1) {
if (cursor.moveToNext()) {
contact = adapter.getCurrentItemContact()
} else {
available = false
}
}
if (available) {
contacts.add(contact)
val position = count
count += 1
adapter.handler.post({
Log.d(TAG, "Position: $position Size: ${adapter.itemCount}")
adapter.notifyItemInserted(position)
})
hash[contact.phoneNumber] = 1
}
}
}
}
}
When I run this code, I am getting an exception which looks like this :-
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{c75176c position=5 id=-1, oldPos=4, pLpos:4 scrap [attachedScrap] tmpDetached not recyclable(1) no parent}
The position, id, oldPos, pLpos, etc. are different every time but the error is essentially the same.
I've tried writing logs as well to make sure that I am not trying to insert an element before the element is added to contacts array list, but the logs say that all's well.
Any idea what might be wrong here?
You are updating the data in a background thread, but notifying on the main thread. By the time the notification happens, you may have further altered the data. I would suggest that you do all your updates on the background and then just do a generic notifyDataSetChanged when everything is done.
Another option would be to calculate the entire new list in a background thread and then update the real data once and notify in the main thread.
Modifying the data and calling notifyItemInserted should synchronously or you will have inconsistent data.
Putting contacts.add(contact) just before notifyItemInserted should solve it.
adapter.handler.post({
Log.d(TAG, "Position: $position Size: ${adapter.itemCount}")
contacts.add(contact)
adapter.notifyItemInserted(position)
})

Categories

Resources