From cf222658e649466c450e166186520ce7f2573bb6 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 18 Mar 2026 21:55:34 +0100 Subject: [PATCH] Initialier Commit --- .gitignore | 15 ++ .idea/.gitignore | 3 + .idea/.name | 1 + .idea/compiler.xml | 6 + .idea/gradle.xml | 19 ++ .idea/misc.xml | 10 + app/.gitignore | 1 + app/build.gradle | 47 +++++ app/proguard-rules.pro | 21 ++ app/src/main/AndroidManifest.xml | 47 +++++ .../java/de/oaa/xxxbdsmgame/Constants.java | 6 + .../oaa/xxxbdsmgame/data/LoginRepository.java | 52 +++++ .../de/oaa/xxxbdsmgame/data/LoginTask.java | 48 +++++ .../xxxbdsmgame/data/RegisterRepository.java | 34 ++++ .../de/oaa/xxxbdsmgame/data/RegisterTask.java | 57 ++++++ .../java/de/oaa/xxxbdsmgame/data/Result.java | 37 ++++ .../xxxbdsmgame/data/model/LoggedInUser.java | 17 ++ .../data/sets/AufgabenGruppeAbrufenTask.java | 57 ++++++ .../data/sets/AufgabenGruppenAbrufenTask.java | 60 ++++++ .../data/sets/FavoritEntfernenTask.java | 57 ++++++ .../data/sets/FavoritErstellenTask.java | 59 ++++++ .../data/sets/FavoritenAbrufenTask.java | 60 ++++++ .../data/sets/FavoritenRepository.java | 51 +++++ .../xxxbdsmgame/data/sets/SetsRepository.java | 64 ++++++ .../anzeige/AufgabenAnzeigeFragment.java | 30 +++ .../anzeige/AufgabenListViewAdapter.java | 163 +++++++++++++++ .../anzeige/GruppeAnzeigenActivity.java | 48 +++++ .../anzeige/GruppeViewPagerAdapter.java | 34 ++++ .../gruppe/anzeige/ToysAnzeigeFragment.java | 29 +++ .../xxxbdsmgame/ui/gruppe/anzeige/aufgabe.png | Bin 0 -> 10983 bytes .../xxxbdsmgame/ui/gruppe/anzeige/sperre.png | Bin 0 -> 8121 bytes .../xxxbdsmgame/ui/gruppe/anzeige/strafe.png | Bin 0 -> 7688 bytes .../ui/login/LoggedInUserView.java | 17 ++ .../xxxbdsmgame/ui/login/LoginActivity.java | 150 ++++++++++++++ .../ui/login/LoginFailedDialog.java | 46 +++++ .../xxxbdsmgame/ui/login/LoginFormState.java | 40 ++++ .../oaa/xxxbdsmgame/ui/login/LoginResult.java | 53 +++++ .../xxxbdsmgame/ui/login/LoginViewModel.java | 74 +++++++ .../ui/login/LoginViewModelFactory.java | 25 +++ .../oaa/xxxbdsmgame/ui/menu/MenuActivity.java | 71 +++++++ .../ui/register/RegisterActivity.java | 139 +++++++++++++ .../ui/register/RegisterFormState.java | 50 +++++ .../ui/register/RegisterResult.java | 36 ++++ .../ui/register/RegisterSuccessDialog.java | 41 ++++ .../ui/register/RegisterUserView.java | 17 ++ .../ui/register/RegisterViewModel.java | 76 +++++++ .../ui/register/RegisterViewModelFactory.java | 25 +++ .../oaa/xxxbdsmgame/ui/sets/SetsActivity.java | 164 ++++++++++++++++ .../ui/sets/SetsListViewAdapter.java | 139 +++++++++++++ .../oaa/xxxbdsmgame/util/PasswordHelper.java | 22 +++ .../de/oaa/xxxbdsmgame/util/TokenHelper.java | 36 ++++ .../main/res/de/oaa/xxxbdsmgame/aufgabe.png | Bin 0 -> 10983 bytes .../main/res/de/oaa/xxxbdsmgame/sperre.png | Bin 0 -> 8121 bytes .../main/res/de/oaa/xxxbdsmgame/strafe.png | Bin 0 -> 7688 bytes .../drawable-v24/ic_launcher_foreground.xml | 30 +++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++++++ .../res/layout/activity_gruppe_anzeigen.xml | 63 ++++++ app/src/main/res/layout/activity_login.xml | 69 +++++++ app/src/main/res/layout/activity_menu.xml | 60 ++++++ app/src/main/res/layout/activity_register.xml | 60 ++++++ app/src/main/res/layout/activity_sets.xml | 44 +++++ .../layout/activity_sets/sets_view_row.xml | 6 + .../res/layout/fragment_aufgaben_anzeige.xml | 29 +++ .../main/res/layout/fragment_toys_anzeige.xml | 13 ++ .../res/layout/sets_aufgaben_anzeigen.xml | 49 +++++ app/src/main/res/layout/sets_row.xml | 67 +++++++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../res/mipmap-anydpi-v33/ic_launcher.xml | 6 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes app/src/main/res/values-land/dimens.xml | 5 + app/src/main/res/values-night/themes.xml | 21 ++ app/src/main/res/values-w1240dp/dimens.xml | 4 + app/src/main/res/values-w600dp/dimens.xml | 5 + app/src/main/res/values-w820dp/dimens.xml | 6 + app/src/main/res/values/attrs.xml | 6 + app/src/main/res/values/colors.xml | 15 ++ app/src/main/res/values/dimens.xml | 10 + app/src/main/res/values/strings.xml | 38 ++++ app/src/main/res/values/styles.xml | 11 ++ app/src/main/res/values/themes.xml | 37 ++++ app/src/main/res/xml/backup_rules.xml | 13 ++ .../main/res/xml/data_extraction_rules.xml | 19 ++ .../main/res/xml/network_security_config.xml | 6 + build.gradle | 5 + gradle.properties | 21 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 ++++++++++++++++++ gradlew.bat | 89 +++++++++ settings.gradle | 16 ++ 100 files changed, 3548 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/misc.xml create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/Constants.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/LoginRepository.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/LoginTask.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/RegisterRepository.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/RegisterTask.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/Result.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/model/LoggedInUser.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/sets/AufgabenGruppeAbrufenTask.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/sets/AufgabenGruppenAbrufenTask.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritEntfernenTask.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritErstellenTask.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritenAbrufenTask.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritenRepository.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/data/sets/SetsRepository.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/AufgabenAnzeigeFragment.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/AufgabenListViewAdapter.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/GruppeAnzeigenActivity.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/GruppeViewPagerAdapter.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/ToysAnzeigeFragment.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/aufgabe.png create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/sperre.png create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/strafe.png create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoggedInUserView.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginActivity.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginFailedDialog.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginFormState.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginResult.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginViewModel.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginViewModelFactory.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/menu/MenuActivity.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterActivity.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterFormState.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterResult.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterSuccessDialog.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterUserView.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterViewModel.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterViewModelFactory.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/sets/SetsActivity.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/ui/sets/SetsListViewAdapter.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/util/PasswordHelper.java create mode 100644 app/src/main/java/de/oaa/xxxbdsmgame/util/TokenHelper.java create mode 100644 app/src/main/res/de/oaa/xxxbdsmgame/aufgabe.png create mode 100644 app/src/main/res/de/oaa/xxxbdsmgame/sperre.png create mode 100644 app/src/main/res/de/oaa/xxxbdsmgame/strafe.png create mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/layout/activity_gruppe_anzeigen.xml create mode 100644 app/src/main/res/layout/activity_login.xml create mode 100644 app/src/main/res/layout/activity_menu.xml create mode 100644 app/src/main/res/layout/activity_register.xml create mode 100644 app/src/main/res/layout/activity_sets.xml create mode 100644 app/src/main/res/layout/activity_sets/sets_view_row.xml create mode 100644 app/src/main/res/layout/fragment_aufgaben_anzeige.xml create mode 100644 app/src/main/res/layout/fragment_toys_anzeige.xml create mode 100644 app/src/main/res/layout/sets_aufgaben_anzeigen.xml create mode 100644 app/src/main/res/layout/sets_row.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/values-land/dimens.xml create mode 100644 app/src/main/res/values-night/themes.xml create mode 100644 app/src/main/res/values-w1240dp/dimens.xml create mode 100644 app/src/main/res/values-w600dp/dimens.xml create mode 100644 app/src/main/res/values-w820dp/dimens.xml create mode 100644 app/src/main/res/values/attrs.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/dimens.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/main/res/xml/network_security_config.xml create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..eaf91e2 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..002fb42 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +XXX BDSM Game \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..7d7ec2e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..00d25f9 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..59a533d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..37eec27 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,47 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'com.example.xxxbdsmgame' + compileSdk 33 + + defaultConfig { + applicationId "com.example.xxxbdsmgame" + minSdk 24 + targetSdk 33 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildFeatures { + viewBinding true + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'com.google.android.material:material:1.5.0' + implementation 'androidx.annotation:annotation:1.3.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1' + implementation 'androidx.navigation:navigation-fragment:2.4.1' + implementation 'androidx.navigation:navigation-ui:2.4.1' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..36b78bc --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/Constants.java b/app/src/main/java/de/oaa/xxxbdsmgame/Constants.java new file mode 100644 index 0000000..29a2ebe --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/Constants.java @@ -0,0 +1,6 @@ +package de.oaa.xxxbdsmgame; + +public class Constants { + + public static final String SERVICE_HOST = "http://10.0.2.2:8080"; +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/LoginRepository.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/LoginRepository.java new file mode 100644 index 0000000..d87877d --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/LoginRepository.java @@ -0,0 +1,52 @@ +package de.oaa.xxxbdsmgame.data; + +import java.util.concurrent.ExecutionException; + +import de.oaa.xxxbdsmgame.data.model.LoggedInUser; + +/** + * Class that requests authentication and user information from the remote data source and + * maintains an in-memory cache of login status and user credentials information. + */ +public class LoginRepository { + + private static volatile LoginRepository instance; + + // If user credentials will be cached in local storage, it is recommended it be encrypted + // @see https://developer.android.com/training/articles/keystore + private LoggedInUser user = null; + + // private constructor : singleton access + private LoginRepository() { + } + + public static LoginRepository getInstance() { + if (instance == null) { + instance = new LoginRepository(); + } + return instance; + } + + public boolean isLoggedIn() { + return user != null; + } + + private void setLoggedInUser(LoggedInUser user) { + this.user = user; + // If user credentials will be cached in local storage, it is recommended it be encrypted + // @see https://developer.android.com/training/articles/keystore + } + + public Result login(String username, String password) { + // handle login + LoginTask task = new LoginTask(); + task.execute(username, password); + try { + return task.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/LoginTask.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/LoginTask.java new file mode 100644 index 0000000..e95fa21 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/LoginTask.java @@ -0,0 +1,48 @@ +package de.oaa.xxxbdsmgame.data; + +import android.os.AsyncTask; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import de.oaa.xxxbdsmgame.Constants; +import de.oaa.xxxbdsmgame.data.model.LoggedInUser; + +import de.oaa.xxxbdsmgame.util.PasswordHelper; + +public class LoginTask extends AsyncTask { + + @Override + protected Result doInBackground(String[] strings) { + + try { + String hash = PasswordHelper.hashPassword(strings[1]); + URL url = new URL(Constants.SERVICE_HOST + + "/user-service/login?email=" + strings[0] + "&hash=" + hash); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + int responseCode = connection.getResponseCode(); + if (responseCode == 200) { + BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuffer response = new StringBuffer(); + String inputLine; + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + LoggedInUser user = new LoggedInUser(response.toString()); + return Result.success(user); + } else { + return Result.error(new IllegalArgumentException("Unknown User")); + } + + + } catch (Exception e) { + e.printStackTrace(); + return Result.error(new IOException("Error logging in", e)); + } + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/RegisterRepository.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/RegisterRepository.java new file mode 100644 index 0000000..6c3472b --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/RegisterRepository.java @@ -0,0 +1,34 @@ +package de.oaa.xxxbdsmgame.data; + +import java.util.concurrent.ExecutionException; + +/** + * Class that requests authentication and user information from the remote data source and + * maintains an in-memory cache of login status and user credentials information. + */ +public class RegisterRepository { + + private static volatile RegisterRepository instance; + + private RegisterRepository() { + } + + public static RegisterRepository getInstance() { + if (instance == null) { + instance = new RegisterRepository(); + } + return instance; + } + + public Result register(String name, String email, String password) { + RegisterTask task = new RegisterTask(); + task.execute(name, email, password); + try { + return task.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/RegisterTask.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/RegisterTask.java new file mode 100644 index 0000000..3b6a4b6 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/RegisterTask.java @@ -0,0 +1,57 @@ +package de.oaa.xxxbdsmgame.data; + +import android.os.AsyncTask; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import de.oaa.xxxbdsmgame.Constants; +import de.oaa.xxxbdsmgame.data.model.LoggedInUser; +import de.oaa.xxxbdsmgame.util.PasswordHelper; + +public class RegisterTask extends AsyncTask { + + @Override + protected Result doInBackground(String... strings) { + try { + String hash = PasswordHelper.hashPassword(strings[2]); + URL url = new URL(Constants.SERVICE_HOST + + "/registration-service/registration/"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + connection.setRequestProperty("Accept","application/json"); + connection.setDoOutput(true); + connection.setDoInput(true); + + JSONObject object = new JSONObject(); + object.put("name", strings[0]); + object.put("email", strings[1]); + object.put("passwordHash", hash); + + DataOutputStream os = new DataOutputStream(connection.getOutputStream()); + //os.writeBytes(URLEncoder.encode(jsonParam.toString(), "UTF-8")); + os.writeBytes(object.toString()); + os.flush(); + os.close(); + + int responseCode = connection.getResponseCode(); + if (responseCode == 202) { + return Result.success(Boolean.TRUE); + } else { + return Result.error(new IllegalArgumentException("Unknown User")); + } + + + } catch (Exception e) { + e.printStackTrace(); + return Result.error(new IOException("Error logging in", e)); + } + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/Result.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/Result.java new file mode 100644 index 0000000..3a3a9e3 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/Result.java @@ -0,0 +1,37 @@ +package de.oaa.xxxbdsmgame.data; + +import android.util.Log; + +/** + * A generic class that holds a result success w/ data or an error exception. + */ +public class Result { + + private T data; + + private Exception error; + + // hide the private constructor to limit subclass types (Success, Error) + private Result() { + } + + public static Result success(T data) { + Result result = new Result<>(); + result.data = data; + return result; + } + + public static Result error(Exception error) { + Result result = new Result<>(); + result.error = error; + return result; + } + + public T getData() { + return data; + } + + public Exception getError() { + return error; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/model/LoggedInUser.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/model/LoggedInUser.java new file mode 100644 index 0000000..d427a94 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/model/LoggedInUser.java @@ -0,0 +1,17 @@ +package de.oaa.xxxbdsmgame.data.model; + +/** + * Data class that captures user information for logged in users retrieved from LoginRepository + */ +public class LoggedInUser { + + private final String token; + + public LoggedInUser(String token) { + this.token = token; + } + + public String getToken() { + return token; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/AufgabenGruppeAbrufenTask.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/AufgabenGruppeAbrufenTask.java new file mode 100644 index 0000000..ba8c02a --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/AufgabenGruppeAbrufenTask.java @@ -0,0 +1,57 @@ +package de.oaa.xxxbdsmgame.data.sets; + +import android.os.AsyncTask; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import de.oaa.xxxbdsmgame.Constants; +import de.oaa.xxxbdsmgame.data.Result; + +public class AufgabenGruppeAbrufenTask extends AsyncTask { + private String token; + + public AufgabenGruppeAbrufenTask(String token) { + this.token = token; + } + + @Override + protected Result doInBackground(String... strings) { + JSONObject result = null; + BufferedReader br = null; + try { + URL url = new URL(Constants.SERVICE_HOST + + "/aufgaben-service/gruppe/" + strings[0]); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty("token", token); + if (connection.getResponseCode() == 200) { + br = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder sb = new StringBuilder(); + + String line; + while ((line = br.readLine()) != null) { + sb.append(line + "\n"); + } + br.close(); + result = new JSONObject(sb.toString()); + } + } catch (Exception e) { + e.printStackTrace(); + return Result.error(new IOException("Request error", e)); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + return Result.error(new IOException("Request error", e)); + } + } + } + return result != null ? Result.success(result) : null; + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/AufgabenGruppenAbrufenTask.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/AufgabenGruppenAbrufenTask.java new file mode 100644 index 0000000..69cb1e6 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/AufgabenGruppenAbrufenTask.java @@ -0,0 +1,60 @@ +package de.oaa.xxxbdsmgame.data.sets; + +import android.os.AsyncTask; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import de.oaa.xxxbdsmgame.Constants; +import de.oaa.xxxbdsmgame.data.Result; +import de.oaa.xxxbdsmgame.util.PasswordHelper; +import de.oaa.xxxbdsmgame.util.TokenHelper; + +public class AufgabenGruppenAbrufenTask extends AsyncTask { + + private String token; + + public AufgabenGruppenAbrufenTask(String token) { + this.token = token; + } + + @Override + protected Result doInBackground(String... strings) { + JSONObject result = null; + BufferedReader br = null; + try { + URL url = new URL(Constants.SERVICE_HOST + + "/aufgaben-service/gruppe/" + strings[0] + (strings.length == 2 ? "?search=" + strings[1] : "")); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty("token", token); + if (connection.getResponseCode() == 200) { + br = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder sb = new StringBuilder(); + + String line; + while ((line = br.readLine()) != null) { + sb.append(line + "\n"); + } + br.close(); + result = new JSONObject(sb.toString()); + } + } catch (Exception e) { + e.printStackTrace(); + return Result.error(new IOException("Request error", e)); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + return Result.error(new IOException("Request error", e)); + } + } + } + return result != null ? Result.success(result) : null; + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritEntfernenTask.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritEntfernenTask.java new file mode 100644 index 0000000..1953e17 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritEntfernenTask.java @@ -0,0 +1,57 @@ +package de.oaa.xxxbdsmgame.data.sets; + +import android.os.AsyncTask; + +import org.json.JSONObject; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import de.oaa.xxxbdsmgame.Constants; +import de.oaa.xxxbdsmgame.data.Result; + +public class FavoritEntfernenTask extends AsyncTask { + private String token; + + public FavoritEntfernenTask(String token) { + this.token = token; + } + + @Override + protected Result doInBackground(String... strings) { + try { + URL url = new URL(Constants.SERVICE_HOST + + "/aufgaben-service/favorit/"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("DELETE"); + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setDoOutput(true); + connection.setDoInput(false); + + connection.setRequestProperty("token", token); + + JSONObject object = new JSONObject(); + object.put("aufgabenGruppeId", strings[0]); + + DataOutputStream os = new DataOutputStream(connection.getOutputStream()); + //os.writeBytes(URLEncoder.encode(jsonParam.toString(), "UTF-8")); + os.writeBytes(object.toString()); + os.flush(); + os.close(); + + int responseCode = connection.getResponseCode(); + if (responseCode == 201) { + return Result.success(Boolean.TRUE); + } else { + return Result.error(new IllegalArgumentException("Unknown User")); + } + + } catch (Exception e) { + e.printStackTrace(); + return Result.error(new IOException("Request error", e)); + } + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritErstellenTask.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritErstellenTask.java new file mode 100644 index 0000000..5b7e33f --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritErstellenTask.java @@ -0,0 +1,59 @@ +package de.oaa.xxxbdsmgame.data.sets; + +import android.os.AsyncTask; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import de.oaa.xxxbdsmgame.Constants; +import de.oaa.xxxbdsmgame.data.Result; + +public class FavoritErstellenTask extends AsyncTask { + private String token; + + public FavoritErstellenTask(String token) { + this.token = token; + } + + @Override + protected Result doInBackground(String... strings) { + try { + URL url = new URL(Constants.SERVICE_HOST + + "/aufgaben-service/favorit/"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + connection.setRequestProperty("Accept","application/json"); + connection.setDoOutput(true); + connection.setDoInput(false); + + connection.setRequestProperty("token", token); + + JSONObject object = new JSONObject(); + object.put("aufgabenGruppeId", strings[0]); + + DataOutputStream os = new DataOutputStream(connection.getOutputStream()); + //os.writeBytes(URLEncoder.encode(jsonParam.toString(), "UTF-8")); + os.writeBytes(object.toString()); + os.flush(); + os.close(); + + int responseCode = connection.getResponseCode(); + if (responseCode == 201) { + return Result.success(Boolean.TRUE); + } else { + return Result.error(new IllegalArgumentException("Unknown User")); + } + + } catch (Exception e) { + e.printStackTrace(); + return Result.error(new IOException("Request error", e)); + } + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritenAbrufenTask.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritenAbrufenTask.java new file mode 100644 index 0000000..1763671 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritenAbrufenTask.java @@ -0,0 +1,60 @@ +package de.oaa.xxxbdsmgame.data.sets; + +import android.os.AsyncTask; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import de.oaa.xxxbdsmgame.Constants; +import de.oaa.xxxbdsmgame.data.Result; + +public class FavoritenAbrufenTask extends AsyncTask { + private String token; + + public FavoritenAbrufenTask(String token) { + this.token = token; + } + + @Override + protected Result doInBackground(String... strings) { + JSONObject result = null; + BufferedReader br = null; + try { + URL url = new URL(Constants.SERVICE_HOST + + "/aufgaben-service/favorit/"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty("token", token); + + if (connection.getResponseCode() == 200) { + br = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder sb = new StringBuilder(); + + String line; + while ((line = br.readLine()) != null) { + sb.append(line + "\n"); + } + br.close(); + result = new JSONObject(sb.toString()); + } + + } catch (Exception e) { + e.printStackTrace(); + return Result.error(new IOException("Request error", e)); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + return Result.error(new IOException("Request error", e)); + } + } + } + return result != null ? Result.success(result) : null; + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritenRepository.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritenRepository.java new file mode 100644 index 0000000..fa97e06 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/FavoritenRepository.java @@ -0,0 +1,51 @@ +package de.oaa.xxxbdsmgame.data.sets; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import de.oaa.xxxbdsmgame.data.Result; + +public class FavoritenRepository { + + private static FavoritenRepository instance; + + private String token; + + private FavoritenRepository(String token) { + this.token = token; + } + + public static FavoritenRepository getInstance(String token) { + if (instance == null) { + instance = new FavoritenRepository(token); + } + return instance; + } + + public void add(String aufgabenGruppeId) { + new FavoritErstellenTask(token).execute(aufgabenGruppeId); + } + + public void remove(String aufgabenGruppeId) { + new FavoritEntfernenTask(token).execute(aufgabenGruppeId); + } + + public List get() { + List gruppen = new ArrayList<>(); + try { + Result result = new FavoritenAbrufenTask(token).execute().get(); + JSONArray array = result.getData().getJSONArray("favoriten"); + for (int i = 0; i < array.length(); i++) { + JSONObject jsonObject = array.getJSONObject(i); + gruppen.add(UUID.fromString(jsonObject.getString("aufgabenGruppeId"))); + } + } catch (Exception exception) { + throw new RuntimeException(exception); + } + return gruppen; + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/SetsRepository.java b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/SetsRepository.java new file mode 100644 index 0000000..0946ab0 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/data/sets/SetsRepository.java @@ -0,0 +1,64 @@ +package de.oaa.xxxbdsmgame.data.sets; + +import org.json.JSONObject; + +import java.util.concurrent.ExecutionException; + +import de.oaa.xxxbdsmgame.data.Result; + +public class SetsRepository { + + private static SetsRepository instance; + private String token; + + private SetsRepository(String token) { + this.token = token; + } + + public static SetsRepository getInstance(String token) { + if (instance == null) { + instance = new SetsRepository(token); + } + return instance; + } + + public Result getAlle(String search) { + AufgabenGruppenAbrufenTask task = new AufgabenGruppenAbrufenTask(token); + if (search == null) { + task.execute("all"); + } else { + task.execute("all", search); + } + try { + return task.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public Result getEigene() { + AufgabenGruppenAbrufenTask task = new AufgabenGruppenAbrufenTask(token); + task.execute("own"); + try { + return task.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public Result getGruppe(String gruppeId) { + AufgabenGruppeAbrufenTask task = new AufgabenGruppeAbrufenTask(token); + task.execute(gruppeId); + try { + return task.get(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/AufgabenAnzeigeFragment.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/AufgabenAnzeigeFragment.java new file mode 100644 index 0000000..b43319c --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/AufgabenAnzeigeFragment.java @@ -0,0 +1,30 @@ +package de.oaa.xxxbdsmgame.ui.gruppe.anzeige; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.example.xxxbdsmgame.R; + +import org.json.JSONObject; + +public class AufgabenAnzeigeFragment extends Fragment { + + public AufgabenAnzeigeFragment(JSONObject gruppe) { + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_toys_anzeige, container, false); + + + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/AufgabenListViewAdapter.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/AufgabenListViewAdapter.java new file mode 100644 index 0000000..badb294 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/AufgabenListViewAdapter.java @@ -0,0 +1,163 @@ +package de.oaa.xxxbdsmgame.ui.gruppe.anzeige; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Icon; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.Switch; +import android.widget.TextView; + +import androidx.annotation.RequiresApi; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.xxxbdsmgame.R; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.File; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Comparator; +import java.util.List; +import java.util.UUID; + +import de.oaa.xxxbdsmgame.ui.sets.SetsListViewAdapter; + + +public class AufgabenListViewAdapter extends RecyclerView.Adapter { + + + private final List aufgaben; + private final List strafen; + private final List sperren; + + private LayoutInflater mInflater; + + private GruppeAnzeigenActivity parent; + private Bitmap aufgabeImage; + private Bitmap strafeImage; + private Bitmap sperreImage; + + AufgabenListViewAdapter(Context context, JSONObject gruppe) { + this.mInflater = LayoutInflater.from(context); + + this.aufgaben = new ArrayList<>(); + this.strafen = new ArrayList<>(); + this.sperren = new ArrayList<>(); + + initData(gruppe); + } + + private void initData(JSONObject gruppe) { + try { + JSONArray aufgaben = gruppe.getJSONArray("aufgaben"); + for (int i = 0; i < aufgaben.length(); i++) { + this.aufgaben.add(aufgaben.getJSONObject(i)); + } + this.aufgaben.sort(levelAndNameComparator()); + JSONArray strafen = gruppe.getJSONArray("strafen"); + for (int i = 0; i < strafen.length(); i++) { + this.strafen.add(strafen.getJSONObject(i)); + } + this.strafen.sort(levelAndNameComparator()); + JSONArray sperren = gruppe.getJSONArray("sperren"); + for (int i = 0; i < sperren.length(); i++) { + this.sperren.add(sperren.getJSONObject(i)); + } + + aufgabeImage = BitmapFactory.decodeStream(getClass().getResourceAsStream("aufgabe.png")); + strafeImage = BitmapFactory.decodeStream(getClass().getResourceAsStream("strafe.png")); + sperreImage = BitmapFactory.decodeStream(getClass().getResourceAsStream("sperre.png")); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private Comparator levelAndNameComparator() { + return new Comparator() { + @Override + public int compare(JSONObject o1, JSONObject o2) { + try { + int level = o1.getString("level").compareTo(o2.getString("level")); + if (level == 0) { + return o1.getString("kurzText").compareTo(o2.getString("kurzText")); + } else { + return level; + } + + } catch (Exception e) { + throw new RuntimeException(); + } + } + }; + } + + @Override + public int getItemCount() { + return aufgaben.size() + strafen.size() + sperren.size(); + } + + @RequiresApi(api = Build.VERSION_CODES.O) + @Override + public void onBindViewHolder(AufgabenListViewAdapter.ViewHolder holder, int position) { + try { + if (position < aufgaben.size()) { + JSONObject aufgabe = aufgaben.get(position); + holder.bild.setImageIcon(Icon.createWithBitmap(aufgabeImage)); + holder.name.setText(aufgabe.getString("kurzText") + " (" + aufgabe.getString("level") + ")"); + holder.text.setText(aufgabe.getString("text")); + } else if (position < (aufgaben.size() + strafen.size())) { + JSONObject strafe = aufgaben.get(position - aufgaben.size()); + holder.bild.setImageIcon(Icon.createWithBitmap(strafeImage)); + holder.name.setText(strafe.getString("kurzText") + " (" + strafe.getString("level") + ")"); + holder.text.setText(strafe.getString("text")); + } else { + JSONObject sperre = aufgaben.get(position - (aufgaben.size() - strafen.size())); + holder.bild.setImageIcon(Icon.createWithBitmap(sperreImage)); + holder.name.setText(sperre.getString("kurzText")); + holder.text.setText(sperre.getString("text")); + } + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + @Override + public AufgabenListViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = mInflater.inflate(R.layout.sets_row, parent, false); + return new AufgabenListViewAdapter.ViewHolder(view); + } + + // stores and recycles views as they are scrolled off screen + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + ImageView bild; + + TextView name; + + TextView text; + + JSONObject object; + + ViewHolder(View itemView) { + super(itemView); + bild = itemView.findViewById(R.id.aufgabeBild); + name = itemView.findViewById(R.id.aufgabeName); + text = itemView.findViewById(R.id.aufgabeText); + + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View view) { + + } + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/GruppeAnzeigenActivity.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/GruppeAnzeigenActivity.java new file mode 100644 index 0000000..7c7e807 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/GruppeAnzeigenActivity.java @@ -0,0 +1,48 @@ +package de.oaa.xxxbdsmgame.ui.gruppe.anzeige; + +import android.content.Intent; +import android.os.Bundle; + +import com.example.xxxbdsmgame.databinding.ActivityGruppeAnzeigenBinding; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.tabs.TabLayout; + +import androidx.viewpager.widget.ViewPager; +import androidx.appcompat.app.AppCompatActivity; +import androidx.viewpager2.widget.ViewPager2; + +import android.view.View; + +import org.json.JSONObject; + +import de.oaa.xxxbdsmgame.data.sets.SetsRepository; +import de.oaa.xxxbdsmgame.util.TokenHelper; + +public class GruppeAnzeigenActivity extends AppCompatActivity { + + private ActivityGruppeAnzeigenBinding binding; + + private SetsRepository setsRepository; + + private JSONObject gruppe; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + binding = ActivityGruppeAnzeigenBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + Intent intent = getIntent(); + setsRepository = SetsRepository.getInstance(TokenHelper.getToken(getApplicationContext())); + + gruppe = setsRepository.getGruppe(intent.getStringExtra("gruppeId")).getData(); + + GruppeViewPagerAdapter gruppePagerAdapter = new GruppeViewPagerAdapter(this, gruppe); + ViewPager2 viewPager = binding.pager; + viewPager.setAdapter(gruppePagerAdapter); + + + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/GruppeViewPagerAdapter.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/GruppeViewPagerAdapter.java new file mode 100644 index 0000000..99cab10 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/GruppeViewPagerAdapter.java @@ -0,0 +1,34 @@ +package de.oaa.xxxbdsmgame.ui.gruppe.anzeige; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; +import androidx.viewpager2.adapter.FragmentStateAdapter; + +import org.json.JSONObject; + +public class GruppeViewPagerAdapter extends FragmentStateAdapter { + + + private final AufgabenAnzeigeFragment aufgabeFragment; + private final ToysAnzeigeFragment toysFragment; + + public GruppeViewPagerAdapter(@NonNull FragmentActivity fragmentActivity, JSONObject gruppe) { + super(fragmentActivity); + this.aufgabeFragment = new AufgabenAnzeigeFragment(gruppe); + this.toysFragment = new ToysAnzeigeFragment(gruppe); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + return position == 0 ? aufgabeFragment : toysFragment; + } + + @Override + public int getItemCount() { + return 2; + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/ToysAnzeigeFragment.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/ToysAnzeigeFragment.java new file mode 100644 index 0000000..2baf2f0 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/ToysAnzeigeFragment.java @@ -0,0 +1,29 @@ +package de.oaa.xxxbdsmgame.ui.gruppe.anzeige; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.fragment.app.Fragment; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.example.xxxbdsmgame.R; + +import org.json.JSONObject; + +public class ToysAnzeigeFragment extends Fragment { + + public ToysAnzeigeFragment(JSONObject gruppe) { + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_toys_anzeige, container, false); + + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/aufgabe.png b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/aufgabe.png new file mode 100644 index 0000000000000000000000000000000000000000..21e9068ee23ea2077d07ba72dd3e63f0ec490e70 GIT binary patch literal 10983 zcmY*fWl$VlwA=+27GK<9kq}ti-5r7k3lN+D!8JI$xO*VMArOMQ6WkLdxN8#J2`v8j z-j7%Hrs~eAJ2iEFobI{R)zi^h8cMiWlvn@&09Qp>Ugx>O|65@6=X;-|#SZ`g7;Y~o zr==n%2X%FKd1vov0|2lj2PR7>_eqmStbZkm)uld!ZY34Ogn~*L5_AR_3|XAQ^=V#l zK7=yks>H;8*)VcFwXNOX*s3TGEr)_1;Vo^#tZmBC>o3AddDh%k`ux}L+^=b%{N=9% zP*dS$Zxlrd$UJWJYOhIvX!Z$=%sj6(-;vgzQkz|$o;`4m)^{8{Qa`BnSi?1$a;@4d z2wWf&jz*$8fm_^cYFK9?XJImAOhLcf4$rA}Qu!0o? zP?%*g8*Z&|UE8YAKd1=QeC*YdF&=TvnFA9V$`Z0hC21>x^bDh>s*)`k)OXzQpK7)> zlhv3FKylRA-(Hbv58bIWdEkBZj{5~M``XD6%B_w8jd3>**uN<8Y<@pc)`oJyi7{x(}Xb!8xL`vtoS%n>q|Srfq=T1 z@;_7?8xV3f?W?w-_R!V9yXLQ}U35ia?(Kc7hR|nR&@I)JoBa79hNp_U0>%y+7|hB>pe+0q0DuBi(w5)*&R&M}q!W;oIU?3?IJ-^S??g+pj9C+CQdJoG;NVnQ+1S`LH@9T;-{!%G5AHz4GSeJZ7V>_pb)7-OqMS~q6;KB)rWvkAx{cutSvhDY}*K1c)Z3kRQM?0O|`cKD|3e#7UHEHL~u5-Olko zB|zU!??X;KW-J*?WtEJZNM`-ydvaXZmNOeVfW%*7DF?54X~NmqJn0h?7-(*(BZC%@ z1coS5lH);F*c6?6PPsnLOPo(q0@BrxT_Sf3X+eynTn4H+5ye<`#OYYz3cb@B*`>i^ z>FkTU#qXB@d1_){ZL$_G8lenC>i)y&VN9|;&U*iF$+x>^-L?B~UFh)K6-mLUE#5Rg zvCM&PA7 zv5+TuEFr4QX)`8M2v^igbEF_c-oUBm$N3Y<8gw}Pz${^l!_Mntoi0cXp)P%ybUK<4 z9iSe`Grtt3v-%+e1F%4;xqEj0qhLvFl=Ha`uNatP9<_;&A0V(h3v4 zUsZbnmy*9AWEc3xP6JuP!=!WTB;nh&^pnVM+~2s(?LV3Rn?nUk2l~Ltm)UnV7}SZys3Fad>dS2})seK$tE#9_Op2d*ruwh|I@n*7`$Rns1z`yu`T?bMmLx>ujR*Ac5E03h#RwfEGyR(;(vYZym=-P}+?q<~$}fDdXqjMW(NCCW1>1SvXx_8l`1+NQ(C_;^=~Dov_ML5u-mlGQ2nSqVE`o`bh~&SMh5FMufb`JJ{|X4or{xX^9SDA`xkpkn5$RzhvqpGP>2^ z%`Djpn`L#&BmE~zGCP)r7Px@=14SB-yeQHDqQ~&dt6tLehgA0=7Qu~6t8IC@N_9$b4B_mgI{{jeC4?< zCSVx*RykqZ#m!$o0qRVpZ2Oo`gjS#qDGMFel3$<-*=W)O{s%SvhDW#BFZx<+3Uo8G zeuEru=}5M3el6i|ak@LPWs9o8`POJbqVOb8h%)6RZiqBxzqG5^>HahG?-J63{6Nx| z%a~hYrMqi7QF-6CV+p$SHd8OD(_&O28p5G>%jt;J zKQclH7kHw-t)~@LSim{8L6Iq`5<<6}wfJ3D6e)N2qzdnj>3BJdl{F-N%FOe4p!o^w z+c9N33etb$a(EY;2_7NqS-|>*Q~e$%uv)a`lVbwIfVREZrX54b+e5$zABxqM$<+Iv z$vwhBT}4@%{#dQ;o6vW5`21F!g=Zz-!%H(NgO~4}hV)RLA;66+e6rt#mXRUvlh;a8 zk+2_Q2eRflC=nV2W+l3^qM-{1CsjIOk^`Ol0Q+*7$TlDB;chb{O#|_|gj}ZFaIJ^~ zlvX!q96lPEcTc34K=t28Lws7_-N>TrY4PjJi^B7jR(_VkX0OZ^R)Wkg2P6`@vIeVF z=}v5(ueE7T4se9@cNWlUkxJCuu!i!KD}Y_eB~f_trkO-8;fC&ppLxDsBa!CGe%wOE zxXPs`qa+3Ow9X6IGT}&98sQ6UtRH|RxP=~$T z@Y;8}K&nsjD*%it*@Z<Zu~u2}2>y?##8i`toF_z7uUHPTx+sO=S2h3AWb2OU# zWPPA5dWr3xBX7SI?b-CL-cGFV#}{P=qUO1!rta_*f_de%ufVj6*M+!ugVPP*k zfnd)9{-VYUs-d5_6~~nR%%@(&R5b!+`C8%JeFk>1PwW|7Zn|tvO!kvT1#)g|nE&!2 z$W~u{Oz7H$7$1&6&oOKkfjR zwaaKWryHJDXUQ~;9wJM`ogXJ)PpAKUujo!|XnHv9DyTyqH`Wxc#Mi7n)hPb_*ryLN z;>r>Pn@qT_;~yNZpIDR}yVl!(%Hk7mn%JIOet-WjK8B5x<7_ko&tvfhl{nT-=o=sF zte?PTZ$FAn_`mR_UiXNI`*+)VHL?JJYWEMIm}u0ZWYfph=INV+OtE&fPk2{7-^c5_0&2jz(w_Ymdkhuw5*0hOfsmZ0*nUAS?c5~;P=3A4BM)_ zsUK^^ou;cV`c(lN>>Fj!O(LW zKkp7zr){4Yphb*|#&4rFySi%e)Tp}+t$H2%*Yr%77;lnDeNTA(Oh8RSZ)r6F`Kz?z zBvLD^1-vCr$yI(buFy4rR^T`I0RrC=0W*}T==!D|UbDt^bf?%RDSw;MDfz>;OI)s! z*-*dkTj3&vIOYczN9}eQ7MYY4{FipDtc=AQ7zOxOwm93n#aPUi+3T=yP~Tdhdgq0i zz~h6l-ZSPbcr1rAfjsEr67AjpT$YAgZAtCgrrszJKXOXSR7y`vGoIpPdQAH?mP#p| zWqsLhQNo5BYl|B!!iLXD3T(xVB zuIu!eN5Iz8n7N3Px^^|DvEXvY_f-y)_6uiJH$MaBQXWL>$J+#_&S5?p``-$ zKM$|_eI}@AcACCKqnYBS84a3UQsd8A&n&Iqu2SaRY_UIUdLHhxFpD8Qo5x*=EZ;G< zIX-W1AmfH_3Wd|_m!m^w7FqdU{vxg<&N6#9^0!KI_1@i&ed)Lhb}q8ECnXK>-&%-? zW|YiB-Vn-#qP6L!|R7YmjXrtP}S9`-|Eh+??ag}&R#HpH; z8mrcPk6+_W?mAT8-KNGtWY=fgUsB=en!2;Hg5tY((X*O6{}KmXDfG?kY!1`+-H?gm zqyy5*zE=%BG?oUFb;C8+rJ#HZe}MXp6IaShWbDXkK1WAY05p1VRaaF zPk4M11S9-NMRMvH@r$jVrbd^d$TG}eCDf*H9ZCX^y%arZuX#hDaQVmkk(n`=!8&G6 ziRVnp$>eLV(&8ggy_XC2{N+VM1bU?Q@l&MT3;no$=B0%BIF6vSQMLg1A!Sn#xLg+l zb=MA6AD;^Pvx};}ZMaNeaSW#OOQxfS6sq~S(>3`phc&`0P#G$X8W*yNCf-C0z(J*2 zniVdazBNz-1Zb4tvz4)s8k!LQEtmAbj(9#YwhHY*Q4RXUu`xX|v70gPd<^-7$@NDl zdwgCFesl2R`d83t{;AUM=L8igX#gc|PZ)!-ag2`kYuXu{42}pG>>J1xpn1A7BXRVr znVU!qR;3!^>c|3wp@ny-p-6+sRZY^2(0_U0*)KHpUW5l~wuoQ4hDOplpY|h|Ur*D{ zmKFRWUis#Ph1juJj**qo=44Y;bZl~VTaeWMJr#?w;H^*3QYfPFy|JOvl`108|1GW0 zxA8<;`?@Fd)IIyea8SW7oLF&L)?$8P4cCW{b?^H8Vy87YT#pwOINN>Tm03AZD6^C6 zK;;7|!oRU0K2*9C3=pibDTSGXS$1L`gIXkPZzKcm#$jfULn6oR%^HpGYhtlVAiGr> zQbN8A7sE{AZS&il)d|ZGm`$wN%|~35>pHI^lUp8>*w|j7$H0W@Ag2V36NgDV%IuV0 z@7}tmY_%v9F(M*K|Erdj<2ieFEaRqyj%^Mq-$#$pz05`GWI9bu?@_d3xkNPtR&SE1 z7OEPmbhvq)qHU|Y5OzaW3SkZP!0LMSS;Rb@`Nii$O{05bxXnb+Y7 zeKFuy6PV5!<@}+j4HUoiY<+}`qU{9mQG2JGBB2oObu-#mY7h4 zwk{<&Xwt*t*FUipQ!Df7TLv;Gs=+3bSlqtA#BpAc<=;KaSu6fe3%4@w~3&d3*j(k+ZG)Hk#?pHI+{6rh`aSza+mqZL_OsQ6^ zf)&kTm#tCUBJ1Il>H#3k95wm~<;CZX{MSTg6# zo^RiB+2OPX@4egc*e5_Mrt9$biHOMST{&mpfq>O=3^^m4?eG-= z=U6J>tIc1U#JkV>6bvS=rqM?`#H~BI1M)nj2bkAKpSP2@P_VuU1g=D!b{OECULFk) z5t1CPB)a|zN6q%tN316D6M4ryzbYX*bc~7(n8}hHLpQlQ=Pk*3U;QeoeKxADIMkYz zP#W%+-nmPKqthD;|aPPW$2+OTmyi)3Mc~T zOKhFH$UKRU<63&_tJyC)*C___41P|0Li-RU?POY`)p0>X)q0;Iqd!e(ws3Zjb@Gs- z{r4IR`(gwwokuH2APs~6B;Y`CeBE^PScY8<>i1IsAFrUvuJh9$pfyTP5~X9J+WMa~-NJ#$>sY6zPx3fm(!qE#i7e*+{o8gOW3-1-+Jhbd4C13(eu!N0I0 zk>y-DgY|h5QS0zl*1r3+9^TA6-w_Di!-js9pLgrUZH9YSohAREeBsN&ofJnnY?9bQ zE1a}|&_3fI`%B2oQkr#G{6+99)&bB?hR@SU&M48{TRb|v{jDZPw*|kl(5a9&9~mi@ zBi4&Ea9lTUUeE@w*tm}l)tMi>%i{Qg7Xx)~VH#PA?_nr7x{E?4O9*|}1^kTk?pEH| zE^{O-F0s2*TVRi#0%;B6Z#~)O{&D@gxVhm%7e_}MWAf(*QbzFN&;u(wP{WXCTgCN| zO`Mxys@&KsVR&NS5k?4rPKuy^GD<)*G^|_8cm!wOe5YL}z?7|aoxd%zE zZ2S@@cmq(R- zyHnLb-PGKlKczkEcy*;ec~d-}?}qT9DPvHP>w;>L^h%45VMNNAZbq~wFu?onASUV$ zANj6|q{39qsw&6#ou2lr>lZE~w1yv%73ShxoIu)RZ}|@6-NQ)ud~O1bfJD0f-iLK( zhrx{j@#q$7IwTWA&@q!v(TVTC)a)pZopmPX6rR-D>a6v^;wdY-b5(3_i9U`CqUELN zK(|e;S26bdTuF=`eO z_Z(kKE{TyL$N0Xdp}t2i(XJYnjx<0vzQ1pNYMg&@h0@&&U-UN1oTS>-@%&|QURGkK z7AdOvH7ZZ5oa*n+eSU#l)jF3*XBYcJXT&Sks@P_3GPfsL<*q-d&}`F(BfCMT*t>Xk zVCjQG6H__%oQgHR9j?bkL|40g#Qej#q^aMk)%`J@)QH6Oyz$v%g1wb*a+m2!Zf8l5 zG+CFv+|rR^7xtlu5hmdD&oHDPKhZ$}Ivnk2vo92Vq}wKszTNfycMHw6XxVTT%0%ef zysWLa+mQ?3n@2)++@{q%I!+!m&o>e&ck|=z3a1T*a<|nB_okU+a6H$CvhormKX=J! zf!${SEfm6&ZArtlrz3*s{%^RL&0r0K<;>4ha{o5<9u2O4=wCR((L7%7m8D=Wy(t{C ziJKXUA#r^BDC;`M)3>T_`1qZ@vqEVm(zO<0-*EmRwOdGO=u964poy|B4c?y(#1x)Z z)=1XICwti>HiE1uf=}Jvzeh)O_q9_|OInMveMan2bj|XJ3t@lSfu-uLQ1R6TcJm=; z!&x5Gc5fvnGxqnRkMqS3*`zrLaL)Vqv_&jU{OA{o*sVq&h_f}nQd9qOmHUaYyMUc> z5GC$NTX34HC=1S*T1fP7HLd9vNGlY|PZ7`PPzCm(b_$A)|9-YY*a_NcTJr3MWp?pm zG~YAldX6uss{mEh5T-!745yt4-0%F;-F#~Y$TTAl_v`m zo2+C*Baep>xTLm@9EuY8I{wMjg=#zLQ&U`N{l`BEiWYeXzakPjM*@$oL!T}GV6>Hm zB{-dXS6;)ESSk^Pb3y;R-~B@+U$>TM=-;IhC`xKbPg-DjZuj-Pl<4%C#&Pkz3rhIe z-WtzJ$(b#rzZF;MJ$cN`!>pg;Y=V!OzG}|@*c_InQ0%Z9h2 zh9M)HtBG#Y+gQRzaxM>hq`PA+Xenh}@T6cCt(Zky6Hb)NWZN#K?E(#Eq6yy-#p z@;-5LwT9&{oemxkXXX)^A`5vZS}mc_P8GtgzBZw$w>C}kQX81Zm(*Wt$qit^n1^D| zzHK$~qMHkP2;n+Z*$yx9m$1JkiEh&yD0#0W;b{qlfM8qib5K!8++^{?_Dsy~bLrAC@uNgUT*|6=y1bEw-P$SCcTyvo<2)Lm#A zJ(;C4&c%=mCGEQU`r6eo{KSh;>POPYjup6lY|t56@N-hYnb%n^;MtMi6@KKwkha;$ zy;!_=2CD1GfHBvm`)!omZw?D@y%IYJQOeV{_y`O9W0Py^OS%4v9;tpo= z5EW{DU>eQ+dis}|!~Dd;rhNAZOTz;ak@UCKm@aFrxeis7`*FLUa-D zOn{w3hiXa8kV6!br(G|36essow$Ida;GO7vN;uU|CyX5z@g>}bQ=ckE@>iqiuD@;- z`{Y8aCdI|0v-t060p4*59=k250Q0TBLlzvT+Za7cPBvo@YH>EQUc(d2=v@4PKTT3%!Hd%6u9}*{-ZZ@#x>6cFV`k7W9*0 z>i9m4ZNFf=7H7|F7ut{h{`_XN4_?l04$~+9sPq^+F_ZRH?nFqTgp4-~ReHX!rmhpe zmk5a8w*1ibX*mzu2}XM_P|yhw%M}~W>$K%_xxc~ffoKL7N6|5D+_zGWjmWuewRh9e za-3y2p)yUbyGoVLB=dv^m2l!lIH`I~Qb}E+rOlE$sLCz-Chj4Jr-Weki%L`7kzOR> zEh0NwiHyw?6O?!p-Q}jd!{yb+Lx0F{YJzgYqhAh9g}wZ{|0k@KXos`6C`+nY`eG8; zf3baj>jHnO*U=vPVj3wm%>1KP>PT17tWb?4O>UXCrVn%5`&EZ6j?Q}!d}>?Ia$i>7 z=lV(XXGRzA#MjE9YN{xnNk^=IAd3gl*z#)FMg2UU$L4e_4l)0p`_<23o857nn;VWT zrM&CVNFgyLt0#od7Yz6lAgF5Haor6F=-$M4O?SV{!2YP7@dowdBum^PT9fmSEcPrY zZb+Is1myVqdqA?uA?8F%tE7{KKNEcdTbOSR!Kf)KFU8SX;?Q?iO-ig5vq?X;%PP2> ztG_XYjoxzE3)EP4wA#yG*HoJIh>TULo111p9NT0&T0!ae=tvf6xFI%yaJawKC~1C~XF6ckMhEo<+l zS@E(2mnx{{4AXm&o2@}AgM6ZW%@iuxuK*40F;>!<=q~Sa{}zOvO-lftKphsI*%$_* zwT*=iTYV<^4Yx&lMo-kXc4&GvJ^DOe5WM`DiC@z<9#OgxV&6O`eHq=!6qqXFA>vX= z=(z*|T*51MuY`-}7&55~ODwOaw%M&k z-I%L1FHZT_lKW(u^j<_gv3=QI@nGl8V(VmfHWxdcZ{tR;^qw+%mO|iAG=LzB+RMPF z27WCHH{dR{5y^5m!^c-TYAdzBb$N@U*FsM}Tda2YMJ6G?&gHwgqI8L7b(Wm~yM#r@ z<%VF5V>~E5d*D3%rur}39V%}^L(LDmrQ?`F=8hs7I3Dsia7CA9Oa7kPgji4jhU>BG zx|0INg>h27JhO`0W7&|il<}b$Jn@RR!CfGqqg|iJn@2rtI0WqgE~Rj$Hoh@fD8U~F z6GbpZG07_RbN`_)`VQnR414aiWMMFp^x?LN>esegfICiix=@lRx~(+n+ZD$8mQ)&s zU;-rjX|8Eso`C>B7J>skls~&$Vs-L{QPsd~(yTx$>nZ4E5|=CmPkcsmC9Q9h=`fBB zu$1X^==FM19Z-|#j~SsXB!)M9dYP%OKN%@uG_!78!!;8VcA7tk*#iLFXFIC&?k`T= z(%M#FdaiR$(iy4+#9$|sEJ4Ur{}|y-X&0Z!y&WMcMdR&TCjf6;N52EsYvUqcJHlXZ zyuEMWP3H}#XJFhqEUAYCa!Jd=q-2pViuD$&DuzsjLO6vHuo^JHq7;WJUkKnAVle_x zj`nU_1dQZsiZ0n^a<0wb9QRvmsb+Lky(eG2J2EZt)qD@=hr80aO7wc9Ss8oVpnA8) zaf)mhnwMTFH(h#a^A<);e#d$BuWNDT5pC)Rr!w7^)&|wj!dNNeft<{4cE7#DSbCP$ zu4lG#<${f}Yus{(gla%O0;k^Q?R8ES2#Z5iinp}VBHreV{TTYT3ji3~VTrzZ#`jx6 zMbf~b9eyNjkQaf=J7BOg<+NwLAM_abh=T0h6~@pYrqS5#n2V;GL?cpN$Lhc}Oq|=5 z#q_rWxATU&_DlMsp6s^x!ui%_yE z-aQvQjS3x~MebYQyy}~H|J4ajvBA6%%0@FDs0x5GlW?L00eyH}X(NEdc0;oGX}>~A zJWHcEmsp0d7-L}i{w%HU%|`^KO&XOAQ+IBZr~B9id1hgcvLyApO|NH0o{Cf^osQPw zT{JX1?d3;7)l12%L_)6Ur;Zyhy0HnGn!u$3qu!I@gk`$&WYnG`m`q_-1?+4MG?u8E zex#7FRG}}V0-bdHChihNo0gQ$(wB-f_JX1E>W_vwtQ+f?c(XxN3u2<3q7#DE`=5`9 z=<11c&CmqD%wR)&NlstOd&F-qPr}e5;_DpKw)35^Wu;lvFs2wt$RJj|L>fWet5uP1 zf&CXD&Qyc=<)8hgsx^`tZg@|30rt|-jga0o^_`I1txqZqA`-SD1 T_;T;Rc%+JghJ2N*dC30&Ywx`F literal 0 HcmV?d00001 diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/sperre.png b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/sperre.png new file mode 100644 index 0000000000000000000000000000000000000000..e6fdb50127a2310b98fa54d8a7de9355b5d452f2 GIT binary patch literal 8121 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 TB-8TY00000NkvXXu0mjf;1+r- literal 0 HcmV?d00001 diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/strafe.png b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/strafe.png new file mode 100644 index 0000000000000000000000000000000000000000..094c27975caf9c6f17bec0a91d9bf92f192af357 GIT binary patch literal 7688 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 literal 0 HcmV?d00001 diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoggedInUserView.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoggedInUserView.java new file mode 100644 index 0000000..920a86d --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoggedInUserView.java @@ -0,0 +1,17 @@ +package de.oaa.xxxbdsmgame.ui.login; + +/** + * Class exposing authenticated user details to the UI. + */ +class LoggedInUserView { + private String displayName; + //... other data fields that may be accessible to the UI + + LoggedInUserView(String displayName) { + this.displayName = displayName; + } + + String getDisplayName() { + return displayName; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginActivity.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginActivity.java new file mode 100644 index 0000000..c531eac --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginActivity.java @@ -0,0 +1,150 @@ +package de.oaa.xxxbdsmgame.ui.login; + +import android.app.Activity; + +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; + +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.example.xxxbdsmgame.databinding.ActivityLoginBinding; + +import de.oaa.xxxbdsmgame.ui.menu.MenuActivity; +import de.oaa.xxxbdsmgame.util.TokenHelper; + +public class LoginActivity extends AppCompatActivity { + + private LoginViewModel loginViewModel; + private ActivityLoginBinding binding; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String token = TokenHelper.getToken(getApplicationContext()); + if (token != null) { + openMenu(); + } else { + initUi(); + } + + } + + private void initUi() { + binding = ActivityLoginBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + loginViewModel = new ViewModelProvider(this, new LoginViewModelFactory()) + .get(LoginViewModel.class); + + Intent intent = getIntent(); + + final EditText emailEditText = binding.email; + final EditText passwordEditText = binding.password; + final Button loginButton = binding.login; + final ProgressBar loadingProgressBar = binding.loading; + emailEditText.setText(intent.getStringExtra("email")); + passwordEditText.setText(intent.getStringExtra("password")); + + loginViewModel.getLoginFormState().observe(this, new Observer() { + @Override + public void onChanged(@Nullable LoginFormState loginFormState) { + if (loginFormState == null) { + return; + } + loginButton.setEnabled(loginFormState.isDataValid()); + if (loginFormState.getUsernameError() != null) { + emailEditText.setError(getString(loginFormState.getUsernameError())); + } + if (loginFormState.getPasswordError() != null) { + passwordEditText.setError(getString(loginFormState.getPasswordError())); + } + } + }); + + loginViewModel.getLoginResult().observe(this, new Observer() { + @Override + public void onChanged(@Nullable LoginResult loginResult) { + if (loginResult == null) { + return; + } + loadingProgressBar.setVisibility(View.GONE); + if (loginResult.getError() != null) { + showLoginFailed(loginResult); + } + if (loginResult.getToken() != null) { + TokenHelper.storeToken(loginResult.getToken(), getApplicationContext()); + openMenu(); + } + setResult(Activity.RESULT_OK); + + //Complete and destroy login activity once successful + finish(); + } + }); + + TextWatcher afterTextChangedListener = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // ignore + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // ignore + } + + @Override + public void afterTextChanged(Editable s) { + loginViewModel.loginDataChanged(emailEditText.getText().toString(), + passwordEditText.getText().toString()); + } + }; + emailEditText.addTextChangedListener(afterTextChangedListener); + passwordEditText.addTextChangedListener(afterTextChangedListener); + passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + loginViewModel.login(emailEditText.getText().toString(), + passwordEditText.getText().toString()); + } + return false; + } + }); + + loginButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + loadingProgressBar.setVisibility(View.VISIBLE); + loginViewModel.login(emailEditText.getText().toString(), + passwordEditText.getText().toString()); + } + }); + } + + private void openMenu() { + Intent intent = new Intent(this, MenuActivity.class); + startActivity(intent); + } + + private void showLoginFailed(LoginResult loginResult) { + LoginFailedDialog dialog = new LoginFailedDialog(loginResult.getEingabeEmail(), loginResult.getEingabePasswort()); + dialog.showNow(getSupportFragmentManager(), "test"); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginFailedDialog.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginFailedDialog.java new file mode 100644 index 0000000..7051769 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginFailedDialog.java @@ -0,0 +1,46 @@ +package de.oaa.xxxbdsmgame.ui.login; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; + +import com.example.xxxbdsmgame.R; + +import de.oaa.xxxbdsmgame.ui.register.RegisterActivity; + +public class LoginFailedDialog extends DialogFragment { + + private String email; + private String password; + + public LoginFailedDialog(String email, String password) { + + this.email = email; + this.password = password; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(R.string.login_failed).setPositiveButton(R.string.back, (dialog, which) -> { + Intent intent = new Intent(getActivity(), LoginActivity.class); + intent.putExtra("email", email); + intent.putExtra("password", ""); + startActivity(intent); + }).setNegativeButton(R.string.action_register, (dialog, which) -> { + Intent intent = new Intent(getActivity(), RegisterActivity.class); + intent.putExtra("email", email); + intent.putExtra("password", password); + startActivity(intent); + }); + return builder.create(); + } +} + diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginFormState.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginFormState.java new file mode 100644 index 0000000..5e76219 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginFormState.java @@ -0,0 +1,40 @@ +package de.oaa.xxxbdsmgame.ui.login; + +import androidx.annotation.Nullable; + +/** + * Data validation state of the login form. + */ +class LoginFormState { + @Nullable + private Integer usernameError; + @Nullable + private Integer passwordError; + private boolean isDataValid; + + LoginFormState(@Nullable Integer usernameError, @Nullable Integer passwordError) { + this.usernameError = usernameError; + this.passwordError = passwordError; + this.isDataValid = false; + } + + LoginFormState(boolean isDataValid) { + this.usernameError = null; + this.passwordError = null; + this.isDataValid = isDataValid; + } + + @Nullable + Integer getUsernameError() { + return usernameError; + } + + @Nullable + Integer getPasswordError() { + return passwordError; + } + + boolean isDataValid() { + return isDataValid; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginResult.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginResult.java new file mode 100644 index 0000000..23c95fd --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginResult.java @@ -0,0 +1,53 @@ +package de.oaa.xxxbdsmgame.ui.login; + +import androidx.annotation.Nullable; + +/** + * Authentication result : success (user details) or error message. + */ +class LoginResult { + @Nullable + private String token; + @Nullable + private Integer error; + + @Nullable + private String eingabeEmail; + + @Nullable + private String eingabePasswort; + + LoginResult(@Nullable Integer error, String eingabeEmail, String eingabePasswort) { + this.error = error; + this.eingabeEmail = eingabeEmail; + this.eingabePasswort = eingabePasswort; + } + + LoginResult(@Nullable String token) { + this.token = token; + } + + @Nullable + String getToken() { + return token; + } + + @Nullable + Integer getError() { + return error; + } + + @Nullable + public String getEingabeEmail() { + return eingabeEmail; + } + + @Nullable + public String getEingabePasswort() { + return eingabePasswort; + } + + public void setEingabePasswort(@Nullable String eingabePasswort) { + this.eingabePasswort = eingabePasswort; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginViewModel.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginViewModel.java new file mode 100644 index 0000000..e16a5b2 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginViewModel.java @@ -0,0 +1,74 @@ +package de.oaa.xxxbdsmgame.ui.login; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import android.content.Intent; +import android.util.Patterns; + +import de.oaa.xxxbdsmgame.data.LoginRepository; +import de.oaa.xxxbdsmgame.data.Result; +import de.oaa.xxxbdsmgame.data.model.LoggedInUser; +import de.oaa.xxxbdsmgame.ui.register.RegisterActivity; +import de.oaa.xxxbdsmgame.util.TokenHelper; + +import com.example.xxxbdsmgame.R; + +public class LoginViewModel extends ViewModel { + + private MutableLiveData loginFormState = new MutableLiveData<>(); + private MutableLiveData loginResult = new MutableLiveData<>(); + private LoginRepository loginRepository; + + LoginViewModel(LoginRepository loginRepository) { + this.loginRepository = loginRepository; + } + + LiveData getLoginFormState() { + return loginFormState; + } + + LiveData getLoginResult() { + return loginResult; + } + + public void login(String username, String password) { + // can be launched in a separate asynchronous job + Result result = loginRepository.login(username, password); + + if (result.getData() != null) { + LoggedInUser data = result.getData(); + loginResult.setValue(new LoginResult(data.getToken())); + } else { + loginResult.setValue(new LoginResult(R.string.login_failed, username, password)); + } + } + + public void loginDataChanged(String username, String password) { + if (!isUserNameValid(username)) { + loginFormState.setValue(new LoginFormState(R.string.invalid_name, null)); + } else if (!isPasswordValid(password)) { + loginFormState.setValue(new LoginFormState(null, R.string.invalid_password)); + } else { + loginFormState.setValue(new LoginFormState(true)); + } + } + + // A placeholder username validation check + private boolean isUserNameValid(String username) { + if (username == null) { + return false; + } + if (username.contains("@")) { + return Patterns.EMAIL_ADDRESS.matcher(username).matches(); + } else { + return !username.trim().isEmpty(); + } + } + + // A placeholder password validation check + private boolean isPasswordValid(String password) { + return password != null && password.trim().length() > 5; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginViewModelFactory.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginViewModelFactory.java new file mode 100644 index 0000000..98c186d --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/login/LoginViewModelFactory.java @@ -0,0 +1,25 @@ +package de.oaa.xxxbdsmgame.ui.login; + +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; +import androidx.annotation.NonNull; + +import de.oaa.xxxbdsmgame.data.LoginRepository; + +/** + * ViewModel provider factory to instantiate LoginViewModel. + * Required given LoginViewModel has a non-empty constructor + */ +public class LoginViewModelFactory implements ViewModelProvider.Factory { + + @NonNull + @Override + @SuppressWarnings("unchecked") + public T create(@NonNull Class modelClass) { + if (modelClass.isAssignableFrom(LoginViewModel.class)) { + return (T) new LoginViewModel(LoginRepository.getInstance()); + } else { + throw new IllegalArgumentException("Unknown ViewModel class"); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/menu/MenuActivity.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/menu/MenuActivity.java new file mode 100644 index 0000000..d9c9b24 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/menu/MenuActivity.java @@ -0,0 +1,71 @@ +package de.oaa.xxxbdsmgame.ui.menu; + +import android.app.Activity; + +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.appcompat.app.AppCompatActivity; + +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.example.xxxbdsmgame.databinding.ActivityMenuBinding; + +import java.util.Map; + +import de.oaa.xxxbdsmgame.ui.sets.SetsActivity; +import de.oaa.xxxbdsmgame.util.TokenHelper; + + +public class MenuActivity extends AppCompatActivity { + private ActivityMenuBinding binding; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + TokenHelper.getToken(getApplicationContext()); + + binding = ActivityMenuBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + binding.buttonProfile.setOnClickListener((v) -> { + + }); + + binding.buttonSettings.setOnClickListener((v) -> { + + }); + + binding.buttonSets.setOnClickListener((v) -> { + Intent intent = new Intent(this, SetsActivity.class); + intent.putExtra("all", Boolean.TRUE); + startActivity(intent); + }); + + binding.buttonOwnSets.setOnClickListener((v) -> { + Intent intent = new Intent(this, SetsActivity.class); + intent.putExtra("all", Boolean.FALSE); + startActivity(intent); + }); + + binding.buttonNewGame.setOnClickListener((v) -> { + + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterActivity.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterActivity.java new file mode 100644 index 0000000..2805972 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterActivity.java @@ -0,0 +1,139 @@ +package de.oaa.xxxbdsmgame.ui.register; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; + +import com.example.xxxbdsmgame.R; +import com.example.xxxbdsmgame.databinding.ActivityRegisterBinding; + +import de.oaa.xxxbdsmgame.ui.login.LoginActivity; + +public class RegisterActivity extends AppCompatActivity { + + private RegisterViewModel viewModel; + private ActivityRegisterBinding binding; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + + binding = ActivityRegisterBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + viewModel = new ViewModelProvider(this, new RegisterViewModelFactory()) + .get(RegisterViewModel.class); + + final EditText nameEditText = binding.name; + final EditText emailEditText = binding.email; + emailEditText.setText(intent.getStringExtra("email")); + final EditText passwordEditText = binding.password; + passwordEditText.setText(intent.getStringExtra("password")); + final Button loginButton = binding.login; + + viewModel.getRegisterFormState().observe(this, new Observer() { + @Override + public void onChanged(@Nullable RegisterFormState loginFormState) { + if (loginFormState == null) { + return; + } + loginButton.setEnabled(loginFormState.isDataValid()); + if (loginFormState.getNameError() != null) { + nameEditText.setError(getString(loginFormState.getNameError())); + } + if (loginFormState.getEmailError() != null) { + emailEditText.setError(getString(loginFormState.getEmailError())); + } + if (loginFormState.getPasswordError() != null) { + passwordEditText.setError(getString(loginFormState.getPasswordError())); + } + } + }); + + viewModel.getRegisterResult().observe(this, new Observer() { + @Override + public void onChanged(@Nullable RegisterResult result) { + if (result == null) { + return; + } + if (result.getError() != null) { + updateAfterError(result.getError()); + } else { + updateAfterSuccess(result); + } + setResult(Activity.RESULT_OK); + } + }); + + TextWatcher afterTextChangedListener = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // ignore + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // ignore + } + + @Override + public void afterTextChanged(Editable s) { + viewModel.registerDataChanged(nameEditText.getText().toString(), + emailEditText.getText().toString(), + passwordEditText.getText().toString()); + } + }; + nameEditText.addTextChangedListener(afterTextChangedListener); + emailEditText.addTextChangedListener(afterTextChangedListener); + passwordEditText.addTextChangedListener(afterTextChangedListener); + passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + viewModel.register(nameEditText.getText().toString(), + emailEditText.getText().toString(), + passwordEditText.getText().toString()); + } + return false; + } + }); + + loginButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + viewModel.register(nameEditText.getText().toString(), + emailEditText.getText().toString(), + passwordEditText.getText().toString()); + } + }); + } + + private void updateAfterSuccess(RegisterResult result) { + RegisterSuccessDialog dialog = new RegisterSuccessDialog(result); + dialog.showNow(getSupportFragmentManager(), "test"); + } + + private void updateAfterError(@StringRes Integer errorString) { + Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_SHORT).show(); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterFormState.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterFormState.java new file mode 100644 index 0000000..5b6238c --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterFormState.java @@ -0,0 +1,50 @@ +package de.oaa.xxxbdsmgame.ui.register; + +import androidx.annotation.Nullable; + +/** + * Data validation state of the login form. + */ +class RegisterFormState { + @Nullable + private Integer nameError; + @Nullable + private Integer emailError; + @Nullable + private Integer passwordError; + private boolean isDataValid; + + RegisterFormState(@Nullable Integer nameError, @Nullable Integer emailError, @Nullable Integer passwordError) { + this.nameError = nameError; + this.emailError = emailError; + this.passwordError = passwordError; + this.isDataValid = false; + } + + RegisterFormState(boolean isDataValid) { + this.nameError = null; + this.emailError = null; + this.passwordError = null; + this.isDataValid = isDataValid; + } + + @Nullable + Integer getNameError() { + + return nameError; + } + + @Nullable + public Integer getEmailError() { + return emailError; + } + + @Nullable + public Integer getPasswordError() { + return passwordError; + } + + boolean isDataValid() { + return isDataValid; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterResult.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterResult.java new file mode 100644 index 0000000..2d3ab92 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterResult.java @@ -0,0 +1,36 @@ +package de.oaa.xxxbdsmgame.ui.register; + +import androidx.annotation.Nullable; + +/** + * Authentication result : success (user details) or error message. + */ +class RegisterResult { + + private String email; + private String password; + @Nullable + private Integer error; + + RegisterResult(@Nullable Integer error) { + this.error = error; + } + + RegisterResult(String email, String password) { + this.email = email; + this.password = password; + } + + @Nullable + Integer getError() { + return error; + } + + public String getEmail() { + return email; + } + + public String getPassword() { + return password; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterSuccessDialog.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterSuccessDialog.java new file mode 100644 index 0000000..558cf17 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterSuccessDialog.java @@ -0,0 +1,41 @@ +package de.oaa.xxxbdsmgame.ui.register; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; + +import com.example.xxxbdsmgame.R; + +import de.oaa.xxxbdsmgame.ui.login.LoginActivity; + +public class RegisterSuccessDialog extends DialogFragment { + + private RegisterResult result; + + public RegisterSuccessDialog(RegisterResult result) { + this.result = result; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(R.string.check_email).setPositiveButton(R.string.ok, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(getActivity(), LoginActivity.class); + intent.putExtra("email", result.getEmail()); + intent.putExtra("password", result.getPassword()); + startActivity(intent); + } + }); + return builder.create(); + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterUserView.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterUserView.java new file mode 100644 index 0000000..d7ae79c --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterUserView.java @@ -0,0 +1,17 @@ +package de.oaa.xxxbdsmgame.ui.register; + +/** + * Class exposing authenticated user details to the UI. + */ +class RegisterUserView { + private String displayName; + //... other data fields that may be accessible to the UI + + RegisterUserView(String displayName) { + this.displayName = displayName; + } + + String getDisplayName() { + return displayName; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterViewModel.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterViewModel.java new file mode 100644 index 0000000..2a6e7a2 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterViewModel.java @@ -0,0 +1,76 @@ +package de.oaa.xxxbdsmgame.ui.register; + +import android.util.Patterns; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import com.example.xxxbdsmgame.R; + +import de.oaa.xxxbdsmgame.data.RegisterRepository; +import de.oaa.xxxbdsmgame.data.Result; + + +public class RegisterViewModel extends ViewModel { + + private MutableLiveData registerFormState = new MutableLiveData<>(); + private MutableLiveData registerResult = new MutableLiveData<>(); + private RegisterRepository repository; + + RegisterViewModel(RegisterRepository loginRepository) { + this.repository = loginRepository; + } + + LiveData getRegisterFormState() { + return registerFormState; + } + + LiveData getRegisterResult() { + return registerResult; + } + + public void register(String name, String email, String password) { + // can be launched in a separate asynchronous job + Result result = repository.register(name, email, password); + + if (result.getData() != null && result.getData()) { + registerResult.setValue(new RegisterResult(email, password)); + } else { + registerResult.setValue(new RegisterResult(R.string.register_failed)); + } + } + + public void registerDataChanged(String name, String email, String password) { + if (!isNameValid(name)) { + registerFormState.setValue(new RegisterFormState(R.string.invalid_name, null, null)); + } else if (!isEmailValid(email)) { + registerFormState.setValue(new RegisterFormState(null, R.string.invalid_email, null)); + } else if (!isPasswordValid(password)) { + registerFormState.setValue(new RegisterFormState(null, null, R.string.invalid_password)); + } else { + registerFormState.setValue(new RegisterFormState(true)); + } + } + + // A placeholder username validation check + private boolean isEmailValid(String email) { + if (email == null) { + return false; + } + if (email.contains("@")) { + return Patterns.EMAIL_ADDRESS.matcher(email).matches(); + } else { + return !email.trim().isEmpty(); + } + } + + private boolean isNameValid(String name) { + return name != null && name.length() > 6; + } + + // A placeholder password validation check + private boolean isPasswordValid(String password) { + return password != null && password.trim().length() > 6; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterViewModelFactory.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterViewModelFactory.java new file mode 100644 index 0000000..c181c11 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/register/RegisterViewModelFactory.java @@ -0,0 +1,25 @@ +package de.oaa.xxxbdsmgame.ui.register; + +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; +import androidx.annotation.NonNull; + +import de.oaa.xxxbdsmgame.data.RegisterRepository; + +/** + * ViewModel provider factory to instantiate LoginViewModel. + * Required given LoginViewModel has a non-empty constructor + */ +public class RegisterViewModelFactory implements ViewModelProvider.Factory { + + @NonNull + @Override + @SuppressWarnings("unchecked") + public T create(@NonNull Class modelClass) { + if (modelClass.isAssignableFrom(RegisterViewModel.class)) { + return (T) new RegisterViewModel(RegisterRepository.getInstance()); + } else { + throw new IllegalArgumentException("Unknown ViewModel class"); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/sets/SetsActivity.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/sets/SetsActivity.java new file mode 100644 index 0000000..44921f2 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/sets/SetsActivity.java @@ -0,0 +1,164 @@ +package de.oaa.xxxbdsmgame.ui.sets; + + +import android.content.Intent; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.xxxbdsmgame.R; +import com.example.xxxbdsmgame.databinding.ActivitySetsBinding; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +import de.oaa.xxxbdsmgame.data.Result; +import de.oaa.xxxbdsmgame.data.sets.FavoritenRepository; +import de.oaa.xxxbdsmgame.data.sets.SetsRepository; +import de.oaa.xxxbdsmgame.ui.gruppe.anzeige.GruppeAnzeigenActivity; +import de.oaa.xxxbdsmgame.util.TokenHelper; + + +public class SetsActivity extends AppCompatActivity implements SetsListViewAdapter.ItemClickListener { + private SetsRepository setsRepository; + private FavoritenRepository favoritenRepository; + + private ActivitySetsBinding binding; + private SetsListViewAdapter adapter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + boolean all = getIntent().getBooleanExtra("all", Boolean.FALSE); + + binding = ActivitySetsBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + String token = TokenHelper.getToken(getApplicationContext()); + setsRepository = SetsRepository.getInstance(token); + favoritenRepository = FavoritenRepository.getInstance(token); + RecyclerView listView = findViewById(R.id.listView); + + Result result = all ? setsRepository.getAlle(null) : setsRepository.getEigene(); + List gruppen = new ArrayList<>(); + if (result.getData() != null) { + try { + JSONArray gruppenJson = result.getData().getJSONArray("gruppen"); + for (int i = 0; i < gruppenJson.length(); i++) { + JSONObject jsonObject = gruppenJson.getJSONObject(i); + gruppen.add(jsonObject); + } + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + listView.setLayoutManager(new LinearLayoutManager(this)); + adapter = new SetsListViewAdapter(this, gruppen, favoritenRepository.get()); + adapter.setClickListener(this); + adapter.setParent(this); + listView.setAdapter(adapter); + + EditText searchField = binding.searchField; + searchField.addTextChangedListener(new TextWatcher() { + + private long letzteBeabeitung; + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + letzteBeabeitung = System.currentTimeMillis(); + new Thread(() -> { + try { + Thread.sleep(1000); + if (letzteBeabeitung + 1000 > System.currentTimeMillis()) { + doSearch(); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }).start(); + } + + private void doSearch() { + SetsActivity.this.runOnUiThread(new Runnable() { + public void run() { + Result filtered = all ? setsRepository.getAlle(searchField.getText().toString()) : setsRepository.getEigene(); + List gruppen = new ArrayList<>(); + if (filtered.getData() != null) { + try { + JSONArray gruppenJson = filtered.getData().getJSONArray("gruppen"); + for (int i = 0; i < gruppenJson.length(); i++) { + JSONObject jsonObject = gruppenJson.getJSONObject(i); + gruppen.add(jsonObject); + } + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + listView.setLayoutManager(new LinearLayoutManager(SetsActivity.this)); + adapter = new SetsListViewAdapter(SetsActivity.this, gruppen, favoritenRepository.get()); + adapter.setClickListener(SetsActivity.this); + adapter.setParent(SetsActivity.this); + listView.setAdapter(adapter); + } + }); + } + }); + } + + @Override + public void onItemClick(View view, int position) { + + } + + @Override + public void onPointerCaptureChanged(boolean hasCapture) { + super.onPointerCaptureChanged(hasCapture); + } + + void favoritChanged(JSONObject object, boolean isChecked) { + try { + if (isChecked) { + favoritenRepository.add(object.getString("gruppenId")); + } else { + favoritenRepository.remove(object.getString("gruppenId")); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + void gruppeCliecked(JSONObject object) { + try { + Intent intent =new Intent(this, GruppeAnzeigenActivity.class); + intent.putExtra("gruppeId", object.getString("gruppenId")); + startActivity(intent); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/ui/sets/SetsListViewAdapter.java b/app/src/main/java/de/oaa/xxxbdsmgame/ui/sets/SetsListViewAdapter.java new file mode 100644 index 0000000..04ab8e1 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/ui/sets/SetsListViewAdapter.java @@ -0,0 +1,139 @@ +package de.oaa.xxxbdsmgame.ui.sets; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Icon; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.Switch; +import android.widget.TextView; + +import androidx.annotation.RequiresApi; +import androidx.recyclerview.widget.RecyclerView; + +import com.example.xxxbdsmgame.R; + +import org.json.JSONObject; + +import java.io.File; +import java.nio.file.Files; +import java.util.Base64; +import java.util.List; +import java.util.UUID; + +public class SetsListViewAdapter extends RecyclerView.Adapter { + + private List mData; + + private List favoriten; + private LayoutInflater mInflater; + private ItemClickListener mClickListener; + private SetsActivity parent; + + // data is passed into the constructor + SetsListViewAdapter(Context context, List data, List favoriten) { + this.mInflater = LayoutInflater.from(context); + this.mData = data; + this.favoriten = favoriten; + } + + // inflates the row layout from xml when needed + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = mInflater.inflate(R.layout.sets_row, parent, false); + return new ViewHolder(view); + } + + // binds the data to the TextView in each row + @RequiresApi(api = Build.VERSION_CODES.O) + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + try { + JSONObject object = mData.get(position); + holder.name.setText(object.getString("name")); + holder.beschreibung.setText(object.getString("beschreibung")); + byte[] blob = Base64.getDecoder().decode(object.getString("bild")); + Bitmap bmp= BitmapFactory.decodeByteArray(blob,0,blob.length); + holder.bild.setImageIcon(Icon.createWithBitmap(bmp)); + holder.object = object; + if (favoriten.contains(UUID.fromString(object.getString("gruppenId")))) { + holder.fav.setChecked(true); + } + holder.von.setText(object.getString("von")); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + // total number of rows + @Override + public int getItemCount() { + return mData.size(); + } + + public JSONObject getItem(int position) { + return mData.get(position); + } + + public void setParent(SetsActivity parent) { + this.parent = parent; + } + + + // stores and recycles views as they are scrolled off screen + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, CompoundButton.OnCheckedChangeListener { + ImageView bild; + + TextView name; + + TextView beschreibung; + + TextView von; + + Switch fav; + + JSONObject object; + + ViewHolder(View itemView) { + super(itemView); + bild = itemView.findViewById(R.id.gruppeBild); + name = itemView.findViewById(R.id.gruppeName); + fav = itemView.findViewById(R.id.favSwitch); + beschreibung = itemView.findViewById((R.id.gruppeText)); + von = itemView.findViewById(R.id.gruppeVonText); + + itemView.setOnClickListener(this); + fav.setOnCheckedChangeListener(this); + } + + @Override + public void onClick(View view) { + if (mClickListener != null) { + parent.gruppeCliecked(object); + } + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if(buttonView.isPressed()) { + parent.favoritChanged(object, isChecked); + } + } + } + + // allows clicks events to be caught + void setClickListener(ItemClickListener itemClickListener) { + this.mClickListener = itemClickListener; + } + + + // parent activity will implement this method to respond to click events + public interface ItemClickListener { + void onItemClick(View view, int position); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/util/PasswordHelper.java b/app/src/main/java/de/oaa/xxxbdsmgame/util/PasswordHelper.java new file mode 100644 index 0000000..d543a77 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/util/PasswordHelper.java @@ -0,0 +1,22 @@ +package de.oaa.xxxbdsmgame.util; + +import java.security.MessageDigest; + +public class PasswordHelper { + + public static String hashPassword(String password) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(password.getBytes()); + byte[] bytes = md.digest(); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1)); + } + return sb.toString(); + } catch (Exception exception) { + return null; + } + } +} diff --git a/app/src/main/java/de/oaa/xxxbdsmgame/util/TokenHelper.java b/app/src/main/java/de/oaa/xxxbdsmgame/util/TokenHelper.java new file mode 100644 index 0000000..c4d0c49 --- /dev/null +++ b/app/src/main/java/de/oaa/xxxbdsmgame/util/TokenHelper.java @@ -0,0 +1,36 @@ +package de.oaa.xxxbdsmgame.util; + +import android.content.Context; +import android.content.SharedPreferences; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class TokenHelper { + + private TokenHelper() { + } + + public static String getToken(Context context) { + SharedPreferences prefs = context.getSharedPreferences("XXXTOKEN", 0); + Map map= prefs.getAll(); + Optional> first = + map.entrySet().stream().filter(entry -> entry.getKey().equals("token")).findFirst(); + return first.isPresent() ? first.get().getValue().toString() : null; + } + + public static void storeToken(String token, Context context) { + SharedPreferences prefs = context.getSharedPreferences("XXXTOKEN", 0); + SharedPreferences.Editor editor = prefs.edit(); + editor.putString("token", token); + editor.apply(); + } + + public static void deleteToken(Context context) { + SharedPreferences prefs = context.getSharedPreferences("XXXTOKEN", 0); + SharedPreferences.Editor editor = prefs.edit(); + editor.remove("token"); + editor.apply(); + } +} diff --git a/app/src/main/res/de/oaa/xxxbdsmgame/aufgabe.png b/app/src/main/res/de/oaa/xxxbdsmgame/aufgabe.png new file mode 100644 index 0000000000000000000000000000000000000000..21e9068ee23ea2077d07ba72dd3e63f0ec490e70 GIT binary patch literal 10983 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 T_;T;Rc%+JghJ2N*dC30&Ywx`F literal 0 HcmV?d00001 diff --git a/app/src/main/res/de/oaa/xxxbdsmgame/sperre.png b/app/src/main/res/de/oaa/xxxbdsmgame/sperre.png new file mode 100644 index 0000000000000000000000000000000000000000..e6fdb50127a2310b98fa54d8a7de9355b5d452f2 GIT binary patch literal 8121 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 TB-8TY00000NkvXXu0mjf;1+r- literal 0 HcmV?d00001 diff --git a/app/src/main/res/de/oaa/xxxbdsmgame/strafe.png b/app/src/main/res/de/oaa/xxxbdsmgame/strafe.png new file mode 100644 index 0000000000000000000000000000000000000000..094c27975caf9c6f17bec0a91d9bf92f192af357 GIT binary patch literal 7688 zcmZXZbx>PR)b~RO?oP3k;!cs`v{=#L?oyz5XmJTvN^vMwTBNwULxE7-0yMY=D5W?Q z_b0!3|9GF7=g#asGkfpsoH@IDKIgj|t*xm{ginVL004+oRTOp6HuS%Si;aHw%G!(q z02q-@3JTh)3JOo5?>rowT&g9@^8I|5wv>(@ssbcjR_MdJf<;6r`l|4()?SE#> z=@x0osKI|9!A_(a6I*~Xg&sTB?V>g+DV{(mtIOk)d;XDzAA1iB_i8s`0p zU+G%fhwLL$n&Gq>YO92?V$#NdaXaa#^h$`@n`>H=ZhnJEX+R`t6IK?C0rs$cvzS_Q&+X?D$|%( zR9$tT0OdUi3%1q)F1T>$)(;Klx37R#PS>@pnch( zcIUbkZjk?i3KGgqY^#rV{9Le4c?p~IT-Q^3^RE)m53`Mvg^tCg#B6EUkLp^iN#Dk1 zrimi;Swuz6TD{Np&$B^q?Bmp-c~7aia?a(mvBzECAIAemFD0CQ6$p2(t{oii-sJoO zr2JZ0p7Pv!?(N-;36u4hiU|>)cL=rY_mLFD^n0Ha{2mj^Fp0Yloc$0UCO?<6zh7fO z3eJ;xd=A&`t=ByDmu~uKkH-}yy6zt)0Vji-oKjc$VYE6O{EMU~!3H*Bu9QkdXnF@> zw}B^cf-sE<$40J8yw_FE98e3|f=Mo16GlP&nDZ8Y5_~bXF?=y8K;H>GpSolNc0;kq z|A0HL5Ec9~ELZ!!4-XU$ZL4{d>hK(!pufVs$$wL`NH{~4;JqpiJNz`3A$;yNG$>k> z7@SJtU=T<^dDKMnmdkEi4Co7d#M1b?C&x@YU)~Uk2YYOs7|Xi;oscaB^V>c$j4yiw zJOk`eEoov^k6xeBgt6(W1$j4VNNhlh5t;qd5)Gv;wBLfe*w9WlXx{RKw#8g7qQ*40 z{@$^pWKkmzhSk-3I9_gCY{K+(;_E{!nKJ(Mhrb$?$~6AuKv_+#%0McZ6;V?UQs@C( zw=dU#=KLA415SF!GuKlV{L-Sb1DaW(#ZCYs-I0*?-%~?Ig{ekFo3a&kovuZPL1IuI zC@haJOC3pS`?wA09_;0C9Q%&g9&O5gATr+1iqNOV$m-2DK%`cLKS!^JBiVox3puVW z9RNBctBYTQ^w##|-tPcR9X*c1&&NAp&Ze_|{efN>H6O#nG@f9OUxung1 z7`1(^M?vK6eJ1?O8x;`cMb$=h$8$_$;0Vt#cKs+4ue@0{&A<%Gp#z&fmg!yi6Dx9O z=OD?BvXe*N=qt}jq7H=O9#X=#l>T^p1Khe`IMX5N>0_UKy8OU0<4UD50}x~4!L8dW4|ZG52s<>^yL)Z7Hc&o@Xwzmb4e1bg+Gum1G02HLC~xhv2_#o zIGrZX(w-#`@6#X4fA7Fpf-g40iEJqN-ulf#3dhSK6q&kz#m4du^2q5$9Q~e)^XHgEQ7rK`%B7#5Dc~mf z?)k4c(<#HN-;hIoFExKnpSaQBma)bj%4U3Vu+U3?)ID*NEwwN;``X3#@)4JrIx;*A zAw;@}nN@fv7kRaSG=iOqeJ!r)x%f;R$@sOnGv(%lFA>5ZbrHR6?|~bXo>vh6+Wx;7 z?R#cPYi(6W7Tc3)75yvr;OY@Njn8=_osjuFCSY{2y~Cuzzu|o3=3L<6?aVGgdY0!1 z@koRxTqnlO7r+BZ2VsuGG4Wr%zuZ7>`vO|M?wf7=1Re+KaJUlz#CVc*B>wIgrXcGt z#cqI$rDJucEpK-ZM^{dDT4u!xqdB9v)7y!y9_T{Q1Hwy6weBD#&-&>cvk%FIoKEGd zB_C9}w%{=oA+{7A0R5}~4)K&b0e~PV3KN7TkZyDf7dz1vXSQr-r1y!$bur3GCbLeh zj9V*n%j13^?M8|m{HXKx7OQT*j$s;!RV^I^|5!e{C$h%7z?j8EbU!lPGOU|hJB7L2 zS)N#)Cs?6EwJ$z>*A{#lW)#x(?jZNVbxqa^o<^6&UUaX+X6+ypoK5fI)+;oI@sSyq zqDRdF3Cnj`9Q9cB@otlnC73xd6sT>0-q z&_-ZxZU(>-X!*6}ZND^R4&k$NQ>3zx)O4kH0;+3Hz{ozTDKpu;45&8t9!;w#H813P zclFz=Rjt*CK?Nv^??xh?1Jw_gpX0Y`y%1P;k%#4a#HM~;t)^XJ%x*kz9T37f%o>=U z4)Q}{tp%kh;IL40?$T9>ncpl|$Z{27C#e?CpH3O_auG=S6{g;pg#-`A|1?pc!s#JQ zYPvYhdZ}v|xT*c@6_o){Ux)2~8_!ZQ^lD*^Bvo_FmB-zyEkl;eh6-8hGsr1hwd_MP zmMG#%J_|cbE9@zuQ`A|M)lJBIZJO^4`$=Fa;PSsQo}A~RU7HWBo||lYK8MnnTzklm zs#T7ETjI|za%lJBA9sYsmQTXj`xN9qy{t9~aI60*Syk%15Y0OR^XQe};st?Oz6d!e z;*usPCSvic(+~K|wsZ_6ktfpT^qM`pSMjgr+RBvwRFY`+ZtmVC-LfM9#AIb)V;;pQ zb?n~lJ6FdN^FU^HPjDr6_9XsDuklj-?zMd7I3+CzL%_< zllvkLt~0mxka9T};wLuoQdjfVTFeJpqINY?HveSoT^;R}tgdElVWZvCNbK+BAVHXy}wmkrTLTI;X{qY$z@)a+>4;bY)IdGD3QaHJ=YSm zANf7fr_^4B?htK$2|1e@%Mz5E`(T{ZT$w9bcf0jU>Nw1H$wWM69m*;>m3jT4r1>LP z?w9coPmq~@^Iq(TuhEC^934?g>bZfU^j_~|cGn$`(roc2M<%ajQLp#EPlly`E@J%oDgu2Tr%Q zF`<_22G?{8tlBj@ea-Fqo9u{4*M$YrUYf+4h7Nz40IUJ4>0IA${_^gFIZO!_y$P^m zLGvy$AyELxFA&lF6JUv@uYjQU@oik|XtTfcP12o{ad+O`ySWvev=k2mJ>=JVg(D<@}QLu<4K-PzCP)|RFDEy!9&YyPQkpA8 zEaj$tI0)Z=SE=u)-}WY)fJOcp;mXw%zz{<604tF9UZGMZ?Tx*T=OFs3HtaS3}-od-**PX?@g5F6;E9i`iq zVi-PEgUMmb0c8mAb2hy@#-pINknz2+lgBM$)kIHvjcK~KTjvCgUQ=J#b;4ukbSuqO zzqR@+UZ#n)Kh!Gp8GjFol5q8rB0iG4+o?8Z6JP)74c5I@A_FWb1=Ks+G4}~Db^OhG zY(4(jP?*o=f;Zo|Ud{F>;k4YEOQawZ_{?5hAFUklteKC^nWfm$HK-wEcA4zMhpYKM zeSsZ5Cs8D_)-I#&rjnAM`Zu+9+hKMRpDokE0kG*5s(mc?qru%g&GW^=6brAo316IA zlM_p231JNTr*gtR#kGU)9#?C5H+H-pK4sKTV3W!x#c-(fQz*S>Z2R!Nts-^??Dl6S zo*sO+WwmzyKxB1q?jo^TL<#!xDHy*v{8K^bIBaNUKAHtb7>f|Khz=Ps{QN@ss%fK> zy@Fr;RyEv$7!L+GbC{VJ)^NyegpAxZS>m=YNyWO^UGk;Ld+kDXJf59IWVcw&nO0wN z_R~o)nU$4?%a6?A)Slk(0Uaa_e?({RoDQIew(Y*qDXALifaJ&FVqvPHR-vpn#gmQp zPkP0B8Y}6Nk?WaW4r@m#ssWOc1h`hGq&Sv1)HN1eP$K)+T)5wNz3M{Uuw?T=OumT0 zms_OeSjP1D*!YY-`*6dJ@>a+mEe2Yg7$m-4yB;}2AZm8xc}YLnMM7lxQq;m({9~J% z?fxK0C8QMgk)16JA-x`Getn^fb_5S6)ru$yeUiA{Pm7sTEyxjqDIR--X$^*1W&P{D z&2fRSZ)?&@UDz`Bbr+MXW+V?k<%=~o>tG~E@}P2LpP75~+G=)7L|oj8Ra9ij3G)lz5wJ9YHongJ}S!>3XYj^O6l`oVA<% z5!2kI`zA3lH^~;^WN~*7-WN`$ddNxP%GASjl;?D>R|04 z{s^J;1n-=cs45jx{3~^BDcXHHfYoQ+9O&JKRL$d}Du+z-Rjw8yb&pZaK*}$}cF7t| zD(P)Z=lOF`x(k12naw{%cCe>7g@Z=#!Ko%D6FbLMFW$MV(TQaTm)*(R66}8p^K$vY z!n$0}IO^6T^L3jE*`j;fnO--_JUJnqWuNgTW2^KHdO)a;SuaDVT1|1~HdBX1G{4DP z^coL9Ty5`ut1c*{y8KLj>oaYo42wLm^#lLq)Z5nbmVEsAz*w?(gF*I6ZMbsa^@BTt zf%uy^nFW%6{L7!z@*H;T^CRF>~z@K`XqtvmlO)o&Z6{n8sg9&wo zcOxxe|3BtjTm8MQT5ZlabJiR_oW>m!RZh1SlGNW3dt0SXgiB8X8n~Ims=V6A%goG8 zKv2r^inJ0lE*Ia%7QUCx!xl7ZT-yLY7niB!>p!0+>%+Mk5ji=hFAi6OyKPL$>!KJF z$V?K(n2l^Qesa0$Gg=>@G9i#S9&?#9+x_-_svTTY98N4jHRgHxJ$LetC$BuXukn~c z?lq3DN>dvvUHO=k5pNv4*vKJe8m8y2_l&MsZe_}k8KK`|ljvJUo2@XpdYM)FEL#P7yZS3^$7qcz(vvAZrV1|;F^z}$-I z#U54EizwV{7Bk#-^60y7YS^st8f@gWJ3R-(<5g8Zx?nO#t}7!^e0(A;EicQQ7C+0S zD&oYf&k{eJ)YP=1ls@>;zcsu{P9)u6JnYyii?$oMyA_<7$WhC}sP70L4O1Q}JG0L$ zuF4s&V!$^RdMw(sSRp)TmZZR2UFljeZ?fsFVe+%zQi|2eU3={>$n>RR-bYyTj}}MG z8)v^fePVBlS&@PokPrGL7_Ko|$Vl9FnAh zQ*;E7mO}uGk5&F-?6K+dBil4#VmNIdV@3lBCyl2-L(D&y5<8m@Kd1A31Cv3c`TL%v z7g|c1b!l)D$)ly6*MSq`SlVJNslZ!#=?|$P<_-Jp$!at;rum&wz>@rgin}H#Ydc+& z*NItO+BA2=X^)=N)z9m}!nz4U!-SG)$J~nM9V{!sCoxW#f^R@|CL6$W(9=f zf5E(y=|zGW#;&erhV?rHbL!F3R2>CJ8IkXIHwthPY zJ3@Dn9BcimPXHE9HeRA#IE9Y@OTrs;qzIDqZP$l@l_WMi;oUxf>@~1e&n)w@VX_P~rXQ485tT zdjqff%*`6s51r_y2uw=7iFEKB8U@Hv=t|T4c|_EU@q@*lC~r1$tNKE(4jFe5$k7_^ zs8GX!jK%Qpm%e?Q$duith&6c`uE8Nvc;-#UbNM<~rRJt}Ap@d&B78g!jnVw^YQ|EG zR_Pp!q6j|Z&#^K_UXZDDc5y?Ck&+1m4wFFILtUycTmp_qg-W_+g~~mtz6BkZ*b>z@ zMc7F+MOZM2)vq{;G2t(|GuIKpxJ>$k;ew+wY9i0u-Y>d6uw9c+BOBRmI(m zxrQyPd*PiSR?ipM8D zDxNtbKDL^vX{mlwgqFNT@$|uup^$={KBu&X4(*x8cq~mWWnH%Yn@$pDWvwAHQF z-}FWmuS;sL4VPEyyaS7TW-5iJvhjG!>%0p3lo+)na+(aUZmh&Npw05QalE9f$4^Op~&czyKWY3j*f(>H4U-+3tyfKMp>P&0h~> zWD?uGf=wh4GHP<6lCDMN_+P`M-ap)tDuCaLV4D0~#tHiFSHlvuZa~kEXIgiE?f*ff zes?=--NVGe@&L7p&>Ra(>p4BZfK09~jee~%8=n(%GxeOHuc)>-o0&jk&a8Z~r(853 z*W3Bf;pOio#B>gS{Z^Y0CE<^w#v@>k{*O7(h_U4C0-YsJ5XC%CZLD(1jRCeO^0qP< zDfp?$`}@2361+M+8e2IdA%6-Iz60*Lz|?TSrj7vkRxOO=Iu~v--`>OT`1&uv61xi> zhMUnME4?2Xu|m{|J~=CBdM9F*;6noW-Do?DQ*U7=^J{YGRaeUd)@j7*OFxGm z&^!}v%3B|t@L0V&I^ErCiPO&xoq7sqW_}3tA1>muzW=Rq<_2%%9i3@LBJ$0KMmgYF zQ`2YEB>XGdRg8Df`$-R1rg)#|&tNg^(y9VKk@I2pwjtgMNag>+vEfPwP)Jop8@!Q3 zX2}F|HH)M-5F8VE%7_*4un$|8RF88ujxvma{20a6?70d>*vy-)JN!i>r&@d|@ko0T nSanMolzIN&-JAaN`m0B%9Nd3dnfc9s_lK%Vnu^u(*5UsHFcrt_ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_gruppe_anzeigen.xml b/app/src/main/res/layout/activity_gruppe_anzeigen.xml new file mode 100644 index 0000000..dcfcecd --- /dev/null +++ b/app/src/main/res/layout/activity_gruppe_anzeigen.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..4a79b6a --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,69 @@ + + + + + + + +