erro ao realizar a tarefa
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
<activity android:name=".StatisticsActivity" />
|
||||
<activity android:name=".FindFriendsActivity" />
|
||||
<activity android:name=".TrophiesActivity" />
|
||||
<activity android:name=".MyFriendsActivity" />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,9 +4,14 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
@@ -24,6 +24,18 @@ public class FirestoreManager {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém os dados do utilizador uma única vez.
|
||||
*/
|
||||
public void getUser(String uid, Consumer<Usuario> callback) {
|
||||
db.collection("users").document(uid).get().addOnSuccessListener(snapshot -> {
|
||||
if (snapshot.exists()) {
|
||||
Usuario user = snapshot.toObject(Usuario.class);
|
||||
if (user != null) callback.accept(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Observa as mudanças no documento do utilizador em tempo real.
|
||||
*/
|
||||
@@ -83,4 +95,16 @@ public class FirestoreManager {
|
||||
public void updateUserStats(String uid, Map<String, Object> updates) {
|
||||
db.collection("users").document(uid).update(updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regista um log de XP.
|
||||
*/
|
||||
public void addXpLog(String uid, int amount, String type) {
|
||||
Map<String, Object> log = new java.util.HashMap<>();
|
||||
log.put("userId", uid);
|
||||
log.put("amount", amount);
|
||||
log.put("type", type);
|
||||
log.put("created_at", com.google.firebase.firestore.FieldValue.serverTimestamp());
|
||||
db.collection("xp_logs").add(log);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Button;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.firestore.ListenerRegistration;
|
||||
import java.util.HashMap;
|
||||
@@ -28,6 +29,7 @@ import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
|
||||
public class InicioFragment extends Fragment {
|
||||
@@ -36,7 +38,9 @@ public class InicioFragment extends Fragment {
|
||||
private TextView tvTodayStreak, tvTodayXP, tvTodayTasksCount, tvNoTasksIncentive;
|
||||
private TextView tvDailyRewardGoal;
|
||||
private ProgressBar pbDailyTasksProgress;
|
||||
private LinearLayout tasksContainer, miniRankingContainer;
|
||||
private androidx.recyclerview.widget.RecyclerView rvTasks;
|
||||
private TasksAdapter tasksAdapter;
|
||||
private LinearLayout miniRankingContainer, layoutTasksSection;
|
||||
private Button btnStartFocus, btnAddTasks, btnClaimReward;
|
||||
|
||||
private CountDownTimer countDownTimer;
|
||||
@@ -56,7 +60,10 @@ public class InicioFragment extends Fragment {
|
||||
"Um passo de cada vez.",
|
||||
"A consistência é a chave.",
|
||||
"Foca no progresso, não na perfeição.",
|
||||
"Hoje é um ótimo dia para ser produtivo!"
|
||||
"Hoje é um ótimo dia para ser produtivo!",
|
||||
"Vence a procrastinação agora.",
|
||||
"A tua versão do futuro vai agradecer.",
|
||||
"Pequenos passos, grandes resultados."
|
||||
};
|
||||
|
||||
|
||||
@@ -70,7 +77,10 @@ public class InicioFragment extends Fragment {
|
||||
tvMotivationalSubtitle = view.findViewById(R.id.tvMotivationalSubtitle);
|
||||
view.findViewById(R.id.cardProfileAvatar).setOnClickListener(v -> {
|
||||
if (getActivity() instanceof MainActivity) {
|
||||
((MainActivity) getActivity()).findViewById(R.id.nav_profile).performClick();
|
||||
BottomNavigationView nav = ((MainActivity) getActivity()).findViewById(R.id.bottom_navigation);
|
||||
if (nav != null) {
|
||||
nav.setSelectedItemId(R.id.nav_profile);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -81,7 +91,17 @@ public class InicioFragment extends Fragment {
|
||||
pbDailyTasksProgress = view.findViewById(R.id.pbDailyTasksProgress);
|
||||
|
||||
// Tasks
|
||||
tasksContainer = view.findViewById(R.id.tasksContainer);
|
||||
layoutTasksSection = view.findViewById(R.id.layoutTasksSection);
|
||||
rvTasks = view.findViewById(R.id.rvTasks);
|
||||
rvTasks.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(getContext()));
|
||||
tasksAdapter = new TasksAdapter(currentTasks, new TasksAdapter.OnTaskClickListener() {
|
||||
@Override public void onTaskFocus(Task task) { selectTaskForFocus(task); }
|
||||
@Override public void onTaskDelete(Task task) { showDeleteConfirmDialog(task); }
|
||||
@Override public void onTaskEdit(Task task) { showEditTaskDialog(task); }
|
||||
});
|
||||
rvTasks.setAdapter(tasksAdapter);
|
||||
setupSwipeToDelete();
|
||||
|
||||
tvNoTasksIncentive = view.findViewById(R.id.tvNoTasksIncentive);
|
||||
btnAddTasks = view.findViewById(R.id.btnAddTasks);
|
||||
btnAddTasks.setOnClickListener(v -> showAddTaskDialog());
|
||||
@@ -92,7 +112,9 @@ public class InicioFragment extends Fragment {
|
||||
btnStartFocus.setOnClickListener(v -> {
|
||||
if (!isTimerRunning) {
|
||||
if (selectedTaskForFocus == null) {
|
||||
Toast.makeText(getContext(), "Seleciona uma tarefa primeiro!", Toast.LENGTH_SHORT).show();
|
||||
if (getContext() != null) {
|
||||
Toast.makeText(getContext(), "Seleciona uma tarefa primeiro!", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
return;
|
||||
}
|
||||
startTimer();
|
||||
@@ -137,7 +159,11 @@ public class InicioFragment extends Fragment {
|
||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
tasksListener = FirestoreManager.getInstance().observeTasks(currentUser.getUid(), tasks -> {
|
||||
currentTasks = tasks;
|
||||
List<Task> activeTasks = new ArrayList<>();
|
||||
for (Task t : tasks) {
|
||||
if (!t.completed) activeTasks.add(t);
|
||||
}
|
||||
currentTasks = activeTasks;
|
||||
updateTasksUI();
|
||||
updateTodayCard();
|
||||
});
|
||||
@@ -159,131 +185,157 @@ public class InicioFragment extends Fragment {
|
||||
}
|
||||
|
||||
// Update Reward Button State
|
||||
if (user.tasks_concluidas_hoje >= user.meta_diaria_tarefas) {
|
||||
String today = new java.text.SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new java.util.Date());
|
||||
boolean alreadyClaimed = today.equals(user.last_reward_claim_date);
|
||||
|
||||
if (user.tasks_concluidas_hoje >= user.meta_diaria_tarefas && !alreadyClaimed) {
|
||||
btnClaimReward.setEnabled(true);
|
||||
btnClaimReward.setAlpha(1.0f);
|
||||
btnClaimReward.setText("Resgatar");
|
||||
} else if (alreadyClaimed) {
|
||||
btnClaimReward.setEnabled(false);
|
||||
btnClaimReward.setAlpha(0.5f);
|
||||
btnClaimReward.setText("Resgatado");
|
||||
} else {
|
||||
btnClaimReward.setEnabled(false);
|
||||
btnClaimReward.setAlpha(0.5f);
|
||||
btnClaimReward.setText("Resgatar");
|
||||
}
|
||||
|
||||
checkDailyReset(user);
|
||||
|
||||
updateTodayCard(user);
|
||||
checkDailyResetAndStreak(user);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTasksUI() {
|
||||
if (getContext() == null || tasksContainer == null) return;
|
||||
tasksContainer.removeAllViews();
|
||||
|
||||
for (Task task : currentTasks) {
|
||||
View taskView = LayoutInflater.from(getContext()).inflate(R.layout.item_task_home, tasksContainer, false);
|
||||
|
||||
TextView tvTitle = taskView.findViewById(R.id.tvTaskTitle);
|
||||
TextView tvDuration = taskView.findViewById(R.id.tvTaskDuration);
|
||||
CheckBox cb = taskView.findViewById(R.id.cbTask);
|
||||
Button btnStart = taskView.findViewById(R.id.btnStartTaskFocus);
|
||||
|
||||
tvTitle.setText(task.title);
|
||||
tvDuration.setText(task.duration + " min");
|
||||
cb.setChecked(task.completed);
|
||||
|
||||
if (task.completed) {
|
||||
tvTitle.setPaintFlags(tvTitle.getPaintFlags() | android.graphics.Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
tvTitle.setTextColor(getResources().getColor(R.color.text_secondary));
|
||||
btnStart.setEnabled(false);
|
||||
btnStart.setAlpha(0.5f);
|
||||
}
|
||||
|
||||
cb.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
if (isChecked && !task.completed) {
|
||||
completeTask(task);
|
||||
}
|
||||
});
|
||||
|
||||
btnStart.setOnClickListener(v -> {
|
||||
selectedTaskForFocus = task;
|
||||
timeLeftInMillis = task.duration * 60 * 1000;
|
||||
updateCountDownText();
|
||||
Toast.makeText(getContext(), "Tarefa selecionada: " + task.title, Toast.LENGTH_SHORT).show();
|
||||
|
||||
// Highlight selection
|
||||
for (int i = 0; i < tasksContainer.getChildCount(); i++) {
|
||||
tasksContainer.getChildAt(i).setBackground(null);
|
||||
}
|
||||
taskView.setBackgroundResource(R.drawable.task_selected_bg);
|
||||
});
|
||||
|
||||
taskView.setOnLongClickListener(v -> {
|
||||
showDeleteConfirmDialog(task);
|
||||
return true;
|
||||
});
|
||||
|
||||
tasksContainer.addView(taskView);
|
||||
if (tasksAdapter != null) {
|
||||
tasksAdapter.setTasks(currentTasks);
|
||||
}
|
||||
updateTodayCard();
|
||||
}
|
||||
|
||||
private void selectTaskForFocus(Task task) {
|
||||
selectedTaskForFocus = task;
|
||||
timeLeftInMillis = task.duration * 60 * 1000;
|
||||
updateCountDownText();
|
||||
if (getContext() != null) {
|
||||
Toast.makeText(getContext(), "Modo Foco: " + task.title, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupSwipeToDelete() {
|
||||
new androidx.recyclerview.widget.ItemTouchHelper(new androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback(0, androidx.recyclerview.widget.ItemTouchHelper.LEFT | androidx.recyclerview.widget.ItemTouchHelper.RIGHT) {
|
||||
@Override
|
||||
public boolean onMove(@NonNull androidx.recyclerview.widget.RecyclerView recyclerView, @NonNull androidx.recyclerview.widget.RecyclerView.ViewHolder viewHolder, @NonNull androidx.recyclerview.widget.RecyclerView.ViewHolder target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(@NonNull androidx.recyclerview.widget.RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
Task task = currentTasks.get(position);
|
||||
showDeleteConfirmDialog(task);
|
||||
tasksAdapter.notifyItemChanged(position); // Revert swipe if canceled? Firestore update will refresh anyway.
|
||||
}
|
||||
}).attachToRecyclerView(rvTasks);
|
||||
}
|
||||
|
||||
private void addXP(int amount) {
|
||||
if (!isAdded()) return;
|
||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
String uid = currentUser.getUid();
|
||||
Map<String, Object> updates = new HashMap<>();
|
||||
updates.put("xp", com.google.firebase.firestore.FieldValue.increment(amount));
|
||||
updates.put("xp_hoje", com.google.firebase.firestore.FieldValue.increment(amount));
|
||||
updates.put("xp_semanal", com.google.firebase.firestore.FieldValue.increment(amount));
|
||||
|
||||
// Check Level Up logic inside observe once
|
||||
com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
||||
.collection("users").document(currentUser.getUid()).get()
|
||||
.addOnSuccessListener(snapshot -> {
|
||||
Usuario user = snapshot.toObject(Usuario.class);
|
||||
if (user != null) {
|
||||
int currentLevel = user.level;
|
||||
int nextLevelThreshold = currentLevel * 500;
|
||||
if (user.xp + amount >= nextLevelThreshold) {
|
||||
updates.put("level", currentLevel + 1);
|
||||
showLevelUpAnimation(currentLevel + 1);
|
||||
}
|
||||
FirestoreManager.getInstance().updateUserStats(currentUser.getUid(), updates);
|
||||
FirestoreManager.getInstance().getUser(uid, user -> {
|
||||
if (user != null && isAdded()) {
|
||||
int currentLevel = user.level;
|
||||
int threshold = (currentLevel * (currentLevel + 1) / 2) * 100;
|
||||
if (user.xp + amount >= threshold) {
|
||||
updates.put("level", currentLevel + 1);
|
||||
showLevelUpAnimation(currentLevel + 1);
|
||||
}
|
||||
});
|
||||
FirestoreManager.getInstance().updateUserStats(uid, updates);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTodayCard() {
|
||||
int total = currentTasks.size();
|
||||
int completed = 0;
|
||||
for (Task task : currentTasks) {
|
||||
if (task.completed) completed++;
|
||||
}
|
||||
|
||||
if (tvTodayTasksCount != null) {
|
||||
tvTodayTasksCount.setText(completed + "/" + (total == 0 ? 3 : total));
|
||||
}
|
||||
|
||||
if (pbDailyTasksProgress != null) {
|
||||
int progress = (total == 0) ? 0 : (completed * 100) / total;
|
||||
pbDailyTasksProgress.setProgress(progress);
|
||||
}
|
||||
|
||||
if (total == 0) {
|
||||
tvNoTasksIncentive.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
tvNoTasksIncentive.setVisibility(View.GONE);
|
||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
FirestoreManager.getInstance().getUser(currentUser.getUid(), this::updateTodayCard);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDailyReset(Usuario user) {
|
||||
private void updateTodayCard(Usuario user) {
|
||||
int completed = user.tasks_concluidas_hoje;
|
||||
int goal = user.meta_diaria_tarefas;
|
||||
if (goal <= 0) goal = 3; // Default fallback
|
||||
|
||||
if (tvTodayTasksCount != null) {
|
||||
tvTodayTasksCount.setText(completed + "/" + goal);
|
||||
}
|
||||
|
||||
if (pbDailyTasksProgress != null) {
|
||||
int progress = (completed * 100) / goal;
|
||||
if (progress > 100) progress = 100;
|
||||
pbDailyTasksProgress.setProgress(progress);
|
||||
updatePathProgress(progress);
|
||||
}
|
||||
|
||||
if (currentTasks != null && tvNoTasksIncentive != null && layoutTasksSection != null) {
|
||||
if (currentTasks.isEmpty()) {
|
||||
tvNoTasksIncentive.setVisibility(View.VISIBLE);
|
||||
layoutTasksSection.setVisibility(View.GONE);
|
||||
} else {
|
||||
tvNoTasksIncentive.setVisibility(View.GONE);
|
||||
layoutTasksSection.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDailyResetAndStreak(Usuario user) {
|
||||
String today = new java.text.SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new java.util.Date());
|
||||
if (!today.equals(user.last_active_date)) {
|
||||
Map<String, Object> updates = new HashMap<>();
|
||||
if (user.tasks_concluidas_hoje == 0 && !user.last_active_date.isEmpty()) {
|
||||
updates.put("streak", 0);
|
||||
|
||||
// Streak logic
|
||||
if (!user.last_active_date.isEmpty()) {
|
||||
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
|
||||
try {
|
||||
java.util.Date lastDate = sdf.parse(user.last_active_date);
|
||||
java.util.Date todayDate = sdf.parse(today);
|
||||
long diff = (todayDate.getTime() - lastDate.getTime()) / (1000 * 60 * 60 * 24);
|
||||
|
||||
if (diff == 1) {
|
||||
// Successive day, but wait for first task completion to increment streak?
|
||||
// Or increment now if they at least logged in? User said "Completar pelo menos 1 tarefa por dia -> streak +1"
|
||||
// So we don't increment here, we just check if we should reset.
|
||||
} else if (diff > 1) {
|
||||
// Failed a day
|
||||
updates.put("streak", 0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
updates.put("xp_hoje", 0);
|
||||
updates.put("tasks_concluidas_hoje", 0);
|
||||
updates.put("tempo_foco_hoje", 0);
|
||||
updates.put("last_active_date", today);
|
||||
FirestoreManager.getInstance().updateUserStats(user.id_usuario, updates);
|
||||
|
||||
// Login reward
|
||||
addXP(10);
|
||||
if (getContext() != null) {
|
||||
Toast.makeText(getContext(), "Recompensa de login: +10 XP!", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,14 +358,23 @@ public class InicioFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void addMiniRankingItem(int rank, String name, int xp) {
|
||||
View item = getLayoutInflater().inflate(android.R.layout.simple_list_item_2, miniRankingContainer, false);
|
||||
TextView tv1 = item.findViewById(android.R.id.text1);
|
||||
TextView tv2 = item.findViewById(android.R.id.text2);
|
||||
tv1.setText(rank + ". " + name);
|
||||
tv1.setTextSize(14);
|
||||
tv1.setTextColor(getResources().getColor(R.color.text_primary));
|
||||
tv2.setText(xp + " XP hoje");
|
||||
tv2.setTextSize(12);
|
||||
if (getContext() == null || miniRankingContainer == null) return;
|
||||
|
||||
View item = getLayoutInflater().inflate(R.layout.item_ranking_user, miniRankingContainer, false);
|
||||
TextView tvRank = item.findViewById(R.id.tvRankPosition);
|
||||
TextView tvName = item.findViewById(R.id.tvRankingName);
|
||||
TextView tvXP = item.findViewById(R.id.tvRankingXP);
|
||||
|
||||
tvRank.setText("#" + rank);
|
||||
tvName.setText(name);
|
||||
tvXP.setText(xp + " XP");
|
||||
|
||||
// Remove elevation for mini ranking to keep it clean
|
||||
if (item instanceof androidx.cardview.widget.CardView) {
|
||||
((androidx.cardview.widget.CardView) item).setCardElevation(0);
|
||||
((androidx.cardview.widget.CardView) item).setCardBackgroundColor(android.graphics.Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
miniRankingContainer.addView(item);
|
||||
}
|
||||
|
||||
@@ -353,15 +414,19 @@ public class InicioFragment extends Fragment {
|
||||
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));
|
||||
if (getContext() != null) {
|
||||
nodeCircle.getBackground().setTint(ContextCompat.getColor(getContext(), R.color.success_green));
|
||||
nodeDayInitial.setTextColor(ContextCompat.getColor(getContext(), 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));
|
||||
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
|
||||
@@ -377,30 +442,31 @@ public class InicioFragment extends Fragment {
|
||||
private void completeTask(Task task) {
|
||||
task.completed = true;
|
||||
FirestoreManager.getInstance().updateTask(task);
|
||||
addXP(30);
|
||||
updateUserTaskCount();
|
||||
boolean allDone = true;
|
||||
for (Task t : currentTasks) {
|
||||
if (!t.completed && !t.id.equals(task.id)) {
|
||||
allDone = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allDone && currentTasks.size() > 0) {
|
||||
addXP(20);
|
||||
Toast.makeText(getContext(), "Todas as tarefas concluídas! +20 XP Bónus", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
triggerVibration();
|
||||
showXpPopup("+30 XP");
|
||||
}
|
||||
|
||||
private void updateUserTaskCount() {
|
||||
if (!isAdded()) return;
|
||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
Map<String, Object> updates = new HashMap<>();
|
||||
updates.put("tasks_concluidas_hoje", com.google.firebase.firestore.FieldValue.increment(1));
|
||||
updates.put("total_tasks_concluidas", com.google.firebase.firestore.FieldValue.increment(1));
|
||||
FirestoreManager.getInstance().updateUserStats(currentUser.getUid(), updates);
|
||||
String uid = currentUser.getUid();
|
||||
FirestoreManager.getInstance().getUser(uid, user -> {
|
||||
if (user != null && isAdded()) {
|
||||
Map<String, Object> updates = new HashMap<>();
|
||||
updates.put("tasks_concluidas_hoje", com.google.firebase.firestore.FieldValue.increment(1));
|
||||
updates.put("total_tasks_concluidas", com.google.firebase.firestore.FieldValue.increment(1));
|
||||
|
||||
if (user.tasks_concluidas_hoje == 0) {
|
||||
updates.put("streak", com.google.firebase.firestore.FieldValue.increment(1));
|
||||
if (user.streak + 1 > user.melhor_streak) {
|
||||
updates.put("melhor_streak", user.streak + 1);
|
||||
}
|
||||
}
|
||||
|
||||
FirestoreManager.getInstance().updateUserStats(uid, updates);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,10 +487,13 @@ public class InicioFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void showXpPopup(String text) {
|
||||
Toast.makeText(getContext(), text, Toast.LENGTH_SHORT).show();
|
||||
if (getContext() != null) {
|
||||
Toast.makeText(getContext(), text, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerVibration() {
|
||||
if (getContext() == null) return;
|
||||
android.os.Vibrator v = (android.os.Vibrator) getContext().getSystemService(android.content.Context.VIBRATOR_SERVICE);
|
||||
if (v != null) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
@@ -436,10 +505,22 @@ public class InicioFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void claimDailyReward() {
|
||||
addXP(100);
|
||||
btnClaimReward.setEnabled(false);
|
||||
btnClaimReward.setText("Resgatado");
|
||||
Toast.makeText(getContext(), "Recompensa diária resgatada! +100 XP", Toast.LENGTH_LONG).show();
|
||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
String today = new java.text.SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new java.util.Date());
|
||||
Map<String, Object> updates = new HashMap<>();
|
||||
updates.put("last_reward_claim_date", today);
|
||||
FirestoreManager.getInstance().updateUserStats(currentUser.getUid(), updates);
|
||||
addXP(100);
|
||||
|
||||
btnClaimReward.setEnabled(false);
|
||||
btnClaimReward.setText("Resgatado");
|
||||
btnClaimReward.setAlpha(0.5f);
|
||||
|
||||
if (getContext() != null) {
|
||||
Toast.makeText(getContext(), "Recompensa diária resgatada! +100 XP", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showDeleteConfirmDialog(Task task) {
|
||||
@@ -459,11 +540,11 @@ public class InicioFragment extends Fragment {
|
||||
ProgressBar nodeProgress = todayNode.findViewById(R.id.nodeProgress);
|
||||
View nodeCircle = todayNode.findViewById(R.id.nodeCircle);
|
||||
if (nodeProgress != null) nodeProgress.setProgress(progress);
|
||||
if (progress == 100) {
|
||||
nodeCircle.getBackground().setTint(getResources().getColor(R.color.success_green));
|
||||
if (progress == 100 && getContext() != null) {
|
||||
nodeCircle.getBackground().setTint(ContextCompat.getColor(getContext(), R.color.success_green));
|
||||
triggerSuccessAnimation(todayNode);
|
||||
} else {
|
||||
nodeCircle.getBackground().setTint(getResources().getColor(R.color.primary_purple));
|
||||
} else if (getContext() != null) {
|
||||
nodeCircle.getBackground().setTint(ContextCompat.getColor(getContext(), R.color.primary_purple));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +558,9 @@ public class InicioFragment extends Fragment {
|
||||
animatorSet.playTogether(scaleX, scaleY);
|
||||
animatorSet.start();
|
||||
|
||||
Toast.makeText(getContext(), "Dia Completado! 🎉", Toast.LENGTH_SHORT).show();
|
||||
if (getContext() != null) {
|
||||
Toast.makeText(getContext(), "Dia Completado! 🎉", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -491,19 +574,123 @@ public class InicioFragment extends Fragment {
|
||||
}
|
||||
@Override
|
||||
public void onFinish() {
|
||||
android.util.Log.d("FLUXUP_DEBUG", "FOCUS_FINISHED_START");
|
||||
isTimerRunning = false;
|
||||
btnStartFocus.setText("Começar Foco");
|
||||
completeTask(selectedTaskForFocus);
|
||||
addXP(50);
|
||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
Map<String, Object> updates = new HashMap<>();
|
||||
updates.put("tempo_foco_total", com.google.firebase.firestore.FieldValue.increment(selectedTaskForFocus.duration));
|
||||
updates.put("tempo_foco_hoje", com.google.firebase.firestore.FieldValue.increment(selectedTaskForFocus.duration));
|
||||
updates.put("sessoes_foco_completas", com.google.firebase.firestore.FieldValue.increment(1));
|
||||
FirestoreManager.getInstance().updateUserStats(currentUser.getUid(), updates);
|
||||
|
||||
if (getActivity() != null && isAdded()) {
|
||||
btnStartFocus.setText("Começar Foco");
|
||||
}
|
||||
|
||||
try {
|
||||
if (selectedTaskForFocus == null) {
|
||||
android.util.Log.e("FLUXUP_DEBUG", "FOCUS_FINISHED_ERROR: selectedTask is null");
|
||||
return;
|
||||
}
|
||||
|
||||
String taskId = selectedTaskForFocus.id;
|
||||
android.util.Log.d("FLUXUP_DEBUG", "TASK_ID: " + taskId);
|
||||
|
||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||
if (currentUser == null) {
|
||||
android.util.Log.e("FLUXUP_DEBUG", "FOCUS_FINISHED_ERROR: USER_ID is null");
|
||||
return;
|
||||
}
|
||||
String userId = currentUser.getUid();
|
||||
android.util.Log.d("FLUXUP_DEBUG", "USER_ID: " + userId);
|
||||
|
||||
if (taskId == null || taskId.isEmpty()) {
|
||||
android.util.Log.e("FLUXUP_DEBUG", "FOCUS_FINISHED_ERROR: task.id is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// Início da sincronização segura
|
||||
android.util.Log.d("FLUXUP_DEBUG", "FOCUS_COMPLETE_START");
|
||||
|
||||
FirestoreManager.getInstance().getUser(userId, user -> {
|
||||
if (user == null) {
|
||||
android.util.Log.e("FLUXUP_DEBUG", "FOCUS_COMPLETE_ERROR: User data is null");
|
||||
return;
|
||||
}
|
||||
if (!isAdded()) return;
|
||||
|
||||
android.util.Log.d("FLUXUP_DEBUG", "SELECTED_TASK: " + taskId);
|
||||
android.util.Log.d("FLUXUP_DEBUG", "USER_ID: " + userId);
|
||||
android.util.Log.d("FLUXUP_DEBUG", "XP_BEFORE: " + user.xp_hoje);
|
||||
|
||||
// 1. Log de XP
|
||||
Map<String, Object> log = new HashMap<>();
|
||||
log.put("userId", userId);
|
||||
log.put("amount", 50);
|
||||
log.put("type", "focus_task");
|
||||
log.put("created_at", com.google.firebase.firestore.FieldValue.serverTimestamp());
|
||||
|
||||
com.google.firebase.firestore.FirebaseFirestore.getInstance().collection("xp_logs")
|
||||
.add(log)
|
||||
.addOnSuccessListener(ref -> android.util.Log.d("FLUXUP_DEBUG", "XP_LOG_INSERT_RESULT: SUCCESS"))
|
||||
.addOnFailureListener(e -> android.util.Log.e("FLUXUP_DEBUG", "XP_LOG_INSERT_RESULT: FAIL - " + e.getMessage()));
|
||||
|
||||
// 2. Preparar updates consolidados
|
||||
Map<String, Object> updates = new HashMap<>();
|
||||
// Usando os nomes exatos de Usuario.java: xp, xp_hoje, xp_semanal
|
||||
updates.put("xp", com.google.firebase.firestore.FieldValue.increment(50));
|
||||
updates.put("xp_hoje", com.google.firebase.firestore.FieldValue.increment(50));
|
||||
updates.put("xp_semanal", com.google.firebase.firestore.FieldValue.increment(50));
|
||||
|
||||
updates.put("tempo_foco_total", com.google.firebase.firestore.FieldValue.increment(selectedTaskForFocus.duration));
|
||||
updates.put("tempo_foco_hoje", com.google.firebase.firestore.FieldValue.increment(selectedTaskForFocus.duration));
|
||||
updates.put("sessoes_foco_completas", 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));
|
||||
|
||||
if (user.tasks_concluidas_hoje == 0) {
|
||||
updates.put("streak", com.google.firebase.firestore.FieldValue.increment(1));
|
||||
if (user.streak + 1 > user.melhor_streak) {
|
||||
updates.put("melhor_streak", user.streak + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Level Up
|
||||
int currentLevel = user.level;
|
||||
int threshold = (currentLevel * (currentLevel + 1) / 2) * 100;
|
||||
if (user.xp + 50 >= threshold) {
|
||||
updates.put("level", currentLevel + 1);
|
||||
}
|
||||
|
||||
// 3. Gravar tudo no Backend (User Stats)
|
||||
com.google.firebase.firestore.FirebaseFirestore.getInstance().collection("users").document(userId)
|
||||
.update(updates)
|
||||
.addOnSuccessListener(aVoid -> {
|
||||
android.util.Log.d("FLUXUP_DEBUG", "USER_XP_UPDATE_RESULT: SUCCESS");
|
||||
if (isAdded()) {
|
||||
android.util.Log.d("FLUXUP_DEBUG", "HOME_REFRESH_RESULT: SUCCESS");
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(e -> android.util.Log.e("FLUXUP_DEBUG", "USER_XP_UPDATE_RESULT: FAIL - " + e.getMessage()));
|
||||
|
||||
// 4. Concluir tarefa no backend
|
||||
selectedTaskForFocus.completed = true;
|
||||
com.google.firebase.firestore.FirebaseFirestore.getInstance().collection("tasks").document(taskId)
|
||||
.set(selectedTaskForFocus)
|
||||
.addOnSuccessListener(aVoid -> android.util.Log.d("FLUXUP_DEBUG", "TASK_UPDATE_RESULT: SUCCESS"))
|
||||
.addOnFailureListener(e -> android.util.Log.e("FLUXUP_DEBUG", "TASK_UPDATE_RESULT: FAIL - " + e.getMessage()));
|
||||
|
||||
if (getContext() != null && isAdded()) {
|
||||
Toast.makeText(getContext(), "Boa! Tarefa concluída +50 XP", Toast.LENGTH_LONG).show();
|
||||
triggerVibration();
|
||||
|
||||
selectedTaskForFocus = null;
|
||||
timeLeftInMillis = 25 * 60 * 1000;
|
||||
updateCountDownText();
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("FLUXUP_DEBUG", "FOCUS_FINISHED_ERROR: " + e.getMessage());
|
||||
if (getContext() != null && isAdded()) {
|
||||
Toast.makeText(getContext(), "Erro ao concluir tarefa. Tenta novamente.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
selectedTaskForFocus = null;
|
||||
}
|
||||
}.start();
|
||||
isTimerRunning = true;
|
||||
@@ -543,6 +730,30 @@ public class InicioFragment extends Fragment {
|
||||
if (countDownTimer != null) countDownTimer.cancel();
|
||||
}
|
||||
|
||||
private void showEditTaskDialog(Task task) {
|
||||
View dialogView = getLayoutInflater().inflate(R.layout.dialog_add_task, null);
|
||||
android.widget.EditText etTitle = dialogView.findViewById(R.id.etTaskTitle);
|
||||
android.widget.EditText etDuration = dialogView.findViewById(R.id.etTaskDuration);
|
||||
|
||||
etTitle.setText(task.title);
|
||||
etDuration.setText(String.valueOf(task.duration));
|
||||
|
||||
new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
|
||||
.setTitle("Editar Tarefa")
|
||||
.setView(dialogView)
|
||||
.setNegativeButton("Cancelar", null)
|
||||
.setPositiveButton("Atualizar", (dialog, which) -> {
|
||||
String title = etTitle.getText().toString().trim();
|
||||
String durationStr = etDuration.getText().toString().trim();
|
||||
if (!title.isEmpty()) {
|
||||
task.title = title;
|
||||
if (!durationStr.isEmpty()) task.duration = Integer.parseInt(durationStr);
|
||||
FirestoreManager.getInstance().updateTask(task);
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showAddTaskDialog() {
|
||||
View dialogView = getLayoutInflater().inflate(R.layout.dialog_add_task, null);
|
||||
android.widget.EditText etTitle = dialogView.findViewById(R.id.etTaskTitle);
|
||||
@@ -564,7 +775,14 @@ public class InicioFragment extends Fragment {
|
||||
return;
|
||||
}
|
||||
int duration = 25;
|
||||
if (!durationStr.isEmpty()) duration = Integer.parseInt(durationStr);
|
||||
if (!durationStr.isEmpty()) {
|
||||
try {
|
||||
duration = Integer.parseInt(durationStr);
|
||||
} catch (NumberFormatException e) {
|
||||
etDuration.setError("Inválido");
|
||||
return;
|
||||
}
|
||||
}
|
||||
saveNewTask(title, duration);
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
@@ -11,6 +11,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.widget.TextView;
|
||||
import android.widget.GridLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
import com.google.firebase.firestore.ListenerRegistration;
|
||||
|
||||
@@ -49,7 +51,7 @@ public class ProfileFragment extends Fragment {
|
||||
});
|
||||
|
||||
view.findViewById(R.id.btnManageFriends).setOnClickListener(v -> {
|
||||
startActivity(new Intent(getActivity(), FindFriendsActivity.class));
|
||||
startActivity(new Intent(getActivity(), MyFriendsActivity.class));
|
||||
});
|
||||
|
||||
view.findViewById(R.id.btnViewStatsDetails).setOnClickListener(v -> {
|
||||
@@ -57,7 +59,7 @@ public class ProfileFragment extends Fragment {
|
||||
});
|
||||
|
||||
view.findViewById(R.id.btnLogout).setOnClickListener(v -> {
|
||||
AuthManager.getInstance().signOut();
|
||||
AuthManager.getInstance().logout();
|
||||
Intent intent = new Intent(getActivity(), LoginActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(intent);
|
||||
@@ -103,13 +105,17 @@ public class ProfileFragment extends Fragment {
|
||||
|
||||
private void renderBadges(Usuario user) {
|
||||
badgesGrid.removeAllViews();
|
||||
if (user.streak >= 7) addBadge("🔥", "Semana Ativa");
|
||||
if (user.total_tasks_concluidas >= 50) addBadge("🏆", "Executor");
|
||||
if (user.tempo_foco_total >= 600) addBadge("🧘", "Zen");
|
||||
if (user.level >= 5) addBadge("⭐", "Nível 5");
|
||||
// 7 days streak
|
||||
addBadge(user.streak >= 7 ? "🔥" : "🔒", "7 Dias", user.streak >= 7);
|
||||
// 50 tasks
|
||||
addBadge(user.total_tasks_concluidas >= 50 ? "🎯" : "🔒", "50 Tarefas", user.total_tasks_concluidas >= 50);
|
||||
// 10 hours focus (600 min)
|
||||
addBadge(user.tempo_foco_total >= 600 ? "⏱️" : "🔒", "10 Horas", user.tempo_foco_total >= 600);
|
||||
// 1000 XP
|
||||
addBadge(user.xp >= 1000 ? "⚡" : "🔒", "1000 XP", user.xp >= 1000);
|
||||
}
|
||||
|
||||
private void addBadge(String emoji, String name) {
|
||||
private void addBadge(String emoji, String name, boolean unlocked) {
|
||||
LinearLayout badge = new LinearLayout(getContext());
|
||||
badge.setOrientation(LinearLayout.VERTICAL);
|
||||
badge.setGravity(android.view.Gravity.CENTER);
|
||||
@@ -117,12 +123,14 @@ public class ProfileFragment extends Fragment {
|
||||
|
||||
TextView tvEmoji = new TextView(getContext());
|
||||
tvEmoji.setText(emoji);
|
||||
tvEmoji.setTextSize(24);
|
||||
tvEmoji.setTextSize(32);
|
||||
if (!unlocked) tvEmoji.setAlpha(0.3f);
|
||||
|
||||
TextView tvName = new TextView(getContext());
|
||||
tvName.setText(name);
|
||||
tvName.setTextSize(8);
|
||||
tvName.setTextSize(10);
|
||||
tvName.setGravity(android.view.Gravity.CENTER);
|
||||
tvName.setTextColor(getResources().getColor(unlocked ? R.color.text_primary : R.color.text_secondary));
|
||||
|
||||
badge.addView(tvEmoji);
|
||||
badge.addView(tvName);
|
||||
|
||||
@@ -12,14 +12,13 @@ import android.widget.EditText;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.widget.ListView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SearchFragment extends Fragment {
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private ListView recyclerView;
|
||||
private EditText searchBar;
|
||||
private ArrayAdapter<String> adapter;
|
||||
private ArrayList<String> allItems;
|
||||
@@ -35,7 +34,6 @@ public class SearchFragment extends Fragment {
|
||||
allItems = new ArrayList<>(Arrays.asList("Item 1", "Item 2", "Item 3", "Another Item", "More Items"));
|
||||
|
||||
adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, allItems);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
searchBar.addTextChangedListener(new TextWatcher() {
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
@@ -36,7 +37,8 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
private void initSettings() {
|
||||
// --- CONTA ---
|
||||
setupClickable(R.id.settingEmail, "Email", FirebaseAuth.getInstance().getCurrentUser().getEmail(), null);
|
||||
setupClickable(R.id.settingEditProfile, "Editar Perfil", "Nome, Bio, Avatar", v -> showEditProfileDialog());
|
||||
setupClickable(R.id.settingEmail, "Email", FirebaseAuth.getInstance().getCurrentUser().getEmail(), v -> showChangeEmailDialog());
|
||||
setupClickable(R.id.settingPassword, "Alterar Palavra-passe", "********", v -> resetPassword());
|
||||
setupSwitch(R.id.settingPublicProfile, "Perfil Público", "public_profile", true);
|
||||
|
||||
@@ -59,7 +61,12 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
setupClickable(R.id.settingExportData, "Exportar Dados", "JSON/CSV", null);
|
||||
|
||||
findViewById(R.id.btnDeleteAccount).setOnClickListener(v -> {
|
||||
Toast.makeText(this, "Funcionalidade disponível em breve", Toast.LENGTH_SHORT).show();
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Eliminar Conta")
|
||||
.setMessage("Tens a certeza? Todos os teus dados serão apagados permanentemente.")
|
||||
.setNegativeButton("Cancelar", null)
|
||||
.setPositiveButton("Sim, Eliminar", (dialog, which) -> deleteAccount())
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -96,6 +103,60 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void showEditProfileDialog() {
|
||||
View view = getLayoutInflater().inflate(R.layout.dialog_edit_profile, null);
|
||||
EditText etName = view.findViewById(R.id.etEditName);
|
||||
EditText etBio = view.findViewById(R.id.etEditBio);
|
||||
|
||||
String uid = FirebaseAuth.getInstance().getUid();
|
||||
FirestoreManager.getInstance().getUser(uid, user -> {
|
||||
etName.setText(user.usuario);
|
||||
etBio.setText(user.bio);
|
||||
});
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Editar Perfil")
|
||||
.setView(view)
|
||||
.setPositiveButton("Guardar", (dialog, which) -> {
|
||||
String name = etName.getText().toString();
|
||||
String bio = etBio.getText().toString();
|
||||
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||
updates.put("usuario", name);
|
||||
updates.put("bio", bio);
|
||||
FirestoreManager.getInstance().updateUserStats(uid, updates);
|
||||
Toast.makeText(this, "Perfil atualizado!", Toast.LENGTH_SHORT).show();
|
||||
})
|
||||
.setNegativeButton("Cancelar", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showChangeEmailDialog() {
|
||||
EditText input = new EditText(this);
|
||||
input.setHint("Novo email");
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Alterar Email")
|
||||
.setView(input)
|
||||
.setPositiveButton("Confirmar", (dialog, which) -> {
|
||||
String newEmail = input.getText().toString();
|
||||
FirebaseAuth.getInstance().getCurrentUser().updateEmail(newEmail)
|
||||
.addOnSuccessListener(aVoid -> Toast.makeText(this, "Email alterado!", Toast.LENGTH_SHORT).show())
|
||||
.addOnFailureListener(e -> Toast.makeText(this, "Erro: " + e.getMessage(), Toast.LENGTH_LONG).show());
|
||||
})
|
||||
.setNegativeButton("Cancelar", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void deleteAccount() {
|
||||
String uid = FirebaseAuth.getInstance().getUid();
|
||||
com.google.firebase.firestore.FirebaseFirestore.getInstance().collection("users").document(uid).delete();
|
||||
FirebaseAuth.getInstance().getCurrentUser().delete()
|
||||
.addOnSuccessListener(aVoid -> {
|
||||
Intent intent = new Intent(this, LoginActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
||||
private void showDurationDialog(String key, String title, int current) {
|
||||
EditText input = new EditText(this);
|
||||
input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
|
||||
@@ -105,9 +166,20 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
.setTitle(title)
|
||||
.setView(input)
|
||||
.setPositiveButton("Guardar", (dialog, which) -> {
|
||||
int val = Integer.parseInt(input.getText().toString());
|
||||
sharedPreferences.edit().putInt(key, val).apply();
|
||||
recreate();
|
||||
try {
|
||||
int val = Integer.parseInt(input.getText().toString());
|
||||
sharedPreferences.edit().putInt(key, val).apply();
|
||||
|
||||
// Also update in Firestore if user is logged in
|
||||
String uid = FirebaseAuth.getInstance().getUid();
|
||||
if (uid != null) {
|
||||
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||
if (key.equals("focus_duration")) updates.put("meta_diaria_foco", val);
|
||||
FirestoreManager.getInstance().updateUserStats(uid, updates);
|
||||
}
|
||||
|
||||
recreate();
|
||||
} catch (Exception e) {}
|
||||
})
|
||||
.setNegativeButton("Cancelar", null)
|
||||
.show();
|
||||
|
||||
@@ -9,6 +9,9 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import java.util.Calendar;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.ListenerRegistration;
|
||||
|
||||
@@ -191,12 +194,3 @@ public class TrophiesActivity extends AppCompatActivity {
|
||||
if (userListener != null) userListener.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (userListener != null) {
|
||||
userListener.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public class Usuario {
|
||||
public int sessoes_foco_completas = 0;
|
||||
public int dias_ativos = 1;
|
||||
public String last_active_date = ""; // YYYY-MM-DD
|
||||
public String last_reward_claim_date = ""; // YYYY-MM-DD
|
||||
public String avatar_url = "";
|
||||
|
||||
public Usuario() {}
|
||||
|
||||
@@ -98,7 +98,7 @@ public class UsuariosService {
|
||||
};
|
||||
for (String title : defaultTasks) {
|
||||
String taskId = getFirestore().collection("tasks").document().getId();
|
||||
Task task = new Task(taskId, title, 50, uid);
|
||||
Task task = new Task(taskId, title, 50, 25, uid);
|
||||
getFirestore().collection("tasks").document(taskId).set(task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
|
||||
<androidx.cardview.widget.CardView style="@style/SettingsCard">
|
||||
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">
|
||||
<include layout="@layout/item_settings_clickable" android:id="@+id/settingEditProfile" />
|
||||
<include layout="@layout/item_settings_clickable" android:id="@+id/settingEmail" />
|
||||
<include layout="@layout/item_settings_clickable" android:id="@+id/settingPassword" />
|
||||
<include layout="@layout/item_settings_switch" android:id="@+id/settingPublicProfile" />
|
||||
|
||||
@@ -175,23 +175,43 @@
|
||||
android:progressDrawable="@drawable/progress_bar_duo" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- 3. 🎯 TAREFAS DO DIA -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:text="Tarefas do Dia"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnAddTasks"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/button_primary"
|
||||
android:text="+ Adicionar Tarefa"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:backgroundTint="@null" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tasksContainer"
|
||||
android:id="@+id/layoutTasksSection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:orientation="vertical" />
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:text="Tarefas do Dia"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvTasks"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:nestedScrollingEnabled="false" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvNoTasksIncentive"
|
||||
@@ -398,19 +418,6 @@
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- 8. ➕ BOTÃO ADICIONAR TAREFA -->
|
||||
<Button
|
||||
android:id="@+id/btnAddTasks"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:background="@drawable/button_primary"
|
||||
android:text="+ Adicionar Tarefa"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:backgroundTint="@null" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Search..." />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
<ListView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<item name="colorOnSecondary">@color/background_light</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">@color/background_light</item>
|
||||
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
|
||||
<item name="android:windowLightStatusBar" tools:targetApi="M">false</item>
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:windowBackground">@color/background_light</item>
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="nodeCircle" type="id">circle</item>
|
||||
<item name="nodeDayInitial" type="id">dayInitial</item>
|
||||
<item name="nodeDayLabel" type="id">nodeDayLabel</item>
|
||||
<item name="nodeProgress" type="id">nodeProgress</item>
|
||||
<item name="nodeCircle" type="id" />
|
||||
<item name="nodeDayInitial" type="id" />
|
||||
<item name="nodeDayLabel" type="id" />
|
||||
<item name="nodeProgress" type="id" />
|
||||
</resources>
|
||||
@@ -11,7 +11,7 @@
|
||||
<item name="colorOnSecondary">#11181C</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">#F9FAFB</item>
|
||||
<item name="android:windowLightStatusBar" tools:targetApi="m">true</item>
|
||||
<item name="android:windowLightStatusBar" tools:targetApi="M">true</item>
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:windowBackground">#F9FAFB</item>
|
||||
</style>
|
||||
|
||||
5145
build_output.txt
5145
build_output.txt
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,3 @@
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.defaults.buildfeatures.resvalues=true
|
||||
android.sdk.defaultTargetSdkToCompileSdkIfUnset=false
|
||||
android.enableAppCompileTimeRClass=false
|
||||
android.usesSdkInManifest.disallowed=false
|
||||
android.uniquePackageNames=false
|
||||
android.dependency.useConstraints=true
|
||||
android.r8.strictFullModeForKeepRules=false
|
||||
android.r8.optimizedResourceShrinking=false
|
||||
android.builtInKotlin=false
|
||||
android.newDsl=false
|
||||
|
||||
Reference in New Issue
Block a user