How to copy values from a recyclerview to clipboard in kotlin? - kotlin

I'm trying to copy text in recyclerview to clipboard. can anyone help me.
val myClipboard = rvItemsList.getContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val myClip = ClipData.newPlainText("label", rvItemsList.toString())
myClipboard.setPrimaryClip(myClip)
this is the code i used. when trying to print i'm getting result
I/System.out: ClipData { text/plain "label" {T:androidx.recyclerview.widget.RecyclerView{1d0e7d4 VFED..... ........ 55,809-1025,1768 #7f08015b app:id/rvItemsList}} }
values in the recycler view is productname and rate
image of app with recyclerview

The issue here that when you call rvItemsList.toString it will return default toString() method on the object.The output is, class name, then ‘at’ sign, and at the end hashCode of object.
If want to copy value just add click listener on your textview. e.g.:
textView.setText("your_value")
textView.setOnClickListener {
val myClipboard = rvItemsList.getContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val myClip = ClipData.newPlainText("label", your_value) myClipboard.setPrimaryClip(myClip)
}

Related

How to navigate from one recyclerview to another based on the clicked date

So I am building this basic weight-tracking app with a Room database, and two recyclerviews.
I am trying to learn how to navigate from the starting recyclerview (which shows a list of the
lowest weight entries of each date) to another recyclerview that shows every entry that was listed
on that date that was clicked in the first recyclerview.
I can only see the results I want if I manually set the date in the viewmodel.
I can't seem to figure out how to achieve this without manually setting the date and I was hoping
someone could help.
I have tried countless times to research this but all I find is how to navigate to a specific
item. Any help is definitely appreciated.
Here are my relevant code snippets.
My Dao Query:
#Query(
"SELECT date_of_weight_entry as date, weight_entry as weight, day_of_weight_entry as day, \n" +
" weight_entry_notes as notes, \n" +
" time_of_weight_entry as time FROM weight \n" +
" where date = :dateOfEntry"
)
fun getAllEntriesForEachDateFragment(dateOfEntry: String): Flow<List<WeightEntriesForEachDate>>
The relevant code from my viewmodel:
var _dateOfEntry = MutableLiveData<String>()
var dateOfEntry: LiveData<String> = _dateOfEntry
var entriesForEachDate: LiveData<List<WeightEntriesForEachDate>> = weightDao
.getAllEntriesForEachDateFragment(_dateOfEntry.value.toString()).asLiveData()
fun getSelectedDate(selectedDate: String):String {
_dateOfEntry.value = selectedDate
return selectedDate
}
This is from the first recyclerview adapter's on Bind View Holder:
(When using Logcat, the dateOfEntry in viewmodel is set correctly, but results do not get
displayed correctly in the next recyclerview after being clicked),
(disregard the navigation arg as it used for a different part of the app):
override fun onBindViewHolder(holder: MinWeightRecordViewHolder, position: Int) {
val current = getItem(position)
holder.itemView.setOnClickListener {
viewModel.getSelectedDate(holder.itemView.tv_weight_record_date.text.toString())
val action = LowestWeightForEachDayFragmentDirections
.actionLowestWeightForEachDayFragmentToEntriesByDateFragment(
date = viewModel.dateOfEntry.value.toString() <-DISREGARD THIS
)
holder.itemView.findNavController().navigate(action)
}
Here is the OnViewCreated code from my entries by date fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val dateAdapter = EntriesByDateAdapter(viewModel)
binding.rvWeightRecordByEntryForListedDate.adapter = dateAdapter
viewModel.entriesForEachDate.observe(this.viewLifecycleOwner) { entries ->
entries
dateAdapter.submitList(entries)
}
binding.rvWeightRecordByEntryForListedDate.layoutManager =
LinearLayoutManager(this.context)
}
Now if I go back into my viewmodel and manually set the date (like below) it gives me the
correct info in the second recyclerview:
var _dateOfEntry = MutableLiveData<String>("02-03-2023")
var dateOfEntry: LiveData<String> = _dateOfEntry
I just need help figuring out what step I am missing. Hopefully these code snippets were enough.

Jetpack compose and Kotlin, dynamic UI losing values on recomp

I am making a dynamic UI using kotlin and Jetpack compose and storing the information in an object box database.
The aim is that i will have a composable that starts off with 1 initial item that is empty and when the contents of the textbox have been filled in would allow the red "+" button to be clicked and then another textfield would appear. These values will need to be able to be edited constantly all the way until the final composable value is stored. The button changes colour currently and the states are fine with the button so i can add and remove rows
The data comes in as a string and is converted into a Hashmap<Int, String>. The int is used to store the position in the map being edited and the string would be the text value.
Using log messages i see that the information is updated in the list and for recomp sake i instantly store the value of the edited list in a converted json string.
At the moment:
When i scroll past the composable it resets and looks like the initial state (even if i have added multiple rows)
Log messages show that my hashmap has the values from before e.g. {"0":"asdfdsa"} but the previous positions are ignored and as the previous information would still be present but not shown on the UI when i enter it into the first field again (the others are not visible at the time) {"0":"asdfdsa","0":"hello"}. This would later cause an error when trying to save new data to the list because of the duplicate key
In the composables my hashmap is called textFields and is defined like this. Number is used to determine how many textfields to draw on the screen
val textFields = remember { getDataStringToMap(data.dataItem.dataValue) }
val number = remember { mutableStateOf(textFields.size) }
the method to getDataStringToMap is created like this
private fun getDataMapToString(textFieldsMap: HashMap<Int, String>): String {
val gson = Gson()
val newMap = hashMapOf<Int, String>()
for (value in textFieldsMap){
if (value.value .isNotBlank()){
newMap[value.key] = value.value
}
}
return gson.toJson(newMap)
}
and the method to getDataStringToMap is created like this (I explicitly define the empty hashmap type because its more readable for me if i can see it)
private fun getDataStringToMap(textsFieldsString: String): HashMap<Int, String> {
val gson = Gson()
return if (textsFieldsString.isBlank()) {
hashMapOf<Int, String>(0 to "")
} else {
val mapType = HashMap<Int, String>().javaClass
gson.fromJson(textsFieldsString, mapType)
}
the composables for the textfields are called like this
items(number.value) { index ->
listItem(
itemValue = textFields[index].orEmpty(),
changeValue = {
textFields[index] = it
setDataValue(getDataMapToString(textFields))
},
addItem = {
columnHeight.value += itemHeight
scope.launch {
scrollState.animateScrollBy(itemHeight)
}
},
deleteItem = {
columnHeight.value -= itemHeight
scope.launch {
scrollState.animateScrollBy(-itemHeight)
}
},
lastItem = index == number.value - 1,
index = index
)
}
Edited 30/12/2022
Answer from #Arthur Kasparian solved issues. Change to rememberSaveable retains the UiState even on scroll and recomp.
Now just to sort out which specific elements are removed and shown after :D
The problem is that remember alone does not save values on configuration changes, whereas rememberSaveable does.
You can read more about this here.

Add a PsiElement without adding text to the PsiFile

I'm trying to add a method (PsiMethod) to a class (PsiClass) so that IDEA shows this method when typing. I did this, but I ran into a problem: when I add PsiMethod to PsiClass, the text of this method appears in the file, and I don't need it. I need to add a method so that it is highlighted by IDEA, but it is not displayed in the file as text.
How can this be done?
Here is my code how I add PsiMethod to PsiClass:
val module = ModuleManager.getInstance(project).modules.first()
val file = FilenameIndex
.getFilesByName(
project,
"TestPsiFile.java",
module.moduleContentScope)
.first()
val newMethod = PsiElementFactory.getInstance(project).createMethod("testMethod", PsiType.VOID)
WriteCommandAction.runWriteCommandAction(project) {
file.children
.filter { it.elementType == JavaElementType.CLASS }
.map { it.add(newMethod) }
}
Link to this question in the Jetbrains Community: link

Use filtered dataProvider contents when FileDownloader is called in Vaadin

I'm trying to download a csv file after applying filters to the DataProvider.
For some reason the filtered results are shown in the Grid, but the downloaded csv file still contains all data.
#AutoView
class FinancialTransactionsView : VerticalLayout(), View {
private val grid: Grid<FinancialTransaction>
private val yearField: ComboBox<Int>
private val dataProvider = DataProvider.ofCollection(FinancialTransaction.findAll())
private val fileDownloader: FileDownloader
init {
label("Financial Transactions") {
styleName = ValoTheme.LABEL_H1
}
yearField = comboBox("Select Year") {
setItems(listOf(2016, 2017, 2018))
addSelectionListener {
// Filter the data based on the selected year
if (it.value != it.oldValue) setDataProvider()
}
}
// Create FileDownloader and initialize with all contents in the DataProvider
fileDownloader = FileDownloader(createCsvResource())
val downloadButton = button("Download csv") {
styleName = ValoTheme.BUTTON_PRIMARY
onLeftClick {
// The idea here is to assign values from the filtered DataProvider to the FileDownloader
fileDownloader.fileDownloadResource = createCsvResource()
}
}
fileDownloader.extend(downloadButton)
fileDownloader.fileDownloadResource = createCsvResource()
grid = grid(dataProvider = dataProvider) {
expandRatio = 1f
setSizeFull()
addColumnFor(FinancialTransaction::companyId)
addColumnFor(FinancialTransaction::fiscalYear)
addColumnFor(FinancialTransaction::fiscalPeriod)
addColumnFor(FinancialTransaction::currency)
addColumnFor(FinancialTransaction::finalizedDebitAmountInCurrency)
addColumnFor(FinancialTransaction::finalizedCreditAmountInCurrency)
appendHeaderRow().generateFilterComponents(this, FinancialTransaction::class)
}
}
private fun createCsvResource(): StreamResource {
return StreamResource(StreamResource.StreamSource {
val csv = dataProvider.items.toList().toCsv()
try {
return#StreamSource csv.byteInputStream()
} catch (e: IOException) {
e.printStackTrace()
return#StreamSource null
}
}, "financial_transactions.csv")
}
private fun setDataProvider() {
dataProvider.clearFilters()
if (!yearField.isEmpty)
dataProvider.setFilterByValue(FinancialTransaction::fiscalYear, yearField.value)
}
}
toCsv() is an extension function List<FinancialTransaction> which returns a string containing csv data.
What can I do to get the filtered results in my csv file?
val csv = dataProvider.items.toList().toCsv()
I am not Kotlin guy, but I assume dataProvider.items is a shorthand to dataProvider.getItems() in Java, i.e. this method (and you use ListDataProvider)
https://vaadin.com/download/release/8.4/8.4.1/docs/api/com/vaadin/data/provider/ListDataProvider.html#getItems--
In Vaadin getItems() returns all items by passing all filters.
So instead you should do either of the following
dataProvider.fetch(..)
https://vaadin.com/download/release/8.4/8.4.1/docs/api/com/vaadin/data/provider/DataProvider.html#fetch-com.vaadin.data.provider.Query-
Where you give the filters you want to apply in the query, or
grid.getDataCommunicator.fetchItemsWithRange(..)
https://vaadin.com/download/release/8.4/8.4.1/docs/api/com/vaadin/data/provider/DataCommunicator.html#fetchItemsWithRange-int-int-
Which returns list of items with filters you have set applied, which I think is ideal for you
Thank you for using Vaadin-on-Kotlin!
I've just updated the Databases Guide which should hopefully answer all of your questions. If not, just let me know and I'll update the guides accordingly.
The ListDataProvider.items will not apply any filters and will always return all items.
You need to use the getAll() extension function in order to obey the filters set by the Grid.
This is now explained in the Exporting data from DataProviders chapter of the Databases Guide.
In your code, both the grid and the yearField will set the filter to the same data provider,
thus overwriting values set by each other. Please read the Chaining Data Providers chapter in the Databases Guide to learn how to AND multiple filters set by multiple components.
When you use private val dataProvider = DataProvider.ofCollection(FinancialTransaction.findAll()), that will load all transactions from the database in-memory. You can use a more memory-efficient way: private val dataProvider = FinancialTransaction.dataProvider (given that FinancialTransaction is an Entity)
Please let me know if this answers your questions. Thanks!

Passing array of arrays with getParcelableArrayList to several instances of a same fragment

I have a TabLayout with a fragment in each tab. The first tab is a fragment instance, and the next has an instance of the same fragment:
adapter.addFragment(fragmentoFichaSerie.newInstance(ficha, cookie), "Ficha");
for (HTMLParser.temporada objTemporada : ficha.temporadas)
{
adapter.addFragment(fragmentoCapitulos.newInstance(objTemporada.capitulos, cookie), objTemporada.nombre);
}
"ficha.temporadas" contains "n" "capitulos" arrays. Each fragment receive one "capitulos" array.
fragmentoCapitulos contains a RecyclerView that i populate with an ArrayList of objects (each item get its data from one array's position).
In the fragment, i receive the array in the "newInstance" method and put it in a bundle with putParcelableArrayList.
public static fragmentoCapitulos newInstance(List<HTMLParser.capitulo> capitulos, String Cookie) {
fragmentoCapitulos fragment = new fragmentoCapitulos();
Bundle args = new Bundle();
args.putParcelableArrayList("capitulos", (ArrayList<HTMLParser.capitulo>) capitulos);
fragment.setArguments(args);
cookie = Cookie;
return fragment;
}
In onCreateView i get the content of the bundle:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.capitulos, container, false);
RecyclerView recycler = (RecyclerView) view.findViewById(R.id.recCapitulos);
TextView empty = (TextView) view.findViewById(R.id.empty);
ImageView imgEmpty = (ImageView) view.findViewById(R.id.imgEmpty);
ArrayList<HTMLParser.capitulo> capitulos;
capitulos = getArguments().getParcelableArrayList("capitulos");
My problem is if i'm in tab 2, i should receive the first array of "ficha.temporadas", but i receive the second array (the first tab contains another fragment that doesn't need any array).
I try to pass a void array to the first tab but doesn't work. What can i do to pass the correct array to each fragment?
Finally i found a solution. I was wrong with the problem, not a parcelable error.
When viewpager creates pages dynamically always generates pages at both sides of current page. This was doing incorrectly. After debuging much time and reading a lot of articles, i changed FragmentPagerAdapter by FragmentStatePagerAdapter and all works fine.
I hope this could be helpful for someone with same problem.