diff --git a/app/build.gradle b/app/build.gradle index 0edf59e..c05ddd7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.nareshkumarrao.eiweblog" minSdkVersion 16 targetSdkVersion 30 - versionCode 5 - versionName "0.10.1" + versionCode 6 + versionName "0.10.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/release/app-release.apk b/app/release/app-release.apk index bcbfcbc..3bb6c96 100644 Binary files a/app/release/app-release.apk and b/app/release/app-release.apk differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 97322b7..b3f99f6 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -10,8 +10,8 @@ { "type": "SINGLE", "filters": [], - "versionCode": 5, - "versionName": "0.10.1", + "versionCode": 6, + "versionName": "0.10.2", "outputFile": "app-release.apk" } ] diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/HISUtility.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/HISUtility.kt index 5ee8d9a..7f162d3 100644 --- a/app/src/main/java/com/nareshkumarrao/eiweblog/HISUtility.kt +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/HISUtility.kt @@ -1,17 +1,20 @@ package com.nareshkumarrao.eiweblog +import android.app.Activity import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build +import android.widget.Toast import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.google.gson.Gson import com.google.gson.reflect.TypeToken import org.jsoup.Connection import org.jsoup.Jsoup +import java.net.SocketTimeoutException data class ExamRow(val name: String, val grade: String, val attempt: String, val date: String) @@ -34,17 +37,17 @@ internal object HISUtility { return } - val newRows: MutableList = mutableListOf() - fetchExamRows(context) { examRows -> - if (examRows != null) { - for (examRow in examRows) { - if (!savedRows.contains(examRow)) { - newRows.add(examRow) - } + val newRows: MutableList = mutableListOf() + fetchExamRows(context) { examRows -> + if (examRows != null) { + for (examRow in examRows) { + if (!savedRows.contains(examRow)) { + newRows.add(examRow) } } - callback(newRows) } + callback(newRows) + } } @@ -77,75 +80,118 @@ internal object HISUtility { ?: return null val runnable = Runnable { - val postData: MutableMap = mutableMapOf() - postData["asdf"] = username - postData["fdsa"] = password - val loginPage = Jsoup.connect(context.getString(R.string.ossc_login_post)) + try { + val postData: MutableMap = mutableMapOf() + postData["asdf"] = username + postData["fdsa"] = password + + val loginPage = Jsoup.connect(context.getString(R.string.ossc_login_post)) .method(Connection.Method.POST) .userAgent("Mozilla") .data(postData) + .timeout(60000) .execute() - val selectNotenspiegel = Jsoup.connect(context.getString(R.string.ossc_select_noten)) - .userAgent("Mozilla") - .cookies(loginPage.cookies()) - .get() - - val notenspiegelURL = selectNotenspiegel.select("a[href]:containsOwn(Notenspiegel)").first()?.attr("href") - ?: kotlin.run { - callback(null) - return@Runnable - } + val selectNotenspiegel = + Jsoup.connect(context.getString(R.string.ossc_select_noten)) + .userAgent("Mozilla") + .cookies(loginPage.cookies()) + .timeout(60000) + .get() + + val notenspiegelURL = + selectNotenspiegel.select("a[href]:containsOwn(Notenspiegel)").first() + ?.attr("href") + ?: kotlin.run { + callback(null) + return@Runnable + } - val selectStudiengangUnhide = Jsoup.connect(notenspiegelURL) + val selectStudiengangUnhide = Jsoup.connect(notenspiegelURL) .userAgent("Mozilla") .cookies(loginPage.cookies()) + .timeout(60000) .get() - val selectStudiengangUnhideURL = selectStudiengangUnhide.select("a[href]:containsOwn(Abschluss)").first().attr("href") + val selectStudiengangUnhideURL = + selectStudiengangUnhide.select("a[href]:containsOwn(Abschluss)").first() + .attr("href") - val selectStudiengang = Jsoup.connect(selectStudiengangUnhideURL) + val selectStudiengang = Jsoup.connect(selectStudiengangUnhideURL) .userAgent("Mozilla") .cookies(loginPage.cookies()) + .timeout(60000) .get() - val studiengangURL = selectStudiengang.select("a[href]:containsOwn(Leistungen anzeigen)").first().attr("href") + val studiengangURL = + selectStudiengang.select("a[href]:containsOwn(Leistungen anzeigen)").first() + .attr("href") - val notenSpiegelPage = Jsoup.connect(studiengangURL) + val notenSpiegelPage = Jsoup.connect(studiengangURL) .userAgent("Mozilla") .cookies(loginPage.cookies()) + .timeout(60000) .get() - val allGradesRows = notenSpiegelPage.select("div.fixedContainer > table > tbody > tr") - val examRows: MutableList = mutableListOf() - for (row in allGradesRows) { - if (row.select("td.tabelle1_alignleft").size < 1) { - continue + val allGradesRows = + notenSpiegelPage.select("div.fixedContainer > table > tbody > tr") + val examRows: MutableList = mutableListOf() + for (row in allGradesRows) { + if (row.select("td.tabelle1_alignleft").size < 1) { + continue + } + val columns = row.select("td") + if (columns.size < 1) { + continue + } + val examRow = ExamRow( + columns[1].text(), + columns[3].text(), + columns[6].text(), + columns[7].text() + ) + examRows.add(examRow) } - val columns = row.select("td") - if (columns.size < 1) { - continue + saveExamRows(context, examRows) + callback(examRows) + } catch (e: Exception) { + if (context is Activity) context.runOnUiThread { + when (e) { + is SocketTimeoutException -> { + Toast.makeText( + context, + context.getString(R.string.ossc_timeout_message), + Toast.LENGTH_LONG + ).show() + } + else -> { + Toast.makeText(context, e.localizedMessage, Toast.LENGTH_LONG).show() + } + } + context.finish() } - val examRow = ExamRow(columns[1].text(), columns[3].text(), columns[6].text(), columns[7].text()) - examRows.add(examRow) } - saveExamRows(context, examRows) - callback(examRows) } return Thread(runnable).start() } - fun sendNotification(context: Context?, examRow: ExamRow, id:Int) { + + fun sendNotification(context: Context?, examRow: ExamRow, id: Int) { val intent = Intent(context, NotificationSettingsActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, 0) - val builder = NotificationCompat.Builder(context!!, context.getString(R.string.grades_notification_channel_id)) - .setSmallIcon(R.drawable.ic_stat_name) - .setContentTitle(context.getString(R.string.exam_results_notification)) - .setStyle(NotificationCompat.BigTextStyle() - .bigText("${examRow.name}: ${examRow.grade}")) + val builder = NotificationCompat.Builder( + context!!, + context.getString(R.string.grades_notification_channel_id) + ) + .setSmallIcon(R.drawable.ic_stat_name) + .setContentTitle(context.getString(R.string.exam_results_notification)) + .setStyle( + NotificationCompat.BigTextStyle() + .bigText("${examRow.name}: ${examRow.grade}") + ) .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setAutoCancel(true) @@ -155,15 +201,21 @@ internal object HISUtility { } - fun createNotificationChannel(context: Context?){ + fun createNotificationChannel(context: Context?) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = context?.getString(R.string.grades_notification_channel_name) - val descriptionText = context?.getString(R.string.grades_notification_channel_description) + val descriptionText = + context?.getString(R.string.grades_notification_channel_description) val importance = NotificationManager.IMPORTANCE_DEFAULT - val channel = NotificationChannel(context?.getString(R.string.grades_notification_channel_id), name, importance).apply { + val channel = NotificationChannel( + context?.getString(R.string.grades_notification_channel_id), + name, + importance + ).apply { description = descriptionText } - val notificationManager: NotificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val notificationManager: NotificationManager = + context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/UpdateWorker.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/UpdateWorker.kt index 773e171..a88d500 100644 --- a/app/src/main/java/com/nareshkumarrao/eiweblog/UpdateWorker.kt +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/UpdateWorker.kt @@ -16,11 +16,13 @@ class UpdateWorker(private val context: Context, workerParams: WorkerParameters) if(weblogNotificationsEnabled!!){ Utilities.weblogList(context) { articles -> + articles ?: return@weblogList val lastArticle = Utilities.getLatestRelevantArticle(articles)!! val hashString = lastArticle.title + lastArticle.content + lastArticle.date val oldHash = md5(hashString) Utilities.fetchWeblogXML(applicationContext){newArticles -> + newArticles ?: return@fetchWeblogXML val lastNewArticle = Utilities.getLatestRelevantArticle(newArticles)!! val newHashString = lastNewArticle.title + lastNewArticle.content + lastNewArticle.date val newHash = md5(newHashString) diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/Utilities.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/Utilities.kt index 08a544e..e9fea5d 100644 --- a/app/src/main/java/com/nareshkumarrao/eiweblog/Utilities.kt +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/Utilities.kt @@ -22,22 +22,22 @@ import java.io.StringReader internal object Utilities { - fun weblogList(context: Context?, function: (d: List
) -> Unit){ + fun weblogList(context: Context?, function: (d: List
?) -> Unit) { val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) val weblogResponse = sharedPref?.getString(context.getString(R.string.weblog_response_key), null) - if (weblogResponse == null){ + if (weblogResponse == null) { fetchWeblogXML(context, function) return } val parser: XmlPullParser = Xml.newPullParser() parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) - parser.setInput( StringReader(weblogResponse) ) + parser.setInput(StringReader(weblogResponse)) parser.nextTag() function(parseXML(parser)) } - fun fetchWeblogXML(context: Context?, function: (d: List
) -> Unit) { + fun fetchWeblogXML(context: Context?, callback: (d: List
?) -> Unit) { val queue = Volley.newRequestQueue(context) @@ -49,7 +49,7 @@ internal object Utilities { val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) if (sharedPref != null) { - with (sharedPref.edit()) { + with(sharedPref.edit()) { putString(context.getString(R.string.weblog_response_key), responseStr) apply() } @@ -58,14 +58,17 @@ internal object Utilities { val parser: XmlPullParser = Xml.newPullParser() parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) //Log.d("XMLLIST", responseStr ) - parser.setInput( StringReader(responseStr) ) + parser.setInput(StringReader(responseStr)) parser.nextTag() val articles = parseXML(parser) - function(articles) + callback(articles) }, - { error -> Log.e("XMLLIST", error.toString()) }) + { error -> + Log.e("XMLLIST", error.toString()) + callback(null) + }) queue.add(stringRequest) } diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/ui/main/SectionsFragment.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/ui/main/SectionsFragment.kt index d12f4f6..561ad3f 100644 --- a/app/src/main/java/com/nareshkumarrao/eiweblog/ui/main/SectionsFragment.kt +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/ui/main/SectionsFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -48,12 +49,18 @@ class SectionsFragment : Fragment() { } } - private fun updateView(get_articles: List
){ - this.swipeRefreshLayout?.isRefreshing=false + private fun updateView(get_articles: List
?) { + this.swipeRefreshLayout?.isRefreshing = false + + if (get_articles == null) { + Toast.makeText(context, getString(R.string.load_weblog_error_message), Toast.LENGTH_LONG).show() + return + } + val articles: MutableList
= mutableListOf() val title = arguments?.getString(ARG_SECTION_NAME) ?: return - for (article in get_articles){ - if(article.category == title){ + for (article in get_articles) { + if (article.category == title) { articles.add(article) } } diff --git a/app/src/main/res/layout/dialog_login.xml b/app/src/main/res/layout/dialog_login.xml index 366baf9..0f2aabe 100644 --- a/app/src/main/res/layout/dialog_login.xml +++ b/app/src/main/res/layout/dialog_login.xml @@ -1,7 +1,6 @@ diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 2d9751d..1d67b20 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -32,4 +32,6 @@ Benachrichtige mich bei neuem Klausurergebnis Anmelden bei OSSC FEHLER! + Timeout-Fehler bei OSSC. Versuch mal später. + Weblog kann nicht aktualisiert werden. Versuch mal später. \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 6e36bd5..20bbdfa 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -33,4 +33,6 @@ Notify me when I get new grades Login to OSSC ERROR! + OSSC has timed out. Try again later. + Could not load new weblog data. Try again later. \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fc2a8e1..6aa51d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,4 +54,6 @@ com.nareshkumarrao.eiweblog.grades_notifications.key Login to OSSC ERROR! + OSSC has timed out. Try again later. + Could not load new weblog data. Try again later. \ No newline at end of file diff --git a/images/launcher.svg b/images/launcher.svg index a003e91..ef94aee 100644 --- a/images/launcher.svg +++ b/images/launcher.svg @@ -1,4 +1,5 @@ +