This commit is contained in:
MeuNome
2026-05-11 17:21:38 +01:00
parent d1a9183fbc
commit 2aa02bd434
10 changed files with 243 additions and 47 deletions

View File

@@ -7,6 +7,7 @@ public class DailyProgress {
public int focusSessions; public int focusSessions;
public int xp; public int xp;
public String status; // "complete", "partial", "empty" public String status; // "complete", "partial", "empty"
public boolean isGoalReached;
public DailyProgress() {} public DailyProgress() {}
@@ -17,10 +18,11 @@ public class DailyProgress {
this.focusSessions = 0; this.focusSessions = 0;
this.xp = 0; this.xp = 0;
this.status = "empty"; this.status = "empty";
this.isGoalReached = false;
} }
public void updateStatus() { public void updateStatus() {
if (completedTasks >= dailyGoal && dailyGoal > 0) { if (isGoalReached || (completedTasks >= dailyGoal && dailyGoal > 0)) {
status = "complete"; status = "complete";
} else if (completedTasks > 0) { } else if (completedTasks > 0) {
status = "partial"; status = "partial";

View File

@@ -258,16 +258,55 @@ public class FirestoreManager {
if ("focus_task".equals(type) || "task_manual_completion".equals(type)) { if ("focus_task".equals(type) || "task_manual_completion".equals(type)) {
dp.completedTasks++; dp.completedTasks++;
if ("focus_task".equals(type)) dp.focusSessions++; if ("focus_task".equals(type)) dp.focusSessions++;
} else if ("daily_goal_reached".equals(type)) {
dp.isGoalReached = true;
} }
} }
} }
} }
for (DailyProgress dp : progressMap.values()) { // Backup: Buscar tarefas concluídas para preencher lacunas nos logs
dp.updateStatus(); db.collection("tasks")
} .whereEqualTo("userId", uid)
.whereEqualTo("completed", true)
.whereGreaterThanOrEqualTo("completedDate", startDate.getTime())
.whereLessThanOrEqualTo("completedDate", endDate.getTime())
.get()
.addOnSuccessListener(taskSnapshots -> {
if (taskSnapshots != null) {
Map<String, Integer> tasksPerDay = new java.util.HashMap<>();
for (com.google.firebase.firestore.QueryDocumentSnapshot doc : taskSnapshots) {
Long cDate = doc.getLong("completedDate");
if (cDate != null) {
String dateStr = sdf.format(new java.util.Date(cDate));
tasksPerDay.put(dateStr, tasksPerDay.getOrDefault(dateStr, 0) + 1);
}
}
callback.accept(progressMap); for (Map.Entry<String, Integer> entry : tasksPerDay.entrySet()) {
String dateStr = entry.getKey();
int taskCount = entry.getValue();
DailyProgress dp = progressMap.get(dateStr);
if (dp == null) {
dp = new DailyProgress(dateStr, dailyGoal);
progressMap.put(dateStr, dp);
}
// Usar o maior valor para garantir que não perdemos progresso
dp.completedTasks = Math.max(dp.completedTasks, taskCount);
}
}
for (DailyProgress dp : progressMap.values()) {
dp.updateStatus();
}
callback.accept(progressMap);
})
.addOnFailureListener(e -> {
for (DailyProgress dp : progressMap.values()) {
dp.updateStatus();
}
callback.accept(progressMap);
});
}) })
.addOnFailureListener(e -> { .addOnFailureListener(e -> {
android.util.Log.e("FLUXUP_DEBUG", "DAILY_PROGRESS_QUERY_FAIL: " + e.getMessage()); android.util.Log.e("FLUXUP_DEBUG", "DAILY_PROGRESS_QUERY_FAIL: " + e.getMessage());
@@ -300,7 +339,8 @@ public class FirestoreManager {
// Check today // Check today
String todayStr = sdf.format(checkCal.getTime()); String todayStr = sdf.format(checkCal.getTime());
DailyProgress todayDp = progressMap.get(todayStr); DailyProgress todayDp = progressMap.get(todayStr);
boolean isTodayComplete = todayDp != null && todayDp.completedTasks >= dailyGoal; if (todayDp != null) todayDp.updateStatus();
boolean isTodayComplete = todayDp != null && "complete".equals(todayDp.status);
if (isTodayComplete) { if (isTodayComplete) {
streak = 1; streak = 1;
@@ -313,7 +353,8 @@ public class FirestoreManager {
while (true) { while (true) {
String dateStr = sdf.format(checkCal.getTime()); String dateStr = sdf.format(checkCal.getTime());
DailyProgress dp = progressMap.get(dateStr); DailyProgress dp = progressMap.get(dateStr);
if (dp != null && dp.completedTasks >= dailyGoal) { if (dp != null) dp.updateStatus();
if (dp != null && "complete".equals(dp.status)) {
if (!dateStr.equals(todayStr)) streak++; if (!dateStr.equals(todayStr)) streak++;
checkCal.add(Calendar.DAY_OF_YEAR, -1); checkCal.add(Calendar.DAY_OF_YEAR, -1);
} else { } else {

View File

@@ -223,7 +223,7 @@ public class InicioFragment extends Fragment {
cal.add(Calendar.DAY_OF_MONTH, 1); cal.add(Calendar.DAY_OF_MONTH, 1);
java.util.Date endDate = cal.getTime(); java.util.Date endDate = cal.getTime();
FirestoreManager.getInstance().getDailyProgress(userId, startDate, endDate, user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 3, progressMap -> { FirestoreManager.getInstance().getDailyProgress(userId, startDate, endDate, user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 4, progressMap -> {
if (!isAdded()) return; if (!isAdded()) return;
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.US); java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.US);
@@ -231,7 +231,7 @@ public class InicioFragment extends Fragment {
DailyProgress todayDp = progressMap.get(todayStr); DailyProgress todayDp = progressMap.get(todayStr);
int completedTasksFromLogs = (todayDp != null) ? todayDp.completedTasks : 0; int completedTasksFromLogs = (todayDp != null) ? todayDp.completedTasks : 0;
int dailyGoal = user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 3; int dailyGoal = user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 4;
// Usamos o maior valor entre o campo do usuário e os logs para evitar atrasos de indexação // Usamos o maior valor entre o campo do usuário e os logs para evitar atrasos de indexação
int completedTasksToday = Math.max(user.tasks_concluidas_hoje, completedTasksFromLogs); int completedTasksToday = Math.max(user.tasks_concluidas_hoje, completedTasksFromLogs);
@@ -260,7 +260,7 @@ public class InicioFragment extends Fragment {
tvTodayXP.setText(String.valueOf(user.xp)); tvTodayXP.setText(String.valueOf(user.xp));
} }
// Atualizar Streak seguindo a regra: <3 -> 0, >=3 -> conta dia // Atualizar Streak seguindo a regra: <4 -> 0, >=4 -> conta dia
syncDailyStreak(user); syncDailyStreak(user);
android.util.Log.d("FLUXUP_DEBUG", "Streak atualizado: " + user.streak); android.util.Log.d("FLUXUP_DEBUG", "Streak atualizado: " + user.streak);
@@ -296,7 +296,7 @@ public class InicioFragment extends Fragment {
android.util.Log.d("FLUXUP_DEBUG", "CURRENT_WEEK_RANGE: " + startDate + " to " + endDate); android.util.Log.d("FLUXUP_DEBUG", "CURRENT_WEEK_RANGE: " + startDate + " to " + endDate);
FirestoreManager.getInstance().getDailyProgress(user.id_usuario, startDate, endDate, user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 3, progressMap -> { FirestoreManager.getInstance().getDailyProgress(user.id_usuario, startDate, endDate, user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 4, progressMap -> {
if (isAdded()) { if (isAdded()) {
updateProgressPath(progressMap, user); updateProgressPath(progressMap, user);
} }
@@ -392,7 +392,7 @@ public class InicioFragment extends Fragment {
String today = new java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.US).format(new java.util.Date()); String today = new java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.US).format(new java.util.Date());
int completedTasksToday = user.tasks_concluidas_hoje; int completedTasksToday = user.tasks_concluidas_hoje;
int dailyTaskGoal = user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 3; int dailyTaskGoal = user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 4;
int currentStreak = user.streak; int currentStreak = user.streak;
String lastStreakDate = user.last_streak_completed_date; String lastStreakDate = user.last_streak_completed_date;
@@ -415,6 +415,9 @@ public class InicioFragment extends Fragment {
} }
FirestoreManager.getInstance().updateUserStats(user.id_usuario, updates); FirestoreManager.getInstance().updateUserStats(user.id_usuario, updates);
// NOVO: Adicionar log de conclusão permanente da meta
FirestoreManager.getInstance().addXpLog(user.id_usuario, 0, "daily_goal_reached");
// Atualizar objeto local // Atualizar objeto local
user.streak = newStreak; user.streak = newStreak;
user.last_streak_completed_date = today; user.last_streak_completed_date = today;
@@ -432,7 +435,7 @@ public class InicioFragment extends Fragment {
if (user == null) return; if (user == null) return;
int completed = user.tasks_concluidas_hoje; int completed = user.tasks_concluidas_hoje;
int goal = user.meta_diaria_tarefas; int goal = user.meta_diaria_tarefas;
if (goal <= 0) goal = 3; // Default fallback if (goal <= 0) goal = 4; // Default fallback
// Sincronizar e mostrar streak // Sincronizar e mostrar streak
syncDailyStreak(user); syncDailyStreak(user);
@@ -625,12 +628,16 @@ public class InicioFragment extends Fragment {
// Se for hoje, usamos o valor mais atualizado do objeto user // 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 completedTasksForDay = isToday ? Math.max(completedTasksFromLogs, (user != null ? user.tasks_concluidas_hoje : 0)) : completedTasksFromLogs;
int dailyTaskGoal = (dp != null && dp.dailyGoal > 0) ? dp.dailyGoal : 3; int dailyTaskGoal = (dp != null && dp.dailyGoal > 0) ? dp.dailyGoal : 4;
if (dailyTaskGoal <= 0) dailyTaskGoal = 3; if (dailyTaskGoal <= 0) dailyTaskGoal = 4;
// LÓGICA DE STATUS SOLICITADA // LÓGICA DE STATUS SOLICITADA
String status = "empty"; String status = "empty";
if (completedTasksForDay >= dailyTaskGoal) { if (dp != null) dp.updateStatus();
if (dp != null && "complete".equals(dp.status)) {
status = "completed";
} else if (completedTasksForDay >= dailyTaskGoal) {
status = "completed"; status = "completed";
} else if (isToday) { } else if (isToday) {
status = "today"; status = "today";
@@ -698,6 +705,7 @@ public class InicioFragment extends Fragment {
private void completeTask(Task task) { private void completeTask(Task task) {
task.completed = true; task.completed = true;
task.completedDate = System.currentTimeMillis();
FirestoreManager.getInstance().updateTask(task); FirestoreManager.getInstance().updateTask(task);
updateUserTaskCount(); updateUserTaskCount();
triggerVibration(); triggerVibration();
@@ -709,7 +717,7 @@ public class InicioFragment extends Fragment {
if (currentUser != null) { if (currentUser != null) {
String uid = currentUser.getUid(); String uid = currentUser.getUid();
FirestoreManager.getInstance().getUser(uid, user -> { FirestoreManager.getInstance().getUser(uid, user -> {
if (user != null && isAdded()) { if (user != null) {
Map<String, Object> updates = new HashMap<>(); Map<String, Object> updates = new HashMap<>();
updates.put("tasks_concluidas_hoje", com.google.firebase.firestore.FieldValue.increment(1)); updates.put("tasks_concluidas_hoje", com.google.firebase.firestore.FieldValue.increment(1));
updates.put("total_tasks_concluidas", com.google.firebase.firestore.FieldValue.increment(1)); updates.put("total_tasks_concluidas", com.google.firebase.firestore.FieldValue.increment(1));
@@ -1045,11 +1053,6 @@ public class InicioFragment extends Fragment {
FirestoreManager.getInstance().addXpLog(userId, finalXp, "focus_task", taskId); FirestoreManager.getInstance().addXpLog(userId, finalXp, "focus_task", taskId);
FirestoreManager.getInstance().getUser(userId, user -> { FirestoreManager.getInstance().getUser(userId, user -> {
if (!isAdded()) {
isCompletingFocus = false;
return;
}
if (user == null) { if (user == null) {
android.util.Log.e("FLUXUP_DEBUG", "FOCUS_COMPLETE_ERROR: User object null"); android.util.Log.e("FLUXUP_DEBUG", "FOCUS_COMPLETE_ERROR: User object null");
isCompletingFocus = false; isCompletingFocus = false;

View File

@@ -146,7 +146,7 @@ public class SearchFragment extends Fragment {
cal.set(Calendar.SECOND, 59); cal.set(Calendar.SECOND, 59);
Date endDate = cal.getTime(); Date endDate = cal.getTime();
firestoreManager.getDailyProgress(currentUserId, startDate, endDate, 3, progressMap -> { firestoreManager.getDailyProgress(currentUserId, startDate, endDate, 4, progressMap -> {
updateChart(progressMap); updateChart(progressMap);
}); });

View File

@@ -36,7 +36,7 @@ public class StreakActivity extends AppCompatActivity {
private Calendar currentCalendar; private Calendar currentCalendar;
private String currentUserId; private String currentUserId;
private int currentDailyGoal = 3; private int currentDailyGoal = 4;
private Usuario currentUserObj; private Usuario currentUserObj;
@Override @Override
@@ -92,7 +92,7 @@ public class StreakActivity extends AppCompatActivity {
if (user != null && !isDestroyed()) { if (user != null && !isDestroyed()) {
currentUserObj = user; currentUserObj = user;
tvStreakCount.setText(user.streak + " dias de ofensiva!"); tvStreakCount.setText(user.streak + " dias de ofensiva!");
currentDailyGoal = user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 3; currentDailyGoal = user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 4;
loadMonthData(); loadMonthData();
} }
}); });
@@ -117,9 +117,10 @@ public class StreakActivity extends AppCompatActivity {
java.util.Date endDate = endCal.getTime(); java.util.Date endDate = endCal.getTime();
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_LOAD_MONTH_START"); android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_LOAD_MONTH_START");
android.util.Log.d("FLUXUP_DEBUG", "USER_ID: " + currentUserId); android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_USER_ID: " + currentUserId);
android.util.Log.d("FLUXUP_DEBUG", "MONTH_START: " + startDate); android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_MONTH_START: " + startDate);
android.util.Log.d("FLUXUP_DEBUG", "MONTH_END: " + endDate); android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_MONTH_END: " + endDate);
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_CURRENT_DAILY_GOAL: " + currentDailyGoal);
FirestoreManager.getInstance().getDailyProgress(currentUserId, startDate, endDate, currentDailyGoal, progressMap -> { FirestoreManager.getInstance().getDailyProgress(currentUserId, startDate, endDate, currentDailyGoal, progressMap -> {
if (isDestroyed()) return; if (isDestroyed()) return;
@@ -127,11 +128,12 @@ public class StreakActivity extends AppCompatActivity {
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_PROGRESS_DATA_SIZE: " + progressMap.size()); android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_PROGRESS_DATA_SIZE: " + progressMap.size());
int focusSessionsCount = 0; int focusSessionsCount = 0;
Map<Integer, Integer> completedTasksPerDay = new HashMap<>(); Map<Integer, DailyProgress> dailyProgressMap = new HashMap<>();
for (Map.Entry<String, DailyProgress> entry : progressMap.entrySet()) { for (Map.Entry<String, DailyProgress> entry : progressMap.entrySet()) {
DailyProgress dp = entry.getValue(); DailyProgress dp = entry.getValue();
focusSessionsCount += dp.focusSessions; 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("-"); String[] parts = entry.getKey().split("-");
if (parts.length == 3) { if (parts.length == 3) {
@@ -139,9 +141,8 @@ public class StreakActivity extends AppCompatActivity {
int m = Integer.parseInt(parts[1]) - 1; // Calendar.MONTH is 0-indexed int m = Integer.parseInt(parts[1]) - 1; // Calendar.MONTH is 0-indexed
int d = Integer.parseInt(parts[2]); int d = Integer.parseInt(parts[2]);
// Filtrar apenas para o mês e ano que estão a ser visualizados
if (y == year && m == month) { if (y == year && m == month) {
completedTasksPerDay.put(d, dp.completedTasks); dailyProgressMap.put(d, dp);
} }
} }
} }
@@ -151,9 +152,14 @@ public class StreakActivity extends AppCompatActivity {
Calendar todayCal = Calendar.getInstance(); Calendar todayCal = Calendar.getInstance();
if (todayCal.get(Calendar.YEAR) == year && todayCal.get(Calendar.MONTH) == month) { if (todayCal.get(Calendar.YEAR) == year && todayCal.get(Calendar.MONTH) == month) {
int todayDay = todayCal.get(Calendar.DAY_OF_MONTH); int todayDay = todayCal.get(Calendar.DAY_OF_MONTH);
int logsToday = completedTasksPerDay.getOrDefault(todayDay, 0); DailyProgress dpToday = dailyProgressMap.get(todayDay);
if (dpToday == null) {
dpToday = new DailyProgress(year + "-" + (month + 1) + "-" + todayDay, currentDailyGoal);
dailyProgressMap.put(todayDay, dpToday);
}
int userObjectToday = (currentUserObj != null) ? currentUserObj.tasks_concluidas_hoje : 0; int userObjectToday = (currentUserObj != null) ? currentUserObj.tasks_concluidas_hoje : 0;
completedTasksPerDay.put(todayDay, Math.max(logsToday, userObjectToday)); dpToday.completedTasks = Math.max(dpToday.completedTasks, userObjectToday);
dpToday.updateStatus();
} }
// Contar quantos dias no mês atingiram a meta // Contar quantos dias no mês atingiram a meta
@@ -162,20 +168,29 @@ public class StreakActivity extends AppCompatActivity {
int maxDaysInMonth = calcCal.getActualMaximum(Calendar.DAY_OF_MONTH); int maxDaysInMonth = calcCal.getActualMaximum(Calendar.DAY_OF_MONTH);
for (int d = 1; d <= maxDaysInMonth; d++) { for (int d = 1; d <= maxDaysInMonth; d++) {
int completedTasks = completedTasksPerDay.getOrDefault(d, 0); DailyProgress dp = dailyProgressMap.get(d);
if (completedTasks >= currentDailyGoal) { if (dp != null) {
completedDaysCount++; 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);
}
} }
} }
android.util.Log.d("FLUXUP_DEBUG", "DIAS_CUMPRIDOS_COUNT: " + completedDaysCount);
tvDaysOfPractice.setText(String.valueOf(completedDaysCount)); tvDaysOfPractice.setText(String.valueOf(completedDaysCount));
tvFocusSessions.setText(String.valueOf(focusSessionsCount)); tvFocusSessions.setText(String.valueOf(focusSessionsCount));
updateCalendar(completedTasksPerDay); updateCalendar(dailyProgressMap);
}); });
} }
private void updateCalendar(Map<Integer, Integer> completedTasksPerDay) { private void updateCalendar(Map<Integer, DailyProgress> dailyProgressMap) {
rvCalendar.setLayoutManager(new GridLayoutManager(this, 7)); rvCalendar.setLayoutManager(new GridLayoutManager(this, 7));
List<CalendarDay> days = new ArrayList<>(); List<CalendarDay> days = new ArrayList<>();
@@ -203,7 +218,6 @@ public class StreakActivity extends AppCompatActivity {
boolean isCurrentMonth = (todayYear == calYear && todayMonth == calMonth); boolean isCurrentMonth = (todayYear == calYear && todayMonth == calMonth);
for (int i = 1; i <= maxDays; i++) { for (int i = 1; i <= maxDays; i++) {
int completed = completedTasksPerDay.getOrDefault(i, 0);
boolean isCurrent = (isCurrentMonth && i == todayDay); boolean isCurrent = (isCurrentMonth && i == todayDay);
boolean isFuture = false; boolean isFuture = false;
@@ -217,18 +231,19 @@ public class StreakActivity extends AppCompatActivity {
} }
} }
// Regra: meta atingida -> complete (verde), caso contrário -> empty (cinzento) // Regra: status do DailyProgress (respeita a meta permanente se existir)
DailyProgress dp = dailyProgressMap.get(i);
String status = "empty"; String status = "empty";
if (completed >= currentDailyGoal) { if (dp != null) {
status = "complete"; dp.updateStatus();
status = dp.status;
} }
if (i == 6 && isCurrentMonth) { if (i == 6 && isCurrentMonth) {
android.util.Log.d("FLUXUP_DEBUG", "DAY_6_COMPLETED_TASKS: " + completed); android.util.Log.d("FLUXUP_DEBUG", "DAY_6_STATUS: " + status);
android.util.Log.d("FLUXUP_DEBUG", "DAY_6_STATUS: " + (completed >= currentDailyGoal ? "COMPLETE" : "EMPTY"));
} }
days.add(new CalendarDay(i, completed, isCurrent, isFuture, status)); days.add(new CalendarDay(i, dp != null ? dp.completedTasks : 0, isCurrent, isFuture, status));
} }
CalendarAdapter adapter = new CalendarAdapter(days); CalendarAdapter adapter = new CalendarAdapter(days);

View File

@@ -27,7 +27,7 @@ public class Usuario {
public int following = 0; public int following = 0;
public int tasks_concluidas_hoje = 0; public int tasks_concluidas_hoje = 0;
public int total_tasks_concluidas = 0; public int total_tasks_concluidas = 0;
public int meta_diaria_tarefas = 3; public int meta_diaria_tarefas = 4;
public int meta_diaria_foco = 60; // em minutos public int meta_diaria_foco = 60; // em minutos
public int tempo_foco_hoje = 0; // em minutos public int tempo_foco_hoje = 0; // em minutos
public int tempo_foco_total = 0; // em minutos public int tempo_foco_total = 0; // em minutos

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/background_light" />
</shape>

View File

@@ -143,7 +143,7 @@
android:id="@+id/tvTodayTasksCount" android:id="@+id/tvTodayTasksCount"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="0/3" android:text="0/4"
android:textStyle="bold" android:textStyle="bold"
android:textSize="16sp" android:textSize="16sp"
android:textColor="@color/success_green"/> android:textColor="@color/success_green"/>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="160dp"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
app:cardCornerRadius="16dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvChallengeIcon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginBottom="12dp"
android:background="@drawable/circle_bg_light"
android:gravity="center"
android:text="🎯"
android:textSize="20sp" />
<TextView
android:id="@+id/tvChallengeName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:ellipsize="end"
android:maxLines="2"
android:text="Desafio de Foco"
android:textColor="@color/text_primary"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvChallengeXP"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:text="+500 XP"
android:textColor="@color/primary_purple"
android:textSize="12sp"
android:textStyle="bold" />
<Button
android:id="@+id/btnStartChallenge"
android:layout_width="match_parent"
android:layout_height="36dp"
android:background="@drawable/button_primary"
android:padding="0dp"
android:text="Iniciar"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="12sp"
android:textStyle="bold"
app:backgroundTint="@null" />
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
app:cardCornerRadius="12dp"
app:cardElevation="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="12dp">
<androidx.cardview.widget.CardView
android:layout_width="48dp"
android:layout_height="48dp"
app:cardCornerRadius="24dp"
app:cardElevation="0dp">
<ImageView
android:id="@+id/ivUserAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_nav_profile" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Username"
android:textColor="@color/text_primary"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvUserStats"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="🔥 12 dias • Bronze"
android:textColor="@color/text_secondary"
android:textSize="12sp" />
</LinearLayout>
<Button
android:id="@+id/btnAddFriend"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:paddingHorizontal="16dp"
android:text="Adicionar"
android:textAllCaps="false"
android:textColor="@color/primary_purple"
android:textSize="12sp"
android:textStyle="bold"
app:strokeColor="@color/primary_purple" />
</LinearLayout>
</androidx.cardview.widget.CardView>