Automatically resolve circular's direct URL

This commit is contained in:
2021-02-01 23:32:24 +01:00
committed by Matte23
parent 78bfec6919
commit fdd817c827
22 changed files with 338 additions and 44 deletions

View File

@@ -80,6 +80,7 @@ dependencies {
// Misc // Misc
implementation(Dependencies.Misc.appIntro) implementation(Dependencies.Misc.appIntro)
implementation(Dependencies.Misc.materialSpinner) implementation(Dependencies.Misc.materialSpinner)
implementation(Dependencies.Misc.materialProgressBar)
// Testing // Testing
testImplementation(Dependencies.Testing.junit) testImplementation(Dependencies.Testing.junit)

View File

@@ -23,19 +23,27 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.TextView import android.widget.TextView
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import net.underdesk.circolapp.MainActivity import net.underdesk.circolapp.MainActivity
import net.underdesk.circolapp.data.AndroidCircularRepository
import net.underdesk.circolapp.databinding.ItemAttachmentBinding import net.underdesk.circolapp.databinding.ItemAttachmentBinding
import net.underdesk.circolapp.shared.data.Circular
import net.underdesk.circolapp.shared.data.CircularRepository
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 AttachmentAdapter( class AttachmentAdapter(
private val attachmentsNames: List<String>, private val circular: Circular,
private val attachmentsUrls: List<String>,
private val mainActivity: MainActivity, private val mainActivity: MainActivity,
private val adapterScope: CoroutineScope,
private val circularHolder: CircularLetterAdapter.CircularLetterViewHolder
) : ) :
RecyclerView.Adapter<AttachmentAdapter.AttachmentViewHolder>() { RecyclerView.Adapter<AttachmentAdapter.AttachmentViewHolder>() {
private val adapterCallback: CircularLetterAdapter.AdapterCallback = mainActivity private val adapterCallback: CircularLetterAdapter.AdapterCallback = mainActivity
private lateinit var circularRepository: CircularRepository
private lateinit var context: Context private lateinit var context: Context
inner class AttachmentViewHolder(binding: ItemAttachmentBinding) : RecyclerView.ViewHolder(binding.root) { inner class AttachmentViewHolder(binding: ItemAttachmentBinding) : RecyclerView.ViewHolder(binding.root) {
@@ -48,26 +56,74 @@ class AttachmentAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AttachmentViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AttachmentViewHolder {
val binding = ItemAttachmentBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding = ItemAttachmentBinding.inflate(LayoutInflater.from(parent.context), parent, false)
context = parent.context context = parent.context
circularRepository = AndroidCircularRepository.getInstance(context)
return AttachmentViewHolder(binding) return AttachmentViewHolder(binding)
} }
override fun onBindViewHolder(holder: AttachmentViewHolder, position: Int) { override fun onBindViewHolder(holder: AttachmentViewHolder, position: Int) {
holder.title.text = attachmentsNames[position] holder.title.text = circular.attachmentsNames[position]
val observer = Observer<Boolean> {
if (it) {
holder.viewButton.isEnabled = false
holder.downloadButton.isEnabled = false
holder.shareButton.isEnabled = false
} else {
holder.viewButton.isEnabled = true
holder.downloadButton.isEnabled = true
holder.shareButton.isEnabled = true
}
}
circularHolder.loading.observe(mainActivity, observer)
circularHolder.observer.add(observer)
holder.viewButton.setOnClickListener { holder.viewButton.setOnClickListener {
FileUtils.viewFile(attachmentsUrls[position], context, mainActivity.customTabsSession) runWhenUrlIsAvailable(position) { url ->
FileUtils.viewFile(url, context, mainActivity.customTabsSession)
}
} }
holder.shareButton.setOnClickListener { holder.shareButton.setOnClickListener {
FileUtils.shareFile(attachmentsUrls[position], context) runWhenUrlIsAvailable(position) { url -> FileUtils.shareFile(url, context) }
} }
holder.downloadButton.setOnClickListener { holder.downloadButton.setOnClickListener {
val file = DownloadableFile(attachmentsNames[position], attachmentsUrls[position]) runWhenUrlIsAvailable(position) { url ->
val file = DownloadableFile(circular.attachmentsNames[position], url)
FileUtils.downloadFile(file, adapterCallback, context) FileUtils.downloadFile(file, adapterCallback, context)
} }
} }
}
override fun getItemCount() = attachmentsNames.size
private fun runWhenUrlIsAvailable(position: Int, code: (url: String) -> Unit) {
if (circular.realAttachmentsUrls.size != circular.attachmentsUrls.size || circular.realAttachmentsUrls[position] == "") {
circularHolder.loading.postValue(true)
adapterScope.launch {
val realUrls = circularRepository.getRealUrlForAttachment(
position,
circular.attachmentsUrls,
circular.realAttachmentsUrls,
circular.id,
circular.school
)
circularHolder.loading.postValue(false)
if (circular.realAttachmentsUrls.size != circular.attachmentsUrls.size) {
circular.realAttachmentsUrls.clear()
repeat(circular.attachmentsUrls.size) { circular.realAttachmentsUrls.add("") }
}
circular.realAttachmentsUrls[position] = realUrls[position]
code(realUrls[position])
}
return
}
code(circular.realAttachmentsUrls[position])
}
override fun getItemCount() = circular.attachmentsNames.size
} }

View File

@@ -26,10 +26,13 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat.getDrawable import androidx.core.content.ContextCompat.getDrawable
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -37,10 +40,12 @@ import kotlinx.coroutines.launch
import net.underdesk.circolapp.AlarmBroadcastReceiver import net.underdesk.circolapp.AlarmBroadcastReceiver
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.AndroidCircularRepository
import net.underdesk.circolapp.data.AndroidDatabase import net.underdesk.circolapp.data.AndroidDatabase
import net.underdesk.circolapp.databinding.ItemCircularBinding import net.underdesk.circolapp.databinding.ItemCircularBinding
import net.underdesk.circolapp.fragments.NewReminderFragment import net.underdesk.circolapp.fragments.NewReminderFragment
import net.underdesk.circolapp.shared.data.Circular import net.underdesk.circolapp.shared.data.Circular
import net.underdesk.circolapp.shared.data.CircularRepository
import net.underdesk.circolapp.utils.DownloadableFile import net.underdesk.circolapp.utils.DownloadableFile
import net.underdesk.circolapp.utils.FileUtils import net.underdesk.circolapp.utils.FileUtils
@@ -51,6 +56,7 @@ class CircularLetterAdapter(
) : ) :
RecyclerView.Adapter<CircularLetterAdapter.CircularLetterViewHolder>() { RecyclerView.Adapter<CircularLetterAdapter.CircularLetterViewHolder>() {
private lateinit var context: Context private lateinit var context: Context
private lateinit var circularRepository: CircularRepository
private val adapterCallback: AdapterCallback = mainActivity private val adapterCallback: AdapterCallback = mainActivity
private var collapsedItems = -1 private var collapsedItems = -1
@@ -60,6 +66,7 @@ class CircularLetterAdapter(
inner class CircularLetterViewHolder(binding: ItemCircularBinding) : RecyclerView.ViewHolder(binding.root) { inner class CircularLetterViewHolder(binding: ItemCircularBinding) : RecyclerView.ViewHolder(binding.root) {
var card: CardView = binding.circularCard var card: CardView = binding.circularCard
var progressBar: ProgressBar = binding.circularProgressBar
var title: TextView = binding.circularTitleTextview var title: TextView = binding.circularTitleTextview
var number: TextView = binding.circularNumberTextview var number: TextView = binding.circularNumberTextview
var date: TextView = binding.circularDateTextview var date: TextView = binding.circularDateTextview
@@ -71,6 +78,9 @@ class CircularLetterAdapter(
var reminderButton: ImageButton = binding.circularReminderButton var reminderButton: ImageButton = binding.circularReminderButton
var attachmentsList: RecyclerView = binding.circularsAttachmentsList var attachmentsList: RecyclerView = binding.circularsAttachmentsList
val loading = MutableLiveData(false)
var observer: MutableList<Observer<in Boolean>> = mutableListOf()
init { init {
attachmentsList.layoutManager = LinearLayoutManager(context) attachmentsList.layoutManager = LinearLayoutManager(context)
} }
@@ -79,6 +89,7 @@ class CircularLetterAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CircularLetterViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CircularLetterViewHolder {
val binding = ItemCircularBinding.inflate(LayoutInflater.from(parent.context), parent, false) val binding = ItemCircularBinding.inflate(LayoutInflater.from(parent.context), parent, false)
context = parent.context context = parent.context
circularRepository = AndroidCircularRepository.getInstance(context)
return CircularLetterViewHolder(binding) return CircularLetterViewHolder(binding)
} }
@@ -88,6 +99,23 @@ class CircularLetterAdapter(
holder.title.text = circulars[position].name holder.title.text = circulars[position].name
holder.date.text = circulars[position].date holder.date.text = circulars[position].date
val observer = Observer<Boolean> {
if (it) {
holder.progressBar.visibility = View.VISIBLE
holder.viewButton.isEnabled = false
holder.downloadButton.isEnabled = false
holder.shareButton.isEnabled = false
} else {
holder.progressBar.visibility = View.GONE
holder.viewButton.isEnabled = true
holder.downloadButton.isEnabled = true
holder.shareButton.isEnabled = true
}
}
holder.loading.observe(mainActivity, observer)
holder.observer.add(observer)
if (circulars[position].read) { if (circulars[position].read) {
holder.number.typeface = Typeface.DEFAULT holder.number.typeface = Typeface.DEFAULT
holder.date.typeface = Typeface.DEFAULT holder.date.typeface = Typeface.DEFAULT
@@ -139,7 +167,7 @@ class CircularLetterAdapter(
holder.attachmentsList.adapter = null holder.attachmentsList.adapter = null
} else { } else {
FileUtils.preloadFiles( FileUtils.preloadFiles(
circulars[position].url, circulars[position].realUrl,
circulars[position].attachmentsUrls, circulars[position].attachmentsUrls,
mainActivity.customTabsSession mainActivity.customTabsSession
) )
@@ -161,9 +189,10 @@ class CircularLetterAdapter(
if (circulars[position].attachmentsNames.isNotEmpty()) { if (circulars[position].attachmentsNames.isNotEmpty()) {
holder.attachmentsList.visibility = View.VISIBLE holder.attachmentsList.visibility = View.VISIBLE
holder.attachmentsList.adapter = AttachmentAdapter( holder.attachmentsList.adapter = AttachmentAdapter(
circulars[position].attachmentsNames, circulars[position],
circulars[position].attachmentsUrls, mainActivity,
mainActivity adapterScope,
holder
) )
} else { } else {
holder.attachmentsList.adapter = null holder.attachmentsList.adapter = null
@@ -181,17 +210,30 @@ class CircularLetterAdapter(
} }
} }
FileUtils.viewFile(circulars[position].url, context, mainActivity.customTabsSession) runWhenUrlIsAvailable(holder, circulars[position]) { url ->
FileUtils.viewFile(
url,
context,
mainActivity.customTabsSession
)
}
} }
holder.shareButton.setOnClickListener { holder.shareButton.setOnClickListener {
FileUtils.shareFile(circulars[position].url, context) runWhenUrlIsAvailable(holder, circulars[position]) { url ->
FileUtils.shareFile(
url,
context
)
}
} }
holder.downloadButton.setOnClickListener { holder.downloadButton.setOnClickListener {
val file = DownloadableFile(circulars[position].name, circulars[position].url) runWhenUrlIsAvailable(holder, circulars[position]) { url ->
val file = DownloadableFile(circulars[position].name, url)
FileUtils.downloadFile(file, adapterCallback, context) FileUtils.downloadFile(file, adapterCallback, context)
} }
}
holder.favouriteButton.setOnClickListener { holder.favouriteButton.setOnClickListener {
adapterScope.launch { adapterScope.launch {
@@ -254,6 +296,32 @@ class CircularLetterAdapter(
} }
} }
override fun onViewRecycled(holder: CircularLetterViewHolder) {
holder.observer.forEach { holder.loading.removeObserver(it) }
holder.observer.clear()
super.onViewRecycled(holder)
}
private fun runWhenUrlIsAvailable(
holder: CircularLetterViewHolder,
circular: Circular,
code: (url: String) -> Unit
) {
if (circular.realUrl == null) {
holder.loading.postValue(true)
adapterScope.launch {
val realUrl =
circularRepository.getRealUrl(circular.url, circular.id, circular.school)
holder.loading.postValue(false)
code(realUrl)
}
return
}
code(circular.realUrl!!)
}
fun changeDataSet(newCirculars: List<Circular>) { fun changeDataSet(newCirculars: List<Circular>) {
if (circulars.size != newCirculars.size) if (circulars.size != newCirculars.size)
collapsedItems = -1 collapsedItems = -1

View File

@@ -33,7 +33,7 @@ class CircolappFirebaseMessagingService : FirebaseMessagingService() {
val url = remoteMessage.data["url"] ?: "" val url = remoteMessage.data["url"] ?: ""
val circular = Circular( val circular = Circular(
id, -1, name, url, "", id, -1, name, url, null, "",
favourite = false, favourite = false,
reminder = false, reminder = false,
attachmentsNames = mutableListOf(), attachmentsNames = mutableListOf(),

View File

@@ -78,7 +78,10 @@ object FileUtils {
} }
} }
fun preloadFiles(url: String, otherUrls: List<String>, session: CustomTabsSession?) { fun preloadFiles(url: String?, otherUrls: List<String>, session: CustomTabsSession?) {
if (url == null)
return
if (!url.endsWith(".pdf")) { if (!url.endsWith(".pdf")) {
val bundles = arrayListOf<Bundle>() val bundles = arrayListOf<Bundle>()

View File

@@ -14,6 +14,19 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/circular_progress_bar"
android:layout_width="0dp"
android:layout_height="4dp"
android:indeterminate="true"
android:visibility="gone"
app:mpb_progressStyle="horizontal"
app:mpb_useIntrinsicPadding="false"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/circular_number_textview" android:id="@+id/circular_number_textview"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -88,6 +88,7 @@ object Dependencies {
const val jsoup = "org.jsoup:jsoup:1.13.1" const val jsoup = "org.jsoup:jsoup:1.13.1"
const val appIntro = "com.github.AppIntro:AppIntro:6.0.0" const val appIntro = "com.github.AppIntro:AppIntro:6.0.0"
const val materialSpinner = "com.github.tiper:MaterialSpinner:1.4.2" const val materialSpinner = "com.github.tiper:MaterialSpinner:1.4.2"
const val materialProgressBar = "me.zhanghai.android.materialprogressbar:library:1.6.1"
} }
object Testing { object Testing {

View File

@@ -25,5 +25,18 @@ import com.squareup.sqldelight.db.SqlDriver
actual class DatabaseDriverFactory(private val context: Context) { actual class DatabaseDriverFactory(private val context: Context) {
actual fun createDriver(): SqlDriver { actual fun createDriver(): SqlDriver {
return AndroidSqliteDriver(AppDatabase.Schema, context, "circolapp.db") return AndroidSqliteDriver(AppDatabase.Schema, context, "circolapp.db")
.also {
var currentVer = DatabaseFactory.getVersion(it)
val schemaVer: Int = AppDatabase.Schema.version
if (currentVer == 0) {
currentVer = 1
}
if (schemaVer > currentVer) {
AppDatabase.Schema.migrate(it, currentVer, schemaVer)
DatabaseFactory.setVersion(it, schemaVer)
}
}
} }
} }

View File

@@ -29,14 +29,26 @@ actual class SpecificCurieServer actual constructor(private val curieServer: Cur
val list = ArrayList<Circular>() val list = ArrayList<Circular>()
htmlList.forEach { element -> htmlList.forEach { element ->
val url = element.attr("href")
if (element.parents().size == 6) { if (element.parents().size == 6) {
list.last().attachmentsNames.add(element.text()) list.last().attachmentsNames.add(element.text())
list.last().attachmentsUrls.add(element.attr("href")) list.last().attachmentsUrls.add(url)
if (url.endsWith(".pdf")) {
list.last().realAttachmentsUrls.add(url)
} else {
list.last().realAttachmentsUrls.add("")
}
} else if (element.parents().size == 4) { } else if (element.parents().size == 4) {
list.add(curieServer.generateFromString(element.text(), element.attr("href"))) list.add(curieServer.generateFromString(element.text(), url))
} }
} }
return list return list
} }
actual fun parseFileUrl(string: String): String {
val document = Jsoup.parseBodyFragment(string)
return document.getElementsByClass("mtli_attachment")[0].attr("href")
}
} }

View File

@@ -23,10 +23,12 @@ data class Circular(
val school: Int, val school: Int,
val name: String, val name: String,
val url: String, val url: String,
val realUrl: String?,
val date: String, val date: String,
var favourite: Boolean = false, var favourite: Boolean = false,
var reminder: Boolean = false, var reminder: Boolean = false,
var read: Boolean = false, var read: Boolean = false,
val attachmentsNames: MutableList<String> = mutableListOf(), val attachmentsNames: MutableList<String> = mutableListOf(),
val attachmentsUrls: MutableList<String> = mutableListOf() val attachmentsUrls: MutableList<String> = mutableListOf(),
val realAttachmentsUrls: MutableList<String> = mutableListOf()
) )

View File

@@ -34,18 +34,20 @@ class CircularDao(
private val appDatabaseQueries = database.appDatabaseQueries private val appDatabaseQueries = database.appDatabaseQueries
private val circularMapper = private val circularMapper =
{ id: Long, school: Long, name: String, url: String, date: String, favourite: Long, reminder: Long, read: Long, attachmentsNames: String, attachmentsUrls: String -> { id: Long, school: Long, name: String, url: String, date: String, favourite: Long, reminder: Long, read: Long, attachmentsNames: String, attachmentsUrls: String, realAttachmentsUrls: String, realUrl: String? ->
Circular( Circular(
id, id,
school.toInt(), school.toInt(),
name, name,
url, url,
realUrl,
date, date,
favourite.toBoolean(), favourite.toBoolean(),
reminder.toBoolean(), reminder.toBoolean(),
read.toBoolean(), read.toBoolean(),
attachmentsNames.toList(), attachmentsNames.toList(),
attachmentsUrls.toList() attachmentsUrls.toList(),
realAttachmentsUrls.toList()
) )
} }
@@ -61,7 +63,9 @@ class CircularDao(
it.reminder.toLong(), it.reminder.toLong(),
it.read.toLong(), it.read.toLong(),
it.attachmentsNames.joinToString(), it.attachmentsNames.joinToString(),
it.attachmentsUrls.joinToString() it.attachmentsUrls.joinToString(),
it.realAttachmentsUrls.joinToString(),
it.realUrl
) )
} }
} }
@@ -76,6 +80,24 @@ class CircularDao(
) )
} }
suspend fun setRealUrl(id: Long, school: Int, url: String?) =
withContext(PlatformDispatcher.IO) {
appDatabaseQueries.setRealUrl(
url,
id,
school.toLong()
)
}
suspend fun setRealAttachmentsUrls(id: Long, school: Int, urls: List<String>) =
withContext(PlatformDispatcher.IO) {
appDatabaseQueries.setRealAttachmentsUrls(
urls.joinToString(),
id,
school.toLong()
)
}
suspend fun markRead(id: Long, school: Int, read: Boolean) = suspend fun markRead(id: Long, school: Int, read: Boolean) =
withContext(PlatformDispatcher.IO) { withContext(PlatformDispatcher.IO) {
appDatabaseQueries.markCircularRead( appDatabaseQueries.markCircularRead(

View File

@@ -57,4 +57,34 @@ class CircularRepository(
} }
return Pair(onlyNewCirculars, errorCode) return Pair(onlyNewCirculars, errorCode)
} }
suspend fun getRealUrl(rawUrl: String, id: Long, school: Int): String {
val result = serverAPI.getRealUrl(rawUrl)
if (result.second != ServerAPI.Companion.Result.SUCCESS)
return rawUrl
circularDao.setRealUrl(id, school, result.first)
return result.first
}
suspend fun getRealUrlForAttachment(
index: Int,
rawUrls: List<String>,
realUrls: List<String>,
id: Long,
school: Int
): List<String> {
val result = serverAPI.getRealUrl(rawUrls[index])
if (result.second != ServerAPI.Companion.Result.SUCCESS)
return realUrls
val newList =
if (realUrls.size != rawUrls.size) MutableList(rawUrls.size) { "" } else realUrls.toMutableList()
newList[index] = result.first
circularDao.setRealAttachmentsUrls(id, school, newList)
return newList
}
} }

View File

@@ -22,4 +22,16 @@ import com.squareup.sqldelight.db.SqlDriver
object DatabaseFactory { object DatabaseFactory {
fun createDatabase(sqlDriver: SqlDriver) = AppDatabase(sqlDriver) fun createDatabase(sqlDriver: SqlDriver) = AppDatabase(sqlDriver)
fun getVersion(driver: SqlDriver): Int {
val sqlCursor = driver.executeQuery(null, "PRAGMA user_version;", 0, null)
if (!sqlCursor.next())
return 0
return sqlCursor.getLong(0)?.toInt() ?: 0
}
fun setVersion(driver: SqlDriver, version: Int) {
driver.execute(null, "PRAGMA user_version = $version;", 0, null)
}
} }

View File

@@ -26,5 +26,6 @@ abstract class Server(
) { ) {
abstract val serverID: Int abstract val serverID: Int
abstract suspend fun getCircularsFromServer(): Pair<List<Circular>, ServerAPI.Companion.Result> abstract suspend fun getCircularsFromServer(): Pair<List<Circular>, ServerAPI.Companion.Result>
abstract suspend fun getRealUrl(rawUrl: String): Pair<String, ServerAPI.Companion.Result>
abstract suspend fun newCircularsAvailable(): Pair<Boolean, ServerAPI.Companion.Result> abstract suspend fun newCircularsAvailable(): Pair<Boolean, ServerAPI.Companion.Result>
} }

View File

@@ -50,6 +50,11 @@ class ServerAPI(serverName: Servers) {
server.getCircularsFromServer() server.getCircularsFromServer()
} }
suspend fun getRealUrl(rawUrl: String): Pair<String, Result> =
withContext(PlatformDispatcher.IO) {
server.getRealUrl(rawUrl)
}
fun changeServer(serverName: Servers) { fun changeServer(serverName: Servers) {
server = createServer(serverName, ktorClient) server = createServer(serverName, ktorClient)
} }

View File

@@ -46,6 +46,23 @@ class CurieServer(ktorClient: HttpClient) : Server(ktorClient) {
} }
} }
override suspend fun getRealUrl(rawUrl: String): Pair<String, ServerAPI.Companion.Result> {
if (rawUrl.endsWith(".pdf"))
return Pair(rawUrl, ServerAPI.Companion.Result.SUCCESS)
return try {
withContext(Dispatchers.Default) {
val html: String = ktorClient.get(rawUrl)
val realUrl = SpecificCurieServer(this@CurieServer).parseFileUrl(html)
Pair(realUrl, ServerAPI.Companion.Result.SUCCESS)
}
} catch (exception: IOException) {
Pair(rawUrl, ServerAPI.Companion.Result.NETWORK_ERROR)
} catch (exception: Exception) {
Pair(rawUrl, ServerAPI.Companion.Result.GENERIC_ERROR)
}
}
override suspend fun newCircularsAvailable(): Pair<Boolean, ServerAPI.Companion.Result> { override suspend fun newCircularsAvailable(): Pair<Boolean, ServerAPI.Companion.Result> {
return Pair(true, ServerAPI.Companion.Result.SUCCESS) return Pair(true, ServerAPI.Companion.Result.SUCCESS)
} }
@@ -61,6 +78,8 @@ class CurieServer(ktorClient: HttpClient) : Server(ktorClient) {
"""(\d+)""".toRegex() """(\d+)""".toRegex()
val idMatcher = idRegex.find(string) val idMatcher = idRegex.find(string)
val realUrl = if (url.endsWith(".pdf")) url else null
val id = idMatcher?.value?.toLong() ?: -1L val id = idMatcher?.value?.toLong() ?: -1L
val dateRegex = val dateRegex =
@@ -75,9 +94,9 @@ class CurieServer(ktorClient: HttpClient) : Server(ktorClient) {
.removePrefix("_") .removePrefix("_")
.removePrefix(" ") .removePrefix(" ")
Circular(id, serverID, title, url, dateMatcher.value) Circular(id, serverID, title, url, realUrl, dateMatcher.value)
} else { } else {
Circular(id, serverID, title, url, "") Circular(id, serverID, title, url, realUrl, "")
} }
} }
@@ -88,4 +107,5 @@ class CurieServer(ktorClient: HttpClient) : Server(ktorClient) {
expect class SpecificCurieServer(curieServer: CurieServer) { expect class SpecificCurieServer(curieServer: CurieServer) {
fun parseHtml(string: String): List<Circular> fun parseHtml(string: String): List<Circular>
fun parseFileUrl(string: String): String
} }

View File

@@ -67,6 +67,10 @@ class PorporatoServer(ktorClient: HttpClient) : Server(ktorClient) {
} }
} }
override suspend fun getRealUrl(rawUrl: String): Pair<String, ServerAPI.Companion.Result> {
return Pair(rawUrl, ServerAPI.Companion.Result.SUCCESS)
}
override suspend fun newCircularsAvailable(): Pair<Boolean, ServerAPI.Companion.Result> { override suspend fun newCircularsAvailable(): Pair<Boolean, ServerAPI.Companion.Result> {
return Pair(true, ServerAPI.Companion.Result.SUCCESS) return Pair(true, ServerAPI.Companion.Result.SUCCESS)
} }
@@ -85,6 +89,7 @@ class PorporatoServer(ktorClient: HttpClient) : Server(ktorClient) {
val parent = list.find { it.id == attachment.id && !it.name.startsWith("All") } val parent = list.find { it.id == attachment.id && !it.name.startsWith("All") }
parent?.attachmentsNames?.add(attachment.name) parent?.attachmentsNames?.add(attachment.name)
parent?.attachmentsUrls?.add(attachment.url) parent?.attachmentsUrls?.add(attachment.url)
parent?.realAttachmentsUrls?.add(attachment.url)
return@removeAll true return@removeAll true
} }
@@ -101,6 +106,7 @@ class PorporatoServer(ktorClient: HttpClient) : Server(ktorClient) {
val parent = list[lastIndex] val parent = list[lastIndex]
parent.attachmentsNames.add(attachment.name) parent.attachmentsNames.add(attachment.name)
parent.attachmentsUrls.add(attachment.url) parent.attachmentsUrls.add(attachment.url)
parent.realAttachmentsUrls.add(attachment.url)
return@removeAll true return@removeAll true
} }
@@ -146,9 +152,9 @@ class PorporatoServer(ktorClient: HttpClient) : Server(ktorClient) {
title = title.removeRange(dateMatcher.range) title = title.removeRange(dateMatcher.range)
.removeSuffix(" (pubb.: )") .removeSuffix(" (pubb.: )")
Circular(id, serverID, title, fullUrl, dateMatcher.value.replace("-", "/")) Circular(id, serverID, title, fullUrl, fullUrl, dateMatcher.value.replace("-", "/"))
} else { } else {
Circular(id, serverID, title, fullUrl, "") Circular(id, serverID, title, fullUrl, fullUrl, "")
} }
} }
} }

View File

@@ -0,0 +1,2 @@
ALTER TABLE Circulars ADD COLUMN realAttachmentsUrls TEXT NOT NULL DEFAULT "";
ALTER TABLE Circulars ADD COLUMN realUrl TEXT;

View File

@@ -9,18 +9,30 @@ CREATE TABLE Circulars (
read INTEGER NOT NULL DEFAULT 0, read INTEGER NOT NULL DEFAULT 0,
attachmentsNames TEXT NOT NULL, attachmentsNames TEXT NOT NULL,
attachmentsUrls TEXT NOT NULL, attachmentsUrls TEXT NOT NULL,
realAttachmentsUrls TEXT NOT NULL,
realUrl TEXT,
PRIMARY KEY (id, school) PRIMARY KEY (id, school)
); );
insertCircular: insertCircular:
INSERT OR IGNORE INTO Circulars(id, school, name, url, date, favourite, reminder, read, attachmentsNames, attachmentsUrls) INSERT OR IGNORE INTO Circulars(id, school, name, url, date, favourite, reminder, read, attachmentsNames, attachmentsUrls, realAttachmentsUrls, realUrl)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?); VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
updateCircular: updateCircular:
UPDATE Circulars UPDATE Circulars
SET favourite = ?, reminder = ? SET favourite = ?, reminder = ?
WHERE id = ? AND school = ?; WHERE id = ? AND school = ?;
setRealUrl:
UPDATE Circulars
SET realUrl = ?
WHERE id = ? AND school = ?;
setRealAttachmentsUrls:
UPDATE Circulars
SET realAttachmentsUrls = ?
WHERE id = ? AND school = ?;
markCircularRead: markCircularRead:
UPDATE Circulars UPDATE Circulars
SET read = ? SET read = ?

View File

@@ -24,5 +24,18 @@ import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
actual class DatabaseDriverFactory { actual class DatabaseDriverFactory {
actual fun createDriver(): SqlDriver { actual fun createDriver(): SqlDriver {
return NativeSqliteDriver(AppDatabase.Schema, "circolapp.db") return NativeSqliteDriver(AppDatabase.Schema, "circolapp.db")
.also {
var currentVer = DatabaseFactory.getVersion(it)
val schemaVer: Int = AppDatabase.Schema.version
if (currentVer == 0) {
currentVer = 1
}
if (schemaVer > currentVer) {
AppDatabase.Schema.migrate(it, currentVer, schemaVer)
DatabaseFactory.setVersion(it, schemaVer)
}
}
} }
} }

View File

@@ -30,14 +30,25 @@ actual class SpecificCurieServer actual constructor(private val curieServer: Cur
val list = ArrayList<Circular>() val list = ArrayList<Circular>()
htmlList?.forEach { element -> htmlList?.forEach { element ->
val url = element.attributes.objectForKey("href").toString()
if (element.parentElement?.parentElement?.parentElement?.tagName == "li") { if (element.parentElement?.parentElement?.parentElement?.tagName == "li") {
list.last().attachmentsNames.add(element.textContent) list.last().attachmentsNames.add(element.textContent)
list.last().attachmentsUrls.add(element.attributes.objectForKey("href").toString()) list.last().attachmentsUrls.add(url)
if (url.endsWith(".pdf")) {
list.last().realAttachmentsUrls.add(url)
} else { } else {
list.add(curieServer.generateFromString(element.textContent, element.attributes.objectForKey("href").toString())) list.last().realAttachmentsUrls.add("")
}
} else {
list.add(curieServer.generateFromString(element.textContent, url))
} }
} }
return list return list
} }
actual fun parseFileUrl(string: String): String {
TODO("Not yet implemented")
}
} }

View File

@@ -25,27 +25,18 @@ actual class DatabaseDriverFactory(private val path: String) {
actual fun createDriver(): SqlDriver { actual fun createDriver(): SqlDriver {
return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY + path) return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY + path)
.also { .also {
val currentVer = getVersion(it) val currentVer = DatabaseFactory.getVersion(it)
if (currentVer == 0) { if (currentVer == 0) {
AppDatabase.Schema.create(it) AppDatabase.Schema.create(it)
setVersion(it, 1) DatabaseFactory.setVersion(it, 1)
} else { } else {
val schemaVer: Int = AppDatabase.Schema.version val schemaVer: Int = AppDatabase.Schema.version
if (schemaVer > currentVer) { if (schemaVer > currentVer) {
AppDatabase.Schema.migrate(it, currentVer, schemaVer) AppDatabase.Schema.migrate(it, currentVer, schemaVer)
setVersion(it, schemaVer) DatabaseFactory.setVersion(it, schemaVer)
println("init: migrated from $currentVer to $schemaVer") println("init: migrated from $currentVer to $schemaVer")
} }
} }
} }
} }
private fun getVersion(driver: SqlDriver): Int {
val sqlCursor = driver.executeQuery(null, "PRAGMA user_version;", 0, null)
return sqlCursor.getLong(0)!!.toInt()
}
private fun setVersion(driver: SqlDriver, version: Int) {
driver.execute(null, String.format("PRAGMA user_version = %d;", version), 0, null)
}
} }