notificacoes

This commit is contained in:
MeuNome
2026-06-08 11:47:28 +01:00
parent 03b36ec9fe
commit b2fae2a058
13 changed files with 187 additions and 16 deletions

View File

@@ -1754,6 +1754,19 @@
<option name="screenX" value="1600" /> <option name="screenX" value="1600" />
<option name="screenY" value="2560" /> <option name="screenY" value="2560" />
</PersistentDeviceSelectionData> </PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="36" />
<option name="brand" value="google" />
<option name="codename" value="tangorpro" />
<option name="formFactor" value="Tablet" />
<option name="id" value="tangorpro" />
<option name="labId" value="google" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Tablet" />
<option name="screenDensity" value="320" />
<option name="screenX" value="1600" />
<option name="screenY" value="2560" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData> <PersistentDeviceSelectionData>
<option name="api" value="35" /> <option name="api" value="35" />
<option name="brand" value="google" /> <option name="brand" value="google" />

View File

@@ -319,16 +319,16 @@ public class AvatarView extends View {
paint.setColor(Color.parseColor("#F59E0B")); // Ouro paint.setColor(Color.parseColor("#F59E0B")); // Ouro
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint); canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint);
paint.setStyle(Paint.Style.FILL); paint.setStyle(Paint.Style.FILL);
} else if ("Platina".equalsIgnoreCase(league) || "Platinum".equalsIgnoreCase(league)) { } else if ("Esmeralda".equalsIgnoreCase(league) || "Platina".equalsIgnoreCase(league) || "Platinum".equalsIgnoreCase(league)) {
paint.setStyle(Paint.Style.STROKE); paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Math.min(w, h) * 0.06f); paint.setStrokeWidth(Math.min(w, h) * 0.06f);
paint.setColor(Color.parseColor("#06B6D4")); // Platina Ciano paint.setColor(Color.parseColor("#4CAF50")); // Esmeralda Verde
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint); canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint);
paint.setStyle(Paint.Style.FILL); paint.setStyle(Paint.Style.FILL);
} else if ("Diamante".equalsIgnoreCase(league) || "Diamond".equalsIgnoreCase(league)) { } else if ("Diamante".equalsIgnoreCase(league) || "Diamond".equalsIgnoreCase(league)) {
paint.setStyle(Paint.Style.STROKE); paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Math.min(w, h) * 0.06f); paint.setStrokeWidth(Math.min(w, h) * 0.06f);
paint.setColor(Color.parseColor("#6366F1")); // Diamante Indigo/Violeta paint.setColor(Color.parseColor("#1E88E5")); // Diamante Azul
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint); canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint);
paint.setStyle(Paint.Style.FILL); paint.setStyle(Paint.Style.FILL);
} }

View File

@@ -59,6 +59,7 @@ public class InicioFragment extends Fragment {
private TextView tvFocusPauseCount, tvFocusPenaltyWarning, tvFocusXpReward; private TextView tvFocusPauseCount, tvFocusPenaltyWarning, tvFocusXpReward;
private int pauseCount = 0; private int pauseCount = 0;
private boolean isCompletingFocus = false; private boolean isCompletingFocus = false;
private Usuario localUser = null;
private boolean isTimerRunning = false; private boolean isTimerRunning = false;
private long timeLeftInMillis = 25 * 60 * 1000; // 25 minutos private long timeLeftInMillis = 25 * 60 * 1000; // 25 minutos
@@ -196,6 +197,7 @@ public class InicioFragment extends Fragment {
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser(); FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
if (currentUser != null) { if (currentUser != null) {
userListener = FirestoreManager.getInstance().observeUser(currentUser.getUid(), user -> { userListener = FirestoreManager.getInstance().observeUser(currentUser.getUid(), user -> {
localUser = user;
if (tvGreeting != null) { if (tvGreeting != null) {
tvGreeting.setText("Olá, " + user.usuario + "!"); tvGreeting.setText("Olá, " + user.usuario + "!");
} }
@@ -1057,7 +1059,8 @@ public class InicioFragment extends Fragment {
final Task task = selectedTaskForFocus; final Task task = selectedTaskForFocus;
final String taskId = task.id; final String taskId = task.id;
final int finalXp = calculateFocusXp(); final boolean taskHasBoost = task.hasBoost;
final int finalXp = taskHasBoost ? calculateFocusXp() * 2 : calculateFocusXp();
android.util.Log.d("FLUXUP_DEBUG", "TASK_UPDATE_START"); android.util.Log.d("FLUXUP_DEBUG", "TASK_UPDATE_START");
task.completed = true; task.completed = true;
@@ -1101,7 +1104,11 @@ public class InicioFragment extends Fragment {
if (isAdded()) { if (isAdded()) {
refreshHomeStats(); refreshHomeStats();
Toast.makeText(getContext(), "Boa! Tarefa concluída +" + finalXp + " XP", Toast.LENGTH_LONG).show(); if (taskHasBoost) {
Toast.makeText(getContext(), "🚀 Boost aplicado! XP dobrado. +" + finalXp + " XP", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getContext(), "Boa! Tarefa concluída +" + finalXp + " XP", Toast.LENGTH_LONG).show();
}
triggerVibration(); triggerVibration();
selectedTaskForFocus = null; selectedTaskForFocus = null;
@@ -1174,6 +1181,39 @@ public class InicioFragment extends Fragment {
android.widget.EditText etTitle = dialogView.findViewById(R.id.etTaskTitle); android.widget.EditText etTitle = dialogView.findViewById(R.id.etTaskTitle);
android.widget.EditText etDuration = dialogView.findViewById(R.id.etTaskDuration); android.widget.EditText etDuration = dialogView.findViewById(R.id.etTaskDuration);
// Boost UI
View cardUseBoost = dialogView.findViewById(R.id.cardUseBoost);
android.widget.ImageView ivBoostIcon = dialogView.findViewById(R.id.ivBoostRocketIcon);
android.widget.TextView tvBoostLabel = dialogView.findViewById(R.id.tvBoostLabel);
android.widget.TextView tvBoostCount = dialogView.findViewById(R.id.tvBoostCount);
final boolean[] boostActive = {false};
int boostQty = (localUser != null && localUser.inventory != null && localUser.inventory.containsKey("boost_xp"))
? localUser.inventory.get("boost_xp") : 0;
if (boostQty > 0 && cardUseBoost != null) {
tvBoostCount.setText("x" + boostQty);
cardUseBoost.setVisibility(View.VISIBLE);
cardUseBoost.setOnClickListener(v -> {
boostActive[0] = !boostActive[0];
if (boostActive[0]) {
// Active state: purple highlight
cardUseBoost.setBackground(ContextCompat.getDrawable(getContext(), R.drawable.boost_selected_bg));
tvBoostLabel.setTextColor(ContextCompat.getColor(getContext(), R.color.primary_purple));
tvBoostCount.setTextColor(ContextCompat.getColor(getContext(), R.color.primary_purple));
ivBoostIcon.setImageTintList(android.content.res.ColorStateList.valueOf(
ContextCompat.getColor(getContext(), R.color.primary_purple)));
} else {
// Inactive state: default gray
cardUseBoost.setBackground(ContextCompat.getDrawable(getContext(), R.drawable.boost_default_bg));
tvBoostLabel.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
tvBoostCount.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
ivBoostIcon.setImageTintList(android.content.res.ColorStateList.valueOf(
ContextCompat.getColor(getContext(), R.color.text_secondary)));
}
});
}
androidx.appcompat.app.AlertDialog dialog = new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext()) androidx.appcompat.app.AlertDialog dialog = new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
.setTitle("Nova Tarefa") .setTitle("Nova Tarefa")
.setView(dialogView) .setView(dialogView)
@@ -1198,15 +1238,15 @@ public class InicioFragment extends Fragment {
return; return;
} }
} }
saveNewTask(title, duration); saveNewTask(title, duration, boostActive[0]);
dialog.dismiss(); dialog.dismiss();
}); });
}); });
dialog.show(); dialog.show();
} }
private void saveNewTask(String title, int duration) { private void saveNewTask(String title, int duration, boolean useBoost) {
android.util.Log.d("FLUXUP_DEBUG", "Criando tarefa: " + title); android.util.Log.d("FLUXUP_DEBUG", "Criando tarefa: " + title + " | boost: " + useBoost);
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser(); FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
if (currentUser == null) { if (currentUser == null) {
android.util.Log.d("FLUXUP_DEBUG", "Erro ao salvar: Usuário nulo"); android.util.Log.d("FLUXUP_DEBUG", "Erro ao salvar: Usuário nulo");
@@ -1215,10 +1255,19 @@ public class InicioFragment extends Fragment {
String uid = currentUser.getUid(); String uid = currentUser.getUid();
String taskId = com.google.firebase.firestore.FirebaseFirestore.getInstance().collection("tasks").document().getId(); String taskId = com.google.firebase.firestore.FirebaseFirestore.getInstance().collection("tasks").document().getId();
Task task = new Task(taskId, title, 30, duration, uid); Task task = new Task(taskId, title, 30, duration, uid);
task.hasBoost = useBoost;
FirestoreManager.getInstance().addTask(task); FirestoreManager.getInstance().addTask(task);
android.util.Log.d("FLUXUP_DEBUG", "Tarefa salva: " + taskId); android.util.Log.d("FLUXUP_DEBUG", "Tarefa salva: " + taskId);
// Decrement boost inventory if boost was applied
if (useBoost && uid != null) {
Map<String, Object> inventoryUpdate = new HashMap<>();
inventoryUpdate.put("inventory.boost_xp", com.google.firebase.firestore.FieldValue.increment(-1));
FirestoreManager.getInstance().updateUserStats(uid, inventoryUpdate);
android.util.Log.d("FLUXUP_DEBUG", "BOOST_CONSUMED: 1 boost_xp removed from inventory");
}
// Refresh local stats // Refresh local stats
refreshHomeStats(); refreshHomeStats();
} }

View File

@@ -26,10 +26,8 @@ public class LeagueHelper {
new LeagueInfo("Bronze", 0, 499, R.drawable.ic_trophy_bronze, "#8D6E63", "Estás a começar a tua jornada!"), new LeagueInfo("Bronze", 0, 499, R.drawable.ic_trophy_bronze, "#8D6E63", "Estás a começar a tua jornada!"),
new LeagueInfo("Prata", 500, 1499, R.drawable.ic_trophy_silver, "#B0BEC5", "A consistência está a dar frutos."), new LeagueInfo("Prata", 500, 1499, R.drawable.ic_trophy_silver, "#B0BEC5", "A consistência está a dar frutos."),
new LeagueInfo("Ouro", 1500, 2999, R.drawable.ic_trophy_gold, "#FFD54F", "Um verdadeiro guerreiro do foco!"), new LeagueInfo("Ouro", 1500, 2999, R.drawable.ic_trophy_gold, "#FFD54F", "Um verdadeiro guerreiro do foco!"),
new LeagueInfo("Platina", 3000, 4999, R.drawable.ic_trophy_platinum, "#4FC3F7", "Elite da produtividade."), new LeagueInfo("Esmeralda", 3000, 4999, R.drawable.ic_trophy_emerald, "#4CAF50", "Elite da produtividade."),
new LeagueInfo("Diamante", 5000, 7499, R.drawable.ic_trophy_diamond, "#1E88E5", "Inquebrável e imparável."), new LeagueInfo("Diamante", 5000, Integer.MAX_VALUE, R.drawable.ic_trophy_diamond, "#1E88E5", "Inquebrável e imparável.")
new LeagueInfo("Mestre", 7500, 9999, R.drawable.ic_trophy_master, "#7C3AED", "Mestre absoluto da Fluxup."),
new LeagueInfo("Lenda", 10000, Integer.MAX_VALUE, R.drawable.ic_trophy_legend, "#D81B60", "Uma lenda viva da disciplina!")
}; };
public static LeagueInfo getCurrentLeague(int totalXp) { public static LeagueInfo getCurrentLeague(int totalXp) {

View File

@@ -664,7 +664,7 @@ public class RewardsFragment extends Fragment {
llStoreContainer.removeAllViews(); llStoreContainer.removeAllViews();
addStoreItem("mystery_box", "Caixa Misteriosa", "Ganha XP e moedas surpresa!", 60, "📦", user); addStoreItem("mystery_box", "Caixa Misteriosa", "Ganha XP e moedas surpresa!", 60, "📦", user);
addStoreItem("boost_xp", "Boost de XP", "Dobra o XP ganho (em breve)", 50, "🚀", user); addStoreItem("boost_xp", "Boost de XP", "Dobra o XP ganho na próxima tarefa", 50, "🚀", user);
addStoreItem("streak_protect", "Proteção de Streak", "Salva a tua ofensiva se falhares um dia", 80, "🛡️", user); addStoreItem("streak_protect", "Proteção de Streak", "Salva a tua ofensiva se falhares um dia", 80, "🛡️", user);
addStoreItem("frame_avatar", "Moldura Épica", "Destaca o teu avatar no ranking", 100, "🖼️", user); addStoreItem("frame_avatar", "Moldura Épica", "Destaca o teu avatar no ranking", 100, "🖼️", user);
} }

View File

@@ -8,6 +8,7 @@ public class Task {
public int duration; // em minutos public int duration; // em minutos
public String userId; public String userId;
public Long completedDate; public Long completedDate;
public boolean hasBoost = false;
public Task() {} public Task() {}

View File

@@ -134,7 +134,7 @@ public class TrophiesActivity extends AppCompatActivity {
tvHeaderXP.setText(user.xp + " / " + next.minXp + " XP"); tvHeaderXP.setText(user.xp + " / " + next.minXp + " XP");
} else { } else {
pbHeaderProgress.setProgress(100); pbHeaderProgress.setProgress(100);
tvHeaderXP.setText(user.xp + " XP (Lenda)"); tvHeaderXP.setText(user.xp + " XP (" + current.name + ")");
} }
// Bottom Card // Bottom Card
@@ -319,6 +319,8 @@ public class TrophiesActivity extends AppCompatActivity {
TextView tvTu = view.findViewById(R.id.tvRankingLabelTu); TextView tvTu = view.findViewById(R.id.tvRankingLabelTu);
androidx.cardview.widget.CardView card = view.findViewById(R.id.cardRankingUser); androidx.cardview.widget.CardView card = view.findViewById(R.id.cardRankingUser);
AvatarView ivAvatar = view.findViewById(R.id.ivRankingAvatar); AvatarView ivAvatar = view.findViewById(R.id.ivRankingAvatar);
TextView tvStreak = view.findViewById(R.id.tvRankingStreak);
ImageView ivTrophy = view.findViewById(R.id.ivRankingTrophy);
tvPos.setText("#" + pos); tvPos.setText("#" + pos);
tvName.setText(user.usuario); tvName.setText(user.usuario);
@@ -328,6 +330,16 @@ public class TrophiesActivity extends AppCompatActivity {
ivAvatar.setLeague(user.league); ivAvatar.setLeague(user.league);
} }
if (tvStreak != null) {
tvStreak.setText("🔥 " + user.streak);
}
if (ivTrophy != null) {
LeagueHelper.LeagueInfo userLeague = LeagueHelper.getCurrentLeague(user.xp);
ivTrophy.setImageResource(userLeague.iconRes);
ivTrophy.setVisibility(View.VISIBLE);
}
if (user.id_usuario.equals(myUid)) { if (user.id_usuario.equals(myUid)) {
tvTu.setVisibility(View.VISIBLE); tvTu.setVisibility(View.VISIBLE);
card.setCardBackgroundColor(Color.parseColor("#F3E5F5")); card.setCardBackgroundColor(Color.parseColor("#F3E5F5"));

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFF" />
<stroke
android:width="1dp"
android:color="#E5E7EB" />
<corners android:radius="12dp" />
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#F3E5F5" />
<stroke
android:width="2dp"
android:color="#7C3AED" />
<corners android:radius="12dp" />
</shape>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#7C3AED"
android:pathData="M12,2.5C12,2.5 6.5,5 6.5,12L6.5,14.5L4,17L4,20L7,20L7,17.5L9.5,17.5C9.5,17.5 10.5,19.5 12,19.5C13.5,19.5 14.5,17.5 14.5,17.5L17,17.5L17,20L20,20L20,17L17.5,14.5L17.5,12C17.5,5 12,2.5 12,2.5ZM12,5.5C12,5.5 15.5,7.5 15.5,12L8.5,12C8.5,7.5 12,5.5 12,5.5ZM10.5,13.5L13.5,13.5C13.5,14.33 12.83,15 12,15C11.17,15 10.5,14.33 10.5,13.5Z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="120dp"
android:height="120dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#4CAF50"
android:pathData="M18,2H6A2,2 0,0 0,4 4V7C4,8.11 4.9,9 6,9H7V11C7,12.71 8.06,14.2 9.5,14.8V17H8V19H16V17H14.5V14.8C15.94,14.2 17,12.71 17,11V9H18A2,2 0,0 0,20 7V4A2,2 0,0 0,18 2M6,7V4H7V7H6M18,7H17V4H18V7Z" />
</vector>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
@@ -33,4 +34,56 @@
android:text="25" /> android:text="25" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<!-- Boost row: aligned to the right, only shown when boosts > 0 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="end"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/cardUseBoost"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/boost_default_bg"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingTop="8dp"
android:paddingEnd="12dp"
android:paddingBottom="8dp"
android:visibility="gone">
<ImageView
android:id="@+id/ivBoostRocketIcon"
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@drawable/ic_rocket"
app:tint="@color/text_secondary" />
<TextView
android:id="@+id/tvBoostLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="Usar Boost"
android:textColor="@color/text_secondary"
android:textSize="13sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvBoostCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:text="x0"
android:textColor="@color/text_secondary"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -88,6 +88,14 @@
android:textSize="14sp" android:textSize="14sp"
android:textStyle="bold" /> android:textStyle="bold" />
<ImageView
android:id="@+id/ivRankingTrophy"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="6dp"
android:src="@drawable/ic_trophy_bronze"
android:visibility="gone" />
<ImageView <ImageView
android:id="@+id/ivRankingTrend" android:id="@+id/ivRankingTrend"
android:layout_width="16dp" android:layout_width="16dp"