diff --git a/app/app/src/main/AndroidManifest.xml b/app/app/src/main/AndroidManifest.xml
index 215816d..b07b3bd 100644
--- a/app/app/src/main/AndroidManifest.xml
+++ b/app/app/src/main/AndroidManifest.xml
@@ -25,8 +25,7 @@
+ android:exported="false" />
{
+ val ingredients = mutableListOf()
+
+ for (i in 0 until ingredientContainer.childCount) {
+ val ingredientView = ingredientContainer.getChildAt(i)
+
+ // Find views inside this ingredient row
+ val nameEditText = ingredientView.findViewById(R.id.ingredientNameEditText)
+ val amountEditText = ingredientView.findViewById(R.id.ingredientAmountEditText)
+ val spinner = ingredientView.findViewById(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
+ }
+
}
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/LoginActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/LoginActivity.kt
index 3091da5..a56504a 100644
--- a/app/app/src/main/java/tech/mercantec/easyeat/LoginActivity.kt
+++ b/app/app/src/main/java/tech/mercantec/easyeat/LoginActivity.kt
@@ -27,7 +27,7 @@ class LoginActivity : AppCompatActivity() {
thread {
try {
- login(email, password)
+ login(this, email, password)
} catch (e: ApiRequestException) {
runOnUiThread {
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/MainActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/MainActivity.kt
index 8355af8..8675456 100644
--- a/app/app/src/main/java/tech/mercantec/easyeat/MainActivity.kt
+++ b/app/app/src/main/java/tech/mercantec/easyeat/MainActivity.kt
@@ -12,8 +12,10 @@ import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.floatingactionbutton.FloatingActionButton
import tech.mercantec.easyeat.databinding.ActivityMainBinding
+import tech.mercantec.easyeat.helpers.getUserInfo
import tech.mercantec.easyeat.models.Dish
import tech.mercantec.easyeat.ui.dishes.DishAdapter
+import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() {
@@ -30,17 +32,15 @@ class MainActivity : AppCompatActivity() {
val navController = findNavController(R.id.nav_host_fragment_activity_main)
val appBarConfiguration = AppBarConfiguration(
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)
navView.setupWithNavController(navController)
-
findViewById(R.id.add_dish).setOnClickListener {
val intent = Intent(this, CreateDishActivity::class.java)
startActivity(intent)
}
-
}
}
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/RegisterActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/RegisterActivity.kt
index 52059b7..d3c0037 100644
--- a/app/app/src/main/java/tech/mercantec/easyeat/RegisterActivity.kt
+++ b/app/app/src/main/java/tech/mercantec/easyeat/RegisterActivity.kt
@@ -34,7 +34,7 @@ class RegisterActivity : AppCompatActivity() {
thread {
try {
- register(email, username, password)
+ register(this, email, username, password)
} catch (e: ApiRequestException) {
runOnUiThread {
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/SplashActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/SplashActivity.kt
index 414bc9a..d6141ce 100644
--- a/app/app/src/main/java/tech/mercantec/easyeat/SplashActivity.kt
+++ b/app/app/src/main/java/tech/mercantec/easyeat/SplashActivity.kt
@@ -3,15 +3,13 @@ package tech.mercantec.easyeat
import android.app.Activity
import android.content.Intent
import android.os.Bundle
+import tech.mercantec.easyeat.helpers.isLoggedIn
class SplashActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- // TODO do auth check
- val loggedIn = false
-
- if (!loggedIn) {
+ if (!isLoggedIn(this)) {
startActivity(Intent(this, WelcomeActivity::class.java))
return
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/api.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/api.kt
index 0adb879..e9a472d 100644
--- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/api.kt
+++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/api.kt
@@ -1,6 +1,7 @@
package tech.mercantec.easyeat.helpers
import android.content.Context
+import android.content.Intent
import android.util.Log
import android.widget.Toast
import kotlinx.serialization.Serializable
@@ -10,19 +11,27 @@ import java.net.HttpURLConnection
import java.net.URL
import kotlinx.serialization.json.*
import kotlinx.serialization.serializer
+import tech.mercantec.easyeat.LoginActivity
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 {
+fun request(ctx: Context, method: String, path: String, data: String?, autoRefresh: Boolean = true): HttpResponse {
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 {
with(url.openConnection() as HttpURLConnection) {
requestMethod = method
+ if (authToken != null)
+ setRequestProperty("Authorization", "Bearer $authToken")
+
if (data != null) {
setRequestProperty("Content-Type", "application/json")
@@ -30,6 +39,18 @@ fun request(method: String, path: String, data: String?): HttpResponse {
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)
return HttpResponse(errorStream.readBytes().decodeToString(), responseCode)
@@ -45,13 +66,13 @@ fun request(method: String, path: String, data: String?): HttpResponse {
@Serializable
class HttpErrorResponse(val message: String)
-inline fun requestJson(method: String, path: String, data: Req?): Res {
+inline fun requestJson(ctx: Context, method: String, path: String, data: Req?): Res {
val requestJson =
if (data != null)
Json.encodeToString(serializer(), data)
else null
- val response = request(method, path, requestJson)
+ val response = request(ctx, method, path, requestJson)
if (response.code >= 400) {
try {
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt
index 0467bc4..6e5e54b 100644
--- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt
+++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt
@@ -1,6 +1,7 @@
package tech.mercantec.easyeat.helpers
import android.content.Context
+import android.util.Log
import kotlinx.serialization.Serializable
@Serializable
@@ -9,20 +10,76 @@ 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) {
+fun login(ctx: Context, email: String, password: String) {
val request = LoginRequest(email, password)
- val response = requestJson("POST", "/api/User/login", request)
-
- // TODO save tokens
+ val response = requestJson(ctx, "POST", "/api/User/login", request)
+ 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
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)
- requestJson("POST", "/api/User/create", request)
+ requestJson(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(
+ 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(ctx, "GET", "/api/User/get", null)
+
+ return response
}
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/models/Ingredient.kt b/app/app/src/main/java/tech/mercantec/easyeat/models/Ingredient.kt
new file mode 100644
index 0000000..7413734
--- /dev/null
+++ b/app/app/src/main/java/tech/mercantec/easyeat/models/Ingredient.kt
@@ -0,0 +1,7 @@
+package tech.mercantec.easyeat.models
+
+data class Ingredient(
+ val Amount: Int,
+ val Unit: String,
+ val Element: String
+)
\ No newline at end of file
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt
index c54aeda..72feca8 100644
--- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt
+++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/DishesFragment.kt
@@ -1,11 +1,13 @@
package tech.mercantec.easyeat.ui.dishes
+import android.content.Intent
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.CreateDishActivity
import tech.mercantec.easyeat.databinding.FragmentDishesBinding
import tech.mercantec.easyeat.models.Dish
@@ -25,6 +27,11 @@ class DishesFragment : Fragment() {
_binding = FragmentDishesBinding.inflate(inflater, container, false)
val root: View = binding.root
+ binding.addDish.setOnClickListener {
+ val intent = Intent(requireContext(), CreateDishActivity::class.java)
+ startActivity(intent)
+ }
+
context?.let { context ->
binding.dishesList.setOnItemClickListener { parent, view, position, id ->
val selectedItem = parent.getItemAtPosition(position) as Dish
@@ -47,9 +54,13 @@ class DishesFragment : Fragment() {
binding.dishesList.adapter = DishAdapter(context, listItems)
}
+
return root
}
+
+
+
override fun onDestroyView() {
super.onDestroyView()
_binding = null
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/notifications/NotificationsFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/notifications/NotificationsFragment.kt
deleted file mode 100644
index d84d797..0000000
--- a/app/app/src/main/java/tech/mercantec/easyeat/ui/notifications/NotificationsFragment.kt
+++ /dev/null
@@ -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
- }
-}
\ No newline at end of file
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/notifications/NotificationsViewModel.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/notifications/NotificationsViewModel.kt
deleted file mode 100644
index 3575983..0000000
--- a/app/app/src/main/java/tech/mercantec/easyeat/ui/notifications/NotificationsViewModel.kt
+++ /dev/null
@@ -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().apply {
- value = "This is notifications Fragment"
- }
- val text: LiveData = _text
-}
\ No newline at end of file
diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/ProfileFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/ProfileFragment.kt
new file mode 100644
index 0000000..506f555
--- /dev/null
+++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/profile/ProfileFragment.kt
@@ -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
+ }
+}
diff --git a/app/app/src/main/res/drawable/ic_account_circle_24px.xml b/app/app/src/main/res/drawable/ic_account_circle_24px.xml
new file mode 100644
index 0000000..3f33c15
--- /dev/null
+++ b/app/app/src/main/res/drawable/ic_account_circle_24px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/app/src/main/res/drawable/ic_list_alt_black_24px.xml b/app/app/src/main/res/drawable/ic_list_alt_24px.xml
similarity index 100%
rename from app/app/src/main/res/drawable/ic_list_alt_black_24px.xml
rename to app/app/src/main/res/drawable/ic_list_alt_24px.xml
diff --git a/app/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/app/src/main/res/drawable/ic_notifications_black_24dp.xml
deleted file mode 100644
index 78b75c3..0000000
--- a/app/app/src/main/res/drawable/ic_notifications_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/app/src/main/res/drawable/ic_person_24px.xml b/app/app/src/main/res/drawable/ic_person_24px.xml
new file mode 100644
index 0000000..6fcf704
--- /dev/null
+++ b/app/app/src/main/res/drawable/ic_person_24px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/app/src/main/res/drawable/ic_restaurant_black_24px.xml b/app/app/src/main/res/drawable/ic_restaurant_24px.xml
similarity index 100%
rename from app/app/src/main/res/drawable/ic_restaurant_black_24px.xml
rename to app/app/src/main/res/drawable/ic_restaurant_24px.xml
diff --git a/app/app/src/main/res/layout/activity_create_dish_form.xml b/app/app/src/main/res/layout/activity_create_dish_form.xml
index 62e333a..cfb4252 100644
--- a/app/app/src/main/res/layout/activity_create_dish_form.xml
+++ b/app/app/src/main/res/layout/activity_create_dish_form.xml
@@ -27,5 +27,13 @@
android:text="Add Ingredient"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"/>
+
+
diff --git a/app/app/src/main/res/layout/activity_create_dish_ingredient_row.xml b/app/app/src/main/res/layout/activity_create_dish_ingredient_row.xml
index 7876c99..9235593 100644
--- a/app/app/src/main/res/layout/activity_create_dish_ingredient_row.xml
+++ b/app/app/src/main/res/layout/activity_create_dish_ingredient_row.xml
@@ -1,80 +1,81 @@
-
-
-
-
-
-
-
+ android:orientation="vertical">
-
+
+
+
+
+
+
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_marginTop="8dp">
-
+
+ android:orientation="vertical">
-
-
+
-
-
+
+
-
+
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_marginStart="8dp">
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
diff --git a/app/app/src/main/res/layout/activity_main.xml b/app/app/src/main/res/layout/activity_main.xml
index 478b74a..37665d9 100644
--- a/app/app/src/main/res/layout/activity_main.xml
+++ b/app/app/src/main/res/layout/activity_main.xml
@@ -8,19 +8,6 @@
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize">
-
-
-
-
+
+
+ android:divider="@android:color/darker_gray"
+ android:dividerHeight="1dp" />
+
+
+
diff --git a/app/app/src/main/res/layout/fragment_notifications.xml b/app/app/src/main/res/layout/fragment_notifications.xml
deleted file mode 100644
index d417935..0000000
--- a/app/app/src/main/res/layout/fragment_notifications.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/app/src/main/res/layout/fragment_profile.xml b/app/app/src/main/res/layout/fragment_profile.xml
new file mode 100644
index 0000000..97bbb99
--- /dev/null
+++ b/app/app/src/main/res/layout/fragment_profile.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/app/src/main/res/menu/bottom_nav_menu.xml b/app/app/src/main/res/menu/bottom_nav_menu.xml
index 40d55c4..0e5609d 100644
--- a/app/app/src/main/res/menu/bottom_nav_menu.xml
+++ b/app/app/src/main/res/menu/bottom_nav_menu.xml
@@ -3,17 +3,17 @@
+ android:id="@+id/navigation_profile"
+ android:icon="@drawable/ic_person_24px"
+ android:title="@string/title_profile" />
\ No newline at end of file
diff --git a/app/app/src/main/res/navigation/mobile_navigation.xml b/app/app/src/main/res/navigation/mobile_navigation.xml
index 8e402fb..3457448 100644
--- a/app/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/app/src/main/res/navigation/mobile_navigation.xml
@@ -18,8 +18,8 @@
tools:layout="@layout/fragment_shopping_list" />
+ android:id="@+id/navigation_profile"
+ android:name="tech.mercantec.easyeat.ui.profile.ProfileFragment"
+ android:label="@string/title_profile"
+ tools:layout="@layout/fragment_profile" />
\ No newline at end of file
diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml
index 26b9786..47af1ac 100644
--- a/app/app/src/main/res/values/strings.xml
+++ b/app/app/src/main/res/values/strings.xml
@@ -1,9 +1,8 @@
EasyEat
- MainActivity
Your Dishes
Shopping list
- Notifications
+ Profile
Welcome to EasyEat
Login
Login to EasyEat
@@ -22,5 +21,6 @@
Measurement
Amount
Add
-
+ Edit profile
+ Loading…
diff --git a/backend/API/appsettings.example.json b/backend/API/appsettings.example.json
new file mode 100644
index 0000000..c3763aa
--- /dev/null
+++ b/backend/API/appsettings.example.json
@@ -0,0 +1,20 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+
+ "ConnectionStrings": {
+ "Database": "Data Source=database.sqlite3"
+ },
+
+ "JwtSettings": {
+ "Issuer": "TemperatureAlarmApi",
+ "Audience": "Customers",
+ "Key": ""
+ }
+}
+