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

This commit is contained in:
Jeas0001 2025-05-01 12:44:07 +02:00
commit 67e6442255
28 changed files with 387 additions and 196 deletions

View File

@ -25,8 +25,7 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="false" android:exported="false" />
android:label="@string/title_activity_main" />
<activity <activity
android:name=".WelcomeActivity" android:name=".WelcomeActivity"

View File

@ -1,11 +1,15 @@
package tech.mercantec.easyeat package tech.mercantec.easyeat
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.Button import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Spinner
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import tech.mercantec.easyeat.models.Ingredient
class CreateDishActivity : AppCompatActivity() { class CreateDishActivity : AppCompatActivity() {
@ -25,6 +29,19 @@ class CreateDishActivity : AppCompatActivity() {
addButton.setOnClickListener { addButton.setOnClickListener {
addIngredientRow() addIngredientRow()
} }
val saveButton: Button = findViewById(R.id.saveDishButton)
saveButton.setOnClickListener {
val ingredientList = collectIngredients()
// Debug/log example
for (ingredient in ingredientList) {
Log.d("INGREDIENT", "Name: ${ingredient.Element}, Amount: ${ingredient.Amount}, Unit: ${ingredient.Unit}")
}
// You can now save to DB, send to backend, etc.
}
} }
private fun addIngredientRow() { private fun addIngredientRow() {
@ -39,4 +56,29 @@ class CreateDishActivity : AppCompatActivity() {
ingredientContainer.addView(ingredientRow) ingredientContainer.addView(ingredientRow)
} }
private fun collectIngredients(): List<Ingredient> {
val ingredients = mutableListOf<Ingredient>()
for (i in 0 until ingredientContainer.childCount) {
val ingredientView = ingredientContainer.getChildAt(i)
// Find views inside this ingredient row
val nameEditText = ingredientView.findViewById<EditText>(R.id.ingredientNameEditText)
val amountEditText = ingredientView.findViewById<EditText>(R.id.ingredientAmountEditText)
val spinner = ingredientView.findViewById<Spinner>(R.id.spinner)
val element = nameEditText.text.toString().trim()
val amount = amountEditText.text.toString().trim()
val unit = spinner.selectedItem.toString()
// Optional: Only add non-empty rows
if (element.isNotEmpty() && amount.isNotEmpty()) {
ingredients.add(Ingredient(Element = element, Amount = amount.toInt(), Unit = unit))
}
}
return ingredients
}
} }

View File

@ -27,7 +27,7 @@ class LoginActivity : AppCompatActivity() {
thread { thread {
try { try {
login(email, password) login(this, email, password)
} catch (e: ApiRequestException) { } catch (e: ApiRequestException) {
runOnUiThread { runOnUiThread {
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show() Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()

View File

@ -12,8 +12,10 @@ import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import tech.mercantec.easyeat.databinding.ActivityMainBinding import tech.mercantec.easyeat.databinding.ActivityMainBinding
import tech.mercantec.easyeat.helpers.getUserInfo
import tech.mercantec.easyeat.models.Dish import tech.mercantec.easyeat.models.Dish
import tech.mercantec.easyeat.ui.dishes.DishAdapter import tech.mercantec.easyeat.ui.dishes.DishAdapter
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -30,17 +32,15 @@ class MainActivity : AppCompatActivity() {
val navController = findNavController(R.id.nav_host_fragment_activity_main) val navController = findNavController(R.id.nav_host_fragment_activity_main)
val appBarConfiguration = AppBarConfiguration( val appBarConfiguration = AppBarConfiguration(
setOf( setOf(
R.id.navigation_dishes, R.id.navigation_shopping_list, R.id.navigation_notifications R.id.navigation_dishes, R.id.navigation_shopping_list, R.id.navigation_profile
) )
) )
setupActionBarWithNavController(navController, appBarConfiguration) setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController) navView.setupWithNavController(navController)
findViewById<FloatingActionButton>(R.id.add_dish).setOnClickListener { findViewById<FloatingActionButton>(R.id.add_dish).setOnClickListener {
val intent = Intent(this, CreateDishActivity::class.java) val intent = Intent(this, CreateDishActivity::class.java)
startActivity(intent) startActivity(intent)
} }
} }
} }

View File

@ -34,7 +34,7 @@ class RegisterActivity : AppCompatActivity() {
thread { thread {
try { try {
register(email, username, password) register(this, email, username, password)
} catch (e: ApiRequestException) { } catch (e: ApiRequestException) {
runOnUiThread { runOnUiThread {
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show() Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()

View File

@ -3,15 +3,13 @@ package tech.mercantec.easyeat
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import tech.mercantec.easyeat.helpers.isLoggedIn
class SplashActivity : Activity() { class SplashActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// TODO do auth check if (!isLoggedIn(this)) {
val loggedIn = false
if (!loggedIn) {
startActivity(Intent(this, WelcomeActivity::class.java)) startActivity(Intent(this, WelcomeActivity::class.java))
return return

View File

@ -1,6 +1,7 @@
package tech.mercantec.easyeat.helpers package tech.mercantec.easyeat.helpers
import android.content.Context import android.content.Context
import android.content.Intent
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -10,19 +11,27 @@ import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import tech.mercantec.easyeat.LoginActivity
import java.io.IOException import java.io.IOException
class ApiRequestException(message: String, cause: Throwable?) : Exception(message, cause) class ApiRequestException(message: String, cause: Throwable?) : Exception(message, cause)
class HttpResponse(val body: String, val code: Int) class HttpResponse(val body: String, val code: Int)
fun request(method: String, path: String, data: String?): HttpResponse { fun request(ctx: Context, method: String, path: String, data: String?, autoRefresh: Boolean = true): HttpResponse {
val url = URL(BuildConfig.API_BASE_URL + path) val url = URL(BuildConfig.API_BASE_URL + path)
val prefs = ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE)
val authToken = prefs.getString("auth-token", null)
val refreshToken = prefs.getString("auth-token", null)
try { try {
with(url.openConnection() as HttpURLConnection) { with(url.openConnection() as HttpURLConnection) {
requestMethod = method requestMethod = method
if (authToken != null)
setRequestProperty("Authorization", "Bearer $authToken")
if (data != null) { if (data != null) {
setRequestProperty("Content-Type", "application/json") setRequestProperty("Content-Type", "application/json")
@ -30,6 +39,18 @@ fun request(method: String, path: String, data: String?): HttpResponse {
outputStream.flush() outputStream.flush()
} }
if (responseCode == 401 && refreshToken != null) {
if (!autoRefresh || !refreshAuthToken(ctx, refreshToken)) {
val intent = Intent(ctx, LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
ctx.startActivity(intent)
throw ApiRequestException("Please sign in again", null)
}
return request(ctx, method, path, data, false)
}
if (responseCode >= 400) if (responseCode >= 400)
return HttpResponse(errorStream.readBytes().decodeToString(), responseCode) return HttpResponse(errorStream.readBytes().decodeToString(), responseCode)
@ -45,13 +66,13 @@ fun request(method: String, path: String, data: String?): HttpResponse {
@Serializable @Serializable
class HttpErrorResponse(val message: String) class HttpErrorResponse(val message: String)
inline fun <reified Req, reified Res> requestJson(method: String, path: String, data: Req?): Res { inline fun <reified Req, reified Res> requestJson(ctx: Context, method: String, path: String, data: Req?): Res {
val requestJson = val requestJson =
if (data != null) if (data != null)
Json.encodeToString(serializer<Req>(), data) Json.encodeToString(serializer<Req>(), data)
else null else null
val response = request(method, path, requestJson) val response = request(ctx, method, path, requestJson)
if (response.code >= 400) { if (response.code >= 400) {
try { try {

View File

@ -1,6 +1,7 @@
package tech.mercantec.easyeat.helpers package tech.mercantec.easyeat.helpers
import android.content.Context import android.content.Context
import android.util.Log
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
@ -9,20 +10,76 @@ data class LoginRequest(val emailUsr: String, val password: String)
@Serializable @Serializable
data class LoginResponse(val token: String, val userName: String, val id: Int, val refreshToken: String) data class LoginResponse(val token: String, val userName: String, val id: Int, val refreshToken: String)
fun login(email: String, password: String) { fun login(ctx: Context, email: String, password: String) {
val request = LoginRequest(email, password) val request = LoginRequest(email, password)
val response = requestJson<LoginRequest, LoginResponse>("POST", "/api/User/login", request) val response = requestJson<LoginRequest, LoginResponse>(ctx, "POST", "/api/User/login", request)
// TODO save tokens
with (ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE).edit()) {
putInt("user-id", response.id)
putString("username", response.userName)
putString("auth-token", response.token)
putString("refresh-token", response.refreshToken)
apply()
}
} }
@Serializable @Serializable
data class CreateUserRequest(val email: String, val userName: String, val password: String) data class CreateUserRequest(val email: String, val userName: String, val password: String)
fun register(email: String, username: String, password: String) { fun register(ctx: Context, email: String, username: String, password: String) {
val request = CreateUserRequest(email, username, password) val request = CreateUserRequest(email, username, password)
requestJson<CreateUserRequest, Boolean>("POST", "/api/User/create", request) requestJson<CreateUserRequest, Boolean>(ctx, "POST", "/api/User/create", request)
}
@Serializable
data class RefreshTokenRequest(val refreshToken: String)
@Serializable
data class RefreshTokenResponse(val token: String, val refreshToken: String)
fun refreshAuthToken(ctx: Context, refreshToken: String): Boolean {
val response: RefreshTokenResponse
try {
response = requestJson<RefreshTokenRequest, RefreshTokenResponse>(
ctx,
"POST", "/api/User/refreshtoken",
RefreshTokenRequest(refreshToken)
)
} catch (e: ApiRequestException) {
if (e.message != null)
Log.e("EasyEat", e.message!!)
return false
}
with (ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE).edit()) {
putString("auth-token", response.token)
putString("refresh-token", response.refreshToken)
apply()
}
return true
}
fun isLoggedIn(ctx: Context): Boolean {
val prefs = ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE)
val authToken = prefs.getString("auth-token", null)
val refreshToken = prefs.getString("refresh-token", null) ?: return false
if (authToken == null) {
return refreshAuthToken(ctx, refreshToken)
}
return true
}
@Serializable
data class UserInfoResponse(val id: Int, val userName: String, val email: String)
fun getUserInfo(ctx: Context): UserInfoResponse {
val response = requestJson<Unit, UserInfoResponse>(ctx, "GET", "/api/User/get", null)
return response
} }

View File

@ -0,0 +1,7 @@
package tech.mercantec.easyeat.models
data class Ingredient(
val Amount: Int,
val Unit: String,
val Element: String
)

View File

@ -1,11 +1,13 @@
package tech.mercantec.easyeat.ui.dishes package tech.mercantec.easyeat.ui.dishes
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import tech.mercantec.easyeat.CreateDishActivity
import tech.mercantec.easyeat.databinding.FragmentDishesBinding import tech.mercantec.easyeat.databinding.FragmentDishesBinding
import tech.mercantec.easyeat.models.Dish import tech.mercantec.easyeat.models.Dish
@ -25,6 +27,11 @@ class DishesFragment : Fragment() {
_binding = FragmentDishesBinding.inflate(inflater, container, false) _binding = FragmentDishesBinding.inflate(inflater, container, false)
val root: View = binding.root val root: View = binding.root
binding.addDish.setOnClickListener {
val intent = Intent(requireContext(), CreateDishActivity::class.java)
startActivity(intent)
}
context?.let { context -> context?.let { context ->
binding.dishesList.setOnItemClickListener { parent, view, position, id -> binding.dishesList.setOnItemClickListener { parent, view, position, id ->
val selectedItem = parent.getItemAtPosition(position) as Dish val selectedItem = parent.getItemAtPosition(position) as Dish
@ -47,9 +54,13 @@ class DishesFragment : Fragment() {
binding.dishesList.adapter = DishAdapter(context, listItems) binding.dishesList.adapter = DishAdapter(context, listItems)
} }
return root return root
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null

View File

@ -1,42 +0,0 @@
package tech.mercantec.easyeat.ui.notifications
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import tech.mercantec.easyeat.databinding.FragmentNotificationsBinding
class NotificationsFragment : Fragment() {
private var _binding: FragmentNotificationsBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val notificationsViewModel =
ViewModelProvider(this).get(NotificationsViewModel::class.java)
_binding = FragmentNotificationsBinding.inflate(inflater, container, false)
val root: View = binding.root
val textView: TextView = binding.textNotifications
notificationsViewModel.text.observe(viewLifecycleOwner) {
textView.text = it
}
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@ -1,13 +0,0 @@
package tech.mercantec.easyeat.ui.notifications
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class NotificationsViewModel : ViewModel() {
private val _text = MutableLiveData<String>().apply {
value = "This is notifications Fragment"
}
val text: LiveData<String> = _text
}

View File

@ -0,0 +1,39 @@
package tech.mercantec.easyeat.ui.profile
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import tech.mercantec.easyeat.databinding.FragmentProfileBinding
import tech.mercantec.easyeat.helpers.ApiRequestException
import tech.mercantec.easyeat.helpers.UserInfoResponse
import tech.mercantec.easyeat.helpers.getUserInfo
import kotlin.concurrent.thread
class ProfileFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val binding = FragmentProfileBinding.inflate(inflater, container, false)
thread {
val userInfo: UserInfoResponse
try {
userInfo = getUserInfo(requireContext())
} catch (e: ApiRequestException) {
activity?.runOnUiThread {
Toast.makeText(activity?.applicationContext, e.message, Toast.LENGTH_LONG).show()
}
return@thread
}
activity?.runOnUiThread {
binding.username.text = userInfo.userName
binding.email.text = userInfo.email
}
}
return binding.root
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M234,684Q285,645 348,622.5Q411,600 480,600Q549,600 612,622.5Q675,645 726,684Q761,643 780.5,591Q800,539 800,480Q800,347 706.5,253.5Q613,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,539 179.5,591Q199,643 234,684ZM480,520Q421,520 380.5,479.5Q340,439 340,380Q340,321 380.5,280.5Q421,240 480,240Q539,240 579.5,280.5Q620,321 620,380Q620,439 579.5,479.5Q539,520 480,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,480Q414,480 367,433Q320,386 320,320Q320,254 367,207Q414,160 480,160Q546,160 593,207Q640,254 640,320Q640,386 593,433Q546,480 480,480ZM160,800L160,688Q160,654 177.5,625.5Q195,597 224,582Q286,551 350,535.5Q414,520 480,520Q546,520 610,535.5Q674,551 736,582Q765,597 782.5,625.5Q800,654 800,688L800,800L160,800Z"/>
</vector>

View File

@ -27,5 +27,13 @@
android:text="Add Ingredient" android:text="Add Ingredient"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"/> android:layout_marginTop="16dp"/>
<Button
android:id="@+id/saveDishButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save"
android:layout_gravity="right"
android:layout_marginTop="16dp"/>
</LinearLayout> </LinearLayout>

View File

@ -1,10 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical">
android:padding="8dp">
<!-- Ingredient Field --> <!-- Ingredient Field -->
<TextView <TextView
@ -14,6 +17,7 @@
android:text="@string/ingredient_label" /> android:text="@string/ingredient_label" />
<EditText <EditText
android:id="@+id/ingredientNameEditText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="58dp" android:layout_height="58dp"
android:hint="Ingredient name" /> android:hint="Ingredient name" />
@ -26,7 +30,7 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:layout_marginTop="8dp"> android:layout_marginTop="8dp">
<!-- Amount field (fixed width) --> <!-- Amount field -->
<LinearLayout <LinearLayout
android:layout_width="174dp" android:layout_width="174dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -38,12 +42,13 @@
android:text="@string/amount_label" /> android:text="@string/amount_label" />
<EditText <EditText
android:id="@+id/ingredientAmountEditText"
android:layout_width="173dp" android:layout_width="173dp"
android:layout_height="48dp" android:layout_height="48dp"
android:hint="0" /> android:hint="0" />
</LinearLayout> </LinearLayout>
<!-- Measurement field (fill rest) --> <!-- Measurement field -->
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -62,7 +67,7 @@
android:layout_height="48dp" /> android:layout_height="48dp" />
</LinearLayout> </LinearLayout>
<!-- Remove Button (fixed width) --> <!-- Remove Button -->
<Button <Button
android:id="@+id/removeButton" android:id="@+id/removeButton"
android:layout_width="48dp" android:layout_width="48dp"
@ -70,11 +75,7 @@
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:text="-" android:text="-"
android:textSize="20dp" android:textSize="20dp"
android:backgroundTint="@color/red"/> android:backgroundTint="@color/red" />
</LinearLayout> </LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@ -8,19 +8,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize"> android:paddingTop="?attr/actionBarSize">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_dish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="@string/add_label"
app:srcCompat="@android:drawable/ic_input_add"
app:backgroundTint="@color/cyan"
app:tint="@android:color/white"
app:layout_constraintBottom_toTopOf="@id/nav_view"
app:layout_constraintEnd_toEndOf="parent" />
<fragment <fragment
android:id="@+id/nav_host_fragment_activity_main" android:id="@+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment" android:name="androidx.navigation.fragment.NavHostFragment"

View File

@ -1,12 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dishes_fragment_root"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<!-- Your list view -->
<ListView <ListView
android:id="@+id/dishes_list" android:id="@+id/dishes_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="8dp" android:divider="@android:color/darker_gray"
/> android:dividerHeight="1dp" />
<!-- FloatingActionButton -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_dish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:backgroundTint="@color/cyan"
app:srcCompat="@android:drawable/ic_input_add"
app:tint="@android:color/white"/>
</FrameLayout> </FrameLayout>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.notifications.NotificationsFragment">
<TextView
android:id="@+id/text_notifications"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingStart="30sp"
android:paddingEnd="30sp"
android:gravity="center">
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:indeterminate="true"
/>
<ImageView
android:layout_width="match_parent"
android:layout_height="72dp"
android:src="@drawable/ic_account_circle_24px"
/>
<TextView
android:id="@+id/username"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textStyle="bold"
android:textAlignment="center"
android:text="@string/loading"
/>
<TextView
android:id="@+id/email"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textAlignment="center"
android:text="@string/loading"
/>
<Button
android:layout_marginTop="40dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit_profile_label"
/>
</LinearLayout>

View File

@ -3,17 +3,17 @@
<item <item
android:id="@+id/navigation_dishes" android:id="@+id/navigation_dishes"
android:icon="@drawable/ic_restaurant_black_24px" android:icon="@drawable/ic_restaurant_24px"
android:title="@string/title_your_dishes" /> android:title="@string/title_your_dishes" />
<item <item
android:id="@+id/navigation_shopping_list" android:id="@+id/navigation_shopping_list"
android:icon="@drawable/ic_list_alt_black_24px" android:icon="@drawable/ic_list_alt_24px"
android:title="@string/title_shopping_list" /> android:title="@string/title_shopping_list" />
<item <item
android:id="@+id/navigation_notifications" android:id="@+id/navigation_profile"
android:icon="@drawable/ic_notifications_black_24dp" android:icon="@drawable/ic_person_24px"
android:title="@string/title_notifications" /> android:title="@string/title_profile" />
</menu> </menu>

View File

@ -18,8 +18,8 @@
tools:layout="@layout/fragment_shopping_list" /> tools:layout="@layout/fragment_shopping_list" />
<fragment <fragment
android:id="@+id/navigation_notifications" android:id="@+id/navigation_profile"
android:name="tech.mercantec.easyeat.ui.notifications.NotificationsFragment" android:name="tech.mercantec.easyeat.ui.profile.ProfileFragment"
android:label="@string/title_notifications" android:label="@string/title_profile"
tools:layout="@layout/fragment_notifications" /> tools:layout="@layout/fragment_profile" />
</navigation> </navigation>

View File

@ -1,9 +1,8 @@
<resources> <resources>
<string name="app_name">EasyEat</string> <string name="app_name">EasyEat</string>
<string name="title_activity_main">MainActivity</string>
<string name="title_your_dishes">Your Dishes</string> <string name="title_your_dishes">Your Dishes</string>
<string name="title_shopping_list">Shopping list</string> <string name="title_shopping_list">Shopping list</string>
<string name="title_notifications">Notifications</string> <string name="title_profile">Profile</string>
<string name="welcome_heading">Welcome to <font color="#0099BA">EasyEat</font></string> <string name="welcome_heading">Welcome to <font color="#0099BA">EasyEat</font></string>
<string name="login_label">Login</string> <string name="login_label">Login</string>
<string name="login_heading">Login to EasyEat</string> <string name="login_heading">Login to EasyEat</string>
@ -22,5 +21,6 @@
<string name="measurement_label">Measurement</string> <string name="measurement_label">Measurement</string>
<string name="amount_label">Amount</string> <string name="amount_label">Amount</string>
<string name="add_label">Add</string> <string name="add_label">Add</string>
<string name="edit_profile_label">Edit profile</string>
<string name="loading">Loading…</string>
</resources> </resources>

View File

@ -0,0 +1,20 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Database": "Data Source=database.sqlite3"
},
"JwtSettings": {
"Issuer": "TemperatureAlarmApi",
"Audience": "Customers",
"Key": ""
}
}