Merge pull request #4552 from nextcloud/issue-4274-migrate-javautil-date-time-1

Migrating away from java.util.Date to java.time - DateTimePickerFragment
This commit is contained in:
Marcel Hibbe 2025-01-10 11:51:25 +01:00 committed by GitHub
commit a34e0c6e7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 395 additions and 532 deletions

View File

@ -40,6 +40,7 @@
</inspection_tool>
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />

View File

@ -54,11 +54,13 @@ import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.view.ContextThemeWrapper
import androidx.cardview.widget.CardView
import androidx.compose.runtime.mutableStateOf
import androidx.core.content.FileProvider
import androidx.core.content.PermissionChecker
import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.drawable.toBitmap
import androidx.core.os.bundleOf
import androidx.core.text.bold
import androidx.emoji2.text.EmojiCompat
import androidx.fragment.app.DialogFragment
@ -147,7 +149,7 @@ import com.nextcloud.talk.ui.PlaybackSpeed
import com.nextcloud.talk.ui.PlaybackSpeedControl
import com.nextcloud.talk.ui.StatusDrawable
import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet
import com.nextcloud.talk.ui.dialog.DateTimePickerFragment
import com.nextcloud.talk.ui.dialog.DateTimeCompose
import com.nextcloud.talk.ui.dialog.FileAttachmentPreviewFragment
import com.nextcloud.talk.ui.dialog.MessageActionsDialog
import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
@ -3502,8 +3504,17 @@ class ChatActivity :
val chatApiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(ApiUtils.API_V1, 1))
val newFragment: DialogFragment = DateTimePickerFragment.newInstance(roomToken, message!!.id, chatApiVersion)
newFragment.show(supportFragmentManager, DateTimePickerFragment.TAG)
val bundle = bundleOf()
bundle.putString(KEY_ROOM_TOKEN, roomToken)
bundle.putString(BundleKeys.KEY_MESSAGE_ID, message!!.id)
bundle.putInt(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion)
binding.genericComposeView.apply {
val shouldDismiss = mutableStateOf(false)
setContent {
DateTimeCompose(bundle).GetDateTimeDialog(shouldDismiss, this@ChatActivity)
}
}
}
fun markAsUnread(message: IMessage?) {

View File

@ -150,6 +150,7 @@ class ChatViewModel @Inject constructor(
object GetReminderStartState : ViewState
open class GetReminderExistState(val reminder: Reminder) : ViewState
object GetReminderStateSet : ViewState
private val _getReminderExistState: MutableLiveData<ViewState> = MutableLiveData(GetReminderStartState)
@ -310,6 +311,10 @@ class ChatViewModel @Inject constructor(
?.subscribe(GetReminderObserver())
}
fun overrideReminderState() {
_getReminderExistState.value = GetReminderStateSet
}
fun deleteReminder(user: User, roomToken: String, messageId: String, chatApiVersion: Int) {
chatNetworkDataSource.deleteReminder(user, roomToken, messageId, chatApiVersion)
.subscribeOn(Schedulers.io())
@ -754,7 +759,7 @@ class ChatViewModel @Inject constructor(
val model = ConversationModel.mapToConversationModel(it, userProvider.currentUser.blockingGet())
ConversationUtils.isNoteToSelfConversation(model)
}
_getNoteToSelfAvailability.value = NoteToSelfAvailableState(noteToSelf.token!!)
_getNoteToSelfAvailability.value = NoteToSelfAvailableState(noteToSelf.token)
} catch (e: NoSuchElementException) {
_getNoteToSelfAvailability.value = NoteToSelfNotAvailableState
Log.e(TAG, "Note to self not found $e")

View File

@ -0,0 +1,368 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2025 Julius Linus <juliuslinus1@gmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.ui.dialog
import android.content.Context
import android.os.Bundle
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material3.DatePicker
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TimePicker
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.material3.rememberTimePickerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.asFlow
import autodagger.AutoInjector
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.bundle.BundleKeys
import java.time.DayOfWeek
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.time.temporal.TemporalAdjusters.nextOrSame
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class DateTimeCompose(val bundle: Bundle) {
private var timeState = mutableStateOf(LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.MIN))
init {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
val user = userManager.currentUser.blockingGet()
val roomToken = bundle.getString(BundleKeys.KEY_ROOM_TOKEN)!!
val messageId = bundle.getString(BundleKeys.KEY_MESSAGE_ID)!!
val apiVersion = bundle.getInt(BundleKeys.KEY_CHAT_API_VERSION)
chatViewModel.getReminder(user, roomToken, messageId, apiVersion)
}
@Inject
lateinit var chatViewModel: ChatViewModel
@Inject
lateinit var userManager: UserManager
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
@Composable
fun GetDateTimeDialog(shouldDismiss: MutableState<Boolean>, context: Context) {
if (shouldDismiss.value) {
return
}
val colorScheme = viewThemeUtils.getColorScheme(context)
val isCollapsed = remember { mutableStateOf(true) }
MaterialTheme(colorScheme = colorScheme) {
Dialog(
onDismissRequest = {
shouldDismiss.value = true
},
properties = DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = true,
usePlatformDefaultWidth = isCollapsed.value
)
) {
Surface(
shape = RoundedCornerShape(INT_8.dp),
modifier = Modifier.animateContentSize()
) {
Column(
modifier = Modifier
.padding(INT_16.dp)
.fillMaxWidth()
) {
Header()
Body()
CollapsableDateTime(shouldDismiss, isCollapsed)
}
}
}
}
}
@Composable
private fun Submission(shouldDismiss: MutableState<Boolean>) {
Row(
modifier = Modifier.fillMaxWidth()
) {
TextButton(
onClick = {
val user = userManager.currentUser.blockingGet()
val roomToken = bundle.getString(BundleKeys.KEY_ROOM_TOKEN)!!
val messageId = bundle.getString(BundleKeys.KEY_MESSAGE_ID)!!
val apiVersion = bundle.getInt(BundleKeys.KEY_CHAT_API_VERSION)
chatViewModel.deleteReminder(user, roomToken, messageId, apiVersion)
shouldDismiss.value = true
},
modifier = Modifier
.weight(CUBED_PADDING)
) {
Text(
stringResource(R.string.nc_delete),
color = Color.Red
)
}
TextButton(
onClick = {
val user = userManager.currentUser.blockingGet()
val roomToken = bundle.getString(BundleKeys.KEY_ROOM_TOKEN)!!
val messageId = bundle.getString(BundleKeys.KEY_MESSAGE_ID)!!
val apiVersion = bundle.getInt(BundleKeys.KEY_CHAT_API_VERSION)
val offset = timeState.value.atZone(ZoneOffset.systemDefault()).offset
val timeVal = timeState.value.toEpochSecond(offset)
chatViewModel.setReminder(user, roomToken, messageId, timeVal.toInt(), apiVersion)
shouldDismiss.value = true
},
modifier = Modifier
.weight(CUBED_PADDING)
) {
Text(stringResource(R.string.set))
}
TextButton(
onClick = {
shouldDismiss.value = true
},
modifier = Modifier
.weight(CUBED_PADDING)
) {
Text(stringResource(R.string.close))
}
}
}
@Composable
private fun Body() {
val currTime = LocalDateTime.now()
val laterToday = LocalDateTime.now()
.withHour(INT_18)
.withMinute(0)
.withSecond(0)
val laterTodayStr = laterToday.format(DateTimeFormatter.ofPattern(PATTERN))
val tomorrow = LocalDateTime.now()
.plusDays(1)
.withHour(INT_8)
.withMinute(0)
.withSecond(0)
val tomorrowStr = tomorrow.format(DateTimeFormatter.ofPattern(PATTERN))
val thisWeekend = LocalDateTime.now()
.with(nextOrSame(DayOfWeek.SATURDAY))
.withHour(INT_8)
.withMinute(0)
.withSecond(0)
val thisWeekendStr = thisWeekend.format(DateTimeFormatter.ofPattern(PATTERN))
val nextWeek = LocalDateTime.now()
.plusWeeks(1)
.with(DayOfWeek.MONDAY)
.withHour(INT_8)
.withMinute(0)
.withSecond(0)
val nextWeekStr = nextWeek.format(DateTimeFormatter.ofPattern(PATTERN))
if (currTime < laterToday) {
TimeOption(
label = stringResource(R.string.later_today),
timeString = laterTodayStr
) {
setTime(laterToday)
}
}
if (tomorrow.dayOfWeek < DayOfWeek.SATURDAY) {
TimeOption(
label = stringResource(R.string.tomorrow),
timeString = tomorrowStr
) {
setTime(tomorrow)
}
}
if (currTime.dayOfWeek < DayOfWeek.SATURDAY) {
TimeOption(
label = stringResource(R.string.this_weekend),
timeString = thisWeekendStr
) {
setTime(thisWeekend)
}
}
TimeOption(
label = stringResource(R.string.next_week),
timeString = nextWeekStr
) {
setTime(nextWeek)
}
HorizontalDivider()
}
private fun setTime(localDateTime: LocalDateTime) {
timeState.value = localDateTime
chatViewModel.overrideReminderState()
}
@Composable
private fun Header() {
Row(
modifier = Modifier
.padding(INT_8.dp)
) {
Text(stringResource(R.string.nc_remind), modifier = Modifier.weight(1f))
val reminderState = chatViewModel.getReminderExistState
.asFlow()
.collectAsState(ChatViewModel.GetReminderStartState)
when (reminderState.value) {
is ChatViewModel.GetReminderExistState -> {
val timeL =
(reminderState.value as ChatViewModel.GetReminderExistState).reminder.timestamp!!.toLong()
timeState.value = LocalDateTime.ofInstant(Instant.ofEpochSecond(timeL), ZoneId.systemDefault())
}
else -> {}
}
if (timeState.value != LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.MIN)) {
Text(timeState.value.format(DateTimeFormatter.ofPattern(PATTERN)))
}
}
HorizontalDivider()
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CollapsableDateTime(shouldDismiss: MutableState<Boolean>, isCollapsed: MutableState<Boolean>) {
GeneralIconButton(icon = Icons.Filled.DateRange, label = "Custom") { isCollapsed.value = !isCollapsed.value }
val scrollState = rememberScrollState()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.verticalScroll(scrollState)
) {
if (!isCollapsed.value) {
val datePickerState = rememberDatePickerState()
val timePickerState = rememberTimePickerState()
DatePicker(
state = datePickerState
)
TimePicker(
state = timePickerState
)
val date = datePickerState.selectedDateMillis?.let {
val instant = Instant.ofEpochMilli(it)
LocalDateTime.ofInstant(instant, ZoneOffset.UTC) // Google sends time in UTC
}
if (date != null) {
val year = date.year
val month = date.month
val day = date.dayOfMonth
val hour = timePickerState.hour
val minute = timePickerState.minute
val newTime = LocalDateTime.of(year, month, day, hour, minute)
setTime(newTime)
} else {
val newTime = LocalDate.now().atTime(timePickerState.hour, timePickerState.minute)
setTime(newTime)
}
}
Submission(shouldDismiss)
}
}
@Composable
fun GeneralIconButton(icon: ImageVector, label: String, onClick: () -> Unit) {
TextButton(
onClick = onClick
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(INT_24.dp)
)
Spacer(modifier = Modifier.width(INT_8.dp))
Text(text = label)
}
}
}
@Composable
private fun TimeOption(label: String, timeString: String, onClick: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(INT_8.dp)
.clickable { onClick() }
) {
Text(label, modifier = Modifier.weight(HALF_WEIGHT))
Text(timeString, modifier = Modifier.weight(HALF_WEIGHT))
}
}
companion object {
private const val PATTERN = "dd MMM, HH:mm a"
private const val HALF_WEIGHT = 0.5f
private const val INT_8 = 8
private const val INT_16 = 16
private const val INT_18 = 18
private const val INT_24 = 24
private const val CUBED_PADDING = 0.33f
}
}

View File

@ -1,315 +0,0 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2023 Julius Linus <julius.linus@nextcloud.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.ui.dialog
import android.app.Dialog
import android.os.Bundle
import android.text.format.DateFormat
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import autodagger.AutoInjector
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointForward
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import com.nextcloud.android.common.ui.theme.utils.ColorRole
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.chat.ChatActivity
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
import com.nextcloud.talk.databinding.DialogDateTimePickerBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.users.UserManager
import java.util.Calendar
import java.util.TimeZone
import javax.inject.Inject
@Suppress("TooManyFunctions")
@AutoInjector(NextcloudTalkApplication::class)
class DateTimePickerFragment : DialogFragment() {
lateinit var binding: DialogDateTimePickerBinding
private var dialogView: View? = null
private lateinit var viewModel: ChatViewModel
private var currentTimeStamp: Long? = null
private lateinit var roomToken: String
private lateinit var messageId: String
private var chatApiVersion: Int = -1
private var laterTodayTimeStamp = 0L
private var tomorrowTimeStamp = 0L
private var weekendTimeStamp = 0L
private var nextWeekTimeStamp = 0L
@Inject
lateinit var userManager: UserManager
@Inject
lateinit var viewThemeUtils: ViewThemeUtils
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogDateTimePickerBinding.inflate(layoutInflater)
dialogView = binding.root
viewModel = (requireActivity() as ChatActivity).chatViewModel
arguments?.let {
roomToken = it.getString(TOKEN_ARG, "")
messageId = it.getString(ID_ARG, "")
chatApiVersion = it.getInt(CHAT_API_VERSION_ARG)
}
return MaterialAlertDialogBuilder(requireContext()).setView(dialogView).create()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
setUpDefaults()
setUpColors()
setListeners()
getReminder()
viewModel.getReminderExistState.observe(this) { state ->
when (state) {
is ChatViewModel.GetReminderExistState -> {
val timeStamp = state.reminder.timestamp?.toLong()?.times(ONE_SEC)
showDelete(true)
setTimeStamp(getTimeFromTimeStamp(timeStamp!!))
}
else -> {
showDelete(false)
binding.dateTimePickerTimestamp.text = ""
}
}
}
return inflater.inflate(R.layout.dialog_date_time_picker, container, false)
}
private fun setUpDefaults() {
val currTime = getTimeFromCalendar()
val currentWeekInYear = Calendar.getInstance().get(Calendar.WEEK_OF_YEAR)
laterTodayTimeStamp = getTimeFromCalendar(hour = HOUR_SIX_PM, minute = 0)
binding.dateTimePickerLaterTodayTextview.text = getTimeFromTimeStamp(laterTodayTimeStamp)
if (Calendar.getInstance().get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
tomorrowTimeStamp = getTimeFromCalendar(
hour = HOUR_EIGHT_AM,
minute = 0,
daysToAdd = 1
)
binding.dateTimePickerWeekend.visibility = View.GONE // because today is the weekend
} else {
tomorrowTimeStamp = getTimeFromCalendar(hour = HOUR_EIGHT_AM, minute = 0, daysToAdd = 1)
weekendTimeStamp = getTimeFromCalendar(hour = HOUR_EIGHT_AM, weekDay = Calendar.SATURDAY, minute = 0)
}
binding.dateTimePickerTomorrowTextview.text = getTimeFromTimeStamp(tomorrowTimeStamp)
binding.dateTimePickerWeekendTextview.text = getTimeFromTimeStamp(weekendTimeStamp)
nextWeekTimeStamp = getTimeFromCalendar(
hour = HOUR_EIGHT_AM,
weekDay = Calendar.MONDAY,
minute = 0,
weekInYear =
currentWeekInYear + 1
) // this should only pick mondays from next week only
binding.dateTimePickerNextWeekTextview.text = getTimeFromTimeStamp(nextWeekTimeStamp)
// This is to hide the later today option, if it's past 6pm
if (currTime > laterTodayTimeStamp) {
binding.dateTimePickerLaterToday.visibility = View.GONE
}
// This is to hide the tomorrow option, if that's also the weekend
if (binding.dateTimePickerTomorrowTextview.text == binding.dateTimePickerWeekendTextview.text) {
binding.dateTimePickerTomorrow.visibility = View.GONE
}
}
private fun getReminder() {
viewModel.getReminder(userManager.currentUser.blockingGet(), roomToken, messageId, chatApiVersion)
}
private fun showDelete(value: Boolean) {
if (value) {
binding.buttonDelete.visibility = View.VISIBLE
} else {
binding.buttonDelete.visibility = View.GONE
}
}
private fun setUpColors() {
binding.root.let {
viewThemeUtils.platform.colorViewBackground(it)
}
binding.dateTimePickerCustomIcon.let {
viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY)
}
binding.dateTimePickerTimestamp.let {
viewThemeUtils.material.themeSearchBarText(it)
}
binding.run {
listOf(
binding.buttonClose,
binding.buttonSet
)
}.forEach(viewThemeUtils.material::colorMaterialButtonPrimaryBorderless)
}
private fun setListeners() {
binding.dateTimePickerLaterToday.setOnClickListener {
currentTimeStamp = laterTodayTimeStamp / ONE_SEC
setTimeStamp(getTimeFromTimeStamp(laterTodayTimeStamp))
}
binding.dateTimePickerTomorrow.setOnClickListener {
currentTimeStamp = tomorrowTimeStamp / ONE_SEC
setTimeStamp(getTimeFromTimeStamp(tomorrowTimeStamp))
}
binding.dateTimePickerWeekend.setOnClickListener {
currentTimeStamp = weekendTimeStamp / ONE_SEC
setTimeStamp(getTimeFromTimeStamp(weekendTimeStamp))
}
binding.dateTimePickerNextWeek.setOnClickListener {
currentTimeStamp = nextWeekTimeStamp / ONE_SEC
setTimeStamp(getTimeFromTimeStamp(nextWeekTimeStamp))
}
binding.dateTimePickerCustom.setOnClickListener {
val constraintsBuilder = CalendarConstraints.Builder()
.setValidator(DateValidatorPointForward.now())
.build()
val time = System.currentTimeMillis()
val datePicker = MaterialDatePicker.Builder.datePicker()
.setTitleText(R.string.nc_remind)
.setSelection(time + TimeZone.getDefault().getOffset(time))
.setCalendarConstraints(constraintsBuilder).build()
datePicker.addOnPositiveButtonClickListener { selection ->
val localTimeInMillis = selection - TimeZone.getDefault().getOffset(selection)
val calendar = Calendar.getInstance()
calendar.timeInMillis = localTimeInMillis
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_YEAR)
setUpTimePicker(year, month, day)
}
datePicker.show(this.parentFragmentManager, TAG)
}
binding.buttonClose.setOnClickListener { dismiss() }
binding.buttonSet.setOnClickListener {
currentTimeStamp?.let { time ->
viewModel.setReminder(
userManager.currentUser.blockingGet(),
roomToken,
messageId,
time.toInt(),
chatApiVersion
)
}
dismiss()
}
binding.buttonDelete.setOnClickListener {
viewModel.deleteReminder(userManager.currentUser.blockingGet(), roomToken, messageId, chatApiVersion)
}
}
private fun setUpTimePicker(year: Int, month: Int, day: Int) {
val locale = if (DateFormat.is24HourFormat(requireContext())) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H
val timePicker = MaterialTimePicker.Builder()
.setTitleText(R.string.nc_remind)
.setTimeFormat(locale)
.build()
timePicker.addOnPositiveButtonClickListener {
val timestamp = getTimeFromCalendar(
year,
month,
day,
timePicker.hour,
timePicker.minute
)
setTimeStamp(getTimeFromTimeStamp(timestamp))
currentTimeStamp = timestamp / ONE_SEC
}
timePicker.show(this.parentFragmentManager, TAG)
}
/**
* You can use `weekDay` or `daysToAdd` or neither but don't use both b/c it messes up the calendar
*/
@Suppress("LongParameterList")
private fun getTimeFromCalendar(
year: Int = Calendar.getInstance().get(Calendar.YEAR),
month: Int = Calendar.getInstance().get(Calendar.MONTH),
day: Int = Calendar.getInstance().get(Calendar.DAY_OF_YEAR),
hour: Int = Calendar.getInstance().get(Calendar.HOUR_OF_DAY),
minute: Int = Calendar.getInstance().get(Calendar.MINUTE),
daysToAdd: Int = 0,
weekInYear: Int = -1,
weekDay: Int = -1
): Long {
val calendar: Calendar = Calendar.getInstance().apply {
set(Calendar.YEAR, year)
set(Calendar.MONTH, month)
set(Calendar.DAY_OF_YEAR, day)
if (weekDay > -1) set(Calendar.DAY_OF_WEEK, weekDay)
if (daysToAdd > 0) add(Calendar.DAY_OF_YEAR, daysToAdd)
if (weekInYear != -1) set(Calendar.WEEK_OF_YEAR, weekInYear)
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
set(Calendar.SECOND, 0)
}
return calendar.timeInMillis
}
private fun setTimeStamp(date: String) {
binding.dateTimePickerTimestamp.text = date
}
private fun getTimeFromTimeStamp(time: Long): String {
return DateUtils.formatDateTime(
requireContext(),
time,
DateUtils.FORMAT_SHOW_DATE
) + ", " + DateUtils.formatDateTime(
requireContext(),
time,
DateUtils.FORMAT_SHOW_TIME
)
}
companion object {
val TAG = DateTimePickerFragment::class.simpleName
private const val ONE_SEC = 1000
private const val HOUR_EIGHT_AM = 8
private const val HOUR_SIX_PM = 18
private const val TOKEN_ARG = "TOKEN_ARG"
private const val ID_ARG = "ID_ARG"
private const val CHAT_API_VERSION_ARG = "CHAT_API_VERSION_ARG"
@JvmStatic
fun newInstance(token: String, id: String, chatApiVersion: Int): DateTimePickerFragment {
val args = Bundle()
args.putString(TOKEN_ARG, token)
args.putString(ID_ARG, id)
args.putInt(CHAT_API_VERSION_ARG, chatApiVersion)
val dateTimePickerFragment = DateTimePickerFragment()
dateTimePickerFragment.arguments = args
return dateTimePickerFragment
}
}
}

View File

@ -269,4 +269,9 @@
android:layout_height="wrap_content"
android:padding="0dp"
/>
<androidx.compose.ui.platform.ComposeView
android:id="@+id/generic_compose_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -1,212 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk - Android Client
~
~ SPDX-FileCopyrightText: 2023 Julius Linus <julius.linus@nextcloud.com>
~ SPDX-License-Identifier: GPL-3.0-or-later
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:background="@color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/standard_margin"
android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/nc_remind"
android:textSize="@dimen/md_title_textsize" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/date_time_picker_timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/supporting_text_text_size"
android:textStyle="bold"
tools:text="Apr 15th, 8:00 AM" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/date_time_picker_later_today"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:padding="@dimen/standard_padding">
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/later_today"
android:textSize="@dimen/headline_text_size" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/date_time_picker_later_today_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="@dimen/headline_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/date_time_picker_tomorrow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:padding="@dimen/standard_padding">
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/tomorrow"
android:textSize="@dimen/headline_text_size" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/date_time_picker_tomorrow_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="@dimen/headline_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/date_time_picker_weekend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:padding="@dimen/standard_padding">
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/this_weekend"
android:textSize="@dimen/headline_text_size" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/date_time_picker_weekend_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="@dimen/headline_text_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/date_time_picker_next_week"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:padding="@dimen/standard_padding">
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/next_week"
android:textSize="@dimen/headline_text_size" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/date_time_picker_next_week_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="@dimen/headline_text_size" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/date_time_picker_custom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal"
android:padding="@dimen/standard_padding">
<ImageView
android:id="@+id/date_time_picker_custom_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/calendar"
android:paddingEnd="@dimen/standard_double_padding"
android:src="@drawable/baseline_calendar_month_24"
tools:ignore="RtlSymmetry" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/custom"
android:textSize="@dimen/headline_text_size" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_delete"
style="@style/Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="@dimen/min_size_clickable_area"
android:text="@string/nc_delete"
android:textColor="@color/design_default_color_error" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_set"
style="@style/Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="@dimen/min_size_clickable_area"
android:text="@string/set" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_close"
style="@style/Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:minHeight="@dimen/min_size_clickable_area"
android:text="@string/close" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -1,2 +1,2 @@
DO NOT TOUCH; GENERATED BY DRONE
<span class="mdl-layout-title">Lint Report: 66 errors and 158 warnings</span>
<span class="mdl-layout-title">Lint Report: 36 errors and 106 warnings</span>