calendario

This commit is contained in:
MeuNome
2026-05-12 17:16:45 +01:00
parent 2aa02bd434
commit 03074a881e
7 changed files with 169 additions and 82 deletions

View File

@@ -1230,6 +1230,18 @@
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="36" />
<option name="brand" value="samsung" />
<option name="codename" value="m1q" />
<option name="id" value="m1q" />
<option name="labId" value="google" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S26" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="36" />
<option name="brand" value="samsung" />

View File

@@ -1,6 +1,7 @@
package com.fluxup.app;
public class DailyProgress {
public String userId;
public String date;
public int completedTasks;
public int dailyGoal;
@@ -8,6 +9,9 @@ public class DailyProgress {
public int xp;
public String status; // "complete", "partial", "empty"
public boolean isGoalReached;
public boolean isCompleted;
public Object createdAt;
public Object updatedAt;
public DailyProgress() {}
@@ -19,11 +23,14 @@ public class DailyProgress {
this.xp = 0;
this.status = "empty";
this.isGoalReached = false;
this.isCompleted = false;
}
public void updateStatus() {
if (isGoalReached || (completedTasks >= dailyGoal && dailyGoal > 0)) {
if (isCompleted || isGoalReached || (completedTasks >= dailyGoal && dailyGoal > 0)) {
status = "complete";
isCompleted = true;
isGoalReached = true;
} else if (completedTasks > 0) {
status = "partial";
} else {

View File

@@ -3,11 +3,14 @@ package com.fluxup.app;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.ListenerRegistration;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.SetOptions;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import com.google.firebase.firestore.FieldValue;
import com.google.firebase.firestore.Query;
public class FirestoreManager {
@@ -369,4 +372,71 @@ public class FirestoreManager {
updateUserStats(uid, updates, () -> callback.accept(finalStreak));
});
}
/**
* Guarda ou atualiza o progresso diário na coleção daily_progress.
*/
public void saveDailyProgress(DailyProgress dp) {
if (dp.userId == null || dp.date == null) return;
String docId = dp.userId + "_" + dp.date;
android.util.Log.d("FLUXUP_DEBUG", "SAVE_DAILY_PROGRESS_START");
android.util.Log.d("FLUXUP_DEBUG", "USER_ID: " + dp.userId);
android.util.Log.d("FLUXUP_DEBUG", "TODAY_DATE: " + dp.date);
android.util.Log.d("FLUXUP_DEBUG", "COMPLETED_TASKS_TODAY: " + dp.completedTasks);
android.util.Log.d("FLUXUP_DEBUG", "DAILY_GOAL: " + dp.dailyGoal);
Map<String, Object> data = new java.util.HashMap<>();
data.put("userId", dp.userId);
data.put("date", dp.date);
data.put("completedTasks", dp.completedTasks);
data.put("dailyGoal", dp.dailyGoal);
data.put("focusSessions", dp.focusSessions);
data.put("xp", dp.xp);
data.put("isCompleted", dp.isCompleted);
data.put("updatedAt", FieldValue.serverTimestamp());
// Se for a primeira vez, definimos o createdAt
db.collection("daily_progress").document(docId).get().addOnSuccessListener(snapshot -> {
if (!snapshot.exists()) {
data.put("createdAt", FieldValue.serverTimestamp());
}
db.collection("daily_progress").document(docId).set(data, SetOptions.merge())
.addOnSuccessListener(aVoid -> android.util.Log.d("FLUXUP_DEBUG", "DAILY_PROGRESS_SAVED"))
.addOnFailureListener(e -> android.util.Log.e("FLUXUP_DEBUG", "DAILY_PROGRESS_SAVE_FAIL: " + e.getMessage()));
});
}
/**
* Procura o progresso diário de um utilizador num intervalo de datas.
*/
public void getMonthlyDailyProgress(String userId, String startDate, String endDate, Consumer<List<DailyProgress>> callback) {
android.util.Log.d("FLUXUP_DEBUG", "LOAD_MONTHLY_PROGRESS_START");
android.util.Log.d("FLUXUP_DEBUG", "MONTH_START: " + startDate);
android.util.Log.d("FLUXUP_DEBUG", "MONTH_END: " + endDate);
db.collection("daily_progress")
.whereEqualTo("userId", userId)
.whereEqualTo("isCompleted", true)
.whereGreaterThanOrEqualTo("date", startDate)
.whereLessThanOrEqualTo("date", endDate)
.get()
.addOnSuccessListener(snapshots -> {
List<DailyProgress> results = new ArrayList<>();
if (snapshots != null) {
for (QueryDocumentSnapshot doc : snapshots) {
DailyProgress dp = doc.toObject(DailyProgress.class);
results.add(dp);
}
}
android.util.Log.d("FLUXUP_DEBUG", "COMPLETED_DAYS_FROM_FIREBASE: " + results.size());
android.util.Log.d("FLUXUP_DEBUG", "DIAS_CUMPRIDOS_COUNT: " + results.size());
callback.accept(results);
})
.addOnFailureListener(e -> {
android.util.Log.e("FLUXUP_DEBUG", "LOAD_MONTHLY_PROGRESS_FAIL: " + e.getMessage());
callback.accept(new ArrayList<>());
});
}
}

View File

@@ -396,7 +396,24 @@ public class InicioFragment extends Fragment {
int currentStreak = user.streak;
String lastStreakDate = user.last_streak_completed_date;
boolean willIncrement = (completedTasksToday >= dailyTaskGoal) && !today.equals(lastStreakDate);
boolean isGoalReached = (completedTasksToday >= dailyTaskGoal);
// PERSISTÊNCIA NO FIRESTORE (daily_progress)
DailyProgress dp = new DailyProgress(today, dailyTaskGoal);
dp.userId = user.id_usuario;
dp.completedTasks = completedTasksToday;
dp.isCompleted = isGoalReached;
dp.updateStatus();
FirestoreManager.getInstance().saveDailyProgress(dp);
// PERSISTÊNCIA DO HISTÓRICO REAL NO OBJETO USUÁRIO (Retrocompatibilidade)
if (isGoalReached && (user.dias_concluidos == null || !user.dias_concluidos.contains(today))) {
if (user.dias_concluidos == null) user.dias_concluidos = new java.util.ArrayList<>();
user.dias_concluidos.add(today);
FirestoreManager.getInstance().updateUserField(user.id_usuario, "dias_concluidos", com.google.firebase.firestore.FieldValue.arrayUnion(today));
}
boolean willIncrement = isGoalReached && !today.equals(lastStreakDate);
int displayedStreak = 0;
if (completedTasksToday < dailyTaskGoal) {
@@ -415,7 +432,7 @@ public class InicioFragment extends Fragment {
}
FirestoreManager.getInstance().updateUserStats(user.id_usuario, updates);
// NOVO: Adicionar log de conclusão permanente da meta
// Adicionar log de conclusão permanente da meta
FirestoreManager.getInstance().addXpLog(user.id_usuario, 0, "daily_goal_reached");
// Atualizar objeto local
@@ -621,35 +638,23 @@ public class InicioFragment extends Fragment {
ProgressBar nodeProgress = node.findViewById(R.id.nodeProgress);
View rightConnector = node.findViewById(R.id.rightConnector);
// LÓGICA DE STATUS SOLICITADA
// LÓGICA DE HISTÓRICO PERSISTENTE SOLICITADA
boolean isToday = (i == currentDayIndex);
int completedTasksFromLogs = (dp != null) ? dp.completedTasks : 0;
boolean isDateCompleted = (user != null && user.dias_concluidos != null && user.dias_concluidos.contains(dateStr));
// Se for hoje, usamos o valor mais atualizado do objeto user
int completedTasksForDay = isToday ? Math.max(completedTasksFromLogs, (user != null ? user.tasks_concluidas_hoje : 0)) : completedTasksFromLogs;
int dailyTaskGoal = (dp != null && dp.dailyGoal > 0) ? dp.dailyGoal : 4;
if (dailyTaskGoal <= 0) dailyTaskGoal = 4;
// LÓGICA DE STATUS SOLICITADA
String status = "empty";
if (dp != null) dp.updateStatus();
if (dp != null && "complete".equals(dp.status)) {
status = "completed";
} else if (completedTasksForDay >= dailyTaskGoal) {
if (isDateCompleted) {
status = "completed";
} else if (isToday) {
status = "today";
}
int completedTasksForDay = isToday ? (user != null ? user.tasks_concluidas_hoje : 0) : ((progressMap != null && progressMap.get(dateStr) != null) ? progressMap.get(dateStr).completedTasks : 0);
int dailyTaskGoal = (progressMap != null && progressMap.get(dateStr) != null && progressMap.get(dateStr).dailyGoal > 0) ? progressMap.get(dateStr).dailyGoal : (user != null ? user.meta_diaria_tarefas : 4);
if (dailyTaskGoal <= 0) dailyTaskGoal = 4;
// DEBUG LOGS
android.util.Log.d("FLUXUP_DEBUG", "WEEK_DAY: " + i);
android.util.Log.d("FLUXUP_DEBUG", "DATE: " + dateStr);
android.util.Log.d("FLUXUP_DEBUG", "COMPLETED_TASKS: " + completedTasksForDay);
android.util.Log.d("FLUXUP_DEBUG", "DAILY_GOAL: " + dailyTaskGoal);
android.util.Log.d("FLUXUP_DEBUG", "IS_TODAY: " + isToday);
android.util.Log.d("FLUXUP_DEBUG", "FINAL_STATUS: " + status);
android.util.Log.d("FLUXUP_DEBUG", "WEEK_DAY: " + i + " | DATE: " + dateStr + " | COMPLETED: " + isDateCompleted + " | STATUS: " + status);
// RENDERIZAÇÃO
if ("completed".equals(status)) {
@@ -660,10 +665,9 @@ public class InicioFragment extends Fragment {
Calendar nextDayCal = (Calendar) dayCal.clone();
nextDayCal.add(Calendar.DAY_OF_YEAR, 1);
String nextDateStr = sdf.format(nextDayCal.getTime());
DailyProgress nextDp = progressMap.get(nextDateStr);
boolean isNextCompleted = (user != null && user.dias_concluidos != null && user.dias_concluidos.contains(nextDateStr));
// Se o próximo dia também estiver completo, conector verde
if (nextDp != null && nextDp.completedTasks >= nextDp.dailyGoal && nextDp.dailyGoal > 0) {
if (isNextCompleted) {
rightConnector.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.success_green));
} else {
rightConnector.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.border_color));

View File

@@ -21,6 +21,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
public class StreakActivity extends AppCompatActivity {
@@ -116,74 +117,72 @@ public class StreakActivity extends AppCompatActivity {
endCal.add(Calendar.MONTH, 1);
java.util.Date endDate = endCal.getTime();
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_LOAD_MONTH_START");
android.util.Log.d("FLUXUP_DEBUG", "STREAK_VALUE_FROM_HOME_SOURCE: " + (currentUserObj != null ? currentUserObj.streak : "null"));
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_USER_ID: " + currentUserId);
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_MONTH_START: " + startDate);
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_MONTH_END: " + endDate);
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_CURRENT_DAILY_GOAL: " + currentDailyGoal);
android.util.Log.d("FLUXUP_DEBUG", "LOAD_MONTHLY_PROGRESS_START");
String startDateStr = String.format(Locale.US, "%04d-%02d-%02d", year, month + 1, 1);
Calendar endMonthCal = (Calendar) startCal.clone();
endMonthCal.set(Calendar.DAY_OF_MONTH, endMonthCal.getActualMaximum(Calendar.DAY_OF_MONTH));
String endDateStr = String.format(Locale.US, "%04d-%02d-%02d", year, month + 1, endMonthCal.getActualMaximum(Calendar.DAY_OF_MONTH));
android.util.Log.d("FLUXUP_DEBUG", "MONTH_START: " + startDateStr);
android.util.Log.d("FLUXUP_DEBUG", "MONTH_END: " + endDateStr);
FirestoreManager.getInstance().getDailyProgress(currentUserId, startDate, endDate, currentDailyGoal, progressMap -> {
FirestoreManager.getInstance().getMonthlyDailyProgress(currentUserId, startDateStr, endDateStr, progressList -> {
if (isDestroyed()) return;
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_PROGRESS_DATA_SIZE: " + progressMap.size());
android.util.Log.d("FLUXUP_DEBUG", "COMPLETED_DAYS_FROM_FIREBASE: " + progressList.size());
int focusSessionsCount = 0;
Map<Integer, DailyProgress> dailyProgressMap = new HashMap<>();
Set<String> completedDatesThisMonth = new java.util.HashSet<>();
for (Map.Entry<String, DailyProgress> entry : progressMap.entrySet()) {
DailyProgress dp = entry.getValue();
for (DailyProgress dp : progressList) {
focusSessionsCount += dp.focusSessions;
android.util.Log.d("FLUXUP_DEBUG", "TASK_DATE: " + entry.getKey() + " | COMPLETED_TASKS: " + dp.completedTasks + " | GOAL: " + dp.dailyGoal + " | PERMANENT: " + dp.isGoalReached);
String[] parts = entry.getKey().split("-");
completedDatesThisMonth.add(dp.date);
String[] parts = dp.date.split("-");
if (parts.length == 3) {
int y = Integer.parseInt(parts[0]);
int m = Integer.parseInt(parts[1]) - 1; // Calendar.MONTH is 0-indexed
int d = Integer.parseInt(parts[2]);
dailyProgressMap.put(d, dp);
}
}
if (y == year && m == month) {
dailyProgressMap.put(d, dp);
// MERGE com dias_concluidos do objeto user para retrocompatibilidade/consistência
if (currentUserObj != null && currentUserObj.dias_concluidos != null) {
String monthPrefix = String.format(Locale.US, "%04d-%02d-", year, month + 1);
for (String date : currentUserObj.dias_concluidos) {
if (date.startsWith(monthPrefix)) {
completedDatesThisMonth.add(date);
}
}
}
android.util.Log.d("FLUXUP_DEBUG", "COMPLETED_DAYS_COUNT: " + completedDatesThisMonth.size());
// Aplicar o fallback seguro para HOJE usando o tasks_concluidas_hoje do utilizador
// devido a potenciais atrasos na indexação dos xp_logs com serverTimestamp
Calendar todayCal = Calendar.getInstance();
if (todayCal.get(Calendar.YEAR) == year && todayCal.get(Calendar.MONTH) == month) {
int todayDay = todayCal.get(Calendar.DAY_OF_MONTH);
DailyProgress dpToday = dailyProgressMap.get(todayDay);
if (dpToday == null) {
dpToday = new DailyProgress(year + "-" + (month + 1) + "-" + todayDay, currentDailyGoal);
dpToday = new DailyProgress(String.format(Locale.US, "%04d-%02d-%02d", year, month + 1, todayDay), currentDailyGoal);
dailyProgressMap.put(todayDay, dpToday);
}
int userObjectToday = (currentUserObj != null) ? currentUserObj.tasks_concluidas_hoje : 0;
dpToday.completedTasks = Math.max(dpToday.completedTasks, userObjectToday);
dpToday.updateStatus();
}
// Contar quantos dias no mês atingiram a meta
int completedDaysCount = 0;
Calendar calcCal = (Calendar) currentCalendar.clone();
int maxDaysInMonth = calcCal.getActualMaximum(Calendar.DAY_OF_MONTH);
for (int d = 1; d <= maxDaysInMonth; d++) {
DailyProgress dp = dailyProgressMap.get(d);
if (dp != null) {
dp.updateStatus();
boolean isComplete = "complete".equals(dp.status);
if (isComplete) {
completedDaysCount++;
}
if (d == 6 || d == 11) {
android.util.Log.d("FLUXUP_DEBUG", "DAY_" + d + "_COMPLETED_TASKS: " + dp.completedTasks);
android.util.Log.d("FLUXUP_DEBUG", "DAY_" + d + "_IS_COMPLETED: " + isComplete);
}
if (dpToday.isCompleted) {
completedDatesThisMonth.add(dpToday.date);
}
}
android.util.Log.d("FLUXUP_DEBUG", "DIAS_CUMPRIDOS_COUNT: " + completedDaysCount);
tvDaysOfPractice.setText(String.valueOf(completedDaysCount));
// Mostrar total de dias concluídos (Histórico Real do Mês)
int diasCumpridos = completedDatesThisMonth.size();
android.util.Log.d("FLUXUP_DEBUG", "DIAS_CUMPRIDOS_VALUE: " + diasCumpridos);
tvDaysOfPractice.setText(String.valueOf(diasCumpridos));
tvFocusSessions.setText(String.valueOf(focusSessionsCount));
updateCalendar(dailyProgressMap);
@@ -231,16 +230,23 @@ public class StreakActivity extends AppCompatActivity {
}
}
// Regra: status do DailyProgress (respeita a meta permanente se existir)
// Regra: Histórico Persistente
DailyProgress dp = dailyProgressMap.get(i);
String dateKey = String.format(Locale.US, "%04d-%02d-%02d", calYear, calMonth + 1, i);
String status = "empty";
if (dp != null) {
if (currentUserObj != null && currentUserObj.dias_concluidos != null && currentUserObj.dias_concluidos.contains(dateKey)) {
status = "complete";
} else if (dp != null) {
dp.updateStatus();
status = dp.status;
}
if (i == 6 && isCurrentMonth) {
android.util.Log.d("FLUXUP_DEBUG", "DAY_6_STATUS: " + status);
if (i == 11 && isCurrentMonth) {
android.util.Log.d("FLUXUP_DEBUG", "DAY_11_STATUS: " + status);
}
if (i == 12 && isCurrentMonth) {
android.util.Log.d("FLUXUP_DEBUG", "DAY_12_STATUS: " + status);
}
days.add(new CalendarDay(i, dp != null ? dp.completedTasks : 0, isCurrent, isFuture, status));

View File

@@ -37,6 +37,7 @@ public class Usuario {
public String last_streak_completed_date = ""; // YYYY-MM-DD
public String last_reward_claim_date = ""; // YYYY-MM-DD
public String avatar_url = "";
public java.util.List<String> dias_concluidos = new java.util.ArrayList<>();
public Usuario() {}

View File

@@ -71,8 +71,7 @@ public class UsuariosService {
android.util.Log.d("FLUXUP_SERVICE", "A guardar utilizador no Firestore (Coleção: users, ID: " + uid + ")...");
getFirestore().collection("users").document(uid).set(usuario)
.addOnSuccessListener(aVoid -> {
android.util.Log.d("FLUXUP_SERVICE", "Utilizador guardado no Firestore com sucesso. A criar tarefas iniciais...");
createInitialTasks(uid);
android.util.Log.d("FLUXUP_SERVICE", "Utilizador guardado no Firestore com sucesso.");
usuario.palavra_passe = tempPass;
callback.onSuccess(usuario);
})
@@ -90,18 +89,6 @@ public class UsuariosService {
});
}
private static void createInitialTasks(String uid) {
String[] defaultTasks = {
"Completar o perfil",
"Iniciar primeira sessão de foco",
"Definir objetivo diário"
};
for (String title : defaultTasks) {
String taskId = getFirestore().collection("tasks").document().getId();
Task task = new Task(taskId, title, 50, 25, uid);
getFirestore().collection("tasks").document(taskId).set(task);
}
}
public static void recuperarPalavraPasse(Context context, String email, ServiceCallback<Void> callback) {
FirebaseAuth.getInstance().sendPasswordResetEmail(email)