diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index d154094e49c9ceabf5fb82f396a60ec5f91e0c5a..c71ea409dc2e4a7940bb99c4384838821ca928a9 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,10 +4,10 @@ <selectionStates> <SelectionState runConfigName="app"> <option name="selectionMode" value="DROPDOWN" /> - <DropdownSelection timestamp="2024-12-18T22:40:17.265726Z"> + <DropdownSelection timestamp="2024-12-23T13:09:16.710436500Z"> <Target type="DEFAULT_BOOT"> <handle> - <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\ale95\.android\avd\Pixel_9_Pro_API_35.avd" /> + <DeviceId pluginId="PhysicalDevice" identifier="serial=R5CW13P1B3Z" /> </handle> </Target> </DropdownSelection> diff --git a/.idea/misc.xml b/.idea/misc.xml index 74dd639e4ea4800753c1dbdb333093410e843492..b2c751a35c77d4f7b713fe3bfc2cfb693e4d984f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..94a25f7f4cb416c083d265558da75d457237d671 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a88f381e3db68e9acf408945253d10d368c588f2..14ea07e592833ec4e8bc0f8c488706b65a3b8848 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,14 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> + <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> + <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> + + <uses-permission + android:name="android.permission.READ_EXTERNAL_STORAGE" + android:maxSdkVersion="32" /> + + <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" diff --git a/app/src/main/java/com/example/taskmanagement/MainActivity.kt b/app/src/main/java/com/example/taskmanagement/MainActivity.kt index 0791753ed1e92817acbd397a944c15f2dc888daa..1df4b53b5ae912661349c1423e8cf52af2c89654 100644 --- a/app/src/main/java/com/example/taskmanagement/MainActivity.kt +++ b/app/src/main/java/com/example/taskmanagement/MainActivity.kt @@ -33,4 +33,15 @@ class MainActivity : AppCompatActivity() { } } + fun hideBottomNavigation() { + val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavMenu) + bottomNavigationView.visibility = android.view.View.GONE + } + + + fun showBottomNavigation() { + val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavMenu) + bottomNavigationView.visibility = android.view.View.VISIBLE + } + } diff --git a/app/src/main/java/com/example/taskmanagement/auth/EditProfileFragment.kt b/app/src/main/java/com/example/taskmanagement/auth/EditProfileFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..bcf385eb469571def7a620f7babf545cf699a3d0 --- /dev/null +++ b/app/src/main/java/com/example/taskmanagement/auth/EditProfileFragment.kt @@ -0,0 +1,102 @@ +package com.example.taskmanagement.auth + +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 androidx.navigation.fragment.findNavController +import com.example.taskmanagement.R +import com.example.taskmanagement.databinding.FragmentEditProfileBinding +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.firestore.FirebaseFirestore + +class EditProfileFragment : Fragment() { + + private lateinit var binding: FragmentEditProfileBinding + private var firebaseAuth: FirebaseAuth? = null + private var firestore: FirebaseFirestore? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditProfileBinding.inflate(inflater, container, false) + + firebaseAuth = FirebaseAuth.getInstance() + firestore = FirebaseFirestore.getInstance() + + + loadUserProfile() + + binding.buttonSaveProfile.setOnClickListener { + saveUserProfile() + findNavController().navigate(R.id.action_editProfileFragment_to_profileFragment) + } + binding.buttonBack.setOnClickListener { + findNavController().popBackStack() + } + + return binding.root + } + + private fun loadUserProfile() { + val userId = firebaseAuth?.currentUser?.email + + userId?.let { + firestore?.collection("usersData") + ?.document(it) + ?.get() + ?.addOnSuccessListener { document -> + if (document.exists()) { + val userData = document.toObject(UserData::class.java) + userData?.let { user -> + binding.editTextName.setText(user.firstName) + binding.editTextSurname.setText(user.lastName) + binding.editTextStreet.setText(user.street) + binding.editTextSkills.setText(user.skills) + } + } + } + ?.addOnFailureListener { + Toast.makeText(context, "Errore nel caricamento dei dati", Toast.LENGTH_SHORT).show() + } + } + } + + + private fun saveUserProfile() { + val email = firebaseAuth?.currentUser?.email + val firstName = binding.editTextName.text.toString().trim() + val lastName = binding.editTextSurname.text.toString().trim() + val street = binding.editTextStreet.text.toString().trim() + val skills = binding.editTextSkills.text.toString().trim() + + if (firstName.isEmpty() || lastName.isEmpty() || street.isEmpty() || skills.isEmpty()) { + Toast.makeText(context, "Compila tutti i campi", Toast.LENGTH_SHORT).show() + return + } + + val userData = UserData( + emailUser = email ?: "", + firstName = firstName, + lastName = lastName, + street = street, + skills = skills + ) + + email?.let { + firestore?.collection("usersData") + ?.document(it) + ?.set(userData) + ?.addOnSuccessListener { + Toast.makeText(context, "Profilo aggiornato", Toast.LENGTH_SHORT).show() + } + ?.addOnFailureListener { + Toast.makeText(context, "Errore nell'aggiornamento del profilo", Toast.LENGTH_SHORT).show() + } + } + } + +} diff --git a/app/src/main/java/com/example/taskmanagement/auth/ProfileFragment.kt b/app/src/main/java/com/example/taskmanagement/auth/ProfileFragment.kt index 99b81f017e3c954f77d7f70b11b11ebe1188d674..41519521acbd4df2a78597a446dc3b6c38243eac 100644 --- a/app/src/main/java/com/example/taskmanagement/auth/ProfileFragment.kt +++ b/app/src/main/java/com/example/taskmanagement/auth/ProfileFragment.kt @@ -1,11 +1,21 @@ package com.example.taskmanagement.auth +import android.Manifest +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.net.Uri +import android.os.Build import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter +import android.widget.ListView import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import com.example.taskmanagement.R @@ -15,6 +25,9 @@ import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.DocumentSnapshot import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.QuerySnapshot +import java.io.File +import java.io.FileOutputStream +import java.io.IOException class ProfileFragment : Fragment() { @@ -25,11 +38,37 @@ class ProfileFragment : Fragment() { private var taskList: MutableList<String> = mutableListOf() private var taskAdapter: ArrayAdapter<String>? = null + private val requestPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> + if (isGranted) { + openGallery() + } else { + Toast.makeText(context, getString(R.string.permission_denied), Toast.LENGTH_SHORT).show() + } + } + + private val pickImageLauncher = + registerForActivityResult(ActivityResultContracts.GetContent()) { imageUri: Uri? -> + if (imageUri != null) { + val bitmap = BitmapFactory.decodeStream( + requireContext().contentResolver.openInputStream(imageUri) + ) + saveProfileImage(bitmap) + setProfileImage(bitmap) + } else { + Toast.makeText(context, getString(R.string.image_loading_error), Toast.LENGTH_SHORT).show() + } + } + + companion object { + private const val PROFILE_IMAGE_FILENAME = "profile_image.jpg" + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = FragmentProfileBinding.inflate(inflater, container, false) firebaseAuth = FirebaseAuth.getInstance() @@ -37,24 +76,140 @@ class ProfileFragment : Fragment() { taskAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, taskList) binding.assignedTasksList.adapter = taskAdapter + binding.assignedTasksList.isNestedScrollingEnabled = true loadUserData() + loadProfileImage() + + binding.buttonEditProfile.setOnClickListener { + findNavController().navigate(R.id.action_profileFragment_to_editProfileFragment) + } binding.buttonLogout.setOnClickListener { firebaseAuth?.signOut() - Toast.makeText(context, "Logout effettuato", Toast.LENGTH_SHORT).show() + Toast.makeText(context, getString(R.string.logout_success), Toast.LENGTH_SHORT).show() HomeFragment.deleteSavedTasksFiles(requireContext()) findNavController().popBackStack(R.id.welcomeFragment, false) } + binding.profileImage.setOnClickListener { + checkPermissions() + } + return binding.root } + private fun checkPermissions() { + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> { + if (requireContext().checkSelfPermission(Manifest.permission.READ_MEDIA_IMAGES) + != PackageManager.PERMISSION_GRANTED + ) { + requestPermissionLauncher.launch(Manifest.permission.READ_MEDIA_IMAGES) + } else { + openGallery() + } + } + + else -> { + if (ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.READ_EXTERNAL_STORAGE + ) != PackageManager.PERMISSION_GRANTED + ) { + requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) + } else { + openGallery() + } + } + } + } + + + private fun openGallery() { + pickImageLauncher.launch("image/*") + } + + private fun saveProfileImage(bitmap: Bitmap) { + val userEmail = firebaseAuth?.currentUser?.email ?: return + val file = File(requireContext().filesDir, "${userEmail}_$PROFILE_IMAGE_FILENAME") + + try { + FileOutputStream(file).use { output -> + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output) + } + Toast.makeText(context, "Immagine salvata correttamente", Toast.LENGTH_SHORT).show() + } catch (e: IOException) { + e.printStackTrace() + Toast.makeText(context, "Errore nel salvataggio dell'immagine", Toast.LENGTH_SHORT) + .show() + } + } + + private fun loadProfileImage() { + val userEmail = firebaseAuth?.currentUser?.email ?: return + val file = File(requireContext().filesDir, "${userEmail}_$PROFILE_IMAGE_FILENAME") + + if (file.exists()) { + val bitmap = BitmapFactory.decodeFile(file.path) + setProfileImage(bitmap) + } else { + binding.profileImage.setImageResource(R.drawable.ic_baseline_account_circle_24) + } + } + + private fun setProfileImage(bitmap: Bitmap) { + val circularBitmap = Bitmap.createScaledBitmap(bitmap, 80, 80, true) + + val matrix = android.graphics.Matrix() + matrix.postRotate(270f) + val rotatedBitmap = Bitmap.createBitmap( + circularBitmap, + 0, + 0, + circularBitmap.width, + circularBitmap.height, + matrix, + true + ) + + // Imposta l'immagine ruotata e rimpicciolita + binding.profileImage.setImageBitmap(rotatedBitmap) + } + + private fun loadUserData() { - val userId = firebaseAuth!!.currentUser!!.uid + val userId = firebaseAuth!!.currentUser!!.email + + if (userId != null) { + firestore!!.collection("usersData") + .document(userId) + .get() + .addOnSuccessListener { document: DocumentSnapshot -> + val firstName = document.getString("firstName") + val lastName = document.getString("lastName") + val skills = document.getString("skills") + + if (!firstName.isNullOrEmpty() && !lastName.isNullOrEmpty()) { + binding.userName.text = getString(R.string.user_name, firstName, lastName) + binding.welcomeMessage.text = getString(R.string.welcome_message, firstName) - binding.userName.text = firebaseAuth!!.currentUser!!.email - binding.welcomeMessage.text = "Benvenuto, ${firebaseAuth!!.currentUser!!.email}" + } else { + val email = firebaseAuth!!.currentUser!!.email + binding.userName.text = email + binding.welcomeMessage.text = getString(R.string.welcome_message, email) + } + + binding.skillsList.text = skills ?: "Nessuna skill disponibile" + } + .addOnFailureListener { + Toast.makeText( + context, + "Errore nel caricamento del profilo", + Toast.LENGTH_SHORT + ).show() + } + } firestore!!.collection("tasks") .whereEqualTo("assignedTo", userId) @@ -62,18 +217,18 @@ class ProfileFragment : Fragment() { .addOnSuccessListener { queryDocumentSnapshots: QuerySnapshot -> taskList.clear() for (snapshot in queryDocumentSnapshots) { - val taskName = snapshot.getString("taskName") - val deadline = snapshot.getString("deadline") - taskList.add("$taskName - Scadenza: $deadline") + val taskName = snapshot.getString("name") + if (taskName != null) { + taskList.add(taskName) + } } + Log.e("ProfileFragment", "Task List: $taskList") taskAdapter!!.notifyDataSetChanged() + listViewHeight(binding.assignedTasksList) } .addOnFailureListener { - Toast.makeText( - context, - "Errore nel caricamento dei task", - Toast.LENGTH_SHORT - ).show() + Toast.makeText(context, getString(R.string.error_loading_tasks), Toast.LENGTH_SHORT).show() + } firestore!!.collection("tasks") @@ -86,26 +241,41 @@ class ProfileFragment : Fragment() { for (snapshot in queryDocumentSnapshots) { val status = snapshot.getString("status") - if (status == "Completed") { - completed++ - } else if (status == "Assigned") { - assigned++ - } else if (status == "Todo") { - todo++ + when (status) { + "Completed" -> completed++ + "Assigned" -> assigned++ + "Todo" -> todo++ } } binding.completedProjects.text = completed.toString() binding.projectsToStart.text = todo.toString() - binding.averageCompletionTime.text = assigned.toString() // Projects Assigned + binding.averageCompletionTime.text = assigned.toString() } .addOnFailureListener { - Toast.makeText( - context, - "Errore nel caricamento delle statistiche", - Toast.LENGTH_SHORT - ).show() + Toast.makeText(context, getString(R.string.error_loading_tasks), Toast.LENGTH_SHORT).show() + } } + private fun listViewHeight(listView: ListView) { + val adapter = listView.adapter ?: return + + var totalHeight = 0 + val itemsToShow = minOf(4, adapter.count) + + for (i in 0 until itemsToShow) { + val listItem = adapter.getView(i, null, listView) + listItem.measure( + View.MeasureSpec.makeMeasureSpec(listView.width, View.MeasureSpec.AT_MOST), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + ) + totalHeight += listItem.measuredHeight + } + + val params = listView.layoutParams + params.height = totalHeight + (listView.dividerHeight * (itemsToShow - 1)) + listView.layoutParams = params + listView.requestLayout() + } } diff --git a/app/src/main/java/com/example/taskmanagement/auth/UserData.kt b/app/src/main/java/com/example/taskmanagement/auth/UserData.kt new file mode 100644 index 0000000000000000000000000000000000000000..82ff99c38c5f84aeb165bf4c34d5bf707e72ccc1 --- /dev/null +++ b/app/src/main/java/com/example/taskmanagement/auth/UserData.kt @@ -0,0 +1,9 @@ +package com.example.taskmanagement.auth + +data class UserData( + val emailUser: String = "", + val firstName: String = "", + val lastName: String = "", + val street: String = "", + val skills: String = "" +) diff --git a/app/src/main/java/com/example/taskmanagement/chat/ChatFragment.kt b/app/src/main/java/com/example/taskmanagement/chat/ChatFragment.kt index ca0cfad8c31de878616c63cc92ff6be28a400454..5c7a2fe2628109c8e9a0ccf7e6ffad21fe03b1a2 100644 --- a/app/src/main/java/com/example/taskmanagement/chat/ChatFragment.kt +++ b/app/src/main/java/com/example/taskmanagement/chat/ChatFragment.kt @@ -15,7 +15,9 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.example.taskmanagement.MainActivity import com.example.taskmanagement.R +import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.ListenerRegistration @@ -73,6 +75,8 @@ class ChatFragment : Fragment() { createChat() nameChatTextView.text = receiverId + (activity as? MainActivity)?.hideBottomNavigation() + buttonBack.setOnClickListener { findNavController().navigateUp() @@ -81,7 +85,6 @@ class ChatFragment : Fragment() { return view } - private fun setupRecyclerView() { adapter = ChatAdapter(currentUserId) recyclerView.layoutManager = LinearLayoutManager(requireContext()) diff --git a/app/src/main/java/com/example/taskmanagement/task/SearchSubFragment.kt b/app/src/main/java/com/example/taskmanagement/task/SearchSubFragment.kt index 83c89f30f17309a55049e50da434e4ebc2f24486..6b8a4e23e2b7169e8fc7990544f6347823102b2f 100644 --- a/app/src/main/java/com/example/taskmanagement/task/SearchSubFragment.kt +++ b/app/src/main/java/com/example/taskmanagement/task/SearchSubFragment.kt @@ -1,6 +1,7 @@ package com.example.taskmanagement.task import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -69,7 +70,6 @@ class SearchSubFragment : Fragment() { private fun loadSubtasks(taskId: String) { subtaskRecyclerView.visibility = View.VISIBLE - lifecycleScope.launch { try { val result = firestore.collection("tasks") @@ -78,16 +78,18 @@ class SearchSubFragment : Fragment() { .get() .await() + Log.d("SubTaskFragment", "Firestore result size: ${result.size()}") + subtaskList.clear() for (document in result) { val subTask = document.toObject(SubTask::class.java) subtaskIdMap[subTask] = document.id subtaskList.add(subTask) } - subtaskList.sortBy { it.name } subtaskAdapter.updateSubTasks(subtaskList) + val noSubtasksMessage: TextView = view?.findViewById(R.id.no_subtasks_message) ?: return@launch if (subtaskList.isEmpty()) { diff --git a/app/src/main/java/com/example/taskmanagement/task/SubTaskFragment.kt b/app/src/main/java/com/example/taskmanagement/task/SubTaskFragment.kt index 08dbcf96ac1a59bb4d20e77380b48730788c164d..e6649de49219513a9d156dec5a8993731031ed9b 100644 --- a/app/src/main/java/com/example/taskmanagement/task/SubTaskFragment.kt +++ b/app/src/main/java/com/example/taskmanagement/task/SubTaskFragment.kt @@ -95,7 +95,6 @@ class SubTaskFragment : Fragment() { private val subtaskIdMap = mutableMapOf<SubTask, String>() private fun loadSubtasks(taskId: String) { - progressBar.visibility = View.VISIBLE subtaskRecyclerView.visibility = View.VISIBLE lifecycleScope.launch { try { @@ -128,8 +127,6 @@ class SubTaskFragment : Fragment() { } catch (e: Exception) { Toast.makeText(requireContext(), "Failed to load subtasks", Toast.LENGTH_SHORT) .show() - } finally { - progressBar.visibility = View.GONE } } } diff --git a/app/src/main/java/com/example/taskmanagement/task/SubtaskAdapter.kt b/app/src/main/java/com/example/taskmanagement/task/SubtaskAdapter.kt index 58a972fe244a0781f06a2bb79790d53ee4015e22..bc0a0711d0052d104a975cd4e26d2d255cffa192 100644 --- a/app/src/main/java/com/example/taskmanagement/task/SubtaskAdapter.kt +++ b/app/src/main/java/com/example/taskmanagement/task/SubtaskAdapter.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import android.widget.Button import android.widget.TextView import android.widget.Toast +import androidx.core.content.ContextCompat.getString import androidx.recyclerview.widget.RecyclerView import com.example.taskmanagement.R import com.google.firebase.firestore.FirebaseFirestore @@ -84,12 +85,7 @@ class SubtaskAdapter( } subtaskDeadline.text = subtask.deadline subtaskDaysRemaining.text = subtask.deadline - subtaskState.text = when (subtask.status) { - "Todo" -> itemView.context.getString(R.string.todo) - "Assigned" -> itemView.context.getString(R.string.assigned) - "Completed" -> itemView.context.getString(R.string.completed) - else -> itemView.context.getString(R.string.unknown) - } + subtaskState.text = subtask.status val daysRemaining = calculateDaysRemaining(subtask.deadline) val formattedText = itemView.context.getString(R.string.days_remaining, daysRemaining) subtaskDaysRemaining.text = formattedText diff --git a/app/src/main/res/drawable/baseline_add_circle_24.xml b/app/src/main/res/drawable/baseline_add_circle_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..d448b37b05ef196c389eb65afed241061dce8083 --- /dev/null +++ b/app/src/main/res/drawable/baseline_add_circle_24.xml @@ -0,0 +1,5 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> + + <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z"/> + +</vector> diff --git a/app/src/main/res/layout/fragment_chat.xml b/app/src/main/res/layout/fragment_chat.xml index b0eefa2af0a9ab68007c7adf77ba7fdf084144f4..7dc7e0a4e3917cce8ad465c442ab4a3111ffbb48 100644 --- a/app/src/main/res/layout/fragment_chat.xml +++ b/app/src/main/res/layout/fragment_chat.xml @@ -55,10 +55,10 @@ android:id="@+id/messageInputLayout" android:layout_width="0dp" android:layout_height="wrap_content" - android:background="@android:color/white" + android:background="@color/WhiteSmoke" android:orientation="horizontal" android:padding="8dp" - app:layout_constraintBottom_toTopOf="@id/bottomNavMenu" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"> @@ -79,14 +79,4 @@ android:text="@string/send" /> </LinearLayout> - <!-- BottomNavigationView --> - <com.google.android.material.bottomnavigation.BottomNavigationView - android:id="@+id/bottomNavMenu" - android:layout_width="0dp" - android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:menu="@menu/bottom_nav_menu" /> - </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/fragment_edit_profile.xml b/app/src/main/res/layout/fragment_edit_profile.xml new file mode 100644 index 0000000000000000000000000000000000000000..b354b7f2648880f78e501b7e9d4a7aaf6ada5c1b --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_profile.xml @@ -0,0 +1,129 @@ +<?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" + android:fitsSystemWindows="true" + tools:context=".auth.EditProfileFragment"> + + <!-- Toolbar --> + <androidx.appcompat.widget.Toolbar + android:id="@+id/toolbarEditProfile" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:background="?attr/colorPrimaryContainer" + android:elevation="0dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal" + android:paddingStart="16dp" + android:paddingEnd="16dp"> + + <ImageButton + android:id="@+id/buttonBack" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:contentDescription="@string/turn_back" + android:src="@drawable/baseline_arrow_back_24" /> + + <TextView + android:id="@+id/nameToolEditProfile" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:text="@string/edit_profile" + android:textColor="@color/Black" + android:textSize="20sp" + android:textStyle="bold" /> + </LinearLayout> + </androidx.appcompat.widget.Toolbar> + + <!-- Scrollable Content --> + <androidx.core.widget.NestedScrollView + android:layout_width="0dp" + android:layout_height="0dp" + android:padding="16dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/toolbarEditProfile"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:dividerPadding="8dp" + android:orientation="vertical" + android:showDividers="middle"> + + <!-- Nome --> + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/inputName" + style="@style/Widget.Material3.TextInputLayout.FilledBox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/first_name"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/editTextName" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/inputSurname" + style="@style/Widget.Material3.TextInputLayout.FilledBox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/last_name"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/editTextSurname" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/inputStreet" + style="@style/Widget.Material3.TextInputLayout.FilledBox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/street"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/editTextStreet" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/inputSkills" + style="@style/Widget.Material3.TextInputLayout.FilledBox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/skills"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/editTextSkills" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </com.google.android.material.textfield.TextInputLayout> + + <!-- Save Button --> + <com.google.android.material.button.MaterialButton + android:id="@+id/buttonSaveProfile" + style="@style/Widget.Material3.Button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:text="@string/save_profile" /> + </LinearLayout> + </androidx.core.widget.NestedScrollView> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml index 2b899205b037736e4cd7640afa2bfd23ead60bd1..3c2f4a9628b3af9db05bfcc0298af43ba1ad2384 100644 --- a/app/src/main/res/layout/fragment_profile.xml +++ b/app/src/main/res/layout/fragment_profile.xml @@ -1,197 +1,218 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" android:fitsSystemWindows="true" android:background="@drawable/gradient_background" + android:fillViewport="true" tools:context="auth.ProfileFragment"> - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="250dp"> - - <ImageView - android:id="@+id/profileImage" - android:layout_width="120dp" - android:layout_height="120dp" - android:layout_centerHorizontal="true" - android:layout_marginTop="30dp" - android:src="@drawable/ic_baseline_account_circle_24" - android:scaleType="centerCrop" - android:background="@drawable/ic_baseline_account_circle_24" /> - - <TextView - android:id="@+id/userName" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/profileImage" - android:layout_centerHorizontal="true" - android:layout_marginTop="15dp" - android:text="Nome Utente" - android:textColor="@android:color/black" - android:textSize="18sp" - android:textStyle="bold" /> - - <TextView - android:id="@+id/welcomeMessage" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/userName" - android:layout_centerHorizontal="true" - android:layout_marginTop="8dp" - android:text="@string/welcome_message" - android:textColor="@android:color/black" - android:textSize="16sp" /> - </RelativeLayout> - - <!-- Task List --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:padding="16dp"> + android:orientation="vertical"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Task Assegnati" - android:textColor="@color/black" - android:textSize="18sp" - android:textStyle="bold" /> - - <ListView - android:id="@+id/assignedTasksList" + <RelativeLayout android:layout_width="match_parent" - android:layout_height="200dp" - android:divider="@android:color/black" - android:dividerHeight="1dp" /> - </LinearLayout> + android:layout_height="250dp"> + + <ImageView + android:id="@+id/profileImage" + android:layout_width="120dp" + android:layout_height="120dp" + android:layout_centerHorizontal="true" + android:layout_marginTop="30dp" + android:src="@drawable/ic_baseline_account_circle_24" + android:scaleType="centerCrop" + android:background="@drawable/ic_baseline_account_circle_24" /> + + <TextView + android:id="@+id/userName" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/profileImage" + android:layout_centerHorizontal="true" + android:layout_marginTop="15dp" + android:text="Nome Utente" + android:textColor="@android:color/black" + android:textSize="18sp" + android:textStyle="bold" /> + + <TextView + android:id="@+id/welcomeMessage" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/userName" + android:layout_centerHorizontal="true" + android:layout_marginTop="8dp" + android:text="@string/welcome_message" + android:textColor="@android:color/black" + android:textSize="16sp" /> + + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/buttonEditProfile" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignEnd="@id/profileImage" + android:layout_alignBottom="@id/profileImage" + android:layout_marginEnd="-19dp" + android:layout_marginBottom="-7dp" + android:backgroundTint="@android:color/transparent" + android:contentDescription="@string/edit_profile" + android:src="@drawable/baseline_add_circle_24" /> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:padding="16dp"> + </RelativeLayout> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Skills" - android:textColor="@color/black" - android:textSize="18sp" - android:textStyle="bold" /> - <TextView - android:id="@+id/skillsList" + <!-- Task List --> + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Skill 1, Skill 2, Skill 3" - android:textColor="@color/black" - android:layout_marginTop="8dp" /> - </LinearLayout> + android:orientation="vertical" + android:padding="16dp"> - <!-- Statistics Section --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:padding="16dp"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Task Assegnati" + android:textColor="@color/black" + android:textSize="18sp" + android:textStyle="bold" /> + + <ListView + android:id="@+id/assignedTasksList" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:divider="@android:color/black" + android:dividerHeight="1dp" + android:nestedScrollingEnabled="true" /> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Statistiche" - android:textColor="@color/black" - android:textSize="18sp" - android:textStyle="bold" /> + </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginTop="16dp"> + android:orientation="vertical" + android:padding="16dp"> - <LinearLayout - android:layout_width="0dp" + <TextView + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_weight="1" - android:orientation="vertical" - android:gravity="center"> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Progetti Conclusi" - android:textColor="@color/black" /> - - <TextView - android:id="@+id/completedProjects" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="5" - android:textColor="@color/black" - android:textSize="18sp" - android:textStyle="bold" /> - </LinearLayout> - - <LinearLayout - android:layout_width="0dp" + android:text="Skills" + android:textColor="@color/black" + android:textSize="18sp" + android:textStyle="bold" /> + + <TextView + android:id="@+id/skillsList" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1" - android:orientation="vertical" - android:gravity="center"> + android:text="Skill 1, Skill 2, Skill 3" + android:textColor="@color/black" + android:layout_marginTop="8dp" /> + </LinearLayout> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Progetti da Iniziare" - android:textColor="@color/black" /> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp"> - <TextView - android:id="@+id/projectsToStart" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="3" - android:textColor="@color/black" - android:textSize="18sp" - android:textStyle="bold" /> - </LinearLayout> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Statistiche" + android:textColor="@color/black" + android:textSize="18sp" + android:textStyle="bold" /> <LinearLayout - android:layout_width="0dp" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1" - android:orientation="vertical" - android:gravity="center"> + android:orientation="horizontal" + android:layout_marginTop="16dp"> - <TextView - android:layout_width="wrap_content" + <LinearLayout + android:layout_width="0dp" android:layout_height="wrap_content" - android:text="Tempo Medio" - android:textColor="@color/black" /> - - <TextView - android:id="@+id/averageCompletionTime" - android:layout_width="wrap_content" + android:layout_weight="1" + android:orientation="vertical" + android:gravity="center"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Progetti Conclusi" + android:textColor="@color/black" /> + + <TextView + android:id="@+id/completedProjects" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="5" + android:textColor="@color/black" + android:textSize="18sp" + android:textStyle="bold" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" android:layout_height="wrap_content" - android:text="10 giorni" - android:textColor="@color/black" - android:textSize="18sp" - android:textStyle="bold" /> + android:layout_weight="1" + android:orientation="vertical" + android:gravity="center"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Progetti da Iniziare" + android:textColor="@color/black" /> + + <TextView + android:id="@+id/projectsToStart" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="3" + android:textColor="@color/black" + android:textSize="18sp" + android:textStyle="bold" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical" + android:gravity="center"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Tempo Medio" + android:textColor="@color/black" /> + + <TextView + android:id="@+id/averageCompletionTime" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="10 giorni" + android:textColor="@color/black" + android:textSize="18sp" + android:textStyle="bold" /> + </LinearLayout> </LinearLayout> </LinearLayout> - </LinearLayout> - <!-- Logout Button --> - <Button - android:id="@+id/buttonLogout" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:layout_marginTop="20dp" - android:text="Logout" - android:textColor="@android:color/white" /> + <Button + android:id="@+id/buttonLogout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginTop="20dp" + android:layout_marginBottom="85dp" + android:text="@string/logout" + android:textColor="@android:color/white" /> -</LinearLayout> + </LinearLayout> +</androidx.core.widget.NestedScrollView> diff --git a/app/src/main/res/layout/fragment_search_sub.xml b/app/src/main/res/layout/fragment_search_sub.xml index b7af3b5382ad4777b2288a5e9d3606f7956b7220..dc2cc4c22ecfa2fbfe435fc1f2b8090af0d302af 100644 --- a/app/src/main/res/layout/fragment_search_sub.xml +++ b/app/src/main/res/layout/fragment_search_sub.xml @@ -7,8 +7,6 @@ android:fitsSystemWindows="true" tools:context=".task.SearchSubFragment"> - <!-- SearchView --> - <ImageButton android:id="@+id/back_button" android:layout_width="wrap_content" @@ -45,7 +43,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/fragmentTitle" /> - <!-- RecyclerView --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="0dp" @@ -57,4 +54,18 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/search_view" /> + <TextView + android:id="@+id/no_subtasks_message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:text="@string/non_ci_sono_subtasks_presenti" + android:textColor="@android:color/black" + android:textSize="18sp" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 754b0b56ffe3104bb57fbe6b00162dac12a12593..e0be06b862df7bed15d658450c7eafc5a35d4290 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -142,6 +142,9 @@ <action android:id="@+id/action_profileFragment_to_searchFragment2" app:destination="@id/searchFragment" /> + <action + android:id="@+id/action_profileFragment_to_editProfileFragment" + app:destination="@id/editProfileFragment" /> </fragment> <fragment android:id="@+id/addSubTaskFragment" @@ -303,6 +306,15 @@ android:id="@+id/action_modifySubTaskFragment_to_searchFragment" app:destination="@id/searchFragment" /> </fragment> + <fragment + android:id="@+id/editProfileFragment" + android:name="com.example.taskmanagement.auth.EditProfileFragment" + android:label="fragment_edit_profile" + tools:layout="@layout/fragment_edit_profile" > + <action + android:id="@+id/action_editProfileFragment_to_profileFragment" + app:destination="@id/profileFragment" /> + </fragment> </navigation> \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 3103fdf449937f18e920496ba88a718b0fa035d5..ad44aa41c7383eae4caf0ebdf037e1558bd247ff 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -154,6 +154,14 @@ <string name="no_files_to_delete">Nessun file da eliminare</string> <string name="error_deleting_files">Errore durante l\'eliminazione dei file</string> <string name="no_chats_present">Nessuna chat presente, inizia a chattare!</string> - - + <string name="edit_profile">Modifica Profilo</string> + <string name="first_name">Nome</string> + <string name="last_name">Cognome</string> + <string name="street">Via</string> + <string name="skills">Competenze</string> + <string name="save_profile">Salva Profilo</string> + <string name="error_loading_tasks">Errore nel caricamento dei task</string> + <string name="permission_denied">Permesso negato</string> + <string name="image_loading_error">Errore nel caricamento dell\'immagine</string> + <string name="logout_success">Logout effettuato</string> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e878fa5a3ece62c79d1d4aae5107f19ed37cb69f..ab00fd6160685a680d81bd8d760a7e4f4ff3c296 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -173,7 +173,17 @@ <string name="no_files_to_delete">No files to delete</string> <string name="error_deleting_files">Error deleting files</string> <string name="no_chats_present">No chat present, start chatting!</string> - + <string name="edit_profile">Edit Profile</string> + <string name="first_name">First Name</string> + <string name="last_name">Last Name</string> + <string name="street">Street</string> + <string name="skills">Skills</string> + <string name="save_profile">Save Profile</string> + <string name="user_name" translatable="false">%1$s %2$s</string> + <string name="error_loading_tasks">Error loading tasks</string> + <string name="permission_denied">Permission denied</string> + <string name="image_loading_error">Image error loading</string> + <string name="logout_success">Logout success!</string> </resources> \ No newline at end of file