Commit fb604787 authored by Niklas Fix's avatar Niklas Fix 🎓

Merge branch 'dev' of ssh://gitlab.akamu.de:26/akamu/game-client-android into string_ressources

# Conflicts:
#	app/src/main/res/values-de/strings.xml
#	app/src/main/res/values-en/strings.xml
#	app/src/main/res/values/strings.xml
parents 86c6bfe7 49fca982
...@@ -135,4 +135,9 @@ fabric.properties ...@@ -135,4 +135,9 @@ fabric.properties
app/release app/release
# End of https://www.gitignore.io/api/androidstudio akamu.jks
\ No newline at end of file
keystore.properties
# End of https://www.gitignore.io/api/androidstudio
keystore.properties
MIT License
Copyright (c) 2020 Akamu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
...@@ -21,8 +21,8 @@ android { ...@@ -21,8 +21,8 @@ android {
applicationId "de.akamu.tudarmstadt" applicationId "de.akamu.tudarmstadt"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 4 versionCode 5
versionName "0.4.0" versionName "1.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
signingConfigs { signingConfigs {
...@@ -34,7 +34,11 @@ android { ...@@ -34,7 +34,11 @@ android {
} }
} }
buildTypes { buildTypes {
debug {
buildConfigField("String", "BASE_URL", '"https://dev.akamu.de/api/v2/"')
}
release { release {
buildConfigField("String", "BASE_URL", '"https://akamu.de/api/v2/"')
signingConfig signingConfigs.release signingConfig signingConfigs.release
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
...@@ -52,16 +56,19 @@ android { ...@@ -52,16 +56,19 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta8' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta8'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.android.material:material:1.3.0-alpha01' implementation 'com.google.android.material:material:1.3.0-alpha03'
implementation 'com.jaredrummler:animated-svg-view:1.0.5' implementation 'com.jaredrummler:animated-svg-view:1.0.5'
implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.okhttp3:okhttp:3.12.1' implementation 'com.squareup.okhttp3:okhttp:3.12.1'
implementation 'com.github.bumptech.glide:glide:4.8.0' implementation 'com.github.bumptech.glide:glide:4.8.0'
implementation 'org.greenrobot:eventbus:3.2.0' implementation 'org.greenrobot:eventbus:3.2.0'
// LANGUAGE
implementation "com.github.YarikSOffice:lingver:1.3.0"
// FIREBASE // FIREBASE
implementation 'com.google.firebase:firebase-analytics:17.4.4' implementation 'com.google.firebase:firebase-analytics:17.4.4'
// Recommended: Add the Firebase SDK for Google Analytics. // Recommended: Add the Firebase SDK for Google Analytics.
......
...@@ -14,7 +14,11 @@ ...@@ -14,7 +14,11 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/WhiteTheme" android:theme="@style/WhiteTheme"
tools:ignore="GoogleAppIndexingWarning"> tools:ignore="GoogleAppIndexingWarning">
<!--activity android:name=".features.crashlytics.CrashlyticsActivity"></activity--> <activity
android:name=".features.password.ForgotPasswordActivity"
android:label="@string/reset_your_password"
android:parentActivityName=".features.login.LoginActivity"
android:screenOrientation="portrait" />
<activity <activity
android:name=".features.summary.SummaryActivity" android:name=".features.summary.SummaryActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
...@@ -54,7 +58,7 @@ ...@@ -54,7 +58,7 @@
<activity <activity
android:name=".features.title.TitleActivity" android:name=".features.title.TitleActivity"
android:label="@string/title_activity_choose_title" android:label="@string/title_activity_choose_title"
android:parentActivityName=".features.title.TitleActivity" android:parentActivityName=".features.profile.ProfileActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name=".features.questionpools.QuestionPoolsActivity" android:name=".features.questionpools.QuestionPoolsActivity"
...@@ -67,7 +71,8 @@ ...@@ -67,7 +71,8 @@
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name="de.akamu.tudarmstadt.features.settings.SettingsActivity" android:name=".features.settings.SettingsActivity"
android:configChanges="locale"
android:label="@string/title_activity_settings" android:label="@string/title_activity_settings"
android:parentActivityName=".features.dashboard.MainActivity" android:parentActivityName=".features.dashboard.MainActivity"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
...@@ -75,6 +80,8 @@ ...@@ -75,6 +80,8 @@
<meta-data <meta-data
android:name="preloaded_fonts" android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" /> android:resource="@array/preloaded_fonts" />
<meta-data android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_akamu_notification_white" />
<service <service
android:name=".notifications.AkamuFirebaseMessagingService" android:name=".notifications.AkamuFirebaseMessagingService"
......
...@@ -2,22 +2,25 @@ package de.akamu.tudarmstadt; ...@@ -2,22 +2,25 @@ package de.akamu.tudarmstadt;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.SharedPreferences;
import android.util.Log; import android.util.Log;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.yariksoffice.lingver.Lingver;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.Locale;
import java.util.Objects;
import de.akamu.tudarmstadt.data.settings.SettingsDataSource; import de.akamu.tudarmstadt.data.settings.SettingsDataSource;
import de.akamu.tudarmstadt.data.settings.SettingsDataSourceImpl; import de.akamu.tudarmstadt.data.settings.SettingsDataSourceImpl;
import de.akamu.tudarmstadt.interactors.SettingsInteractor; import de.akamu.tudarmstadt.interactors.SettingsInteractor;
import de.akamu.tudarmstadt.model.MyObjectBox; import de.akamu.tudarmstadt.model.MyObjectBox;
import de.akamu.tudarmstadt.util.Constants; import de.akamu.tudarmstadt.util.Constants;
import de.akamu.tudarmstadt.util.LocaleManager;
import de.akamu.tudarmstadt.util.SettingsPresenter; import de.akamu.tudarmstadt.util.SettingsPresenter;
import io.objectbox.BoxStore; import io.objectbox.BoxStore;
...@@ -36,6 +39,13 @@ public class App extends Application implements SettingsDataSource.FetchSettings ...@@ -36,6 +39,13 @@ public class App extends Application implements SettingsDataSource.FetchSettings
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
SharedPreferences prefs = android.preference.PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String deviceLang = Locale.getDefault().getDisplayLanguage();
String langEN = getApplicationContext().getResources().getString(R.string.pref_language_eng_value);
String langDE = getApplicationContext().getResources().getString(R.string.pref_language_ger_value);
String defaultLang = deviceLang.equals(langEN) || deviceLang.equals(langDE) ? deviceLang : langDE;
String defaultLanguage = prefs.getString(Constants.USER_LANG, defaultLang);
Lingver.init(this, Objects.requireNonNull(defaultLanguage));
boolean isNightMode = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(Constants.IS_NIGHT_MODE, false); boolean isNightMode = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(Constants.IS_NIGHT_MODE, false);
if (isNightMode) { if (isNightMode) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
...@@ -52,27 +62,6 @@ public class App extends Application implements SettingsDataSource.FetchSettings ...@@ -52,27 +62,6 @@ public class App extends Application implements SettingsDataSource.FetchSettings
return boxStore; return boxStore;
} }
/**
* This method calls the LocaleManager to
* update change language settings.
*/
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleManager.setLocale(base));
Log.d(TAG, "attachBaseContext");
}
/**
* This method calls the LocaleManager to
* update change language settings if configuration changes occur.
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
LocaleManager.setLocale(this);
Log.d(TAG, "onConfigurationChanged: " + newConfig.locale.getLanguage());
}
@Override @Override
public void onFetchSettingsSuccess(@NotNull JSONObject settings) { public void onFetchSettingsSuccess(@NotNull JSONObject settings) {
System.out.println("Settings fetched successfully."); System.out.println("Settings fetched successfully.");
...@@ -80,6 +69,6 @@ public class App extends Application implements SettingsDataSource.FetchSettings ...@@ -80,6 +69,6 @@ public class App extends Application implements SettingsDataSource.FetchSettings
@Override @Override
public void onFetchSettingsFailed(@NotNull String reason) { public void onFetchSettingsFailed(@NotNull String reason) {
System.out.println("Fetching settings failed."); System.out.println("Fetching settings failed: " + reason);
} }
} }
\ No newline at end of file
...@@ -2,9 +2,8 @@ package de.akamu.tudarmstadt ...@@ -2,9 +2,8 @@ package de.akamu.tudarmstadt
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.view.MotionEvent
import android.content.pm.PackageManager.GET_META_DATA import android.view.inputmethod.InputMethodManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import de.akamu.tudarmstadt.api.V2API import de.akamu.tudarmstadt.api.V2API
import de.akamu.tudarmstadt.events.JWTRefreshedEvent import de.akamu.tudarmstadt.events.JWTRefreshedEvent
...@@ -12,37 +11,12 @@ import de.akamu.tudarmstadt.events.UnauthorizedEvent ...@@ -12,37 +11,12 @@ import de.akamu.tudarmstadt.events.UnauthorizedEvent
import de.akamu.tudarmstadt.features.login.LoginActivity import de.akamu.tudarmstadt.features.login.LoginActivity
import de.akamu.tudarmstadt.util.AppUserUtil import de.akamu.tudarmstadt.util.AppUserUtil
import de.akamu.tudarmstadt.util.Constants import de.akamu.tudarmstadt.util.Constants
import de.akamu.tudarmstadt.util.LocaleManager
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
abstract class BaseActivity : AppCompatActivity() { abstract class BaseActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(LocaleManager.setLocale(newBase))
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
resetTitle()
}
/**
* Method for resetting the titles in the action bar
* This is used for displaying the correct action bar text
*/
private fun resetTitle() {
try {
val label = packageManager.getActivityInfo(componentName, GET_META_DATA).labelRes
if (label != 0) {
setTitle(label)
}
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
}
public override fun onStart() { public override fun onStart() {
super.onStart() super.onStart()
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
...@@ -70,4 +44,13 @@ abstract class BaseActivity : AppCompatActivity() { ...@@ -70,4 +44,13 @@ abstract class BaseActivity : AppCompatActivity() {
fun onJWTRefreshedEvent(e: JWTRefreshedEvent) { fun onJWTRefreshedEvent(e: JWTRefreshedEvent) {
AppUserUtil.saveLoginToken(this, V2API.getToken()) AppUserUtil.saveLoginToken(this, V2API.getToken())
} }
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
if (currentFocus != null) {
val imm: InputMethodManager =
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(currentFocus!!.windowToken, 0)
}
return super.dispatchTouchEvent(ev)
}
} }
\ No newline at end of file
package de.akamu.tudarmstadt.api; package de.akamu.tudarmstadt.api;
import android.app.Application;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
...@@ -16,12 +14,12 @@ import java.util.Map; ...@@ -16,12 +14,12 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.TimeZone; import java.util.TimeZone;
import de.akamu.tudarmstadt.BuildConfig;
import de.akamu.tudarmstadt.api.interceptor.LoggingInterceptor; import de.akamu.tudarmstadt.api.interceptor.LoggingInterceptor;
import de.akamu.tudarmstadt.api.interceptor.TokenInterceptor; import de.akamu.tudarmstadt.api.interceptor.TokenInterceptor;
import de.akamu.tudarmstadt.api.interceptor.UnauthorizedInterceptor; import de.akamu.tudarmstadt.api.interceptor.UnauthorizedInterceptor;
import de.akamu.tudarmstadt.exceptions.AkamuAPIException; import de.akamu.tudarmstadt.exceptions.AkamuAPIException;
import de.akamu.tudarmstadt.model.JWT; import de.akamu.tudarmstadt.model.JWT;
import de.akamu.tudarmstadt.util.AppUserUtil;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
...@@ -38,9 +36,7 @@ import okhttp3.Response; ...@@ -38,9 +36,7 @@ import okhttp3.Response;
public final class V2API { public final class V2API {
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private static final String baseURL = "https://dev.akamu.de/api/v2/"; // Dev version of server private static final String baseURL = BuildConfig.BASE_URL;
// private static final String baseURL = "http://192.168.0.186:8080/"; // Docker version of server
// private static final String baseURL = "http://172.18.95.200:8080/"; // Docker version of server
private static String token = ""; private static String token = "";
private static Date expire; private static Date expire;
private static final OkHttpClient client = new OkHttpClient.Builder() private static final OkHttpClient client = new OkHttpClient.Builder()
...@@ -220,4 +216,7 @@ public final class V2API { ...@@ -220,4 +216,7 @@ public final class V2API {
return code < 300 && code >= 200; return code < 300 && code >= 200;
} }
public static String getBaseURL() {
return baseURL;
}
} }
package de.akamu.tudarmstadt.api;
import android.util.ArrayMap;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Map;
import de.akamu.tudarmstadt.exceptions.AkamuAPIException;
public final class V2APIForgotPassword {
public static void requestNewPassword(String usernameOrEmail) throws IOException, AkamuAPIException, JSONException {
Map<String, String> parameters = new ArrayMap<>(0);
JSONObject json = new JSONObject();
if (usernameOrEmail.contains("@")) {
json.put("email", usernameOrEmail);
} else {
json.put("username", usernameOrEmail);
}
V2API.post("forgotpassword", json.toString(), parameters);
}
}
...@@ -19,6 +19,7 @@ interface FriendsDataSource { ...@@ -19,6 +19,7 @@ interface FriendsDataSource {
fun onChallengeRequestSuccess(friendName: String) fun onChallengeRequestSuccess(friendName: String)
fun onChallengeRequestFailed(reason : String) fun onChallengeRequestFailed(reason : String)
fun onChallengeRequestFailedNoPoolsSelected() fun onChallengeRequestFailedNoPoolsSelected()
fun onChallengeRequestFailedNoPoolsInCommon()
} }
interface GetAllUsersCallback { interface GetAllUsersCallback {
......
...@@ -3,6 +3,7 @@ package de.akamu.tudarmstadt.data.friends ...@@ -3,6 +3,7 @@ package de.akamu.tudarmstadt.data.friends
import android.os.AsyncTask import android.os.AsyncTask
import de.akamu.tudarmstadt.api.V2APIDuel import de.akamu.tudarmstadt.api.V2APIDuel
import de.akamu.tudarmstadt.api.V2APIFriend import de.akamu.tudarmstadt.api.V2APIFriend
import de.akamu.tudarmstadt.api.V2APIPool
import de.akamu.tudarmstadt.api.V2APIUser import de.akamu.tudarmstadt.api.V2APIUser
import de.akamu.tudarmstadt.exceptions.AkamuAPIException import de.akamu.tudarmstadt.exceptions.AkamuAPIException
import de.akamu.tudarmstadt.model.User import de.akamu.tudarmstadt.model.User
...@@ -51,16 +52,24 @@ class FriendsDataSourceImpl : FriendsDataSource { ...@@ -51,16 +52,24 @@ class FriendsDataSourceImpl : FriendsDataSource {
var errorMessage : String = "Oops! Something went wrong..." var errorMessage : String = "Oops! Something went wrong..."
private var noPoolsSelected: Boolean = false private var noPoolsSelected: Boolean = false
private var noPoolsInCommon: Boolean = false
override fun doInBackground(vararg params: Void?): Boolean { override fun doInBackground(vararg params: Void?): Boolean {
return try { return try {
V2APIDuel.startDuel(friend.id.toInt()) val playablePools = V2APIPool.getPlayablePools()
true val myPools = playablePools.filter { pool -> pool.isSelected }
if (myPools.isEmpty()) {
noPoolsSelected = true
false
} else {
V2APIDuel.startDuel(friend.id.toInt())
true
}
} catch (ae : AkamuAPIException) { } catch (ae : AkamuAPIException) {
ae.printStackTrace() ae.printStackTrace()
errorMessage = ae.message!! errorMessage = ae.message!!
if (ae.code == 409) { if (ae.code == 409) {
noPoolsSelected = true noPoolsInCommon = true
} }
false false
} catch (e : Exception) { } catch (e : Exception) {
...@@ -76,6 +85,8 @@ class FriendsDataSourceImpl : FriendsDataSource { ...@@ -76,6 +85,8 @@ class FriendsDataSourceImpl : FriendsDataSource {
} else { } else {
if (noPoolsSelected) { if (noPoolsSelected) {
callback.onChallengeRequestFailedNoPoolsSelected() callback.onChallengeRequestFailedNoPoolsSelected()
} else if (noPoolsInCommon) {
callback.onChallengeRequestFailedNoPoolsInCommon()
} else { } else {
callback.onChallengeRequestFailed(errorMessage) callback.onChallengeRequestFailed(errorMessage)
} }
......
...@@ -4,7 +4,7 @@ import de.akamu.tudarmstadt.model.User ...@@ -4,7 +4,7 @@ import de.akamu.tudarmstadt.model.User
interface PasswordDataSource { interface PasswordDataSource {
interface VerifyPasswordDataSource { interface VerifyPasswordCallback {
/** /**
* Called when the given password matches the user's password * Called when the given password matches the user's password
...@@ -23,6 +23,21 @@ interface PasswordDataSource { ...@@ -23,6 +23,21 @@ interface PasswordDataSource {
/** /**
* Checks if the given [password] matches the user's password * Checks if the given [password] matches the user's password
*/ */
fun verifyPassword(user: User, password: String, firebaseToken: String, callback: PasswordDataSource.VerifyPasswordDataSource) fun verifyPassword(user: User, password: String, firebaseToken: String, callback: VerifyPasswordCallback)
interface RequestNewPasswordCallback {
fun onRequestNewPasswordSuccess()
fun onRequestNewPasswordFailed(reason: String)
fun onNoSuchUserError()
}
/**
* Sends a request for resetting the user's password
*
* @param usernameOrEmail the user or its email address
* @param callback RequestNewPasswordCallback
*/
fun requestNewPassword(usernameOrEmail: String, callback: RequestNewPasswordCallback)
} }
\ No newline at end of file
package de.akamu.tudarmstadt.data.password package de.akamu.tudarmstadt.data.password
import android.os.AsyncTask import android.os.AsyncTask
import de.akamu.tudarmstadt.api.V2APIForgotPassword
import de.akamu.tudarmstadt.api.V2APILogin import de.akamu.tudarmstadt.api.V2APILogin
import de.akamu.tudarmstadt.exceptions.AkamuAPIException import de.akamu.tudarmstadt.exceptions.AkamuAPIException
import de.akamu.tudarmstadt.model.Login import de.akamu.tudarmstadt.model.Login
import de.akamu.tudarmstadt.model.User import de.akamu.tudarmstadt.model.User
import java.lang.Exception