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"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
<bytecodeTargetLevel target="17" />
</component>
</project>

View File

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</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" />
</component>
<component name="ProjectType">

View File

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

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"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"

View File

@ -3,10 +3,12 @@ package tech.mercantec.easyeat
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import androidx.activity.enableEdgeToEdge
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import tech.mercantec.easyeat.helpers.ApiRequestException
import tech.mercantec.easyeat.helpers.login
import kotlin.concurrent.thread
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -15,11 +17,24 @@ class LoginActivity : AppCompatActivity() {
setContentView(R.layout.activity_login)
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)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK // Prevent going back to login screen
startActivity(intent)
thread {
try {
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 {

View File

@ -3,10 +3,12 @@ package tech.mercantec.easyeat
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import androidx.activity.enableEdgeToEdge
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import tech.mercantec.easyeat.helpers.ApiRequestException
import tech.mercantec.easyeat.helpers.register
import kotlin.concurrent.thread
class RegisterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -15,10 +17,30 @@ class RegisterActivity : AppCompatActivity() {
setContentView(R.layout.activity_register)
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))
finish()
if (password != confirmPassword) {
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 {

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

View File

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

View File

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

View File

@ -21,3 +21,5 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true

View File

@ -6,6 +6,7 @@ junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
appcompat = "1.6.1"
kotlinxSerializationJson = "1.2.2"
material = "1.10.0"
constraintlayout = "2.1.4"
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-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
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" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" }