Dark mode

This commit is contained in:
Philipp Heckel 2022-01-19 21:05:41 -05:00
parent 71b8ea1e6d
commit 2d9171311f
14 changed files with 170 additions and 53 deletions

View file

@ -118,7 +118,7 @@
android:resource="@drawable/ic_notification"/> android:resource="@drawable/ic_notification"/>
<!-- FileProvider required for older Android versions (<= P), to allow passing the file URI in the open intent. <!-- FileProvider required for older Android versions (<= P), to allow passing the file URI in the open intent.
Avoids "exposed beyong app through Intent.getData" exception, see see https://stackoverflow.com/a/57288352/1440785 --> Avoids "exposed beyond app through Intent.getData" exception, see see https://stackoverflow.com/a/57288352/1440785 -->
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider" android:authorities="${applicationId}.provider"

View file

@ -5,8 +5,10 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.* import androidx.lifecycle.*
import io.heckel.ntfy.log.Log import io.heckel.ntfy.log.Log
import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
@ -179,6 +181,22 @@ class Repository(private val sharedPrefs: SharedPreferences, private val subscri
.apply() .apply()
} }
fun setDarkMode(mode: Int) {
if (mode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
sharedPrefs.edit()
.remove(SHARED_PREFS_DARK_MODE)
.apply()
} else {
sharedPrefs.edit()
.putInt(SHARED_PREFS_DARK_MODE, mode)
.apply()
}
}
fun getDarkMode(): Int {
return sharedPrefs.getInt(SHARED_PREFS_DARK_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
fun getWakelockEnabled(): Boolean { fun getWakelockEnabled(): Boolean {
return sharedPrefs.getBoolean(SHARED_PREFS_WAKELOCK_ENABLED, false) // Disabled by default! return sharedPrefs.getBoolean(SHARED_PREFS_WAKELOCK_ENABLED, false) // Disabled by default!
} }
@ -358,12 +376,17 @@ class Repository(private val sharedPrefs: SharedPreferences, private val subscri
const val SHARED_PREFS_AUTO_DOWNLOAD_MAX_SIZE = "AutoDownload" const val SHARED_PREFS_AUTO_DOWNLOAD_MAX_SIZE = "AutoDownload"
const val SHARED_PREFS_WAKELOCK_ENABLED = "WakelockEnabled" const val SHARED_PREFS_WAKELOCK_ENABLED = "WakelockEnabled"
const val SHARED_PREFS_CONNECTION_PROTOCOL = "ConnectionProtocol" const val SHARED_PREFS_CONNECTION_PROTOCOL = "ConnectionProtocol"
const val SHARED_PREFS_DARK_MODE = "DarkMode"
const val SHARED_PREFS_BROADCAST_ENABLED = "BroadcastEnabled" const val SHARED_PREFS_BROADCAST_ENABLED = "BroadcastEnabled"
const val SHARED_PREFS_RECORD_LOGS_ENABLED = "RecordLogs" const val SHARED_PREFS_RECORD_LOGS_ENABLED = "RecordLogs"
const val SHARED_PREFS_BATTERY_OPTIMIZATIONS_REMIND_TIME = "BatteryOptimizationsRemindTime" const val SHARED_PREFS_BATTERY_OPTIMIZATIONS_REMIND_TIME = "BatteryOptimizationsRemindTime"
const val SHARED_PREFS_UNIFIED_PUSH_ENABLED = "UnifiedPushEnabled" const val SHARED_PREFS_UNIFIED_PUSH_ENABLED = "UnifiedPushEnabled"
const val SHARED_PREFS_UNIFIED_PUSH_BASE_URL = "UnifiedPushBaseURL" const val SHARED_PREFS_UNIFIED_PUSH_BASE_URL = "UnifiedPushBaseURL"
const val MUTED_UNTIL_SHOW_ALL = 0L
const val MUTED_UNTIL_FOREVER = 1L
const val MUTED_UNTIL_TOMORROW = 2L
const val AUTO_DOWNLOAD_NEVER = 0L const val AUTO_DOWNLOAD_NEVER = 0L
const val AUTO_DOWNLOAD_ALWAYS = 1L const val AUTO_DOWNLOAD_ALWAYS = 1L
const val AUTO_DOWNLOAD_DEFAULT = 1024 * 1024L // Must match a value in values.xml const val AUTO_DOWNLOAD_DEFAULT = 1024 * 1024L // Must match a value in values.xml

View file

@ -22,6 +22,7 @@ import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.Notification import io.heckel.ntfy.db.Notification
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.firebase.FirebaseMessenger import io.heckel.ntfy.firebase.FirebaseMessenger
import io.heckel.ntfy.log.Log import io.heckel.ntfy.log.Log
import io.heckel.ntfy.msg.ApiService import io.heckel.ntfy.msg.ApiService
@ -289,7 +290,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
notificationFragment.show(supportFragmentManager, NotificationFragment.TAG) notificationFragment.show(supportFragmentManager, NotificationFragment.TAG)
} else { } else {
Log.d(TAG, "Re-enabling notifications ${topicShortUrl(subscriptionBaseUrl, subscriptionTopic)}") Log.d(TAG, "Re-enabling notifications ${topicShortUrl(subscriptionBaseUrl, subscriptionTopic)}")
onNotificationMutedUntilChanged(0L) onNotificationMutedUntilChanged(Repository.MUTED_UNTIL_SHOW_ALL)
} }
} }

View file

@ -16,6 +16,9 @@ import android.widget.Button
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -150,6 +153,9 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
// Subscribe to control Firebase channel (so we can re-start the foreground service if it dies) // Subscribe to control Firebase channel (so we can re-start the foreground service if it dies)
messenger.subscribe(ApiService.CONTROL_TOPIC) messenger.subscribe(ApiService.CONTROL_TOPIC)
// Darrkkkk mode
AppCompatDelegate.setDefaultNightMode(repository.getDarkMode())
// Background things // Background things
startPeriodicPollWorker() startPeriodicPollWorker()
startPeriodicServiceRestartWorker() startPeriodicServiceRestartWorker()
@ -304,7 +310,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
notificationFragment.show(supportFragmentManager, NotificationFragment.TAG) notificationFragment.show(supportFragmentManager, NotificationFragment.TAG)
} else { } else {
Log.d(TAG, "Re-enabling global notifications") Log.d(TAG, "Re-enabling global notifications")
onNotificationMutedUntilChanged(0L) onNotificationMutedUntilChanged(Repository.MUTED_UNTIL_SHOW_ALL)
} }
} }

View file

@ -64,6 +64,7 @@ class NotificationFragment : DialogFragment() {
muteUntilTomorrowButton = view.findViewById(R.id.notification_dialog_tomorrow) muteUntilTomorrowButton = view.findViewById(R.id.notification_dialog_tomorrow)
muteUntilTomorrowButton.setOnClickListener { muteUntilTomorrowButton.setOnClickListener {
// Duplicate code in SettingsActivity, :shrug: ...
val date = Calendar.getInstance() val date = Calendar.getInstance()
date.add(Calendar.DAY_OF_MONTH, 1) date.add(Calendar.DAY_OF_MONTH, 1)
date.set(Calendar.HOUR_OF_DAY, 8) date.set(Calendar.HOUR_OF_DAY, 8)
@ -74,7 +75,7 @@ class NotificationFragment : DialogFragment() {
} }
muteForeverButton = view.findViewById(R.id.notification_dialog_forever) muteForeverButton = view.findViewById(R.id.notification_dialog_forever)
muteForeverButton.setOnClickListener{ onClick(1) } muteForeverButton.setOnClickListener{ onClick(Repository.MUTED_UNTIL_FOREVER) }
return AlertDialog.Builder(activity) return AlertDialog.Builder(activity)
.setView(view) .setView(view)

View file

@ -12,9 +12,9 @@ import android.text.TextUtils
import android.widget.Toast import android.widget.Toast
import androidx.annotation.Keep import androidx.annotation.Keep
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.* import androidx.preference.*
import androidx.preference.Preference.OnPreferenceClickListener import androidx.preference.Preference.OnPreferenceClickListener
@ -32,6 +32,7 @@ import kotlinx.coroutines.launch
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
@ -44,7 +45,7 @@ class SettingsActivity : AppCompatActivity() {
Log.d(TAG, "Create $this") Log.d(TAG, "Create $this")
if (savedInstanceState == null) { if (savedInstanceState == null) {
fragment = SettingsFragment(supportFragmentManager) fragment = SettingsFragment()
supportFragmentManager supportFragmentManager
.beginTransaction() .beginTransaction()
.replace(R.id.settings_layout, fragment) .replace(R.id.settings_layout, fragment)
@ -58,7 +59,7 @@ class SettingsActivity : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
} }
class SettingsFragment(private val supportFragmentManager: FragmentManager) : PreferenceFragmentCompat() { class SettingsFragment : PreferenceFragmentCompat() {
private lateinit var repository: Repository private lateinit var repository: Repository
private var autoDownloadSelection = AUTO_DOWNLOAD_SELECTION_NOT_SET private var autoDownloadSelection = AUTO_DOWNLOAD_SELECTION_NOT_SET
@ -75,34 +76,43 @@ class SettingsActivity : AppCompatActivity() {
// Notifications muted until (global) // Notifications muted until (global)
val mutedUntilPrefId = context?.getString(R.string.settings_notifications_muted_until_key) ?: return val mutedUntilPrefId = context?.getString(R.string.settings_notifications_muted_until_key) ?: return
val mutedUntilSummary = { s: Long -> val mutedUntil: ListPreference? = findPreference(mutedUntilPrefId)
when (s) { mutedUntil?.value = repository.getGlobalMutedUntil().toString()
0L -> getString(R.string.settings_notifications_muted_until_enabled) mutedUntil?.preferenceDataStore = object : PreferenceDataStore() {
1L -> getString(R.string.settings_notifications_muted_until_disabled_forever) override fun putString(key: String?, value: String?) {
else -> { val mutedUntilValue = value?.toLongOrNull() ?:return
val formattedDate = formatDateShort(s) when (mutedUntilValue) {
getString(R.string.settings_notifications_muted_until_disabled_until, formattedDate) Repository.MUTED_UNTIL_SHOW_ALL -> repository.setGlobalMutedUntil(mutedUntilValue)
} Repository.MUTED_UNTIL_FOREVER -> repository.setGlobalMutedUntil(mutedUntilValue)
} Repository.MUTED_UNTIL_TOMORROW -> {
} val date = Calendar.getInstance()
val mutedUntil: Preference? = findPreference(mutedUntilPrefId) date.add(Calendar.DAY_OF_MONTH, 1)
mutedUntil?.preferenceDataStore = object : PreferenceDataStore() { } // Dummy store to protect from accidentally overwriting date.set(Calendar.HOUR_OF_DAY, 8)
mutedUntil?.summary = mutedUntilSummary(repository.getGlobalMutedUntil()) date.set(Calendar.MINUTE, 30)
mutedUntil?.onPreferenceClickListener = OnPreferenceClickListener { date.set(Calendar.SECOND, 0)
if (repository.getGlobalMutedUntil() > 0) { date.set(Calendar.MILLISECOND, 0)
repository.setGlobalMutedUntil(0) repository.setGlobalMutedUntil(date.timeInMillis/1000)
mutedUntil?.summary = mutedUntilSummary(0) }
} else { else -> {
val notificationFragment = NotificationFragment() val mutedUntilTimestamp = System.currentTimeMillis()/1000 + mutedUntilValue * 60
notificationFragment.settingsListener = object : NotificationFragment.NotificationSettingsListener {
override fun onNotificationMutedUntilChanged(mutedUntilTimestamp: Long) {
repository.setGlobalMutedUntil(mutedUntilTimestamp) repository.setGlobalMutedUntil(mutedUntilTimestamp)
mutedUntil?.summary = mutedUntilSummary(mutedUntilTimestamp)
} }
} }
notificationFragment.show(supportFragmentManager, NotificationFragment.TAG)
} }
true override fun getString(key: String?, defValue: String?): String {
return repository.getGlobalMutedUntil().toString()
}
}
mutedUntil?.summaryProvider = Preference.SummaryProvider<ListPreference> { _ ->
val mutedUntilValue = repository.getGlobalMutedUntil()
when (mutedUntilValue) {
Repository.MUTED_UNTIL_SHOW_ALL -> getString(R.string.settings_notifications_muted_until_show_all)
Repository.MUTED_UNTIL_FOREVER -> getString(R.string.settings_notifications_muted_until_forever)
else -> {
val formattedDate = formatDateShort(mutedUntilValue)
getString(R.string.settings_notifications_muted_until_x, formattedDate)
}
}
} }
// Minimum priority // Minimum priority
@ -163,6 +173,30 @@ class SettingsActivity : AppCompatActivity() {
} }
} }
// Dark mode
val darkModePrefId = context?.getString(R.string.settings_appearance_dark_mode_key) ?: return
val darkMode: ListPreference? = findPreference(darkModePrefId)
darkMode?.value = repository.getDarkMode().toString()
darkMode?.preferenceDataStore = object : PreferenceDataStore() {
override fun putString(key: String?, value: String?) {
val darkModeValue = value?.toIntOrNull() ?: return
repository.setDarkMode(darkModeValue)
AppCompatDelegate.setDefaultNightMode(darkModeValue)
}
override fun getString(key: String?, defValue: String?): String {
return repository.getDarkMode().toString()
}
}
darkMode?.summaryProvider = Preference.SummaryProvider<ListPreference> { pref ->
val darkModeValue = pref.value.toIntOrNull() ?: repository.getDarkMode()
when (darkModeValue) {
AppCompatDelegate.MODE_NIGHT_NO -> getString(R.string.settings_appearance_dark_mode_summary_light)
AppCompatDelegate.MODE_NIGHT_YES -> getString(R.string.settings_appearance_dark_mode_summary_dark)
else -> getString(R.string.settings_appearance_dark_mode_summary_system)
}
}
// UnifiedPush enabled // UnifiedPush enabled
val upEnabledPrefId = context?.getString(R.string.settings_unified_push_enabled_key) ?: return val upEnabledPrefId = context?.getString(R.string.settings_unified_push_enabled_key) ?: return
val upEnabled: SwitchPreference? = findPreference(upEnabledPrefId) val upEnabled: SwitchPreference? = findPreference(upEnabledPrefId)

View file

@ -42,7 +42,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/detail_item_message_text" android:id="@+id/detail_item_message_text"
android:textColor="@color/primaryTextColor" android:textColor="?android:attr/textColorPrimary"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:autoLink="web" android:autoLink="web"
app:layout_constraintTop_toBottomOf="@id/detail_item_title_text" app:layout_constraintTop_toBottomOf="@id/detail_item_title_text"
@ -54,7 +54,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/detail_item_title_text" android:id="@+id/detail_item_title_text"
android:textColor="@color/primaryTextColor" android:textColor="?android:attr/textColorPrimary"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:autoLink="web" android:autoLink="web"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="10dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="10dp"
@ -112,7 +112,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/detail_item_attachment_info" android:id="@+id/detail_item_attachment_info"
android:textColor="@color/primaryTextColor" android:textColor="?android:attr/textColorPrimary"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toEndOf="@+id/detail_item_attachment_icon" app:layout_constraintStart_toEndOf="@+id/detail_item_attachment_icon"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
@ -22,7 +21,7 @@
app:layout_constraintBottom_toTopOf="@+id/main_item_status" app:layout_constraintBottom_toTopOf="@+id/main_item_status"
android:layout_marginStart="10dp" app:layout_constraintStart_toEndOf="@+id/main_item_image" android:layout_marginStart="10dp" app:layout_constraintStart_toEndOf="@+id/main_item_image"
app:layout_constraintVertical_bias="0.0" android:textAppearance="@style/TextAppearance.AppCompat.Medium" app:layout_constraintVertical_bias="0.0" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/primaryTextColor" android:layout_marginTop="10dp" android:textColor="?android:attr/textColorPrimary" android:layout_marginTop="10dp"
app:layout_constraintEnd_toStartOf="@+id/main_item_instant_image"/> app:layout_constraintEnd_toStartOf="@+id/main_item_instant_image"/>
<TextView <TextView
android:text="89 notifications, reconnecting ... This may wrap in the case of UnifiedPush" android:text="89 notifications, reconnecting ... This may wrap in the case of UnifiedPush"

View file

@ -16,10 +16,10 @@
android:paddingBottom="8dp" android:paddingBottom="8dp"
android:text="@string/notification_dialog_title" android:text="@string/notification_dialog_title"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="?android:attr/textAppearanceLarge"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" android:paddingStart="5dp" android:paddingEnd="5dp" app:layout_constraintEnd_toEndOf="parent" android:paddingStart="5dp" android:paddingEnd="5dp"
android:textColor="@color/primaryTextColor"/> android:textColor="?android:attr/textColorPrimary"/>
<RadioGroup <RadioGroup
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View file

@ -3,13 +3,9 @@
<color name="primaryColor">#338574</color> <color name="primaryColor">#338574</color>
<color name="primaryLightColor">#338574</color> <color name="primaryLightColor">#338574</color>
<color name="primaryDarkColor">#2A6E60</color> <color name="primaryDarkColor">#2A6E60</color>
<color name="primaryTextColor">#000000</color>
<color name="primaryLightTextColor">#FFFFFF</color> <color name="primaryLightTextColor">#FFFFFF</color>
<color name="primarySelectedRowColor">#EEEEEE</color> <color name="primarySelectedRowColor">#EEEEEE</color>
<color name="primaryDangerButtonColor">#C30000</color> <color name="primaryDangerButtonColor">#C30000</color>
<color name="primaryPriorityUrgentColor">#C30000</color> <color name="primaryPriorityUrgentColor">#C30000</color>
<color name="primaryPriorityHighColor">#E10000</color> <color name="primaryPriorityHighColor">#E10000</color>
</resources> </resources>

View file

@ -173,13 +173,14 @@
<string name="notification_dialog_save">Save</string> <string name="notification_dialog_save">Save</string>
<string name="notification_dialog_enabled_toast_message">Notifications re-enabled</string> <string name="notification_dialog_enabled_toast_message">Notifications re-enabled</string>
<string name="notification_dialog_muted_forever_toast_message">Notifications are now paused</string> <string name="notification_dialog_muted_forever_toast_message">Notifications are now paused</string>
<string name="notification_dialog_muted_until_toast_message">Notifications are now paused until %1$s</string> <string name="notification_dialog_muted_until_toast_message">Notifications are paused until %1$s</string>
<string name="notification_dialog_show_all">Show all notifications</string>
<string name="notification_dialog_30min">30 minutes</string> <string name="notification_dialog_30min">30 minutes</string>
<string name="notification_dialog_1h">1 hour</string> <string name="notification_dialog_1h">1 hour</string>
<string name="notification_dialog_2h">2 hours</string> <string name="notification_dialog_2h">2 hours</string>
<string name="notification_dialog_8h">8 hours</string> <string name="notification_dialog_8h">8 hours</string>
<string name="notification_dialog_tomorrow">Until tomorrow</string> <string name="notification_dialog_tomorrow">Until tomorrow</string>
<string name="notification_dialog_forever">Forever</string> <string name="notification_dialog_forever">Until re-enabled</string>
<!-- Notification popup --> <!-- Notification popup -->
<string name="notification_popup_action_open">Open</string> <string name="notification_popup_action_open">Open</string>
@ -196,9 +197,9 @@
<string name="settings_notifications_header">Notifications</string> <string name="settings_notifications_header">Notifications</string>
<string name="settings_notifications_muted_until_key">MutedUntil</string> <string name="settings_notifications_muted_until_key">MutedUntil</string>
<string name="settings_notifications_muted_until_title">Pause notifications</string> <string name="settings_notifications_muted_until_title">Pause notifications</string>
<string name="settings_notifications_muted_until_enabled">All notifications will be displayed</string> <string name="settings_notifications_muted_until_show_all">All notifications will be displayed</string>
<string name="settings_notifications_muted_until_disabled_forever">Notifications muted until re-enabled</string> <string name="settings_notifications_muted_until_forever">Notifications muted until re-enabled</string>
<string name="settings_notifications_muted_until_disabled_until">Notifications muted until %1$s</string> <string name="settings_notifications_muted_until_x">Notifications muted until %1$s</string>
<string name="settings_notifications_min_priority_key">MinPriority</string> <string name="settings_notifications_min_priority_key">MinPriority</string>
<string name="settings_notifications_min_priority_title">Minimum priority</string> <string name="settings_notifications_min_priority_title">Minimum priority</string>
<string name="settings_notifications_min_priority_summary_any">Notifications of all priorities are shown</string> <string name="settings_notifications_min_priority_summary_any">Notifications of all priorities are shown</string>
@ -222,6 +223,15 @@
<string name="settings_notifications_auto_download_5m">If smaller than 5 MB</string> <string name="settings_notifications_auto_download_5m">If smaller than 5 MB</string>
<string name="settings_notifications_auto_download_10m">If smaller than 10 MB</string> <string name="settings_notifications_auto_download_10m">If smaller than 10 MB</string>
<string name="settings_notifications_auto_download_50m">If smaller than 50 MB</string> <string name="settings_notifications_auto_download_50m">If smaller than 50 MB</string>
<string name="settings_appearance_header">Appearance</string>
<string name="settings_appearance_dark_mode_key">DarkMode</string>
<string name="settings_appearance_dark_mode_title">Dark mode</string>
<string name="settings_appearance_dark_mode_summary_system">Use the system default</string>
<string name="settings_appearance_dark_mode_summary_light">Light mode is enabled</string>
<string name="settings_appearance_dark_mode_summary_dark">Dark mode is enabled. Are you a vampire?</string>
<string name="settings_appearance_dark_mode_entry_system">Use system default</string>
<string name="settings_appearance_dark_mode_entry_light">Light mode</string>
<string name="settings_appearance_dark_mode_entry_dark">Dark mode</string>
<string name="settings_unified_push_header">UnifiedPush</string> <string name="settings_unified_push_header">UnifiedPush</string>
<string name="settings_unified_push_header_summary">Allows other apps to use ntfy as a message distributor. Find out more at unifiedpush.org.</string> <string name="settings_unified_push_header_summary">Allows other apps to use ntfy as a message distributor. Find out more at unifiedpush.org.</string>
<string name="settings_unified_push_enabled_key">UnifiedPushEnabled</string> <string name="settings_unified_push_enabled_key">UnifiedPushEnabled</string>

View file

@ -1,13 +1,19 @@
<resources> <resources>
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar"> <!-- DayNight mode for easy dark mode, see https://material.io/develop/android/theming/dark -->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/primaryColor</item> <item name="colorPrimary">@color/primaryColor</item>
<item name="colorPrimaryVariant">@color/primaryDarkColor</item>
<item name="colorAccent">@color/primaryLightColor</item> <item name="colorAccent">@color/primaryLightColor</item>
<item name="actionBarStyle">@style/Custom.ActionBar</item>
<item name="android:statusBarColor">@color/primaryColor</item> <item name="android:statusBarColor">@color/primaryColor</item>
<item name="actionModeBackground">@color/primaryDarkColor</item> <item name="actionModeBackground">@color/primaryDarkColor</item>
</style> </style>
<!-- Rounded corners in images: https://stackoverflow.com/a/61960983/1440785 --> <!-- Action bar color identical in dark mode, see https://stackoverflow.com/a/58368668/1440785 -->
<style name="Custom.ActionBar" parent="Widget.MaterialComponents.Light.ActionBar.Solid">
<item name="background">@color/primaryColor</item>
</style>
<!-- Rounded corners in images, see https://stackoverflow.com/a/61960983/1440785 -->
<style name="roundedCornersImageView" parent=""> <style name="roundedCornersImageView" parent="">
<item name="cornerFamily">rounded</item> <item name="cornerFamily">rounded</item>
<item name="cornerSize">5dp</item> <item name="cornerSize">5dp</item>

View file

@ -1,5 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string-array name="settings_notifications_muted_until_entries">
<item>@string/notification_dialog_show_all</item>
<item>@string/notification_dialog_30min</item>
<item>@string/notification_dialog_1h</item>
<item>@string/notification_dialog_2h</item>
<item>@string/notification_dialog_8h</item>
<item>@string/notification_dialog_tomorrow</item>
<item>@string/notification_dialog_forever</item>
</string-array>
<string-array name="settings_notifications_muted_until_values">
<item>0</item> <!-- See Repository -->
<item>30</item> <!-- See Minutes -->
<item>60</item>
<item>120</item>
<item>480</item>
<item>2</item> <!-- See Repository -->
<item>1</item> <!-- See Repository -->
</string-array>
<string-array name="settings_notifications_min_priority_entries"> <string-array name="settings_notifications_min_priority_entries">
<item>@string/settings_notifications_min_priority_min</item> <item>@string/settings_notifications_min_priority_min</item>
<item>@string/settings_notifications_min_priority_low</item> <item>@string/settings_notifications_min_priority_low</item>
@ -50,4 +68,15 @@
<item>copy</item> <item>copy</item>
<item>upload</item> <item>upload</item>
</string-array> </string-array>
<string-array name="settings_appearance_dark_mode_entries">
<item>@string/settings_appearance_dark_mode_entry_system</item>
<item>@string/settings_appearance_dark_mode_entry_light</item>
<item>@string/settings_appearance_dark_mode_entry_dark</item>
</string-array>
<string-array name="settings_appearance_dark_mode_values">
<!-- Must match values in AppCompatDelegate -->
<item>-1</item>
<item>1</item>
<item>2</item>
</string-array>
</resources> </resources>

View file

@ -1,12 +1,14 @@
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto" <PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
app:title="@string/settings_title"> app:title="@string/settings_title">
<PreferenceCategory <PreferenceCategory
app:title="@string/settings_notifications_header" app:title="@string/settings_notifications_header"
app:layout="@layout/preference_category_material_edited"> app:layout="@layout/preference_category_material_edited">
<Preference <ListPreference
app:key="@string/settings_notifications_muted_until_key" app:key="@string/settings_notifications_muted_until_key"
app:title="@string/settings_notifications_muted_until_title"/> app:title="@string/settings_notifications_muted_until_title"
app:entries="@array/settings_notifications_muted_until_entries"
app:entryValues="@array/settings_notifications_muted_until_values"
app:defaultValue="0"/>
<ListPreference <ListPreference
app:key="@string/settings_notifications_min_priority_key" app:key="@string/settings_notifications_min_priority_key"
app:title="@string/settings_notifications_min_priority_title" app:title="@string/settings_notifications_min_priority_title"
@ -20,6 +22,16 @@
app:entryValues="@array/settings_notifications_auto_download_values" app:entryValues="@array/settings_notifications_auto_download_values"
app:defaultValue="1"/> app:defaultValue="1"/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
app:title="@string/settings_appearance_header"
app:layout="@layout/preference_category_material_edited">
<ListPreference
app:key="@string/settings_appearance_dark_mode_key"
app:title="@string/settings_appearance_dark_mode_title"
app:entries="@array/settings_appearance_dark_mode_entries"
app:entryValues="@array/settings_appearance_dark_mode_values"
app:defaultValue="-1"/>
</PreferenceCategory>
<PreferenceCategory <PreferenceCategory
app:title="@string/settings_unified_push_header" app:title="@string/settings_unified_push_header"
app:summary="@string/settings_unified_push_header_summary" app:summary="@string/settings_unified_push_header_summary"