Add database support for multiple servers (schools)

This commit is contained in:
Matte23
2020-10-25 22:30:50 +01:00
parent 109c56d111
commit 4c09e91e99
20 changed files with 168 additions and 63 deletions

View File

@@ -37,6 +37,7 @@ class AlarmBroadcastReceiver : BroadcastReceiver() {
companion object {
const val CHANNEL_ID = "net.underdesk.circolapp.REMINDER"
const val CIRCULAR_ID = "circular_id"
const val SCHOOL_ID = "school_id"
}
override fun onReceive(context: Context, intent: Intent) {
@@ -47,6 +48,10 @@ class AlarmBroadcastReceiver : BroadcastReceiver() {
intent.getLongExtra(
CIRCULAR_ID,
0
),
intent.getIntExtra(
SCHOOL_ID,
0
)
)
createNotification(

View File

@@ -24,7 +24,7 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
@Database(entities = [Circular::class], version = 1, exportSchema = false)
@Database(entities = [Circular::class], version = 2, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun circularDao(): CircularDao
@@ -42,7 +42,7 @@ abstract class AppDatabase : RoomDatabase() {
context,
AppDatabase::class.java,
DATABASE_NAME
).build().also { instance = it }
).fallbackToDestructiveMigration().build().also { instance = it }
}
}
}

View File

@@ -20,13 +20,13 @@ package net.underdesk.circolapp.data
import android.os.Parcelable
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.android.parcel.Parcelize
@Parcelize
@Entity(tableName = "circulars")
@Entity(tableName = "circulars", primaryKeys = ["id", "school"])
data class Circular(
@PrimaryKey val id: Long,
val id: Long,
val school: Int,
val name: String,
val url: String,
val date: String,

View File

@@ -23,29 +23,29 @@ import androidx.room.*
@Dao
interface CircularDao {
@Query("SELECT * FROM circulars ORDER BY id DESC")
fun getCirculars(): List<Circular>
@Query("SELECT * FROM circulars WHERE school is :school ORDER BY id DESC")
fun getCirculars(school: Int): List<Circular>
@Query("SELECT * FROM circulars ORDER BY id DESC")
fun getLiveCirculars(): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE school is :school ORDER BY id DESC")
fun getLiveCirculars(school: Int): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE name LIKE :query ORDER BY id DESC")
fun searchCirculars(query: String): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE school is :school AND name LIKE :query ORDER BY id DESC")
fun searchCirculars(query: String, school: Int): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE id = :id ORDER BY id DESC")
fun getCircular(id: Long): Circular
@Query("SELECT * FROM circulars WHERE school is :school AND id = :id ORDER BY id DESC")
fun getCircular(id: Long, school: Int): Circular
@Query("SELECT * FROM circulars WHERE favourite ORDER BY id DESC")
fun getFavourites(): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE school is :school AND favourite ORDER BY id DESC")
fun getFavourites(school: Int): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE favourite AND name LIKE :query ORDER BY id DESC")
fun searchFavourites(query: String): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE school is :school AND favourite AND name LIKE :query ORDER BY id DESC")
fun searchFavourites(query: String, school: Int): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE reminder ORDER BY id DESC")
fun getReminders(): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE school is :school AND reminder ORDER BY id DESC")
fun getReminders(school: Int): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE reminder AND name LIKE :query ORDER BY id DESC")
fun searchReminders(query: String): LiveData<List<Circular>>
@Query("SELECT * FROM circulars WHERE school is :school AND reminder AND name LIKE :query ORDER BY id DESC")
fun searchReminders(query: String, school: Int): LiveData<List<Circular>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insertAll(circulars: List<Circular>)

View File

@@ -16,7 +16,7 @@ class CircularRepository(
if (result.second == ServerAPI.Companion.Result.ERROR)
return@withContext Pair(emptyList(), false)
val oldCirculars = circularDao.getCirculars()
val oldCirculars = circularDao.getCirculars(serverAPI.serverID())
val newCirculars = result.first
if (newCirculars.size != oldCirculars.size) {

View File

@@ -48,7 +48,8 @@ class CircularLetterFragment :
CircularRepository.getInstance(
AppDatabase.getInstance(requireContext()).circularDao(),
ServerAPI.getInstance(requireContext())
)
),
requireActivity().application
)
}

View File

@@ -42,7 +42,8 @@ class FavouritesFragment : Fragment(), MainActivity.SearchCallback {
CircularRepository.getInstance(
AppDatabase.getInstance(requireContext()).circularDao(),
ServerAPI.getInstance(requireContext())
)
),
requireActivity().application
)
}

View File

@@ -112,7 +112,8 @@ class NewReminderFragment : DialogFragment() {
context,
circular.id.toInt(),
Intent(context, AlarmBroadcastReceiver::class.java)
.putExtra(AlarmBroadcastReceiver.CIRCULAR_ID, circular.id),
.putExtra(AlarmBroadcastReceiver.CIRCULAR_ID, circular.id)
.putExtra(AlarmBroadcastReceiver.SCHOOL_ID, circular.school),
0
)

View File

@@ -42,7 +42,8 @@ class RemindersFragment : Fragment(), MainActivity.SearchCallback {
CircularRepository.getInstance(
AppDatabase.getInstance(requireContext()).circularDao(),
ServerAPI.getInstance(requireContext())
)
),
requireActivity().application
)
}

View File

@@ -21,6 +21,7 @@ package net.underdesk.circolapp.server
import net.underdesk.circolapp.data.Circular
abstract class Server {
abstract val serverID: Int
abstract suspend fun getCircularsFromServer(): Pair<List<Circular>, ServerAPI.Companion.Result>
abstract suspend fun newCircularsAvailable(): Pair<Boolean, ServerAPI.Companion.Result>
}

View File

@@ -27,6 +27,8 @@ import net.underdesk.circolapp.server.porporato.PorporatoServer
class ServerAPI(
private var server: Server
) {
fun serverID(): Int = server.serverID
suspend fun getCircularsFromServer(): Pair<List<Circular>, Result> {
val newCircularsAvailable = server.newCircularsAvailable()
@@ -52,6 +54,10 @@ class ServerAPI(
SUCCESS, ERROR
}
fun getServerId(server: Servers): Int {
return Servers.values().indexOf(server)
}
fun getServerName(server: Servers) = when (server) {
Servers.CURIE -> "Liceo scientifico Maria Curie"
Servers.PORPORATO -> "Liceo G.F. Porporato"

View File

@@ -18,6 +18,8 @@ class CurieServer : Server() {
private val responseAdapter = moshi.adapter(Response::class.java)
private val client = OkHttpClient()
override val serverID = ServerAPI.getServerId(ServerAPI.Companion.Servers.CURIE)
override suspend fun getCircularsFromServer(): Pair<List<Circular>, ServerAPI.Companion.Result> {
return try {
withContext(Dispatchers.Default) {
@@ -85,9 +87,9 @@ class CurieServer : Server() {
.removePrefix("_")
.removePrefix(" ")
Circular(id.toLong(), title, url, matcherDate.group(1) ?: "")
Circular(id.toLong(), serverID, title, url, matcherDate.group(1) ?: "")
} else {
Circular(id.toLong(), title, url, "")
Circular(id.toLong(), serverID, title, url, "")
}
}

View File

@@ -30,6 +30,8 @@ class PorporatoServer : Server() {
"https://www.liceoporporato.edu.it/ARCHIVIO/PR/VP/circolari.php?dirname=CIRCOLARIP/- CIRCOLARI 2020-21/-12-Agosto/"
)
override val serverID = ServerAPI.getServerId(ServerAPI.Companion.Servers.PORPORATO)
override suspend fun getCircularsFromServer(): Pair<List<Circular>, ServerAPI.Companion.Result> {
return try {
val list = arrayListOf<Circular>()
@@ -150,9 +152,9 @@ class PorporatoServer : Server() {
title = title.removeRange(matcherDate.start(), matcherDate.end())
.removeSuffix(" (pubb.: )")
Circular(id, title, fullUrl, matcherDate.group(1)?.replace("-", "/") ?: "")
Circular(id, serverID, title, fullUrl, matcherDate.group(1)?.replace("-", "/") ?: "")
} else {
Circular(id, title, fullUrl, "")
Circular(id, serverID, title, fullUrl, "")
}
}
}

View File

@@ -0,0 +1,12 @@
package net.underdesk.circolapp.utils
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
// Taken from https://stackoverflow.com/questions/49493772/mediatorlivedata-or-switchmap-transformation-with-multiple-parameters
class DoubleTrigger<A, B>(a: LiveData<A>, b: LiveData<B>) : MediatorLiveData<Pair<A?, B?>>() {
init {
addSource(a) { value = it to b.value }
addSource(b) { value = a.value to it }
}
}

View File

@@ -18,24 +18,40 @@
package net.underdesk.circolapp.viewmodels
import android.app.Application
import android.content.SharedPreferences
import androidx.lifecycle.*
import androidx.preference.PreferenceManager
import kotlinx.coroutines.launch
import net.underdesk.circolapp.data.Circular
import net.underdesk.circolapp.data.CircularRepository
import net.underdesk.circolapp.utils.DoubleTrigger
class CircularLetterViewModel internal constructor(
private val circularRepository: CircularRepository
) : ViewModel() {
private val circularRepository: CircularRepository,
application: Application
) : AndroidViewModel(application), SharedPreferences.OnSharedPreferenceChangeListener {
private val preferenceManager = PreferenceManager.getDefaultSharedPreferences(application)
private val schoolID =
MutableLiveData(preferenceManager.getString("school", "0")?.toInt() ?: 0)
init {
updateCirculars()
preferenceManager.registerOnSharedPreferenceChangeListener(this)
}
val query = MutableLiveData("")
val circulars: LiveData<List<Circular>> = Transformations.switchMap(query) { input ->
if (input == null || input == "") {
circularRepository.circularDao.getLiveCirculars()
val circulars: LiveData<List<Circular>> =
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
if (input.first == null || input.first == "") {
circularRepository.circularDao.getLiveCirculars(input.second ?: 0)
} else {
circularRepository.circularDao.searchCirculars("%$input%")
circularRepository.circularDao.searchCirculars(
"%${input.first}%",
input.second ?: 0
)
}
}
@@ -57,4 +73,9 @@ class CircularLetterViewModel internal constructor(
}
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == "school")
schoolID.postValue(preferenceManager.getString("school", "0")?.toInt() ?: 0)
}
}

View File

@@ -1,15 +1,17 @@
package net.underdesk.circolapp.viewmodels
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import net.underdesk.circolapp.data.CircularRepository
class CircularLetterViewModelFactory(
private val circularRepository: CircularRepository
) : ViewModelProvider.Factory {
private val circularRepository: CircularRepository,
val application: Application
) : ViewModelProvider.AndroidViewModelFactory(application) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return CircularLetterViewModel(circularRepository) as T
return CircularLetterViewModel(circularRepository, application) as T
}
}

View File

@@ -18,22 +18,45 @@
package net.underdesk.circolapp.viewmodels
import android.app.Application
import android.content.SharedPreferences
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import androidx.preference.PreferenceManager
import net.underdesk.circolapp.data.Circular
import net.underdesk.circolapp.data.CircularRepository
import net.underdesk.circolapp.utils.DoubleTrigger
class FavouritesViewModel internal constructor(
private val circularRepository: CircularRepository
) : ViewModel() {
val query = MutableLiveData("")
val circulars: LiveData<List<Circular>> = Transformations.switchMap(query) { input ->
if (input == null || input == "") {
circularRepository.circularDao.getFavourites()
} else {
circularRepository.circularDao.searchFavourites("%$input%")
private val circularRepository: CircularRepository,
application: Application
) : AndroidViewModel(application), SharedPreferences.OnSharedPreferenceChangeListener {
private val preferenceManager = PreferenceManager.getDefaultSharedPreferences(application)
private val schoolID =
MutableLiveData(preferenceManager.getString("school", "0")?.toInt() ?: 0)
init {
preferenceManager.registerOnSharedPreferenceChangeListener(this)
}
val query = MutableLiveData("")
val circulars: LiveData<List<Circular>> =
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
if (input.first == null || input.first == "") {
circularRepository.circularDao.getFavourites(input.second ?: 0)
} else {
circularRepository.circularDao.searchFavourites(
"%${input.first}%",
input.second ?: 0
)
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == "school")
schoolID.postValue(preferenceManager.getString("school", "0")?.toInt() ?: 0)
}
}

View File

@@ -1,15 +1,17 @@
package net.underdesk.circolapp.viewmodels
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import net.underdesk.circolapp.data.CircularRepository
class FavouritesViewModelFactory(
private val circularRepository: CircularRepository
) : ViewModelProvider.Factory {
private val circularRepository: CircularRepository,
val application: Application
) : ViewModelProvider.AndroidViewModelFactory(application) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return FavouritesViewModel(circularRepository) as T
return FavouritesViewModel(circularRepository, application) as T
}
}

View File

@@ -18,22 +18,45 @@
package net.underdesk.circolapp.viewmodels
import android.app.Application
import android.content.SharedPreferences
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import androidx.preference.PreferenceManager
import net.underdesk.circolapp.data.Circular
import net.underdesk.circolapp.data.CircularRepository
import net.underdesk.circolapp.utils.DoubleTrigger
class RemindersViewModel internal constructor(
private val circularRepository: CircularRepository
) : ViewModel() {
val query = MutableLiveData("")
val circulars: LiveData<List<Circular>> = Transformations.switchMap(query) { input ->
if (input == null || input == "") {
circularRepository.circularDao.getReminders()
} else {
circularRepository.circularDao.searchReminders("%$input%")
private val circularRepository: CircularRepository,
application: Application
) : AndroidViewModel(application), SharedPreferences.OnSharedPreferenceChangeListener {
private val preferenceManager = PreferenceManager.getDefaultSharedPreferences(application)
private val schoolID =
MutableLiveData(preferenceManager.getString("school", "0")?.toInt() ?: 0)
init {
preferenceManager.registerOnSharedPreferenceChangeListener(this)
}
val query = MutableLiveData("")
val circulars: LiveData<List<Circular>> =
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
if (input.first == null || input.first == "") {
circularRepository.circularDao.getReminders(input.second ?: 0)
} else {
circularRepository.circularDao.searchReminders(
"%${input.first}%",
input.second ?: 0
)
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == "school")
schoolID.postValue(preferenceManager.getString("school", "0")?.toInt() ?: 0)
}
}

View File

@@ -1,15 +1,17 @@
package net.underdesk.circolapp.viewmodels
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import net.underdesk.circolapp.data.CircularRepository
class RemindersViewModelFactory(
private val circularRepository: CircularRepository
) : ViewModelProvider.Factory {
private val circularRepository: CircularRepository,
val application: Application
) : ViewModelProvider.AndroidViewModelFactory(application) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return RemindersViewModel(circularRepository) as T
return RemindersViewModel(circularRepository, application) as T
}
}