Add push notifications

This commit is contained in:
Matte23
2020-10-29 22:53:36 +01:00
parent 8f824b81b3
commit 76b4886c6e
12 changed files with 144 additions and 19 deletions

View File

@@ -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'
} }

View File

@@ -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>

View File

@@ -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())
}
}
} }
} }

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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) }
}
}
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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
} }