Can't select date with datepickerdialog - kotlin

I have some trouble using the DataPickerDialog in kotlin...
This is how I would like it to work:
First, when the user selects a date, it must update a textview and the dialog must close
But for now, when the user selects a date, nothing happens (apart from the update in the dialog itself).
Here is my code :
#AndroidEntryPoint
class AppointmentListFragment : Fragment() {
private var _binding: FragmentAppointmentListBinding? = null
private val binding: FragmentAppointmentListBinding
get() = _binding!!
private val viewModel: AppointmentMainViewModel by viewModels()
private val today = Calendar.getInstance()
private val day = today.get(Calendar.DAY_OF_MONTH)
private val month = today.get(Calendar.MONTH) + 1
private val year = today.get(Calendar.YEAR)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentAppointmentListBinding.inflate(inflater)
setDateText(
getString(
R.string.date_placeholder,
String.format("%02d", day),
String.format("%02d", month),
year
)
)
binding.appointmentDateButton.setOnClickListener {
val dpd = DatePickerDialog(
it.context,
{ datePicker, y, m, d ->
datePicker.minDate = System.currentTimeMillis()
setDateText(
getString(
R.string.date_placeholder,
String.format("%02d", d),
String.format("%02d", m),
y
)
)
},
year,
month,
day
)
dpd.show()
}
return binding.root
}
private fun setDateText(date: String) {
binding.appointmentDateButton.text = date
}
What am I missing ?
Thanks in advance

What do you get in setDateText? I copied almost all code and launched. Then got y, m, d inside onDateSet callback. So, a string
getString(
R.string.date_placeholder,
String.format("%02d", d),
String.format("%02d", m),
y
)
should be right (but I don't know what you have put in date_placeholder).
So, this string is passed to setDateText and is written in appointmentDateButton (19 05 2022). I don't know, what happened.
By the way, initial month is incremented by 1. It's a mistake (you open the picker on 19th' June).

Related

Kotlin: How to use higher order functions with OpenEndRange<LocalDate>

I want to use higher order functions like map for open end ranges.
val from = LocalDate.now().minusDays(10)
val to = LocalDate.now()
(from ..< to).forEach(::println)
I tried to copy an example for ClosedRange<LocalDate> but it does not work.
package de.otto.di.extensions
import java.time.LocalDate
class OpenEndRangeLocalDateIterator(
startDate: LocalDate,
private val endExclusive: LocalDate,
private val stepDays: Long
) : Iterator<LocalDate> {
private var currentDate = startDate
override fun hasNext() = currentDate.plusDays(stepDays) <= endExclusive
override fun next(): LocalDate {
val next = currentDate
currentDate = currentDate.plusDays(stepDays)
return next
}
}
#OptIn(ExperimentalStdlibApi::class)
class OpenEndLocalDateRange(
override val start: LocalDate,
override val endExclusive: LocalDate,
private val stepDays: Long = 1
) : Iterable<LocalDate>, OpenEndRange<LocalDate> {
override fun iterator(): Iterator<LocalDate> =
OpenEndRangeLocalDateIterator(start, endExclusive, stepDays)
infix fun step(days: Long) = OpenEndLocalDateRange(start, endExclusive, days)
}
infix operator fun LocalDate.rangeUntil(to: LocalDate): OpenEndLocalDateRange =
OpenEndLocalDateRange(this, to)
It is implemented for Int so I assume it must be possible somehow. How can I achieve this?
The issue here is that you've defined the operator function to return OpenEndRange<LocalDate> rather than OpenEndedLocalDateRange. If you change the return type of your operator function that should fix the issue.
The reason why it isn't working as is is because OpenEndRange doesn't have the higher order functions defined for it (ClosedRange doesn't have them defined as well). Int has it because the operators return an IntRange which indirectly extends Iterable<Int> via IntProgression and Iterable has these higher order functions defined, so, the only missing piece is failing to return the correct type from your operator function.

Can you change the color of a textview in a recyclerview adapter after a certain condition is met in Main Activity?

I have a basic function that displays the elapsed time every time the button is pressed. I cannot get the logic in MainActivity to transfer to the recyclerview adapter. I simply want the text output color to change to red after the time passes 5 seconds. I have tried to research how to do this for the past week and I cannot find the exact answer. I'm hoping someone can help.
I have tried it with and without the boolean in the data class. I wasn't sure if that was required.
Here is my code:
Main Activity:`
class MainActivity : AppCompatActivity() {
var startTime = SystemClock.elapsedRealtime()
var displaySeconds = 0
private lateinit var binding: ActivityMainBinding
private val secondsList = generateSecondsList()
private val secondsAdapter = Adapter(secondsList)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
recyclerView.adapter = secondsAdapter
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(false)
binding.button.setOnClickListener {
getDuration()
addSecondsToRecyclerView()
}
}
fun getDuration(): Int {
val endTime = SystemClock.elapsedRealtime()
val elapsedMilliSeconds: Long = endTime - startTime
val elapsedSeconds = elapsedMilliSeconds / 1000.0
displaySeconds = elapsedSeconds.toInt()
return displaySeconds
}
private fun generateSecondsList(): ArrayList<Seconds> {
return ArrayList()
}
fun addSecondsToRecyclerView() {
val addSeconds =
Seconds(getDuration(), true)
secondsList.add(addSeconds)
secondsAdapter.notifyItemInserted(secondsList.size - 1)
}
}
Adapter:
var adapterSeconds = MainActivity().getDuration()
class Adapter(
private val rvDisplay: MutableList<Seconds>
) : RecyclerView.Adapter<Adapter.AdapterViewHolder>() {
class AdapterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView1: TextView = itemView.tv_seconds
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AdapterViewHolder {
val myItemView = LayoutInflater.from(parent.context).inflate(
R.layout.rv_item,
parent, false
)
return AdapterViewHolder(myItemView)
}
override fun onBindViewHolder(holder: Adapter.AdapterViewHolder, position: Int) {
val currentDisplay = rvDisplay[position]
currentDisplay.isRed = adapterSeconds > 5
holder.itemView.apply {
val redColor = ContextCompat.getColor(context, R.color.red).toString()
val blackColor = ContextCompat.getColor(context, R.color.black).toString()
if (currentDisplay.isRed) {
holder.textView1.setTextColor(redColor.toInt())
holder.textView1.text = currentDisplay.rvSeconds.toString()
} else {
holder.textView1.setTextColor(blackColor.toInt())
holder.textView1.text = currentDisplay.rvSeconds.toString()
}
}
}
override fun getItemCount() = rvDisplay.size
}
Data Class:
data class Seconds(
var rvSeconds: Int,
var isRed: Boolean
)
when you call secondsList.add(addSeconds) then the data that is already inside secondsList should be updated too.
you could do something like
private var secondsList = generateSecondsList() // make this var
fun addSecondsToRecyclerView() {
val addSeconds =
Seconds(getDuration(), true)
secondsList.add(addSeconds)
if ( /* TODO check if time has passed */) {
secondsList = secondsList.map { it.isRed = true }
secondsAdapter.rvDisplay = secondsList // TODO also make rvDisplay a var
secondsAdapter.notifyDatasetChanged() // also need to tell rv to redraw the all views
} else {
secondsAdapter.notifyItemInserted(secondsList.size - 1)
}
}
that might work, but to be honest it looks bad... There is already a lot of logic inside Activity. Read about MVVM architecture and LiveData, there should be another class called ViewModel that would keep track of time and the data. Activity should be as simple as possible, because it has lifecycle, so if you rotate the screen, all your state will be lost.
Your code isn't really working because of this:
var adapterSeconds = MainActivity().getDuration()
override fun onBindViewHolder(holder: Adapter.AdapterViewHolder, position: Int) {
...
currentDisplay.isRed = adapterSeconds > 5
...
}
You're only setting adapterSeconds right there, so it never updates as time passes. I assume you want to know the moment 5 seconds has elapsed, and then update the RecyclerView at that moment - in that case you'll need some kind of timer task that will fire after 5 seconds, and can tell the adapter to display things as red. Let's deal with that first:
class Adapter( private val rvDisplay: MutableList ) : RecyclerView.Adapter<Adapter.AdapterViewHolder>() {
private var displayRed = false
set(value) {
field = value
// Refresh the display - the ItemChanged methods mean something about the items
// has changed, rather than a structural change in the list
// But you can use notifyDataSetChanged if you want (better to be specific though)
notifyItemRangeChanged(0, itemCount)
}
override fun onBindViewHolder(holder: Adapter.AdapterViewHolder, position: Int) {
if (displayRed) {
// show things as red - you shouldn't need to store that state in the items
// themselves, it's not about them - it's an overall display state, right?
} else {
// display as not red
}
}
So with that setter function, every time you update displayRed it'll refresh the display, which calls onBindViewHolder, which checks displayRed to see how to style things. It's better to put all this internal refreshing stuff inside the adapter - just pass it data and events, let it worry about what needs to happen internally and to the RecyclerView it's managing, y'know?
Now we have a thing we can set to control how the list looks, you just need a timer to change it. Lots of ways to do this - a CountdownTimer, a coroutine, but let's keep things simple for this example and just post a task to the thread's Looper. We can do that through any View instead of creating a Handler:
// in MainActivity
recyclerView.postDelayed({ secondsAdapter.displayRed = true }, 5000)
That's it! Using any view, post a delayed function that tells the adapter to display as red.
It might be more helpful to store that runnable as an object:
private val showRedTask = Runnable { secondsAdapter.displayRed = true }
...
recyclerView.postDelayed(showRedTask, 5000)
because then you can easily cancel it
recyclerView.removeCallbacks(showRedTask)
Hopefully that's enough for you to put some logic together to get what you want. Set displayRed = false to reset the styling, use removeCallbacks to cancel any running task, and postDelayed to start a new countdown. Not the only way to do it, but it's pretty neat!
I finally figured it out using a companion object in Main Activity with a boolean set to false. If the time exceeded 5 seconds, then it set to true.
The adapter was able to recognize the companion object and change the color of seconds to red if they exceeded 5.

Removing an item from RecyclerView leaves a gap?

I have an activity which displays reservation list
The reservation list is stored in SQLite, which works OK
override fun onCreate(savedInstanceState: Bundle?) {
var reservationList = arrayListOf<ReservationModel>()
reservationList.addAll(reservationDB!!.reservationDao().getReservationList())
reservationAdapter = reservationListAdapter(reservationList, this#ReservationListActivity)
val llm01 = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, true)
reservation_recview.layoutManager = llm01
reservation_recview.adapter = reservationAdapter
}
Then I want to remove "Zenbu City". This is the code:
reservationDB.reservationDao().deleteReservation(selectedReservation)
val newReservationList = arrayListOf<ReservationModel>()
newReservationList.addAll(reservationDB.reservationDao().getAllReservations())
reservationAdapter.removeItemAndRefresh(selectedReservation, newReservationList)
This is how removeItemAndRefresh() implemented in ReservationAdapter:
fun removeItemAndRefresh(selectedItem: ReservationModel, newList: ArrayList<ReservationModel>){
theList.remove(selectedItem)
theList.clear()
theList.addAll(newList)
notifyDataSetChanged()
}
The result is this:
See a noticable gap/empty space on the RecyclerView? How to fix that?

Kotlin:How to make a popup date pick when click the EditText

I would love to make a popup datepicker ,while click the EditText ,And the date can be set to different languages according to the device 's language .I will need it use Kotlin.
More or less like as below :
Could anyone show me an example please ?
Thank you so much in advance !
I have tried this link ,but apppears two problems:
The pop up dialog design is not what I expected .
2.The selected result can't fill into the EditText
https://developer.android.com/guide/topics/ui/controls/pickers
DatePickerActivity.kt
class DatePickerActivity : AppCompatActivity() {
val myCalendar: Calendar = Calendar.getInstance()
lateinit var edt : EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_date_picker)
val day :Int = myCalendar.get(Calendar.DAY_OF_MONTH)
val month:Int = myCalendar.get(Calendar.MONTH)
val year :Int = myCalendar.get(Calendar.YEAR)
edt = findViewById(R.id.edt_birthday)
edt.setOnClickListener {
showDatePickerDialog(it)
edt.setText("" + day+ " " + month+1 + ", " + year)
}
}
fun showDatePickerDialog(v: View){
val newFragment = DatePickerFragment()
newFragment.show(supportFragmentManager,"datePicker")
}
}
DatePickerFragment.kt
import android.app.DatePickerDialog
import android.app.Dialog
import android.os.Bundle
import android.widget.DatePicker
import androidx.fragment.app.DialogFragment
import java.util.*
class DatePickerFragment : DialogFragment(), DatePickerDialog.OnDateSetListener {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Use the current date as the default date in the picker
val c = Calendar.getInstance()
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
// Create a new instance of DatePickerDialog and return it
return DatePickerDialog(requireActivity(), this, year, month, day)
}
override fun onDateSet(view: DatePicker, year: Int, month: Int, day: Int) {
}
}
And it turns out like this :
The default DatePicker looks like a calendar. You can change the behavior back to the old spinner style by adding this extra style in themes.xml.
<style name="MyDatePickerStyle" parent="android:Widget.Material.DatePicker">
<item name="android:datePickerMode">spinner</item>
</style>
and then inside your style for the app's theme, add this line:
<item name="android:datePickerStyle">#style/MyDatePickerStyle</item>
Note that the calendar style is the default because it has been found to be preferred by most users.

When trying to implement Branch.io deep-linking using their share sheet, no action executes

I am trying to implement deep linking with branch and having issues with the Share Sheet. It just doesn't work. No matter what I click, the relevant action doesn't happen and it just goes back to the bottom of the screen. Even when I click copy, the text doesn't copy. there are no errors so I don't know what's wrong.
This is my code (it is a single item in a recyclerView. I am using GroupieAdapter):
class SingleCommunityOption(val community: Community, val activity : MainActivity) : Item<ViewHolder>() {
private lateinit var buo: BranchUniversalObject
private lateinit var lp: LinkProperties
override fun getLayout(): Int {
return R.layout.community_option_layout
}
override fun bind(viewHolder: ViewHolder, position: Int) {
val firebaseAnalytics = FirebaseAnalytics.getInstance(viewHolder.root.context!!)
val title = viewHolder.itemView.community_option_title
val description = viewHolder.itemView.community_option_description
val memberCount = viewHolder.itemView.community_option_members_count
val share= viewHolder.itemView.community_share
title.text = community.title
description.text = community.description
memberCount.text = "${community.members}"
buo = BranchUniversalObject()
.setCanonicalIdentifier(community.id)
.setTitle(community.title)
.setContentDescription("")
.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setContentMetadata(ContentMetadata().addCustomMetadata("type", "community"))
lp = LinkProperties()
buo.listOnGoogleSearch(viewHolder.root.context)
share.setOnClickListener {
val ss = ShareSheetStyle(activity, "Republic invite", "Join me in this republic.")
.setCopyUrlStyle(activity.resources.getDrawable(android.R.drawable.ic_menu_send), "Copy", "Added to clipboard")
.setMoreOptionStyle(activity.resources.getDrawable(android.R.drawable.ic_menu_search), "Show more")
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK_MESSENGER)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.WHATS_APP)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.TWITTER)
.setAsFullWidthStyle(true)
.setSharingTitle("Share With")
buo.showShareSheet(activity, lp, ss, object : Branch.BranchLinkShareListener {
override fun onShareLinkDialogLaunched() {}
override fun onShareLinkDialogDismissed() {}
override fun onLinkShareResponse(sharedLink: String, sharedChannel: String, error: BranchError) {}
override fun onChannelSelected(channelName: String) {
firebaseAnalytics.logEvent("community_shared_$channelName", null)
}
})
}
}
}