add login dialog
continuous-integration/drone/push Build is passing Details

* first step towards a working his online integration
* rework AddSubjectDialog
* activate fragment_on_login
This commit is contained in:
Jannik 2020-08-11 17:09:46 +02:00
parent be43d87b1a
commit 1f660bdd37
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
10 changed files with 236 additions and 56 deletions

View File

@ -26,6 +26,7 @@ import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.EditText
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -39,13 +40,17 @@ import org.mosad.seil0.projectlaogai.onboarding.ViewPagerAdapter
class OnboardingActivity : AppCompatActivity() { class OnboardingActivity : AppCompatActivity() {
companion object { companion object {
val layouts = intArrayOf(R.layout.fragment_on_welcome, R.layout.fragment_on_course) //, R.layout.fragment_on_login) val layouts = intArrayOf(R.layout.fragment_on_welcome, R.layout.fragment_on_course, R.layout.fragment_on_login)
} }
private lateinit var viewPager: ViewPager private lateinit var viewPager: ViewPager
private lateinit var viewPagerAdapter: ViewPagerAdapter private lateinit var viewPagerAdapter: ViewPagerAdapter
private lateinit var linLayoutDots: LinearLayout private lateinit var linLayoutDots: LinearLayout
private lateinit var dots: Array<TextView> private lateinit var dots: Array<TextView>
private lateinit var editTextEmail: EditText
private lateinit var editTextPassword: EditText
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_onboarding) setContentView(R.layout.activity_onboarding)
@ -59,7 +64,8 @@ class OnboardingActivity : AppCompatActivity() {
viewPager.adapter = viewPagerAdapter viewPager.adapter = viewPagerAdapter
viewPager.addOnPageChangeListener(viewPagerPageChangeListener) viewPager.addOnPageChangeListener(viewPagerPageChangeListener)
btn_Skip.visibility = View.GONE // without the the skip button is visible // we don't use the skip button, instead we use the start button to skip the last fragment
btn_Skip.visibility = View.GONE
} }
fun btnNextClick(@Suppress("UNUSED_PARAMETER")v: View) { fun btnNextClick(@Suppress("UNUSED_PARAMETER")v: View) {
@ -77,11 +83,25 @@ class OnboardingActivity : AppCompatActivity() {
fun btnSelectCourseClick(@Suppress("UNUSED_PARAMETER")v: View) { fun btnSelectCourseClick(@Suppress("UNUSED_PARAMETER")v: View) {
SettingsFragment().selectCourse(this).show { SettingsFragment().selectCourse(this).show {
onDismiss { onDismiss {
launchHomeScreen() // launch the home-screen activity after the dialog is dismissed btnNextClick(v) // show the next fragment
} }
} }
} }
fun btnLoginClick(@Suppress("UNUSED_PARAMETER")v: View) {
editTextEmail = findViewById(R.id.editText_email)
editTextPassword = findViewById(R.id.editText_password)
// get login credentials from gui
val email = editTextEmail.text.toString()
val password = editTextPassword.text.toString()
println("Login: $email:$password")
// call LoginController
//launchHomeScreen()
}
private fun addBottomDots(currentPage: Int) { private fun addBottomDots(currentPage: Int) {
dots = Array(layouts.size) { TextView(this) } dots = Array(layouts.size) { TextView(this) }
linLayoutDots.removeAllViews() linLayoutDots.removeAllViews()
@ -106,9 +126,9 @@ class OnboardingActivity : AppCompatActivity() {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
addBottomDots(position) addBottomDots(position)
// changing the next button text // changing the next button text to skip for the login fragment
if (position == layouts.size - 1) { if (position == layouts.size - 1) {
btn_Next.text = getString(R.string.start) btn_Next.text = getString(R.string.skip)
btn_Next.visibility = View.VISIBLE btn_Next.visibility = View.VISIBLE
btn_Skip.visibility = View.GONE btn_Skip.visibility = View.GONE
} else { } else {
@ -121,5 +141,4 @@ class OnboardingActivity : AppCompatActivity() {
override fun onPageScrollStateChanged(arg0: Int) {} override fun onPageScrollStateChanged(arg0: Int) {}
} }
} }

View File

@ -0,0 +1,27 @@
/**
* ProjectLaogai
*
* Copyright 2019-2020 <seil0@mosad.xyz>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
package org.mosad.seil0.projectlaogai.controller.login
class LoginController {
// TODO implement
}

View File

@ -55,6 +55,7 @@ import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cShowBuffet import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cShowBuffet
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController import org.mosad.seil0.projectlaogai.controller.cache.TimetableController
import org.mosad.seil0.projectlaogai.uicomponents.dialogs.LoginDialog
import org.mosad.seil0.projectlaogai.util.DataTypes import org.mosad.seil0.projectlaogai.util.DataTypes
import java.util.* import java.util.*
@ -135,6 +136,14 @@ class SettingsFragment : Fragment() {
private fun initActions() { private fun initActions() {
linLayoutUser.setOnClickListener { linLayoutUser.setOnClickListener {
// open a new dialog // open a new dialog
LoginDialog(context!!)
.positiveButton {
println("Test: $password")
}
.show {
email = resources.getString(R.string.sample_user)
password = "Test123"
}
} }
linLayoutUser.setOnLongClickListener { linLayoutUser.setOnLongClickListener {

View File

@ -29,16 +29,13 @@ import android.view.ViewGroup
import android.widget.ScrollView import android.widget.ScrollView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton
import com.afollestad.materialdialogs.actions.getActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.android.synthetic.main.fragment_timetable.* import kotlinx.android.synthetic.main.fragment_timetable.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.mosad.seil0.projectlaogai.R import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.controller.PreferencesController
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController import org.mosad.seil0.projectlaogai.controller.cache.TimetableController
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController.Companion.timetable import org.mosad.seil0.projectlaogai.controller.cache.TimetableController.Companion.timetable
import org.mosad.seil0.projectlaogai.uicomponents.AddSubjectDialog import org.mosad.seil0.projectlaogai.uicomponents.dialogs.AddSubjectDialog
import org.mosad.seil0.projectlaogai.uicomponents.DayCardView import org.mosad.seil0.projectlaogai.uicomponents.DayCardView
import org.mosad.seil0.projectlaogai.util.NotRetardedCalendar import org.mosad.seil0.projectlaogai.util.NotRetardedCalendar
@ -86,10 +83,11 @@ class TimeTableFragment : Fragment() {
// show the AddLessonDialog if the ftaBtn is clicked // show the AddLessonDialog if the ftaBtn is clicked
faBtnAddSubject.setOnClickListener { faBtnAddSubject.setOnClickListener {
AddSubjectDialog(context!!).initialize(this@TimeTableFragment).show{ AddSubjectDialog(context!!)
getActionButton(WhichButton.POSITIVE).updateTextColor(PreferencesController.cColorAccent) .positiveButton {
getActionButton(WhichButton.NEGATIVE).updateTextColor(PreferencesController.cColorAccent) TimetableController.addSubject(selectedCourse, selectedSubject, context)
} runBlocking { reloadTimetableUI() }
}.show()
} }
// hide the btnCardValue if the user is scrolling down // hide the btnCardValue if the user is scrolling down
@ -133,7 +131,7 @@ class TimeTableFragment : Fragment() {
/** /**
* clear linLayout_Timetable, add the updated timetable * clear linLayout_Timetable, add the updated timetable
*/ */
fun reloadTimetableUI() = GlobalScope.launch(Dispatchers.Default) { private fun reloadTimetableUI() = GlobalScope.launch(Dispatchers.Default) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
// remove all lessons from the layout // remove all lessons from the layout
linLayout_Timetable.removeAllViews() linLayout_Timetable.removeAllViews()

View File

@ -20,7 +20,7 @@
* *
*/ */
package org.mosad.seil0.projectlaogai.uicomponents package org.mosad.seil0.projectlaogai.uicomponents.dialogs
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
@ -29,27 +29,29 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.Spinner import android.widget.Spinner
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton
import com.afollestad.materialdialogs.actions.getActionButton
import com.afollestad.materialdialogs.bottomsheets.BottomSheet import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.bottomsheets.setPeekHeight import com.afollestad.materialdialogs.bottomsheets.setPeekHeight
import com.afollestad.materialdialogs.customview.customView import com.afollestad.materialdialogs.customview.customView
import com.afollestad.materialdialogs.customview.getCustomView import com.afollestad.materialdialogs.customview.getCustomView
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.mosad.seil0.projectlaogai.R import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.controller.PreferencesController
import org.mosad.seil0.projectlaogai.controller.cache.CacheController import org.mosad.seil0.projectlaogai.controller.cache.CacheController
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController
import org.mosad.seil0.projectlaogai.fragments.TimeTableFragment
import org.mosad.seil0.projectlaogai.util.Course import org.mosad.seil0.projectlaogai.util.Course
import java.util.stream.Collectors import java.util.stream.Collectors
/** /**
* This class can create a new AddLessonDialog. * This class creates a new AddLessonDialog.
*/ */
class AddSubjectDialog(_context: Context) { class AddSubjectDialog(val context: Context) {
private val context = _context
private lateinit var spinnerCourses: Spinner private val dialog = MaterialDialog(context, BottomSheet())
private lateinit var spinnerSubjects: Spinner
private var spinnerCourses: Spinner
private var spinnerSubjects: Spinner
private val subjectsList = ArrayList<String>() private val subjectsList = ArrayList<String>()
private val courseNamesList = getCourseNames() private val courseNamesList = getCourseNames()
@ -57,33 +59,45 @@ class AddSubjectDialog(_context: Context) {
var selectedCourse = "" var selectedCourse = ""
var selectedSubject = "" var selectedSubject = ""
/** init {
* create a new AddLessonDialog (BottomSheet) dialog.title(R.string.add_lesson)
*/
fun initialize(ttf: TimeTableFragment): MaterialDialog {
val dialog = MaterialDialog(context, BottomSheet())
.title(R.string.add_lesson)
.message(R.string.add_lesson_desc) .message(R.string.add_lesson_desc)
.customView(R.layout.dialog_add_lesson) .customView(R.layout.dialog_add_lesson)
.setPeekHeight(900) .positiveButton(R.string.add)
.positiveButton(R.string.add) {
val lessons = TCoRAPIController.getLessons(selectedCourse, selectedSubject, 0)
println("add lesson \"$selectedCourse: $selectedSubject\"")
println(lessons.toString())
TimetableController.addSubject(selectedCourse, selectedSubject,context)
runBlocking { ttf.reloadTimetableUI() }
}
.negativeButton(R.string.cancel) .negativeButton(R.string.cancel)
.setPeekHeight(900)
// initialize the spinners
spinnerCourses = dialog.getCustomView().findViewById(R.id.spinner_Courses) spinnerCourses = dialog.getCustomView().findViewById(R.id.spinner_Courses)
spinnerSubjects = dialog.getCustomView().findViewById(R.id.spinner_Lessons) spinnerSubjects = dialog.getCustomView().findViewById(R.id.spinner_Lessons)
// fix not working accent color
dialog.getActionButton(WhichButton.POSITIVE).updateTextColor(PreferencesController.cColorAccent)
dialog.getActionButton(WhichButton.NEGATIVE).updateTextColor(PreferencesController.cColorAccent)
initSpinners()
}
fun positiveButton(func: AddSubjectDialog.() -> Unit): AddSubjectDialog = apply {
dialog.positiveButton {
func()
}
}
fun show() {
dialog.show()
}
fun show(func: AddSubjectDialog.() -> Unit): AddSubjectDialog = apply {
func()
this.show()
}
private fun initSpinners() {
setArrayAdapter(spinnerCourses, courseNamesList) setArrayAdapter(spinnerCourses, courseNamesList)
val lessonsAdapter = setArrayAdapter(spinnerSubjects, subjectsList) val lessonsAdapter = setArrayAdapter(spinnerSubjects, subjectsList)
spinnerCourses.setSelection(0,false) // don't call onItemSelected() on spinnerCourses.onItemSelectedListener // don't call onItemSelected() on spinnerCourses.onItemSelectedListener
spinnerCourses.setSelection(0,false)
spinnerCourses.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { spinnerCourses.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {
selectedCourse = parent.getItemAtPosition(pos).toString() selectedCourse = parent.getItemAtPosition(pos).toString()
@ -103,7 +117,8 @@ class AddSubjectDialog(_context: Context) {
} }
} }
spinnerSubjects.setSelection(0,false) // don't call onItemSelected() on spinnerCourses.onItemSelectedListener // don't call onItemSelected() on spinnerCourses.onItemSelectedListener
spinnerSubjects.setSelection(0,false)
spinnerSubjects.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { spinnerSubjects.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {
selectedSubject = parent.getItemAtPosition(pos).toString() selectedSubject = parent.getItemAtPosition(pos).toString()
@ -113,8 +128,6 @@ class AddSubjectDialog(_context: Context) {
// Another interface callback // Another interface callback
} }
} }
return dialog
} }
/** /**

View File

@ -0,0 +1,81 @@
/**
* ProjectLaogai
*
* Copyright 2019-2020 <seil0@mosad.xyz>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
package org.mosad.seil0.projectlaogai.uicomponents.dialogs
import android.content.Context
import android.widget.EditText
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton
import com.afollestad.materialdialogs.actions.getActionButton
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.bottomsheets.setPeekHeight
import com.afollestad.materialdialogs.customview.customView
import com.afollestad.materialdialogs.customview.getCustomView
import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.controller.PreferencesController
class LoginDialog(val context: Context) {
private val dialog = MaterialDialog(context, BottomSheet())
private val editTextEmail: EditText
private val editTextPassword: EditText
var email = ""
var password = ""
init {
dialog.title(R.string.grades_heading)
.message(R.string.grades_desc_on)
.customView(R.layout.dialog_login)
.positiveButton(R.string.save)
.negativeButton(R.string.cancel)
.setPeekHeight(900)
editTextEmail = dialog.getCustomView().findViewById(R.id.editText_email)
editTextPassword = dialog.getCustomView().findViewById(R.id.editText_password)
// fix not working accent color
dialog.getActionButton(WhichButton.POSITIVE).updateTextColor(PreferencesController.cColorAccent)
dialog.getActionButton(WhichButton.NEGATIVE).updateTextColor(PreferencesController.cColorAccent)
}
fun positiveButton(func: LoginDialog.() -> Unit): LoginDialog = apply {
dialog.positiveButton {
email = editTextEmail.text.toString()
password = editTextPassword.text.toString()
func()
}
}
fun show(func: LoginDialog.() -> Unit): LoginDialog = apply {
func()
editTextEmail.setText(email)
editTextPassword.setText(password)
dialog.show()
}
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linLayout_login"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingStart="24dp"
android:paddingEnd="24dp">
<EditText
android:id="@+id/editText_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="7dp"
android:ems="10"
android:hint="@string/email"
android:importantForAutofill="no"
android:inputType="textEmailAddress" />
<EditText
android:id="@+id/editText_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="7dp"
android:ems="10"
android:hint="@string/password"
android:importantForAutofill="no"
android:inputType="textPassword" />
</LinearLayout>

View File

@ -54,7 +54,7 @@
android:textSize="18sp" /> android:textSize="18sp" />
<EditText <EditText
android:id="@+id/editText2" android:id="@+id/editText_email"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="7dp" android:layout_margin="7dp"
@ -64,7 +64,7 @@
android:inputType="textEmailAddress" /> android:inputType="textEmailAddress" />
<EditText <EditText
android:id="@+id/editText" android:id="@+id/editText_password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="7dp" android:layout_margin="7dp"
@ -74,15 +74,16 @@
android:inputType="textPassword" /> android:inputType="textPassword" />
<Button <Button
android:id="@+id/btnLogin" android:id="@+id/btnLogin"
style="@style/Widget.AppCompat.Button.Colored" style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"
android:text="@string/login" android:onClick="btnLoginClick"
android:textColor="#FFFFFFFF" android:text="@string/login"
android:textStyle="bold" /> android:textColor="#FFFFFFFF"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>

View File

@ -19,7 +19,7 @@
<string name="course_heading">Studiengang</string> <string name="course_heading">Studiengang</string>
<string name="course_desc_on">Wähle deinen aktuellen Studiengang.\nZusätzliche Vorlesungen können später hinzugefügt werden.</string> <string name="course_desc_on">Wähle deinen aktuellen Studiengang.\nZusätzliche Vorlesungen können später hinzugefügt werden.</string>
<string name="grades_heading">Noten</string> <string name="grades_heading">Noten</string>
<string name="grades_desc_on">Project Laogai kann uaf die Notenverwaltung zugreifen. Deine Login-Daten werden verschlüsselt auf deinem Gerät gespeichert.</string> <string name="grades_desc_on">Project Laogai kann auf die Notenverwaltung zugreifen. Deine Login-Daten werden verschlüsselt auf deinem Gerät gespeichert. Diese Funktion wird "as is" und ohne Garantie bereitgestellt.</string>
<string name="email">E-Mail</string> <string name="email">E-Mail</string>
<string name="password">Passwort</string> <string name="password">Passwort</string>
<string name="login">login</string> <string name="login">login</string>
@ -63,6 +63,7 @@
<string name="delete">löschen</string> <string name="delete">löschen</string>
<string name="cancel">abbrechen</string> <string name="cancel">abbrechen</string>
<string name="select">auswählen</string> <string name="select">auswählen</string>
<string name="save">speichern</string>
<string name="mensa_credit">Mensa-Guthaben</string> <string name="mensa_credit">Mensa-Guthaben</string>
<string name="mensa_current">aktuell: %1$s\n</string> <string name="mensa_current">aktuell: %1$s\n</string>
<string name="mensa_last">letzte Abbuchung: %1$s</string> <string name="mensa_last">letzte Abbuchung: %1$s</string>

View File

@ -21,7 +21,7 @@
<string name="course_heading">Course</string> <string name="course_heading">Course</string>
<string name="course_desc_on">Select your current course.\nAdditional lessons can be added later.</string> <string name="course_desc_on">Select your current course.\nAdditional lessons can be added later.</string>
<string name="grades_heading">Grades</string> <string name="grades_heading">Grades</string>
<string name="grades_desc_on">Project Laogai can connect to the HIS Online-Portal. Your Login-Data will be stored encrypted on your device.</string> <string name="grades_desc_on">Project Laogai can connect to the HIS Online-Portal. Your Login-Data will be stored encrypted on your device. This feature is provided "as is", without any warranty.</string>
<string name="email">E-Mail</string> <string name="email">E-Mail</string>
<string name="password">Password</string> <string name="password">Password</string>
<string name="login">login</string> <string name="login">login</string>
@ -68,6 +68,7 @@
<string name="delete">delete</string> <string name="delete">delete</string>
<string name="cancel">@android:string/cancel</string> <string name="cancel">@android:string/cancel</string>
<string name="select">select</string> <string name="select">select</string>
<string name="save">save</string>
<string name="mensa_credit">Mensa credit</string> <string name="mensa_credit">Mensa credit</string>
<string name="mensa_current">current: %1$s\n</string> <string name="mensa_current">current: %1$s\n</string>
<string name="mensa_last">last: %1$s</string> <string name="mensa_last">last: %1$s</string>