mirror of
https://github.com/Matte23/circolapp.git
synced 2025-12-06 07:29:10 +00:00
Add push notifications
This commit is contained in:
@@ -2,6 +2,7 @@ apply plugin: 'com.android.application'
|
|||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
|
apply plugin: 'com.google.gms.google-services'
|
||||||
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
|
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -39,7 +40,7 @@ dependencies {
|
|||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.core:core-ktx:1.3.2'
|
||||||
implementation 'com.google.android.material:material:1.3.0-alpha03'
|
implementation 'com.google.android.material:material:1.3.0-alpha03'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.3'
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
|
||||||
@@ -60,4 +61,7 @@ dependencies {
|
|||||||
implementation 'com.github.tiper:MaterialSpinner:1.4.2'
|
implementation 'com.github.tiper:MaterialSpinner:1.4.2'
|
||||||
implementation "com.mikepenz:aboutlibraries-core:$about_libs_version"
|
implementation "com.mikepenz:aboutlibraries-core:$about_libs_version"
|
||||||
implementation "com.mikepenz:aboutlibraries:$about_libs_version"
|
implementation "com.mikepenz:aboutlibraries:$about_libs_version"
|
||||||
|
|
||||||
|
implementation platform('com.google.firebase:firebase-bom:26.0.0')
|
||||||
|
implementation 'com.google.firebase:firebase-messaging-ktx'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,21 @@
|
|||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".AlarmBroadcastReceiver" />
|
<receiver android:name=".AlarmBroadcastReceiver" />
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".push.CircolappFirebaseMessagingService"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||||
|
android:resource="@drawable/ic_notification" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -18,12 +18,14 @@
|
|||||||
|
|
||||||
package net.underdesk.circolapp
|
package net.underdesk.circolapp
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.preference.*
|
import androidx.preference.*
|
||||||
import kotlinx.android.synthetic.main.settings_activity.*
|
import kotlinx.android.synthetic.main.settings_activity.*
|
||||||
|
import net.underdesk.circolapp.push.FirebaseTopicUtils
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
import net.underdesk.circolapp.server.ServerAPI
|
||||||
import net.underdesk.circolapp.works.PollWork
|
import net.underdesk.circolapp.works.PollWork
|
||||||
|
|
||||||
@@ -40,15 +42,19 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
class SettingsFragment : PreferenceFragmentCompat(),
|
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||||
|
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||||
|
|
||||||
val schoolPreference = findPreference<ListPreference>("school")
|
val schoolPreference = findPreference<ListPreference>("school")
|
||||||
schoolPreference?.let { setSchoolListPreference(it) }
|
schoolPreference?.let { setSchoolListPreference(it) }
|
||||||
val schoolPreferenceListener =
|
val schoolPreferenceListener =
|
||||||
Preference.OnPreferenceChangeListener { _, value ->
|
Preference.OnPreferenceChangeListener { _, value ->
|
||||||
ServerAPI.changeServer(value.toString().toInt())
|
ServerAPI.changeServer(value.toString().toInt(), requireContext())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
schoolPreference?.onPreferenceChangeListener = schoolPreferenceListener
|
schoolPreference?.onPreferenceChangeListener = schoolPreferenceListener
|
||||||
@@ -69,17 +75,6 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
pollIntervalPreference?.setOnBindEditTextListener { editText ->
|
pollIntervalPreference?.setOnBindEditTextListener { editText ->
|
||||||
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
||||||
}
|
}
|
||||||
|
|
||||||
val notificationPreference =
|
|
||||||
findPreference<SwitchPreferenceCompat>("notify_new_circulars")
|
|
||||||
|
|
||||||
val notificationPrefChangedListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _, _ ->
|
|
||||||
activity?.let { PollWork.enqueue(it) }
|
|
||||||
true
|
|
||||||
}
|
|
||||||
pollIntervalPreference?.onPreferenceChangeListener = notificationPrefChangedListener
|
|
||||||
notificationPreference?.onPreferenceChangeListener = notificationPrefChangedListener
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSchoolListPreference(listPreference: ListPreference) {
|
private fun setSchoolListPreference(listPreference: ListPreference) {
|
||||||
@@ -96,5 +91,31 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
listPreference.entryValues = entryValues.toTypedArray()
|
listPreference.entryValues = entryValues.toTypedArray()
|
||||||
listPreference.entries = entryNames.toTypedArray()
|
listPreference.entries = entryNames.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSharedPreferenceChanged(
|
||||||
|
sharedPreferences: SharedPreferences?,
|
||||||
|
key: String?
|
||||||
|
) {
|
||||||
|
if (key != "notify_new_circulars" && key != "enable_polling" && key != "poll_interval")
|
||||||
|
return
|
||||||
|
|
||||||
|
if (sharedPreferences == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
activity?.let { PollWork.enqueue(it) }
|
||||||
|
|
||||||
|
if (sharedPreferences.getBoolean(
|
||||||
|
"notify_new_circulars",
|
||||||
|
true
|
||||||
|
) && !sharedPreferences.getBoolean("enable_polling", false)
|
||||||
|
) {
|
||||||
|
val serverID = ServerAPI.getInstance(requireContext()).serverID()
|
||||||
|
val serverToken = ServerAPI.Companion.Servers.values()[serverID].toString()
|
||||||
|
|
||||||
|
FirebaseTopicUtils.selectTopic(serverToken, requireContext())
|
||||||
|
} else {
|
||||||
|
FirebaseTopicUtils.unsubscribe(requireContext())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class SchoolSelectionFragment : Fragment(), SlidePolicy, MaterialSpinner.OnItemS
|
|||||||
editor.putString("school", position.toString())
|
editor.putString("school", position.toString())
|
||||||
editor.apply()
|
editor.apply()
|
||||||
|
|
||||||
ServerAPI.changeServer(position)
|
ServerAPI.changeServer(position, requireContext())
|
||||||
schoolSelected = true
|
schoolSelected = true
|
||||||
|
|
||||||
parent.error = null
|
parent.error = null
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package net.underdesk.circolapp.push
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import com.google.firebase.messaging.FirebaseMessagingService
|
||||||
|
import com.google.firebase.messaging.RemoteMessage
|
||||||
|
import net.underdesk.circolapp.works.PollWork
|
||||||
|
|
||||||
|
// We don't need to get an Instance Token for topic notifications
|
||||||
|
@SuppressLint("MissingFirebaseInstanceTokenRefresh")
|
||||||
|
class CircolappFirebaseMessagingService : FirebaseMessagingService() {
|
||||||
|
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||||
|
if (remoteMessage.data.isNotEmpty()) {
|
||||||
|
PollWork.runWork(applicationContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package net.underdesk.circolapp.push
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.google.firebase.ktx.Firebase
|
||||||
|
import com.google.firebase.messaging.ktx.messaging
|
||||||
|
|
||||||
|
class FirebaseTopicUtils {
|
||||||
|
companion object {
|
||||||
|
fun selectTopic(newTopic: String, context: Context) {
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
unsubscribeFromTopic(sharedPreferences)
|
||||||
|
|
||||||
|
Firebase.messaging.subscribeToTopic(newTopic)
|
||||||
|
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putString("topic", newTopic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unsubscribe(context: Context) {
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
|
unsubscribeFromTopic(sharedPreferences)
|
||||||
|
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putString("topic", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unsubscribeFromTopic(sharedPreferences: SharedPreferences) {
|
||||||
|
val oldTopic = sharedPreferences.getString("topic", null)
|
||||||
|
oldTopic?.let { Firebase.messaging.unsubscribeFromTopic(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ package net.underdesk.circolapp.server
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import net.underdesk.circolapp.data.Circular
|
import net.underdesk.circolapp.data.Circular
|
||||||
|
import net.underdesk.circolapp.push.FirebaseTopicUtils
|
||||||
import net.underdesk.circolapp.server.curie.CurieServer
|
import net.underdesk.circolapp.server.curie.CurieServer
|
||||||
import net.underdesk.circolapp.server.porporato.PorporatoServer
|
import net.underdesk.circolapp.server.porporato.PorporatoServer
|
||||||
|
|
||||||
@@ -84,8 +85,18 @@ class ServerAPI(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeServer(index: Int) {
|
fun changeServer(index: Int, context: Context) {
|
||||||
instance?.changeServer(createServer(Servers.values()[index]))
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val newServer = Servers.values()[index]
|
||||||
|
|
||||||
|
if (sharedPreferences.getBoolean(
|
||||||
|
"notify_new_circulars",
|
||||||
|
true
|
||||||
|
) && !sharedPreferences.getBoolean("enable_polling", false)
|
||||||
|
)
|
||||||
|
FirebaseTopicUtils.selectTopic(newServer.toString(), context)
|
||||||
|
|
||||||
|
instance?.changeServer(createServer(newServer))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createServer(server: Servers) = when (server) {
|
private fun createServer(server: Servers) = when (server) {
|
||||||
|
|||||||
@@ -65,7 +65,11 @@ class PollWork(appContext: Context, workerParams: WorkerParameters) :
|
|||||||
fun enqueue(context: Context) {
|
fun enqueue(context: Context) {
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
if (sharedPreferences.getBoolean("notify_new_circulars", true)) {
|
if (sharedPreferences.getBoolean(
|
||||||
|
"notify_new_circulars",
|
||||||
|
true
|
||||||
|
) && sharedPreferences.getBoolean("enable_polling", false)
|
||||||
|
) {
|
||||||
WorkManager.getInstance(context)
|
WorkManager.getInstance(context)
|
||||||
.enqueueUniquePeriodicWork(
|
.enqueueUniquePeriodicWork(
|
||||||
pollWorkName,
|
pollWorkName,
|
||||||
@@ -82,6 +86,11 @@ class PollWork(appContext: Context, workerParams: WorkerParameters) :
|
|||||||
.cancelUniqueWork(pollWorkName)
|
.cancelUniqueWork(pollWorkName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun runWork(context: Context) {
|
||||||
|
val oneTimeWork = OneTimeWorkRequestBuilder<PollWork>().build()
|
||||||
|
WorkManager.getInstance(context).enqueue(oneTimeWork)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun doWork(): Result = coroutineScope {
|
override suspend fun doWork(): Result = coroutineScope {
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
<string name="preferences_school">Scuola</string>
|
<string name="preferences_school">Scuola</string>
|
||||||
<string name="preferences_dark_theme">Tema scuro</string>
|
<string name="preferences_dark_theme">Tema scuro</string>
|
||||||
<string name="preferences_notify_new_circulars">Mostra notifiche</string>
|
<string name="preferences_notify_new_circulars">Mostra notifiche</string>
|
||||||
|
<string name="preferences_polling">Esegui controllo dal telefono</string>
|
||||||
<string name="preferences_poll_interval">Intervallo di sincronizzazione</string>
|
<string name="preferences_poll_interval">Intervallo di sincronizzazione</string>
|
||||||
<string name="preferences_notify_new_circulars_summary">Mostra una notifica quando una nuova circolare è pubblicata</string>
|
<string name="preferences_notify_new_circulars_summary">Mostra una notifica quando una nuova circolare è pubblicata</string>
|
||||||
|
<string name="preferences_polling_summary">Sconsigliato. Disabilitare questa opzione per utilizzare le notifiche push</string>
|
||||||
<string name="preferences_poll_interval_summary">Quanto tempo attendere tra una sincronizzazione ed un altra</string>
|
<string name="preferences_poll_interval_summary">Quanto tempo attendere tra una sincronizzazione ed un altra</string>
|
||||||
|
|
||||||
<string name="notification_title">Circolare numero %1$d</string>
|
<string name="notification_title">Circolare numero %1$d</string>
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
<string name="preferences_school">School</string>
|
<string name="preferences_school">School</string>
|
||||||
<string name="preferences_dark_theme">Dark theme</string>
|
<string name="preferences_dark_theme">Dark theme</string>
|
||||||
<string name="preferences_notify_new_circulars">Show notifications</string>
|
<string name="preferences_notify_new_circulars">Show notifications</string>
|
||||||
|
<string name="preferences_polling">Run checks from this device</string>
|
||||||
<string name="preferences_poll_interval">Synchronization interval</string>
|
<string name="preferences_poll_interval">Synchronization interval</string>
|
||||||
<string name="preferences_notify_new_circulars_summary">Show a notification when a new circular is published</string>
|
<string name="preferences_notify_new_circulars_summary">Show a notification when a new circular is published</string>
|
||||||
|
<string name="preferences_polling_summary">Not recommended. Disable this option to use push notifications</string>
|
||||||
<string name="preferences_poll_interval_summary">How long to wait before checking if new circulars are published</string>
|
<string name="preferences_poll_interval_summary">How long to wait before checking if new circulars are published</string>
|
||||||
|
|
||||||
<string name="notification_title">Circular letter number %1$d</string>
|
<string name="notification_title">Circular letter number %1$d</string>
|
||||||
|
|||||||
@@ -26,9 +26,16 @@
|
|||||||
app:key="notify_new_circulars"
|
app:key="notify_new_circulars"
|
||||||
app:title="@string/preferences_notify_new_circulars" />
|
app:title="@string/preferences_notify_new_circulars" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:summary="@string/preferences_polling_summary"
|
||||||
|
app:dependency="notify_new_circulars"
|
||||||
|
app:key="enable_polling"
|
||||||
|
app:title="@string/preferences_polling" />
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:defaultValue="15"
|
android:defaultValue="15"
|
||||||
android:dependency="notify_new_circulars"
|
android:dependency="enable_polling"
|
||||||
android:key="poll_interval"
|
android:key="poll_interval"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ buildscript {
|
|||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libs_version"
|
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libs_version"
|
||||||
classpath "org.jlleitschuh.gradle:ktlint-gradle:9.4.0"
|
classpath "org.jlleitschuh.gradle:ktlint-gradle:9.4.0"
|
||||||
|
classpath 'com.google.gms:google-services:4.3.4'
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user