diff --git a/app/build.gradle b/app/build.gradle index e4af4a9..0edf59e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.nareshkumarrao.eiweblog" minSdkVersion 16 targetSdkVersion 30 - versionCode 4 - versionName "0.9.3" + versionCode 5 + versionName "0.10.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -42,12 +42,13 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' implementation 'com.android.volley:volley:1.2.0' implementation "androidx.work:work-runtime-ktx:2.5.0" - - + implementation 'org.jsoup:jsoup:1.13.1' + implementation 'com.google.code.gson:gson:2.8.6' } diff --git a/app/release/app-release.apk b/app/release/app-release.apk index 6e7cb04..bcbfcbc 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 ed7cac1..97322b7 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -10,8 +10,8 @@ { "type": "SINGLE", "filters": [], - "versionCode": 4, - "versionName": "0.9.3", + "versionCode": 5, + "versionName": "0.10.1", "outputFile": "app-release.apk" } ] diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2669787..533a159 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,14 +11,18 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.EIWeblog"> + + android:parentActivityName=".MainActivity" + android:theme="@style/Theme.EIWeblog.NoActionBar" /> + android:parentActivityName=".MainActivity" + android:theme="@style/Theme.EIWeblog.NoActionBar" /> (R.id.grades_toolbar) as Toolbar + setSupportActionBar(myToolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowTitleEnabled(false) + + findViewById(R.id.grades_recycler).apply { + layoutManager = LinearLayoutManager(this@GradesActivity) + adapter = ItemArticleAdapter(listOf()) + } + findViewById(R.id.grades_recycler).addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) + val swipeRefreshLayout = findViewById(R.id.grades_swipe_refresh) + swipeRefreshLayout?.setOnRefreshListener { + HISUtility.fetchExamRows(this, ::updateExamRows) + } + + val sharedPref = getSharedPreferences( + getString(R.string.preference_file_key), + Context.MODE_PRIVATE + ) + + val username = sharedPref?.getString(getString(R.string.username_key), null) + if( username == null ){ + val loginDialog = LoginDialogFragment(this, false) { + HISUtility.fetchExamRows(this, ::updateExamRows) + } + loginDialog.show(supportFragmentManager, "loginDialog") + }else{ + val savedRows = HISUtility.getSavedExamRows(this) + if(savedRows == null){ + HISUtility.fetchExamRows(this, ::updateExamRows) + }else{ + updateExamRows(savedRows) + } + } + } + + private fun updateExamRows(examRows: List?){ + examRows ?: run { + val loginDialog = LoginDialogFragment(this, true){ + HISUtility.fetchExamRows(this, ::updateExamRows) + } + loginDialog.show(supportFragmentManager, "loginDialog") + return + } + this@GradesActivity.runOnUiThread { + findViewById(R.id.grades_recycler)?.apply { + layoutManager = LinearLayoutManager(this@GradesActivity) + adapter = ItemGradesAdapter(examRows) + } + findViewById(R.id.grades_swipe_refresh).isRefreshing=false + findViewById(R.id.gradesProgressBar).visibility=View.GONE + } + } +} + +class LoginDialogFragment(val context: GradesActivity, private val isError: Boolean, val loginCallback: () -> Unit?) : DialogFragment() { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return activity?.let { + val builder = AlertDialog.Builder(it) + val inflater = requireActivity().layoutInflater + val dialogView = inflater.inflate(R.layout.dialog_login, null) + if(isError){ + dialogView.findViewById(R.id.errorText).visibility=View.VISIBLE + }else{ + dialogView.findViewById(R.id.errorText).visibility=View.INVISIBLE + } + builder.setView(dialogView) + .setPositiveButton( + R.string.login + ) { _, _ -> + val username = dialogView.findViewById(R.id.loginUsername).text.toString() + val password = dialogView.findViewById(R.id.loginPassword).text.toString() + HISUtility.setUsernamePassword(context, username, password) + loginCallback() + } + .setNegativeButton( + R.string.cancel + ) { _, _ -> + dialog?.cancel() + context.finish() + } + builder.create() + } ?: throw IllegalStateException("Activity cannot be null") + + } +} diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/HISUtility.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/HISUtility.kt new file mode 100644 index 0000000..5ee8d9a --- /dev/null +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/HISUtility.kt @@ -0,0 +1,173 @@ +package com.nareshkumarrao.eiweblog + +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 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 + +data class ExamRow(val name: String, val grade: String, val attempt: String, val date: String) + +internal object HISUtility { + + fun setUsernamePassword(context: Context?, username: String, password: String) { + val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) + if (sharedPref != null) { + with(sharedPref.edit()) { + putString(context.getString(R.string.username_key), username) + putString(context.getString(R.string.password_key), password) + apply() + } + } + } + + fun checkForUpdates(context: Context?, callback: (examRows: List?) -> Unit) { + val savedRows = getSavedExamRows(context) ?: run { + callback(null) + return + } + + val newRows: MutableList = mutableListOf() + fetchExamRows(context) { examRows -> + if (examRows != null) { + for (examRow in examRows) { + if (!savedRows.contains(examRow)) { + newRows.add(examRow) + } + } + } + callback(newRows) + } + + + } + + fun getSavedExamRows(context: Context?): List? { + val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) + val examRowsJson = sharedPref?.getString(context.getString(R.string.exam_rows_key), null) + ?: return null + val examRowsType = object : TypeToken>() {}.type + return Gson().fromJson(examRowsJson, examRowsType) + } + + private fun saveExamRows(context: Context?, examRows: List) { + val examRowsJson = Gson().toJson(examRows) + val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) + if (sharedPref != null) { + with(sharedPref.edit()) { + putString(context.getString(R.string.exam_rows_key), examRowsJson) + apply() + } + } + } + + @Throws(LoginFailedException::class) + fun fetchExamRows(context: Context?, callback: (examRows: List?) -> Unit): Unit? { + val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) + val username = sharedPref?.getString(context.getString(R.string.username_key), null) + ?: return null + val password = sharedPref.getString(context.getString(R.string.password_key), null) + ?: 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)) + .method(Connection.Method.POST) + .userAgent("Mozilla") + .data(postData) + .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 selectStudiengangUnhide = Jsoup.connect(notenspiegelURL) + .userAgent("Mozilla") + .cookies(loginPage.cookies()) + .get() + val selectStudiengangUnhideURL = selectStudiengangUnhide.select("a[href]:containsOwn(Abschluss)").first().attr("href") + + val selectStudiengang = Jsoup.connect(selectStudiengangUnhideURL) + .userAgent("Mozilla") + .cookies(loginPage.cookies()) + .get() + val studiengangURL = selectStudiengang.select("a[href]:containsOwn(Leistungen anzeigen)").first().attr("href") + + + val notenSpiegelPage = Jsoup.connect(studiengangURL) + .userAgent("Mozilla") + .cookies(loginPage.cookies()) + .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 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) + } + saveExamRows(context, examRows) + callback(examRows) + } + + return Thread(runnable).start() + } + 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}")) + .setContentIntent(pendingIntent) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setAutoCancel(true) + with(NotificationManagerCompat.from(context)) { + notify(id, builder.build()) + } + + } + + 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 importance = NotificationManager.IMPORTANCE_DEFAULT + 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 + notificationManager.createNotificationChannel(channel) + } + } +} + + +class LoginFailedException : Throwable() diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/MainActivity.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/MainActivity.kt index 839c1e7..a04e440 100644 --- a/app/src/main/java/com/nareshkumarrao/eiweblog/MainActivity.kt +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/MainActivity.kt @@ -35,6 +35,7 @@ class MainActivity : AppCompatActivity() { setSupportActionBar(myToolbar) Utilities.createNotificationChannel(this) + HISUtility.createNotificationChannel(this) val uploadWorkRequest: WorkRequest = PeriodicWorkRequestBuilder(1, TimeUnit.HOURS) @@ -42,8 +43,6 @@ class MainActivity : AppCompatActivity() { WorkManager.getInstance(this).enqueue(uploadWorkRequest) Utilities.fetchRepoReleaseInformation(this, ::repoReleaseCallback) - - } private fun repoReleaseCallback(version: String, log: String, url: String?){ @@ -97,4 +96,8 @@ class MainActivity : AppCompatActivity() { val intent = Intent(this, AboutActivity::class.java) startActivity(intent) } + + fun showGrades(item: MenuItem){ + startActivity(Intent(this, GradesActivity::class.java)) + } } \ No newline at end of file diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/NotificationSettingsActivity.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/NotificationSettingsActivity.kt index b4069bd..06e69ed 100644 --- a/app/src/main/java/com/nareshkumarrao/eiweblog/NotificationSettingsActivity.kt +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/NotificationSettingsActivity.kt @@ -18,16 +18,25 @@ class NotificationSettingsActivity : AppCompatActivity() { supportActionBar?.setDisplayShowTitleEnabled(false) val sharedPref = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE) - val weblogResponse = sharedPref?.getBoolean(getString(R.string.enable_notifications_key), true) - val notificationSwitch = findViewById(R.id.notification_switch) - notificationSwitch.isChecked = weblogResponse!! - notificationSwitch.setOnCheckedChangeListener { _, isChecked -> + val weblogResponse = sharedPref?.getBoolean(getString(R.string.enable_weblog_notifications_key), true) + val weblogNotificationSwitch = findViewById(R.id.weblog_notification_switch) + weblogNotificationSwitch.isChecked = weblogResponse!! + weblogNotificationSwitch.setOnCheckedChangeListener { _, isChecked -> with(sharedPref.edit()) { - putBoolean(getString(R.string.enable_notifications_key), isChecked) + putBoolean(getString(R.string.enable_weblog_notifications_key), isChecked) + apply() + } + } + + val gradeResponse = sharedPref.getBoolean(getString(R.string.enable_grades_notifications_key), true) + val gradeNotificationSwitch = findViewById(R.id.grades_notification_switch) + gradeNotificationSwitch.isChecked = gradeResponse + gradeNotificationSwitch.setOnCheckedChangeListener { _, isChecked -> + with(sharedPref.edit()) { + putBoolean(getString(R.string.enable_grades_notifications_key), isChecked) apply() } - //Toast.makeText(this, "Notifications are set to $isChecked", Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/UpdateWorker.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/UpdateWorker.kt index 52b3cb2..773e171 100644 --- a/app/src/main/java/com/nareshkumarrao/eiweblog/UpdateWorker.kt +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/UpdateWorker.kt @@ -10,23 +10,34 @@ class UpdateWorker(private val context: Context, workerParams: WorkerParameters) override fun doWork(): Result { val sharedPref = context.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) - val notificationsEnabled = sharedPref?.getBoolean(context.getString(R.string.enable_notifications_key), true) - if(!notificationsEnabled!!){ - return Result.success() - } - Utilities.weblogList(context) { articles -> - val lastArticle = Utilities.getLatestRelevantArticle(articles)!! - val hashString = lastArticle.title + lastArticle.content + lastArticle.date - val oldHash = md5(hashString) + val weblogNotificationsEnabled = sharedPref?.getBoolean(context.getString(R.string.enable_weblog_notifications_key), true) + val gradesNotificationsEnabled = sharedPref?.getBoolean(context.getString(R.string.enable_grades_notifications_key), true) + + if(weblogNotificationsEnabled!!){ + Utilities.weblogList(context) { articles -> + val lastArticle = Utilities.getLatestRelevantArticle(articles)!! + val hashString = lastArticle.title + lastArticle.content + lastArticle.date + val oldHash = md5(hashString) + + Utilities.fetchWeblogXML(applicationContext){newArticles -> + val lastNewArticle = Utilities.getLatestRelevantArticle(newArticles)!! + val newHashString = lastNewArticle.title + lastNewArticle.content + lastNewArticle.date + val newHash = md5(newHashString) - Utilities.fetchWeblogXML(applicationContext){newArticles -> - val lastNewArticle = Utilities.getLatestRelevantArticle(newArticles)!! - val newHashString = lastNewArticle.title + lastNewArticle.content + lastNewArticle.date - val newHash = md5(newHashString) + if(oldHash != newHash){ + Utilities.sendNotification(context, lastNewArticle, newArticles.size) + } + } + } + } - if(oldHash != newHash){ - Utilities.sendNotification(context, lastNewArticle, newArticles.size) + if(gradesNotificationsEnabled!!){ + HISUtility.checkForUpdates(context) { gradeUpdates -> + if (gradeUpdates != null) { + for (grade in gradeUpdates) { + HISUtility.sendNotification(context, grade, gradeUpdates.indexOf(grade)) + } } } } diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/Utilities.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/Utilities.kt index 74646c3..08a544e 100644 --- a/app/src/main/java/com/nareshkumarrao/eiweblog/Utilities.kt +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/Utilities.kt @@ -55,8 +55,6 @@ internal object Utilities { } } - - val parser: XmlPullParser = Xml.newPullParser() parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) //Log.d("XMLLIST", responseStr ) @@ -140,7 +138,7 @@ internal object Utilities { } val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, 0) - val builder = NotificationCompat.Builder(context!!, context.getString(R.string.channel_id)) + val builder = NotificationCompat.Builder(context!!, context.getString(R.string.weblog_notification_channel_id)) .setSmallIcon(R.drawable.ic_stat_name) .setContentTitle(article.title) .setStyle(NotificationCompat.BigTextStyle() @@ -158,10 +156,10 @@ internal object Utilities { fun createNotificationChannel(context: Context?){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val name = context?.getString(R.string.channel_name) - val descriptionText = context?.getString(R.string.channel_description) + val name = context?.getString(R.string.weblog_notification_channel_name) + val descriptionText = context?.getString(R.string.weblog_notification_channel_description) val importance = NotificationManager.IMPORTANCE_DEFAULT - val channel = NotificationChannel(context?.getString(R.string.channel_id), name, importance).apply { + val channel = NotificationChannel(context?.getString(R.string.weblog_notification_channel_id), name, importance).apply { description = descriptionText } val notificationManager: NotificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager diff --git a/app/src/main/java/com/nareshkumarrao/eiweblog/ui/main/ItemGradesAdapter.kt b/app/src/main/java/com/nareshkumarrao/eiweblog/ui/main/ItemGradesAdapter.kt new file mode 100644 index 0000000..6560729 --- /dev/null +++ b/app/src/main/java/com/nareshkumarrao/eiweblog/ui/main/ItemGradesAdapter.kt @@ -0,0 +1,46 @@ +package com.nareshkumarrao.eiweblog.ui.main + +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.nareshkumarrao.eiweblog.ExamRow +import com.nareshkumarrao.eiweblog.R + +class ItemGradesAdapter(private val examRows: List) : RecyclerView.Adapter() { + inner class ViewHolder(inflater: LayoutInflater, parent: ViewGroup) : RecyclerView.ViewHolder(inflater.inflate(R.layout.item_grades, parent, false)) { + private var name: TextView? = null + private var attempt: TextView? = null + private var date: TextView? = null + private var grade: TextView? = null + + init { + name = itemView.findViewById(R.id.examNameText) + attempt = itemView.findViewById(R.id.versuchText) + date = itemView.findViewById(R.id.examDateText) + grade = itemView.findViewById(R.id.gradeText) + } + + fun bind(examRow: ExamRow) { + name?.text = examRow.name + attempt?.text = examRow.attempt + grade?.text = examRow.grade + date?.text = examRow.date + } + + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemGradesAdapter.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return ViewHolder(inflater, parent) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val examRow = examRows[position] + holder.bind(examRow) + } + + override fun getItemCount(): Int { + return examRows.size + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 1110b8e..b846f0a 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -15,8 +15,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_notification_settings.xml b/app/src/main/res/layout/activity_notification_settings.xml index 175418b..5431d6d 100644 --- a/app/src/main/res/layout/activity_notification_settings.xml +++ b/app/src/main/res/layout/activity_notification_settings.xml @@ -15,8 +15,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - - + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_login.xml b/app/src/main/res/layout/dialog_login.xml new file mode 100644 index 0000000..366baf9 --- /dev/null +++ b/app/src/main/res/layout/dialog_login.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_grades.xml b/app/src/main/res/layout/item_grades.xml new file mode 100644 index 0000000..29b8fb7 --- /dev/null +++ b/app/src/main/res/layout/item_grades.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/toolbar_menu.xml b/app/src/main/res/menu/toolbar_menu.xml index bd7cc32..b5c6f88 100644 --- a/app/src/main/res/menu/toolbar_menu.xml +++ b/app/src/main/res/menu/toolbar_menu.xml @@ -9,4 +9,9 @@ android:onClick="showAbout" android:title="@string/about" app:showAsAction="never" /> + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 78eda5d..92e3a2a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -4,7 +4,7 @@ Info Shick mir €5 für ein Bier über PayPal Benachrichtigung - Benachrichtige mich über Neuerungen auf der Weblog + Benachrichtige mich über Neuerungen auf der Weblog Ich bin Naresh und mache gerade mein Master in Automatisierungstechnik hier an der HSD.\n \nIch habe diese App geschrieben, da die EI-Weblog für mich unzufriedenstellend war. Ich vergesse @@ -20,4 +20,16 @@ Herunterladen Abbrechen Neues App-Version verfügbar + Benutzername + Passwort + Notenspiegel + Versuch: + Prüfungsdatum: + Anmelden + Neues Klausurergebnis! + Klausurergebnis + Benachrichtigung bei neuem Klausurergebnis + Benachrichtige mich bei neuem Klausurergebnis + Anmelden bei OSSC + FEHLER! \ 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 6a8cfff..a067e29 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -9,7 +9,7 @@ About Send me €5 for beer on PayPal Notifications - Notify me when new updates are posted to the weblog + Notify me when new updates are posted to the weblog My name is Naresh and I am doing my Master\'s in Automation at the HSD.\n \nI wrote this app because I was unsatisfied with the EI weblog that was always silently being @@ -21,4 +21,16 @@ Download Cancel New version of app available + Username + Password + Grades + Attempt: + Exam date: + Login + New exam results out! + Exam Results + Notification when you get a new exam result + Notify me when I get new grades + Login to OSSC + ERROR! \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8f4dff5..9eb4847 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,7 +1,6 @@ #E60028 - #FF6200EE #FF3700B3 #FF03DAC5 #FF018786 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ed5774..fc2a8e1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,15 +24,34 @@ as contained within the terms of the GPLv3 license. More information about the license and the source code of this project can be found on its GitHub repository. - EI Weblog Notifications - Latest updates from the EI Weblog - com.nareshkumarrao.eiweblog.update + EI Weblog Notifications + Latest updates from the EI Weblog + com.nareshkumarrao.eiweblog.weblogupdate Benachrichtigung - Notify me when new updates are posted to the weblog - com.nareshkumarrao.notifications.key + Notify me when new updates are posted to the weblog + com.nareshkumarrao.weblog_notifications.key GitHub Repository https://api.github.com/repos/naresh97/ei-weblog-android/releases New version of app available Download Cancel + + + exam_rows + username_key + password_key + Username + Password + Notenspiegel + Versuch: + Prüfungsdatum: + Anmelden + New exam results out! + Exam Results + Notification when you get a new exam result + com.nareshkumarrao.eiweblog.grades + Notify me when I get new grades + com.nareshkumarrao.eiweblog.grades_notifications.key + Login to OSSC + ERROR! \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 917b396..01368f3 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -2,7 +2,7 @@