Merge branch 'master' of git.reim.ar:ReiMerc/easyeat

This commit is contained in:
Jeas0001 2025-04-30 13:06:13 +02:00
commit f5d580afe7
14 changed files with 180 additions and 21 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" /> <bytecodeTargetLevel target="17" />
</component> </component>
</project> </project>

View File

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" /> <file type="web" url="file://$PROJECT_DIR$" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -1,6 +1,9 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
id("org.jetbrains.kotlin.plugin.serialization")
} }
android { android {
@ -15,6 +18,8 @@ android {
versionName = "1.0" versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "API_BASE_URL", project.property("API_BASE_URL").toString())
} }
buildTypes { buildTypes {
@ -36,7 +41,6 @@ android {
} }
dependencies { dependencies {
implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat) implementation(libs.androidx.appcompat)
implementation(libs.material) implementation(libs.material)
@ -48,7 +52,5 @@ dependencies {
implementation(libs.navigation.fragment.ktx) implementation(libs.navigation.fragment.ktx)
implementation(libs.navigation.ui.ktx) implementation(libs.navigation.ui.ktx)
implementation(libs.androidx.activity) implementation(libs.androidx.activity)
testImplementation(libs.junit) implementation(libs.kotlinx.serialization.json)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
} }

View File

@ -0,0 +1 @@
API_BASE_URL="https://easyeat.mercantec.tech"

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:allowBackup="true" android:allowBackup="true"

View File

@ -3,10 +3,12 @@ package tech.mercantec.easyeat
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.widget.Button import android.widget.Button
import androidx.activity.enableEdgeToEdge import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import tech.mercantec.easyeat.helpers.ApiRequestException
import androidx.core.view.WindowInsetsCompat import tech.mercantec.easyeat.helpers.login
import kotlin.concurrent.thread
class LoginActivity : AppCompatActivity() { class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -15,11 +17,24 @@ class LoginActivity : AppCompatActivity() {
setContentView(R.layout.activity_login) setContentView(R.layout.activity_login)
findViewById<Button>(R.id.login).setOnClickListener { findViewById<Button>(R.id.login).setOnClickListener {
// TODO authenticate val email = findViewById<EditText>(R.id.email).text.toString()
val password = findViewById<EditText>(R.id.password).text.toString()
val intent = Intent(this, MainActivity::class.java) thread {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK // Prevent going back to login screen try {
startActivity(intent) login(email, password)
} catch (e: ApiRequestException) {
runOnUiThread {
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()
}
return@thread
}
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK // Prevent going back to login screen
startActivity(intent)
}
} }
findViewById<Button>(R.id.back).setOnClickListener { findViewById<Button>(R.id.back).setOnClickListener {

View File

@ -3,10 +3,12 @@ package tech.mercantec.easyeat
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.widget.Button import android.widget.Button
import androidx.activity.enableEdgeToEdge import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import tech.mercantec.easyeat.helpers.ApiRequestException
import androidx.core.view.WindowInsetsCompat import tech.mercantec.easyeat.helpers.register
import kotlin.concurrent.thread
class RegisterActivity : AppCompatActivity() { class RegisterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -15,10 +17,30 @@ class RegisterActivity : AppCompatActivity() {
setContentView(R.layout.activity_register) setContentView(R.layout.activity_register)
findViewById<Button>(R.id.register).setOnClickListener { findViewById<Button>(R.id.register).setOnClickListener {
// TODO create account val username = findViewById<EditText>(R.id.username).text.toString()
val email = findViewById<EditText>(R.id.email).text.toString()
val password = findViewById<EditText>(R.id.password).text.toString()
val confirmPassword = findViewById<EditText>(R.id.confirm_password).text.toString()
startActivity(Intent(this, LoginActivity::class.java)) if (password != confirmPassword) {
finish() Toast.makeText(this, "Passwords do not match", Toast.LENGTH_LONG).show()
return@setOnClickListener
}
thread {
try {
register(email, username, password)
} catch (e: ApiRequestException) {
runOnUiThread {
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()
}
return@thread
}
startActivity(Intent(this, LoginActivity::class.java))
finish()
}
} }
findViewById<Button>(R.id.back).setOnClickListener { findViewById<Button>(R.id.back).setOnClickListener {

View File

@ -0,0 +1,79 @@
package tech.mercantec.easyeat.helpers
import android.content.Context
import android.util.Log
import android.widget.Toast
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import tech.mercantec.easyeat.BuildConfig
import java.net.HttpURLConnection
import java.net.URL
import kotlinx.serialization.json.*
import kotlinx.serialization.serializer
import java.io.IOException
class ApiRequestException(message: String, cause: Throwable?) : Exception(message, cause)
class HttpResponse(val body: String, val code: Int)
fun request(method: String, path: String, data: String?): HttpResponse {
val url = URL(BuildConfig.API_BASE_URL + path)
try {
with(url.openConnection() as HttpURLConnection) {
requestMethod = method
if (data != null) {
setRequestProperty("Content-Type", "application/json")
outputStream.write(data.toByteArray())
outputStream.flush()
}
if (responseCode >= 400)
return HttpResponse(errorStream.readBytes().decodeToString(), responseCode)
return HttpResponse(inputStream.readBytes().decodeToString(), responseCode)
}
} catch (e: IOException) {
Log.e("EasyEat", e.toString())
throw ApiRequestException("Failed to connect to server: " + e.message, e)
}
}
@Serializable
class HttpErrorResponse(val message: String)
inline fun <reified Req, reified Res> requestJson(method: String, path: String, data: Req?): Res {
val requestJson =
if (data != null)
Json.encodeToString(serializer<Req>(), data)
else null
val response = request(method, path, requestJson)
if (response.code >= 400) {
try {
val error = Json.decodeFromString<HttpErrorResponse>(response.body)
throw ApiRequestException(error.message, null)
} catch (e: SerializationException) {
if (e.message != null)
Log.e("EasyEat", e.message!!)
Log.e("EasyEat", response.body)
throw ApiRequestException("Request failed with HTTP status code ${response.code}", e)
}
}
try {
return Json.decodeFromString<Res>(response.body)
} catch (e: SerializationException) {
if (e.message != null)
Log.e("EasyEat", e.message!!)
Log.e("EasyEat", response.body)
throw ApiRequestException("Failed to parse response: $response", e)
}
}

View File

@ -0,0 +1,28 @@
package tech.mercantec.easyeat.helpers
import android.content.Context
import kotlinx.serialization.Serializable
@Serializable
data class LoginRequest(val emailUsr: String, val password: String)
@Serializable
data class LoginResponse(val token: String, val userName: String, val id: Int, val refreshToken: String)
fun login(email: String, password: String) {
val request = LoginRequest(email, password)
val response = requestJson<LoginRequest, LoginResponse>("POST", "/api/User/login", request)
// TODO save tokens
}
@Serializable
data class CreateUserRequest(val email: String, val userName: String, val password: String)
fun register(email: String, username: String, password: String) {
val request = CreateUserRequest(email, username, password)
requestJson<CreateUserRequest, Boolean>("POST", "/api/User/create", request)
}

View File

@ -24,6 +24,7 @@
/> />
<EditText <EditText
android:id="@+id/email"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/email_hint" android:hint="@string/email_hint"
@ -39,6 +40,7 @@
/> />
<EditText <EditText
android:id="@+id/password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/password_hint" android:hint="@string/password_hint"

View File

@ -24,10 +24,10 @@
/> />
<EditText <EditText
android:id="@+id/username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/username_hint" android:hint="@string/username_hint"
android:autofillHints="emailAddress"
/> />
<TextView <TextView
@ -39,6 +39,7 @@
/> />
<EditText <EditText
android:id="@+id/email"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/email_hint" android:hint="@string/email_hint"
@ -54,6 +55,7 @@
/> />
<EditText <EditText
android:id="@+id/password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/password_hint" android:hint="@string/password_hint"
@ -70,6 +72,7 @@
/> />
<EditText <EditText
android:id="@+id/confirm_password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/password_hint" android:hint="@string/password_hint"

View File

@ -2,4 +2,5 @@
plugins { plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.android) apply false
id("org.jetbrains.kotlin.plugin.serialization") version "1.7.10" apply false
} }

View File

@ -20,4 +20,6 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the # Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true

View File

@ -6,6 +6,7 @@ junit = "4.13.2"
junitVersion = "1.1.5" junitVersion = "1.1.5"
espressoCore = "3.5.1" espressoCore = "3.5.1"
appcompat = "1.6.1" appcompat = "1.6.1"
kotlinxSerializationJson = "1.2.2"
material = "1.10.0" material = "1.10.0"
constraintlayout = "2.1.4" constraintlayout = "2.1.4"
lifecycleLivedataKtx = "2.6.1" lifecycleLivedataKtx = "2.6.1"
@ -22,6 +23,7 @@ junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" } material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" } androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" }