kopia lustrzana https://github.com/ryukoposting/Signal-Android
386 wiersze
16 KiB
Kotlin
386 wiersze
16 KiB
Kotlin
package org.thoughtcrime.securesms.components.settings.app.notifications
|
|
|
|
import android.app.Activity
|
|
import android.content.ActivityNotFoundException
|
|
import android.content.Intent
|
|
import android.graphics.ColorFilter
|
|
import android.graphics.PorterDuff
|
|
import android.graphics.PorterDuffColorFilter
|
|
import android.media.Ringtone
|
|
import android.media.RingtoneManager
|
|
import android.net.Uri
|
|
import android.os.Build
|
|
import android.provider.Settings
|
|
import android.text.TextUtils
|
|
import android.view.View
|
|
import android.widget.Toast
|
|
import androidx.annotation.RequiresApi
|
|
import androidx.core.content.ContextCompat
|
|
import androidx.lifecycle.ViewModelProvider
|
|
import androidx.navigation.fragment.findNavController
|
|
import androidx.preference.PreferenceManager
|
|
import org.signal.core.util.getParcelableExtraCompat
|
|
import org.signal.core.util.logging.Log
|
|
import org.thoughtcrime.securesms.R
|
|
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
|
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
|
import org.thoughtcrime.securesms.components.settings.PreferenceViewHolder
|
|
import org.thoughtcrime.securesms.components.settings.RadioListPreference
|
|
import org.thoughtcrime.securesms.components.settings.RadioListPreferenceViewHolder
|
|
import org.thoughtcrime.securesms.components.settings.configure
|
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
|
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
|
import org.thoughtcrime.securesms.util.RingtoneUtil
|
|
import org.thoughtcrime.securesms.util.ViewUtil
|
|
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
|
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
|
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
|
|
|
private const val MESSAGE_SOUND_SELECT: Int = 1
|
|
private const val CALL_RINGTONE_SELECT: Int = 2
|
|
private val TAG = Log.tag(NotificationsSettingsFragment::class.java)
|
|
|
|
class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__notifications) {
|
|
|
|
private val repeatAlertsValues by lazy { resources.getStringArray(R.array.pref_repeat_alerts_values) }
|
|
private val repeatAlertsLabels by lazy { resources.getStringArray(R.array.pref_repeat_alerts_entries) }
|
|
|
|
private val notificationPrivacyValues by lazy { resources.getStringArray(R.array.pref_notification_privacy_values) }
|
|
private val notificationPrivacyLabels by lazy { resources.getStringArray(R.array.pref_notification_privacy_entries) }
|
|
|
|
private val notificationPriorityValues by lazy { resources.getStringArray(R.array.pref_notification_priority_values) }
|
|
private val notificationPriorityLabels by lazy { resources.getStringArray(R.array.pref_notification_priority_entries) }
|
|
|
|
private val ledColorValues by lazy { resources.getStringArray(R.array.pref_led_color_values) }
|
|
private val ledColorLabels by lazy { resources.getStringArray(R.array.pref_led_color_entries) }
|
|
|
|
private val ledBlinkValues by lazy { resources.getStringArray(R.array.pref_led_blink_pattern_values) }
|
|
private val ledBlinkLabels by lazy { resources.getStringArray(R.array.pref_led_blink_pattern_entries) }
|
|
|
|
private lateinit var viewModel: NotificationsSettingsViewModel
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
if (requestCode == MESSAGE_SOUND_SELECT && resultCode == Activity.RESULT_OK && data != null) {
|
|
val uri: Uri? = data.getParcelableExtraCompat(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, Uri::class.java)
|
|
viewModel.setMessageNotificationsSound(uri)
|
|
} else if (requestCode == CALL_RINGTONE_SELECT && resultCode == Activity.RESULT_OK && data != null) {
|
|
val uri: Uri? = data.getParcelableExtraCompat(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, Uri::class.java)
|
|
viewModel.setCallRingtone(uri)
|
|
}
|
|
}
|
|
|
|
override fun bindAdapter(adapter: MappingAdapter) {
|
|
adapter.registerFactory(
|
|
LedColorPreference::class.java,
|
|
LayoutFactory(::LedColorPreferenceViewHolder, R.layout.dsl_preference_item)
|
|
)
|
|
|
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
|
val factory = NotificationsSettingsViewModel.Factory(sharedPreferences)
|
|
|
|
viewModel = ViewModelProvider(this, factory)[NotificationsSettingsViewModel::class.java]
|
|
|
|
viewModel.state.observe(viewLifecycleOwner) {
|
|
adapter.submitList(getConfiguration(it).toMappingModelList())
|
|
}
|
|
}
|
|
|
|
private fun getConfiguration(state: NotificationsSettingsState): DSLConfiguration {
|
|
return configure {
|
|
sectionHeaderPref(R.string.NotificationsSettingsFragment__messages)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from(R.string.preferences__notifications),
|
|
isChecked = state.messageNotificationsState.notificationsEnabled,
|
|
onClick = {
|
|
viewModel.setMessageNotificationsEnabled(!state.messageNotificationsState.notificationsEnabled)
|
|
}
|
|
)
|
|
|
|
if (Build.VERSION.SDK_INT >= 30) {
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.preferences__customize),
|
|
summary = DSLSettingsText.from(R.string.preferences__change_sound_and_vibration),
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onClick = {
|
|
NotificationChannels.getInstance().openChannelSettings(requireActivity(), NotificationChannels.getInstance().messagesChannel, null)
|
|
}
|
|
)
|
|
} else {
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.preferences__sound),
|
|
summary = DSLSettingsText.from(getRingtoneSummary(state.messageNotificationsState.sound)),
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onClick = {
|
|
launchMessageSoundSelectionIntent()
|
|
}
|
|
)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from(R.string.preferences__vibrate),
|
|
isChecked = state.messageNotificationsState.vibrateEnabled,
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onClick = {
|
|
viewModel.setMessageNotificationVibration(!state.messageNotificationsState.vibrateEnabled)
|
|
}
|
|
)
|
|
|
|
customPref(
|
|
LedColorPreference(
|
|
colorValues = ledColorValues,
|
|
radioListPreference = RadioListPreference(
|
|
title = DSLSettingsText.from(R.string.preferences__led_color),
|
|
listItems = ledColorLabels,
|
|
selected = ledColorValues.indexOf(state.messageNotificationsState.ledColor),
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onSelected = {
|
|
viewModel.setMessageNotificationLedColor(ledColorValues[it])
|
|
}
|
|
)
|
|
)
|
|
)
|
|
|
|
if (!NotificationChannels.supported()) {
|
|
radioListPref(
|
|
title = DSLSettingsText.from(R.string.preferences__pref_led_blink_title),
|
|
listItems = ledBlinkLabels,
|
|
selected = ledBlinkValues.indexOf(state.messageNotificationsState.ledBlink),
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onSelected = {
|
|
viewModel.setMessageNotificationLedBlink(ledBlinkValues[it])
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from(R.string.preferences_notifications__in_chat_sounds),
|
|
isChecked = state.messageNotificationsState.inChatSoundsEnabled,
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onClick = {
|
|
viewModel.setMessageNotificationInChatSoundsEnabled(!state.messageNotificationsState.inChatSoundsEnabled)
|
|
}
|
|
)
|
|
|
|
radioListPref(
|
|
title = DSLSettingsText.from(R.string.preferences__repeat_alerts),
|
|
listItems = repeatAlertsLabels,
|
|
selected = repeatAlertsValues.indexOf(state.messageNotificationsState.repeatAlerts.toString()),
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onSelected = {
|
|
viewModel.setMessageRepeatAlerts(repeatAlertsValues[it].toInt())
|
|
}
|
|
)
|
|
|
|
radioListPref(
|
|
title = DSLSettingsText.from(R.string.preferences_notifications__show),
|
|
listItems = notificationPrivacyLabels,
|
|
selected = notificationPrivacyValues.indexOf(state.messageNotificationsState.messagePrivacy),
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onSelected = {
|
|
viewModel.setMessageNotificationPrivacy(notificationPrivacyValues[it])
|
|
}
|
|
)
|
|
|
|
if (Build.VERSION.SDK_INT < 30) {
|
|
if (NotificationChannels.supported()) {
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.preferences_notifications__priority),
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onClick = {
|
|
launchNotificationPriorityIntent()
|
|
}
|
|
)
|
|
} else {
|
|
radioListPref(
|
|
title = DSLSettingsText.from(R.string.preferences_notifications__priority),
|
|
listItems = notificationPriorityLabels,
|
|
selected = notificationPriorityValues.indexOf(state.messageNotificationsState.priority.toString()),
|
|
isEnabled = state.messageNotificationsState.notificationsEnabled,
|
|
onSelected = {
|
|
viewModel.setMessageNotificationPriority(notificationPriorityValues[it].toInt())
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(R.string.NotificationsSettingsFragment__calls)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from(R.string.preferences__notifications),
|
|
isChecked = state.callNotificationsState.notificationsEnabled,
|
|
onClick = {
|
|
viewModel.setCallNotificationsEnabled(!state.callNotificationsState.notificationsEnabled)
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.preferences_notifications__ringtone),
|
|
summary = DSLSettingsText.from(getRingtoneSummary(state.callNotificationsState.ringtone)),
|
|
isEnabled = state.callNotificationsState.notificationsEnabled,
|
|
onClick = {
|
|
launchCallRingtoneSelectionIntent()
|
|
}
|
|
)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from(R.string.preferences__vibrate),
|
|
isChecked = state.callNotificationsState.vibrateEnabled,
|
|
isEnabled = state.callNotificationsState.notificationsEnabled,
|
|
onClick = {
|
|
viewModel.setCallVibrateEnabled(!state.callNotificationsState.vibrateEnabled)
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(R.string.NotificationsSettingsFragment__notification_profiles)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.NotificationsSettingsFragment__profiles),
|
|
summary = DSLSettingsText.from(R.string.NotificationsSettingsFragment__create_a_profile_to_receive_notifications_only_from_people_and_groups_you_choose),
|
|
onClick = {
|
|
findNavController().safeNavigate(R.id.action_notificationsSettingsFragment_to_notificationProfilesFragment)
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(R.string.NotificationsSettingsFragment__notify_when)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from(R.string.NotificationsSettingsFragment__contact_joins_signal),
|
|
isChecked = state.notifyWhenContactJoinsSignal,
|
|
onClick = {
|
|
viewModel.setNotifyWhenContactJoinsSignal(!state.notifyWhenContactJoinsSignal)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun getRingtoneSummary(uri: Uri): String {
|
|
return if (TextUtils.isEmpty(uri.toString())) {
|
|
getString(R.string.preferences__silent)
|
|
} else {
|
|
val tone: Ringtone? = RingtoneUtil.getRingtone(requireContext(), uri)
|
|
if (tone != null) {
|
|
try {
|
|
tone.getTitle(requireContext()) ?: getString(R.string.NotificationsSettingsFragment__unknown_ringtone)
|
|
} catch (e: SecurityException) {
|
|
Log.w(TAG, "Unable to get title for ringtone", e)
|
|
return getString(R.string.NotificationsSettingsFragment__unknown_ringtone)
|
|
}
|
|
} else {
|
|
getString(R.string.preferences__default)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun launchMessageSoundSelectionIntent() {
|
|
val current = SignalStore.settings().messageNotificationSound
|
|
|
|
val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION)
|
|
intent.putExtra(
|
|
RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
|
|
Settings.System.DEFAULT_NOTIFICATION_URI
|
|
)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, current)
|
|
|
|
openRingtonePicker(intent, MESSAGE_SOUND_SELECT)
|
|
}
|
|
|
|
@RequiresApi(26)
|
|
private fun launchNotificationPriorityIntent() {
|
|
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
|
intent.putExtra(
|
|
Settings.EXTRA_CHANNEL_ID,
|
|
NotificationChannels.getInstance().messagesChannel
|
|
)
|
|
intent.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
|
|
startActivity(intent)
|
|
}
|
|
|
|
private fun launchCallRingtoneSelectionIntent() {
|
|
val current = SignalStore.settings().callRingtone
|
|
|
|
val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE)
|
|
intent.putExtra(
|
|
RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
|
|
Settings.System.DEFAULT_RINGTONE_URI
|
|
)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, current)
|
|
|
|
openRingtonePicker(intent, CALL_RINGTONE_SELECT)
|
|
}
|
|
|
|
@Suppress("DEPRECATION")
|
|
private fun openRingtonePicker(intent: Intent, requestCode: Int) {
|
|
try {
|
|
startActivityForResult(intent, requestCode)
|
|
} catch (e: ActivityNotFoundException) {
|
|
Toast.makeText(requireContext(), R.string.NotificationSettingsFragment__failed_to_open_picker, Toast.LENGTH_LONG).show()
|
|
}
|
|
}
|
|
|
|
private class LedColorPreference(
|
|
val colorValues: Array<String>,
|
|
val radioListPreference: RadioListPreference
|
|
) : PreferenceModel<LedColorPreference>(
|
|
title = radioListPreference.title,
|
|
icon = radioListPreference.icon,
|
|
summary = radioListPreference.summary
|
|
) {
|
|
override fun areContentsTheSame(newItem: LedColorPreference): Boolean {
|
|
return super.areContentsTheSame(newItem) && radioListPreference.areContentsTheSame(newItem.radioListPreference)
|
|
}
|
|
}
|
|
|
|
private class LedColorPreferenceViewHolder(itemView: View) :
|
|
PreferenceViewHolder<LedColorPreference>(itemView) {
|
|
|
|
val radioListPreferenceViewHolder = RadioListPreferenceViewHolder(itemView)
|
|
|
|
override fun bind(model: LedColorPreference) {
|
|
super.bind(model)
|
|
radioListPreferenceViewHolder.bind(model.radioListPreference)
|
|
|
|
summaryView.visibility = View.GONE
|
|
|
|
val circleDrawable = requireNotNull(ContextCompat.getDrawable(context, R.drawable.circle_tintable))
|
|
circleDrawable.setBounds(0, 0, ViewUtil.dpToPx(20), ViewUtil.dpToPx(20))
|
|
circleDrawable.colorFilter = model.colorValues[model.radioListPreference.selected].toColorFilter()
|
|
|
|
if (ViewUtil.isLtr(itemView)) {
|
|
titleView.setCompoundDrawables(null, null, circleDrawable, null)
|
|
} else {
|
|
titleView.setCompoundDrawables(circleDrawable, null, null, null)
|
|
}
|
|
}
|
|
|
|
private fun String.toColorFilter(): ColorFilter {
|
|
val color = when (this) {
|
|
"green" -> ContextCompat.getColor(context, R.color.green_500)
|
|
"red" -> ContextCompat.getColor(context, R.color.red_500)
|
|
"blue" -> ContextCompat.getColor(context, R.color.blue_500)
|
|
"yellow" -> ContextCompat.getColor(context, R.color.yellow_500)
|
|
"cyan" -> ContextCompat.getColor(context, R.color.cyan_500)
|
|
"magenta" -> ContextCompat.getColor(context, R.color.pink_500)
|
|
"white" -> ContextCompat.getColor(context, R.color.white)
|
|
else -> ContextCompat.getColor(context, R.color.transparent)
|
|
}
|
|
|
|
return PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
|
|
}
|
|
}
|
|
}
|