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

This commit is contained in:
Jeas0001 2025-05-06 13:57:11 +02:00
commit b8ebac5970
21 changed files with 476 additions and 45 deletions

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" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
@ -24,24 +25,32 @@
</activity> </activity>
<activity <activity
android:name=".MainActivity" android:name=".ui.MainActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".WelcomeActivity" android:name=".ui.auth.WelcomeActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".LoginActivity" android:name=".ui.auth.LoginActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".RegisterActivity" android:name=".ui.auth.RegisterActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".CreateDishActivity" /> android:name=".ui.dishes.CreateDishActivity"
android:exported="false" />
<activity
android:name=".ui.profile.ChangePasswordActivity"
android:exported="false" />
<activity
android:name=".ui.profile.EditProfileActivity"
android:exported="false" />
</application> </application>
</manifest> </manifest>

View File

@ -4,6 +4,8 @@ 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 import tech.mercantec.easyeat.helpers.isLoggedIn
import tech.mercantec.easyeat.ui.MainActivity
import tech.mercantec.easyeat.ui.auth.WelcomeActivity
class SplashActivity : Activity() { class SplashActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -17,4 +19,4 @@ class SplashActivity : Activity() {
startActivity(Intent(this, MainActivity::class.java)) startActivity(Intent(this, MainActivity::class.java))
} }
} }

View File

@ -3,7 +3,6 @@ package tech.mercantec.easyeat.helpers
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.Log import android.util.Log
import android.widget.Toast
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException import kotlinx.serialization.SerializationException
import tech.mercantec.easyeat.BuildConfig import tech.mercantec.easyeat.BuildConfig
@ -11,7 +10,7 @@ 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 tech.mercantec.easyeat.ui.auth.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)
@ -39,8 +38,8 @@ fun request(ctx: Context, method: String, path: String, data: String?, autoRefre
outputStream.flush() outputStream.flush()
} }
if (responseCode == 401 && refreshToken != null) { if (responseCode == 401) {
if (!autoRefresh || !refreshAuthToken(ctx, refreshToken)) { if (!autoRefresh || refreshToken == null || !refreshAuthToken(ctx, refreshToken)) {
val intent = Intent(ctx, LoginActivity::class.java) val intent = Intent(ctx, LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
ctx.startActivity(intent) ctx.startActivity(intent)

View File

@ -24,6 +24,16 @@ fun login(ctx: Context, email: String, password: String) {
} }
} }
fun logout(ctx: Context) {
with (ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE).edit()) {
remove("user-id")
remove("username")
remove("auth-token")
remove("refresh-token")
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)
@ -79,7 +89,23 @@ fun isLoggedIn(ctx: Context): Boolean {
data class UserInfoResponse(val id: Int, val userName: String, val email: String) data class UserInfoResponse(val id: Int, val userName: String, val email: String)
fun getUserInfo(ctx: Context): UserInfoResponse { fun getUserInfo(ctx: Context): UserInfoResponse {
val response = requestJson<Unit, UserInfoResponse>(ctx, "GET", "/api/User/get", null) return requestJson<Unit, UserInfoResponse>(ctx, "GET", "/api/User/get", null)
}
return response
@Serializable
data class UpdateUserRequest(val userName: String, val email: String)
fun updateUser(ctx: Context, username: String, email: String) {
val request = UpdateUserRequest(username, email)
return requestJson<UpdateUserRequest, Unit>(ctx, "PUT", "/api/User/update", request)
}
@Serializable
data class ChangePasswordRequest(val oldPassword: String, val newPassword: String)
fun changePassword(ctx: Context, oldPassword: String, newPassword: String) {
val request = ChangePasswordRequest(oldPassword, newPassword)
return requestJson<ChangePasswordRequest, Unit>(ctx, "PUT", "/api/User/change-password", request)
} }

View File

@ -1,9 +1,7 @@
package tech.mercantec.easyeat package tech.mercantec.easyeat.ui
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.widget.ListView
import android.widget.Toast
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController import androidx.navigation.findNavController
@ -12,10 +10,7 @@ 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.ui.dishes.CreateDishActivity
import tech.mercantec.easyeat.models.Dish
import tech.mercantec.easyeat.ui.dishes.DishAdapter
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -29,16 +24,18 @@ class MainActivity : AppCompatActivity() {
val navView: BottomNavigationView = binding.navView val navView: BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main) val navController = findNavController(tech.mercantec.easyeat.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_profile tech.mercantec.easyeat.R.id.navigation_dishes,
tech.mercantec.easyeat.R.id.navigation_shopping_list,
tech.mercantec.easyeat.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>(tech.mercantec.easyeat.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

@ -1,4 +1,4 @@
package tech.mercantec.easyeat package tech.mercantec.easyeat.ui.auth
import android.app.ProgressDialog import android.app.ProgressDialog
import android.content.Intent import android.content.Intent
@ -7,6 +7,8 @@ import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import tech.mercantec.easyeat.ui.MainActivity
import tech.mercantec.easyeat.R
import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.ApiRequestException
import tech.mercantec.easyeat.helpers.login import tech.mercantec.easyeat.helpers.login
import kotlin.concurrent.thread import kotlin.concurrent.thread

View File

@ -1,4 +1,4 @@
package tech.mercantec.easyeat package tech.mercantec.easyeat.ui.auth
import android.app.ProgressDialog import android.app.ProgressDialog
import android.content.Intent import android.content.Intent
@ -7,6 +7,7 @@ import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import tech.mercantec.easyeat.R
import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.ApiRequestException
import tech.mercantec.easyeat.helpers.register import tech.mercantec.easyeat.helpers.register
import kotlin.concurrent.thread import kotlin.concurrent.thread

View File

@ -1,12 +1,10 @@
package tech.mercantec.easyeat package tech.mercantec.easyeat.ui.auth
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 androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import tech.mercantec.easyeat.R
import androidx.core.view.WindowInsetsCompat
class WelcomeActivity : AppCompatActivity() { class WelcomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

View File

@ -1,4 +1,4 @@
package tech.mercantec.easyeat package tech.mercantec.easyeat.ui.dishes
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@ -10,7 +10,7 @@ import android.widget.ImageButton
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Spinner import android.widget.Spinner
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContentProviderCompat.requireContext import tech.mercantec.easyeat.R
import tech.mercantec.easyeat.models.Ingredient import tech.mercantec.easyeat.models.Ingredient
class CreateDishActivity : AppCompatActivity() { class CreateDishActivity : AppCompatActivity() {

View File

@ -7,7 +7,6 @@ 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

View File

@ -0,0 +1,57 @@
package tech.mercantec.easyeat.ui.profile
import android.app.ProgressDialog
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import tech.mercantec.easyeat.R
import tech.mercantec.easyeat.helpers.ApiRequestException
import tech.mercantec.easyeat.helpers.changePassword
import kotlin.concurrent.thread
class ChangePasswordActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_change_password)
findViewById<Button>(R.id.save).setOnClickListener {
val currentPassword = findViewById<EditText>(R.id.current_password).text.toString()
val newPassword = findViewById<EditText>(R.id.new_password).text.toString()
val confirmPassword = findViewById<EditText>(R.id.confirm_password).text.toString()
if (newPassword != confirmPassword) {
Toast.makeText(this, "Passwords must match", Toast.LENGTH_LONG).show()
return@setOnClickListener
}
val progressDialog = ProgressDialog(this)
progressDialog.setMessage("Loading...")
progressDialog.show()
thread {
try {
changePassword(this, currentPassword, newPassword)
} catch (e: ApiRequestException) {
runOnUiThread {
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()
}
return@thread
} finally {
runOnUiThread {
progressDialog.hide()
}
}
runOnUiThread {
Toast.makeText(this, "Password changed successfully", Toast.LENGTH_LONG).show()
}
finish()
}
}
}
}

View File

@ -0,0 +1,50 @@
package tech.mercantec.easyeat.ui.profile
import android.app.ProgressDialog
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import tech.mercantec.easyeat.R
import tech.mercantec.easyeat.helpers.ApiRequestException
import tech.mercantec.easyeat.helpers.updateUser
import kotlin.concurrent.thread
class EditProfileActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_profile)
findViewById<EditText>(R.id.username).setText(intent.extras?.getString("username"), TextView.BufferType.EDITABLE)
findViewById<EditText>(R.id.email).setText(intent.extras?.getString("email"), TextView.BufferType.EDITABLE)
findViewById<Button>(R.id.save).setOnClickListener {
val username = findViewById<EditText>(R.id.username).text.toString().trim()
val email = findViewById<EditText>(R.id.email).text.toString().trim()
val progressDialog = ProgressDialog(this)
progressDialog.setMessage("Loading...")
progressDialog.show()
thread {
try {
updateUser(this, username, email)
} catch (e: ApiRequestException) {
runOnUiThread {
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()
}
return@thread
} finally {
runOnUiThread {
progressDialog.hide()
}
}
finish()
}
}
}
}

View File

@ -1,15 +1,18 @@
package tech.mercantec.easyeat.ui.profile package tech.mercantec.easyeat.ui.profile
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.ui.auth.WelcomeActivity
import tech.mercantec.easyeat.databinding.FragmentProfileBinding import tech.mercantec.easyeat.databinding.FragmentProfileBinding
import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.ApiRequestException
import tech.mercantec.easyeat.helpers.UserInfoResponse import tech.mercantec.easyeat.helpers.UserInfoResponse
import tech.mercantec.easyeat.helpers.getUserInfo import tech.mercantec.easyeat.helpers.getUserInfo
import tech.mercantec.easyeat.helpers.logout
import kotlin.concurrent.thread import kotlin.concurrent.thread
class ProfileFragment : Fragment() { class ProfileFragment : Fragment() {
@ -31,9 +34,28 @@ class ProfileFragment : Fragment() {
activity?.runOnUiThread { activity?.runOnUiThread {
binding.username.text = userInfo.userName binding.username.text = userInfo.userName
binding.email.text = userInfo.email binding.email.text = userInfo.email
binding.editProfile.setOnClickListener {
val intent = Intent(activity, EditProfileActivity::class.java)
intent.putExtra("username", userInfo.userName)
intent.putExtra("email", userInfo.email)
startActivity(intent)
}
} }
} }
binding.changePassword.setOnClickListener {
startActivity(Intent(activity, ChangePasswordActivity::class.java))
}
binding.logout.setOnClickListener {
logout(requireContext())
val intent = Intent(activity, WelcomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
}
return binding.root return binding.root
} }
} }

View File

@ -1,10 +1,14 @@
package tech.mercantec.easyeat.ui.shopping_list package tech.mercantec.easyeat.ui.shopping_list
import android.app.AlertDialog
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.ArrayAdapter
import android.widget.Spinner
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import tech.mercantec.easyeat.R
import tech.mercantec.easyeat.databinding.FragmentShoppingListBinding import tech.mercantec.easyeat.databinding.FragmentShoppingListBinding
class ShoppingListFragment : Fragment() { class ShoppingListFragment : Fragment() {
@ -23,6 +27,27 @@ class ShoppingListFragment : Fragment() {
_binding = FragmentShoppingListBinding.inflate(inflater, container, false) _binding = FragmentShoppingListBinding.inflate(inflater, container, false)
val root: View = binding.root val root: View = binding.root
binding.addToShoppingList.setOnClickListener {
val view = requireActivity().layoutInflater.inflate(R.layout.dialog_add_to_shopping_list, null)
val dialog = AlertDialog.Builder(activity)
.setView(view)
.setPositiveButton(R.string.add_label, { dialog, id ->
})
.setNegativeButton(R.string.cancel_label, { dialog, id ->
dialog.cancel()
})
.create()
dialog.show()
val adapter = ArrayAdapter.createFromResource(requireContext(), R.array.units, android.R.layout.simple_spinner_item)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
dialog.findViewById<Spinner>(R.id.unit_selector).adapter = adapter
}
return root return root
} }

View File

@ -0,0 +1,68 @@
<?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:padding="10dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="2dp"
android:text="@string/current_password_label"
/>
<EditText
android:id="@+id/current_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/password_hint"
android:autofillHints="password"
android:inputType="textPassword"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="2dp"
android:text="@string/new_password_label"
/>
<EditText
android:id="@+id/new_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/password_hint"
android:autofillHints="password"
android:inputType="textPassword"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="2dp"
android:text="@string/confirm_new_password_label"
/>
<EditText
android:id="@+id/confirm_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/password_hint"
android:autofillHints="password"
android:inputType="textPassword"
/>
<Button
android:id="@+id/save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:text="@string/save_label"
/>
</LinearLayout>

View File

@ -0,0 +1,51 @@
<?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:padding="10dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="2dp"
android:text="@string/username_label"
/>
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/username_hint"
android:autofillHints="username"
android:inputType="text"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="2dp"
android:text="@string/email_label"
/>
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/email_hint"
android:autofillHints="emailAddress"
android:inputType="textEmailAddress"
/>
<Button
android:id="@+id/save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:text="@string/save_label"
/>
</LinearLayout>

View File

@ -17,7 +17,7 @@
<TextView <TextView
android:layout_marginTop="50sp" android:layout_marginTop="50sp"
android:layout_marginLeft="2sp" android:layout_marginStart="2sp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/email_label" android:text="@string/email_label"
@ -33,11 +33,11 @@
<TextView <TextView
android:layout_marginTop="20sp" android:layout_marginTop="20sp"
android:layout_marginLeft="2sp" android:layout_marginStart="2sp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/password_label" android:text="@string/password_label"
/> />
<EditText <EditText
android:id="@+id/password" android:id="@+id/password"

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="20dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_marginBottom="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add_shopping_item_label"
android:textAlignment="center"
android:textStyle="bold"
android:textSize="18sp"
/>
<TextView
android:id="@+id/amount_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/title"
android:text="@string/ingredient_amount_label"
/>
<EditText
android:id="@+id/amount"
android:layout_width="72dp"
android:layout_height="wrap_content"
android:layout_below="@id/amount_label"
android:layout_alignParentStart="true"
android:hint="@string/ingredient_amount_hint"
/>
<Spinner
android:id="@+id/unit_selector"
android:layout_width="72dp"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/amount"
android:layout_below="@+id/amount_label"
/>
<TextView
android:id="@+id/name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_alignStart="@id/name"
android:text="@string/ingredient_name_label"
/>
<EditText
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@id/name_label"
android:layout_toEndOf="@id/unit_selector"
android:layout_alignParentEnd="true"
android:hint="@string/ingredient_name_hint"
/>
</RelativeLayout>

View File

@ -43,11 +43,36 @@
android:text="@string/loading" android:text="@string/loading"
/> />
<Button <LinearLayout
android:layout_marginTop="40dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/edit_profile_label" android:orientation="vertical">
/>
</LinearLayout> <Button
android:id="@+id/edit_profile"
android:layout_marginTop="40dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/edit_profile_label"
/>
<Button
android:id="@+id/change_password"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/change_password_label"
/>
<Button
android:id="@+id/logout"
android:layout_marginTop="40dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/dark_gray"
android:text="@string/logout_label"
/>
</LinearLayout>
</LinearLayout>

View File

@ -1,9 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
tools:context=".ui.shopping_list.ShoppingListFragment">
</androidx.constraintlayout.widget.ConstraintLayout> <ListView
android:id="@+id/shopping_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@android:color/darker_gray"
android:dividerHeight="1dp"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_to_shopping_list"
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>

View File

@ -14,8 +14,12 @@
<string name="email_hint">john@doe.com</string> <string name="email_hint">john@doe.com</string>
<string name="password_label">Password</string> <string name="password_label">Password</string>
<string name="confirm_password_label">Confirm password</string> <string name="confirm_password_label">Confirm password</string>
<string name="current_password_label">Current password</string>
<string name="new_password_label">New password</string>
<string name="confirm_new_password_label">Confirm new password</string>
<string name="password_hint">&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;</string> <string name="password_hint">&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;&#8226;</string>
<string name="back_label">Back</string> <string name="back_label">Back</string>
<string name="save_label">Save</string>
<string name="create_dish_heading">Create Dish</string> <string name="create_dish_heading">Create Dish</string>
<string name="ingredient_label">Ingredient</string> <string name="ingredient_label">Ingredient</string>
<string name="measurement_label">Measurement</string> <string name="measurement_label">Measurement</string>
@ -26,4 +30,19 @@
<string name="general">General</string> <string name="general">General</string>
<string name="ingredients">Ingredients</string> <string name="ingredients">Ingredients</string>
<string name="Instructions">Instructions</string> <string name="Instructions">Instructions</string>
<string name="cancel_label">Cancel</string>
<string name="add_shopping_item_label">Add shopping item</string>
<string name="ingredient_amount_label">Amount</string>
<string name="ingredient_amount_hint">500</string>
<string name="ingredient_name_label">Name</string>
<string name="ingredient_name_hint">Beef</string>
<string name="change_password_label">Change password</string>
<string name="logout_label">Log out</string>
<string-array name="units">
<item>g</item>
<item>kg</item>
<item>ml</item>
<item>dl</item>
<item>l</item>
</string-array>
</resources> </resources>