406 lines
15 KiB
Java
406 lines
15 KiB
Java
package com.fluxup.app;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.os.Bundle;
|
|
import android.os.CountDownTimer;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.CheckBox;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.TextView;
|
|
import com.google.firebase.auth.FirebaseUser;
|
|
import com.google.firebase.firestore.ListenerRegistration;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
import android.widget.Toast;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.fragment.app.Fragment;
|
|
import android.view.animation.BounceInterpolator;
|
|
import android.animation.AnimatorSet;
|
|
import android.animation.ObjectAnimator;
|
|
import java.util.Calendar;
|
|
import java.util.Locale;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
|
|
public class InicioFragment extends Fragment {
|
|
|
|
private TextView tvTimer, tvProgressText, tvGreeting;
|
|
private FrameLayout timerBlock;
|
|
private LinearLayout tasksContainer;
|
|
private ProgressBar pbDailyTasks;
|
|
private CountDownTimer countDownTimer;
|
|
private LinearLayout progressPathContainer;
|
|
private List<View> dayNodes = new ArrayList<>();
|
|
private int currentDayIndex = 0;
|
|
private ListenerRegistration tasksListener, userListener;
|
|
private List<Task> currentTasks = new ArrayList<>();
|
|
|
|
private boolean isTimerRunning = false;
|
|
private long timeLeftInMillis = 25 * 60 * 1000; // 25 minutos
|
|
|
|
|
|
@Nullable
|
|
@Override
|
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
|
View view = inflater.inflate(R.layout.fragment_inicio, container, false);
|
|
|
|
tvTimer = view.findViewById(R.id.tvTimer);
|
|
tvGreeting = view.findViewById(R.id.tvGreeting);
|
|
timerBlock = view.findViewById(R.id.timerBlock);
|
|
tasksContainer = view.findViewById(R.id.tasksContainer);
|
|
tvProgressText = view.findViewById(R.id.tvProgressText);
|
|
pbDailyTasks = view.findViewById(R.id.pbDailyTasks);
|
|
progressPathContainer = view.findViewById(R.id.progressPathContainer);
|
|
|
|
progressPathContainer = view.findViewById(R.id.progressPathContainer);
|
|
|
|
initProgressPath();
|
|
startObservingTasks();
|
|
startObservingUser();
|
|
|
|
|
|
View btnStartFocus = view.findViewById(R.id.btnStartFocus);
|
|
if (btnStartFocus != null) {
|
|
btnStartFocus.setOnClickListener(v -> {
|
|
if (!isTimerRunning) {
|
|
startTimer();
|
|
((TextView)btnStartFocus).setText("Pausar Foco");
|
|
} else {
|
|
pauseTimer();
|
|
((TextView)btnStartFocus).setText("Continuar Foco");
|
|
}
|
|
});
|
|
}
|
|
|
|
view.findViewById(R.id.btnAddTasks).setOnClickListener(v -> showAddTaskDialog());
|
|
|
|
View btnStreak = view.findViewById(R.id.btnStreak);
|
|
if (btnStreak != null) {
|
|
btnStreak.setOnClickListener(v -> {
|
|
android.content.Intent intent = new android.content.Intent(getActivity(), StreakActivity.class);
|
|
startActivity(intent);
|
|
});
|
|
}
|
|
|
|
updateCountDownText();
|
|
|
|
|
|
return view;
|
|
}
|
|
|
|
private void startObservingTasks() {
|
|
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
|
if (currentUser != null) {
|
|
tasksListener = FirestoreManager.getInstance().observeTasks(currentUser.getUid(), tasks -> {
|
|
currentTasks = tasks;
|
|
updateTasksUI();
|
|
});
|
|
}
|
|
}
|
|
|
|
private void startObservingUser() {
|
|
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
|
if (currentUser != null) {
|
|
userListener = FirestoreManager.getInstance().observeUser(currentUser.getUid(), user -> {
|
|
if (tvGreeting != null) {
|
|
tvGreeting.setText("Olá, " + user.usuario + "!");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
private void updateTasksUI() {
|
|
if (getContext() == null || tasksContainer == null) return;
|
|
tasksContainer.removeAllViews();
|
|
|
|
for (Task task : currentTasks) {
|
|
androidx.cardview.widget.CardView card = new androidx.cardview.widget.CardView(getContext());
|
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
);
|
|
params.setMargins(0, 0, 0, 16);
|
|
card.setLayoutParams(params);
|
|
card.setRadius(getResources().getDimension(R.dimen.radius_md));
|
|
card.setCardElevation(2f);
|
|
card.setContentPadding(16, 16, 16, 16);
|
|
|
|
LinearLayout layout = new LinearLayout(getContext());
|
|
layout.setOrientation(LinearLayout.HORIZONTAL);
|
|
layout.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
|
|
|
CheckBox cb = new CheckBox(getContext());
|
|
cb.setText(task.title);
|
|
cb.setTextColor(getResources().getColor(R.color.text_primary));
|
|
cb.setTextSize(16);
|
|
cb.setChecked(task.completed);
|
|
|
|
if (task.completed) {
|
|
cb.setTextColor(getResources().getColor(R.color.success_green));
|
|
}
|
|
|
|
cb.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
|
task.completed = isChecked;
|
|
FirestoreManager.getInstance().updateTask(task);
|
|
// The observer will trigger updateTasksUI again, so we don't need manual UI update here
|
|
|
|
if (isChecked) {
|
|
addXP(task.xpReward);
|
|
}
|
|
});
|
|
|
|
layout.addView(cb);
|
|
card.addView(layout);
|
|
tasksContainer.addView(card);
|
|
}
|
|
updateProgress();
|
|
}
|
|
|
|
private void addXP(int amount) {
|
|
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
|
if (currentUser != null) {
|
|
com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
|
.collection("users").document(currentUser.getUid())
|
|
.update("xp", com.google.firebase.firestore.FieldValue.increment(amount));
|
|
}
|
|
}
|
|
|
|
private void updateProgress() {
|
|
int total = currentTasks.size();
|
|
int completed = 0;
|
|
for (Task task : currentTasks) {
|
|
if (task.completed) completed++;
|
|
}
|
|
|
|
if (tvProgressText != null) {
|
|
tvProgressText.setText(completed + " de " + total + " concluídos");
|
|
}
|
|
if (pbDailyTasks != null && total > 0) {
|
|
int progress = (completed * 100) / total;
|
|
pbDailyTasks.setProgress(progress);
|
|
updatePathProgress(progress);
|
|
}
|
|
}
|
|
|
|
private void initProgressPath() {
|
|
if (getContext() == null || progressPathContainer == null) return;
|
|
|
|
progressPathContainer.removeAllViews();
|
|
dayNodes.clear();
|
|
|
|
String[] days = {"SEG", "TER", "QUA", "QUI", "SEX", "SÁB", "DOM"};
|
|
String[] initials = {"S", "T", "Q", "Q", "S", "S", "D"};
|
|
|
|
// Get current day of week (Calendar.MONDAY is 2, SUNDAY is 1)
|
|
Calendar calendar = Calendar.getInstance();
|
|
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
|
|
// Map to 0-6 (Mon-Sun)
|
|
currentDayIndex = (dayOfWeek + 5) % 7;
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
View node = LayoutInflater.from(getContext()).inflate(R.layout.item_day_node, progressPathContainer, false);
|
|
|
|
View topConnector = node.findViewById(R.id.topConnector);
|
|
View bottomConnector = node.findViewById(R.id.bottomConnector);
|
|
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);
|
|
|
|
nodeDayLabel.setText(days[i]);
|
|
nodeDayInitial.setText(initials[i]);
|
|
|
|
// Hide connectors at ends
|
|
if (i == 0) topConnector.setVisibility(View.INVISIBLE);
|
|
if (i == 6) bottomConnector.setVisibility(View.INVISIBLE);
|
|
|
|
// Style based on state
|
|
if (i < currentDayIndex) {
|
|
// Past day - Assume completed for demo
|
|
nodeCircle.setBackgroundResource(R.drawable.node_circle_bg);
|
|
nodeCircle.getBackground().setTint(getResources().getColor(R.color.success_green));
|
|
nodeDayInitial.setTextColor(getResources().getColor(R.color.white));
|
|
nodeProgress.setVisibility(View.GONE);
|
|
} else if (i == currentDayIndex) {
|
|
// Today
|
|
nodeCircle.setBackgroundResource(R.drawable.node_circle_bg);
|
|
nodeCircle.getBackground().setTint(getResources().getColor(R.color.primary_purple));
|
|
nodeDayInitial.setTextColor(getResources().getColor(R.color.white));
|
|
nodeDayLabel.setTextColor(getResources().getColor(R.color.primary_purple));
|
|
nodeProgress.setVisibility(View.VISIBLE);
|
|
} else {
|
|
// Future
|
|
node.setAlpha(0.4f);
|
|
nodeProgress.setVisibility(View.GONE);
|
|
}
|
|
|
|
dayNodes.add(node);
|
|
progressPathContainer.addView(node);
|
|
}
|
|
}
|
|
|
|
private void updatePathProgress(int progress) {
|
|
if (currentDayIndex >= dayNodes.size()) return;
|
|
|
|
View todayNode = dayNodes.get(currentDayIndex);
|
|
ProgressBar nodeProgress = todayNode.findViewById(R.id.nodeProgress);
|
|
View nodeCircle = todayNode.findViewById(R.id.nodeCircle);
|
|
TextView nodeDayInitial = todayNode.findViewById(R.id.nodeDayInitial);
|
|
|
|
if (nodeProgress != null) {
|
|
nodeProgress.setProgress(progress);
|
|
}
|
|
|
|
if (progress == 100) {
|
|
// Task completion animation
|
|
nodeCircle.getBackground().setTint(getResources().getColor(R.color.success_green));
|
|
triggerSuccessAnimation(todayNode);
|
|
} else {
|
|
nodeCircle.getBackground().setTint(getResources().getColor(R.color.primary_purple));
|
|
}
|
|
}
|
|
|
|
private void triggerSuccessAnimation(View view) {
|
|
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.2f, 1f);
|
|
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 1.2f, 1f);
|
|
|
|
AnimatorSet animatorSet = new AnimatorSet();
|
|
animatorSet.setDuration(500);
|
|
animatorSet.setInterpolator(new BounceInterpolator());
|
|
animatorSet.playTogether(scaleX, scaleY);
|
|
animatorSet.start();
|
|
|
|
Toast.makeText(getContext(), "Dia Completado! 🎉", Toast.LENGTH_SHORT).show();
|
|
}
|
|
|
|
|
|
private void startTimer() {
|
|
countDownTimer = new CountDownTimer(timeLeftInMillis, 1000) {
|
|
@Override
|
|
public void onTick(long millisUntilFinished) {
|
|
timeLeftInMillis = millisUntilFinished;
|
|
updateCountDownText();
|
|
}
|
|
|
|
@Override
|
|
public void onFinish() {
|
|
isTimerRunning = false;
|
|
if(getContext() != null) {
|
|
Toast.makeText(getContext(), "Foco concluído! +50 XP", Toast.LENGTH_LONG).show();
|
|
addXP(50);
|
|
}
|
|
}
|
|
}.start();
|
|
|
|
isTimerRunning = true;
|
|
}
|
|
|
|
private void pauseTimer() {
|
|
if (countDownTimer != null) {
|
|
countDownTimer.cancel();
|
|
}
|
|
isTimerRunning = false;
|
|
}
|
|
|
|
|
|
private void updateCountDownText() {
|
|
int minutes = (int) (timeLeftInMillis / 1000) / 60;
|
|
int seconds = (int) (timeLeftInMillis / 1000) % 60;
|
|
|
|
String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
|
|
if(tvTimer != null) tvTimer.setText(timeLeftFormatted);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroyView() {
|
|
super.onDestroyView();
|
|
if (tasksListener != null) {
|
|
tasksListener.remove();
|
|
}
|
|
if (userListener != null) {
|
|
userListener.remove();
|
|
}
|
|
pauseTimer(); // Parar o timer se a view for destruída
|
|
}
|
|
|
|
private void showAddTaskDialog() {
|
|
if (getContext() == null) return;
|
|
|
|
// Build input field
|
|
android.widget.EditText editText = new android.widget.EditText(getContext());
|
|
editText.setHint("Escreve o teu desafio…");
|
|
editText.setInputType(android.text.InputType.TYPE_CLASS_TEXT
|
|
| android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
|
|
editText.setTextSize(16);
|
|
editText.setSingleLine(false);
|
|
editText.setMaxLines(3);
|
|
|
|
// Wrap with padding
|
|
android.widget.FrameLayout container = new android.widget.FrameLayout(getContext());
|
|
android.widget.FrameLayout.LayoutParams lp = new android.widget.FrameLayout.LayoutParams(
|
|
android.widget.FrameLayout.LayoutParams.MATCH_PARENT,
|
|
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT);
|
|
int dp24 = (int) (24 * getResources().getDisplayMetrics().density);
|
|
lp.setMargins(dp24, dp24 / 2, dp24, 0);
|
|
editText.setLayoutParams(lp);
|
|
container.addView(editText);
|
|
|
|
androidx.appcompat.app.AlertDialog dialog = new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
|
|
.setTitle("Novo Desafio")
|
|
.setView(container)
|
|
.setNegativeButton("Cancelar", null)
|
|
.setPositiveButton("Adicionar", null)
|
|
.create();
|
|
|
|
|
|
dialog.setOnShowListener(d -> {
|
|
// Style buttons
|
|
android.widget.Button btnAdd = dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE);
|
|
android.widget.Button btnCancel = dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEGATIVE);
|
|
if (btnAdd != null) btnAdd.setTextColor(getResources().getColor(R.color.primary_purple));
|
|
if (btnCancel != null) btnCancel.setTextColor(getResources().getColor(R.color.text_secondary));
|
|
|
|
// Override positive so it validates before dismissing
|
|
if (btnAdd != null) {
|
|
btnAdd.setOnClickListener(v -> {
|
|
String title = editText.getText().toString().trim();
|
|
if (title.isEmpty()) {
|
|
editText.setError("Escreve um desafio");
|
|
return;
|
|
}
|
|
saveNewTask(title);
|
|
dialog.dismiss();
|
|
});
|
|
}
|
|
|
|
|
|
editText.requestFocus();
|
|
if (dialog.getWindow() != null) {
|
|
dialog.getWindow().setSoftInputMode(
|
|
android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
|
}
|
|
});
|
|
|
|
dialog.show();
|
|
}
|
|
|
|
private void saveNewTask(String title) {
|
|
com.google.firebase.auth.FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
|
if (currentUser == null) return;
|
|
|
|
String uid = currentUser.getUid();
|
|
String taskId = com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
|
.collection("tasks").document().getId();
|
|
|
|
Task task = new Task(taskId, title, 10, uid);
|
|
FirestoreManager.getInstance().addTask(task);
|
|
}
|
|
}
|