mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Indicator in meetings list for today
This commit is contained in:
parent
a129cc5f95
commit
4368e1a5f7
8 changed files with 82 additions and 42 deletions
|
|
@ -21,8 +21,6 @@ import org.linphone.utils.HeaderAdapter
|
|||
class MeetingsListAdapter(
|
||||
private val viewLifecycleOwner: LifecycleOwner
|
||||
) : ListAdapter<MeetingModel, RecyclerView.ViewHolder>(MeetingDiffCallback()), HeaderAdapter {
|
||||
var selectedAdapterPosition = -1
|
||||
|
||||
val meetingClickedEvent: MutableLiveData<Event<MeetingModel>> by lazy {
|
||||
MutableLiveData<Event<MeetingModel>>()
|
||||
}
|
||||
|
|
@ -60,11 +58,6 @@ class MeetingsListAdapter(
|
|||
(holder as ViewHolder).bind(getItem(position))
|
||||
}
|
||||
|
||||
fun resetSelection() {
|
||||
notifyItemChanged(selectedAdapterPosition)
|
||||
selectedAdapterPosition = -1
|
||||
}
|
||||
|
||||
inner class ViewHolder(
|
||||
val binding: MeetingListCellBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
|
@ -73,24 +66,13 @@ class MeetingsListAdapter(
|
|||
with(binding) {
|
||||
model = meetingModel
|
||||
|
||||
val hasPrevious = bindingAdapterPosition > 0
|
||||
firstMeetingOfTheDay = if (hasPrevious) {
|
||||
val previous = getItem(bindingAdapterPosition - 1)
|
||||
previous.day != meetingModel.day || previous.dayNumber != meetingModel.dayNumber
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
lifecycleOwner = viewLifecycleOwner
|
||||
|
||||
binding.cardview.isSelected = bindingAdapterPosition == selectedAdapterPosition
|
||||
|
||||
binding.setOnClickListener {
|
||||
meetingClickedEvent.value = Event(meetingModel)
|
||||
}
|
||||
|
||||
binding.setOnLongClickListener {
|
||||
selectedAdapterPosition = bindingAdapterPosition
|
||||
binding.root.isSelected = true
|
||||
meetingLongClickedEvent.value = Event(meetingModel)
|
||||
true
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import org.linphone.databinding.MeetingsListFragmentBinding
|
|||
import org.linphone.ui.main.fragment.AbstractTopBarFragment
|
||||
import org.linphone.ui.main.meetings.adapter.MeetingsListAdapter
|
||||
import org.linphone.ui.main.meetings.viewmodel.MeetingsListViewModel
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.RecyclerViewHeaderDecoration
|
||||
import org.linphone.utils.hideKeyboard
|
||||
|
|
@ -97,6 +98,7 @@ class MeetingsListFragment : AbstractTopBarFragment() {
|
|||
adapter.submitList(it)
|
||||
Log.i("$TAG Meetings list ready with [${it.size}] items")
|
||||
|
||||
val newCount = it.size
|
||||
if (currentCount < it.size) {
|
||||
(view.parent as? ViewGroup)?.doOnPreDraw {
|
||||
startPostponedEnterTransition()
|
||||
|
|
@ -147,19 +149,12 @@ class MeetingsListFragment : AbstractTopBarFragment() {
|
|||
private fun scrollToToday() {
|
||||
Log.i("$TAG Scrolling to today's meeting (if any)")
|
||||
val todayMeeting = listViewModel.meetings.value.orEmpty().find {
|
||||
it.isToday
|
||||
it.displayTodayIndicator.value == true
|
||||
}
|
||||
val position = if (todayMeeting != null) {
|
||||
val index = listViewModel.meetings.value.orEmpty().indexOf(todayMeeting)
|
||||
Log.i(
|
||||
"$TAG Found (at least) a meeting for today [${todayMeeting.subject.value}] at index [$index]"
|
||||
)
|
||||
// Return the element before so today's event will be properly displayed (due to header)
|
||||
if (index > 0) index - 1 else index
|
||||
} else {
|
||||
Log.i("$TAG No meeting found for today")
|
||||
0 // TODO FIXME: improve by getting closest meeting
|
||||
}
|
||||
binding.meetingsList.smoothScrollToPosition(position)
|
||||
val index = listViewModel.meetings.value.orEmpty().indexOf(todayMeeting)
|
||||
(binding.meetingsList.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
|
||||
index,
|
||||
AppUtils.getDimension(R.dimen.meeting_list_decoration_height).toInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import org.linphone.utils.TimestampUtils
|
|||
class MeetingModel @WorkerThread constructor(conferenceInfo: ConferenceInfo) {
|
||||
val id = conferenceInfo.uri?.asStringUriOnly() ?: ""
|
||||
|
||||
private val timestamp = conferenceInfo.dateTime
|
||||
val timestamp = conferenceInfo.dateTime
|
||||
|
||||
val day = TimestampUtils.dayOfWeek(timestamp)
|
||||
|
||||
|
|
@ -48,6 +48,10 @@ class MeetingModel @WorkerThread constructor(conferenceInfo: ConferenceInfo) {
|
|||
|
||||
val subject = MutableLiveData<String>()
|
||||
|
||||
val firstMeetingOfTheDay = MutableLiveData<Boolean>()
|
||||
|
||||
val displayTodayIndicator = MutableLiveData<Boolean>()
|
||||
|
||||
init {
|
||||
subject.postValue(conferenceInfo.subject)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.linphone.core.CoreListenerStub
|
|||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.main.meetings.model.MeetingModel
|
||||
import org.linphone.ui.main.viewmodel.AbstractTopBarViewModel
|
||||
import org.linphone.utils.TimestampUtils
|
||||
|
||||
class MeetingsListViewModel @UiThread constructor() : AbstractTopBarViewModel() {
|
||||
companion object {
|
||||
|
|
@ -86,6 +87,9 @@ class MeetingsListViewModel @UiThread constructor() : AbstractTopBarViewModel()
|
|||
)
|
||||
source = coreContext.core.conferenceInformationList
|
||||
}
|
||||
|
||||
var previousModel: MeetingModel? = null
|
||||
var firstMeetingOfTodayFound = false
|
||||
for (info: ConferenceInfo in source) {
|
||||
val add = if (filter.isNotEmpty()) {
|
||||
val organizerCheck = info.organizer?.asStringUriOnly()?.contains(
|
||||
|
|
@ -103,7 +107,30 @@ class MeetingsListViewModel @UiThread constructor() : AbstractTopBarViewModel()
|
|||
}
|
||||
if (add) {
|
||||
val model = MeetingModel(info)
|
||||
val firstMeetingOfTheDay = if (previousModel != null) {
|
||||
previousModel.day != model.day || previousModel.dayNumber != model.dayNumber
|
||||
} else {
|
||||
true
|
||||
}
|
||||
model.firstMeetingOfTheDay.postValue(firstMeetingOfTheDay)
|
||||
|
||||
if (firstMeetingOfTheDay && model.isToday) {
|
||||
firstMeetingOfTodayFound = true
|
||||
model.displayTodayIndicator.postValue(true)
|
||||
}
|
||||
|
||||
list.add(model)
|
||||
previousModel = model
|
||||
}
|
||||
}
|
||||
|
||||
if (!firstMeetingOfTodayFound) {
|
||||
val firstMeetingAfterToday = list.find {
|
||||
TimestampUtils.isAfterToday(it.timestamp)
|
||||
}
|
||||
Log.i("$TAG $firstMeetingAfterToday")
|
||||
if (firstMeetingAfterToday != null) {
|
||||
firstMeetingAfterToday.displayTodayIndicator.postValue(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,17 @@ class TimestampUtils {
|
|||
return isSameDay(cal, Calendar.getInstance())
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
fun isAfterToday(timestamp: Long, timestampInSecs: Boolean = true): Boolean {
|
||||
val cal = Calendar.getInstance()
|
||||
cal.timeInMillis = if (timestampInSecs) timestamp * 1000 else timestamp
|
||||
|
||||
val tomorrow = Calendar.getInstance()
|
||||
return cal[Calendar.ERA] >= tomorrow[Calendar.ERA] &&
|
||||
cal[Calendar.YEAR] >= tomorrow[Calendar.YEAR] &&
|
||||
cal[Calendar.DAY_OF_YEAR] >= tomorrow[Calendar.DAY_OF_YEAR]
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
fun isYesterday(timestamp: Long, timestampInSecs: Boolean = true): Boolean {
|
||||
val yesterday = Calendar.getInstance()
|
||||
|
|
|
|||
|
|
@ -14,29 +14,46 @@
|
|||
<variable
|
||||
name="onLongClickListener"
|
||||
type="View.OnLongClickListener" />
|
||||
<variable
|
||||
name="firstMeetingOfTheDay"
|
||||
type="Boolean" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_300"
|
||||
android:id="@+id/today_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:text="@string/meetings_list_today_indicator"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/orange_main_500"
|
||||
android:visibility="@{model.displayTodayIndicator ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/today_separator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
android:background="@color/orange_main_500"
|
||||
android:visibility="@{model.displayTodayIndicator ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintTop_toBottomOf="@id/today_indicator" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/header_day"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@{firstMeetingOfTheDay ? @dimen/meeting_margin : @dimen/zero, default=@dimen/zero}"
|
||||
android:layout_marginTop="@{model.firstMeetingOfTheDay ? @dimen/meeting_margin : @dimen/zero, default=@dimen/zero}"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@{model.day, default=`Mon.`}"
|
||||
android:visibility="@{firstMeetingOfTheDay ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:visibility="@{model.firstMeetingOfTheDay ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:textColor="@color/gray_main2_500"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="@id/header_day_number"
|
||||
app:layout_constraintEnd_toEndOf="@id/header_day_number"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/today_separator"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/today_background"
|
||||
|
|
@ -44,7 +61,7 @@
|
|||
android:layout_height="32dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:src="@drawable/shape_circle_primary_background"
|
||||
android:visibility="@{model.isToday && firstMeetingOfTheDay ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:visibility="@{model.isToday && model.firstMeetingOfTheDay ? View.VISIBLE : View.INVISIBLE}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_day" />
|
||||
|
||||
|
|
@ -54,7 +71,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{model.dayNumber, default=`19`}"
|
||||
android:visibility="@{firstMeetingOfTheDay ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:visibility="@{model.firstMeetingOfTheDay ? View.VISIBLE : View.INVISIBLE}"
|
||||
android:textColor="@{model.isToday ? @color/white : @color/gray_main2_500, default=@color/gray_main2_500}"
|
||||
android:textSize="20sp"
|
||||
android:paddingBottom="4dp"
|
||||
|
|
@ -71,14 +88,14 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginTop="@{firstMeetingOfTheDay ? @dimen/meeting_margin : @dimen/zero, default=@dimen/zero}"
|
||||
android:layout_marginTop="@{model.firstMeetingOfTheDay ? @dimen/meeting_margin : @dimen/zero, default=@dimen/zero}"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:cardBackgroundColor="@color/list_cell_background_color"
|
||||
app:cardElevation="5dp"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:layout_constraintStart_toEndOf="@id/header_day"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/today_separator"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
<dimen name="call_button_icon_padding">15dp</dimen>
|
||||
<dimen name="call_extra_button_top_margin">30dp</dimen>
|
||||
|
||||
<dimen name="meeting_list_decoration_height">66dp</dimen>
|
||||
|
||||
<dimen name="toast_max_width">400dp</dimen>
|
||||
<dimen name="toast_text_max_width">300dp</dimen>
|
||||
<dimen name="button_max_width">400dp</dimen>
|
||||
|
|
|
|||
|
|
@ -373,6 +373,8 @@
|
|||
<string name="message_reactions_info_emoji_title">%s %s</string>
|
||||
|
||||
<string name="meetings_list_empty">No meeting for the moment…</string>
|
||||
<string name="meetings_list_today_indicator">Today</string>
|
||||
|
||||
<string name="meeting_schedule_title">New meeting</string>
|
||||
<string name="meeting_schedule_meeting_label">Meeting</string>
|
||||
<string name="meeting_schedule_broadcast_label">Broadcast</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue