diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt
index 360bdbee9..71b7f9321 100644
--- a/app/src/main/java/org/linphone/core/CoreContext.kt
+++ b/app/src/main/java/org/linphone/core/CoreContext.kt
@@ -324,6 +324,9 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C
AuthMethod.Tls -> {
Log.w("$TAG Authentication requested method is TLS, not doing anything...")
}
+ else -> {
+ Log.w("$TAG Unexpected authentication request method [$method]")
+ }
}
}
diff --git a/app/src/main/java/org/linphone/ui/main/meetings/fragment/EditMeetingFragment.kt b/app/src/main/java/org/linphone/ui/main/meetings/fragment/EditMeetingFragment.kt
index 74593d171..720eaf886 100644
--- a/app/src/main/java/org/linphone/ui/main/meetings/fragment/EditMeetingFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/meetings/fragment/EditMeetingFragment.kt
@@ -24,6 +24,8 @@ import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
import androidx.annotation.UiThread
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
@@ -53,6 +55,17 @@ class EditMeetingFragment : SlidingPaneChildFragment() {
private val args: EditMeetingFragmentArgs by navArgs()
+ private val timeZonePickerListener = object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+ val timeZone = viewModel.availableTimeZones[position]
+ Log.i("$TAG Selected time zone is now [$timeZone] at index [$position]")
+ viewModel.updateTimeZone(timeZone)
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {
+ }
+ }
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -195,5 +208,23 @@ class EditMeetingFragment : SlidingPaneChildFragment() {
viewModel.setParticipants(list)
}
}
+
+ setupTimeZonePicker()
+ }
+
+ private fun setupTimeZonePicker() {
+ val timeZoneIndex = viewModel.availableTimeZones.indexOf(viewModel.selectedTimeZone.value)
+ Log.i("$TAG Setting default time zone at index [$timeZoneIndex]")
+ val adapter = ArrayAdapter(
+ requireContext(),
+ R.layout.drop_down_item,
+ viewModel.availableTimeZones
+ )
+ adapter.setDropDownViewResource(
+ R.layout.generic_dropdown_cell
+ )
+ binding.timezonePicker.adapter = adapter
+ binding.timezonePicker.onItemSelectedListener = timeZonePickerListener
+ binding.timezonePicker.setSelection(if (timeZoneIndex == -1) 0 else timeZoneIndex)
}
}
diff --git a/app/src/main/java/org/linphone/ui/main/meetings/fragment/ScheduleMeetingFragment.kt b/app/src/main/java/org/linphone/ui/main/meetings/fragment/ScheduleMeetingFragment.kt
index e5df970fe..47092be80 100644
--- a/app/src/main/java/org/linphone/ui/main/meetings/fragment/ScheduleMeetingFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/meetings/fragment/ScheduleMeetingFragment.kt
@@ -24,6 +24,8 @@ import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
import androidx.annotation.UiThread
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
@@ -53,6 +55,17 @@ class ScheduleMeetingFragment : GenericMainFragment() {
private val args: ScheduleMeetingFragmentArgs by navArgs()
+ private val timeZonePickerListener = object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+ val timeZone = viewModel.availableTimeZones[position]
+ Log.i("$TAG Selected time zone is now [$timeZone] at index [$position]")
+ viewModel.updateTimeZone(timeZone)
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {
+ }
+ }
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -195,5 +208,23 @@ class ScheduleMeetingFragment : GenericMainFragment() {
viewModel.setParticipants(list)
}
}
+
+ setupTimeZonePicker()
+ }
+
+ private fun setupTimeZonePicker() {
+ val timeZoneIndex = viewModel.availableTimeZones.indexOf(viewModel.selectedTimeZone.value)
+ Log.i("$TAG Setting default time zone at index [$timeZoneIndex]")
+ val adapter = ArrayAdapter(
+ requireContext(),
+ R.layout.drop_down_item,
+ viewModel.availableTimeZones
+ )
+ adapter.setDropDownViewResource(
+ R.layout.generic_dropdown_cell
+ )
+ binding.timezonePicker.adapter = adapter
+ binding.timezonePicker.onItemSelectedListener = timeZonePickerListener
+ binding.timezonePicker.setSelection(if (timeZoneIndex == -1) 0 else timeZoneIndex)
}
}
diff --git a/app/src/main/java/org/linphone/ui/main/meetings/model/TimeZoneModel.kt b/app/src/main/java/org/linphone/ui/main/meetings/model/TimeZoneModel.kt
new file mode 100644
index 000000000..0db61373b
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/main/meetings/model/TimeZoneModel.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2010-2024 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.linphone.ui.main.meetings.model
+
+import androidx.annotation.UiThread
+import java.util.TimeZone
+import java.util.concurrent.TimeUnit
+import kotlin.math.abs
+
+class TimeZoneModel @UiThread constructor(timeZone: TimeZone) : Comparable {
+ val id: String = timeZone.id
+
+ private val hours: Long = TimeUnit.MILLISECONDS.toHours(timeZone.rawOffset.toLong())
+
+ private val minutes: Long = abs(
+ TimeUnit.MILLISECONDS.toMinutes(timeZone.rawOffset.toLong()) -
+ TimeUnit.HOURS.toMinutes(hours)
+ )
+
+ private val gmt: String = if (hours >= 0) {
+ String.format("GMT+%02d:%02d - %s", hours, minutes, timeZone.id)
+ } else {
+ String.format("GMT%02d:%02d - %s", hours, minutes, timeZone.id)
+ }
+
+ override fun toString(): String {
+ return gmt
+ }
+
+ override fun compareTo(other: TimeZoneModel): Int {
+ if (hours == other.hours) {
+ if (minutes == other.minutes) {
+ return id.compareTo(other.id)
+ }
+ return minutes.compareTo(other.minutes)
+ }
+ return hours.compareTo(other.hours)
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/ScheduleMeetingViewModel.kt b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/ScheduleMeetingViewModel.kt
index 7a3ebac7b..909d5b6fe 100644
--- a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/ScheduleMeetingViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/ScheduleMeetingViewModel.kt
@@ -24,7 +24,6 @@ import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import java.util.Calendar
-import java.util.Locale
import java.util.TimeZone
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
@@ -39,8 +38,8 @@ import org.linphone.core.Participant
import org.linphone.core.ParticipantInfo
import org.linphone.core.tools.Log
import org.linphone.ui.GenericViewModel
+import org.linphone.ui.main.meetings.model.TimeZoneModel
import org.linphone.ui.main.model.SelectedAddressModel
-import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.TimestampUtils
@@ -67,7 +66,10 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
val toTime = MutableLiveData()
- val timezone = MutableLiveData()
+ val availableTimeZones: List = TimeZone.getAvailableIDs().map { id ->
+ TimeZoneModel(TimeZone.getTimeZone(id))
+ }.toList().sorted()
+ var selectedTimeZone = MutableLiveData()
val sendInvitations = MutableLiveData()
@@ -194,8 +196,14 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
allDayMeeting.value = false
sendInvitations.value = true
+ selectedTimeZone.value = availableTimeZones.find {
+ it.id == TimeZone.getDefault().id
+ }
+
val now = System.currentTimeMillis()
- val cal = Calendar.getInstance()
+ val cal = Calendar.getInstance(
+ TimeZone.getTimeZone(selectedTimeZone.value?.id ?: TimeZone.getDefault().id)
+ )
cal.timeInMillis = now
cal.add(Calendar.HOUR, 1)
cal.set(Calendar.MINUTE, 0)
@@ -219,7 +227,6 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
computeDateLabels()
computeTimeLabels()
- updateTimezone()
}
override fun onCleared() {
@@ -295,6 +302,12 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
computeDateLabels()
}
+ @UiThread
+ fun updateTimeZone(timeZone: TimeZoneModel) {
+ selectedTimeZone.value = timeZone
+ computeTimeLabels()
+ }
+
@UiThread
fun setStartTime(hours: Int, minutes: Int) {
startHour = hours
@@ -486,19 +499,24 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
isBroadcastSelected.postValue(false) // TODO FIXME: not implemented yet
- startHour = 0
- startMinutes = 0
- endHour = 0
- endMinutes = 0
startTimestamp = conferenceInfo.dateTime * 1000 /* Linphone timestamps are in seconds */
endTimestamp =
(conferenceInfo.dateTime + conferenceInfo.duration * 60) * 1000 /* Linphone timestamps are in seconds */
Log.i(
"$TAG Loaded start date is [$startTimestamp], loaded end date is [$endTimestamp]"
)
+ val cal = Calendar.getInstance(
+ TimeZone.getTimeZone(selectedTimeZone.value?.id ?: TimeZone.getDefault().id)
+ )
+ cal.timeInMillis = startTimestamp
+ startHour = cal.get(Calendar.HOUR_OF_DAY)
+ startMinutes = cal.get(Calendar.MINUTE)
+ cal.timeInMillis = endTimestamp
+ endHour = cal.get(Calendar.HOUR_OF_DAY)
+ endMinutes = cal.get(Calendar.MINUTE)
+
computeDateLabels()
computeTimeLabels()
- updateTimezone()
val list = arrayListOf()
for (participant in conferenceInfo.participantInfos) {
@@ -554,9 +572,13 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
@AnyThread
private fun computeTimeLabels() {
- val cal = Calendar.getInstance()
+ val timeZoneId = selectedTimeZone.value?.id ?: TimeZone.getDefault().id
+ Log.i("$TAG Updating timestamps using time zone [${selectedTimeZone.value}]($timeZoneId)")
+ val cal = Calendar.getInstance(
+ TimeZone.getTimeZone(timeZoneId)
+ )
cal.timeInMillis = startTimestamp
- if (startHour != 0 && startMinutes != 0) {
+ if (startHour != -1 && startMinutes != -1) {
cal.set(Calendar.HOUR_OF_DAY, startHour)
cal.set(Calendar.MINUTE, startMinutes)
}
@@ -565,7 +587,7 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
fromTime.postValue(start)
cal.timeInMillis = endTimestamp
- if (endHour != 0 && endMinutes != 0) {
+ if (endHour != -1 && endMinutes != -1) {
cal.set(Calendar.HOUR_OF_DAY, endHour)
cal.set(Calendar.MINUTE, endMinutes)
}
@@ -573,22 +595,4 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
Log.i("$TAG Computed end time for timestamp [$endTimestamp] is [$end]")
toTime.postValue(end)
}
-
- @AnyThread
- private fun updateTimezone() {
- timezone.postValue(
- AppUtils.getFormattedString(
- R.string.meeting_schedule_timezone_title,
- TimeZone.getDefault().displayName.replaceFirstChar {
- if (it.isLowerCase()) {
- it.titlecase(
- Locale.getDefault()
- )
- } else {
- it.toString()
- }
- }
- )
- )
- }
}
diff --git a/app/src/main/res/layout/meeting_edit_fragment.xml b/app/src/main/res/layout/meeting_edit_fragment.xml
index ec81464e5..2814699f0 100644
--- a/app/src/main/res/layout/meeting_edit_fragment.xml
+++ b/app/src/main/res/layout/meeting_edit_fragment.xml
@@ -195,13 +195,13 @@
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/timezone_picker" />
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/timezone_picker" />
Nouvelle réunion
Réunion
Webinar
- Informations sur le mode Webinar. En savoir plus
+ Informations sur le mode Webinar.\nEn savoir plus
Ajouter un titre…
Toute la journée
Date de début
Date de fin
Heure de début
Heure de fin
- Fuseau horaire : %s
+ Fuseau horaire
Une seule fois
Ajouter une description
Ajouter des participants
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3dad41f6c..0bf997505 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -545,14 +545,14 @@
New meeting
Meeting
Broadcast
- Info about broadcast. Learn more
+ Info about broadcast.\nLearn more
Add title…
All day
Choose the start date
Choose the end date
Choose the start time
Choose the end time
- Timezone: %s
+ Timezone
One time
Add description
Add participants