Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Material 3 #59

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/src/main/java/io/heckel/ntfy/app/Application.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.heckel.ntfy.app

import android.app.Application
import com.google.android.material.color.DynamicColors
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.util.Log

Expand All @@ -12,4 +13,9 @@ class Application : Application() {
}
repository
}

override fun onCreate() {
DynamicColors.applyToActivitiesIfAvailable(this)
super.onCreate()
}
}
3 changes: 2 additions & 1 deletion app/src/main/java/io/heckel/ntfy/ui/AddFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.view.inputmethod.InputMethodManager
import android.widget.*
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import io.heckel.ntfy.BuildConfig
Expand Down Expand Up @@ -144,7 +145,7 @@ class AddFragment : DialogFragment() {
loginPasswordText.addTextChangedListener(loginTextWatcher)

// Build dialog
val dialog = AlertDialog.Builder(activity)
val dialog = MaterialAlertDialogBuilder(requireContext())
.setView(view)
.setPositiveButton(R.string.add_dialog_button_subscribe) { _, _ ->
// This will be overridden below to avoid closing the dialog immediately
Expand Down
50 changes: 50 additions & 0 deletions app/src/main/java/io/heckel/ntfy/ui/BasePreferenceFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.heckel.ntfy.ui

import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import io.heckel.ntfy.R

abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
/**
* Show [ListPreference] and [EditTextPreference] dialog by [MaterialAlertDialogBuilder]
*/
override fun onDisplayPreferenceDialog(preference: Preference) {
when (preference) {
is ListPreference -> {
val prefIndex = preference.entryValues.indexOf(preference.value)
MaterialAlertDialogBuilder(requireContext())
.setTitle(preference.title)
.setSingleChoiceItems(preference.entries, prefIndex) { dialog, index ->
val newValue = preference.entryValues[index].toString()
if (preference.callChangeListener(newValue)) {
preference.value = newValue
}
dialog.dismiss()
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
is EditTextPreference -> {
val view = layoutInflater.inflate(R.layout.dialog_edit_text_preference, null)
val editText = view.findViewById<TextInputEditText>(R.id.editText)
editText.setText(preference.text.toString())
MaterialAlertDialogBuilder(requireContext())
.setTitle(preference.title)
.setView(view)
.setPositiveButton(android.R.string.ok) { _, _ ->
val newValue = editText.text.toString()
if (preference.callChangeListener(newValue)) {
preference.text = newValue
}
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
else -> super.onDisplayPreferenceDialog(preference)
}
}
}
29 changes: 13 additions & 16 deletions app/src/main/java/io/heckel/ntfy/ui/Colors.kt
Original file line number Diff line number Diff line change
@@ -1,48 +1,45 @@
package io.heckel.ntfy.ui

import android.content.Context
import android.graphics.Color
import androidx.core.content.ContextCompat
import com.google.android.material.color.MaterialColors
import com.google.android.material.elevation.SurfaceColors
import io.heckel.ntfy.R
import io.heckel.ntfy.util.isDarkThemeOn

class Colors {
companion object {
const val refreshProgressIndicator = R.color.teal

fun notificationIcon(context: Context): Int {
return if (isDarkThemeOn(context)) R.color.teal_light else R.color.teal
}

fun itemSelectedBackground(context: Context): Int {
return if (isDarkThemeOn(context)) R.color.black_800b else R.color.gray_400
}

fun cardBackground(context: Context): Int {
return if (isDarkThemeOn(context)) R.color.black_800b else R.color.white
}

fun cardSelectedBackground(context: Context): Int {
return if (isDarkThemeOn(context)) R.color.black_700b else R.color.gray_500
return SurfaceColors.getColorForElevation(context, 10f)
}

fun cardBackgroundColor(context: Context): Int {
return ContextCompat.getColor(context, cardBackground(context))
return SurfaceColors.getColorForElevation(context, 5f)
}

fun cardSelectedBackgroundColor(context: Context): Int {
return ContextCompat.getColor(context, cardSelectedBackground(context))
return SurfaceColors.getColorForElevation(context, 20f)
}

fun statusBarNormal(context: Context): Int {
return if (isDarkThemeOn(context)) R.color.black_900 else R.color.teal
return MaterialColors.getColor(context, R.attr.backgroundColor, Color.BLACK)
}

fun statusBarActionMode(context: Context): Int {
return if (isDarkThemeOn(context)) R.color.black_900 else R.color.teal_dark
return MaterialColors.getColor(context, R.attr.backgroundColor, Color.BLACK)
}

fun dangerText(context: Context): Int {
return if (isDarkThemeOn(context)) R.color.red_light else R.color.red_dark
return MaterialColors.getColor(context, R.attr.colorError, Color.RED)
}

fun swipeToRefreshColor(context: Context): Int {
return MaterialColors.getColor(context, R.attr.colorPrimary, Color.GREEN)
}
}
}
20 changes: 7 additions & 13 deletions app/src/main/java/io/heckel/ntfy/ui/DetailActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
Expand Down Expand Up @@ -190,7 +191,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
// Swipe to refresh
mainListContainer = findViewById(R.id.detail_notification_list_container)
mainListContainer.setOnRefreshListener { refresh() }
mainListContainer.setColorSchemeResources(Colors.refreshProgressIndicator)
mainListContainer.setColorSchemeColors(Colors.swipeToRefreshColor(this))

// Update main list based on viewModel (& its datasource/livedata)
val noEntriesText: View = findViewById(R.id.detail_no_notifications)
Expand Down Expand Up @@ -568,8 +569,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
private fun onClearClick() {
Log.d(TAG, "Clearing all notifications for ${topicShortUrl(subscriptionBaseUrl, subscriptionTopic)}")

val builder = AlertDialog.Builder(this)
val dialog = builder
val dialog = MaterialAlertDialogBuilder(this)
.setMessage(R.string.detail_clear_dialog_message)
.setPositiveButton(R.string.detail_clear_dialog_permanently_delete) { _, _ ->
lifecycleScope.launch(Dispatchers.IO) {
Expand Down Expand Up @@ -600,8 +600,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
private fun onDeleteClick() {
Log.d(TAG, "Deleting subscription ${topicShortUrl(subscriptionBaseUrl, subscriptionTopic)}")

val builder = AlertDialog.Builder(this)
val dialog = builder
val dialog = MaterialAlertDialogBuilder(this)
.setMessage(R.string.detail_delete_dialog_message)
.setPositiveButton(R.string.detail_delete_dialog_permanently_delete) { _, _ ->
Log.d(TAG, "Deleting subscription with subscription ID $subscriptionId (topic: $subscriptionTopic)")
Expand Down Expand Up @@ -716,8 +715,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
private fun onMultiDeleteClick() {
Log.d(TAG, "Showing multi-delete dialog for selected items")

val builder = AlertDialog.Builder(this)
val dialog = builder
val dialog = MaterialAlertDialogBuilder(this)
.setMessage(R.string.detail_action_mode_delete_dialog_message)
.setPositiveButton(R.string.detail_action_mode_delete_dialog_permanently_delete) { _, _ ->
adapter.selected.map { notificationId -> viewModel.markAsDeleted(notificationId) }
Expand All @@ -744,9 +742,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
adapter.toggleSelection(notification.id)

// Fade status bar color
val fromColor = ContextCompat.getColor(this, Colors.statusBarNormal(this))
val toColor = ContextCompat.getColor(this, Colors.statusBarActionMode(this))
fadeStatusBarColor(window, fromColor, toColor)
fadeStatusBarColor(window, Colors.statusBarNormal(this), Colors.statusBarActionMode(this))
}

private fun finishActionMode() {
Expand All @@ -760,9 +756,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
adapter.notifyItemRangeChanged(0, adapter.currentList.size)

// Fade status bar color
val fromColor = ContextCompat.getColor(this, Colors.statusBarActionMode(this))
val toColor = ContextCompat.getColor(this, Colors.statusBarNormal(this))
fadeStatusBarColor(window, fromColor, toColor)
fadeStatusBarColor(window, Colors.statusBarActionMode(this), Colors.statusBarNormal(this))
}

companion object {
Expand Down
8 changes: 4 additions & 4 deletions app/src/main/java/io/heckel/ntfy/ui/DetailSettingsActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class DetailSettingsActivity : AppCompatActivity() {
private fun loadInstantPref() {
val appBaseUrl = getString(R.string.app_base_url)
val prefId = context?.getString(R.string.detail_settings_notifications_instant_key) ?: return
val pref: SwitchPreference? = findPreference(prefId)
val pref: SwitchPreferenceCompat? = findPreference(prefId)
pref?.isVisible = BuildConfig.FIREBASE_AVAILABLE && subscription.baseUrl == appBaseUrl
pref?.isChecked = subscription.instant
pref?.preferenceDataStore = object : PreferenceDataStore() {
Expand All @@ -148,7 +148,7 @@ class DetailSettingsActivity : AppCompatActivity() {
return subscription.instant
}
}
pref?.summaryProvider = Preference.SummaryProvider<SwitchPreference> { preference ->
pref?.summaryProvider = Preference.SummaryProvider<SwitchPreferenceCompat> { preference ->
if (preference.isChecked) {
getString(R.string.detail_settings_notifications_instant_summary_on)
} else {
Expand All @@ -159,7 +159,7 @@ class DetailSettingsActivity : AppCompatActivity() {

private fun loadDedicatedChannelsPrefs() {
val prefId = context?.getString(R.string.detail_settings_notifications_dedicated_channels_key) ?: return
val pref: SwitchPreference? = findPreference(prefId)
val pref: SwitchPreferenceCompat? = findPreference(prefId)
pref?.isVisible = true
pref?.isChecked = subscription.dedicatedChannels
pref?.preferenceDataStore = object : PreferenceDataStore() {
Expand All @@ -176,7 +176,7 @@ class DetailSettingsActivity : AppCompatActivity() {
return subscription.dedicatedChannels
}
}
pref?.summaryProvider = Preference.SummaryProvider<SwitchPreference> { preference ->
pref?.summaryProvider = Preference.SummaryProvider<SwitchPreferenceCompat> { preference ->
if (preference.isChecked) {
getString(R.string.detail_settings_notifications_dedicated_channels_summary_on)
} else {
Expand Down
14 changes: 5 additions & 9 deletions app/src/main/java/io/heckel/ntfy/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.work.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.FloatingActionButton
import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
Expand Down Expand Up @@ -96,7 +97,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
// Swipe to refresh
mainListContainer = findViewById(R.id.main_subscriptions_list_container)
mainListContainer.setOnRefreshListener { refreshAllSubscriptions() }
mainListContainer.setColorSchemeResources(Colors.refreshProgressIndicator)
mainListContainer.setColorSchemeColors(Colors.swipeToRefreshColor(this))

// Update main list based on viewModel (& its datasource/livedata)
val noEntries: View = findViewById(R.id.main_no_subscriptions)
Expand Down Expand Up @@ -608,8 +609,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
private fun onMultiDeleteClick() {
Log.d(DetailActivity.TAG, "Showing multi-delete dialog for selected items")

val builder = AlertDialog.Builder(this)
val dialog = builder
val dialog = MaterialAlertDialogBuilder(this)
.setMessage(R.string.main_action_mode_delete_dialog_message)
.setPositiveButton(R.string.main_action_mode_delete_dialog_permanently_delete) { _, _ ->
adapter.selected.map { subscriptionId -> viewModel.remove(this, subscriptionId) }
Expand Down Expand Up @@ -648,9 +648,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
})

// Fade status bar color
val fromColor = ContextCompat.getColor(this, Colors.statusBarNormal(this))
val toColor = ContextCompat.getColor(this, Colors.statusBarActionMode(this))
fadeStatusBarColor(window, fromColor, toColor)
fadeStatusBarColor(window, Colors.statusBarNormal(this), Colors.statusBarActionMode(this))
}

private fun finishActionMode() {
Expand All @@ -677,9 +675,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
})

// Fade status bar color
val fromColor = ContextCompat.getColor(this, Colors.statusBarActionMode(this))
val toColor = ContextCompat.getColor(this, Colors.statusBarNormal(this))
fadeStatusBarColor(window, fromColor, toColor)
fadeStatusBarColor(window, Colors.statusBarActionMode(this), Colors.statusBarNormal(this))
}

private fun redrawList() {
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/io/heckel/ntfy/ui/MainAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class MainAdapter(private val repository: Repository, private val onClick: (Subs
itemView.setOnClickListener { onClick(subscription) }
itemView.setOnLongClickListener { onLongClick(subscription); true }
if (selected.contains(subscription.id)) {
itemView.setBackgroundResource(Colors.itemSelectedBackground(context))
itemView.setBackgroundColor(Colors.itemSelectedBackground(context))
} else {
itemView.setBackgroundColor(Color.TRANSPARENT)
}
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/io/heckel/ntfy/ui/NotificationFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.os.Bundle
import android.widget.RadioButton
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.heckel.ntfy.R
import io.heckel.ntfy.db.Repository
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -74,7 +75,7 @@ class NotificationFragment : DialogFragment() {
muteForeverButton = view.findViewById(R.id.notification_dialog_forever)
muteForeverButton.setOnClickListener{ onClick(Repository.MUTED_UNTIL_FOREVER) }

return AlertDialog.Builder(activity)
return MaterialAlertDialogBuilder(requireContext())
.setView(view)
.create()
}
Expand Down
Loading