commit cf222658e649466c450e166186520ce7f2573bb6 Author: Mario Date: Wed Mar 18 21:55:34 2026 +0100 Initialier Commit 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 0000000..21e9068 Binary files /dev/null and b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/aufgabe.png differ 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 0000000..e6fdb50 Binary files /dev/null and b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/sperre.png differ 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 0000000..094c279 Binary files /dev/null and b/app/src/main/java/de/oaa/xxxbdsmgame/ui/gruppe/anzeige/strafe.png differ 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 0000000..21e9068 Binary files /dev/null and b/app/src/main/res/de/oaa/xxxbdsmgame/aufgabe.png differ 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 0000000..e6fdb50 Binary files /dev/null and b/app/src/main/res/de/oaa/xxxbdsmgame/sperre.png differ 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 0000000..094c279 Binary files /dev/null and b/app/src/main/res/de/oaa/xxxbdsmgame/strafe.png differ 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 @@ + + + + + + + +