diff --git a/.idea/gradle.xml b/.idea/gradle.xml index ae733f102bc738ae414aa3051109a2cbd3664356..7b3006b6ee03430a07b5c217a8e9f2ad8c7bdaf5 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> + <component name="GradleMigrationSettings" migrationVersion="1" /> <component name="GradleSettings"> <option name="linkedExternalProjectsSettings"> <GradleProjectSettings> diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..edfda9b2f00474def4b6e2e1f56ee269490db5d9 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/example/taskmanagement/chat/ChatViewAdapter.kt b/app/src/main/java/com/example/taskmanagement/chat/ChatViewAdapter.kt index 7368af5dcfb7955ac485d0d404fc25deb8737d66..81beda3aef203454822c48c354325bf9b94199a8 100644 --- a/app/src/main/java/com/example/taskmanagement/chat/ChatViewAdapter.kt +++ b/app/src/main/java/com/example/taskmanagement/chat/ChatViewAdapter.kt @@ -6,6 +6,7 @@ import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.example.taskmanagement.R +import com.google.firebase.auth.FirebaseAuth class ChatViewAdapter( private val chatList: List<Chat>, @@ -24,7 +25,12 @@ class ChatViewAdapter( override fun onBindViewHolder(holder: ChatViewHolder, position: Int) { val chat = chatList[position] - holder.chatName.text = chat.participants.joinToString(", ") + val currentUserEmail = FirebaseAuth.getInstance().currentUser?.email + + val otherParticipant = chat.participants.firstOrNull { it != currentUserEmail } + + holder.chatName.text = otherParticipant ?: + holder.itemView.context.getString(R.string.user_not_authenticated) holder.lastMessage.text = chat.lastMessage?.text ?: holder.itemView.context.getString(R.string.no_message) @@ -33,5 +39,6 @@ class ChatViewAdapter( } } + override fun getItemCount() = chatList.size } diff --git a/app/src/main/java/com/example/taskmanagement/chat/DialogFragment.kt b/app/src/main/java/com/example/taskmanagement/chat/DialogFragment.kt index f591b51612faba605077f5186a4ecdc0b15d608d..b80a96ebf4a2ecc534e1ad6d01103bf640fbc1a5 100644 --- a/app/src/main/java/com/example/taskmanagement/chat/DialogFragment.kt +++ b/app/src/main/java/com/example/taskmanagement/chat/DialogFragment.kt @@ -42,28 +42,56 @@ class DialogFragment : DialogFragment() { private fun loadUsers() { + val currentUser = FirebaseAuth.getInstance().currentUser + if (currentUser == null) { + Toast.makeText(requireContext(), getString(R.string.error_loading_users), Toast.LENGTH_SHORT).show() + return + } + firestore.collection("users") + .document(currentUser.uid) .get() - .addOnSuccessListener { documents -> - userList.clear() - for (document in documents) { - val user = document.toObject(User::class.java) - if (user.email != FirebaseAuth.getInstance().currentUser?.uid) { - userList.add(user) - } + .addOnSuccessListener { document -> + val currentUserRole = document.getString("role") ?: return@addOnSuccessListener + + val rolesToLoad = when (currentUserRole) { + "PM" -> listOf("PL") + "PL" -> listOf("PM", "Dev") + "Dev" -> listOf("PL", "Dev") + else -> emptyList() } - recyclerViewUsers.adapter?.notifyDataSetChanged() + + firestore.collection("users") + .whereIn("role", rolesToLoad) + .get() + .addOnSuccessListener { documents -> + userList.clear() + for (document in documents) { + val user = document.toObject(User::class.java) + if (user.email != currentUser.email) { + userList.add(user) + } + } + recyclerViewUsers.adapter?.notifyDataSetChanged() + } + .addOnFailureListener { e -> + Toast.makeText( + requireContext(), + getString(R.string.error_loading_users, e.message), + Toast.LENGTH_SHORT + ).show() + } } .addOnFailureListener { e -> Toast.makeText( requireContext(), - getString(R.string.error_loading_users, e.message), + getString(R.string.error_loading_chats, e.message), Toast.LENGTH_SHORT ).show() - } } + private fun openChat(selectedUser: String) { val bundle = Bundle() bundle.putString("selectedUser", selectedUser) diff --git a/app/src/main/java/com/example/taskmanagement/chat/UserSelectionFragment.kt b/app/src/main/java/com/example/taskmanagement/chat/UserSelectionFragment.kt index 05fbe18572804ac90dd6f5b5b9f5c6cff9911b32..c2ecaf38a7ffdd333a946e851dd992813ba3d3f3 100644 --- a/app/src/main/java/com/example/taskmanagement/chat/UserSelectionFragment.kt +++ b/app/src/main/java/com/example/taskmanagement/chat/UserSelectionFragment.kt @@ -96,9 +96,34 @@ class UserSelectionFragment : Fragment() { private fun openChat(chat: Chat) { val currentUserId = currentUser?.email - val otherParticipant = chat.participants.firstOrNull { it != currentUserId } + if (currentUserId.isNullOrEmpty()) { + if (isAdded) { + Toast.makeText( + requireContext(), + getString(R.string.user_not_authenticated), + Toast.LENGTH_SHORT + ).show() + } + return + } - if (otherParticipant == null) { + if (!chat.participants.contains(currentUserId)) { + if (isAdded) { + Toast.makeText( + requireContext(), + getString(R.string.user_not_found), + Toast.LENGTH_SHORT + ).show() + } + return + } + + val otherParticipant = chat.participants + .filter { it != currentUserId } + .sorted() + .joinToString("_") + + if (otherParticipant.isEmpty()) { if (isAdded) { Toast.makeText( requireContext(), @@ -112,26 +137,17 @@ class UserSelectionFragment : Fragment() { firestore.collection("users") .document(otherParticipant) .get() - .addOnSuccessListener { document -> + .addOnSuccessListener { if (isAdded) { - val otherUserRole = document.getString("role") - val currentUserRole = fetchUserRole(currentUserId) - - if (canCommunicate(currentUserRole, otherUserRole)) { - val bundle = Bundle().apply { - putString("selectedUser", otherParticipant) - } - findNavController().navigate( - R.id.action_userSelectionFragment_to_chatFragment, - bundle - ) - } else { - Toast.makeText( - requireContext(), - getString(R.string.cannot_communicate), - Toast.LENGTH_SHORT - ).show() + val chatUser = chat.participants.firstOrNull { it != currentUser?.email } + val bundle = Bundle().apply { + putString("selectedUser", chatUser) } + findNavController().navigate( + R.id.action_userSelectionFragment_to_chatFragment, + bundle + ) + } } .addOnFailureListener { @@ -145,36 +161,4 @@ class UserSelectionFragment : Fragment() { } } - private fun canCommunicate(currentUserRole: String?, otherUserRole: String?): Boolean { - return when (currentUserRole) { - "PM" -> otherUserRole == "PL" - "PL" -> otherUserRole == "PM" || otherUserRole == "DEV" - "DEV" -> otherUserRole == "PL" || otherUserRole == "DEV" - else -> false - } - } - - private fun fetchUserRole(userId: String?): String? { - var role: String? = null - if (userId.isNullOrEmpty()) return role - - firestore.collection("users") - .document(userId) - .get() - .addOnSuccessListener { document -> - if (isAdded) { - role = document.getString("role") - } - } - .addOnFailureListener { - if (isAdded) { - Toast.makeText( - requireContext(), - getString(R.string.error_retrieving_role), - Toast.LENGTH_SHORT - ).show() - } - } - return role - } } diff --git a/app/src/main/java/com/example/taskmanagement/task/HomeFragment.kt b/app/src/main/java/com/example/taskmanagement/task/HomeFragment.kt index 4e5396df7547a11ddd64c5ec67c551e466295d64..f3af89d0cfca2a40c99e0ac9b68595c6c41dff39 100644 --- a/app/src/main/java/com/example/taskmanagement/task/HomeFragment.kt +++ b/app/src/main/java/com/example/taskmanagement/task/HomeFragment.kt @@ -113,18 +113,31 @@ class HomeFragment : Fragment() { .get() .await() - val allTasks = mutableListOf<QueryDocumentSnapshot>() - allTasks.addAll(createdByPMResult.documents as List<QueryDocumentSnapshot>) + // Inizializza una lista per i task + val allTasks = mutableListOf<Task>() - for (document in allTasks) { + // Processa i task creati dal Project Manager + createdByPMResult.documents.forEach { document -> val task = document.toObject(Task::class.java) - taskList.add(task) - taskIdMap[task] = document.id + task?.let { + allTasks.add(it) + taskIdMap[it] = document.id + } + } + + // Aggiorna i task aggiuntivi con il calcolo dei subtasks + for (task in allTasks) { + calculateSubTasks(task, taskIdMap[task] ?: return) + } + + // Aggiungi i task caricati alla lista principale e aggiorna l'interfaccia utente + taskList.clear() + taskList.addAll(allTasks) - calculateSubTasks(task, document.id) + if (isAdded) { + fabMain.visibility = View.VISIBLE } - if (isAdded) fabMain.visibility = View.VISIBLE } catch (e: Exception) { if (isAdded) { Toast.makeText( @@ -137,6 +150,7 @@ class HomeFragment : Fragment() { } + private suspend fun loadTasksForPL() { try { val currentUserEmail = auth.currentUser?.email ?: return @@ -150,23 +164,37 @@ class HomeFragment : Fragment() { .get() .await() - val allTasks = mutableListOf<QueryDocumentSnapshot>() - allTasks.addAll(createdByPLResult.documents as List<QueryDocumentSnapshot>) - allTasks.addAll(assignedToPLResult.documents as List<QueryDocumentSnapshot>) + val allTasks = mutableListOf<Task>() + + createdByPLResult.documents.forEach { document -> + val task = document.toObject(Task::class.java) + task?.let { + allTasks.add(it) + taskIdMap[it] = document.id + } + } - for (document in allTasks) { + assignedToPLResult.documents.forEach { document -> val task = document.toObject(Task::class.java) + task?.let { + if (!taskIdMap.containsKey(it)) { + allTasks.add(it) + taskIdMap[it] = document.id + } + } + } + for (task in allTasks) { + calculateSubTasks(task, taskIdMap[task] ?: return) + } - calculateSubTasks(task, document.id) + taskList.clear() + taskList.addAll(allTasks) - if (!taskIdMap.containsKey(task)) { - taskList.add(task) - taskIdMap[task] = document.id - } + if (isAdded) { + fabMain.visibility = View.VISIBLE } - if (isAdded) fabMain.visibility = View.VISIBLE } catch (e: Exception) { if (isAdded) { Toast.makeText( @@ -181,6 +209,7 @@ class HomeFragment : Fragment() { + private suspend fun loadTasksForDev() { try { val currentUserEmail = auth.currentUser?.email ?: return @@ -189,19 +218,31 @@ class HomeFragment : Fragment() { .get() .await() - val allTasks = mutableListOf<QueryDocumentSnapshot>() - allTasks.addAll(assignedToDevResult.documents as List<QueryDocumentSnapshot>) + // Inizializza una lista per i task + val allTasks = mutableListOf<Task>() - for (document in allTasks) { + // Processa i task assegnati al Developer + assignedToDevResult.documents.forEach { document -> val task = document.toObject(Task::class.java) + task?.let { + allTasks.add(it) + taskIdMap[it] = document.id + } + } + + // Aggiorna i task aggiuntivi con il calcolo dei subtasks + for (task in allTasks) { + calculateSubTasks(task, taskIdMap[task] ?: return) + } - calculateSubTasks(task, document.id) + // Aggiungi i task caricati alla lista principale e aggiorna l'interfaccia utente + taskList.clear() + taskList.addAll(allTasks) - taskList.add(task) - taskIdMap[task] = document.id + if (isAdded) { + fabMain.visibility = View.VISIBLE } - if (isAdded) fabMain.visibility = View.VISIBLE } catch (e: Exception) { if (isAdded) { Toast.makeText( @@ -214,6 +255,7 @@ class HomeFragment : Fragment() { } + private suspend fun calculateSubTasks(task: Task, taskId: String) { try { val subTasksResult = firestore.collection("tasks") diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000000000000000000000000000000000..036d09bc5fd523323794379703c4a111d1e28a04 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_launcher_background"/> + <foreground android:drawable="@mipmap/ic_launcher_foreground"/> +</adaptive-icon> \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000000000000000000000000000000000..036d09bc5fd523323794379703c4a111d1e28a04 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_launcher_background"/> + <foreground android:drawable="@mipmap/ic_launcher_foreground"/> +</adaptive-icon> \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml deleted file mode 100644 index 6f3b755bf50c6b03d8714a9c6184705e6a08389f..0000000000000000000000000000000000000000 --- a/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@drawable/ic_launcher_foreground" /> - <monochrome android:drawable="@drawable/ic_launcher_foreground" /> -</adaptive-icon> \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml deleted file mode 100644 index 6f3b755bf50c6b03d8714a9c6184705e6a08389f..0000000000000000000000000000000000000000 --- a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@drawable/ic_launcher_foreground" /> - <monochrome android:drawable="@drawable/ic_launcher_foreground" /> -</adaptive-icon> \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp index c209e78ecd372343283f4157dcfd918ec5165bb3..d024adc221a344a9bc102402ff2e87c9a5b12a12 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..b15416f1119015dc668d55b433a776f72754f0f5 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp index b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9..a98dd3b5146a6a58faf5e270b7463128de588136 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..7e70fc6fddf4f34e2c4d7144eb40ceef557e4610 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..20dda99cfc86943de3d401ac61ea48f60691d59e Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp index 62b611da081676d42f6c3f78a2c91e7bcedddedb..cea27151d5cb91cf842f5cd274c6f04283295825 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index 948a3070fe34c611c42c0d3ad3013a0dce358be0..4c86b7da90077c6cf381982ddbf56becbfe60bca 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..c241d1407a9d36a9669ed861255944073dca746f Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index 1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f..0c6a5b6fa4bcfd9018cbaa412982e9216ed1facf 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp index 28d4b77f9f036a47549d47db79c16788749dca10..f5a4950cea298f4c311d3be8d5355877e490e6f0 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..bd96deacd02105a272ae2ea1adbaf849eb52c52b Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp index 9287f5083623b375139afb391af71cc533a7dd37..5ca973b3e2e4db1b5dc7a8ba233f7e067687254e 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index aa7d6427e6fa1074b79ccd52ef67ac15c5637e85..02c6f2ecc94892b5735278cbd0a906a39872947d 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..92093fe197ad9286e35e93b52d182f84d15d5aca Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index 9126ae37cbc3587421d6889eadd1d91fbf1994d4..483a83eaaa54b428eea37c7354cb77986812bcf9 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 474080bf5caf0b5e2ad9e4347f8fb84d418b7182..528a75b469d16f7c68e5b5afe59d43b4b57d2ec8 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,7 +1,6 @@ <resources> <!-- Base application theme. --> <style name="Base.Theme.TaskManagement" parent="Theme.Material3.DayNight.NoActionBar"> - <!-- Customize your dark theme here. --> - <!-- <item name="colorPrimary">@color/my_dark_primary</item> --> + </style> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml index ec89e05c5294be322470aa51794f456e99912a1f..d22d07a86880f368ec9e26bfffeb66cd4ccd0428 100644 --- a/app/src/main/res/values/ic_launcher_background.xml +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <color name="ic_launcher_background">@color/Gainsboro</color> + <color name="ic_launcher_background">#35398F</color> </resources> \ No newline at end of file