mirror of
https://github.com/Matte23/circolapp.git
synced 2025-12-06 07:29:10 +00:00
Add shared module
This commit is contained in:
@@ -46,8 +46,13 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
|
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
|
||||||
|
|
||||||
|
// Shared module
|
||||||
|
implementation(project(":shared"))
|
||||||
|
|
||||||
// Kotlin
|
// Kotlin
|
||||||
implementation(Dependencies.Kotlin.core)
|
implementation(Dependencies.Kotlin.core)
|
||||||
|
implementation(Dependencies.Kotlin.coroutinesCore)
|
||||||
|
implementation(Dependencies.Kotlin.coroutinesAndroid)
|
||||||
|
|
||||||
// AndroidX
|
// AndroidX
|
||||||
implementation(Dependencies.AndroidX.appcompat)
|
implementation(Dependencies.AndroidX.appcompat)
|
||||||
@@ -55,13 +60,11 @@ dependencies {
|
|||||||
implementation(Dependencies.AndroidX.constraintLayout)
|
implementation(Dependencies.AndroidX.constraintLayout)
|
||||||
implementation(Dependencies.AndroidX.swipeRefreshLayout)
|
implementation(Dependencies.AndroidX.swipeRefreshLayout)
|
||||||
implementation(Dependencies.AndroidX.lifecycleExtensions)
|
implementation(Dependencies.AndroidX.lifecycleExtensions)
|
||||||
|
implementation(Dependencies.AndroidX.lifecycleLiveData)
|
||||||
implementation(Dependencies.AndroidX.preference)
|
implementation(Dependencies.AndroidX.preference)
|
||||||
implementation(Dependencies.AndroidX.navigationFragment)
|
implementation(Dependencies.AndroidX.navigationFragment)
|
||||||
implementation(Dependencies.AndroidX.navigationUi)
|
implementation(Dependencies.AndroidX.navigationUi)
|
||||||
implementation(Dependencies.AndroidX.workManager)
|
implementation(Dependencies.AndroidX.workManager)
|
||||||
implementation(Dependencies.AndroidX.Room.roomRuntime)
|
|
||||||
implementation(Dependencies.AndroidX.Room.roomKtx)
|
|
||||||
kapt(Dependencies.AndroidX.Room.roomCompiler)
|
|
||||||
|
|
||||||
// Google
|
// Google
|
||||||
implementation(Dependencies.Google.material)
|
implementation(Dependencies.Google.material)
|
||||||
@@ -70,17 +73,11 @@ dependencies {
|
|||||||
implementation(platform(Dependencies.Firebase.bom))
|
implementation(platform(Dependencies.Firebase.bom))
|
||||||
implementation(Dependencies.Firebase.messaging)
|
implementation(Dependencies.Firebase.messaging)
|
||||||
|
|
||||||
// Square
|
|
||||||
implementation(Dependencies.Square.okhttp)
|
|
||||||
implementation(Dependencies.Square.moshi)
|
|
||||||
kapt(Dependencies.Square.moshiCodegen)
|
|
||||||
|
|
||||||
// AboutLibraries
|
// AboutLibraries
|
||||||
implementation(Dependencies.AboutLibraries.aboutLibrariesCore)
|
implementation(Dependencies.AboutLibraries.aboutLibrariesCore)
|
||||||
implementation(Dependencies.AboutLibraries.aboutLibraries)
|
implementation(Dependencies.AboutLibraries.aboutLibraries)
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
implementation(Dependencies.Misc.jsoup)
|
|
||||||
implementation(Dependencies.Misc.appIntro)
|
implementation(Dependencies.Misc.appIntro)
|
||||||
implementation(Dependencies.Misc.materialSpinner)
|
implementation(Dependencies.Misc.materialSpinner)
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ import android.os.Build
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.app.TaskStackBuilder
|
import androidx.core.app.TaskStackBuilder
|
||||||
import net.underdesk.circolapp.data.AppDatabase
|
import kotlinx.coroutines.GlobalScope
|
||||||
import net.underdesk.circolapp.data.Circular
|
import kotlinx.coroutines.launch
|
||||||
|
import net.underdesk.circolapp.data.AndroidDatabase
|
||||||
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
|
|
||||||
class AlarmBroadcastReceiver : BroadcastReceiver() {
|
class AlarmBroadcastReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
@@ -41,27 +43,25 @@ class AlarmBroadcastReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
object : Thread() {
|
GlobalScope.launch {
|
||||||
override fun run() {
|
createNotificationChannel(context)
|
||||||
createNotificationChannel(context)
|
val circular = AndroidDatabase.getDaoInstance(context).getCircular(
|
||||||
val circular = AppDatabase.getInstance(context).circularDao().getCircular(
|
intent.getLongExtra(
|
||||||
intent.getLongExtra(
|
CIRCULAR_ID,
|
||||||
CIRCULAR_ID,
|
0
|
||||||
0
|
),
|
||||||
),
|
intent.getIntExtra(
|
||||||
intent.getIntExtra(
|
SCHOOL_ID,
|
||||||
SCHOOL_ID,
|
0
|
||||||
0
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
createNotification(
|
)
|
||||||
context,
|
createNotification(
|
||||||
circular
|
context,
|
||||||
)
|
circular
|
||||||
AppDatabase.getInstance(context).circularDao()
|
)
|
||||||
.update(circular.apply { reminder = false })
|
AndroidDatabase.getDaoInstance(context)
|
||||||
}
|
.update(circular.id, circular.school, circular.favourite, false)
|
||||||
}.start()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNotification(context: Context, circular: Circular) {
|
private fun createNotification(context: Context, circular: Circular) {
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ 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.push.FirebaseTopicUtils
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
import net.underdesk.circolapp.server.AndroidServerApi
|
||||||
|
import net.underdesk.circolapp.shared.server.ServerAPI
|
||||||
import net.underdesk.circolapp.works.PollWork
|
import net.underdesk.circolapp.works.PollWork
|
||||||
|
|
||||||
class SettingsActivity : AppCompatActivity() {
|
class SettingsActivity : AppCompatActivity() {
|
||||||
@@ -55,7 +56,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
schoolPreference?.let { setSchoolListPreference(it) }
|
schoolPreference?.let { setSchoolListPreference(it) }
|
||||||
val schoolPreferenceListener =
|
val schoolPreferenceListener =
|
||||||
Preference.OnPreferenceChangeListener { _, value ->
|
Preference.OnPreferenceChangeListener { _, value ->
|
||||||
ServerAPI.changeServer(value.toString().toInt(), requireContext())
|
AndroidServerApi.changeServer(value.toString().toInt(), requireContext())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
schoolPreference?.onPreferenceChangeListener = schoolPreferenceListener
|
schoolPreference?.onPreferenceChangeListener = schoolPreferenceListener
|
||||||
@@ -109,7 +110,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
val enablePolling = sharedPreferences.getBoolean("enable_polling", false)
|
val enablePolling = sharedPreferences.getBoolean("enable_polling", false)
|
||||||
|
|
||||||
if (notifyNewCirculars && !enablePolling) {
|
if (notifyNewCirculars && !enablePolling) {
|
||||||
val serverID = ServerAPI.getInstance(requireContext()).serverID()
|
val serverID = AndroidServerApi.getInstance(requireContext()).serverID()
|
||||||
val serverToken = ServerAPI.Companion.Servers.values()[serverID].toString()
|
val serverToken = ServerAPI.Companion.Servers.values()[serverID].toString()
|
||||||
|
|
||||||
FirebaseTopicUtils.selectTopic(serverToken, requireContext())
|
FirebaseTopicUtils.selectTopic(serverToken, requireContext())
|
||||||
|
|||||||
@@ -32,17 +32,20 @@ import androidx.fragment.app.FragmentActivity
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.android.synthetic.main.item_circular.view.*
|
import kotlinx.android.synthetic.main.item_circular.view.*
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import net.underdesk.circolapp.AlarmBroadcastReceiver
|
import net.underdesk.circolapp.AlarmBroadcastReceiver
|
||||||
import net.underdesk.circolapp.R
|
import net.underdesk.circolapp.R
|
||||||
import net.underdesk.circolapp.data.AppDatabase
|
import net.underdesk.circolapp.data.AndroidDatabase
|
||||||
import net.underdesk.circolapp.data.Circular
|
|
||||||
import net.underdesk.circolapp.fragments.NewReminderFragment
|
import net.underdesk.circolapp.fragments.NewReminderFragment
|
||||||
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
import net.underdesk.circolapp.utils.DownloadableFile
|
import net.underdesk.circolapp.utils.DownloadableFile
|
||||||
import net.underdesk.circolapp.utils.FileUtils
|
import net.underdesk.circolapp.utils.FileUtils
|
||||||
|
|
||||||
class CircularLetterAdapter(
|
class CircularLetterAdapter(
|
||||||
private var circulars: List<Circular>,
|
private var circulars: List<Circular>,
|
||||||
private val adapterCallback: AdapterCallback
|
private val adapterCallback: AdapterCallback,
|
||||||
|
private val adapterScope: CoroutineScope
|
||||||
) :
|
) :
|
||||||
RecyclerView.Adapter<CircularLetterAdapter.CircularLetterViewHolder>() {
|
RecyclerView.Adapter<CircularLetterAdapter.CircularLetterViewHolder>() {
|
||||||
private lateinit var context: Context
|
private lateinit var context: Context
|
||||||
@@ -158,31 +161,37 @@ class CircularLetterAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
holder.favouriteButton.setOnClickListener {
|
holder.favouriteButton.setOnClickListener {
|
||||||
object : Thread() {
|
adapterScope.launch {
|
||||||
override fun run() {
|
AndroidDatabase.getDaoInstance(context).update(
|
||||||
AppDatabase.getInstance(context).circularDao()
|
circulars[position].id,
|
||||||
.update(circulars[position].apply { favourite = !favourite })
|
circulars[position].school,
|
||||||
}
|
!circulars[position].favourite,
|
||||||
}.start()
|
circulars[position].reminder
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.reminderButton.setOnClickListener {
|
holder.reminderButton.setOnClickListener {
|
||||||
if (circulars[position].reminder) {
|
if (circulars[position].reminder) {
|
||||||
object : Thread() {
|
|
||||||
override fun run() {
|
adapterScope.launch {
|
||||||
val pendingIntent = PendingIntent.getBroadcast(
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
circulars[position].id.toInt(),
|
circulars[position].id.toInt(),
|
||||||
Intent(context, AlarmBroadcastReceiver::class.java),
|
Intent(context, AlarmBroadcastReceiver::class.java),
|
||||||
0
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
pendingIntent.cancel()
|
||||||
|
|
||||||
|
AndroidDatabase.getDaoInstance(context)
|
||||||
|
.update(
|
||||||
|
circulars[position].id,
|
||||||
|
circulars[position].school,
|
||||||
|
circulars[position].favourite,
|
||||||
|
false
|
||||||
)
|
)
|
||||||
|
}
|
||||||
pendingIntent.cancel()
|
|
||||||
|
|
||||||
AppDatabase.getInstance(context).circularDao()
|
|
||||||
.update(circulars[position].apply { reminder = false })
|
|
||||||
}
|
|
||||||
}.start()
|
|
||||||
} else {
|
} else {
|
||||||
NewReminderFragment.create(circulars[position])
|
NewReminderFragment.create(circulars[position])
|
||||||
.show((context as FragmentActivity).supportFragmentManager, "NewReminderDialog")
|
.show((context as FragmentActivity).supportFragmentManager, "NewReminderDialog")
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package net.underdesk.circolapp.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import net.underdesk.circolapp.server.AndroidServerApi
|
||||||
|
import net.underdesk.circolapp.shared.data.CircularDao
|
||||||
|
import net.underdesk.circolapp.shared.data.CircularRepository
|
||||||
|
import net.underdesk.circolapp.shared.server.ServerAPI
|
||||||
|
|
||||||
|
object AndroidCircularRepository {
|
||||||
|
@Volatile
|
||||||
|
private var instance: CircularRepository? = null
|
||||||
|
|
||||||
|
fun getInstance(circularDao: CircularDao, serverAPI: ServerAPI) =
|
||||||
|
instance ?: synchronized(this) {
|
||||||
|
instance ?: CircularRepository(circularDao, serverAPI).also { instance = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInstance(context: Context) =
|
||||||
|
getInstance(AndroidDatabase.getDaoInstance(context), AndroidServerApi.getInstance(context))
|
||||||
|
}
|
||||||
@@ -18,31 +18,27 @@
|
|||||||
|
|
||||||
package net.underdesk.circolapp.data
|
package net.underdesk.circolapp.data
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import android.content.Context
|
||||||
|
import net.underdesk.circolapp.shared.data.AppDatabase
|
||||||
|
import net.underdesk.circolapp.shared.data.CircularDao
|
||||||
|
import net.underdesk.circolapp.shared.data.DatabaseDriverFactory
|
||||||
|
|
||||||
class Converters {
|
object AndroidDatabase {
|
||||||
|
|
||||||
@TypeConverter
|
@Volatile
|
||||||
fun stringToList(data: String?): List<String> {
|
private var instance: AppDatabase? = null
|
||||||
val list: MutableList<String> = mutableListOf()
|
|
||||||
|
|
||||||
if (data != null) {
|
fun getInstance(context: Context): AppDatabase {
|
||||||
for (attachment in data.split("˜")) {
|
return instance ?: synchronized(this) {
|
||||||
list.add(attachment)
|
instance ?: AppDatabase(
|
||||||
}
|
DatabaseDriverFactory(
|
||||||
|
context
|
||||||
|
).createDriver()
|
||||||
|
).also { instance = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.dropLast(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
fun getDaoInstance(context: Context): CircularDao {
|
||||||
fun listToString(list: List<String>): String {
|
return CircularDao(getInstance(context))
|
||||||
var string = ""
|
|
||||||
|
|
||||||
for (attachment in list) {
|
|
||||||
string += "$attachment˜"
|
|
||||||
}
|
|
||||||
|
|
||||||
return string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Circolapp
|
|
||||||
* Copyright (C) 2019-2020 Matteo Schiff
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.underdesk.circolapp.data
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.room.Database
|
|
||||||
import androidx.room.Room
|
|
||||||
import androidx.room.RoomDatabase
|
|
||||||
import androidx.room.TypeConverters
|
|
||||||
|
|
||||||
@Database(entities = [Circular::class], version = 2, exportSchema = false)
|
|
||||||
@TypeConverters(Converters::class)
|
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
|
||||||
abstract fun circularDao(): CircularDao
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
private var instance: AppDatabase? = null
|
|
||||||
|
|
||||||
private const val DATABASE_NAME = "database"
|
|
||||||
|
|
||||||
fun getInstance(context: Context): AppDatabase {
|
|
||||||
return instance ?: synchronized(this) {
|
|
||||||
instance ?: Room.databaseBuilder(
|
|
||||||
context,
|
|
||||||
AppDatabase::class.java,
|
|
||||||
DATABASE_NAME
|
|
||||||
).fallbackToDestructiveMigration().build().also { instance = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Circolapp
|
|
||||||
* Copyright (C) 2019-2020 Matteo Schiff
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.underdesk.circolapp.data
|
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.room.*
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface CircularDao {
|
|
||||||
@Query("SELECT * FROM circulars WHERE school is :school ORDER BY id DESC")
|
|
||||||
fun getCirculars(school: Int): 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 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 school is :school AND id = :id ORDER BY id DESC")
|
|
||||||
fun getCircular(id: Long, school: Int): 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 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 school is :school AND reminder ORDER BY id DESC")
|
|
||||||
fun getReminders(school: Int): 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>)
|
|
||||||
|
|
||||||
@Update
|
|
||||||
fun update(circular: Circular)
|
|
||||||
|
|
||||||
@Query("DELETE FROM circulars")
|
|
||||||
fun deleteAll()
|
|
||||||
}
|
|
||||||
@@ -1,50 +1 @@
|
|||||||
package net.underdesk.circolapp.data
|
package net.underdesk.circolapp.data
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
|
||||||
|
|
||||||
class CircularRepository(
|
|
||||||
val circularDao: CircularDao,
|
|
||||||
private val serverAPI: ServerAPI
|
|
||||||
) {
|
|
||||||
suspend fun updateCirculars(returnNewCirculars: Boolean = true): Pair<List<Circular>, Boolean> {
|
|
||||||
var onlyNewCirculars = listOf<Circular>()
|
|
||||||
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
val result = serverAPI.getCircularsFromServer()
|
|
||||||
if (result.second == ServerAPI.Companion.Result.ERROR)
|
|
||||||
return@withContext Pair(emptyList(), false)
|
|
||||||
|
|
||||||
val oldCirculars = circularDao.getCirculars(serverAPI.serverID())
|
|
||||||
val newCirculars = result.first
|
|
||||||
|
|
||||||
if (newCirculars.size != oldCirculars.size) {
|
|
||||||
if (newCirculars.size < oldCirculars.size) {
|
|
||||||
circularDao.deleteAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (returnNewCirculars) {
|
|
||||||
val oldCircularsSize =
|
|
||||||
if (newCirculars.size < oldCirculars.size) 0 else oldCirculars.size
|
|
||||||
|
|
||||||
val circularCount = newCirculars.size - oldCircularsSize
|
|
||||||
onlyNewCirculars = newCirculars.subList(0, circularCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
circularDao.insertAll(newCirculars)
|
|
||||||
}
|
|
||||||
Pair(onlyNewCirculars, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@Volatile
|
|
||||||
private var instance: CircularRepository? = null
|
|
||||||
|
|
||||||
fun getInstance(circularDao: CircularDao, serverAPI: ServerAPI) =
|
|
||||||
instance ?: synchronized(this) {
|
|
||||||
instance ?: CircularRepository(circularDao, serverAPI).also { instance = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.view.ViewGroup
|
|||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.fragment_circular_letters.*
|
import kotlinx.android.synthetic.main.fragment_circular_letters.*
|
||||||
@@ -32,9 +33,7 @@ import kotlinx.android.synthetic.main.fragment_circular_letters.view.*
|
|||||||
import net.underdesk.circolapp.MainActivity
|
import net.underdesk.circolapp.MainActivity
|
||||||
import net.underdesk.circolapp.R
|
import net.underdesk.circolapp.R
|
||||||
import net.underdesk.circolapp.adapters.CircularLetterAdapter
|
import net.underdesk.circolapp.adapters.CircularLetterAdapter
|
||||||
import net.underdesk.circolapp.data.AppDatabase
|
import net.underdesk.circolapp.data.AndroidCircularRepository
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
|
||||||
import net.underdesk.circolapp.viewmodels.CircularLetterViewModel
|
import net.underdesk.circolapp.viewmodels.CircularLetterViewModel
|
||||||
import net.underdesk.circolapp.viewmodels.CircularLetterViewModelFactory
|
import net.underdesk.circolapp.viewmodels.CircularLetterViewModelFactory
|
||||||
|
|
||||||
@@ -45,10 +44,7 @@ class CircularLetterFragment :
|
|||||||
|
|
||||||
private val circularLetterViewModel: CircularLetterViewModel by viewModels {
|
private val circularLetterViewModel: CircularLetterViewModel by viewModels {
|
||||||
CircularLetterViewModelFactory(
|
CircularLetterViewModelFactory(
|
||||||
CircularRepository.getInstance(
|
AndroidCircularRepository.getInstance(requireContext()),
|
||||||
AppDatabase.getInstance(requireContext()).circularDao(),
|
|
||||||
ServerAPI.getInstance(requireContext())
|
|
||||||
),
|
|
||||||
requireActivity().application
|
requireActivity().application
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -67,7 +63,7 @@ class CircularLetterFragment :
|
|||||||
{
|
{
|
||||||
if (root.circulars_list.adapter == null) {
|
if (root.circulars_list.adapter == null) {
|
||||||
root.circulars_list.adapter =
|
root.circulars_list.adapter =
|
||||||
CircularLetterAdapter(it, activity as MainActivity)
|
CircularLetterAdapter(it, activity as MainActivity, lifecycleScope)
|
||||||
} else {
|
} else {
|
||||||
(root.circulars_list.adapter as CircularLetterAdapter).changeDataSet(it)
|
(root.circulars_list.adapter as CircularLetterAdapter).changeDataSet(it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,13 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import kotlinx.android.synthetic.main.fragment_circular_letters.view.*
|
import kotlinx.android.synthetic.main.fragment_circular_letters.view.*
|
||||||
import net.underdesk.circolapp.MainActivity
|
import net.underdesk.circolapp.MainActivity
|
||||||
import net.underdesk.circolapp.R
|
import net.underdesk.circolapp.R
|
||||||
import net.underdesk.circolapp.adapters.CircularLetterAdapter
|
import net.underdesk.circolapp.adapters.CircularLetterAdapter
|
||||||
import net.underdesk.circolapp.data.AppDatabase
|
import net.underdesk.circolapp.data.AndroidCircularRepository
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
|
||||||
import net.underdesk.circolapp.viewmodels.FavouritesViewModel
|
import net.underdesk.circolapp.viewmodels.FavouritesViewModel
|
||||||
import net.underdesk.circolapp.viewmodels.FavouritesViewModelFactory
|
import net.underdesk.circolapp.viewmodels.FavouritesViewModelFactory
|
||||||
|
|
||||||
@@ -39,10 +38,7 @@ class FavouritesFragment : Fragment(), MainActivity.SearchCallback {
|
|||||||
|
|
||||||
private val favouritesViewModel: FavouritesViewModel by viewModels {
|
private val favouritesViewModel: FavouritesViewModel by viewModels {
|
||||||
FavouritesViewModelFactory(
|
FavouritesViewModelFactory(
|
||||||
CircularRepository.getInstance(
|
AndroidCircularRepository.getInstance(requireContext()),
|
||||||
AppDatabase.getInstance(requireContext()).circularDao(),
|
|
||||||
ServerAPI.getInstance(requireContext())
|
|
||||||
),
|
|
||||||
requireActivity().application
|
requireActivity().application
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -62,7 +58,7 @@ class FavouritesFragment : Fragment(), MainActivity.SearchCallback {
|
|||||||
{
|
{
|
||||||
if (root.circulars_list.adapter == null) {
|
if (root.circulars_list.adapter == null) {
|
||||||
root.circulars_list.adapter =
|
root.circulars_list.adapter =
|
||||||
CircularLetterAdapter(it, activity as MainActivity)
|
CircularLetterAdapter(it, activity as MainActivity, lifecycleScope)
|
||||||
} else {
|
} else {
|
||||||
(root.circulars_list.adapter as CircularLetterAdapter).changeDataSet(it)
|
(root.circulars_list.adapter as CircularLetterAdapter).changeDataSet(it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,36 +29,33 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.app.AlarmManagerCompat
|
import androidx.core.app.AlarmManagerCompat
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import kotlinx.android.synthetic.main.dialog_reminder.*
|
import kotlinx.android.synthetic.main.dialog_reminder.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import net.underdesk.circolapp.AlarmBroadcastReceiver
|
import net.underdesk.circolapp.AlarmBroadcastReceiver
|
||||||
import net.underdesk.circolapp.R
|
import net.underdesk.circolapp.R
|
||||||
import net.underdesk.circolapp.data.AppDatabase
|
import net.underdesk.circolapp.data.AndroidDatabase
|
||||||
import net.underdesk.circolapp.data.Circular
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class NewReminderFragment : DialogFragment() {
|
class NewReminderFragment : DialogFragment() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val CIRCULAR = "circular"
|
|
||||||
|
|
||||||
fun create(circular: Circular): NewReminderFragment {
|
fun create(circular: Circular): NewReminderFragment {
|
||||||
val dialog = NewReminderFragment()
|
val dialog = NewReminderFragment()
|
||||||
dialog.arguments = Bundle().apply {
|
dialog.circular = circular
|
||||||
putParcelable(CIRCULAR, circular)
|
|
||||||
}
|
|
||||||
return dialog
|
return dialog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var dateNotChosen = true
|
private var dateNotChosen = true
|
||||||
var circular: Circular? = null
|
lateinit var circular: Circular
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
circular = arguments?.getParcelable(CIRCULAR)
|
|
||||||
return inflater.inflate(R.layout.dialog_reminder, container)
|
return inflater.inflate(R.layout.dialog_reminder, container)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,33 +98,31 @@ class NewReminderFragment : DialogFragment() {
|
|||||||
minute
|
minute
|
||||||
)
|
)
|
||||||
|
|
||||||
object : Thread() {
|
lifecycleScope.launch {
|
||||||
override fun run() {
|
context?.let { context ->
|
||||||
context?.let { context ->
|
circular.let { circular ->
|
||||||
circular?.let { circular ->
|
AndroidDatabase.getDaoInstance(context)
|
||||||
AppDatabase.getInstance(context).circularDao()
|
.update(circular.id, circular.school, circular.favourite, true)
|
||||||
.update(circular.apply { reminder = true })
|
|
||||||
|
|
||||||
val pendingIntent = PendingIntent.getBroadcast(
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
circular.id.toInt(),
|
circular.id.toInt(),
|
||||||
Intent(context, AlarmBroadcastReceiver::class.java)
|
Intent(context, AlarmBroadcastReceiver::class.java)
|
||||||
.putExtra(AlarmBroadcastReceiver.CIRCULAR_ID, circular.id)
|
.putExtra(AlarmBroadcastReceiver.CIRCULAR_ID, circular.id)
|
||||||
.putExtra(AlarmBroadcastReceiver.SCHOOL_ID, circular.school),
|
.putExtra(AlarmBroadcastReceiver.SCHOOL_ID, circular.school),
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
AlarmManagerCompat.setExactAndAllowWhileIdle(
|
AlarmManagerCompat.setExactAndAllowWhileIdle(
|
||||||
context.getSystemService(Context.ALARM_SERVICE) as AlarmManager,
|
context.getSystemService(Context.ALARM_SERVICE) as AlarmManager,
|
||||||
AlarmManager.RTC_WAKEUP,
|
AlarmManager.RTC_WAKEUP,
|
||||||
calendar.timeInMillis,
|
calendar.timeInMillis,
|
||||||
pendingIntent
|
pendingIntent
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dismiss()
|
|
||||||
}
|
}
|
||||||
}.start()
|
dismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,14 +24,13 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import kotlinx.android.synthetic.main.fragment_circular_letters.view.*
|
import kotlinx.android.synthetic.main.fragment_circular_letters.view.*
|
||||||
import net.underdesk.circolapp.MainActivity
|
import net.underdesk.circolapp.MainActivity
|
||||||
import net.underdesk.circolapp.R
|
import net.underdesk.circolapp.R
|
||||||
import net.underdesk.circolapp.adapters.CircularLetterAdapter
|
import net.underdesk.circolapp.adapters.CircularLetterAdapter
|
||||||
import net.underdesk.circolapp.data.AppDatabase
|
import net.underdesk.circolapp.data.AndroidCircularRepository
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
|
||||||
import net.underdesk.circolapp.viewmodels.RemindersViewModel
|
import net.underdesk.circolapp.viewmodels.RemindersViewModel
|
||||||
import net.underdesk.circolapp.viewmodels.RemindersViewModelFactory
|
import net.underdesk.circolapp.viewmodels.RemindersViewModelFactory
|
||||||
|
|
||||||
@@ -39,10 +38,7 @@ class RemindersFragment : Fragment(), MainActivity.SearchCallback {
|
|||||||
|
|
||||||
private val remindersViewModel: RemindersViewModel by viewModels {
|
private val remindersViewModel: RemindersViewModel by viewModels {
|
||||||
RemindersViewModelFactory(
|
RemindersViewModelFactory(
|
||||||
CircularRepository.getInstance(
|
AndroidCircularRepository.getInstance(requireContext()),
|
||||||
AppDatabase.getInstance(requireContext()).circularDao(),
|
|
||||||
ServerAPI.getInstance(requireContext())
|
|
||||||
),
|
|
||||||
requireActivity().application
|
requireActivity().application
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -62,7 +58,7 @@ class RemindersFragment : Fragment(), MainActivity.SearchCallback {
|
|||||||
{
|
{
|
||||||
if (root.circulars_list.adapter == null) {
|
if (root.circulars_list.adapter == null) {
|
||||||
root.circulars_list.adapter =
|
root.circulars_list.adapter =
|
||||||
CircularLetterAdapter(it, activity as MainActivity)
|
CircularLetterAdapter(it, activity as MainActivity, lifecycleScope)
|
||||||
} else {
|
} else {
|
||||||
(root.circulars_list.adapter as CircularLetterAdapter).changeDataSet(it)
|
(root.circulars_list.adapter as CircularLetterAdapter).changeDataSet(it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import com.github.appintro.SlidePolicy
|
|||||||
import com.tiper.MaterialSpinner
|
import com.tiper.MaterialSpinner
|
||||||
import kotlinx.android.synthetic.main.fragment_school_selection.view.*
|
import kotlinx.android.synthetic.main.fragment_school_selection.view.*
|
||||||
import net.underdesk.circolapp.R
|
import net.underdesk.circolapp.R
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
import net.underdesk.circolapp.server.AndroidServerApi
|
||||||
|
import net.underdesk.circolapp.shared.server.ServerAPI
|
||||||
|
|
||||||
class SchoolSelectionFragment : Fragment(), SlidePolicy, MaterialSpinner.OnItemSelectedListener {
|
class SchoolSelectionFragment : Fragment(), SlidePolicy, MaterialSpinner.OnItemSelectedListener {
|
||||||
private lateinit var preferenceManager: SharedPreferences
|
private lateinit var preferenceManager: SharedPreferences
|
||||||
@@ -53,7 +54,7 @@ class SchoolSelectionFragment : Fragment(), SlidePolicy, MaterialSpinner.OnItemS
|
|||||||
editor.putString("school", position.toString())
|
editor.putString("school", position.toString())
|
||||||
editor.apply()
|
editor.apply()
|
||||||
|
|
||||||
ServerAPI.changeServer(position, requireContext())
|
AndroidServerApi.changeServer(position, requireContext())
|
||||||
schoolSelected = true
|
schoolSelected = true
|
||||||
|
|
||||||
parent.error = null
|
parent.error = null
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package net.underdesk.circolapp.server
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import net.underdesk.circolapp.push.FirebaseTopicUtils
|
||||||
|
import net.underdesk.circolapp.shared.server.ServerAPI
|
||||||
|
|
||||||
|
object AndroidServerApi {
|
||||||
|
@Volatile
|
||||||
|
private var instance: ServerAPI? = null
|
||||||
|
|
||||||
|
fun getInstance(server: ServerAPI.Companion.Servers): ServerAPI {
|
||||||
|
return instance ?: synchronized(this) {
|
||||||
|
instance ?: ServerAPI(ServerAPI.createServer(server)).also { instance = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInstance(context: Context): ServerAPI {
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val serverID = sharedPreferences.getString("school", "0")?.toInt() ?: 0
|
||||||
|
|
||||||
|
val server = ServerAPI.Companion.Servers.values()[serverID]
|
||||||
|
|
||||||
|
return instance ?: synchronized(this) {
|
||||||
|
instance ?: ServerAPI(ServerAPI.createServer(server)).also { instance = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeServer(index: Int, context: Context) {
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val newServer = ServerAPI.Companion.Servers.values()[index]
|
||||||
|
|
||||||
|
val notifyNewCirculars = sharedPreferences.getBoolean("notify_new_circulars", true)
|
||||||
|
val enablePolling = sharedPreferences.getBoolean("enable_polling", false)
|
||||||
|
|
||||||
|
if (notifyNewCirculars && !enablePolling)
|
||||||
|
FirebaseTopicUtils.selectTopic(newServer.toString(), context)
|
||||||
|
|
||||||
|
instance?.changeServer(ServerAPI.createServer(newServer))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
* Circolapp
|
|
||||||
* Copyright (C) 2019-2020 Matteo Schiff
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.underdesk.circolapp.server
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import net.underdesk.circolapp.data.Circular
|
|
||||||
import net.underdesk.circolapp.push.FirebaseTopicUtils
|
|
||||||
import net.underdesk.circolapp.server.curie.CurieServer
|
|
||||||
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()
|
|
||||||
|
|
||||||
if (newCircularsAvailable.second == Result.ERROR)
|
|
||||||
return Pair(emptyList(), Result.ERROR)
|
|
||||||
|
|
||||||
if (!newCircularsAvailable.first)
|
|
||||||
return Pair(emptyList(), Result.SUCCESS)
|
|
||||||
|
|
||||||
return server.getCircularsFromServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeServer(server: Server) {
|
|
||||||
this.server = server
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
enum class Servers {
|
|
||||||
CURIE, PORPORATO
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Result {
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
private var instance: ServerAPI? = null
|
|
||||||
|
|
||||||
fun getInstance(server: Servers): ServerAPI {
|
|
||||||
return instance ?: synchronized(this) {
|
|
||||||
instance ?: ServerAPI(createServer(server)).also { instance = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getInstance(context: Context): ServerAPI {
|
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
val serverID = sharedPreferences.getString("school", "0")?.toInt() ?: 0
|
|
||||||
|
|
||||||
val server = Servers.values()[serverID]
|
|
||||||
|
|
||||||
return instance ?: synchronized(this) {
|
|
||||||
instance ?: ServerAPI(createServer(server)).also { instance = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changeServer(index: Int, context: Context) {
|
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
val newServer = Servers.values()[index]
|
|
||||||
|
|
||||||
val notifyNewCirculars = sharedPreferences.getBoolean("notify_new_circulars", true)
|
|
||||||
val enablePolling = sharedPreferences.getBoolean("enable_polling", false)
|
|
||||||
|
|
||||||
if (notifyNewCirculars && !enablePolling)
|
|
||||||
FirebaseTopicUtils.selectTopic(newServer.toString(), context)
|
|
||||||
|
|
||||||
instance?.changeServer(createServer(newServer))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createServer(server: Servers) = when (server) {
|
|
||||||
Servers.CURIE -> CurieServer()
|
|
||||||
Servers.PORPORATO -> PorporatoServer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package net.underdesk.circolapp.server.curie
|
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import net.underdesk.circolapp.data.Circular
|
|
||||||
import net.underdesk.circolapp.server.Server
|
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
|
||||||
import net.underdesk.circolapp.server.curie.pojo.Response
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import java.io.IOException
|
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
class CurieServer : Server() {
|
|
||||||
private val moshi = Moshi.Builder().build()
|
|
||||||
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) {
|
|
||||||
val json = retrieveDataFromServer()
|
|
||||||
|
|
||||||
val document = Jsoup.parseBodyFragment(json.content.rendered)
|
|
||||||
val htmlList = document.getElementsByTag("ul")[0].getElementsByTag("a")
|
|
||||||
|
|
||||||
val list = ArrayList<Circular>()
|
|
||||||
|
|
||||||
htmlList.forEach { element ->
|
|
||||||
if (element.parents().size == 6) {
|
|
||||||
list.last().attachmentsNames.add(element.text())
|
|
||||||
list.last().attachmentsUrls.add(element.attr("href"))
|
|
||||||
} else if (element.parents().size == 4) {
|
|
||||||
list.add(generateFromString(element.text(), element.attr("href")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Pair(list, ServerAPI.Companion.Result.SUCCESS)
|
|
||||||
}
|
|
||||||
} catch (exception: IOException) {
|
|
||||||
Pair(emptyList(), ServerAPI.Companion.Result.ERROR)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun newCircularsAvailable(): Pair<Boolean, ServerAPI.Companion.Result> {
|
|
||||||
return Pair(true, ServerAPI.Companion.Result.SUCCESS)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private suspend fun retrieveDataFromServer(): Response {
|
|
||||||
val request = Request.Builder()
|
|
||||||
.url(ENDPOINT_URL)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
val response = client.newCall(request).execute()
|
|
||||||
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
throw IOException("HTTP error code: ${response.code})")
|
|
||||||
}
|
|
||||||
|
|
||||||
responseAdapter.fromJson(
|
|
||||||
response.body!!.string()
|
|
||||||
)!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateFromString(string: String, url: String): Circular {
|
|
||||||
val idRegex =
|
|
||||||
"""(\d+)"""
|
|
||||||
val matcherId = Pattern.compile(idRegex).matcher(string)
|
|
||||||
matcherId.find()
|
|
||||||
val id = matcherId.group(1)
|
|
||||||
|
|
||||||
val dateRegex =
|
|
||||||
"""(\d{2}/\d{2}/\d{4})"""
|
|
||||||
val matcherDate = Pattern.compile(dateRegex).matcher(string)
|
|
||||||
|
|
||||||
var title = string.removeSuffix("-signed")
|
|
||||||
|
|
||||||
return if (matcherDate.find()) {
|
|
||||||
title = title.removeRange(0, matcherDate.end())
|
|
||||||
.removePrefix(" ")
|
|
||||||
.removePrefix("_")
|
|
||||||
.removePrefix(" ")
|
|
||||||
|
|
||||||
Circular(id.toLong(), serverID, title, url, matcherDate.group(1) ?: "")
|
|
||||||
} else {
|
|
||||||
Circular(id.toLong(), serverID, title, url, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val ENDPOINT_URL = "https://www.curiepinerolo.edu.it/wp-json/wp/v2/pages/5958"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package net.underdesk.circolapp.server.curie.pojo
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonClass
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class Content(
|
|
||||||
val rendered: String
|
|
||||||
)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package net.underdesk.circolapp.server.curie.pojo
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonClass
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class Response(
|
|
||||||
val content: Content
|
|
||||||
)
|
|
||||||
@@ -23,8 +23,8 @@ import android.content.SharedPreferences
|
|||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.underdesk.circolapp.data.Circular
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
import net.underdesk.circolapp.shared.data.CircularRepository
|
||||||
import net.underdesk.circolapp.utils.DoubleTrigger
|
import net.underdesk.circolapp.utils.DoubleTrigger
|
||||||
|
|
||||||
class CircularLetterViewModel internal constructor(
|
class CircularLetterViewModel internal constructor(
|
||||||
@@ -40,12 +40,12 @@ class CircularLetterViewModel internal constructor(
|
|||||||
val circulars: LiveData<List<Circular>> =
|
val circulars: LiveData<List<Circular>> =
|
||||||
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
|
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
|
||||||
if (input.first == null || input.first == "") {
|
if (input.first == null || input.first == "") {
|
||||||
circularRepository.circularDao.getLiveCirculars(input.second ?: 0)
|
circularRepository.circularDao.getFlowCirculars(input.second ?: 0).asLiveData()
|
||||||
} else {
|
} else {
|
||||||
circularRepository.circularDao.searchCirculars(
|
circularRepository.circularDao.searchCirculars(
|
||||||
"%${input.first}%",
|
"%${input.first}%",
|
||||||
input.second ?: 0
|
input.second ?: 0
|
||||||
)
|
).asLiveData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package net.underdesk.circolapp.viewmodels
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
import net.underdesk.circolapp.shared.data.CircularRepository
|
||||||
|
|
||||||
class CircularLetterViewModelFactory(
|
class CircularLetterViewModelFactory(
|
||||||
private val circularRepository: CircularRepository,
|
private val circularRepository: CircularRepository,
|
||||||
|
|||||||
@@ -20,13 +20,10 @@ package net.underdesk.circolapp.viewmodels
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.*
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.Transformations
|
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import net.underdesk.circolapp.data.Circular
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
import net.underdesk.circolapp.shared.data.CircularRepository
|
||||||
import net.underdesk.circolapp.utils.DoubleTrigger
|
import net.underdesk.circolapp.utils.DoubleTrigger
|
||||||
|
|
||||||
class FavouritesViewModel internal constructor(
|
class FavouritesViewModel internal constructor(
|
||||||
@@ -46,12 +43,12 @@ class FavouritesViewModel internal constructor(
|
|||||||
val circulars: LiveData<List<Circular>> =
|
val circulars: LiveData<List<Circular>> =
|
||||||
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
|
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
|
||||||
if (input.first == null || input.first == "") {
|
if (input.first == null || input.first == "") {
|
||||||
circularRepository.circularDao.getFavourites(input.second ?: 0)
|
circularRepository.circularDao.getFavourites(input.second ?: 0).asLiveData()
|
||||||
} else {
|
} else {
|
||||||
circularRepository.circularDao.searchFavourites(
|
circularRepository.circularDao.searchFavourites(
|
||||||
"%${input.first}%",
|
"%${input.first}%",
|
||||||
input.second ?: 0
|
input.second ?: 0
|
||||||
)
|
).asLiveData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package net.underdesk.circolapp.viewmodels
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
import net.underdesk.circolapp.shared.data.CircularRepository
|
||||||
|
|
||||||
class FavouritesViewModelFactory(
|
class FavouritesViewModelFactory(
|
||||||
private val circularRepository: CircularRepository,
|
private val circularRepository: CircularRepository,
|
||||||
|
|||||||
@@ -20,13 +20,10 @@ package net.underdesk.circolapp.viewmodels
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.*
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.Transformations
|
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import net.underdesk.circolapp.data.Circular
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
import net.underdesk.circolapp.shared.data.CircularRepository
|
||||||
import net.underdesk.circolapp.utils.DoubleTrigger
|
import net.underdesk.circolapp.utils.DoubleTrigger
|
||||||
|
|
||||||
class RemindersViewModel internal constructor(
|
class RemindersViewModel internal constructor(
|
||||||
@@ -46,12 +43,12 @@ class RemindersViewModel internal constructor(
|
|||||||
val circulars: LiveData<List<Circular>> =
|
val circulars: LiveData<List<Circular>> =
|
||||||
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
|
Transformations.switchMap(DoubleTrigger(query, schoolID)) { input ->
|
||||||
if (input.first == null || input.first == "") {
|
if (input.first == null || input.first == "") {
|
||||||
circularRepository.circularDao.getReminders(input.second ?: 0)
|
circularRepository.circularDao.getReminders(input.second ?: 0).asLiveData()
|
||||||
} else {
|
} else {
|
||||||
circularRepository.circularDao.searchReminders(
|
circularRepository.circularDao.searchReminders(
|
||||||
"%${input.first}%",
|
"%${input.first}%",
|
||||||
input.second ?: 0
|
input.second ?: 0
|
||||||
)
|
).asLiveData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package net.underdesk.circolapp.viewmodels
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
import net.underdesk.circolapp.shared.data.CircularRepository
|
||||||
|
|
||||||
class RemindersViewModelFactory(
|
class RemindersViewModelFactory(
|
||||||
private val circularRepository: CircularRepository,
|
private val circularRepository: CircularRepository,
|
||||||
|
|||||||
@@ -33,10 +33,8 @@ import androidx.work.*
|
|||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import net.underdesk.circolapp.MainActivity
|
import net.underdesk.circolapp.MainActivity
|
||||||
import net.underdesk.circolapp.R
|
import net.underdesk.circolapp.R
|
||||||
import net.underdesk.circolapp.data.AppDatabase
|
import net.underdesk.circolapp.data.AndroidCircularRepository
|
||||||
import net.underdesk.circolapp.data.Circular
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
import net.underdesk.circolapp.data.CircularRepository
|
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class PollWork(appContext: Context, workerParams: WorkerParameters) :
|
class PollWork(appContext: Context, workerParams: WorkerParameters) :
|
||||||
@@ -93,10 +91,7 @@ class PollWork(appContext: Context, workerParams: WorkerParameters) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun doWork(): Result = coroutineScope {
|
override suspend fun doWork(): Result = coroutineScope {
|
||||||
val circularRepository = CircularRepository.getInstance(
|
val circularRepository = AndroidCircularRepository.getInstance(applicationContext)
|
||||||
AppDatabase.getInstance(applicationContext).circularDao(),
|
|
||||||
ServerAPI.getInstance(applicationContext)
|
|
||||||
)
|
|
||||||
|
|
||||||
val result = circularRepository.updateCirculars()
|
val result = circularRepository.updateCirculars()
|
||||||
if (!result.second)
|
if (!result.second)
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ buildscript {
|
|||||||
dependencies {
|
dependencies {
|
||||||
classpath(Config.Plugin.android)
|
classpath(Config.Plugin.android)
|
||||||
classpath(Config.Plugin.kotlin)
|
classpath(Config.Plugin.kotlin)
|
||||||
|
classpath(Config.Plugin.serialization)
|
||||||
classpath(Config.Plugin.google)
|
classpath(Config.Plugin.google)
|
||||||
|
classpath(Config.Plugin.sqlDelight)
|
||||||
classpath(Config.Plugin.ktlint)
|
classpath(Config.Plugin.ktlint)
|
||||||
classpath(Config.Plugin.aboutLibraries)
|
classpath(Config.Plugin.aboutLibraries)
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
object Config {
|
object Config {
|
||||||
object Plugin {
|
object Plugin {
|
||||||
const val android = "com.android.tools.build:gradle:4.1.0"
|
const val android = "com.android.tools.build:gradle:4.1.1"
|
||||||
const val kotlin =
|
const val kotlin =
|
||||||
"org.jetbrains.kotlin:kotlin-gradle-plugin:${Dependencies.Kotlin.version}"
|
"org.jetbrains.kotlin:kotlin-gradle-plugin:${Dependencies.Kotlin.version}"
|
||||||
|
const val serialization =
|
||||||
|
"org.jetbrains.kotlin:kotlin-serialization:${Dependencies.Kotlin.version}"
|
||||||
const val google = "com.google.gms:google-services:4.3.4"
|
const val google = "com.google.gms:google-services:4.3.4"
|
||||||
const val ktlint = "org.jlleitschuh.gradle:ktlint-gradle:9.4.0"
|
const val ktlint = "org.jlleitschuh.gradle:ktlint-gradle:9.4.0"
|
||||||
|
const val sqlDelight =
|
||||||
|
"com.squareup.sqldelight:gradle-plugin:${Dependencies.SQLDelight.version}"
|
||||||
const val aboutLibraries =
|
const val aboutLibraries =
|
||||||
"com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${Dependencies.AboutLibraries.version}"
|
"com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${Dependencies.AboutLibraries.version}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ object Dependencies {
|
|||||||
object Kotlin {
|
object Kotlin {
|
||||||
const val version = "1.4.10"
|
const val version = "1.4.10"
|
||||||
const val core = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${version}"
|
const val core = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${version}"
|
||||||
|
const val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
|
||||||
|
const val coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
|
||||||
}
|
}
|
||||||
|
|
||||||
object AndroidX {
|
object AndroidX {
|
||||||
@@ -10,6 +12,7 @@ object Dependencies {
|
|||||||
const val constraintLayout = "androidx.constraintlayout:constraintlayout:2.0.3"
|
const val constraintLayout = "androidx.constraintlayout:constraintlayout:2.0.3"
|
||||||
const val swipeRefreshLayout = "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
const val swipeRefreshLayout = "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
const val lifecycleExtensions = "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
const val lifecycleExtensions = "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
||||||
|
const val lifecycleLiveData = "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
|
||||||
const val preference = "androidx.preference:preference-ktx:1.1.1"
|
const val preference = "androidx.preference:preference-ktx:1.1.1"
|
||||||
|
|
||||||
private const val navigationVersion = "2.3.1"
|
private const val navigationVersion = "2.3.1"
|
||||||
@@ -18,13 +21,6 @@ object Dependencies {
|
|||||||
const val navigationUi = "androidx.navigation:navigation-ui-ktx:${navigationVersion}"
|
const val navigationUi = "androidx.navigation:navigation-ui-ktx:${navigationVersion}"
|
||||||
|
|
||||||
const val workManager = "androidx.work:work-runtime-ktx:2.4.0"
|
const val workManager = "androidx.work:work-runtime-ktx:2.4.0"
|
||||||
|
|
||||||
object Room {
|
|
||||||
private const val version = "2.2.5"
|
|
||||||
const val roomRuntime = "androidx.room:room-runtime:${version}"
|
|
||||||
const val roomKtx = "androidx.room:room-ktx:${version}"
|
|
||||||
const val roomCompiler = "androidx.room:room-compiler:${version}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Google {
|
object Google {
|
||||||
@@ -36,12 +32,25 @@ object Dependencies {
|
|||||||
const val messaging = "com.google.firebase:firebase-messaging-ktx"
|
const val messaging = "com.google.firebase:firebase-messaging-ktx"
|
||||||
}
|
}
|
||||||
|
|
||||||
object Square {
|
object Ktor {
|
||||||
const val okhttp = "com.squareup.okhttp3:okhttp:4.8.1"
|
private const val version = "1.4.1"
|
||||||
|
const val ktorCore = "io.ktor:ktor-client-core:$version"
|
||||||
|
const val ktorOkhttp = "io.ktor:ktor-client-okhttp:$version"
|
||||||
|
const val ktorIos = "io.ktor:ktor-client-ios:$version"
|
||||||
|
const val ktorJson = "io.ktor:ktor-client-json:$version"
|
||||||
|
const val ktorSerialization = "io.ktor:ktor-client-serialization:$version"
|
||||||
|
}
|
||||||
|
|
||||||
private const val moshiVersion = "1.9.3"
|
object Serialization {
|
||||||
const val moshi = "com.squareup.moshi:moshi:${moshiVersion}"
|
const val json = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
|
||||||
const val moshiCodegen = "com.squareup.moshi:moshi-kotlin-codegen:${moshiVersion}"
|
}
|
||||||
|
|
||||||
|
object SQLDelight {
|
||||||
|
const val version = "1.4.4"
|
||||||
|
const val sqlDelightRuntime = "com.squareup.sqldelight:runtime:$version"
|
||||||
|
const val sqlDelightCoroutines = "com.squareup.sqldelight:coroutines-extensions:$version"
|
||||||
|
const val sqlDelightAndroid = "com.squareup.sqldelight:android-driver:$version"
|
||||||
|
const val sqlDelightNative = "com.squareup.sqldelight:native-driver:$version"
|
||||||
}
|
}
|
||||||
|
|
||||||
object AboutLibraries {
|
object AboutLibraries {
|
||||||
|
|||||||
@@ -19,3 +19,8 @@ android.useAndroidX=true
|
|||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
# Kotlin code style for this project: "official" or "obsolete":
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
kotlin.mpp.stability.nowarn=true
|
||||||
|
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||||
|
kotlin.native.enableDependencyPropagation=false
|
||||||
|
|
||||||
|
xcodeproj=./ios/circolapp
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
|
include(":shared")
|
||||||
include(":app")
|
include(":app")
|
||||||
rootProject.name = "Circolapp"
|
rootProject.name = "Circolapp"
|
||||||
|
|||||||
1
shared/.gitignore
vendored
Normal file
1
shared/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
124
shared/build.gradle.kts
Normal file
124
shared/build.gradle.kts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("multiplatform")
|
||||||
|
kotlin("plugin.serialization")
|
||||||
|
id("com.android.library")
|
||||||
|
id("kotlin-android-extensions")
|
||||||
|
id("com.squareup.sqldelight")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url = uri("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
android()
|
||||||
|
|
||||||
|
val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
|
||||||
|
|
||||||
|
if (sdkName.startsWith("iphoneos")) {
|
||||||
|
iosArm64("ios") {
|
||||||
|
binaries {
|
||||||
|
framework {
|
||||||
|
baseName = "Shared"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iosX64("ios") {
|
||||||
|
binaries {
|
||||||
|
framework {
|
||||||
|
baseName = "Shared"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val commonMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(Dependencies.Kotlin.coroutinesCore)
|
||||||
|
|
||||||
|
// Ktor
|
||||||
|
implementation(Dependencies.Ktor.ktorCore)
|
||||||
|
implementation(Dependencies.Ktor.ktorJson)
|
||||||
|
implementation(Dependencies.Ktor.ktorSerialization)
|
||||||
|
|
||||||
|
// Serialization
|
||||||
|
implementation(Dependencies.Serialization.json)
|
||||||
|
|
||||||
|
// SqlDelight
|
||||||
|
implementation(Dependencies.SQLDelight.sqlDelightRuntime)
|
||||||
|
implementation(Dependencies.SQLDelight.sqlDelightCoroutines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val androidMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(Dependencies.Kotlin.coroutinesAndroid)
|
||||||
|
|
||||||
|
// Ktor
|
||||||
|
implementation(Dependencies.Ktor.ktorOkhttp)
|
||||||
|
|
||||||
|
// SqlDelight
|
||||||
|
implementation(Dependencies.SQLDelight.sqlDelightAndroid)
|
||||||
|
|
||||||
|
// Misc
|
||||||
|
implementation(Dependencies.Misc.jsoup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val iosMain by getting {
|
||||||
|
dependencies {
|
||||||
|
// Ktor
|
||||||
|
implementation(Dependencies.Ktor.ktorIos)
|
||||||
|
|
||||||
|
// SqlDelight
|
||||||
|
implementation(Dependencies.SQLDelight.sqlDelightNative)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion(Config.Android.compileSdk)
|
||||||
|
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion(Config.Android.minSdk)
|
||||||
|
targetSdkVersion(Config.Android.targetSdk)
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
getByName("release") {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqldelight {
|
||||||
|
database("AppDatabase") {
|
||||||
|
packageName = "net.underdesk.circolapp.shared.data"
|
||||||
|
sourceFolders = listOf("sqldelight")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val packForXcode by tasks.creating(Sync::class) {
|
||||||
|
group = "build"
|
||||||
|
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
|
||||||
|
val targetName = "ios"
|
||||||
|
val framework =
|
||||||
|
kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode)
|
||||||
|
inputs.property("mode", mode)
|
||||||
|
dependsOn(framework.linkTask)
|
||||||
|
val targetDir = File(buildDir, "xcode-frameworks")
|
||||||
|
from({ framework.outputDirectory })
|
||||||
|
into(targetDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.getByName("build").dependsOn(packForXcode)
|
||||||
2
shared/src/androidMain/AndroidManifest.xml
Normal file
2
shared/src/androidMain/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="net.underdesk.circolapp.shared.android" />
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package net.underdesk.circolapp.shared
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
actual object PlatformDispatcher {
|
||||||
|
actual val IO = Dispatchers.IO
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package net.underdesk.circolapp.shared.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.squareup.sqldelight.android.AndroidSqliteDriver
|
||||||
|
import com.squareup.sqldelight.db.SqlDriver
|
||||||
|
|
||||||
|
actual class DatabaseDriverFactory(private val context: Context) {
|
||||||
|
actual fun createDriver(): SqlDriver {
|
||||||
|
return AndroidSqliteDriver(AppDatabase.Schema, context, "circolapp.db")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package net.underdesk.circolapp.shared.server
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.engine.okhttp.*
|
||||||
|
import io.ktor.client.features.json.*
|
||||||
|
import io.ktor.client.features.json.serializer.*
|
||||||
|
|
||||||
|
actual class KtorFactory actual constructor() {
|
||||||
|
actual fun createClient() = HttpClient(OkHttp) {
|
||||||
|
install(JsonFeature) {
|
||||||
|
serializer = KotlinxSerializer(
|
||||||
|
kotlinx.serialization.json.Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package net.underdesk.circolapp.shared.server.curie
|
||||||
|
|
||||||
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
|
||||||
|
actual class SpecificCurieServer actual constructor(private val curieServer: CurieServer) {
|
||||||
|
actual fun parseHtml(string: String): List<Circular> {
|
||||||
|
val document = Jsoup.parseBodyFragment(string)
|
||||||
|
val htmlList = document.getElementsByTag("ul")[0].getElementsByTag("a")
|
||||||
|
|
||||||
|
val list = ArrayList<Circular>()
|
||||||
|
|
||||||
|
htmlList.forEach { element ->
|
||||||
|
if (element.parents().size == 6) {
|
||||||
|
list.last().attachmentsNames.add(element.text())
|
||||||
|
list.last().attachmentsUrls.add(element.attr("href"))
|
||||||
|
} else if (element.parents().size == 4) {
|
||||||
|
list.add(curieServer.generateFromString(element.text(), element.attr("href")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package net.underdesk.circolapp.shared.server.porporato
|
||||||
|
|
||||||
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
|
||||||
|
actual class SpecificPorporatoServer actual constructor(private val porporatoServer: PorporatoServer) {
|
||||||
|
actual fun parseHtml(string: String): List<Circular> {
|
||||||
|
val document = Jsoup.parseBodyFragment(string)
|
||||||
|
val htmlList = document.getElementsByTag("table")[2]
|
||||||
|
.getElementsByTag("td")[2]
|
||||||
|
.getElementsByTag("a")
|
||||||
|
|
||||||
|
val list = ArrayList<Circular>()
|
||||||
|
|
||||||
|
for (i in 0 until htmlList.size) {
|
||||||
|
list.add(
|
||||||
|
porporatoServer.generateFromString(
|
||||||
|
htmlList[i].text(),
|
||||||
|
htmlList[i].attr("href"),
|
||||||
|
i.toLong()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package net.underdesk.circolapp.shared
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
|
||||||
|
expect object PlatformDispatcher {
|
||||||
|
val IO: CoroutineDispatcher
|
||||||
|
}
|
||||||
@@ -16,14 +16,8 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.underdesk.circolapp.data
|
package net.underdesk.circolapp.shared.data
|
||||||
|
|
||||||
import android.os.Parcelable
|
|
||||||
import androidx.room.Entity
|
|
||||||
import kotlinx.android.parcel.Parcelize
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
@Entity(tableName = "circulars", primaryKeys = ["id", "school"])
|
|
||||||
data class Circular(
|
data class Circular(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val school: Int,
|
val school: Int,
|
||||||
@@ -34,4 +28,4 @@ data class Circular(
|
|||||||
var reminder: Boolean = false,
|
var reminder: Boolean = false,
|
||||||
val attachmentsNames: MutableList<String> = mutableListOf(),
|
val attachmentsNames: MutableList<String> = mutableListOf(),
|
||||||
val attachmentsUrls: MutableList<String> = mutableListOf()
|
val attachmentsUrls: MutableList<String> = mutableListOf()
|
||||||
) : Parcelable
|
)
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package net.underdesk.circolapp.shared.data
|
||||||
|
|
||||||
|
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
||||||
|
import com.squareup.sqldelight.runtime.coroutines.mapToList
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import net.underdesk.circolapp.shared.PlatformDispatcher
|
||||||
|
import net.underdesk.circolapp.shared.utils.SqlUtils.joinToString
|
||||||
|
import net.underdesk.circolapp.shared.utils.SqlUtils.toBoolean
|
||||||
|
import net.underdesk.circolapp.shared.utils.SqlUtils.toList
|
||||||
|
import net.underdesk.circolapp.shared.utils.SqlUtils.toLong
|
||||||
|
|
||||||
|
class CircularDao(
|
||||||
|
database: AppDatabase
|
||||||
|
) {
|
||||||
|
private val appDatabaseQueries = database.appDatabaseQueries
|
||||||
|
|
||||||
|
private val circularMapper =
|
||||||
|
{ id: Long, school: Long, name: String, url: String, date: String, favourite: Long, reminder: Long, attachmentsNames: String, attachmentsUrls: String ->
|
||||||
|
Circular(
|
||||||
|
id,
|
||||||
|
school.toInt(),
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
date,
|
||||||
|
favourite.toBoolean(),
|
||||||
|
reminder.toBoolean(),
|
||||||
|
attachmentsNames.toList(),
|
||||||
|
attachmentsUrls.toList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun insertAll(circulars: List<Circular>) = withContext(PlatformDispatcher.IO) {
|
||||||
|
circulars.forEach {
|
||||||
|
appDatabaseQueries.insertCircular(
|
||||||
|
it.id,
|
||||||
|
it.school.toLong(),
|
||||||
|
it.name,
|
||||||
|
it.url,
|
||||||
|
it.date,
|
||||||
|
it.favourite.toLong(),
|
||||||
|
it.reminder.toLong(),
|
||||||
|
it.attachmentsNames.joinToString(),
|
||||||
|
it.attachmentsUrls.joinToString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun update(id: Long, school: Int, favourite: Boolean, reminder: Boolean) =
|
||||||
|
withContext(PlatformDispatcher.IO) {
|
||||||
|
appDatabaseQueries.updateCircular(
|
||||||
|
favourite.toLong(),
|
||||||
|
reminder.toLong(),
|
||||||
|
id,
|
||||||
|
school.toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteAll() = withContext(PlatformDispatcher.IO) {
|
||||||
|
appDatabaseQueries.deleteAllCirculars()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCircular(id: Long, school: Int) = appDatabaseQueries.getCircular(id, school.toLong(), circularMapper).executeAsOne()
|
||||||
|
|
||||||
|
fun getCirculars(school: Int) =
|
||||||
|
appDatabaseQueries.getCirculars(school.toLong(), circularMapper).executeAsList()
|
||||||
|
|
||||||
|
fun getFlowCirculars(school: Int) =
|
||||||
|
appDatabaseQueries.getCirculars(school.toLong(), circularMapper).asFlow().mapToList()
|
||||||
|
|
||||||
|
fun searchCirculars(query: String, school: Int) =
|
||||||
|
appDatabaseQueries.searchCirculars(school.toLong(), query, circularMapper).asFlow()
|
||||||
|
.mapToList()
|
||||||
|
|
||||||
|
fun getFavourites(school: Int) =
|
||||||
|
appDatabaseQueries.getFavourites(school.toLong(), circularMapper).asFlow().mapToList()
|
||||||
|
|
||||||
|
fun searchFavourites(query: String, school: Int) =
|
||||||
|
appDatabaseQueries.searchFavourites(school.toLong(), query, circularMapper).asFlow()
|
||||||
|
.mapToList()
|
||||||
|
|
||||||
|
fun getReminders(school: Int) =
|
||||||
|
appDatabaseQueries.getReminders(school.toLong(), circularMapper).asFlow().mapToList()
|
||||||
|
|
||||||
|
fun searchReminders(query: String, school: Int) =
|
||||||
|
appDatabaseQueries.searchReminders(school.toLong(), query, circularMapper).asFlow()
|
||||||
|
.mapToList()
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package net.underdesk.circolapp.shared.data
|
||||||
|
|
||||||
|
import net.underdesk.circolapp.shared.server.ServerAPI
|
||||||
|
|
||||||
|
class CircularRepository(
|
||||||
|
val circularDao: CircularDao,
|
||||||
|
private val serverAPI: ServerAPI
|
||||||
|
) {
|
||||||
|
suspend fun updateCirculars(returnNewCirculars: Boolean = true): Pair<List<Circular>, Boolean> {
|
||||||
|
var onlyNewCirculars = listOf<Circular>()
|
||||||
|
|
||||||
|
val result = serverAPI.getCircularsFromServer()
|
||||||
|
if (result.second == ServerAPI.Companion.Result.ERROR)
|
||||||
|
return Pair(emptyList(), false)
|
||||||
|
|
||||||
|
val oldCirculars = circularDao.getCirculars(serverAPI.serverID())
|
||||||
|
val newCirculars = result.first
|
||||||
|
|
||||||
|
if (newCirculars.size != oldCirculars.size) {
|
||||||
|
if (newCirculars.size < oldCirculars.size) {
|
||||||
|
circularDao.deleteAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnNewCirculars) {
|
||||||
|
val oldCircularsSize =
|
||||||
|
if (newCirculars.size < oldCirculars.size) 0 else oldCirculars.size
|
||||||
|
|
||||||
|
val circularCount = newCirculars.size - oldCircularsSize
|
||||||
|
onlyNewCirculars = newCirculars.subList(0, circularCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
circularDao.insertAll(newCirculars)
|
||||||
|
}
|
||||||
|
return Pair(onlyNewCirculars, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package net.underdesk.circolapp.shared.data
|
||||||
|
|
||||||
|
import com.squareup.sqldelight.db.SqlDriver
|
||||||
|
|
||||||
|
expect class DatabaseDriverFactory {
|
||||||
|
fun createDriver(): SqlDriver
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package net.underdesk.circolapp.shared.server
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
|
||||||
|
expect class KtorFactory() {
|
||||||
|
fun createClient(): HttpClient
|
||||||
|
}
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.underdesk.circolapp.server
|
package net.underdesk.circolapp.shared.server
|
||||||
|
|
||||||
import net.underdesk.circolapp.data.Circular
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
|
|
||||||
abstract class Server {
|
abstract class Server {
|
||||||
abstract val serverID: Int
|
abstract val serverID: Int
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Circolapp
|
||||||
|
* Copyright (C) 2019-2020 Matteo Schiff
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.underdesk.circolapp.shared.server
|
||||||
|
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import net.underdesk.circolapp.shared.PlatformDispatcher
|
||||||
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
|
import net.underdesk.circolapp.shared.server.curie.CurieServer
|
||||||
|
import net.underdesk.circolapp.shared.server.porporato.PorporatoServer
|
||||||
|
|
||||||
|
class ServerAPI(
|
||||||
|
private var server: Server
|
||||||
|
) {
|
||||||
|
fun serverID(): Int = server.serverID
|
||||||
|
|
||||||
|
suspend fun getCircularsFromServer(): Pair<List<Circular>, Result> = withContext(PlatformDispatcher.IO) {
|
||||||
|
val newCircularsAvailable = server.newCircularsAvailable()
|
||||||
|
|
||||||
|
if (newCircularsAvailable.second == Result.ERROR)
|
||||||
|
return@withContext Pair(emptyList(), Result.ERROR)
|
||||||
|
|
||||||
|
if (!newCircularsAvailable.first)
|
||||||
|
return@withContext Pair(emptyList(), Result.SUCCESS)
|
||||||
|
|
||||||
|
server.getCircularsFromServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeServer(server: Server) {
|
||||||
|
this.server = server
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
enum class Servers {
|
||||||
|
CURIE, PORPORATO
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Result {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createServer(server: Servers) = when (server) {
|
||||||
|
Servers.CURIE -> CurieServer()
|
||||||
|
Servers.PORPORATO -> PorporatoServer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package net.underdesk.circolapp.shared.server.curie
|
||||||
|
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.utils.io.errors.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
|
import net.underdesk.circolapp.shared.server.KtorFactory
|
||||||
|
import net.underdesk.circolapp.shared.server.Server
|
||||||
|
import net.underdesk.circolapp.shared.server.ServerAPI
|
||||||
|
import net.underdesk.circolapp.shared.server.pojo.Response
|
||||||
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
|
||||||
|
class CurieServer : Server() {
|
||||||
|
private val client = KtorFactory().createClient()
|
||||||
|
|
||||||
|
override val serverID = ServerAPI.getServerId(ServerAPI.Companion.Servers.CURIE)
|
||||||
|
|
||||||
|
override suspend fun getCircularsFromServer(): Pair<List<Circular>, ServerAPI.Companion.Result> {
|
||||||
|
return try {
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
val json = retrieveDataFromServer()
|
||||||
|
val list = SpecificCurieServer(this@CurieServer).parseHtml(json.content.rendered)
|
||||||
|
Pair(list, ServerAPI.Companion.Result.SUCCESS)
|
||||||
|
}
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
Pair(emptyList(), ServerAPI.Companion.Result.ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun newCircularsAvailable(): Pair<Boolean, ServerAPI.Companion.Result> {
|
||||||
|
return Pair(true, ServerAPI.Companion.Result.SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
@Throws(IOException::class, CancellationException::class)
|
||||||
|
private suspend fun retrieveDataFromServer(): Response {
|
||||||
|
return client.get(ENDPOINT_URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateFromString(string: String, url: String): Circular {
|
||||||
|
val idRegex =
|
||||||
|
"""(\d+)""".toRegex()
|
||||||
|
val idMatcher = idRegex.find(string)
|
||||||
|
|
||||||
|
val id = idMatcher?.value?.toLong() ?: -1L
|
||||||
|
|
||||||
|
val dateRegex =
|
||||||
|
"""(\d{2}/\d{2}/\d{4})""".toRegex()
|
||||||
|
val dateMatcher = dateRegex.find(string)
|
||||||
|
|
||||||
|
var title = string.removeSuffix("-signed")
|
||||||
|
|
||||||
|
return if (dateMatcher != null) {
|
||||||
|
title = title.removeRange(0, dateMatcher.range.last + 1)
|
||||||
|
.removePrefix(" ")
|
||||||
|
.removePrefix("_")
|
||||||
|
.removePrefix(" ")
|
||||||
|
|
||||||
|
Circular(id, serverID, title, url, dateMatcher.value)
|
||||||
|
} else {
|
||||||
|
Circular(id, serverID, title, url, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ENDPOINT_URL = "https://www.curiepinerolo.edu.it/wp-json/wp/v2/pages/5958"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect class SpecificCurieServer(curieServer: CurieServer) {
|
||||||
|
fun parseHtml(string: String): List<Circular>
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package net.underdesk.circolapp.shared.server.pojo
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Response(
|
||||||
|
val content: Content
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Content(
|
||||||
|
val rendered: String
|
||||||
|
)
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
package net.underdesk.circolapp.server.porporato
|
package net.underdesk.circolapp.shared.server.porporato
|
||||||
|
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.client.statement.*
|
||||||
|
import io.ktor.utils.io.charsets.*
|
||||||
|
import io.ktor.utils.io.errors.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.underdesk.circolapp.data.Circular
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
import net.underdesk.circolapp.server.Server
|
import net.underdesk.circolapp.shared.server.KtorFactory
|
||||||
import net.underdesk.circolapp.server.ServerAPI
|
import net.underdesk.circolapp.shared.server.Server
|
||||||
import okhttp3.OkHttpClient
|
import net.underdesk.circolapp.shared.server.ServerAPI
|
||||||
import okhttp3.Request
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import java.io.IOException
|
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
class PorporatoServer : Server() {
|
class PorporatoServer : Server() {
|
||||||
private val client = OkHttpClient()
|
private val client = KtorFactory().createClient()
|
||||||
|
|
||||||
private val baseUrl = "https://www.liceoporporato.edu.it/ARCHIVIO/PR/VP/"
|
private val baseUrl = "https://www.liceoporporato.edu.it/ARCHIVIO/PR/VP/"
|
||||||
private val endpointUrls = listOf(
|
private val endpointUrls = listOf(
|
||||||
@@ -52,27 +53,13 @@ class PorporatoServer : Server() {
|
|||||||
return Pair(true, ServerAPI.Companion.Result.SUCCESS)
|
return Pair(true, ServerAPI.Companion.Result.SUCCESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
@Throws(IOException::class, CancellationException::class)
|
||||||
private suspend fun parsePage(url: String): List<Circular> {
|
private suspend fun parsePage(url: String): List<Circular> {
|
||||||
val response = retrieveDataFromServer(url)
|
val response = retrieveDataFromServer(url)
|
||||||
|
|
||||||
return withContext(Dispatchers.Default) {
|
return withContext(Dispatchers.Default) {
|
||||||
val document = Jsoup.parseBodyFragment(response)
|
val list = SpecificPorporatoServer(this@PorporatoServer).parseHtml(response).toMutableList()
|
||||||
val htmlList = document.getElementsByTag("table")[2]
|
|
||||||
.getElementsByTag("td")[2]
|
|
||||||
.getElementsByTag("a")
|
|
||||||
|
|
||||||
val list = ArrayList<Circular>()
|
|
||||||
|
|
||||||
for (i in 0 until htmlList.size) {
|
|
||||||
list.add(
|
|
||||||
generateFromString(
|
|
||||||
htmlList[i].text(),
|
|
||||||
htmlList[i].attr("href"),
|
|
||||||
i.toLong()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify and group all attachments
|
// Identify and group all attachments
|
||||||
list.removeAll { attachment ->
|
list.removeAll { attachment ->
|
||||||
@@ -109,52 +96,45 @@ class PorporatoServer : Server() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
@Throws(IOException::class, CancellationException::class)
|
||||||
private suspend fun retrieveDataFromServer(url: String): String {
|
private suspend fun retrieveDataFromServer(url: String): String {
|
||||||
val request = Request.Builder()
|
return client.request<HttpResponse>(url).readText(Charsets.ISO_8859_1)
|
||||||
.url(url)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
val response = client.newCall(request).execute()
|
|
||||||
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
throw IOException("HTTP error code: ${response.code})")
|
|
||||||
}
|
|
||||||
|
|
||||||
response.body!!.string()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateFromString(string: String, path: String, index: Long): Circular {
|
fun generateFromString(string: String, path: String, index: Long): Circular {
|
||||||
val fullUrl = baseUrl + path
|
val fullUrl = baseUrl + path
|
||||||
var title = string
|
var title = string
|
||||||
|
|
||||||
val idRegex =
|
val idRegex =
|
||||||
"""(\d+)"""
|
"""(\d+)""".toRegex()
|
||||||
val matcherId = Pattern.compile(idRegex).matcher(string)
|
val idMatcher = idRegex.find(string)
|
||||||
val id = if (!string.startsWith("Avviso") && matcherId.find()) {
|
val id = if (!string.startsWith("Avviso") && idMatcher != null) {
|
||||||
title = title.removeRange(matcherId.start(), matcherId.end())
|
title = title.removeRange(idMatcher.range)
|
||||||
.removePrefix(" ")
|
.removePrefix(" ")
|
||||||
.removePrefix("-")
|
.removePrefix("-")
|
||||||
.removePrefix(" ")
|
.removePrefix(" ")
|
||||||
|
|
||||||
matcherId.group(1)?.toLong() ?: -index
|
idMatcher.value.toLong()
|
||||||
} else {
|
} else {
|
||||||
-index
|
-index
|
||||||
}
|
}
|
||||||
|
|
||||||
val dateRegex =
|
val dateRegex =
|
||||||
"""(\d{2}-\d{2}-\d{4})"""
|
"""(\d{2}-\d{2}-\d{4})""".toRegex()
|
||||||
val matcherDate = Pattern.compile(dateRegex).matcher(title)
|
val dateMatcher = dateRegex.find(title)
|
||||||
|
|
||||||
return if (matcherDate.find()) {
|
return if (dateMatcher != null) {
|
||||||
title = title.removeRange(matcherDate.start(), matcherDate.end())
|
title = title.removeRange(dateMatcher.range)
|
||||||
.removeSuffix(" (pubb.: )")
|
.removeSuffix(" (pubb.: )")
|
||||||
|
|
||||||
Circular(id, serverID, title, fullUrl, matcherDate.group(1)?.replace("-", "/") ?: "")
|
Circular(id, serverID, title, fullUrl, dateMatcher.value.replace("-", "/"))
|
||||||
} else {
|
} else {
|
||||||
Circular(id, serverID, title, fullUrl, "")
|
Circular(id, serverID, title, fullUrl, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect class SpecificPorporatoServer(porporatoServer: PorporatoServer) {
|
||||||
|
fun parseHtml(string: String): List<Circular>
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package net.underdesk.circolapp.shared.utils
|
||||||
|
|
||||||
|
object SqlUtils {
|
||||||
|
fun Boolean.toLong() = if (this) 1L else 0L
|
||||||
|
|
||||||
|
fun Long.toBoolean() = this == 1L
|
||||||
|
|
||||||
|
fun String?.toList(): MutableList<String> {
|
||||||
|
val list: MutableList<String> = mutableListOf()
|
||||||
|
|
||||||
|
if (this != null) {
|
||||||
|
for (attachment in this.split("˜")) {
|
||||||
|
list.add(attachment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.dropLast(1).toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<String>.joinToString(): String {
|
||||||
|
var string = ""
|
||||||
|
|
||||||
|
for (attachment in this) {
|
||||||
|
string += "$attachment˜"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
CREATE TABLE Circulars (
|
||||||
|
id INTEGER NOT NULL,
|
||||||
|
school INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
date TEXT NOT NULL,
|
||||||
|
favourite INTEGER NOT NULL DEFAULT 0,
|
||||||
|
reminder INTEGER NOT NULL DEFAULT 0,
|
||||||
|
attachmentsNames TEXT NOT NULL,
|
||||||
|
attachmentsUrls TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (id, school)
|
||||||
|
);
|
||||||
|
|
||||||
|
insertCircular:
|
||||||
|
INSERT OR IGNORE INTO Circulars(id, school, name, url, date, favourite, reminder, attachmentsNames, attachmentsUrls)
|
||||||
|
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||||
|
|
||||||
|
updateCircular:
|
||||||
|
UPDATE Circulars
|
||||||
|
SET favourite = ?, reminder = ?
|
||||||
|
WHERE id = ? AND school = ?;
|
||||||
|
|
||||||
|
deleteAllCirculars:
|
||||||
|
DELETE FROM Circulars;
|
||||||
|
|
||||||
|
getCirculars:
|
||||||
|
SELECT * FROM Circulars
|
||||||
|
WHERE school IS ?
|
||||||
|
ORDER BY id DESC;
|
||||||
|
|
||||||
|
getCircular:
|
||||||
|
SELECT * FROM Circulars
|
||||||
|
WHERE id IS ? AND school = ?
|
||||||
|
ORDER BY id DESC;
|
||||||
|
|
||||||
|
searchCirculars:
|
||||||
|
SELECT * FROM Circulars
|
||||||
|
WHERE school IS ? AND name LIKE ?
|
||||||
|
ORDER BY id DESC;
|
||||||
|
|
||||||
|
getFavourites:
|
||||||
|
SELECT * FROM Circulars
|
||||||
|
WHERE school IS ? AND favourite
|
||||||
|
ORDER BY id DESC;
|
||||||
|
|
||||||
|
searchFavourites:
|
||||||
|
SELECT * FROM Circulars
|
||||||
|
WHERE school IS ? AND favourite AND name LIKE ?
|
||||||
|
ORDER BY id DESC;
|
||||||
|
|
||||||
|
getReminders:
|
||||||
|
SELECT * FROM Circulars
|
||||||
|
WHERE school IS ? AND reminder
|
||||||
|
ORDER BY id DESC;
|
||||||
|
|
||||||
|
searchReminders:
|
||||||
|
SELECT * FROM Circulars
|
||||||
|
WHERE school IS ? AND reminder AND name LIKE ?
|
||||||
|
ORDER BY id DESC;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package net.underdesk.circolapp.shared
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
actual object PlatformDispatcher {
|
||||||
|
actual val IO = Dispatchers.Default
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package net.underdesk.circolapp.shared.data
|
||||||
|
|
||||||
|
import com.squareup.sqldelight.db.SqlDriver
|
||||||
|
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
|
||||||
|
|
||||||
|
actual class DatabaseDriverFactory {
|
||||||
|
actual fun createDriver(): SqlDriver {
|
||||||
|
return NativeSqliteDriver(AppDatabase.Schema, "circolapp.db")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package net.underdesk.circolapp.shared.server
|
||||||
|
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.engine.ios.*
|
||||||
|
import io.ktor.client.features.json.*
|
||||||
|
import io.ktor.client.features.json.serializer.*
|
||||||
|
|
||||||
|
actual class KtorFactory actual constructor() {
|
||||||
|
actual fun createClient() = HttpClient(Ios) {
|
||||||
|
install(JsonFeature) {
|
||||||
|
serializer = KotlinxSerializer(
|
||||||
|
kotlinx.serialization.json.Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package net.underdesk.circolapp.shared.server.curie
|
||||||
|
|
||||||
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
|
|
||||||
|
actual class SpecificCurieServer actual constructor(val curieServer: CurieServer) {
|
||||||
|
actual fun parseHtml(string: String): List<Circular> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package net.underdesk.circolapp.shared.server.porporato
|
||||||
|
|
||||||
|
import net.underdesk.circolapp.shared.data.Circular
|
||||||
|
|
||||||
|
actual class SpecificPorporatoServer actual constructor(porporatoServer: PorporatoServer) {
|
||||||
|
actual fun parseHtml(string: String): List<Circular> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user