diff --git a/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.java b/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.java deleted file mode 100644 index 2b6bb4509f..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.content.Context; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import java.util.concurrent.TimeUnit; - -public class MuteDialog extends AlertDialog { - - - protected MuteDialog(Context context) { - super(context); - } - - protected MuteDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { - super(context, cancelable, cancelListener); - } - - protected MuteDialog(Context context, int theme) { - super(context, theme); - } - - public static void show(final Context context, final @NonNull MuteSelectionListener listener) { - show(context, listener, null); - } - - public static void show(final Context context, final @NonNull MuteSelectionListener listener, @Nullable Runnable cancelListener) { - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context); - builder.setTitle(R.string.MuteDialog_mute_notifications); - builder.setItems(R.array.mute_durations, (dialog, which) -> { - final long muteUntil; - - switch (which) { - case 0: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); break; - case 1: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(8); break; - case 2: muteUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1); break; - case 3: muteUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(7); break; - case 4: muteUntil = Long.MAX_VALUE; break; - default: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); break; - } - - listener.onMuted(muteUntil); - }); - - if (cancelListener != null) { - builder.setOnCancelListener(dialog -> { - cancelListener.run(); - dialog.dismiss(); - }); - } - - builder.show(); - - } - - public interface MuteSelectionListener { - public void onMuted(long until); - } - -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt new file mode 100644 index 0000000000..2f461df98a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt @@ -0,0 +1,73 @@ +package org.thoughtcrime.securesms + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.ImageView +import android.widget.TextView +import androidx.annotation.DrawableRes +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.LifecycleOwner +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.thoughtcrime.securesms.components.settings.conversation.MuteUntilTimePickerBottomSheet +import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.hours + +object MuteDialog { + + private const val MUTE_UNTIL: Long = -1L + + private data class MuteOption( + @DrawableRes val iconRes: Int, + val title: String, + val duration: Long + ) + + @JvmStatic + fun show(context: Context, fragmentManager: FragmentManager, lifecycleOwner: LifecycleOwner, action: MuteSelectionListener) { + fragmentManager.setFragmentResultListener(MuteUntilTimePickerBottomSheet.REQUEST_KEY, lifecycleOwner) { _, bundle -> + action.onMuted(bundle.getLong(MuteUntilTimePickerBottomSheet.RESULT_TIMESTAMP)) + } + + val options = listOf( + MuteOption(R.drawable.ic_daytime_24, context.getString(R.string.arrays__mute_for_one_hour), 1.hours.inWholeMilliseconds), + MuteOption(R.drawable.ic_nighttime_26, context.getString(R.string.arrays__mute_for_eight_hours), 8.hours.inWholeMilliseconds), + MuteOption(R.drawable.symbol_calendar_one, context.getString(R.string.arrays__mute_for_one_day), 1.days.inWholeMilliseconds), + MuteOption(R.drawable.symbol_calendar_week, context.getString(R.string.arrays__mute_for_seven_days), 7.days.inWholeMilliseconds), + MuteOption(R.drawable.symbol_calendar_24, context.getString(R.string.MuteDialog__mute_until), MUTE_UNTIL), + MuteOption(R.drawable.symbol_bell_slash_24, context.getString(R.string.arrays__always), Long.MAX_VALUE) + ) + + val adapter = object : BaseAdapter() { + override fun getCount(): Int = options.size + override fun getItem(position: Int): MuteOption = options[position] + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.mute_dialog_item, parent, false) + val option = options[position] + view.findViewById(R.id.mute_dialog_icon).setImageResource(option.iconRes) + view.findViewById(R.id.mute_dialog_title).text = option.title + return view + } + } + + MaterialAlertDialogBuilder(context) + .setTitle(R.string.MuteDialog_mute_notifications) + .setAdapter(adapter) { _, which -> + val option = options[which] + when (option.duration) { + MUTE_UNTIL -> MuteUntilTimePickerBottomSheet.show(fragmentManager) + Long.MAX_VALUE -> action.onMuted(Long.MAX_VALUE) + else -> action.onMuted(System.currentTimeMillis() + option.duration) + } + } + .show() + } + + fun interface MuteSelectionListener { + fun onMuted(until: Long) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt index aea029bff3..ca9628d325 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt @@ -38,7 +38,6 @@ import org.signal.core.util.orNull import org.signal.donations.InAppPaymentType import org.thoughtcrime.securesms.AvatarPreviewActivity import org.thoughtcrime.securesms.BlockUnblockDialog -import org.thoughtcrime.securesms.MuteDialog import org.thoughtcrime.securesms.PushContactSelectionActivity import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.badges.BadgeImageView @@ -119,10 +118,11 @@ private const val REQUEST_CODE_ADD_CONTACT = 2 private const val REQUEST_CODE_ADD_MEMBERS_TO_GROUP = 3 private const val REQUEST_CODE_RETURN_FROM_MEDIA = 4 -class ConversationSettingsFragment : DSLSettingsFragment( - layoutId = R.layout.conversation_settings_fragment, - menuId = R.menu.conversation_settings -) { +class ConversationSettingsFragment : + DSLSettingsFragment( + layoutId = R.layout.conversation_settings_fragment, + menuId = R.menu.conversation_settings + ) { private val args: ConversationSettingsFragmentArgs by navArgs() private val alertTint by lazy { ContextCompat.getColor(requireContext(), R.color.signal_alert_primary) } @@ -469,9 +469,11 @@ class ConversationSettingsFragment : DSLSettingsFragment( YouAreAlreadyInACallSnackbar.show(requireView()) } }, - onMuteClick = { + onMuteClick = { view -> if (!state.buttonStripState.isMuted) { - MuteDialog.show(requireContext(), viewModel::setMuteUntil) + MuteContextMenu.show(view, requireView() as ViewGroup, childFragmentManager, viewLifecycleOwner) { duration -> + viewModel.setMuteUntil(duration) + } } else { MaterialAlertDialogBuilder(requireContext()) .setMessage(state.recipient.muteUntil.formatMutedUntil(requireContext())) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/MuteContextMenu.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/MuteContextMenu.kt new file mode 100644 index 0000000000..7c2384d76a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/MuteContextMenu.kt @@ -0,0 +1,49 @@ +package org.thoughtcrime.securesms.components.settings.conversation + +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.LifecycleOwner +import org.signal.core.util.dp +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.menu.ActionItem +import org.thoughtcrime.securesms.components.menu.SignalContextMenu +import java.util.concurrent.TimeUnit + +object MuteContextMenu { + + @JvmStatic + fun show(anchor: View, container: ViewGroup, fragmentManager: FragmentManager, lifecycleOwner: LifecycleOwner, action: (Long) -> Unit): SignalContextMenu { + fragmentManager.setFragmentResultListener(MuteUntilTimePickerBottomSheet.REQUEST_KEY, lifecycleOwner) { _, bundle -> + action(bundle.getLong(MuteUntilTimePickerBottomSheet.RESULT_TIMESTAMP)) + } + + val context = anchor.context + val actionItems = listOf( + ActionItem(R.drawable.ic_daytime_24, context.getString(R.string.arrays__mute_for_one_hour)) { + action(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1)) + }, + ActionItem(R.drawable.ic_nighttime_26, context.getString(R.string.arrays__mute_for_eight_hours)) { + action(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(8)) + }, + ActionItem(R.drawable.symbol_calendar_one, context.getString(R.string.arrays__mute_for_one_day)) { + action(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)) + }, + ActionItem(R.drawable.symbol_calendar_week, context.getString(R.string.arrays__mute_for_seven_days)) { + action(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(7)) + }, + ActionItem(R.drawable.symbol_calendar_24, context.getString(R.string.MuteDialog__mute_until)) { + MuteUntilTimePickerBottomSheet.show(fragmentManager) + }, + ActionItem(R.drawable.symbol_bell_slash_24, context.getString(R.string.arrays__always)) { + action(Long.MAX_VALUE) + } + ) + + return SignalContextMenu.Builder(anchor, container) + .offsetX(12.dp) + .offsetY(12.dp) + .preferredVerticalPosition(SignalContextMenu.VerticalPosition.ABOVE) + .show(actionItems) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/MuteUntilTimePickerBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/MuteUntilTimePickerBottomSheet.kt new file mode 100644 index 0000000000..150381b5fe --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/MuteUntilTimePickerBottomSheet.kt @@ -0,0 +1,272 @@ +package org.thoughtcrime.securesms.components.settings.conversation + +import android.text.format.DateFormat +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.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.core.os.bundleOf +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.setFragmentResult +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.timepicker.MaterialTimePicker +import com.google.android.material.timepicker.TimeFormat +import org.signal.core.ui.BottomSheetUtil +import org.signal.core.ui.compose.BottomSheets +import org.signal.core.ui.compose.Buttons +import org.signal.core.ui.compose.ComposeBottomSheetDialogFragment +import org.signal.core.ui.compose.DayNightPreviews +import org.signal.core.ui.compose.Previews +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.util.atMidnight +import org.thoughtcrime.securesms.util.atUTC +import org.thoughtcrime.securesms.util.formatHours +import org.thoughtcrime.securesms.util.toLocalDateTime +import org.thoughtcrime.securesms.util.toMillis +import java.time.DayOfWeek +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.ZoneId +import java.time.ZoneOffset +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.TemporalAdjusters +import java.util.Locale + +class MuteUntilTimePickerBottomSheet : ComposeBottomSheetDialogFragment() { + + override val peekHeightPercentage: Float = 0.66f + + companion object { + const val REQUEST_KEY = "mute_until_result" + const val RESULT_TIMESTAMP = "timestamp" + + @JvmStatic + fun show(fragmentManager: FragmentManager) { + val fragment = MuteUntilTimePickerBottomSheet() + fragment.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) + } + } + + @Composable + override fun SheetContent() { + val context = LocalContext.current + val now = remember { LocalDateTime.now() } + + val defaultDateTime = remember { + if (now.hour < 17) { + now.withHour(17).withMinute(0).withSecond(0).withNano(0) + } else { + val nextMorning = if (now.dayOfWeek == DayOfWeek.FRIDAY || now.dayOfWeek == DayOfWeek.SATURDAY || now.dayOfWeek == DayOfWeek.SUNDAY) { + now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)) + } else { + now.plusDays(1) + } + nextMorning.withHour(8).withMinute(0).withSecond(0).withNano(0) + } + } + + var selectedDate by remember { mutableLongStateOf(defaultDateTime.toMillis()) } + var selectedHour by remember { mutableIntStateOf(defaultDateTime.hour) } + var selectedMinute by remember { mutableIntStateOf(defaultDateTime.minute) } + + val dateText = remember(selectedDate) { + DateUtils.getDayPrecisionTimeString(context, Locale.getDefault(), selectedDate) + } + + val timeText = remember(selectedHour, selectedMinute) { + LocalTime.of(selectedHour, selectedMinute).formatHours(context) + } + + val zonedDateTime = remember { ZonedDateTime.now() } + val timezoneDisclaimer = remember { + val zoneOffsetFormatter = DateTimeFormatter.ofPattern("OOOO") + val zoneNameFormatter = DateTimeFormatter.ofPattern("zzzz") + context.getString( + R.string.MuteUntilTimePickerBottomSheet__timezone_disclaimer, + zoneOffsetFormatter.format(zonedDateTime), + zoneNameFormatter.format(zonedDateTime) + ) + } + + MuteUntilSheetContent( + dateText = dateText, + timeText = timeText, + timezoneDisclaimer = timezoneDisclaimer, + onDateClick = { + val local = LocalDateTime.now().atMidnight().atUTC().toMillis() + val datePicker = MaterialDatePicker.Builder.datePicker() + .setTitleText(context.getString(R.string.MuteUntilTimePickerBottomSheet__select_date_title)) + .setSelection(selectedDate) + .setCalendarConstraints(CalendarConstraints.Builder().setStart(local).setValidator(DateValidatorPointForward.now()).build()) + .build() + + datePicker.addOnDismissListener { + datePicker.clearOnDismissListeners() + datePicker.clearOnPositiveButtonClickListeners() + } + + datePicker.addOnPositiveButtonClickListener { + selectedDate = it.toLocalDateTime(ZoneOffset.UTC).atZone(ZoneId.systemDefault()).toMillis() + } + datePicker.show(childFragmentManager, "DATE_PICKER") + }, + onTimeClick = { + val timeFormat = if (DateFormat.is24HourFormat(context)) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H + val timePicker = MaterialTimePicker.Builder() + .setTimeFormat(timeFormat) + .setHour(selectedHour) + .setMinute(selectedMinute) + .setTitleText(context.getString(R.string.MuteUntilTimePickerBottomSheet__select_time_title)) + .build() + + timePicker.addOnDismissListener { + timePicker.clearOnDismissListeners() + timePicker.clearOnPositiveButtonClickListeners() + } + + timePicker.addOnPositiveButtonClickListener { + selectedHour = timePicker.hour + selectedMinute = timePicker.minute + } + timePicker.show(childFragmentManager, "TIME_PICKER") + }, + onMuteClick = { + val timestamp = selectedDate.toLocalDateTime() + .withHour(selectedHour) + .withMinute(selectedMinute) + .withSecond(0) + .withNano(0) + .toMillis() + + if (timestamp > System.currentTimeMillis()) { + setFragmentResult(REQUEST_KEY, bundleOf(RESULT_TIMESTAMP to timestamp)) + dismissAllowingStateLoss() + } + } + ) + } +} + +@Composable +private fun MuteUntilSheetContent( + dateText: String, + timeText: String, + timezoneDisclaimer: String, + onDateClick: () -> Unit, + onTimeClick: () -> Unit, + onMuteClick: () -> Unit +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth() + ) { + BottomSheets.Handle() + + Text( + text = stringResource(R.string.MuteUntilTimePickerBottomSheet__dialog_title), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 18.dp, bottom = 24.dp) + ) + + Text( + text = timezoneDisclaimer, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f), + modifier = Modifier + .padding(horizontal = 56.dp) + .align(Alignment.Start) + ) + + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp, vertical = 16.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.clickable(onClick = onDateClick) + ) { + Text( + text = dateText, + style = MaterialTheme.typography.bodyLarge + ) + Icon( + painter = painterResource(R.drawable.ic_expand_down_24), + contentDescription = null, + modifier = Modifier + .padding(start = 8.dp) + .size(24.dp) + ) + } + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.clickable(onClick = onTimeClick) + ) { + Text( + text = timeText, + style = MaterialTheme.typography.bodyLarge + ) + Icon( + painter = painterResource(R.drawable.ic_expand_down_24), + contentDescription = null, + modifier = Modifier + .padding(start = 8.dp) + .size(24.dp) + ) + } + } + + Row( + horizontalArrangement = Arrangement.End, + modifier = Modifier + .fillMaxWidth() + .padding(start = 18.dp, end = 18.dp, top = 14.dp, bottom = 24.dp) + ) { + Buttons.MediumTonal( + onClick = onMuteClick + ) { + Text(stringResource(R.string.MuteUntilTimePickerBottomSheet__mute_notifications)) + } + } + } +} + +@DayNightPreviews +@Composable +private fun MuteUntilSheetContentPreview() { + Previews.BottomSheetContentPreview { + MuteUntilSheetContent( + dateText = "Today", + timeText = "5:00 PM", + timezoneDisclaimer = "All times in (GMT-05:00) Eastern Standard Time", + onDateClick = {}, + onTimeClick = {}, + onMuteClick = {} + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/ButtonStripPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/ButtonStripPreference.kt index ec666b43e9..208ade467a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/ButtonStripPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/ButtonStripPreference.kt @@ -30,7 +30,7 @@ object ButtonStripPreference { val onMessageClick: () -> Unit = {}, val onVideoClick: () -> Unit = {}, val onAudioClick: () -> Unit = {}, - val onMuteClick: () -> Unit = {}, + val onMuteClick: (View) -> Unit = {}, val onSearchClick: () -> Unit = {} ) : PreferenceModel() { override fun areContentsTheSame(newItem: Model): Boolean { @@ -97,7 +97,7 @@ object ButtonStripPreference { message.setOnClickListener { model.onMessageClick() } videoCall.setOnClickListener { model.onVideoClick() } audioCall.setOnClickListener { model.onAudioClick() } - mute.setOnClickListener { model.onMuteClick() } + mute.setOnClickListener { model.onMuteClick(it) } search.setOnClickListener { model.onSearchClick() } addToStory.setOnClickListener { model.onAddToStoryClick() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/sounds/SoundsAndNotificationsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/sounds/SoundsAndNotificationsSettingsFragment.kt index 1965832a79..db8f2a9335 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/sounds/SoundsAndNotificationsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/sounds/SoundsAndNotificationsSettingsFragment.kt @@ -16,9 +16,10 @@ import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter import org.thoughtcrime.securesms.util.navigation.safeNavigate -class SoundsAndNotificationsSettingsFragment : DSLSettingsFragment( - titleId = R.string.ConversationSettingsFragment__sounds_and_notifications -) { +class SoundsAndNotificationsSettingsFragment : + DSLSettingsFragment( + titleId = R.string.ConversationSettingsFragment__sounds_and_notifications + ) { private val mentionLabels: Array by lazy { resources.getStringArray(R.array.SoundsAndNotificationsSettingsFragment__mention_labels) @@ -66,7 +67,7 @@ class SoundsAndNotificationsSettingsFragment : DSLSettingsFragment( summary = DSLSettingsText.from(muteSummary), onClick = { if (state.muteUntil <= 0) { - MuteDialog.show(requireContext(), viewModel::setMuteUntil) + MuteDialog.show(requireContext(), childFragmentManager, viewLifecycleOwner, viewModel::setMuteUntil) } else { MaterialAlertDialogBuilder(requireContext()) .setMessage(muteSummary) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index 9c88fc9d93..15bdeddb4f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -3992,7 +3992,7 @@ class ConversationFragment : } override fun handleMuteNotifications() { - MuteDialog.show(requireContext(), viewModel::muteConversation) + MuteDialog.show(requireContext(), childFragmentManager, viewLifecycleOwner, viewModel::muteConversation) } override fun handleUnmuteNotifications() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 2cf881399c..dbbb92c958 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -1177,9 +1177,7 @@ public class ConversationListFragment extends MainFragment implements Conversati } private void handleMute(@NonNull Collection conversations) { - MuteDialog.show(requireContext(), until -> { - updateMute(conversations, until); - }); + MuteDialog.show(requireContext(), getChildFragmentManager(), getViewLifecycleOwner(), until -> updateMute(conversations, until)); } private void handleUnmute(@NonNull Collection conversations) { @@ -1612,7 +1610,7 @@ public class ConversationListFragment extends MainFragment implements Conversati @Override public void onMuteAll(@NonNull ChatFolderRecord chatFolder) { - MuteDialog.show(requireContext(), until -> viewModel.onUpdateMute(chatFolder, until)); + MuteDialog.show(requireContext(), getChildFragmentManager(), getViewLifecycleOwner(), until -> viewModel.onUpdateMute(chatFolder, until)); } @Override @@ -1945,6 +1943,7 @@ public class ConversationListFragment extends MainFragment implements Conversati void onMultiSelectFinished(); } + } diff --git a/app/src/main/res/drawable/symbol_calendar_one.xml b/app/src/main/res/drawable/symbol_calendar_one.xml new file mode 100644 index 0000000000..ce7c0f771c --- /dev/null +++ b/app/src/main/res/drawable/symbol_calendar_one.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/symbol_calendar_week.xml b/app/src/main/res/drawable/symbol_calendar_week.xml new file mode 100644 index 0000000000..bfa3e38c14 --- /dev/null +++ b/app/src/main/res/drawable/symbol_calendar_week.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/layout/mute_dialog_item.xml b/app/src/main/res/layout/mute_dialog_item.xml new file mode 100644 index 0000000000..6cbbc7dca8 --- /dev/null +++ b/app/src/main/res/layout/mute_dialog_item.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9d95155ed8..a191db5655 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3212,7 +3212,22 @@ Message encrypted for non-existing session + Mute notifications + + Mute until… + + + + Mute notifications until… + + Select date + + Select time + + Mute notifications + + All times in (%1$s) %2$s Touch to open.