melhorar o calendario
This commit is contained in:
@@ -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<>());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user