mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Fixed time picker in conference scheduler + added time zone picker in schedule meeting UI
This commit is contained in:
parent
5289dc4824
commit
5ae345e794
9 changed files with 230 additions and 41 deletions
|
|
@ -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]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<TimeZoneModel> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String>()
|
||||
|
||||
val timezone = MutableLiveData<String>()
|
||||
val availableTimeZones: List<TimeZoneModel> = TimeZone.getAvailableIDs().map { id ->
|
||||
TimeZoneModel(TimeZone.getTimeZone(id))
|
||||
}.toList().sorted()
|
||||
var selectedTimeZone = MutableLiveData<TimeZoneModel>()
|
||||
|
||||
val sendInvitations = MutableLiveData<Boolean>()
|
||||
|
||||
|
|
@ -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<SelectedAddressModel>()
|
||||
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()
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,13 +195,13 @@
|
|||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_700"
|
||||
android:id="@+id/timezone"
|
||||
android:id="@+id/timezone_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="26dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@{viewModel.timezone, default=@string/meeting_schedule_timezone_title}"
|
||||
android:text="@string/meeting_schedule_timezone_title"
|
||||
android:textColor="?attr/color_main2_600"
|
||||
android:textSize="14sp"
|
||||
android:maxLines="1"
|
||||
|
|
@ -213,6 +213,38 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/to_date" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/timezone_picker"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/gray_main2_600"
|
||||
android:gravity="center_vertical"
|
||||
android:overlapAnchor="false"
|
||||
android:dropDownVerticalOffset="25dp"
|
||||
android:spinnerMode="dropdown"
|
||||
android:popupBackground="@drawable/shape_squircle_white_background"
|
||||
android:background="@drawable/edit_text_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/timezone_label"
|
||||
app:layout_constraintStart_toStartOf="@id/timezone_label"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/timezone_picker_caret"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/caret_down"
|
||||
android:contentDescription="@null"
|
||||
app:layout_constraintTop_toTopOf="@id/timezone_picker"
|
||||
app:layout_constraintBottom_toBottomOf="@id/timezone_picker"
|
||||
app:layout_constraintEnd_toEndOf="@id/timezone_picker"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_700"
|
||||
android:id="@+id/repeat"
|
||||
|
|
@ -229,7 +261,7 @@
|
|||
android:drawableTint="?attr/color_main2_600"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/timezone" />
|
||||
app:layout_constraintTop_toBottomOf="@id/timezone_picker" />
|
||||
|
||||
<View
|
||||
android:id="@+id/separator_2"
|
||||
|
|
|
|||
|
|
@ -339,13 +339,13 @@
|
|||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_700"
|
||||
android:id="@+id/timezone"
|
||||
android:id="@+id/timezone_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@{viewModel.timezone, default=@string/meeting_schedule_timezone_title}"
|
||||
android:text="@string/meeting_schedule_timezone_title"
|
||||
android:textColor="?attr/color_main2_600"
|
||||
android:textSize="14sp"
|
||||
android:maxLines="1"
|
||||
|
|
@ -357,6 +357,38 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/all_day_switch" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/timezone_picker"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/gray_main2_600"
|
||||
android:gravity="center_vertical"
|
||||
android:overlapAnchor="false"
|
||||
android:dropDownVerticalOffset="25dp"
|
||||
android:spinnerMode="dropdown"
|
||||
android:popupBackground="@drawable/shape_squircle_white_background"
|
||||
android:background="@drawable/edit_text_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/timezone_label"
|
||||
app:layout_constraintStart_toStartOf="@id/timezone_label"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/timezone_picker_caret"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/caret_down"
|
||||
android:contentDescription="@null"
|
||||
app:layout_constraintTop_toTopOf="@id/timezone_picker"
|
||||
app:layout_constraintBottom_toBottomOf="@id/timezone_picker"
|
||||
app:layout_constraintEnd_toEndOf="@id/timezone_picker"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style_700"
|
||||
android:id="@+id/repeat"
|
||||
|
|
@ -373,7 +405,7 @@
|
|||
android:drawableTint="?attr/color_main2_600"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/timezone" />
|
||||
app:layout_constraintTop_toBottomOf="@id/timezone_picker" />
|
||||
|
||||
<View
|
||||
android:id="@+id/separator_2"
|
||||
|
|
|
|||
|
|
@ -508,14 +508,14 @@
|
|||
<string name="meeting_schedule_title">Nouvelle réunion</string>
|
||||
<string name="meeting_schedule_meeting_label">Réunion</string>
|
||||
<string name="meeting_schedule_broadcast_label">Webinar</string>
|
||||
<string name="meeting_schedule_broadcast_help">Informations sur le mode Webinar. <u>En savoir plus</u></string>
|
||||
<string name="meeting_schedule_broadcast_help">Informations sur le mode Webinar.\n<u>En savoir plus</u></string>
|
||||
<string name="meeting_schedule_subject_hint">Ajouter un titre…</string>
|
||||
<string name="meeting_schedule_date_all_day_title">Toute la journée</string>
|
||||
<string name="meeting_schedule_pick_start_date_title">Date de début</string>
|
||||
<string name="meeting_schedule_pick_end_date_title">Date de fin</string>
|
||||
<string name="meeting_schedule_pick_start_time_title">Heure de début</string>
|
||||
<string name="meeting_schedule_pick_end_time_title">Heure de fin</string>
|
||||
<string name="meeting_schedule_timezone_title">Fuseau horaire : %s</string>
|
||||
<string name="meeting_schedule_timezone_title">Fuseau horaire</string>
|
||||
<string name="meeting_schedule_one_time_label">Une seule fois</string>
|
||||
<string name="meeting_schedule_description_hint">Ajouter une description</string>
|
||||
<string name="meeting_schedule_add_participants_title">Ajouter des participants</string>
|
||||
|
|
|
|||
|
|
@ -545,14 +545,14 @@
|
|||
<string name="meeting_schedule_title">New meeting</string>
|
||||
<string name="meeting_schedule_meeting_label">Meeting</string>
|
||||
<string name="meeting_schedule_broadcast_label">Broadcast</string>
|
||||
<string name="meeting_schedule_broadcast_help">Info about broadcast. <u>Learn more</u></string>
|
||||
<string name="meeting_schedule_broadcast_help">Info about broadcast.\n<u>Learn more</u></string>
|
||||
<string name="meeting_schedule_subject_hint">Add title…</string>
|
||||
<string name="meeting_schedule_date_all_day_title">All day</string>
|
||||
<string name="meeting_schedule_pick_start_date_title">Choose the start date</string>
|
||||
<string name="meeting_schedule_pick_end_date_title">Choose the end date</string>
|
||||
<string name="meeting_schedule_pick_start_time_title">Choose the start time</string>
|
||||
<string name="meeting_schedule_pick_end_time_title">Choose the end time</string>
|
||||
<string name="meeting_schedule_timezone_title">Timezone: %s</string>
|
||||
<string name="meeting_schedule_timezone_title">Timezone</string>
|
||||
<string name="meeting_schedule_one_time_label">One time</string>
|
||||
<string name="meeting_schedule_description_hint">Add description</string>
|
||||
<string name="meeting_schedule_add_participants_title">Add participants</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue