diff --git a/app/src/main/java/net/underdesk/circolapp/adapters/CircularLetterAdapter.kt b/app/src/main/java/net/underdesk/circolapp/adapters/CircularLetterAdapter.kt index 3e20865..119f970 100644 --- a/app/src/main/java/net/underdesk/circolapp/adapters/CircularLetterAdapter.kt +++ b/app/src/main/java/net/underdesk/circolapp/adapters/CircularLetterAdapter.kt @@ -44,6 +44,7 @@ import net.underdesk.circolapp.data.AndroidCircularRepository import net.underdesk.circolapp.data.AndroidDatabase import net.underdesk.circolapp.databinding.ItemCircularBinding import net.underdesk.circolapp.fragments.NewReminderFragment +import net.underdesk.circolapp.server.AndroidServerApi import net.underdesk.circolapp.shared.data.Circular import net.underdesk.circolapp.shared.data.CircularRepository import net.underdesk.circolapp.utils.DownloadableFile @@ -57,6 +58,7 @@ class CircularLetterAdapter( RecyclerView.Adapter() { private lateinit var context: Context private lateinit var circularRepository: CircularRepository + private var idsAreHumanReadable = true private val adapterCallback: AdapterCallback = mainActivity private var collapsedItems = -1 @@ -90,14 +92,22 @@ class CircularLetterAdapter( val binding = ItemCircularBinding.inflate(LayoutInflater.from(parent.context), parent, false) context = parent.context circularRepository = AndroidCircularRepository.getInstance(context) + idsAreHumanReadable = AndroidServerApi.getInstance(context).idsAreHumanReadable() return CircularLetterViewHolder(binding) } override fun onBindViewHolder(holder: CircularLetterViewHolder, position: Int) { - holder.number.text = context.getString(R.string.notification_title, circulars[position].id) + if (idsAreHumanReadable) { + holder.number.text = + context.getString(R.string.notification_title_id, circulars[position].id) + holder.date.text = circulars[position].date + } else { + holder.number.text = + context.getString(R.string.notification_title_date, circulars[position].date) + holder.date.text = "" + } holder.title.text = circulars[position].name - holder.date.text = circulars[position].date val observer = Observer { if (it) { diff --git a/app/src/main/java/net/underdesk/circolapp/push/NotificationsUtils.kt b/app/src/main/java/net/underdesk/circolapp/push/NotificationsUtils.kt index 10cc3b9..3293ba5 100644 --- a/app/src/main/java/net/underdesk/circolapp/push/NotificationsUtils.kt +++ b/app/src/main/java/net/underdesk/circolapp/push/NotificationsUtils.kt @@ -30,19 +30,21 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.app.TaskStackBuilder import net.underdesk.circolapp.MainActivity import net.underdesk.circolapp.R +import net.underdesk.circolapp.server.AndroidServerApi import net.underdesk.circolapp.shared.data.Circular import net.underdesk.circolapp.works.PollWork object NotificationsUtils { fun createNotificationsForCirculars(circulars: List, context: Context) { createNotificationChannel(context) + val idsAreHumanReadable = AndroidServerApi.getInstance(context).idsAreHumanReadable() val summaryStyle = NotificationCompat.InboxStyle() .setBigContentTitle(context.getString(R.string.notification_summary_title)) .setSummaryText(context.getString(R.string.notification_summary)) for (circular in circulars) { - createNotification(circular, context) + createNotification(circular, context, idsAreHumanReadable) summaryStyle.addLine(circular.name) } @@ -67,7 +69,11 @@ object NotificationsUtils { } } - private fun createNotification(circular: Circular, context: Context) { + private fun createNotification( + circular: Circular, + context: Context, + idsAreHumanReadable: Boolean + ) { val mainIntent = Intent(context, MainActivity::class.java) val viewIntent = Intent(Intent.ACTION_VIEW) @@ -88,7 +94,6 @@ object NotificationsUtils { val builder = NotificationCompat.Builder(context, PollWork.CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) - .setContentTitle(context.getString(R.string.notification_title, circular.id)) .setContentText(circular.name) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) @@ -99,6 +104,17 @@ object NotificationsUtils { .bigText(circular.name) ) + if (idsAreHumanReadable) { + builder.setContentTitle(context.getString(R.string.notification_title_id, circular.id)) + } else { + builder.setContentTitle( + context.getString( + R.string.notification_title_date, + circular.date + ) + ) + } + with(NotificationManagerCompat.from(context)) { notify(circular.id.toInt(), builder.build()) } diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 7b93059..2f1386d 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -22,7 +22,8 @@ Sconsigliato. Disabilitare questa opzione per utilizzare le notifiche push Quanto tempo attendere tra una sincronizzazione ed un altra - Circolare numero %1$d + Circolare numero %1$d + Circolare pubblicata il %1$s Nuove circolari pubblicate Nuove circolari diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9f1e4fd..651614c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -22,7 +22,8 @@ Not recommended. Disable this option to use push notifications How long to wait before checking if new circulars are published - Circular letter number %1$d + Circular letter number %1$d + Circular published on %1$s New circulars published New circulars diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 6bbad96..e67e5de 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -20,6 +20,7 @@ object Dependencies { object Kotlin { const val version = "1.4.21" const val core = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${version}" + const val serializationJson = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1" const val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2-native-mt" const val coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2-native-mt" @@ -65,10 +66,6 @@ object Dependencies { const val slf4j = "org.slf4j:slf4j-simple:1.7.30" } - object Serialization { - const val json = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1" - } - object SQLDelight { const val version = "1.4.4" const val sqlDelightRuntime = "com.squareup.sqldelight:runtime:$version" diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 58f419f..cf6d33c 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -44,20 +44,19 @@ kotlin { sourceSets { val commonMain by getting { dependencies { + // KotlinX implementation(Dependencies.Kotlin.coroutinesCore) { version { strictly("1.4.2-native-mt") } } + implementation(Dependencies.Kotlin.serializationJson) // 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) diff --git a/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/Server.kt b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/Server.kt index d399da2..2551104 100644 --- a/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/Server.kt +++ b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/Server.kt @@ -25,6 +25,7 @@ abstract class Server( val ktorClient: HttpClient ) { abstract val serverID: Int + open val idsAreHumanReadable = true abstract suspend fun getCircularsFromServer(): Pair, ServerAPI.Companion.Result> abstract suspend fun getRealUrl(rawUrl: String): Pair abstract suspend fun newCircularsAvailable(): Pair diff --git a/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/ServerAPI.kt b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/ServerAPI.kt index 8d68cc5..d4423e6 100644 --- a/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/ServerAPI.kt +++ b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/ServerAPI.kt @@ -24,12 +24,15 @@ 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 +import net.underdesk.circolapp.shared.server.prever.PreverAgrarioServer +import net.underdesk.circolapp.shared.server.prever.PreverAlberghieroServer class ServerAPI(serverName: Servers) { private val ktorClient = KtorFactory().createClient() private var server: Server fun serverID(): Int = server.serverID + fun idsAreHumanReadable() = server.idsAreHumanReadable init { server = createServer(serverName, ktorClient) @@ -61,7 +64,7 @@ class ServerAPI(serverName: Servers) { companion object { enum class Servers { - CURIE, PORPORATO + CURIE, PORPORATO, PREVER_AGRARIO, PREVER_ALBERGHIERO } enum class Result { @@ -85,16 +88,22 @@ class ServerAPI(serverName: Servers) { fun getServerName(server: Servers) = when (server) { Servers.CURIE -> "Liceo scientifico Maria Curie" Servers.PORPORATO -> "Liceo G.F. Porporato" + Servers.PREVER_AGRARIO -> "I.I.S. Arturo Prever - Agrario" + Servers.PREVER_ALBERGHIERO -> "I.I.S. Arturo Prever - Alberghiero" } fun getServerWebsite(server: Servers) = when (server) { Servers.CURIE -> "https://www.curiepinerolo.edu.it/" Servers.PORPORATO -> "https://www.liceoporporato.edu.it/" + Servers.PREVER_AGRARIO -> "https://www.prever.edu.it/agrario/" + Servers.PREVER_ALBERGHIERO -> "https://www.prever.edu.it/alberghiero/" } fun createServer(server: Servers, ktorClient: HttpClient) = when (server) { Servers.CURIE -> CurieServer(ktorClient) Servers.PORPORATO -> PorporatoServer(ktorClient) + Servers.PREVER_AGRARIO -> PreverAgrarioServer(ktorClient) + Servers.PREVER_ALBERGHIERO -> PreverAlberghieroServer(ktorClient) } } } diff --git a/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/pojo/WordpressPosts.kt b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/pojo/WordpressPosts.kt new file mode 100644 index 0000000..6f605ed --- /dev/null +++ b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/pojo/WordpressPosts.kt @@ -0,0 +1,34 @@ +/* + * Circolapp + * Copyright (C) 2019-2021 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 . + */ + +package net.underdesk.circolapp.shared.server.pojo + +import kotlinx.serialization.Serializable + +@Serializable +data class Post( + val id: Long, + val date: String, + val link: String, + val title: Title +) + +@Serializable +data class Title( + val rendered: String +) diff --git a/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/prever/GenericPreverServer.kt b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/prever/GenericPreverServer.kt new file mode 100644 index 0000000..92bfaeb --- /dev/null +++ b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/prever/GenericPreverServer.kt @@ -0,0 +1,107 @@ +/* + * Circolapp + * Copyright (C) 2019-2021 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 . + */ + +package net.underdesk.circolapp.shared.server.prever + +import io.ktor.client.* +import io.ktor.client.features.* +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.Server +import net.underdesk.circolapp.shared.server.ServerAPI +import net.underdesk.circolapp.shared.server.pojo.Post +import kotlin.coroutines.cancellation.CancellationException + +abstract class GenericPreverServer(ktorClient: HttpClient) : Server(ktorClient) { + abstract val categoryId: Int + + override val idsAreHumanReadable = false + + override suspend fun getCircularsFromServer() + : Pair, ServerAPI.Companion.Result> { + return try { + val list = arrayListOf() + var page = 1 + var newCircularsInPage: List + + do { + newCircularsInPage = parsePage(page) + list.addAll(newCircularsInPage) + + page++ + } while (newCircularsInPage.size >= 99) + + list.sortByDescending { it.id } + + Pair(list, ServerAPI.Companion.Result.SUCCESS) + } catch (exception: IOException) { + Pair(emptyList(), ServerAPI.Companion.Result.NETWORK_ERROR) + } catch (exception: Exception) { + Pair(emptyList(), ServerAPI.Companion.Result.GENERIC_ERROR) + } + } + + @OptIn(ExperimentalStdlibApi::class) + @Throws(IOException::class, CancellationException::class) + private suspend fun parsePage(page: Int): List { + val posts = retrievePageFromServer(page) + + return withContext(Dispatchers.Default) { + val list = arrayListOf() + + for (post in posts) { + list.add(generateFromString(post.id, post.title.rendered, post.date, post.link)) + } + + list + } + } + + override suspend fun getRealUrl(rawUrl: String): Pair { + return Pair(rawUrl, ServerAPI.Companion.Result.SUCCESS) + } + + override suspend fun newCircularsAvailable(): Pair { + return Pair(true, ServerAPI.Companion.Result.SUCCESS) + } + + @OptIn(ExperimentalStdlibApi::class) + @Throws(IOException::class, CancellationException::class) + private suspend fun retrievePageFromServer(page: Int): List { + return try { + ktorClient.get(getEndpointUrl(page)) + } catch (ex: ClientRequestException) { + emptyList() + } + } + + private fun generateFromString(id: Long, string: String, date: String, url: String): Circular { + val title = string.replace("–", "-") + + val dateList = date.split("T")[0].split("-") + val realDate = dateList[2] + "/" + dateList[1] + "/" + dateList[0] + + return Circular(id, serverID, title, url, url, realDate) + } + + private fun getEndpointUrl(page: Int) = + "https://www.prever.edu.it/wp-json/wp/v2/posts?_fields=id,date,link,title&categories=${categoryId}&per_page=100&page=${page}&after=2020-09-01T00:00:00Z" +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/prever/PreverAgrarioServer.kt b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/prever/PreverAgrarioServer.kt new file mode 100644 index 0000000..49e3f69 --- /dev/null +++ b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/prever/PreverAgrarioServer.kt @@ -0,0 +1,27 @@ +/* + * Circolapp + * Copyright (C) 2019-2021 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 . + */ + +package net.underdesk.circolapp.shared.server.prever + +import io.ktor.client.* +import net.underdesk.circolapp.shared.server.ServerAPI + +class PreverAgrarioServer(ktorClient: HttpClient) : GenericPreverServer(ktorClient) { + override val categoryId = 476 + override val serverID = ServerAPI.getServerId(ServerAPI.Companion.Servers.PREVER_AGRARIO) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/prever/PreverAlberghieroServer.kt b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/prever/PreverAlberghieroServer.kt new file mode 100644 index 0000000..8651756 --- /dev/null +++ b/shared/src/commonMain/kotlin/net/underdesk/circolapp/shared/server/prever/PreverAlberghieroServer.kt @@ -0,0 +1,27 @@ +/* + * Circolapp + * Copyright (C) 2019-2021 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 . + */ + +package net.underdesk.circolapp.shared.server.prever + +import io.ktor.client.* +import net.underdesk.circolapp.shared.server.ServerAPI + +class PreverAlberghieroServer(ktorClient: HttpClient) : GenericPreverServer(ktorClient) { + override val categoryId = 94 + override val serverID = ServerAPI.getServerId(ServerAPI.Companion.Servers.PREVER_ALBERGHIERO) +} \ No newline at end of file