melhorar o calendario

This commit is contained in:
MeuNome
2026-04-30 18:04:23 +01:00
parent ba523f16ad
commit 84dbdac0f2
6 changed files with 490 additions and 59 deletions

View File

@@ -172,4 +172,96 @@ public class FirestoreManager {
callback.accept(0); callback.accept(0);
}); });
} }
/**
* Obtém os logs de XP de um determinado mês e ano.
*/
public void getMonthXpLogs(String uid, int year, int month, Consumer<List<Map<String, Object>>> callback) {
if (uid == null) {
callback.accept(new ArrayList<>());
return;
}
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.set(year, month, 1, 0, 0, 0);
cal.set(java.util.Calendar.MILLISECOND, 0);
java.util.Date start = cal.getTime();
cal.add(java.util.Calendar.MONTH, 1);
java.util.Date end = cal.getTime();
db.collection("xp_logs")
.whereEqualTo("userId", uid)
.whereGreaterThanOrEqualTo("created_at", start)
.whereLessThan("created_at", end)
.get()
.addOnSuccessListener(snapshots -> {
List<Map<String, Object>> logs = new ArrayList<>();
if (snapshots != null) {
for (com.google.firebase.firestore.QueryDocumentSnapshot doc : snapshots) {
Map<String, Object> log = doc.getData();
logs.add(log);
}
}
callback.accept(logs);
})
.addOnFailureListener(e -> {
android.util.Log.e("FLUXUP_DEBUG", "MONTH_XP_QUERY_FAIL: " + e.getMessage());
callback.accept(new ArrayList<>());
});
}
/**
* Calculates daily progress (completed tasks, focus sessions, xp) for a date range
*/
public void getDailyProgress(String uid, java.util.Date startDate, java.util.Date endDate, int dailyGoal, Consumer<Map<String, DailyProgress>> callback) {
if (uid == null) {
callback.accept(new java.util.HashMap<>());
return;
}
db.collection("xp_logs")
.whereEqualTo("userId", uid)
.whereGreaterThanOrEqualTo("created_at", startDate)
.whereLessThanOrEqualTo("created_at", endDate)
.get()
.addOnSuccessListener(snapshots -> {
Map<String, DailyProgress> progressMap = new java.util.HashMap<>();
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault());
if (snapshots != null) {
for (com.google.firebase.firestore.QueryDocumentSnapshot doc : snapshots) {
com.google.firebase.Timestamp ts = doc.getTimestamp("created_at");
if (ts != null) {
String dateStr = sdf.format(ts.toDate());
DailyProgress dp = progressMap.get(dateStr);
if (dp == null) {
dp = new DailyProgress(dateStr, dailyGoal);
progressMap.put(dateStr, dp);
}
String type = doc.getString("type");
Long amount = doc.getLong("amount");
if (amount != null) {
dp.xp += amount.intValue();
}
if ("focus_task".equals(type)) {
dp.completedTasks++;
dp.focusSessions++;
}
}
}
}
for (DailyProgress dp : progressMap.values()) {
dp.updateStatus();
}
callback.accept(progressMap);
})
.addOnFailureListener(e -> {
android.util.Log.e("FLUXUP_DEBUG", "DAILY_PROGRESS_QUERY_FAIL: " + e.getMessage());
callback.accept(new java.util.HashMap<>());
});
}
} }

View File

@@ -43,6 +43,7 @@ public class InicioFragment extends Fragment {
private TasksAdapter tasksAdapter; private TasksAdapter tasksAdapter;
private LinearLayout miniRankingContainer, layoutTasksSection; private LinearLayout miniRankingContainer, layoutTasksSection;
private Button btnStartFocus, btnAddTasks, btnClaimReward; private Button btnStartFocus, btnAddTasks, btnClaimReward;
private androidx.cardview.widget.CardView btnStreakPage;
private CountDownTimer countDownTimer; private CountDownTimer countDownTimer;
private LinearLayout progressPathContainer; private LinearLayout progressPathContainer;
@@ -139,6 +140,12 @@ public class InicioFragment extends Fragment {
btnClaimReward = view.findViewById(R.id.btnClaimReward); btnClaimReward = view.findViewById(R.id.btnClaimReward);
btnClaimReward.setOnClickListener(v -> claimDailyReward()); btnClaimReward.setOnClickListener(v -> claimDailyReward());
// Streak Page
btnStreakPage = view.findViewById(R.id.btnStreakPage);
btnStreakPage.setOnClickListener(v -> {
startActivity(new android.content.Intent(getActivity(), StreakActivity.class));
});
setRandomMotivationalQuote(); setRandomMotivationalQuote();
initProgressPath(); initProgressPath();
startObservingTasks(); startObservingTasks();
@@ -203,10 +210,43 @@ public class InicioFragment extends Fragment {
updateTodayCard(user); updateTodayCard(user);
checkDailyResetAndStreak(user); checkDailyResetAndStreak(user);
loadWeeklyProgress(user);
}); });
} }
} }
private void loadWeeklyProgress(Usuario user) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); // 1 = Sunday, 2 = Monday
int daysToSubtract = (dayOfWeek + 5) % 7; // Monday = 0, Sunday = 6
Calendar startCal = (Calendar) cal.clone();
startCal.add(Calendar.DAY_OF_YEAR, -daysToSubtract);
java.util.Date startDate = startCal.getTime();
Calendar endCal = (Calendar) startCal.clone();
endCal.add(Calendar.DAY_OF_YEAR, 7);
java.util.Date endDate = endCal.getTime();
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 -> {
if (!isAdded()) return;
String todayStr = new java.text.SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault()).format(new java.util.Date());
DailyProgress dpToday = progressMap.get(todayStr);
android.util.Log.d("FLUXUP_DEBUG", "HOME_TODAY_COMPLETED_TASKS: " + (dpToday != null ? dpToday.completedTasks : 0));
android.util.Log.d("FLUXUP_DEBUG", "HOME_PROGRESS_DATA: " + progressMap.size());
updateProgressPath(progressMap);
});
}
private void updateTasksUI() { private void updateTasksUI() {
if (tasksAdapter != null) { if (tasksAdapter != null) {
tasksAdapter.setTasks(currentTasks); tasksAdapter.setTasks(currentTasks);
@@ -435,35 +475,100 @@ public class InicioFragment extends Fragment {
if (i == 0) leftConnector.setVisibility(View.INVISIBLE); if (i == 0) leftConnector.setVisibility(View.INVISIBLE);
if (i == 6) rightConnector.setVisibility(View.INVISIBLE); if (i == 6) rightConnector.setVisibility(View.INVISIBLE);
// Style based on state // Default empty state
if (i < currentDayIndex) {
// Past day - Assume completed for demo
nodeCircle.setBackgroundResource(R.drawable.node_circle_bg); nodeCircle.setBackgroundResource(R.drawable.node_circle_bg);
if (getContext() != null) { if (getContext() != null) {
nodeCircle.getBackground().setTint(ContextCompat.getColor(getContext(), R.color.success_green)); nodeCircle.getBackground().setTint(android.graphics.Color.parseColor("#E0E0E0"));
nodeDayInitial.setTextColor(ContextCompat.getColor(getContext(), R.color.white)); nodeDayInitial.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
nodeDayLabel.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
} }
nodeProgress.setVisibility(View.GONE); nodeProgress.setVisibility(View.GONE);
} else if (i == currentDayIndex) {
// Today
nodeCircle.setBackgroundResource(R.drawable.node_circle_bg);
if (getContext() != null) {
nodeCircle.getBackground().setTint(ContextCompat.getColor(getContext(), R.color.primary_purple));
nodeDayInitial.setTextColor(ContextCompat.getColor(getContext(), R.color.white));
nodeDayLabel.setTextColor(ContextCompat.getColor(getContext(), R.color.primary_purple));
}
nodeProgress.setVisibility(View.VISIBLE);
} else {
// Future
node.setAlpha(0.4f);
nodeProgress.setVisibility(View.GONE);
}
dayNodes.add(node); dayNodes.add(node);
progressPathContainer.addView(node); progressPathContainer.addView(node);
} }
} }
private void updateProgressPath(Map<String, DailyProgress> progressMap) {
if (getContext() == null || dayNodes == null || dayNodes.isEmpty()) return;
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
int daysToSubtract = (dayOfWeek + 5) % 7;
Calendar startCal = (Calendar) cal.clone();
startCal.add(Calendar.DAY_OF_YEAR, -daysToSubtract);
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
for (int i = 0; i < 7; i++) {
Calendar dayCal = (Calendar) startCal.clone();
dayCal.add(Calendar.DAY_OF_YEAR, i);
String dateStr = sdf.format(dayCal.getTime());
DailyProgress dp = progressMap.get(dateStr);
View node = dayNodes.get(i);
View nodeCircle = node.findViewById(R.id.nodeCircle);
TextView nodeDayInitial = node.findViewById(R.id.nodeDayInitial);
TextView nodeDayLabel = node.findViewById(R.id.nodeDayLabel);
ProgressBar nodeProgress = node.findViewById(R.id.nodeProgress);
View rightConnector = node.findViewById(R.id.rightConnector);
if (dp != null && "complete".equals(dp.status)) {
nodeCircle.getBackground().setTint(ContextCompat.getColor(getContext(), R.color.success_green));
nodeDayInitial.setTextColor(ContextCompat.getColor(getContext(), R.color.white));
} else if (dp != null && "partial".equals(dp.status)) {
nodeCircle.getBackground().setTint(android.graphics.Color.parseColor("#4FC3F7"));
nodeDayInitial.setTextColor(ContextCompat.getColor(getContext(), R.color.white));
} else {
nodeCircle.getBackground().setTint(android.graphics.Color.parseColor("#E0E0E0"));
nodeDayInitial.setTextColor(ContextCompat.getColor(getContext(), R.color.text_primary));
}
// Streak connector color logic
if (i < 6) {
Calendar nextDayCal = (Calendar) dayCal.clone();
nextDayCal.add(Calendar.DAY_OF_YEAR, 1);
DailyProgress nextDp = progressMap.get(sdf.format(nextDayCal.getTime()));
if (dp != null && "complete".equals(dp.status) && nextDp != null && "complete".equals(nextDp.status)) {
rightConnector.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.success_green));
} else {
rightConnector.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.border_color));
}
}
if (i == currentDayIndex) {
nodeDayLabel.setVisibility(View.VISIBLE);
nodeDayLabel.setTextColor(ContextCompat.getColor(getContext(), R.color.primary_purple));
if (dp == null || !"complete".equals(dp.status)) {
nodeCircle.getBackground().setTint(ContextCompat.getColor(getContext(), R.color.primary_purple));
nodeDayInitial.setTextColor(ContextCompat.getColor(getContext(), R.color.white));
}
nodeProgress.setVisibility(View.VISIBLE);
if (dp != null && dp.dailyGoal > 0) {
nodeProgress.setProgress(dp.completedTasks * 100 / dp.dailyGoal);
} else {
nodeProgress.setProgress(0);
}
} else {
nodeDayLabel.setVisibility(View.GONE);
nodeProgress.setVisibility(View.GONE);
}
if (i > currentDayIndex) {
node.setAlpha(0.4f);
} else {
node.setAlpha(1.0f);
}
}
}
private void completeTask(Task task) { private void completeTask(Task task) {
task.completed = true; task.completed = true;
FirestoreManager.getInstance().updateTask(task); FirestoreManager.getInstance().updateTask(task);

View File

@@ -1,5 +1,7 @@
package com.fluxup.app; package com.fluxup.app;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -11,8 +13,14 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import com.google.firebase.auth.FirebaseUser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map;
public class StreakActivity extends AppCompatActivity { public class StreakActivity extends AppCompatActivity {
@@ -20,6 +28,15 @@ public class StreakActivity extends AppCompatActivity {
private TextView tvStreakCount; private TextView tvStreakCount;
private ImageButton btnClose; private ImageButton btnClose;
private TabLayout tabLayout; private TabLayout tabLayout;
private TextView tvMonthName;
private ImageButton btnPrevMonth;
private ImageButton btnNextMonth;
private TextView tvDaysOfPractice;
private TextView tvFocusSessions;
private Calendar currentCalendar;
private String currentUserId;
private int currentDailyGoal = 3;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -30,46 +47,191 @@ public class StreakActivity extends AppCompatActivity {
tvStreakCount = findViewById(R.id.tvStreakCount); tvStreakCount = findViewById(R.id.tvStreakCount);
btnClose = findViewById(R.id.btnClose); btnClose = findViewById(R.id.btnClose);
tabLayout = findViewById(R.id.tabLayout); tabLayout = findViewById(R.id.tabLayout);
tvMonthName = findViewById(R.id.tvMonthName);
btnPrevMonth = findViewById(R.id.btnPrevMonth);
btnNextMonth = findViewById(R.id.btnNextMonth);
tvDaysOfPractice = findViewById(R.id.tvDaysOfPractice);
tvFocusSessions = findViewById(R.id.tvFocusSessions);
btnClose.setOnClickListener(v -> finish()); btnClose.setOnClickListener(v -> finish());
setupCalendar(); currentCalendar = Calendar.getInstance();
FirebaseUser user = AuthManager.getInstance().getCurrentUser();
if (user != null) {
currentUserId = user.getUid();
loadUserData();
}
btnPrevMonth.setOnClickListener(v -> {
currentCalendar.add(Calendar.MONTH, -1);
loadMonthData();
});
btnNextMonth.setOnClickListener(v -> {
currentCalendar.add(Calendar.MONTH, 1);
loadMonthData();
});
setupTabs(); setupTabs();
} }
private void setupTabs() { private void setupTabs() {
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override @Override
public void onTabSelected(TabLayout.Tab tab) { public void onTabSelected(TabLayout.Tab tab) {}
// Future: toggle between personal and friends stats
}
@Override @Override
public void onTabUnselected(TabLayout.Tab tab) {} public void onTabUnselected(TabLayout.Tab tab) {}
@Override @Override
public void onTabReselected(TabLayout.Tab tab) {} public void onTabReselected(TabLayout.Tab tab) {}
}); });
} }
private void setupCalendar() { private void loadUserData() {
FirestoreManager.getInstance().getUser(currentUserId, user -> {
if (user != null && !isDestroyed()) {
tvStreakCount.setText(user.streak + " dias de ofensiva!");
currentDailyGoal = user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 3;
loadMonthData();
}
});
}
private void loadMonthData() {
int year = currentCalendar.get(Calendar.YEAR);
int month = currentCalendar.get(Calendar.MONTH);
String monthName = currentCalendar.getDisplayName(Calendar.MONTH, Calendar.LONG, new Locale("pt", "PT"));
if (monthName != null && monthName.length() > 0) {
tvMonthName.setText(monthName.substring(0, 1).toUpperCase() + monthName.substring(1) + " de " + year);
}
Calendar startCal = Calendar.getInstance();
startCal.set(year, month, 1, 0, 0, 0);
startCal.set(Calendar.MILLISECOND, 0);
java.util.Date startDate = startCal.getTime();
Calendar endCal = (Calendar) startCal.clone();
endCal.set(Calendar.DAY_OF_MONTH, startCal.getActualMaximum(Calendar.DAY_OF_MONTH));
endCal.set(Calendar.HOUR_OF_DAY, 23);
endCal.set(Calendar.MINUTE, 59);
endCal.set(Calendar.SECOND, 59);
endCal.set(Calendar.MILLISECOND, 999);
java.util.Date endDate = endCal.getTime();
android.util.Log.d("FLUXUP_DEBUG", "CURRENT_MONTH_RANGE: " + startDate + " to " + endDate);
FirestoreManager.getInstance().getDailyProgress(currentUserId, startDate, endDate, currentDailyGoal, progressMap -> {
if (isDestroyed()) return;
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_PROGRESS_DATA: " + progressMap.size());
int focusSessionsCount = 0;
int activeDaysCount = 0;
Map<Integer, Integer> completedTasksPerDay = new HashMap<>();
for (Map.Entry<String, DailyProgress> entry : progressMap.entrySet()) {
DailyProgress dp = entry.getValue();
focusSessionsCount += dp.focusSessions;
if (dp.completedTasks > 0) activeDaysCount++;
String[] parts = entry.getKey().split("-");
if (parts.length == 3) {
int day = Integer.parseInt(parts[2]);
completedTasksPerDay.put(day, dp.completedTasks);
android.util.Log.d("FLUXUP_DEBUG", "COMPLETED_TASKS_BY_DATE: " + entry.getKey() + " -> " + dp.completedTasks);
android.util.Log.d("FLUXUP_DEBUG", "DAY_STATUS_CALCULATED: " + entry.getKey() + " -> " + dp.status);
}
}
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_DAY_30_COMPLETED_TASKS: " + completedTasksPerDay.getOrDefault(30, 0));
tvDaysOfPractice.setText(String.valueOf(activeDaysCount));
tvFocusSessions.setText(String.valueOf(focusSessionsCount));
updateCalendar(completedTasksPerDay);
});
}
private void updateCalendar(Map<Integer, Integer> completedTasksPerDay) {
rvCalendar.setLayoutManager(new GridLayoutManager(this, 7)); rvCalendar.setLayoutManager(new GridLayoutManager(this, 7));
List<CalendarDay> days = new ArrayList<>(); List<CalendarDay> days = new ArrayList<>();
// Simulating April 2026 (Starts on a Wednesday) Calendar calcCal = (Calendar) currentCalendar.clone();
// Add empty slots for Mon, Tue calcCal.set(Calendar.DAY_OF_MONTH, 1);
days.add(new CalendarDay(0, false, false)); // Mon int maxDays = calcCal.getActualMaximum(Calendar.DAY_OF_MONTH);
days.add(new CalendarDay(0, false, false)); // Tue
// Days 1 to 30 int startDayOfWeek = calcCal.get(Calendar.DAY_OF_WEEK);
for (int i = 1; i <= 30; i++) { int emptySlots = startDayOfWeek - 2;
boolean isActive = (i >= 1 && i <= 7); // 7-day streak for demo if (emptySlots < 0) emptySlots = 6;
boolean isCurrent = (i == 7);
days.add(new CalendarDay(i, isActive, isCurrent)); for (int i = 0; i < emptySlots; i++) {
days.add(new CalendarDay(0, 0, false, false, "empty"));
} }
CalendarAdapter adapter = new CalendarAdapter(days); Calendar todayCal = Calendar.getInstance();
int todayYear = todayCal.get(Calendar.YEAR);
int todayMonth = todayCal.get(Calendar.MONTH);
int todayDay = todayCal.get(Calendar.DAY_OF_MONTH);
int calYear = currentCalendar.get(Calendar.YEAR);
int calMonth = currentCalendar.get(Calendar.MONTH);
boolean isCurrentMonth = (todayYear == calYear && todayMonth == calMonth);
boolean started = false;
int missedDaysInRow = 0;
for (int i = 1; i <= maxDays; i++) {
int completed = completedTasksPerDay.getOrDefault(i, 0);
boolean isCurrent = (isCurrentMonth && i == todayDay);
boolean isPastOrToday = false;
boolean isFuture = false;
if (calYear < todayYear) {
isPastOrToday = true;
} else if (calYear == todayYear) {
if (calMonth < todayMonth) {
isPastOrToday = true;
} else if (calMonth == todayMonth) {
if (i <= todayDay) isPastOrToday = true;
else isFuture = true;
} else {
isFuture = true;
}
} else {
isFuture = true;
}
boolean isComplete = completed >= currentDailyGoal;
String status = "normal";
if (isFuture) {
status = "normal";
} else if (isComplete) {
started = true;
missedDaysInRow = 0;
status = "fire";
} else if (!started) {
status = "normal";
} else {
if (missedDaysInRow < 2) {
missedDaysInRow++;
status = "ice";
} else {
status = "normal";
}
}
if (i == 30) {
android.util.Log.d("FLUXUP_DEBUG", "OFFENSIVA_DAY_30_STATUS: " + status);
}
days.add(new CalendarDay(i, completed, isCurrent, isPastOrToday, status));
}
CalendarAdapter adapter = new CalendarAdapter(days, currentDailyGoal);
rvCalendar.setAdapter(adapter); rvCalendar.setAdapter(adapter);
} }
@@ -77,21 +239,27 @@ public class StreakActivity extends AppCompatActivity {
private static class CalendarDay { private static class CalendarDay {
int dayNumber; int dayNumber;
boolean isActive; int completedTasks;
boolean isCurrent; boolean isCurrent;
boolean isPastOrToday;
String status;
CalendarDay(int dayNumber, boolean isActive, boolean isCurrent) { CalendarDay(int dayNumber, int completedTasks, boolean isCurrent, boolean isPastOrToday, String status) {
this.dayNumber = dayNumber; this.dayNumber = dayNumber;
this.isActive = isActive; this.completedTasks = completedTasks;
this.isCurrent = isCurrent; this.isCurrent = isCurrent;
this.isPastOrToday = isPastOrToday;
this.status = status;
} }
} }
private class CalendarAdapter extends RecyclerView.Adapter<CalendarAdapter.ViewHolder> { private class CalendarAdapter extends RecyclerView.Adapter<CalendarAdapter.ViewHolder> {
private final List<CalendarDay> days; private final List<CalendarDay> days;
private final int dailyGoal;
CalendarAdapter(List<CalendarDay> days) { CalendarAdapter(List<CalendarDay> days, int dailyGoal) {
this.days = days; this.days = days;
this.dailyGoal = dailyGoal;
} }
@NonNull @NonNull
@@ -109,23 +277,44 @@ public class StreakActivity extends AppCompatActivity {
holder.tvDayNumber.setText(""); holder.tvDayNumber.setText("");
holder.dayBackground.setVisibility(View.GONE); holder.dayBackground.setVisibility(View.GONE);
holder.streakConnector.setVisibility(View.GONE); holder.streakConnector.setVisibility(View.GONE);
holder.dayIndicator.setVisibility(View.GONE);
if (holder.tvEmoji != null) holder.tvEmoji.setVisibility(View.GONE);
} else { } else {
holder.tvDayNumber.setText(String.valueOf(day.dayNumber)); holder.tvDayNumber.setText(String.valueOf(day.dayNumber));
if (holder.tvEmoji != null) holder.tvEmoji.setVisibility(View.GONE);
if (day.isActive) { if ("fire".equals(day.status)) {
// AMARELO / FOGO
holder.dayBackground.setVisibility(View.VISIBLE); holder.dayBackground.setVisibility(View.VISIBLE);
holder.tvDayNumber.setTextColor(getResources().getColor(R.color.white)); holder.dayBackground.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#FFB020")));
holder.tvDayNumber.setTextColor(Color.BLACK);
if (holder.tvEmoji != null) {
holder.tvEmoji.setVisibility(View.VISIBLE);
holder.tvEmoji.setText("🔥");
}
// Simple logic for streak connection: if previous day was also active if (position > 0 && "fire".equals(days.get(position-1).status) && days.get(position-1).dayNumber != 0) {
if (position > 0 && days.get(position-1).isActive && day.dayNumber > 1) {
holder.streakConnector.setVisibility(View.VISIBLE); holder.streakConnector.setVisibility(View.VISIBLE);
holder.streakConnector.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#FFB020")));
} else { } else {
holder.streakConnector.setVisibility(View.GONE); holder.streakConnector.setVisibility(View.GONE);
} }
} else { } else if ("ice".equals(day.status)) {
holder.dayBackground.setVisibility(View.GONE); // AZUL / GELO
holder.dayBackground.setVisibility(View.VISIBLE);
holder.dayBackground.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#4FC3F7")));
holder.tvDayNumber.setTextColor(Color.WHITE);
if (holder.tvEmoji != null) {
holder.tvEmoji.setVisibility(View.VISIBLE);
holder.tvEmoji.setText("🧊");
}
holder.streakConnector.setVisibility(View.GONE);
} else {
// CINZENTO (NORMAL / FUTURO)
holder.dayBackground.setVisibility(View.VISIBLE);
holder.dayBackground.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#E0E0E0")));
holder.tvDayNumber.setTextColor(Color.BLACK);
holder.streakConnector.setVisibility(View.GONE); holder.streakConnector.setVisibility(View.GONE);
holder.tvDayNumber.setTextColor(getResources().getColor(R.color.text_primary));
} }
if (day.isCurrent) { if (day.isCurrent) {
@@ -143,6 +332,7 @@ public class StreakActivity extends AppCompatActivity {
class ViewHolder extends RecyclerView.ViewHolder { class ViewHolder extends RecyclerView.ViewHolder {
TextView tvDayNumber; TextView tvDayNumber;
TextView tvEmoji;
View dayBackground; View dayBackground;
View streakConnector; View streakConnector;
View dayIndicator; View dayIndicator;
@@ -150,6 +340,7 @@ public class StreakActivity extends AppCompatActivity {
ViewHolder(View view) { ViewHolder(View view) {
super(view); super(view);
tvDayNumber = view.findViewById(R.id.tvDayNumber); tvDayNumber = view.findViewById(R.id.tvDayNumber);
tvEmoji = view.findViewById(R.id.tvEmoji);
dayBackground = view.findViewById(R.id.dayBackground); dayBackground = view.findViewById(R.id.dayBackground);
streakConnector = view.findViewById(R.id.streakConnector); streakConnector = view.findViewById(R.id.streakConnector);
dayIndicator = view.findViewById(R.id.dayIndicator); dayIndicator = view.findViewById(R.id.dayIndicator);

View File

@@ -134,6 +134,7 @@
android:layout_alignParentEnd="true"> android:layout_alignParentEnd="true">
<ImageButton <ImageButton
android:id="@+id/btnPrevMonth"
android:layout_width="32dp" android:layout_width="32dp"
android:layout_height="32dp" android:layout_height="32dp"
android:background="?attr/selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
@@ -141,6 +142,7 @@
app:tint="@color/text_secondary" /> app:tint="@color/text_secondary" />
<ImageButton <ImageButton
android:id="@+id/btnNextMonth"
android:layout_width="32dp" android:layout_width="32dp"
android:layout_height="32dp" android:layout_height="32dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
@@ -202,9 +204,10 @@
android:textColor="@color/text_secondary" android:textColor="@color/text_secondary"
android:textSize="12sp" /> android:textSize="12sp" />
<TextView <TextView
android:id="@+id/tvDaysOfPractice"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="12" android:text="0"
android:textColor="@color/text_primary" android:textColor="@color/text_primary"
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
@@ -234,9 +237,10 @@
android:textColor="@color/text_secondary" android:textColor="@color/text_secondary"
android:textSize="12sp" /> android:textSize="12sp" />
<TextView <TextView
android:id="@+id/tvFocusSessions"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="24" android:text="0"
android:textColor="@color/text_primary" android:textColor="@color/text_primary"
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"

View File

@@ -296,15 +296,42 @@
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<!-- 5. 📊 PROGRESSO DIÁRIO --> <!-- 5. 📊 PROGRESSO DIÁRIO -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="12dp" android:layout_centerVertical="true"
android:text="Progresso Semanal" android:text="Progresso Semanal"
android:textColor="@color/text_primary" android:textColor="@color/text_primary"
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" /> android:textStyle="bold" />
<androidx.cardview.widget.CardView
android:id="@+id/btnStreakPage"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/card_background"
app:cardCornerRadius="18dp"
app:cardElevation="2dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="🔥"
android:textSize="18sp" />
</androidx.cardview.widget.CardView>
</RelativeLayout>
<HorizontalScrollView <HorizontalScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@@ -34,6 +34,18 @@
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" /> android:textStyle="bold" />
<!-- Emoji Overlay -->
<TextView
android:id="@+id/tvEmoji"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="🔥"
android:textSize="12sp"
android:visibility="gone" />
<!-- Indicator Dot (Optional) --> <!-- Indicator Dot (Optional) -->
<View <View
android:id="@+id/dayIndicator" android:id="@+id/dayIndicator"