adicionar loja
This commit is contained in:
966
app/src/main/java/com/fluxup/app/RewardsFragment.java
Normal file
966
app/src/main/java/com/fluxup/app/RewardsFragment.java
Normal file
@@ -0,0 +1,966 @@
|
|||||||
|
package com.fluxup.app;
|
||||||
|
|
||||||
|
import android.animation.AnimatorSet;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.BounceInterpolator;
|
||||||
|
import android.view.animation.CycleInterpolator;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
|
import com.google.firebase.firestore.FieldValue;
|
||||||
|
import com.google.firebase.firestore.ListenerRegistration;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class RewardsFragment extends Fragment {
|
||||||
|
|
||||||
|
// Views
|
||||||
|
private TextView tvStatsXP, tvStatsStreak, tvStatsLevel, tvStatsCoins;
|
||||||
|
private TextView tvDailyPresentGoal, tvDailyPresentProgressText;
|
||||||
|
private ProgressBar pbDailyPresent;
|
||||||
|
private Button btnClaimDailyPresent;
|
||||||
|
|
||||||
|
private LinearLayout llSequenceContainer;
|
||||||
|
private Button btnClaimSequence;
|
||||||
|
|
||||||
|
private android.widget.LinearLayout llMissionsContainer;
|
||||||
|
private android.widget.LinearLayout llStreakRewardsContainer;
|
||||||
|
private android.widget.LinearLayout llStoreContainer;
|
||||||
|
|
||||||
|
private int previousCoins = -1;
|
||||||
|
|
||||||
|
private TextView tvMysteryBoxIcon, tvMysteryBoxStatus;
|
||||||
|
private Button btnOpenMysteryBox;
|
||||||
|
|
||||||
|
// Listeners and Data
|
||||||
|
private ListenerRegistration userListener;
|
||||||
|
private Usuario currentUserData;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_rewards, container, false);
|
||||||
|
|
||||||
|
// Bind Views
|
||||||
|
tvStatsXP = view.findViewById(R.id.tvStatsXP);
|
||||||
|
tvStatsStreak = view.findViewById(R.id.tvStatsStreak);
|
||||||
|
tvStatsLevel = view.findViewById(R.id.tvStatsLevel);
|
||||||
|
tvStatsCoins = view.findViewById(R.id.tvStatsCoins);
|
||||||
|
|
||||||
|
tvDailyPresentGoal = view.findViewById(R.id.tvDailyPresentGoal);
|
||||||
|
tvDailyPresentProgressText = view.findViewById(R.id.tvDailyPresentProgressText);
|
||||||
|
pbDailyPresent = view.findViewById(R.id.pbDailyPresent);
|
||||||
|
btnClaimDailyPresent = view.findViewById(R.id.btnClaimDailyPresent);
|
||||||
|
|
||||||
|
llSequenceContainer = view.findViewById(R.id.llSequenceContainer);
|
||||||
|
btnClaimSequence = view.findViewById(R.id.btnClaimSequence);
|
||||||
|
|
||||||
|
llMissionsContainer = view.findViewById(R.id.llMissionsContainer);
|
||||||
|
llStreakRewardsContainer = view.findViewById(R.id.llStreakRewardsContainer);
|
||||||
|
llStoreContainer = view.findViewById(R.id.llStoreContainer);
|
||||||
|
|
||||||
|
tvMysteryBoxIcon = view.findViewById(R.id.tvMysteryBoxIcon);
|
||||||
|
tvMysteryBoxStatus = view.findViewById(R.id.tvMysteryBoxStatus);
|
||||||
|
btnOpenMysteryBox = view.findViewById(R.id.btnOpenMysteryBox);
|
||||||
|
|
||||||
|
// Initialize observers
|
||||||
|
startObservingUser();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startObservingUser() {
|
||||||
|
FirebaseUser user = AuthManager.getInstance().getCurrentUser();
|
||||||
|
if (user != null) {
|
||||||
|
userListener = FirestoreManager.getInstance().observeUser(user.getUid(), usuario -> {
|
||||||
|
if (usuario != null && isAdded()) {
|
||||||
|
currentUserData = usuario;
|
||||||
|
updateUI(usuario);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUI(Usuario user) {
|
||||||
|
String today = getTodayDateString();
|
||||||
|
|
||||||
|
// 1. Header Stats
|
||||||
|
tvStatsXP.setText(user.xp + " XP");
|
||||||
|
tvStatsStreak.setText(user.streak + " dias");
|
||||||
|
tvStatsLevel.setText("Nível " + user.level);
|
||||||
|
tvStatsCoins.setText(user.coins + " moedas");
|
||||||
|
|
||||||
|
if (previousCoins != -1 && user.coins > previousCoins) {
|
||||||
|
int diff = user.coins - previousCoins;
|
||||||
|
showFloatingText(tvStatsCoins, "+" + diff + " moedas");
|
||||||
|
pulseAnimation(tvStatsCoins);
|
||||||
|
}
|
||||||
|
previousCoins = user.coins;
|
||||||
|
|
||||||
|
// 2. Daily Present Card
|
||||||
|
int completedTasks = user.tasks_concluidas_hoje;
|
||||||
|
int dailyGoal = user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 4;
|
||||||
|
tvDailyPresentGoal.setText("Completa " + dailyGoal + " tarefas hoje para ganhar +100 XP");
|
||||||
|
tvDailyPresentProgressText.setText(completedTasks + "/" + dailyGoal);
|
||||||
|
|
||||||
|
float progress = (float) completedTasks / dailyGoal;
|
||||||
|
pbDailyPresent.setProgress((int) (progress * 100));
|
||||||
|
|
||||||
|
boolean isDailyPresentClaimed = today.equals(user.last_reward_claim_date);
|
||||||
|
if (isDailyPresentClaimed) {
|
||||||
|
btnClaimDailyPresent.setEnabled(false);
|
||||||
|
btnClaimDailyPresent.setText("Resgatado");
|
||||||
|
btnClaimDailyPresent.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
btnClaimDailyPresent.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
|
||||||
|
} else if (completedTasks >= dailyGoal) {
|
||||||
|
btnClaimDailyPresent.setEnabled(true);
|
||||||
|
btnClaimDailyPresent.setText("Resgatar");
|
||||||
|
btnClaimDailyPresent.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.white));
|
||||||
|
btnClaimDailyPresent.setTextColor(ContextCompat.getColor(getContext(), R.color.primary_purple));
|
||||||
|
btnClaimDailyPresent.setOnClickListener(v -> claimDailyPresent());
|
||||||
|
} else {
|
||||||
|
btnClaimDailyPresent.setEnabled(false);
|
||||||
|
btnClaimDailyPresent.setText("Bloqueado");
|
||||||
|
btnClaimDailyPresent.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
btnClaimDailyPresent.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 7-Day Sequence
|
||||||
|
setupSequenceCalendar(user);
|
||||||
|
|
||||||
|
// 4. Daily Missions
|
||||||
|
setupDailyMissions(user);
|
||||||
|
|
||||||
|
// 5. Streak Rewards
|
||||||
|
setupStreakRewards(user);
|
||||||
|
|
||||||
|
// 6. Loja
|
||||||
|
setupStore(user);
|
||||||
|
|
||||||
|
// 6. Mystery Box
|
||||||
|
setupMysteryBox(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DAILY PRESENT ---
|
||||||
|
private void claimDailyPresent() {
|
||||||
|
if (currentUserData == null) return;
|
||||||
|
String today = getTodayDateString();
|
||||||
|
|
||||||
|
if (today.equals(currentUserData.last_reward_claim_date)) {
|
||||||
|
showToast("Já resgataste o presente de hoje!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dailyGoal = currentUserData.meta_diaria_tarefas > 0 ? currentUserData.meta_diaria_tarefas : 4;
|
||||||
|
if (currentUserData.tasks_concluidas_hoje < dailyGoal) {
|
||||||
|
showToast("Completa as tuas tarefas primeiro!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("last_reward_claim_date", today);
|
||||||
|
updates.put("xp", FieldValue.increment(100));
|
||||||
|
updates.put("xp_hoje", FieldValue.increment(100));
|
||||||
|
updates.put("xp_semanal", FieldValue.increment(100));
|
||||||
|
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUserData.id_usuario, updates, () -> {
|
||||||
|
FirestoreManager.getInstance().addXpLog(currentUserData.id_usuario, 100, "daily_present_claim");
|
||||||
|
FirestoreManager.getInstance().addCoins(currentUserData.id_usuario, 20, "daily_goal_reward");
|
||||||
|
showLevelUpCheck(100);
|
||||||
|
showRewardDialog("🎁 Presente Diário", "+100 XP e +20 moedas", "Excelente trabalho ao completares as tuas tarefas de hoje!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 7-DAY SEQUENCE ---
|
||||||
|
private void setupSequenceCalendar(Usuario user) {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
llSequenceContainer.removeAllViews();
|
||||||
|
|
||||||
|
String today = getTodayDateString();
|
||||||
|
String lastClaim = user.last_login_claim_date;
|
||||||
|
|
||||||
|
int activeStreak = user.login_streak;
|
||||||
|
boolean claimedToday = today.equals(lastClaim);
|
||||||
|
boolean claimedYesterday = isYesterday(lastClaim);
|
||||||
|
|
||||||
|
int nextClaimDay;
|
||||||
|
if (claimedToday) {
|
||||||
|
nextClaimDay = activeStreak; // showing the day we claimed today
|
||||||
|
} else if (claimedYesterday) {
|
||||||
|
nextClaimDay = (activeStreak % 7) + 1; // advancing to the next day
|
||||||
|
} else {
|
||||||
|
nextClaimDay = 1; // streak broken, reset to 1
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] rewards = {"+50 XP", "+75 XP", "+100 XP", "+125 XP 🎁", "+150 XP ⚡", "+175 XP 🏅", "+250 XP 👑"};
|
||||||
|
String[] icons = {"⚡", "⚡", "⚡", "🎁", "⚡", "🏅", "👑"};
|
||||||
|
|
||||||
|
for (int i = 1; i <= 7; i++) {
|
||||||
|
View node = LayoutInflater.from(getContext()).inflate(R.layout.item_sequence_day, llSequenceContainer, false);
|
||||||
|
TextView tvDayLabel = node.findViewById(R.id.tvDayLabel);
|
||||||
|
View flNodeBackground = node.findViewById(R.id.flNodeBackground);
|
||||||
|
TextView tvDayRewardIcon = node.findViewById(R.id.tvDayRewardIcon);
|
||||||
|
ImageView ivDayClaimedCheck = node.findViewById(R.id.ivDayClaimedCheck);
|
||||||
|
TextView tvDayRewardValue = node.findViewById(R.id.tvDayRewardValue);
|
||||||
|
|
||||||
|
tvDayLabel.setText("Dia " + i);
|
||||||
|
tvDayRewardValue.setText(rewards[i - 1]);
|
||||||
|
tvDayRewardIcon.setText(icons[i - 1]);
|
||||||
|
|
||||||
|
// Determine State
|
||||||
|
if (claimedToday) {
|
||||||
|
if (i <= activeStreak) {
|
||||||
|
// Claimed
|
||||||
|
flNodeBackground.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.success_green));
|
||||||
|
tvDayRewardIcon.setVisibility(View.GONE);
|
||||||
|
ivDayClaimedCheck.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
// Locked
|
||||||
|
flNodeBackground.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
tvDayRewardIcon.setVisibility(View.VISIBLE);
|
||||||
|
ivDayClaimedCheck.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (claimedYesterday) {
|
||||||
|
if (i < nextClaimDay) {
|
||||||
|
// Claimed previously
|
||||||
|
flNodeBackground.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.success_green));
|
||||||
|
tvDayRewardIcon.setVisibility(View.GONE);
|
||||||
|
ivDayClaimedCheck.setVisibility(View.VISIBLE);
|
||||||
|
} else if (i == nextClaimDay) {
|
||||||
|
// Current day to claim (Active)
|
||||||
|
flNodeBackground.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.primary_purple));
|
||||||
|
tvDayRewardIcon.setVisibility(View.VISIBLE);
|
||||||
|
tvDayRewardIcon.setTextColor(Color.WHITE);
|
||||||
|
ivDayClaimedCheck.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
// Future lock
|
||||||
|
flNodeBackground.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
tvDayRewardIcon.setVisibility(View.VISIBLE);
|
||||||
|
ivDayClaimedCheck.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Streak broken, only Day 1 is active, others are locked
|
||||||
|
if (i == 1) {
|
||||||
|
flNodeBackground.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.primary_purple));
|
||||||
|
tvDayRewardIcon.setVisibility(View.VISIBLE);
|
||||||
|
tvDayRewardIcon.setTextColor(Color.WHITE);
|
||||||
|
ivDayClaimedCheck.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
flNodeBackground.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
tvDayRewardIcon.setVisibility(View.VISIBLE);
|
||||||
|
ivDayClaimedCheck.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llSequenceContainer.addView(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Claim Button state
|
||||||
|
if (claimedToday) {
|
||||||
|
btnClaimSequence.setEnabled(false);
|
||||||
|
btnClaimSequence.setText("Recompensado Hoje (Dia " + activeStreak + ")");
|
||||||
|
btnClaimSequence.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
btnClaimSequence.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
|
||||||
|
} else {
|
||||||
|
btnClaimSequence.setEnabled(true);
|
||||||
|
btnClaimSequence.setText("Resgatar Dia " + nextClaimDay);
|
||||||
|
btnClaimSequence.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.primary_purple));
|
||||||
|
btnClaimSequence.setTextColor(Color.WHITE);
|
||||||
|
btnClaimSequence.setOnClickListener(v -> claimSequence(nextClaimDay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void claimSequence(int dayToClaim) {
|
||||||
|
if (currentUserData == null) return;
|
||||||
|
String today = getTodayDateString();
|
||||||
|
|
||||||
|
if (today.equals(currentUserData.last_login_claim_date)) {
|
||||||
|
showToast("Já resgataste a recompensa de login de hoje!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] xpRewards = {50, 75, 100, 125, 150, 175, 250};
|
||||||
|
int xpReward = xpRewards[dayToClaim - 1];
|
||||||
|
|
||||||
|
// Coins rewards to demonstrate preparation for currency
|
||||||
|
int coinsReward = dayToClaim * 5;
|
||||||
|
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("login_streak", dayToClaim);
|
||||||
|
updates.put("last_login_claim_date", today);
|
||||||
|
updates.put("xp", FieldValue.increment(xpReward));
|
||||||
|
updates.put("xp_hoje", FieldValue.increment(xpReward));
|
||||||
|
updates.put("xp_semanal", FieldValue.increment(xpReward));
|
||||||
|
updates.put("coins", FieldValue.increment(coinsReward));
|
||||||
|
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUserData.id_usuario, updates, () -> {
|
||||||
|
FirestoreManager.getInstance().addXpLog(currentUserData.id_usuario, xpReward, "daily_login_claim");
|
||||||
|
showLevelUpCheck(xpReward);
|
||||||
|
|
||||||
|
String rewardDesc = "+" + xpReward + " XP e +" + coinsReward + " 🪙";
|
||||||
|
if (dayToClaim == 4) rewardDesc += "\n🎁 Acessório Extra Desbloqueado!";
|
||||||
|
if (dayToClaim == 6) rewardDesc += "\n🏅 Emblema Diário Obtido!";
|
||||||
|
if (dayToClaim == 7) rewardDesc += "\n👑 Item Raro Desbloqueado!";
|
||||||
|
|
||||||
|
showRewardDialog("📅 Sequência Diária (Dia " + dayToClaim + ")", rewardDesc, "Volta amanhã para continuar a sequência!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DAILY MISSIONS ---
|
||||||
|
private void setupDailyMissions(Usuario user) {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
llMissionsContainer.removeAllViews();
|
||||||
|
|
||||||
|
String today = getTodayDateString();
|
||||||
|
|
||||||
|
// Safe reset client-side if dates don't match
|
||||||
|
if (!today.equals(user.claimed_missions_date)) {
|
||||||
|
user.claimed_missions_date = today;
|
||||||
|
user.claimed_missions_today = new ArrayList<>();
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("claimed_missions_date", today);
|
||||||
|
updates.put("claimed_missions_today", new ArrayList<>());
|
||||||
|
FirestoreManager.getInstance().updateUserStats(user.id_usuario, updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dailyGoal = user.meta_diaria_tarefas > 0 ? user.meta_diaria_tarefas : 4;
|
||||||
|
|
||||||
|
List<DailyMission> missions = new ArrayList<>();
|
||||||
|
missions.add(new DailyMission("m_tasks", "Completar " + dailyGoal + " tarefas hoje", "✅", user.tasks_concluidas_hoje, dailyGoal, 50));
|
||||||
|
missions.add(new DailyMission("m_focus", "Fazer 1 sessão de foco (25 min)", "⏱️", user.tempo_foco_hoje >= 25 ? 1 : 0, 1, 50));
|
||||||
|
missions.add(new DailyMission("m_xp", "Ganhar 100 XP hoje", "⚡", user.xp_hoje, 100, 50));
|
||||||
|
missions.add(new DailyMission("m_streak", "Manter a ofensiva diária", "🔥", user.streak >= 1 ? 1 : 0, 1, 50));
|
||||||
|
|
||||||
|
for (DailyMission mission : missions) {
|
||||||
|
View itemView = LayoutInflater.from(getContext()).inflate(R.layout.item_mission, llMissionsContainer, false);
|
||||||
|
TextView tvMissionIcon = itemView.findViewById(R.id.tvMissionIcon);
|
||||||
|
TextView tvMissionTitle = itemView.findViewById(R.id.tvMissionTitle);
|
||||||
|
ProgressBar pbMissionProgress = itemView.findViewById(R.id.pbMissionProgress);
|
||||||
|
TextView tvMissionProgressText = itemView.findViewById(R.id.tvMissionProgressText);
|
||||||
|
TextView tvMissionReward = itemView.findViewById(R.id.tvMissionReward);
|
||||||
|
Button btnClaimMission = itemView.findViewById(R.id.btnClaimMission);
|
||||||
|
|
||||||
|
tvMissionIcon.setText(mission.icon);
|
||||||
|
tvMissionTitle.setText(mission.title);
|
||||||
|
tvMissionReward.setText("+" + mission.xpReward + " XP");
|
||||||
|
tvMissionProgressText.setText(mission.progress + "/" + mission.max);
|
||||||
|
|
||||||
|
int progressPercent = (int) (((float) mission.progress / mission.max) * 100);
|
||||||
|
pbMissionProgress.setProgress(progressPercent);
|
||||||
|
|
||||||
|
boolean isClaimed = user.claimed_missions_today.contains(mission.id);
|
||||||
|
|
||||||
|
if (isClaimed) {
|
||||||
|
btnClaimMission.setEnabled(false);
|
||||||
|
btnClaimMission.setText("Resgatado");
|
||||||
|
btnClaimMission.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
btnClaimMission.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
|
||||||
|
} else if (mission.progress >= mission.max) {
|
||||||
|
btnClaimMission.setEnabled(true);
|
||||||
|
btnClaimMission.setText("Resgatar");
|
||||||
|
btnClaimMission.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.primary_purple));
|
||||||
|
btnClaimMission.setTextColor(Color.WHITE);
|
||||||
|
btnClaimMission.setOnClickListener(v -> claimMission(mission));
|
||||||
|
} else {
|
||||||
|
btnClaimMission.setEnabled(false);
|
||||||
|
btnClaimMission.setText("Resgatar");
|
||||||
|
btnClaimMission.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
btnClaimMission.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
llMissionsContainer.addView(itemView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void claimMission(DailyMission mission) {
|
||||||
|
if (currentUserData == null) return;
|
||||||
|
|
||||||
|
if (currentUserData.claimed_missions_today.contains(mission.id)) {
|
||||||
|
showToast("Já resgataste esta missão!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("claimed_missions_today", FieldValue.arrayUnion(mission.id));
|
||||||
|
updates.put("xp", FieldValue.increment(mission.xpReward));
|
||||||
|
updates.put("xp_hoje", FieldValue.increment(mission.xpReward));
|
||||||
|
updates.put("xp_semanal", FieldValue.increment(mission.xpReward));
|
||||||
|
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUserData.id_usuario, updates, () -> {
|
||||||
|
FirestoreManager.getInstance().addXpLog(currentUserData.id_usuario, mission.xpReward, "daily_mission_" + mission.id);
|
||||||
|
showLevelUpCheck(mission.xpReward);
|
||||||
|
showRewardDialog("🎯 Missão Concluída", "+" + mission.xpReward + " XP", mission.title);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- STREAK REWARDS ---
|
||||||
|
private void setupStreakRewards(Usuario user) {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
llStreakRewardsContainer.removeAllViews();
|
||||||
|
|
||||||
|
List<StreakReward> rewards = new ArrayList<>();
|
||||||
|
rewards.add(new StreakReward("streak_3", 3, "Ofensiva de 3 dias", "Recompensa: +100 XP", 100, null, "🔥"));
|
||||||
|
rewards.add(new StreakReward("streak_7", 7, "Ofensiva de 7 dias", "Recompensa: Aura de Fogo + 250 XP", 250, "effect_fire", "⚡"));
|
||||||
|
rewards.add(new StreakReward("streak_14", 14, "Ofensiva de 14 dias", "Recompensa: Casaco Premium + 500 XP", 500, "clothes_outfit", "🧥"));
|
||||||
|
rewards.add(new StreakReward("streak_30", 30, "Ofensiva de 30 dias", "Recompensa: Moldura Dourada + 1000 XP", 1000, "frame_gold", "🏆"));
|
||||||
|
|
||||||
|
for (StreakReward reward : rewards) {
|
||||||
|
View itemView = LayoutInflater.from(getContext()).inflate(R.layout.item_streak_reward, llStreakRewardsContainer, false);
|
||||||
|
TextView tvStreakRewardIcon = itemView.findViewById(R.id.tvStreakRewardIcon);
|
||||||
|
TextView tvStreakTitle = itemView.findViewById(R.id.tvStreakTitle);
|
||||||
|
TextView tvStreakRewardDetail = itemView.findViewById(R.id.tvStreakRewardDetail);
|
||||||
|
Button btnClaimStreak = itemView.findViewById(R.id.btnClaimStreak);
|
||||||
|
|
||||||
|
tvStreakRewardIcon.setText(reward.icon);
|
||||||
|
tvStreakTitle.setText(reward.title);
|
||||||
|
tvStreakRewardDetail.setText(reward.rewardDetail);
|
||||||
|
|
||||||
|
boolean isClaimed = user.claimed_streak_rewards.contains(reward.id);
|
||||||
|
boolean isEligible = user.streak >= reward.requiredStreak;
|
||||||
|
|
||||||
|
if (isClaimed) {
|
||||||
|
btnClaimStreak.setEnabled(false);
|
||||||
|
btnClaimStreak.setText("Resgatado");
|
||||||
|
btnClaimStreak.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
btnClaimStreak.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
|
||||||
|
} else if (isEligible) {
|
||||||
|
btnClaimStreak.setEnabled(true);
|
||||||
|
btnClaimStreak.setText("Resgatar");
|
||||||
|
btnClaimStreak.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.primary_purple));
|
||||||
|
btnClaimStreak.setTextColor(Color.WHITE);
|
||||||
|
btnClaimStreak.setOnClickListener(v -> claimStreakReward(reward));
|
||||||
|
} else {
|
||||||
|
btnClaimStreak.setEnabled(false);
|
||||||
|
btnClaimStreak.setText(user.streak + "/" + reward.requiredStreak + " d");
|
||||||
|
btnClaimStreak.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
btnClaimStreak.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
llStreakRewardsContainer.addView(itemView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void claimStreakReward(StreakReward reward) {
|
||||||
|
if (currentUserData == null) return;
|
||||||
|
|
||||||
|
if (currentUserData.claimed_streak_rewards.contains(reward.id)) {
|
||||||
|
showToast("Já resgataste este prémio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUserData.streak < reward.requiredStreak) {
|
||||||
|
showToast("Ainda não atingiste o streak necessário!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("claimed_streak_rewards", FieldValue.arrayUnion(reward.id));
|
||||||
|
updates.put("xp", FieldValue.increment(reward.xpReward));
|
||||||
|
updates.put("xp_hoje", FieldValue.increment(reward.xpReward));
|
||||||
|
updates.put("xp_semanal", FieldValue.increment(reward.xpReward));
|
||||||
|
|
||||||
|
// Prepare coins simulation
|
||||||
|
int bonusCoins = reward.requiredStreak * 3;
|
||||||
|
updates.put("coins", FieldValue.increment(bonusCoins));
|
||||||
|
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUserData.id_usuario, updates, () -> {
|
||||||
|
FirestoreManager.getInstance().addXpLog(currentUserData.id_usuario, reward.xpReward, "streak_reward_" + reward.id);
|
||||||
|
showLevelUpCheck(reward.xpReward);
|
||||||
|
|
||||||
|
String rewardMsg = "+" + reward.xpReward + " XP e +" + bonusCoins + " 🪙";
|
||||||
|
if (reward.itemReward != null) {
|
||||||
|
rewardMsg += "\n✨ Desbloqueaste um Item Cosmético!";
|
||||||
|
}
|
||||||
|
|
||||||
|
showRewardDialog("🏆 Recompensa de Ofensiva", rewardMsg, reward.title);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- MYSTERY BOX ---
|
||||||
|
private void setupMysteryBox(Usuario user) {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
String today = getTodayDateString();
|
||||||
|
|
||||||
|
boolean openedToday = today.equals(user.last_box_open_date);
|
||||||
|
|
||||||
|
if (openedToday) {
|
||||||
|
tvMysteryBoxIcon.setText("🔓");
|
||||||
|
tvMysteryBoxStatus.setText("Volta amanhã para abrir outra caixa!");
|
||||||
|
btnOpenMysteryBox.setEnabled(false);
|
||||||
|
btnOpenMysteryBox.setText("Concluído");
|
||||||
|
btnOpenMysteryBox.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
btnOpenMysteryBox.setTextColor(ContextCompat.getColor(getContext(), R.color.text_secondary));
|
||||||
|
} else {
|
||||||
|
tvMysteryBoxIcon.setText("📦");
|
||||||
|
tvMysteryBoxStatus.setText("Abre uma caixa grátis hoje!");
|
||||||
|
btnOpenMysteryBox.setEnabled(true);
|
||||||
|
btnOpenMysteryBox.setText("Abrir");
|
||||||
|
btnOpenMysteryBox.setBackgroundTintList(ContextCompat.getColorStateList(getContext(), android.R.color.white));
|
||||||
|
btnOpenMysteryBox.setTextColor(Color.parseColor("#854D0E"));
|
||||||
|
btnOpenMysteryBox.setOnClickListener(v -> openMysteryBox());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openMysteryBox() {
|
||||||
|
if (currentUserData == null || getContext() == null) return;
|
||||||
|
String today = getTodayDateString();
|
||||||
|
|
||||||
|
if (today.equals(currentUserData.last_box_open_date)) {
|
||||||
|
showToast("Já abriste a caixa misteriosa hoje!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shaking animation
|
||||||
|
ObjectAnimator rotate = ObjectAnimator.ofFloat(tvMysteryBoxIcon, "rotation", 0f, 15f, -15f, 15f, -15f, 10f, -10f, 0f);
|
||||||
|
rotate.setDuration(1000);
|
||||||
|
rotate.setInterpolator(new CycleInterpolator(1.5f));
|
||||||
|
rotate.start();
|
||||||
|
|
||||||
|
btnOpenMysteryBox.setEnabled(false);
|
||||||
|
|
||||||
|
tvMysteryBoxIcon.postDelayed(() -> {
|
||||||
|
if (!isAdded()) return;
|
||||||
|
|
||||||
|
// Generate random reward
|
||||||
|
Random r = new Random();
|
||||||
|
int roll = r.nextInt(100);
|
||||||
|
int xpGained = 0;
|
||||||
|
int coinsGained = 0;
|
||||||
|
String prizeName = "";
|
||||||
|
|
||||||
|
if (roll < 40) { // 40% chance
|
||||||
|
xpGained = 50;
|
||||||
|
coinsGained = 5;
|
||||||
|
prizeName = "Caixa Comum: +50 XP e +5 Moedas 🪙";
|
||||||
|
} else if (roll < 70) { // 30% chance
|
||||||
|
xpGained = 100;
|
||||||
|
coinsGained = 10;
|
||||||
|
prizeName = "Caixa Incomum: +100 XP e +10 Moedas 🪙";
|
||||||
|
} else if (roll < 85) { // 15% chance
|
||||||
|
xpGained = 150;
|
||||||
|
coinsGained = 20;
|
||||||
|
prizeName = "Caixa Rara: +150 XP e +20 Moedas 🪙";
|
||||||
|
} else if (roll < 95) { // 10% chance
|
||||||
|
xpGained = 200;
|
||||||
|
coinsGained = 40;
|
||||||
|
prizeName = "Caixa Épica: +200 XP e +40 Moedas 🪙";
|
||||||
|
} else { // 5% chance
|
||||||
|
xpGained = 500;
|
||||||
|
coinsGained = 100;
|
||||||
|
prizeName = "🎁 Caixa Lendária: +500 XP e +100 Moedas 🪙!";
|
||||||
|
}
|
||||||
|
|
||||||
|
final int finalXp = xpGained;
|
||||||
|
final int finalCoins = coinsGained;
|
||||||
|
final String finalPrize = prizeName;
|
||||||
|
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("last_box_open_date", today);
|
||||||
|
updates.put("xp", FieldValue.increment(finalXp));
|
||||||
|
updates.put("xp_hoje", FieldValue.increment(finalXp));
|
||||||
|
updates.put("xp_semanal", FieldValue.increment(finalXp));
|
||||||
|
updates.put("coins", FieldValue.increment(finalCoins));
|
||||||
|
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUserData.id_usuario, updates, () -> {
|
||||||
|
FirestoreManager.getInstance().addXpLog(currentUserData.id_usuario, finalXp, "mystery_box_open");
|
||||||
|
showLevelUpCheck(finalXp);
|
||||||
|
tvMysteryBoxIcon.setText("🔓");
|
||||||
|
showRewardDialog("📦 Caixa Misteriosa", finalPrize, "Parabéns pela tua recompensa aleatória!");
|
||||||
|
});
|
||||||
|
|
||||||
|
}, 1200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- HELPERS ---
|
||||||
|
private String getTodayDateString() {
|
||||||
|
return new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isYesterday(String dateStr) {
|
||||||
|
if (dateStr == null || dateStr.isEmpty()) return false;
|
||||||
|
try {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||||
|
Date date = sdf.parse(dateStr);
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.add(Calendar.DAY_OF_YEAR, -1);
|
||||||
|
String yesterdayStr = sdf.format(cal.getTime());
|
||||||
|
return yesterdayStr.equals(dateStr);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showToast(String message) {
|
||||||
|
if (getContext() != null) {
|
||||||
|
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showRewardDialog(String title, String rewardText, String message) {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
new MaterialAlertDialogBuilder(getContext())
|
||||||
|
.setTitle(title)
|
||||||
|
.setMessage(message + "\n\nGanhaste:\n" + rewardText)
|
||||||
|
.setPositiveButton("Incrível!", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLevelUpCheck(int addedXp) {
|
||||||
|
if (currentUserData == null || !isAdded()) return;
|
||||||
|
int currentXp = currentUserData.xp;
|
||||||
|
int currentLevel = currentUserData.level;
|
||||||
|
int nextLevelThreshold = (currentLevel * (currentLevel + 1) / 2) * 100;
|
||||||
|
|
||||||
|
if (currentXp + addedXp >= nextLevelThreshold) {
|
||||||
|
// Trigger Level Up in database
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("level", currentLevel + 1);
|
||||||
|
FirestoreManager.getInstance().addCoins(currentUserData.id_usuario, 50, "level_up");
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUserData.id_usuario, updates, () -> {
|
||||||
|
if (getContext() != null) {
|
||||||
|
NotificationHelper.showNotification(getContext(), "🎉 SUBISTE DE NÍVEL!", "Chegaste ao nível " + (currentLevel + 1) + "! Continua assim.");
|
||||||
|
View dialogView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_level_up, null);
|
||||||
|
TextView tvNewLevel = dialogView.findViewById(R.id.tvNewLevel);
|
||||||
|
tvNewLevel.setText(String.valueOf(currentLevel + 1));
|
||||||
|
|
||||||
|
new MaterialAlertDialogBuilder(getContext())
|
||||||
|
.setView(dialogView)
|
||||||
|
.setPositiveButton("Incrível!", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupStore(Usuario user) {
|
||||||
|
if (getContext() == null || llStoreContainer == null) return;
|
||||||
|
llStoreContainer.removeAllViews();
|
||||||
|
|
||||||
|
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("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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStoreItem(String id, String title, String desc, int cost, String icon, Usuario user) {
|
||||||
|
View item = getLayoutInflater().inflate(R.layout.item_store, llStoreContainer, false);
|
||||||
|
|
||||||
|
android.widget.TextView tvStoreIcon = item.findViewById(R.id.tvStoreIcon);
|
||||||
|
android.widget.TextView tvStoreTitle = item.findViewById(R.id.tvStoreTitle);
|
||||||
|
android.widget.TextView tvStoreDesc = item.findViewById(R.id.tvStoreDesc);
|
||||||
|
android.widget.TextView tvStoreOwned = item.findViewById(R.id.tvStoreOwned);
|
||||||
|
android.widget.Button btnBuyStoreItem = item.findViewById(R.id.btnBuyStoreItem);
|
||||||
|
|
||||||
|
tvStoreIcon.setText(icon);
|
||||||
|
tvStoreTitle.setText(title);
|
||||||
|
tvStoreDesc.setText(desc);
|
||||||
|
btnBuyStoreItem.setText(cost + " 🪙");
|
||||||
|
|
||||||
|
boolean alreadyOwnedOneTime = false;
|
||||||
|
|
||||||
|
if (id.equals("frame_avatar")) {
|
||||||
|
if (user.unlockedItems != null && user.unlockedItems.contains(id)) {
|
||||||
|
tvStoreOwned.setVisibility(android.view.View.VISIBLE);
|
||||||
|
tvStoreOwned.setText("Possuído");
|
||||||
|
alreadyOwnedOneTime = true;
|
||||||
|
}
|
||||||
|
} else if (!id.equals("mystery_box")) {
|
||||||
|
int qty = (user.inventory != null && user.inventory.containsKey(id)) ? user.inventory.get(id) : 0;
|
||||||
|
if (qty > 0) {
|
||||||
|
tvStoreOwned.setVisibility(android.view.View.VISIBLE);
|
||||||
|
tvStoreOwned.setText("Possuis: " + qty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer userCoins = user.coins;
|
||||||
|
if (userCoins == null) {
|
||||||
|
userCoins = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alreadyOwnedOneTime) {
|
||||||
|
btnBuyStoreItem.setEnabled(false);
|
||||||
|
btnBuyStoreItem.setText("Comprado");
|
||||||
|
btnBuyStoreItem.setBackgroundTintList(androidx.core.content.ContextCompat.getColorStateList(getContext(), R.color.border_color));
|
||||||
|
} else if (userCoins < cost) {
|
||||||
|
btnBuyStoreItem.setEnabled(true);
|
||||||
|
btnBuyStoreItem.setOnClickListener(v -> {
|
||||||
|
showToast("Moedas insuficientes");
|
||||||
|
pulseAnimation(tvStatsCoins);
|
||||||
|
});
|
||||||
|
btnBuyStoreItem.setBackgroundTintList(androidx.core.content.ContextCompat.getColorStateList(getContext(), android.R.color.darker_gray));
|
||||||
|
} else {
|
||||||
|
btnBuyStoreItem.setOnClickListener(v -> buyStoreItem(id, title, cost, item));
|
||||||
|
}
|
||||||
|
|
||||||
|
llStoreContainer.addView(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buyStoreItem(String itemId, String itemName, int cost, View itemView) {
|
||||||
|
android.util.Log.d("FLUXUP_DEBUG", "SHOP_BUY_CLICKED");
|
||||||
|
|
||||||
|
if (getContext() == null) return;
|
||||||
|
|
||||||
|
if (itemId == null || itemName == null) {
|
||||||
|
android.util.Log.e("FLUXUP_DEBUG", "SHOP_ERROR: item null");
|
||||||
|
showToast("Erro: item inválido.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
android.util.Log.d("FLUXUP_DEBUG", "SHOP_ITEM_ID: " + itemId);
|
||||||
|
android.util.Log.d("FLUXUP_DEBUG", "SHOP_PRICE: " + cost);
|
||||||
|
|
||||||
|
if (currentUserData == null || currentUserData.id_usuario == null) {
|
||||||
|
android.util.Log.e("FLUXUP_DEBUG", "SHOP_ERROR: userId null");
|
||||||
|
showToast("Erro: utilizador não encontrado.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
android.util.Log.d("FLUXUP_DEBUG", "SHOP_USER_ID: " + currentUserData.id_usuario);
|
||||||
|
Integer currentCoins = currentUserData.coins;
|
||||||
|
if (currentCoins == null) {
|
||||||
|
currentCoins = 0;
|
||||||
|
}
|
||||||
|
android.util.Log.d("FLUXUP_DEBUG", "SHOP_COINS: " + currentCoins);
|
||||||
|
|
||||||
|
if (currentCoins < cost) {
|
||||||
|
android.util.Log.w("FLUXUP_DEBUG", "SHOP_ERROR: moedas insuficientes");
|
||||||
|
showToast("Moedas insuficientes");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUserData.unlockedItems == null) {
|
||||||
|
currentUserData.unlockedItems = new java.util.ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUserData.inventory == null) {
|
||||||
|
currentUserData.inventory = new java.util.HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
showGlowEffect(itemView);
|
||||||
|
|
||||||
|
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||||
|
|
||||||
|
if (itemId.equals("frame_avatar")) {
|
||||||
|
updates.put("unlockedItems", com.google.firebase.firestore.FieldValue.arrayUnion(itemId));
|
||||||
|
} else if (!itemId.equals("mystery_box")) {
|
||||||
|
int currentQty = currentUserData.inventory.containsKey(itemId) ? currentUserData.inventory.get(itemId) : 0;
|
||||||
|
updates.put("inventory." + itemId, currentQty + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUserData.id_usuario, updates, () -> {
|
||||||
|
FirestoreManager.getInstance().addCoins(currentUserData.id_usuario, -cost, "store_purchase_" + itemId);
|
||||||
|
|
||||||
|
showConfetti();
|
||||||
|
|
||||||
|
if (itemId.equals("mystery_box")) {
|
||||||
|
openPaidMysteryBox();
|
||||||
|
} else {
|
||||||
|
showRewardDialog("🛍️ Compra Concluída", itemName, "Compraste " + itemName + " com sucesso!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
android.util.Log.e("FLUXUP_DEBUG", "SHOP_ERROR: " + e.getMessage());
|
||||||
|
showToast("Erro ao processar compra.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openPaidMysteryBox() {
|
||||||
|
if (getContext() == null || currentUserData == null) return;
|
||||||
|
|
||||||
|
int roll = new java.util.Random().nextInt(100);
|
||||||
|
int finalXp = 0;
|
||||||
|
int finalCoins = 0;
|
||||||
|
|
||||||
|
if (roll < 40) {
|
||||||
|
finalXp = 50;
|
||||||
|
finalCoins = 5;
|
||||||
|
} else if (roll < 75) {
|
||||||
|
finalXp = 100;
|
||||||
|
finalCoins = 15;
|
||||||
|
} else if (roll < 95) {
|
||||||
|
finalXp = 250;
|
||||||
|
finalCoins = 30;
|
||||||
|
} else {
|
||||||
|
finalXp = 500;
|
||||||
|
finalCoins = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
||||||
|
updates.put("xp", com.google.firebase.firestore.FieldValue.increment(finalXp));
|
||||||
|
updates.put("xp_hoje", com.google.firebase.firestore.FieldValue.increment(finalXp));
|
||||||
|
updates.put("xp_semanal", com.google.firebase.firestore.FieldValue.increment(finalXp));
|
||||||
|
|
||||||
|
int capturedXp = finalXp;
|
||||||
|
int capturedCoins = finalCoins;
|
||||||
|
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUserData.id_usuario, updates, () -> {
|
||||||
|
FirestoreManager.getInstance().addXpLog(currentUserData.id_usuario, capturedXp, "paid_mystery_box");
|
||||||
|
if (capturedCoins > 0) {
|
||||||
|
FirestoreManager.getInstance().addCoins(currentUserData.id_usuario, capturedCoins, "paid_mystery_box_reward");
|
||||||
|
}
|
||||||
|
showLevelUpCheck(capturedXp);
|
||||||
|
showRewardDialog("🎁 Caixa Misteriosa (Paga)", "+" + capturedXp + " XP e +" + capturedCoins + " 🪙", "A caixa misteriosa revelou recompensas incríveis!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animators
|
||||||
|
private void pulseAnimation(View view) {
|
||||||
|
if (view == null) return;
|
||||||
|
android.animation.ObjectAnimator scaleX = android.animation.ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.2f, 1f);
|
||||||
|
android.animation.ObjectAnimator scaleY = android.animation.ObjectAnimator.ofFloat(view, "scaleY", 1f, 1.2f, 1f);
|
||||||
|
android.animation.AnimatorSet animatorSet = new android.animation.AnimatorSet();
|
||||||
|
animatorSet.setDuration(400);
|
||||||
|
animatorSet.playTogether(scaleX, scaleY);
|
||||||
|
animatorSet.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showGlowEffect(View view) {
|
||||||
|
if (view == null) return;
|
||||||
|
android.animation.ObjectAnimator alpha = android.animation.ObjectAnimator.ofFloat(view, "alpha", 1f, 0.5f, 1f);
|
||||||
|
alpha.setDuration(300);
|
||||||
|
pulseAnimation(view);
|
||||||
|
alpha.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showFloatingText(View anchor, String text) {
|
||||||
|
if (getContext() == null || anchor == null || anchor.getParent() == null) return;
|
||||||
|
android.view.ViewGroup parent = (android.view.ViewGroup) getView();
|
||||||
|
if (parent == null) return;
|
||||||
|
|
||||||
|
android.widget.TextView floatingText = new android.widget.TextView(getContext());
|
||||||
|
floatingText.setText(text);
|
||||||
|
floatingText.setTextColor(androidx.core.content.ContextCompat.getColor(getContext(), R.color.primary_purple));
|
||||||
|
floatingText.setTextSize(18f);
|
||||||
|
floatingText.setTypeface(null, android.graphics.Typeface.BOLD);
|
||||||
|
|
||||||
|
int[] location = new int[2];
|
||||||
|
anchor.getLocationInWindow(location);
|
||||||
|
|
||||||
|
int[] parentLocation = new int[2];
|
||||||
|
parent.getLocationInWindow(parentLocation);
|
||||||
|
|
||||||
|
float x = location[0] - parentLocation[0];
|
||||||
|
float y = location[1] - parentLocation[1] - 30;
|
||||||
|
|
||||||
|
floatingText.setX(x);
|
||||||
|
floatingText.setY(y);
|
||||||
|
|
||||||
|
parent.addView(floatingText);
|
||||||
|
|
||||||
|
floatingText.animate()
|
||||||
|
.translationYBy(-100f)
|
||||||
|
.alpha(0f)
|
||||||
|
.setDuration(1200)
|
||||||
|
.setInterpolator(new android.view.animation.DecelerateInterpolator())
|
||||||
|
.withEndAction(() -> parent.removeView(floatingText))
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showConfetti() {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
android.view.ViewGroup parent = (android.view.ViewGroup) getView();
|
||||||
|
if (parent == null) return;
|
||||||
|
|
||||||
|
String[] emojis = {"🎉", "✨", "🪙", "🌟"};
|
||||||
|
java.util.Random random = new java.util.Random();
|
||||||
|
|
||||||
|
int width = parent.getWidth();
|
||||||
|
if (width <= 0) width = 1000;
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
android.widget.TextView confetti = new android.widget.TextView(getContext());
|
||||||
|
confetti.setText(emojis[random.nextInt(emojis.length)]);
|
||||||
|
confetti.setTextSize(random.nextInt(16) + 16f);
|
||||||
|
|
||||||
|
float startX = random.nextInt(width);
|
||||||
|
confetti.setX(startX);
|
||||||
|
confetti.setY(-100f);
|
||||||
|
|
||||||
|
parent.addView(confetti);
|
||||||
|
|
||||||
|
confetti.animate()
|
||||||
|
.translationY(parent.getHeight() + 100f)
|
||||||
|
.translationX(startX + (random.nextBoolean() ? 150 : -150))
|
||||||
|
.rotation(random.nextInt(360))
|
||||||
|
.setDuration(1500 + random.nextInt(1000))
|
||||||
|
.setInterpolator(new android.view.animation.AccelerateInterpolator())
|
||||||
|
.withEndAction(() -> parent.removeView(confetti))
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
if (userListener != null) {
|
||||||
|
userListener.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Model structures
|
||||||
|
private static class DailyMission {
|
||||||
|
final String id;
|
||||||
|
final String title;
|
||||||
|
final String icon;
|
||||||
|
final int progress;
|
||||||
|
final int max;
|
||||||
|
final int xpReward;
|
||||||
|
|
||||||
|
DailyMission(String id, String title, String icon, int progress, int max, int xpReward) {
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.icon = icon;
|
||||||
|
this.progress = Math.min(progress, max);
|
||||||
|
this.max = max;
|
||||||
|
this.xpReward = xpReward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StreakReward {
|
||||||
|
final String id;
|
||||||
|
final int requiredStreak;
|
||||||
|
final String title;
|
||||||
|
final String rewardDetail;
|
||||||
|
final int xpReward;
|
||||||
|
final String itemReward;
|
||||||
|
final String icon;
|
||||||
|
|
||||||
|
StreakReward(String id, int requiredStreak, String title, String rewardDetail, int xpReward, String itemReward, String icon) {
|
||||||
|
this.id = id;
|
||||||
|
this.requiredStreak = requiredStreak;
|
||||||
|
this.title = title;
|
||||||
|
this.rewardDetail = rewardDetail;
|
||||||
|
this.xpReward = xpReward;
|
||||||
|
this.itemReward = itemReward;
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
app/src/main/res/drawable/card_gradient_gold.xml
Normal file
10
app/src/main/res/drawable/card_gradient_gold.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="135"
|
||||||
|
android:startColor="#FDE047"
|
||||||
|
android:endColor="#EAB308"
|
||||||
|
android:type="linear"/>
|
||||||
|
<corners android:radius="@dimen/radius_duo"/>
|
||||||
|
</shape>
|
||||||
10
app/src/main/res/drawable/card_gradient_purple.xml
Normal file
10
app/src/main/res/drawable/card_gradient_purple.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="135"
|
||||||
|
android:startColor="#9D4EDD"
|
||||||
|
android:endColor="#7C3AED"
|
||||||
|
android:type="linear"/>
|
||||||
|
<corners android:radius="@dimen/radius_duo"/>
|
||||||
|
</shape>
|
||||||
9
app/src/main/res/drawable/ic_nav_rewards.xml
Normal file
9
app/src/main/res/drawable/ic_nav_rewards.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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="#FF000000"
|
||||||
|
android:pathData="M20,6h-3c0,-1.66 -1.34,-3 -3,-3h-4C8.34,3 7,4.34 7,6L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,5c0.55,0 1,0.45 1,1h-2c0,-0.55 0.45,-1 1,-1zM10,5c0.55,0 1,0.45 1,1L9,6c0,-0.55 0.45,-1 1,-1zM20,19L4,19v-4h16v4zM20,13L4,13v-5h3c0,1.66 1.34,3 3,3h4c1.66,0 3,-1.34 3,-3h3v5z"/>
|
||||||
|
</vector>
|
||||||
33
app/src/main/res/layout/dialog_duration_picker.xml
Normal file
33
app/src/main/res/layout/dialog_duration_picker.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDurationTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Duração"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/tilDuration"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Duração (minutos)"
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/etDuration"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
487
app/src/main/res/layout/fragment_rewards.xml
Normal file
487
app/src/main/res/layout/fragment_rewards.xml
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.core.widget.NestedScrollView 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_height="match_parent"
|
||||||
|
android:background="@color/background_light"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<!-- 1. 👋 HEADER -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="20dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvRewardsTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Recompensas"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="26sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvRewardsSubtitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Continua consistente para desbloquear prémios."
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!-- Stats Chips Row -->
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:layout_marginBottom="24dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<!-- XP Chip -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="1dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="14dp"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="⚡"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStatsXP"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:text="1200 XP"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- Streak Chip -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="1dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="14dp"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🔥"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStatsStreak"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:text="5 dias"
|
||||||
|
android:textColor="@color/streak_orange"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- Level Chip -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="1dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="14dp"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="⭐"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStatsLevel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:text="Nível 3"
|
||||||
|
android:textColor="@color/success_green"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- Coins Chip (Prep for Future Coins) -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardCornerRadius="20dp"
|
||||||
|
app:cardElevation="1dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="14dp"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🪙"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStatsCoins"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:text="0 moedas"
|
||||||
|
android:textColor="@color/reward_yellow"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</LinearLayout>
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<!-- 2. 🎁 PRESENTE DIÁRIO -->
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:cardCornerRadius="@dimen/radius_duo"
|
||||||
|
app:cardElevation="3dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/card_gradient_purple"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="52dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:text="🎁"
|
||||||
|
android:textSize="40sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/node_circle_bg"
|
||||||
|
android:backgroundTint="#40FFFFFF"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginStart="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Presente diário"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="18sp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDailyPresentGoal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Completa 4 tarefas hoje"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:alpha="0.9"
|
||||||
|
android:textSize="13sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/pbDailyPresent"
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="12dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toStartOf="@id/tvDailyPresentProgressText"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="0"
|
||||||
|
android:progressDrawable="@drawable/progress_bar_duo"
|
||||||
|
android:progressTint="@color/white"
|
||||||
|
android:progressBackgroundTint="#40FFFFFF"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDailyPresentProgressText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:text="0/4"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:weightSum="2">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Recompensa: +100 XP"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="13sp"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnClaimDailyPresent"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Resgatar"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:backgroundTint="@color/white"
|
||||||
|
android:textStyle="bold"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- 3. 🗓️ SEQUÊNCIA DIÁRIA (7 DIAS) -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Sequência de Acessos (7 dias)"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Resgata bónus adicionais todos os dias consecutivamente."
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llSequenceContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_gravity="center_horizontal"/>
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnClaimSequence"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="Resgatar Recompensa de Hoje"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:backgroundTint="@color/primary_purple"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- 4. 🎯 MISSÕES DIÁRIAS -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Missões do Dia"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llMissionsContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<!-- 5. 🔥 RECOMPENSAS DE STREAK -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Recompensas de Ofensiva"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llStreakRewardsContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<!-- 6. 📦 CAIXA SURPRESA -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Caixa Misteriosa"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
app:cardCornerRadius="@dimen/radius_duo"
|
||||||
|
app:cardElevation="3dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llMysteryBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/card_gradient_gold"
|
||||||
|
android:padding="24dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMysteryBoxIcon"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:text="📦"
|
||||||
|
android:textSize="64sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Caixa Misteriosa"
|
||||||
|
android:textColor="#854D0E"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMysteryBoxStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Abre uma caixa grátis hoje!"
|
||||||
|
android:textColor="#A16207"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnOpenMysteryBox"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:text="Abrir"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:backgroundTint="#854D0E"
|
||||||
|
android:textStyle="bold"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- 7. 🛒 LOJA -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Loja da App"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:layout_marginTop="16dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llStoreContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginBottom="32dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
82
app/src/main/res/layout/item_friend_request.xml
Normal file
82
app/src/main/res/layout/item_friend_request.xml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView 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_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="12dp">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
app:cardCornerRadius="25dp"
|
||||||
|
app:cardElevation="0dp">
|
||||||
|
<com.fluxup.app.AvatarView
|
||||||
|
android:id="@+id/ivFriendAvatar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvFriendName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Nome"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvFriendStats"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="450 XP"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnAcceptFriend"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Aceitar"
|
||||||
|
android:textColor="@color/success_green"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingHorizontal="8dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnRejectFriend"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Recusar"
|
||||||
|
android:textColor="@color/error_red"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingHorizontal="8dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
95
app/src/main/res/layout/item_mission.xml
Normal file
95
app/src/main/res/layout/item_mission.xml
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView 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_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMissionIcon"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:text="🎯"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/node_circle_bg"
|
||||||
|
android:backgroundTint="#F3E8FF"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMissionTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Missão Diária"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/pbMissionProgress"
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="50"
|
||||||
|
android:progressDrawable="@drawable/progress_bar_duo"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMissionProgressText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="2/4"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="end">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMissionReward"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="+50 XP"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnClaimMission"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:text="Reclamar"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:backgroundTint="@color/primary_purple"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
58
app/src/main/res/layout/item_sequence_day.xml
Normal file
58
app/src/main/res/layout/item_sequence_day.xml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_marginHorizontal="4dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDayLabel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Dia 1"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="6dp"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/flNodeBackground"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:background="@drawable/node_circle_bg"
|
||||||
|
android:backgroundTint="@color/border_color">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDayRewardIcon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="⚡"
|
||||||
|
android:textSize="22sp"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivDayClaimedCheck"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/ic_back"
|
||||||
|
android:rotation="180"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:tint="@color/white"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDayRewardValue"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="+50 XP"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"/>
|
||||||
|
</LinearLayout>
|
||||||
77
app/src/main/res/layout/item_store.xml
Normal file
77
app/src/main/res/layout/item_store.xml
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView 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_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStoreIcon"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:text="📦"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/circle_bg_light"
|
||||||
|
android:layout_marginEnd="16dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStoreTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Item"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStoreDesc"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Descrição do item"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:layout_marginTop="4dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStoreOwned"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Possuis: 0"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnBuyStoreItem"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:text="60 🪙"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:backgroundTint="@color/primary_purple"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:cornerRadius="12dp"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
68
app/src/main/res/layout/item_streak_reward.xml
Normal file
68
app/src/main/res/layout/item_streak_reward.xml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView 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_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardBackgroundColor="@color/card_background">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStreakRewardIcon"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:text="🔥"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/node_circle_bg"
|
||||||
|
android:backgroundTint="#FFF7ED"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStreakTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Ofensiva de 3 dias"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStreakRewardDetail"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Recompensa: +100 XP"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnClaimStreak"
|
||||||
|
android:layout_width="90dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:text="Resgatar"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:backgroundTint="@color/primary_purple"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
Reference in New Issue
Block a user