Add profile page, implement authentication
This commit is contained in:
parent
9d9a2975f5
commit
7c45526563
@ -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"
|
||||||
|
@ -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,11 +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 {
|
||||||
|
val intent = Intent(this, CreateDishActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,32 +11,46 @@ 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(ctx: Context, 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 prefs = ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE)
|
||||||
val authToken = prefs.getString("auth-token", null)
|
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 (data != null) {
|
|
||||||
setRequestProperty("Content-Type", "application/json")
|
|
||||||
|
|
||||||
if (authToken != null)
|
if (authToken != null)
|
||||||
setRequestProperty("Authorization", "Bearer $authToken")
|
setRequestProperty("Authorization", "Bearer $authToken")
|
||||||
|
|
||||||
|
if (data != null) {
|
||||||
|
setRequestProperty("Content-Type", "application/json")
|
||||||
|
|
||||||
outputStream.write(data.toByteArray())
|
outputStream.write(data.toByteArray())
|
||||||
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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
@ -47,6 +48,9 @@ fun refreshAuthToken(ctx: Context, refreshToken: String): Boolean {
|
|||||||
RefreshTokenRequest(refreshToken)
|
RefreshTokenRequest(refreshToken)
|
||||||
)
|
)
|
||||||
} catch (e: ApiRequestException) {
|
} catch (e: ApiRequestException) {
|
||||||
|
if (e.message != null)
|
||||||
|
Log.e("EasyEat", e.message!!)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,3 +74,12 @@ fun isLoggedIn(ctx: Context): Boolean {
|
|||||||
|
|
||||||
return true
|
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
|
||||||
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
10
app/app/src/main/res/drawable/ic_account_circle_24px.xml
Normal file
10
app/app/src/main/res/drawable/ic_account_circle_24px.xml
Normal 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>
|
@ -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>
|
|
10
app/app/src/main/res/drawable/ic_person_24px.xml
Normal file
10
app/app/src/main/res/drawable/ic_person_24px.xml
Normal 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>
|
@ -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>
|
|
53
app/app/src/main/res/layout/fragment_profile.xml
Normal file
53
app/app/src/main/res/layout/fragment_profile.xml
Normal 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>
|
@ -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>
|
@ -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>
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user