diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml
index f9eb024..997a73a 100644
--- a/.idea/caches/deviceStreaming.xml
+++ b/.idea/caches/deviceStreaming.xml
@@ -682,6 +682,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c01ef9c..c2288bd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -31,7 +31,6 @@
-
diff --git a/app/src/main/java/com/fluxup/app/FindFriendsActivity.java b/app/src/main/java/com/fluxup/app/FindFriendsActivity.java
index 6522b60..7e22e8b 100644
--- a/app/src/main/java/com/fluxup/app/FindFriendsActivity.java
+++ b/app/src/main/java/com/fluxup/app/FindFriendsActivity.java
@@ -1,40 +1,60 @@
package com.fluxup.app;
import android.os.Bundle;
-import android.view.LayoutInflater;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
-
-import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
+import com.google.android.material.tabs.TabLayout;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.firestore.DocumentSnapshot;
+import com.google.firebase.firestore.FieldValue;
+import com.google.firebase.firestore.FirebaseFirestore;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class FindFriendsActivity extends AppCompatActivity {
private EditText etSearch;
private LinearLayout resultsContainer;
- private com.google.android.material.tabs.TabLayout tabLayout;
+ private TabLayout tabLayout;
private ImageButton btnClose;
- private String currentTab = "Sugestões";
+
+ private FirebaseFirestore db;
+ private String myUid;
+ private int activeTabPosition = 0; // 0: Sugestões, 1: Os meus amigos, 2: Pendentes
+
+ private List allFriends = new ArrayList<>();
+
+ private static class PendingRequest {
+ Usuario sender;
+ String requestId;
+ PendingRequest(Usuario sender, String requestId) {
+ this.sender = sender;
+ this.requestId = requestId;
+ }
+ }
+ private List allRequests = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_friends);
+ db = FirebaseFirestore.getInstance();
+ myUid = FirebaseAuth.getInstance().getUid();
+
initViews();
setupListeners();
- loadSuggestions();
+ loadTabContent();
}
private void initViews() {
@@ -47,62 +67,152 @@ public class FindFriendsActivity extends AppCompatActivity {
private void setupListeners() {
btnClose.setOnClickListener(v -> finish());
- etSearch.addTextChangedListener(new android.text.TextWatcher() {
- @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
- @Override public void onTextChanged(CharSequence s, int start, int before, int count) {
- if (s.length() > 0) {
- performSearch(s.toString());
- } else {
- loadSuggestions();
- }
+ tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ activeTabPosition = tab.getPosition();
+ etSearch.setText(""); // Clear search when switching tabs
+ updateSearchHint();
+ loadTabContent();
}
- @Override public void afterTextChanged(android.text.Editable s) {}
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {}
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {}
});
- tabLayout.addOnTabSelectedListener(new com.google.android.material.tabs.TabLayout.OnTabSelectedListener() {
+ etSearch.addTextChangedListener(new TextWatcher() {
@Override
- public void onTabSelected(com.google.android.material.tabs.TabLayout.Tab tab) {
- currentTab = tab.getText().toString();
- if (currentTab.equals("Sugestões")) loadSuggestions();
- else resultsContainer.removeAllViews(); // Placeholder for others
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ performSearch(s.toString());
}
- @Override public void onTabUnselected(com.google.android.material.tabs.TabLayout.Tab tab) {}
- @Override public void onTabReselected(com.google.android.material.tabs.TabLayout.Tab tab) {}
+
+ @Override
+ public void afterTextChanged(Editable s) {}
});
}
+ private void updateSearchHint() {
+ if (activeTabPosition == 0) {
+ etSearch.setHint("Procurar novas pessoas...");
+ } else if (activeTabPosition == 1) {
+ etSearch.setHint("Procurar amigos...");
+ } else {
+ etSearch.setHint("Procurar pedidos...");
+ }
+ }
+
+ private void loadTabContent() {
+ if (myUid == null) return;
+
+ resultsContainer.removeAllViews();
+
+ if (activeTabPosition == 0) {
+ loadSuggestionsTab("");
+ } else if (activeTabPosition == 1) {
+ loadFriendsTab();
+ } else {
+ loadRequestsTab();
+ }
+ }
+
private void performSearch(String query) {
- com.google.firebase.firestore.FirebaseFirestore.getInstance()
- .collection("users")
- .orderBy("usuario")
- .startAt(query)
- .endAt(query + "\uf8ff")
- .limit(10)
+ if (activeTabPosition == 0) {
+ loadSuggestionsTab(query);
+ } else if (activeTabPosition == 1) {
+ filterFriends(query);
+ } else {
+ filterRequests(query);
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // TAB 0: SUGESTÕES (Discover)
+ // -------------------------------------------------------------------------
+ private void loadSuggestionsTab(String query) {
+ // Fetch all friend IDs + sent/received request IDs to exclude
+ db.collection("friendships")
+ .whereArrayContains("users", myUid)
.get()
- .addOnSuccessListener(snapshots -> {
- resultsContainer.removeAllViews();
- for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
- Usuario u = doc.toObject(Usuario.class);
- if (u != null) addFriendItem(u);
+ .addOnSuccessListener(friendSnapshots -> {
+ List excludedIds = new ArrayList<>();
+ excludedIds.add(myUid);
+
+ for (DocumentSnapshot doc : friendSnapshots.getDocuments()) {
+ List users = (List) doc.get("users");
+ if (users != null) {
+ for (String uId : users) {
+ if (!uId.equals(myUid)) excludedIds.add(uId);
+ }
+ }
}
+
+ // Fetch sent requests
+ db.collection("friend_requests")
+ .whereEqualTo("fromUserId", myUid)
+ .get()
+ .addOnSuccessListener(sentSnap -> {
+ for (DocumentSnapshot doc : sentSnap.getDocuments()) {
+ String toUser = doc.getString("toUserId");
+ if (toUser != null) excludedIds.add(toUser);
+ }
+
+ // Fetch received requests
+ db.collection("friend_requests")
+ .whereEqualTo("toUserId", myUid)
+ .get()
+ .addOnSuccessListener(recvSnap -> {
+ for (DocumentSnapshot doc : recvSnap.getDocuments()) {
+ String fromUser = doc.getString("fromUserId");
+ if (fromUser != null) excludedIds.add(fromUser);
+ }
+
+ // Now query users
+ queryDiscoverUsers(excludedIds, query);
+ });
+ });
});
}
- private void loadSuggestions() {
- com.google.firebase.firestore.FirebaseFirestore.getInstance()
- .collection("users")
- .limit(10)
- .get()
- .addOnSuccessListener(snapshots -> {
- resultsContainer.removeAllViews();
- for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
- Usuario u = doc.toObject(Usuario.class);
- if (u != null) addFriendItem(u);
- }
- });
+ private void queryDiscoverUsers(List excludedIds, String query) {
+ if (query.isEmpty()) {
+ db.collection("users")
+ .limit(50)
+ .get()
+ .addOnSuccessListener(snapshots -> {
+ resultsContainer.removeAllViews();
+ for (DocumentSnapshot doc : snapshots.getDocuments()) {
+ Usuario u = doc.toObject(Usuario.class);
+ if (u != null && !excludedIds.contains(u.id_usuario)) {
+ addDiscoverItem(u);
+ }
+ }
+ });
+ } else {
+ db.collection("users")
+ .orderBy("usuario")
+ .startAt(query)
+ .endAt(query + "\uf8ff")
+ .limit(50)
+ .get()
+ .addOnSuccessListener(snapshots -> {
+ resultsContainer.removeAllViews();
+ for (DocumentSnapshot doc : snapshots.getDocuments()) {
+ Usuario u = doc.toObject(Usuario.class);
+ if (u != null && !excludedIds.contains(u.id_usuario)) {
+ addDiscoverItem(u);
+ }
+ }
+ });
+ }
}
- private void addFriendItem(Usuario user) {
+ private void addDiscoverItem(Usuario user) {
View view = getLayoutInflater().inflate(R.layout.item_friend_action, resultsContainer, false);
TextView tvName = view.findViewById(R.id.tvFriendName);
TextView tvStats = view.findViewById(R.id.tvFriendStats);
@@ -110,18 +220,239 @@ public class FindFriendsActivity extends AppCompatActivity {
tvName.setText(user.usuario);
tvStats.setText("Nível " + user.level + " • " + user.xp + " XP");
-
+ btnAction.setText("Adicionar");
+
AvatarView ivAvatar = view.findViewById(R.id.ivFriendAvatar);
if (ivAvatar != null && user.avatar != null) {
ivAvatar.setAvatarData(user.avatar);
ivAvatar.setLeague(user.league);
}
-
+
btnAction.setOnClickListener(v -> {
btnAction.setText("Pendente");
btnAction.setEnabled(false);
btnAction.setAlpha(0.5f);
- Toast.makeText(this, "Pedido enviado para " + user.usuario, Toast.LENGTH_SHORT).show();
+
+ Map req = new HashMap<>();
+ req.put("fromUserId", myUid);
+ req.put("toUserId", user.id_usuario);
+ req.put("status", "pending");
+ req.put("timestamp", FieldValue.serverTimestamp());
+
+ String reqId = myUid + "_" + user.id_usuario;
+ db.collection("friend_requests").document(reqId).set(req)
+ .addOnSuccessListener(aVoid -> {
+ Toast.makeText(this, "Pedido enviado para " + user.usuario, Toast.LENGTH_SHORT).show();
+ })
+ .addOnFailureListener(e -> {
+ btnAction.setText("Adicionar");
+ btnAction.setEnabled(true);
+ btnAction.setAlpha(1.0f);
+ Toast.makeText(this, "Erro ao enviar pedido.", Toast.LENGTH_SHORT).show();
+ });
+ });
+
+ resultsContainer.addView(view);
+ }
+
+ // -------------------------------------------------------------------------
+ // TAB 1: OS MEUS AMIGOS
+ // -------------------------------------------------------------------------
+ private void loadFriendsTab() {
+ db.collection("friendships")
+ .whereArrayContains("users", myUid)
+ .get()
+ .addOnSuccessListener(snapshots -> {
+ allFriends.clear();
+ if (snapshots.isEmpty()) {
+ resultsContainer.removeAllViews();
+ return;
+ }
+
+ List friendIds = new ArrayList<>();
+ for (DocumentSnapshot doc : snapshots.getDocuments()) {
+ List users = (List) doc.get("users");
+ if (users != null) {
+ for (String uId : users) {
+ if (!uId.equals(myUid)) friendIds.add(uId);
+ }
+ }
+ }
+
+ if (friendIds.isEmpty()) {
+ resultsContainer.removeAllViews();
+ return;
+ }
+
+ final int[] remaining = {friendIds.size()};
+ for (String friendId : friendIds) {
+ db.collection("users").document(friendId).get().addOnSuccessListener(snapshot -> {
+ Usuario friend = snapshot.toObject(Usuario.class);
+ if (friend != null) {
+ allFriends.add(friend);
+ }
+ remaining[0]--;
+ if (remaining[0] == 0) {
+ filterFriends(etSearch.getText().toString());
+ }
+ }).addOnFailureListener(e -> {
+ remaining[0]--;
+ if (remaining[0] == 0) {
+ filterFriends(etSearch.getText().toString());
+ }
+ });
+ }
+ });
+ }
+
+ private void filterFriends(String query) {
+ resultsContainer.removeAllViews();
+ for (Usuario friend : allFriends) {
+ if (query.isEmpty() || friend.usuario.toLowerCase().contains(query.toLowerCase())) {
+ addFriendItem(friend);
+ }
+ }
+ }
+
+ private void addFriendItem(Usuario friend) {
+ View view = getLayoutInflater().inflate(R.layout.item_friend_action, resultsContainer, false);
+ TextView tvName = view.findViewById(R.id.tvFriendName);
+ TextView tvStats = view.findViewById(R.id.tvFriendStats);
+ Button btnAction = view.findViewById(R.id.btnFriendAction);
+
+ tvName.setText(friend.usuario);
+ tvStats.setText("Nível " + friend.level + " • " + friend.xp + " XP • 🔥 " + friend.streak);
+ btnAction.setText("Ver perfil");
+
+ AvatarView ivAvatar = view.findViewById(R.id.ivFriendAvatar);
+ if (ivAvatar != null && friend.avatar != null) {
+ ivAvatar.setAvatarData(friend.avatar);
+ ivAvatar.setLeague(friend.league);
+ }
+
+ View.OnClickListener clickListener = v -> {
+ Toast.makeText(this, "Perfil de " + friend.usuario, Toast.LENGTH_SHORT).show();
+ };
+
+ btnAction.setOnClickListener(clickListener);
+ view.setOnClickListener(clickListener);
+
+ resultsContainer.addView(view);
+ }
+
+ // -------------------------------------------------------------------------
+ // TAB 2: PENDENTES (Requests)
+ // -------------------------------------------------------------------------
+ private void loadRequestsTab() {
+ db.collection("friend_requests")
+ .whereEqualTo("toUserId", myUid)
+ .whereEqualTo("status", "pending")
+ .get()
+ .addOnSuccessListener(snapshots -> {
+ allRequests.clear();
+ if (snapshots.isEmpty()) {
+ resultsContainer.removeAllViews();
+ return;
+ }
+
+ List senderIds = new ArrayList<>();
+ Map requestMap = new HashMap<>(); // senderId -> requestId
+
+ for (DocumentSnapshot doc : snapshots.getDocuments()) {
+ String fromUserId = doc.getString("fromUserId");
+ if (fromUserId != null) {
+ senderIds.add(fromUserId);
+ requestMap.put(fromUserId, doc.getId());
+ }
+ }
+
+ final int[] remaining = {senderIds.size()};
+ for (String senderId : senderIds) {
+ db.collection("users").document(senderId).get().addOnSuccessListener(userSnap -> {
+ Usuario sender = userSnap.toObject(Usuario.class);
+ if (sender != null) {
+ allRequests.add(new PendingRequest(sender, requestMap.get(senderId)));
+ }
+ remaining[0]--;
+ if (remaining[0] == 0) {
+ filterRequests(etSearch.getText().toString());
+ }
+ }).addOnFailureListener(e -> {
+ remaining[0]--;
+ if (remaining[0] == 0) {
+ filterRequests(etSearch.getText().toString());
+ }
+ });
+ }
+ });
+ }
+
+ private void filterRequests(String query) {
+ resultsContainer.removeAllViews();
+ for (PendingRequest req : allRequests) {
+ if (query.isEmpty() || req.sender.usuario.toLowerCase().contains(query.toLowerCase())) {
+ addRequestItem(req.sender, req.requestId);
+ }
+ }
+ }
+
+ private void addRequestItem(Usuario sender, String requestId) {
+ View view = getLayoutInflater().inflate(R.layout.item_friend_request, resultsContainer, false);
+ TextView tvName = view.findViewById(R.id.tvFriendName);
+ TextView tvStats = view.findViewById(R.id.tvFriendStats);
+ Button btnAccept = view.findViewById(R.id.btnAcceptFriend);
+ Button btnReject = view.findViewById(R.id.btnRejectFriend);
+
+ tvName.setText(sender.usuario);
+ tvStats.setText(sender.xp + " XP");
+
+ AvatarView ivAvatar = view.findViewById(R.id.ivFriendAvatar);
+ if (ivAvatar != null && sender.avatar != null) {
+ ivAvatar.setAvatarData(sender.avatar);
+ ivAvatar.setLeague(sender.league);
+ }
+
+ btnAccept.setOnClickListener(v -> {
+ btnAccept.setEnabled(false);
+ btnReject.setEnabled(false);
+
+ String friendshipId = myUid.compareTo(sender.id_usuario) < 0 ? myUid + "_" + sender.id_usuario : sender.id_usuario + "_" + myUid;
+ Map friendship = new HashMap<>();
+ List users = new ArrayList<>();
+ users.add(myUid);
+ users.add(sender.id_usuario);
+ friendship.put("users", users);
+ friendship.put("timestamp", FieldValue.serverTimestamp());
+
+ db.collection("friendships").document(friendshipId).set(friendship)
+ .addOnSuccessListener(aVoid -> {
+ db.collection("friend_requests").document(requestId).delete()
+ .addOnSuccessListener(aVoid2 -> {
+ Toast.makeText(this, "Pedido de " + sender.usuario + " aceite!", Toast.LENGTH_SHORT).show();
+ loadTabContent();
+ });
+ })
+ .addOnFailureListener(e -> {
+ btnAccept.setEnabled(true);
+ btnReject.setEnabled(true);
+ Toast.makeText(this, "Erro ao aceitar pedido.", Toast.LENGTH_SHORT).show();
+ });
+ });
+
+ btnReject.setOnClickListener(v -> {
+ btnAccept.setEnabled(false);
+ btnReject.setEnabled(false);
+
+ db.collection("friend_requests").document(requestId).delete()
+ .addOnSuccessListener(aVoid -> {
+ Toast.makeText(this, "Pedido recusado.", Toast.LENGTH_SHORT).show();
+ loadTabContent();
+ })
+ .addOnFailureListener(e -> {
+ btnAccept.setEnabled(true);
+ btnReject.setEnabled(true);
+ Toast.makeText(this, "Erro ao recusar pedido.", Toast.LENGTH_SHORT).show();
+ });
});
resultsContainer.addView(view);
diff --git a/app/src/main/java/com/fluxup/app/InicioFragment.java b/app/src/main/java/com/fluxup/app/InicioFragment.java
index bfe92a4..13a77ca 100644
--- a/app/src/main/java/com/fluxup/app/InicioFragment.java
+++ b/app/src/main/java/com/fluxup/app/InicioFragment.java
@@ -42,12 +42,11 @@ public class InicioFragment extends Fragment {
private TextView tvTimer, tvGreeting, tvMotivationalSubtitle, tvFocusStatus, tvFocusTitle;
private AvatarView ivUserAvatar;
private TextView tvTodayStreak, tvTodayXP, tvTodayTasksCount, tvNoTasksIncentive;
- private TextView tvDailyRewardGoal;
private ProgressBar pbDailyTasksProgress;
private androidx.recyclerview.widget.RecyclerView rvTasks;
private TasksAdapter tasksAdapter;
private LinearLayout miniRankingContainer, layoutTasksSection;
- private Button btnStartFocus, btnSecondaryFocus, btnAddTasks, btnClaimReward;
+ private Button btnStartFocus, btnSecondaryFocus, btnAddTasks;
private androidx.cardview.widget.CardView btnStreakPage;
private CountDownTimer countDownTimer;
@@ -145,10 +144,15 @@ public class InicioFragment extends Fragment {
startActivity(new android.content.Intent(getActivity(), TrophiesActivity.class));
});
- // Reward
- tvDailyRewardGoal = view.findViewById(R.id.tvDailyRewardGoal);
- btnClaimReward = view.findViewById(R.id.btnClaimReward);
- btnClaimReward.setOnClickListener(v -> claimDailyReward());
+ // Promo Card to Rewards Tab
+ view.findViewById(R.id.cardRewardsPromo).setOnClickListener(v -> {
+ if (getActivity() instanceof MainActivity) {
+ BottomNavigationView nav = ((MainActivity) getActivity()).findViewById(R.id.bottom_navigation);
+ if (nav != null) {
+ nav.setSelectedItemId(R.id.nav_rewards);
+ }
+ }
+ });
// Streak Page
btnStreakPage = view.findViewById(R.id.btnStreakPage);
@@ -797,33 +801,27 @@ public class InicioFragment extends Fragment {
}
}
- private void claimDailyReward() {
- FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
- if (currentUser != null) {
- String today = new java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.US).format(new java.util.Date());
- Map updates = new HashMap<>();
- updates.put("last_reward_claim_date", today);
- FirestoreManager.getInstance().updateUserStats(currentUser.getUid(), updates);
- addXP(100);
-
- btnClaimReward.setEnabled(false);
- btnClaimReward.setText("Resgatado");
- btnClaimReward.setAlpha(0.5f);
-
- if (getContext() != null) {
- Toast.makeText(getContext(), "Recompensa diária resgatada! +100 XP", Toast.LENGTH_LONG).show();
- }
- }
- }
+
private void showDeleteConfirmDialog(Task task) {
new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
- .setTitle("Eliminar Tarefa")
- .setMessage("Tens a certeza que queres eliminar esta tarefa?")
+ .setTitle("Apagar Tarefa")
+ .setMessage("Tens a certeza que queres apagar esta tarefa?")
.setNegativeButton("Cancelar", null)
- .setPositiveButton("Sim", (dialog, which) -> {
- FirestoreManager.getInstance().deleteTask(task.id);
- refreshHomeStats();
+ .setPositiveButton("Apagar", (dialog, which) -> {
+ com.google.firebase.firestore.FirebaseFirestore.getInstance()
+ .collection("tasks").document(task.id).delete()
+ .addOnSuccessListener(aVoid -> {
+ refreshHomeStats();
+ if (getContext() != null) {
+ Toast.makeText(getContext(), "Tarefa apagada.", Toast.LENGTH_SHORT).show();
+ }
+ })
+ .addOnFailureListener(e -> {
+ if (getContext() != null) {
+ Toast.makeText(getContext(), "Erro ao apagar tarefa: " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
+ }
+ });
})
.show();
}
@@ -1150,6 +1148,7 @@ public class InicioFragment extends Fragment {
new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
.setTitle("Editar Tarefa")
.setView(dialogView)
+ .setNeutralButton("Apagar", (dialog, which) -> showDeleteConfirmDialog(task))
.setNegativeButton("Cancelar", null)
.setPositiveButton("Atualizar", (dialog, which) -> {
String title = etTitle.getText().toString().trim();
diff --git a/app/src/main/java/com/fluxup/app/MainActivity.java b/app/src/main/java/com/fluxup/app/MainActivity.java
index f23658b..197bb5a 100644
--- a/app/src/main/java/com/fluxup/app/MainActivity.java
+++ b/app/src/main/java/com/fluxup/app/MainActivity.java
@@ -48,6 +48,8 @@ public class MainActivity extends AppCompatActivity {
} else if (itemId == R.id.nav_trophies) {
startActivity(new android.content.Intent(this, TrophiesActivity.class));
return true;
+ } else if (itemId == R.id.nav_rewards) {
+ selectedFragment = new RewardsFragment();
} else if (itemId == R.id.nav_profile) {
selectedFragment = new ProfileFragment();
} else if (itemId == R.id.nav_search) {
diff --git a/app/src/main/java/com/fluxup/app/MyFriendsActivity.java b/app/src/main/java/com/fluxup/app/MyFriendsActivity.java
deleted file mode 100644
index 938cb3a..0000000
--- a/app/src/main/java/com/fluxup/app/MyFriendsActivity.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package com.fluxup.app;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-import androidx.appcompat.app.AppCompatActivity;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.firestore.FirebaseFirestore;
-import java.util.ArrayList;
-import java.util.List;
-
-public class MyFriendsActivity extends AppCompatActivity {
-
- private EditText etSearch;
- private LinearLayout friendsContainer, emptyState;
- private TextView tvFriendsCount;
- private FirebaseFirestore db;
- private String myUid;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_my_friends);
-
- db = FirebaseFirestore.getInstance();
- myUid = FirebaseAuth.getInstance().getUid();
-
- etSearch = findViewById(R.id.etSearchFriends);
- friendsContainer = findViewById(R.id.friendsListContainer);
- emptyState = findViewById(R.id.emptyStateContainer);
- tvFriendsCount = findViewById(R.id.tvFriendsCount);
-
- findViewById(R.id.btnBack).setOnClickListener(v -> finish());
- findViewById(R.id.btnFindNewFriends).setOnClickListener(v -> startActivity(new Intent(this, FindFriendsActivity.class)));
- findViewById(R.id.btnGoToFindFriends).setOnClickListener(v -> startActivity(new Intent(this, FindFriendsActivity.class)));
-
- loadFriends();
- }
-
- private void loadFriends() {
- if (myUid == null) return;
-
- // Fetching users I follow
- db.collection("follows")
- .whereEqualTo("followerId", myUid)
- .get()
- .addOnSuccessListener(snapshots -> {
- if (snapshots.isEmpty()) {
- showEmptyState(true);
- return;
- }
-
- showEmptyState(false);
- tvFriendsCount.setText(snapshots.size() + " Amigos");
- friendsContainer.removeAllViews();
-
- for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
- String friendId = doc.getString("followingId");
- if (friendId != null) {
- fetchFriendDetails(friendId);
- }
- }
- });
- }
-
- private void fetchFriendDetails(String friendId) {
- db.collection("users").document(friendId).get().addOnSuccessListener(snapshot -> {
- Usuario friend = snapshot.toObject(Usuario.class);
- if (friend != null) {
- addFriendItem(friend);
- }
- });
- }
-
- private void addFriendItem(Usuario friend) {
- View view = getLayoutInflater().inflate(R.layout.item_ranking_user, friendsContainer, false);
- TextView tvName = view.findViewById(R.id.tvRankingName);
- TextView tvXP = view.findViewById(R.id.tvRankingXP);
- TextView tvLabel = view.findViewById(R.id.tvRankingLabelTu);
-
- tvName.setText(friend.usuario);
- tvXP.setText(friend.xp + " XP");
- tvLabel.setText("Nível " + friend.level);
- tvLabel.setVisibility(View.VISIBLE);
- tvLabel.setTextColor(getResources().getColor(R.color.primary_purple));
-
- AvatarView ivAvatar = view.findViewById(R.id.ivRankingAvatar);
- if (ivAvatar != null && friend.avatar != null) {
- ivAvatar.setAvatarData(friend.avatar);
- ivAvatar.setLeague(friend.league);
- }
-
- view.setOnClickListener(v -> {
- // Open public profile (future)
- Toast.makeText(this, "Perfil de " + friend.usuario, Toast.LENGTH_SHORT).show();
- });
-
- friendsContainer.addView(view);
- }
-
- private void showEmptyState(boolean show) {
- emptyState.setVisibility(show ? View.VISIBLE : View.GONE);
- friendsContainer.setVisibility(show ? View.GONE : View.VISIBLE);
- }
-}
diff --git a/app/src/main/java/com/fluxup/app/ProfileFragment.java b/app/src/main/java/com/fluxup/app/ProfileFragment.java
index 29a5dc9..0f2c315 100644
--- a/app/src/main/java/com/fluxup/app/ProfileFragment.java
+++ b/app/src/main/java/com/fluxup/app/ProfileFragment.java
@@ -134,19 +134,19 @@ public class ProfileFragment extends Fragment {
// Add Friends (+)
View btnAddFriends = view.findViewById(R.id.btnAddFriends);
if (btnAddFriends != null) {
- btnAddFriends.setOnClickListener(v -> startActivity(new Intent(getActivity(), MyFriendsActivity.class)));
+ btnAddFriends.setOnClickListener(v -> startActivity(new Intent(getActivity(), FindFriendsActivity.class)));
}
// View All Friends
View btnViewAllFriends = view.findViewById(R.id.btnViewAllFriends);
if (btnViewAllFriends != null) {
- btnViewAllFriends.setOnClickListener(v -> startActivity(new Intent(getActivity(), MyFriendsActivity.class)));
+ btnViewAllFriends.setOnClickListener(v -> startActivity(new Intent(getActivity(), FindFriendsActivity.class)));
}
// Find Friends (ADICIONAR AMIGOS)
View btnFindFriends = view.findViewById(R.id.btnFindFriends);
if (btnFindFriends != null) {
- btnFindFriends.setOnClickListener(v -> startActivity(new Intent(getActivity(), MyFriendsActivity.class)));
+ btnFindFriends.setOnClickListener(v -> startActivity(new Intent(getActivity(), FindFriendsActivity.class)));
}
// View All Badges
@@ -408,21 +408,39 @@ public class ProfileFragment extends Fragment {
}
private void startObservingFriends() {
+ if (AuthManager.getInstance().getCurrentUser() == null) return;
+ String myUid = AuthManager.getInstance().getCurrentUser().getUid();
friendsListener = com.google.firebase.firestore.FirebaseFirestore.getInstance()
- .collection("users")
+ .collection("friendships")
+ .whereArrayContains("users", myUid)
.limit(3)
.addSnapshotListener((snapshots, e) -> {
if (e != null || snapshots == null) return;
friendsListContainer.removeAllViews();
for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
- Usuario f = doc.toObject(Usuario.class);
- if (f != null && !f.id_usuario.equals(AuthManager.getInstance().getCurrentUser().getUid())) {
- addFriendItem(f);
+ List users = (List) doc.get("users");
+ if (users != null) {
+ for (String uId : users) {
+ if (!uId.equals(myUid)) {
+ fetchAndAddProfileFriendItem(uId);
+ }
+ }
}
}
});
}
+ private void fetchAndAddProfileFriendItem(String friendId) {
+ com.google.firebase.firestore.FirebaseFirestore.getInstance()
+ .collection("users").document(friendId).get().addOnSuccessListener(snapshot -> {
+ if (getContext() == null || view == null) return;
+ Usuario friend = snapshot.toObject(Usuario.class);
+ if (friend != null) {
+ addFriendItem(friend);
+ }
+ });
+ }
+
private void addFriendItem(Usuario friend) {
View v = getLayoutInflater().inflate(R.layout.item_ranking_user, friendsListContainer, false);
((TextView) v.findViewById(R.id.tvRankingName)).setText(friend.usuario);
diff --git a/app/src/main/java/com/fluxup/app/TrophiesActivity.java b/app/src/main/java/com/fluxup/app/TrophiesActivity.java
index db36d0a..5dabce0 100644
--- a/app/src/main/java/com/fluxup/app/TrophiesActivity.java
+++ b/app/src/main/java/com/fluxup/app/TrophiesActivity.java
@@ -12,6 +12,8 @@ import androidx.appcompat.app.AppCompatActivity;
import android.widget.Button;
import android.widget.ProgressBar;
import java.util.Calendar;
+import java.util.ArrayList;
+import java.util.List;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.ListenerRegistration;
import android.graphics.Color;
@@ -219,26 +221,94 @@ public class TrophiesActivity extends AppCompatActivity {
}
private void observeRanking(String filter) {
- if (rankingListener != null) rankingListener.remove();
+ if (rankingListener != null) {
+ rankingListener.remove();
+ rankingListener = null;
+ }
- com.google.firebase.firestore.Query query = com.google.firebase.firestore.FirebaseFirestore.getInstance()
- .collection("users")
- .orderBy("xp_semanal", com.google.firebase.firestore.Query.Direction.DESCENDING)
- .limit(20);
+ String myUid = mAuth.getUid();
+ android.util.Log.d("FLUXUP_DEBUG", "CURRENT_USER_ID: " + myUid);
+ android.util.Log.d("FLUXUP_DEBUG", "LEAGUE_TAB_SELECTED: " + filter);
- rankingListener = query.addSnapshotListener((snapshots, e) -> {
- if (e != null || snapshots == null) return;
- rankingContainer.removeAllViews();
-
- int position = 1;
- String myUid = mAuth.getUid();
+ if ("friends".equals(filter)) {
+ // Load accepted friends from "friendships"
+ com.google.firebase.firestore.FirebaseFirestore.getInstance()
+ .collection("friendships")
+ .whereArrayContains("users", myUid)
+ .get()
+ .addOnSuccessListener(snapshots -> {
+ List friendIds = new ArrayList<>();
+ for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
+ List users = (List) doc.get("users");
+ if (users != null) {
+ for (String uId : users) {
+ if (!uId.equals(myUid)) {
+ friendIds.add(uId);
+ }
+ }
+ }
+ }
- for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
- Usuario user = doc.toObject(Usuario.class);
- if (user == null) continue;
- addRankingItem(position++, user, myUid);
- }
- });
+ android.util.Log.d("FLUXUP_DEBUG", "MY_ACCEPTED_FRIENDS_COUNT: " + friendIds.size());
+ android.util.Log.d("FLUXUP_DEBUG", "LEAGUE_FRIENDS_LIST: " + friendIds.toString());
+
+ rankingContainer.removeAllViews();
+ if (friendIds.isEmpty()) {
+ return;
+ }
+
+ // Fetch details of all friends and sort by xp_semanal desc
+ List friendList = new ArrayList<>();
+ final int[] remaining = {friendIds.size()};
+ for (String friendId : friendIds) {
+ com.google.firebase.firestore.FirebaseFirestore.getInstance()
+ .collection("users").document(friendId).get().addOnSuccessListener(userSnap -> {
+ Usuario friend = userSnap.toObject(Usuario.class);
+ if (friend != null) {
+ friendList.add(friend);
+ }
+ remaining[0]--;
+ if (remaining[0] == 0) {
+ // Sort by xp_semanal descending
+ friendList.sort((u1, u2) -> Integer.compare(u2.xp_semanal, u1.xp_semanal));
+ int pos = 1;
+ for (Usuario u : friendList) {
+ addRankingItem(pos++, u, myUid);
+ }
+ }
+ }).addOnFailureListener(e -> {
+ remaining[0]--;
+ if (remaining[0] == 0) {
+ friendList.sort((u1, u2) -> Integer.compare(u2.xp_semanal, u1.xp_semanal));
+ int pos = 1;
+ for (Usuario u : friendList) {
+ addRankingItem(pos++, u, myUid);
+ }
+ }
+ });
+ }
+ });
+ } else {
+ // Global ranking
+ com.google.firebase.firestore.Query query = com.google.firebase.firestore.FirebaseFirestore.getInstance()
+ .collection("users")
+ .orderBy("xp_semanal", com.google.firebase.firestore.Query.Direction.DESCENDING)
+ .limit(20);
+
+ rankingListener = query.addSnapshotListener((snapshots, e) -> {
+ if (e != null || snapshots == null) return;
+
+ android.util.Log.d("FLUXUP_DEBUG", "GLOBAL_RANKING_COUNT: " + snapshots.size());
+
+ rankingContainer.removeAllViews();
+ int position = 1;
+ for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
+ Usuario user = doc.toObject(Usuario.class);
+ if (user == null) continue;
+ addRankingItem(position++, user, myUid);
+ }
+ });
+ }
}
private void addRankingItem(int pos, Usuario user, String myUid) {
diff --git a/app/src/main/java/com/fluxup/app/Usuario.java b/app/src/main/java/com/fluxup/app/Usuario.java
index 9add5a2..1437202 100644
--- a/app/src/main/java/com/fluxup/app/Usuario.java
+++ b/app/src/main/java/com/fluxup/app/Usuario.java
@@ -40,6 +40,15 @@ public class Usuario {
public AvatarData avatar = new AvatarData(); // Avatar gamificado
public java.util.List dias_concluidos = new java.util.ArrayList<>();
+ // Gamification & Rewards
+ public int login_streak = 0;
+ public String last_login_claim_date = ""; // YYYY-MM-DD
+ public java.util.List claimed_missions_today = new java.util.ArrayList<>();
+ public String claimed_missions_date = ""; // YYYY-MM-DD
+ public java.util.List claimed_streak_rewards = new java.util.ArrayList<>();
+ public String last_box_open_date = ""; // YYYY-MM-DD
+ public int coins = 0;
+
public Usuario() {}
public Usuario(String id_usuario, String usuario, String email, String palavra_passe, String numero) {
diff --git a/app/src/main/res/layout/activity_my_friends.xml b/app/src/main/res/layout/activity_my_friends.xml
deleted file mode 100644
index 95cef3f..0000000
--- a/app/src/main/res/layout/activity_my_friends.xml
+++ /dev/null
@@ -1,122 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/fragment_inicio.xml b/app/src/main/res/layout/fragment_inicio.xml
index 43caaed..0a3b6a1 100644
--- a/app/src/main/res/layout/fragment_inicio.xml
+++ b/app/src/main/res/layout/fragment_inicio.xml
@@ -443,14 +443,17 @@
-
+
+ app:cardElevation="2dp">
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="8dp">
-
+
diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml
index 77fc9e1..f81df82 100644
--- a/app/src/main/res/menu/bottom_nav_menu.xml
+++ b/app/src/main/res/menu/bottom_nav_menu.xml
@@ -8,6 +8,10 @@
android:id="@+id/nav_trophies"
android:icon="@drawable/ic_nav_trophy"
android:title="Troféus" />
+