- Forum-Beiträge: 648
18.08.2024, 20:35:44 via Website
18.08.2024 20:35:44 via Website
Hallo zusammen
Ich möchte eine Datei auf dem Handy speichern und wieder lesen. Die App erstelle ich in Kotlin. Dies mache ich mit folgendem Code:
private fun writeToFile(data: String, datname : String) {
try { //MODE_PRIVATE Daten können nur von der App gelesen werden
// var str_pfad = ctx.applicationInfo.dataDir
// var str_file = File(str_pfad, datname).toString()
val fileOutputStream: FileOutputStream = openFileOutput(datname, MODE_PRIVATE)
fileOutputStream.write(data.toByteArray())
fileOutputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun readFromFile(datname : String): String {
var fileInputStream: FileInputStream? = null
var inputStreamReader: InputStreamReader? = null
var bufferedReader: BufferedReader? = null
val stringBuilder = StringBuilder()
try {
// var str_pfad = ctx.applicationInfo.dataDir
// var str_file = File(str_pfad, datname).toString()
fileInputStream = openFileInput(datname)
inputStreamReader = InputStreamReader(fileInputStream)
bufferedReader = BufferedReader(inputStreamReader)
var text: String? = bufferedReader.readLine()
while (text != null) {
text += '\n'
stringBuilder.append(text)
text = bufferedReader.readLine()
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
bufferedReader?.close()
inputStreamReader?.close()
fileInputStream?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return stringBuilder.toString()
}
Dieser Code läuft auf meinem Handy mit Android 12 ohne Probleme. Mein Kollege hat Android 13 und dort läuft es nicht. Dort kommt die folgende Fehlermeldung:
java.io.FileNotFoundException: /data/user/0/ch.robbisoft.lustigesrechnen/files: open failed: EISDIR (Is a directory)
Wenn ich nun den Dateinamen mit dem Pfad erweitere, läuft die App nicht mehr.
var str_pfad = ctx.applicationInfo.dataDir
var str_file = File(str_pfad, datname).toString()
Das kann ich auch verstehen, weil "openFileInput" keine Trenner akzeptiert. Die Lösungen, welche ich im Internet gefunden habe, funktionieren bei mir nicht.
Wahrscheinlich mache ich grundsätzlich ein Fehler. Darum meine Frage, wie muss ich eine Datei schreiben und lesen, damit es funktioniert?
package ch.robbisoft.lustigesrechnen
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.speech.tts.TextToSpeech
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.util.Log
import android.view.KeyEvent
import android.view.View
import android.widget.EditText
import android.widget.SeekBar
import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.*
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.widget.addTextChangedListener
import ch.robbisoft.lustigesrechnen.databinding.ActivityMainBinding
import kotlin.math.abs
import androidx.preference.PreferenceManager
import java.util.Locale
import kotlin.math.absoluteValue
import java.io.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class MainActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
var rechte = arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
private var str_text: String? = null
private var tts: TextToSpeech? = null
private lateinit var ctx: Context
private lateinit var ac_ctx: Context
private lateinit var binding: ActivityMainBinding
private var zahl_eins: Int = 0
private var zahl_zwei: Int = 0
private var resultat: Int = 0
private var frage : String = ""
private var addi: Boolean = true //Additionrchnung
private var subi: Boolean = false //Subtraktionrechnung
private var mult: Boolean = false //Multiplikationrechnung
private var divi: Boolean = false //Divisionenrechnung
private var gros: Int = 20 //Wie gross ist der Zahlenraum
private var negativ: Boolean = false //Negative Resultate zulassen
private var hundert: Boolean = true //Hundertergrenze überschreiten zulassen
private var zahl_richtig : Int = 0 // Anzahl der richtig gelösten Aufgaben
private var zahl_falsch : Int = 0 // Anzahl der falsch gelösten Aufgaben
private var str_rechnung : String = "" //Rechnung für Sprachausgabe
private var str_protokoll : String = ""
private var str_proname : String = "" //Name der Protokolldatei
private val formater = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
private var b_schreib : Boolean = true //Soll ins Protokoll geschrieben werden
//Damit beim Aufruf der Activity anzeige nicht ins Protokoll geschrieben wird
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
// setContentView(R.layout.activity_main)
ctx = applicationContext
ac_ctx = this
tts = TextToSpeech(ac_ctx, ac_ctx as MainActivity)
//Daten laden
// val prefs = PreferenceManager.getDefaultSharedPreferences(ac_ctx)
val prefs = ctx.getSharedPreferences("lustig", Context.MODE_PRIVATE)
if (prefs.contains("Key_ad")) {
addi = prefs.getBoolean("Key_ad", true)
subi = prefs.getBoolean("Key_su", true)
mult = prefs.getBoolean("Key_mu", false)
divi = prefs.getBoolean("Key_di", false)
gros = prefs.getInt("Key_gr", 20)
negativ = prefs.getBoolean("Key_negativ", false)
hundert = prefs.getBoolean("Key_hundert", true)
str_proname = prefs.getString("Key_proname", "protokoll.txt").toString()
binding.edtName.setText(str_proname)
}
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.lay_main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
//Dialog richtig
val melde_gut = AlertDialog.Builder(this)
melde_gut.setTitle(resources.getString(R.string.m_gut))
melde_gut.setMessage(resources.getString(R.string.m_guttext))
melde_gut.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.lachen, null))
melde_gut.setPositiveButton(resources.getString(R.string.btn_weit)) { _, _ ->
anzeige()
}
//Dialog falsch
val melde_falsch = AlertDialog.Builder(this)
melde_falsch.setTitle(resources.getString(R.string.m_fel))
melde_falsch.setMessage(resources.getString(R.string.m_feltext))
melde_falsch.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.traurig, null))
melde_falsch.setPositiveButton(resources.getString(R.string.btn_weit)) { _, _ ->
str_protokoll += frage + '\n'
}
anzeige()
binding.btnEins.setOnClickListener {
var button_res = binding.btnEins.text.toString()
if (teste(button_res)) {
zahl_richtig++
melde_gut.show()
} else {
zahl_falsch++
binding.txtAnzahl.setText(getAnzahl(zahl_richtig, zahl_falsch))
melde_falsch.show()
}
}
binding.btnZwei.setOnClickListener {
var button_res = binding.btnZwei.text.toString()
if (teste(button_res)) {
zahl_richtig++
melde_gut.show()
} else {
zahl_falsch++
binding.txtAnzahl.setText(getAnzahl(zahl_richtig, zahl_falsch))
melde_falsch.show()
}
}
binding.btnDrei.setOnClickListener {
var button_res = binding.btnDrei.text.toString()
if (teste(button_res)) {
zahl_richtig++
binding.txtAnzahl.setText(getAnzahl(zahl_richtig, zahl_falsch))
melde_gut.show()
} else {
zahl_falsch++
binding.txtAnzahl.setText(getAnzahl(zahl_richtig, zahl_falsch))
melde_falsch.show()
}
}
binding.btnVier.setOnClickListener {
var button_res = binding.btnVier.text.toString()
if (teste(button_res)) {
zahl_richtig++
melde_gut.show()
} else {
zahl_falsch++
binding.txtAnzahl.setText(getAnzahl(zahl_richtig, zahl_falsch))
melde_falsch.show()
}
}
binding.editTextNumber.setText(gros.toString())
binding.seeRaum.progress = gros
binding.seeRaum.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (seekBar != null) {
gros = seekBar.progress
binding.editTextNumber.setText(gros.toString())
}
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
binding.editTextNumber.setOnEditorActionListener(object : TextView.OnEditorActionListener {
override fun onEditorAction(view: TextView, actionId: Int, event: KeyEvent?): Boolean {
gros = view.text.toString().toInt()
binding.seeRaum.progress = gros
KeyBoard().toggle(this@MainActivity)
return true
}
})
binding.chipAdd.isChecked = addi
binding.chipAdd.setOnCheckedChangeListener { buttonView, isChecked ->
addi = isChecked
}
binding.chipSub.isChecked = subi
binding.chipSub.setOnCheckedChangeListener { buttonView, isChecked ->
subi = isChecked
}
binding.chipMulti.isChecked = mult
binding.chipMulti.setOnCheckedChangeListener { buttonView, isChecked ->
mult = isChecked
}
binding.chipDiv.isChecked = divi
binding.chipDiv.setOnCheckedChangeListener { buttonView, isChecked ->
divi = isChecked
}
binding.swtNegativ.isChecked = negativ
binding.swtNegativ.setOnCheckedChangeListener { buttonView, isChecked ->
negativ = isChecked
}
binding.swiHundert.isChecked = hundert
binding.swiHundert.setOnCheckedChangeListener { buttonView, isChecked ->
hundert = isChecked
}
binding.edtName.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
}
})
binding.btnAnzeigen.setOnClickListener {
//Layout aufrufen Daten übergeben und anzeigen
val intent = Intent(ctx, Protokoll::class.java)
b_schreib = false
str_protokoll += getAnzahl(zahl_richtig, zahl_falsch) + '\n'
intent.putExtra("proto", str_protokoll)
protokollhorcher.launch(intent)
}
binding.btnLoeschen.setOnClickListener {
str_protokoll = ""
writeToFile(str_protokoll, str_proname)
zahl_richtig = 0
zahl_falsch = 0
binding.txtAnzahl.setText(getAnzahl(zahl_richtig, zahl_falsch))
val str_loesch = resources.getString(R.string.lbl_loeschen)
Toast.makeText(ctx, str_loesch, Toast.LENGTH_LONG).show()
//Zeitstempel schreiben
val zeitstempel = LocalDateTime.now().format(formater)
str_protokoll = resources.getString(R.string.lbl_start) + zeitstempel + '\n'
}
binding.edtName.afterTextChanged {
//altes Protokoll Speichern
str_protokoll += getAnzahl(zahl_richtig, zahl_falsch) + '\n'
//Zeitstempel schreiben
val zeitstempel = LocalDateTime.now().format(formater)
str_protokoll += resources.getString(R.string.lbl_ende) + zeitstempel + '\n'
writeToFile(str_protokoll, str_proname)
//neues Protokoll anlegen
str_proname = it.toString()
//bisheriges Protokoll lesen
str_protokoll = readFromFile(str_proname)
//Zeitstempel schreiben
str_protokoll += resources.getString(R.string.lbl_start) + zeitstempel + '\n'
zahl_richtig = 0
zahl_falsch = 0
binding.txtAnzahl.setText(getAnzahl(zahl_richtig, zahl_falsch))
}
binding.txtAnzeige.setOnClickListener {
//Rechnung vorlesen
tts!!.speak(str_rechnung, TextToSpeech.QUEUE_FLUSH, null,"")
}
requestPermissionLauncher.launch( rechte )
}
override fun onStart() {
super.onStart()
//Protokoll aus Dateilesen
val str_dat = binding.edtName.text.toString()
str_protokoll = readFromFile( str_dat )
if( b_schreib ) {
//Zeitstempel schreiben
val zeitstempel = LocalDateTime.now().format(formater)
str_protokoll += resources.getString(R.string.lbl_start) + zeitstempel + '\n'
// Auf null Setzen damit es bei jedem Start wieder von vorne beginnt
zahl_richtig = 0
zahl_falsch = 0
}
}
override fun onStop() {
if( b_schreib ) {
str_protokoll += getAnzahl(zahl_richtig, zahl_falsch) + '\n'
val zeitstempel = LocalDateTime.now().format(formater)
str_protokoll += resources.getString(R.string.lbl_ende) + zeitstempel + '\n'
}
val prefs = ctx.getSharedPreferences("lustig", Context.MODE_PRIVATE)
val prefw = prefs.edit()
prefw.putBoolean("Key_ad", addi)
prefw.putBoolean("Key_su", subi)
prefw.putBoolean("Key_mu", mult)
prefw.putBoolean("Key_di", divi)
prefw.putInt("Key_gr", gros)
prefw.putBoolean("Key_negativ", negativ)
prefw.putBoolean("Key_hundert", hundert)
prefw.putString("Key_proname", str_proname)
prefw.apply()
prefw.commit()
//Protokoll in Datei speichern
writeToFile(str_protokoll, str_proname)
super.onStop()
}
override fun onDestroy() {
// Shutdown TTS Sprachausgabe
if (tts != null) {
tts!!.stop()
tts!!.shutdown()
}
super.onDestroy()
}
fun anzeige() {
//Rechenaufgabe stellen
var fragefrage = getString(R.string.lbl_frage)
frage = rechnung(gros, addi, subi, mult, divi)
fragefrage += frage
binding.txtAnzeige.setText(fragefrage)
binding.txtAnzahl.setText(getAnzahl(zahl_richtig, zahl_falsch))
//Sprachausgabe
tts!!.speak(str_rechnung, TextToSpeech.QUEUE_FLUSH, null,"")
// Welcher Button zeigt das richtige Resultat
val was = (1..4).random()
when (was) {
1 -> {
binding.btnEins.setText(resultat.toString())
binding.btnZwei.setText(zufallres())
binding.btnDrei.setText(zufallres())
binding.btnVier.setText(zufallres())
}
2 -> {
binding.btnEins.setText(zufallres())
binding.btnZwei.setText(resultat.toString())
binding.btnDrei.setText(zufallres())
binding.btnVier.setText(zufallres())
}
3 -> {
binding.btnEins.setText(zufallres())
binding.btnZwei.setText(zufallres())
binding.btnDrei.setText(resultat.toString())
binding.btnVier.setText(zufallres())
}
4 -> {
binding.btnEins.setText(zufallres())
binding.btnZwei.setText(zufallres())
binding.btnDrei.setText(zufallres())
binding.btnVier.setText(resultat.toString())
}
}
}
fun rechnung(raum: Int, ad: Boolean, su: Boolean, mu: Boolean, di: Boolean): String {
zahl_eins = (1..raum).random()
zahl_zwei = (1..raum).random()
var was: Int = 0
var res: String = ""
val opera: Array<operator> = operator.values()
var tun: Array<Boolean> = arrayOf(ad, su, mu, di)
//Wahl der Operation und Prüfen ob es möglich ist
do {
was = (0..3).random()
} while (!tun.get(was))
when (opera.get(was).toString()) {
"AD" -> {
resultat = zahl_eins + zahl_zwei
if(!hundert){ //Obergrenze darf nicht überschritten werden
do{
zahl_zwei -= 1
resultat = zahl_eins + zahl_zwei
}while (resultat > gros)
}
str_rechnung = zahl_eins.toString() + " plus " + zahl_zwei.toString()
res = zahl_eins.toString() + " + " + zahl_zwei.toString()
}
"SU" -> {
if (!negativ) {
if (zahl_zwei > zahl_eins) {
var pool = zahl_eins
zahl_eins = zahl_zwei
zahl_zwei = pool
}
}
resultat = zahl_eins - zahl_zwei
if(!hundert){ //Obergrenze darf nicht überschritten werden
do{
zahl_eins += 1
resultat = zahl_eins - zahl_zwei
}while(resultat.absoluteValue > gros)
}
str_rechnung = zahl_eins.toString() + " minus " + zahl_zwei.toString()
res = zahl_eins.toString() + " - " + zahl_zwei.toString()
}
"MU" -> {
resultat = zahl_eins * zahl_zwei
if (!hundert) { //Obergrenze darf nicht überschritten werden
do {
zahl_zwei -= 2
resultat = zahl_eins * zahl_zwei
}while (resultat > gros)
}
str_rechnung = zahl_eins.toString() + " mal " + zahl_zwei.toString()
res = zahl_eins.toString() + " x " + zahl_zwei.toString()
}
"DI" -> {
zahl_zwei = (1..9).random()
resultat = zahl_eins * zahl_zwei
if(!hundert){ //Obergrenze darf nicht überschritten werden
do{
zahl_eins -= 1
resultat = zahl_eins * zahl_zwei
}while(resultat > gros)
}
var pool = resultat
resultat = zahl_eins
zahl_eins = pool
str_rechnung = zahl_eins.toString() + " durch " + zahl_zwei.toString()
res = zahl_eins.toString() + " : " + zahl_zwei.toString()
}
else -> {
resultat = zahl_eins + zahl_zwei
if(!hundert){ //Obergrenze darf nicht überschritten werden
do{
zahl_zwei -= 1
resultat = zahl_eins + zahl_zwei
}while (resultat > gros)
}
str_rechnung = zahl_eins.toString() + " plus " + zahl_zwei.toString()
res = zahl_eins.toString() + " + " + zahl_zwei.toString()
}
}
return res
}
// Zufallsresultate erstellen
fun zufallres(): String {
var zufallzahl : Int = 0
if (negativ) {
zufallzahl = ((gros * -1)..gros).random()
} else{
zufallzahl = (1..gros).random() //nur Positive Resultate zulassen
}
if( zufallzahl == resultat){
zufallzahl + 5
}
return (resultat + zufallzahl).toString()
}
//Testen ob der richtige Button geklickt wurde
fun teste(s_wert : String) : Boolean{
val but_res = s_wert.toFloat()
var res : Float = 0F
res = but_res - resultat
if( abs(res) < 1 )
return true
else
return false
}
private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){ isRecht : Map<String, Boolean> ->
if( (isRecht.get(android.Manifest.permission.READ_EXTERNAL_STORAGE)!!) && (isRecht.get(
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)!!)){
str_text = getString(R.string.lbl_recht_neu)
Toast.makeText(ctx, str_text, Toast.LENGTH_SHORT).show()
}else{
str_text = getString(R.string.lbl_recht_fasch)
Toast.makeText(ctx, str_text, Toast.LENGTH_SHORT).show()
finish()
}
}
fun onClickRequestPermission( iew : View){
var result : Int
val listPermissionsNeeded : MutableList<String> = ArrayList<String>()
for( p in rechte ){
result = ContextCompat.checkSelfPermission(this, p)
if(result != PackageManager.PERMISSION_GRANTED){
listPermissionsNeeded.add(p)
}
}
if( !listPermissionsNeeded.isEmpty() ){
ActivityCompat.requestPermissions(this, (listPermissionsNeeded.toTypedArray() as Array<String?>, MULTIPLE_PERMISSIONS)
}
}
private fun pruefrechte(): Boolean {
var result: Int
val listPermissionsNeeded: MutableList<String> = ArrayList<String>()
for (p in rechte) {
result = ContextCompat.checkSelfPermission(this, p)
if (result != PackageManager.PERMISSION_GRANTED) {
listPermissionsNeeded.add((p))
}
}
if (!listPermissionsNeeded.isEmpty()) {
ActivityCompat.requestPermissions(this, (listPermissionsNeeded.toTypedArray() as Array<String?>, MULTIPLE_PERMISSIONS)
// keine permissions gesetzt
return false
}
// alle permissions gesetzt
return true
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
MULTIPLE_PERMISSIONS -> {
if (grantResults.size > 0) {
var permissionsDenied = ""
for (per in permissions) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
permissionsDenied += "\n" + per
}
}
// Nach dem ersten Male
}
// return
}
}
}
companion object {
const val MULTIPLE_PERMISSIONS = 99
}
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
// set US English as language for tts
val result = tts!!.setLanguage(Locale.GERMAN)
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
val str_sprache = getString(R.string.lbl_sprache)
Log.e("TTS","The Language specified is not supported!")
Toast.makeText(ctx, str_sprache, Toast.LENGTH_SHORT).show()
} else {
//alles ist gut gegangen
}
} else {
Log.e("TTS", "Initilization Failed!")
}
}
private fun writeToFile(data: String, datname : String) {
try { //MODE_PRIVATE Daten können nur von der App gelesen werden
// var str_pfad = ctx.applicationInfo.dataDir
// var str_file = File(str_pfad, datname).toString()
val fileOutputStream: FileOutputStream = openFileOutput(datname, MODE_PRIVATE)
fileOutputStream.write(data.toByteArray())
fileOutputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun readFromFile(datname : String): String {
var fileInputStream: FileInputStream? = null
var inputStreamReader: InputStreamReader? = null
var bufferedReader: BufferedReader? = null
val stringBuilder = StringBuilder()
try {
var str_pfad = ctx.applicationInfo.dataDir
var str_file = File(str_pfad, datname).toString()
fileInputStream = openFileInput(datname)
inputStreamReader = InputStreamReader(fileInputStream)
bufferedReader = BufferedReader(inputStreamReader)
var text: String? = bufferedReader.readLine()
while (text != null) {
text += '\n'
stringBuilder.append(text)
text = bufferedReader.readLine()
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
bufferedReader?.close()
inputStreamReader?.close()
fileInputStream?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return stringBuilder.toString()
}
fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
})
}
fun getAnzahl(n_richtig : Int, n_falsch : Int) : String{
var str_stand : String = ""
str_stand+= resources.getString(R.string.lbl_richtig) + n_richtig + " / "
str_stand += resources.getString(R.string.lbl_falsch) + n_falsch
return str_stand
}
var protokollhorcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result ->
if(result.resultCode == Activity.RESULT_OK){
b_schreib = true
}
}
}
Gruss Renato
Empfohlener redaktioneller Inhalt
Mit Deiner Zustimmung wird hier ein externer Inhalt geladen.
Mit Klick auf den oben stehenden Button erklärst Du Dich damit einverstanden, dass Dir externe Inhalte angezeigt werden dürfen. Dabei können personenbezogene Daten an Drittanbieter übermittelt werden. Mehr Infos dazu findest Du in unserer Datenschutzerklärung.