mudar
This commit is contained in:
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -1,4 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
|||||||
@@ -61,4 +61,5 @@ dependencies {
|
|||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||||
|
implementation 'com.github.PhilJay:MPAndroidChart:3.1.0'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<activity android:name=".MainActivity" />
|
<activity android:name=".MainActivity" />
|
||||||
<activity android:name=".SettingsActivity" />
|
<activity android:name=".SettingsActivity" />
|
||||||
<activity android:name=".StreakActivity" />
|
<activity android:name=".StreakActivity" />
|
||||||
|
<activity android:name=".StatisticsActivity" />
|
||||||
<activity android:name=".FindFriendsActivity" />
|
<activity android:name=".FindFriendsActivity" />
|
||||||
<activity android:name=".TrophiesActivity" />
|
<activity android:name=".TrophiesActivity" />
|
||||||
|
|
||||||
|
|||||||
@@ -11,104 +11,108 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.google.android.material.button.MaterialButton;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class FindFriendsActivity extends AppCompatActivity {
|
public class FindFriendsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private EditText etSearch;
|
||||||
|
private LinearLayout resultsContainer;
|
||||||
|
private com.google.android.material.tabs.TabLayout tabLayout;
|
||||||
private ImageButton btnClose;
|
private ImageButton btnClose;
|
||||||
private RecyclerView rvSuggestions;
|
private String currentTab = "Sugestões";
|
||||||
private View btnContacts, btnSearchByName, btnProfileLink;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_find_friends);
|
setContentView(R.layout.activity_find_friends);
|
||||||
|
|
||||||
btnClose = findViewById(R.id.btnClose);
|
initViews();
|
||||||
rvSuggestions = findViewById(R.id.rvSuggestions);
|
setupListeners();
|
||||||
btnContacts = findViewById(R.id.btnContacts);
|
loadSuggestions();
|
||||||
btnSearchByName = findViewById(R.id.btnSearchByName);
|
}
|
||||||
btnProfileLink = findViewById(R.id.btnProfileLink);
|
|
||||||
|
|
||||||
|
private void initViews() {
|
||||||
|
etSearch = findViewById(R.id.etSearchFriends);
|
||||||
|
resultsContainer = findViewById(R.id.friendsResultsContainer);
|
||||||
|
tabLayout = findViewById(R.id.tabLayoutFriends);
|
||||||
|
btnClose = findViewById(R.id.btnClose);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupListeners() {
|
||||||
btnClose.setOnClickListener(v -> finish());
|
btnClose.setOnClickListener(v -> finish());
|
||||||
|
|
||||||
setupSuggestions();
|
etSearch.addTextChangedListener(new android.text.TextWatcher() {
|
||||||
}
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
private void setupSuggestions() {
|
if (s.length() > 0) {
|
||||||
rvSuggestions.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
performSearch(s.toString());
|
||||||
|
} else {
|
||||||
List<Suggestion> suggestions = new ArrayList<>();
|
loadSuggestions();
|
||||||
suggestions.add(new Suggestion("Maria Silva", "Segue você", R.color.reward_yellow));
|
}
|
||||||
suggestions.add(new Suggestion("João Pereira", "Amigo em comum", R.color.success_green));
|
|
||||||
suggestions.add(new Suggestion("Ana Costa", "Segue você", R.color.streak_orange));
|
|
||||||
suggestions.add(new Suggestion("Ricardo M.", "Novo no Fluxup", R.color.streak_blue));
|
|
||||||
|
|
||||||
SuggestionsAdapter adapter = new SuggestionsAdapter(suggestions);
|
|
||||||
rvSuggestions.setAdapter(adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Data Model ---
|
|
||||||
private static class Suggestion {
|
|
||||||
String name;
|
|
||||||
String info;
|
|
||||||
int colorRes;
|
|
||||||
|
|
||||||
Suggestion(String name, String info, int colorRes) {
|
|
||||||
this.name = name;
|
|
||||||
this.info = info;
|
|
||||||
this.colorRes = colorRes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Adapter ---
|
|
||||||
private class SuggestionsAdapter extends RecyclerView.Adapter<SuggestionsAdapter.ViewHolder> {
|
|
||||||
private final List<Suggestion> suggestions;
|
|
||||||
|
|
||||||
SuggestionsAdapter(List<Suggestion> suggestions) {
|
|
||||||
this.suggestions = suggestions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
|
||||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_friend_suggestion, parent, false);
|
|
||||||
return new ViewHolder(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
|
||||||
Suggestion item = suggestions.get(position);
|
|
||||||
holder.tvName.setText(item.name);
|
|
||||||
holder.tvInfo.setText(item.info);
|
|
||||||
holder.ivAvatar.setColorFilter(getResources().getColor(item.colorRes));
|
|
||||||
|
|
||||||
holder.btnFollow.setOnClickListener(v -> {
|
|
||||||
holder.btnFollow.setText("Seguindo");
|
|
||||||
holder.btnFollow.setEnabled(false);
|
|
||||||
holder.btnFollow.setAlpha(0.6f);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return suggestions.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
ImageView ivAvatar;
|
|
||||||
TextView tvName, tvInfo;
|
|
||||||
MaterialButton btnFollow;
|
|
||||||
|
|
||||||
ViewHolder(View view) {
|
|
||||||
super(view);
|
|
||||||
ivAvatar = view.findViewById(R.id.ivFriendAvatar);
|
|
||||||
tvName = view.findViewById(R.id.tvFriendName);
|
|
||||||
tvInfo = view.findViewById(R.id.tvFriendInfo);
|
|
||||||
btnFollow = view.findViewById(R.id.btnFollowBack);
|
|
||||||
}
|
}
|
||||||
}
|
@Override public void afterTextChanged(android.text.Editable s) {}
|
||||||
|
});
|
||||||
|
|
||||||
|
tabLayout.addOnTabSelectedListener(new com.google.android.material.tabs.TabLayout.OnTabSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onTabSelected(com.google.android.material.tabs.TabLayout.Tab tab) {
|
||||||
|
currentTab = tab.getText().toString();
|
||||||
|
if (currentTab.equals("Sugestões")) loadSuggestions();
|
||||||
|
else resultsContainer.removeAllViews(); // Placeholder for others
|
||||||
|
}
|
||||||
|
@Override public void onTabUnselected(com.google.android.material.tabs.TabLayout.Tab tab) {}
|
||||||
|
@Override public void onTabReselected(com.google.android.material.tabs.TabLayout.Tab tab) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void performSearch(String query) {
|
||||||
|
com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
||||||
|
.collection("users")
|
||||||
|
.orderBy("usuario")
|
||||||
|
.startAt(query)
|
||||||
|
.endAt(query + "\uf8ff")
|
||||||
|
.limit(10)
|
||||||
|
.get()
|
||||||
|
.addOnSuccessListener(snapshots -> {
|
||||||
|
resultsContainer.removeAllViews();
|
||||||
|
for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
|
||||||
|
Usuario u = doc.toObject(Usuario.class);
|
||||||
|
if (u != null) addFriendItem(u);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadSuggestions() {
|
||||||
|
com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
||||||
|
.collection("users")
|
||||||
|
.limit(10)
|
||||||
|
.get()
|
||||||
|
.addOnSuccessListener(snapshots -> {
|
||||||
|
resultsContainer.removeAllViews();
|
||||||
|
for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
|
||||||
|
Usuario u = doc.toObject(Usuario.class);
|
||||||
|
if (u != null) addFriendItem(u);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFriendItem(Usuario user) {
|
||||||
|
View view = getLayoutInflater().inflate(R.layout.item_friend_action, resultsContainer, false);
|
||||||
|
TextView tvName = view.findViewById(R.id.tvFriendName);
|
||||||
|
TextView tvStats = view.findViewById(R.id.tvFriendStats);
|
||||||
|
Button btnAction = view.findViewById(R.id.btnFriendAction);
|
||||||
|
|
||||||
|
tvName.setText(user.usuario);
|
||||||
|
tvStats.setText("Nível " + user.level + " • " + user.xp + " XP");
|
||||||
|
|
||||||
|
btnAction.setOnClickListener(v -> {
|
||||||
|
btnAction.setText("Pendente");
|
||||||
|
btnAction.setEnabled(false);
|
||||||
|
btnAction.setAlpha(0.5f);
|
||||||
|
Toast.makeText(this, "Pedido enviado para " + user.usuario, Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
resultsContainer.addView(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,13 @@ public class FirestoreManager {
|
|||||||
db.collection("tasks").document(task.id).set(task);
|
db.collection("tasks").document(task.id).set(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Elimina uma tarefa.
|
||||||
|
*/
|
||||||
|
public void deleteTask(String taskId) {
|
||||||
|
db.collection("tasks").document(taskId).delete();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Atualiza campos específicos do perfil do utilizador (ex: XP, Streak).
|
* Atualiza campos específicos do perfil do utilizador (ex: XP, Streak).
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import android.widget.FrameLayout;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Button;
|
||||||
import com.google.firebase.auth.FirebaseUser;
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
import com.google.firebase.firestore.ListenerRegistration;
|
import com.google.firebase.firestore.ListenerRegistration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -31,76 +32,114 @@ import java.util.List;
|
|||||||
|
|
||||||
public class InicioFragment extends Fragment {
|
public class InicioFragment extends Fragment {
|
||||||
|
|
||||||
private TextView tvTimer, tvProgressText, tvGreeting;
|
private TextView tvTimer, tvGreeting, tvMotivationalSubtitle;
|
||||||
private FrameLayout timerBlock;
|
private TextView tvTodayStreak, tvTodayXP, tvTodayTasksCount, tvNoTasksIncentive;
|
||||||
private LinearLayout tasksContainer;
|
private TextView tvDailyRewardGoal;
|
||||||
private ProgressBar pbDailyTasks;
|
private ProgressBar pbDailyTasksProgress;
|
||||||
|
private LinearLayout tasksContainer, miniRankingContainer;
|
||||||
|
private Button btnStartFocus, btnAddTasks, btnClaimReward;
|
||||||
|
|
||||||
private CountDownTimer countDownTimer;
|
private CountDownTimer countDownTimer;
|
||||||
private LinearLayout progressPathContainer;
|
private LinearLayout progressPathContainer;
|
||||||
private List<View> dayNodes = new ArrayList<>();
|
private List<View> dayNodes = new ArrayList<>();
|
||||||
private int currentDayIndex = 0;
|
private int currentDayIndex = 0;
|
||||||
private ListenerRegistration tasksListener, userListener;
|
private ListenerRegistration tasksListener, userListener, rankingListener;
|
||||||
private List<Task> currentTasks = new ArrayList<>();
|
private List<Task> currentTasks = new ArrayList<>();
|
||||||
|
private Task selectedTaskForFocus = 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
|
||||||
|
|
||||||
|
private String[] motivationalQuotes = {
|
||||||
|
"Pronto para vencer hoje?",
|
||||||
|
"Só precisas de começar.",
|
||||||
|
"Um passo de cada vez.",
|
||||||
|
"A consistência é a chave.",
|
||||||
|
"Foca no progresso, não na perfeição.",
|
||||||
|
"Hoje é um ótimo dia para ser produtivo!"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_inicio, container, false);
|
View view = inflater.inflate(R.layout.fragment_inicio, container, false);
|
||||||
|
|
||||||
tvTimer = view.findViewById(R.id.tvTimer);
|
// Header
|
||||||
tvGreeting = view.findViewById(R.id.tvGreeting);
|
tvGreeting = view.findViewById(R.id.tvGreeting);
|
||||||
timerBlock = view.findViewById(R.id.timerBlock);
|
tvMotivationalSubtitle = view.findViewById(R.id.tvMotivationalSubtitle);
|
||||||
|
view.findViewById(R.id.cardProfileAvatar).setOnClickListener(v -> {
|
||||||
|
if (getActivity() instanceof MainActivity) {
|
||||||
|
((MainActivity) getActivity()).findViewById(R.id.nav_profile).performClick();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Today Card
|
||||||
|
tvTodayStreak = view.findViewById(R.id.tvTodayStreak);
|
||||||
|
tvTodayXP = view.findViewById(R.id.tvTodayXP);
|
||||||
|
tvTodayTasksCount = view.findViewById(R.id.tvTodayTasksCount);
|
||||||
|
pbDailyTasksProgress = view.findViewById(R.id.pbDailyTasksProgress);
|
||||||
|
|
||||||
|
// Tasks
|
||||||
tasksContainer = view.findViewById(R.id.tasksContainer);
|
tasksContainer = view.findViewById(R.id.tasksContainer);
|
||||||
tvProgressText = view.findViewById(R.id.tvProgressText);
|
tvNoTasksIncentive = view.findViewById(R.id.tvNoTasksIncentive);
|
||||||
pbDailyTasks = view.findViewById(R.id.pbDailyTasks);
|
btnAddTasks = view.findViewById(R.id.btnAddTasks);
|
||||||
|
btnAddTasks.setOnClickListener(v -> showAddTaskDialog());
|
||||||
|
|
||||||
|
// Focus Mode
|
||||||
|
tvTimer = view.findViewById(R.id.tvTimer);
|
||||||
|
btnStartFocus = view.findViewById(R.id.btnStartFocus);
|
||||||
|
btnStartFocus.setOnClickListener(v -> {
|
||||||
|
if (!isTimerRunning) {
|
||||||
|
if (selectedTaskForFocus == null) {
|
||||||
|
Toast.makeText(getContext(), "Seleciona uma tarefa primeiro!", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startTimer();
|
||||||
|
} else {
|
||||||
|
pauseTimerWithWarning();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Progress
|
||||||
progressPathContainer = view.findViewById(R.id.progressPathContainer);
|
progressPathContainer = view.findViewById(R.id.progressPathContainer);
|
||||||
|
|
||||||
progressPathContainer = view.findViewById(R.id.progressPathContainer);
|
// Mini Ranking
|
||||||
|
miniRankingContainer = view.findViewById(R.id.miniRankingContainer);
|
||||||
|
view.findViewById(R.id.btnViewFullRanking).setOnClickListener(v -> {
|
||||||
|
startActivity(new android.content.Intent(getActivity(), TrophiesActivity.class));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reward
|
||||||
|
tvDailyRewardGoal = view.findViewById(R.id.tvDailyRewardGoal);
|
||||||
|
btnClaimReward = view.findViewById(R.id.btnClaimReward);
|
||||||
|
btnClaimReward.setOnClickListener(v -> claimDailyReward());
|
||||||
|
|
||||||
|
setRandomMotivationalQuote();
|
||||||
initProgressPath();
|
initProgressPath();
|
||||||
startObservingTasks();
|
startObservingTasks();
|
||||||
startObservingUser();
|
startObservingUser();
|
||||||
|
startObservingRanking();
|
||||||
|
|
||||||
View btnStartFocus = view.findViewById(R.id.btnStartFocus);
|
|
||||||
if (btnStartFocus != null) {
|
|
||||||
btnStartFocus.setOnClickListener(v -> {
|
|
||||||
if (!isTimerRunning) {
|
|
||||||
startTimer();
|
|
||||||
((TextView)btnStartFocus).setText("Pausar Foco");
|
|
||||||
} else {
|
|
||||||
pauseTimer();
|
|
||||||
((TextView)btnStartFocus).setText("Continuar Foco");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
view.findViewById(R.id.btnAddTasks).setOnClickListener(v -> showAddTaskDialog());
|
|
||||||
|
|
||||||
View btnStreak = view.findViewById(R.id.btnStreak);
|
|
||||||
if (btnStreak != null) {
|
|
||||||
btnStreak.setOnClickListener(v -> {
|
|
||||||
android.content.Intent intent = new android.content.Intent(getActivity(), StreakActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCountDownText();
|
updateCountDownText();
|
||||||
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setRandomMotivationalQuote() {
|
||||||
|
int index = (int) (Math.random() * motivationalQuotes.length);
|
||||||
|
if (tvMotivationalSubtitle != null) {
|
||||||
|
tvMotivationalSubtitle.setText(motivationalQuotes[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void startObservingTasks() {
|
private void startObservingTasks() {
|
||||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
tasksListener = FirestoreManager.getInstance().observeTasks(currentUser.getUid(), tasks -> {
|
tasksListener = FirestoreManager.getInstance().observeTasks(currentUser.getUid(), tasks -> {
|
||||||
currentTasks = tasks;
|
currentTasks = tasks;
|
||||||
updateTasksUI();
|
updateTasksUI();
|
||||||
|
updateTodayCard();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,6 +151,23 @@ public class InicioFragment extends Fragment {
|
|||||||
if (tvGreeting != null) {
|
if (tvGreeting != null) {
|
||||||
tvGreeting.setText("Olá, " + user.usuario + "!");
|
tvGreeting.setText("Olá, " + user.usuario + "!");
|
||||||
}
|
}
|
||||||
|
if (tvTodayStreak != null) {
|
||||||
|
tvTodayStreak.setText(user.streak + " dias");
|
||||||
|
}
|
||||||
|
if (tvTodayXP != null) {
|
||||||
|
tvTodayXP.setText(String.valueOf(user.xp_hoje));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Reward Button State
|
||||||
|
if (user.tasks_concluidas_hoje >= user.meta_diaria_tarefas) {
|
||||||
|
btnClaimReward.setEnabled(true);
|
||||||
|
btnClaimReward.setAlpha(1.0f);
|
||||||
|
} else {
|
||||||
|
btnClaimReward.setEnabled(false);
|
||||||
|
btnClaimReward.setAlpha(0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDailyReset(user);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,72 +177,144 @@ public class InicioFragment extends Fragment {
|
|||||||
tasksContainer.removeAllViews();
|
tasksContainer.removeAllViews();
|
||||||
|
|
||||||
for (Task task : currentTasks) {
|
for (Task task : currentTasks) {
|
||||||
androidx.cardview.widget.CardView card = new androidx.cardview.widget.CardView(getContext());
|
View taskView = LayoutInflater.from(getContext()).inflate(R.layout.item_task_home, tasksContainer, false);
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
);
|
|
||||||
params.setMargins(0, 0, 0, 16);
|
|
||||||
card.setLayoutParams(params);
|
|
||||||
card.setRadius(getResources().getDimension(R.dimen.radius_md));
|
|
||||||
card.setCardElevation(2f);
|
|
||||||
card.setContentPadding(16, 16, 16, 16);
|
|
||||||
|
|
||||||
LinearLayout layout = new LinearLayout(getContext());
|
TextView tvTitle = taskView.findViewById(R.id.tvTaskTitle);
|
||||||
layout.setOrientation(LinearLayout.HORIZONTAL);
|
TextView tvDuration = taskView.findViewById(R.id.tvTaskDuration);
|
||||||
layout.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
CheckBox cb = taskView.findViewById(R.id.cbTask);
|
||||||
|
Button btnStart = taskView.findViewById(R.id.btnStartTaskFocus);
|
||||||
|
|
||||||
CheckBox cb = new CheckBox(getContext());
|
tvTitle.setText(task.title);
|
||||||
cb.setText(task.title);
|
tvDuration.setText(task.duration + " min");
|
||||||
cb.setTextColor(getResources().getColor(R.color.text_primary));
|
|
||||||
cb.setTextSize(16);
|
|
||||||
cb.setChecked(task.completed);
|
cb.setChecked(task.completed);
|
||||||
|
|
||||||
if (task.completed) {
|
if (task.completed) {
|
||||||
cb.setTextColor(getResources().getColor(R.color.success_green));
|
tvTitle.setPaintFlags(tvTitle.getPaintFlags() | android.graphics.Paint.STRIKE_THRU_TEXT_FLAG);
|
||||||
|
tvTitle.setTextColor(getResources().getColor(R.color.text_secondary));
|
||||||
|
btnStart.setEnabled(false);
|
||||||
|
btnStart.setAlpha(0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
cb.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
cb.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
task.completed = isChecked;
|
if (isChecked && !task.completed) {
|
||||||
FirestoreManager.getInstance().updateTask(task);
|
completeTask(task);
|
||||||
// The observer will trigger updateTasksUI again, so we don't need manual UI update here
|
|
||||||
|
|
||||||
if (isChecked) {
|
|
||||||
addXP(task.xpReward);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
layout.addView(cb);
|
btnStart.setOnClickListener(v -> {
|
||||||
card.addView(layout);
|
selectedTaskForFocus = task;
|
||||||
tasksContainer.addView(card);
|
timeLeftInMillis = task.duration * 60 * 1000;
|
||||||
|
updateCountDownText();
|
||||||
|
Toast.makeText(getContext(), "Tarefa selecionada: " + task.title, Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
// Highlight selection
|
||||||
|
for (int i = 0; i < tasksContainer.getChildCount(); i++) {
|
||||||
|
tasksContainer.getChildAt(i).setBackground(null);
|
||||||
|
}
|
||||||
|
taskView.setBackgroundResource(R.drawable.task_selected_bg);
|
||||||
|
});
|
||||||
|
|
||||||
|
taskView.setOnLongClickListener(v -> {
|
||||||
|
showDeleteConfirmDialog(task);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
tasksContainer.addView(taskView);
|
||||||
}
|
}
|
||||||
updateProgress();
|
updateTodayCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addXP(int amount) {
|
private void addXP(int amount) {
|
||||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("xp", com.google.firebase.firestore.FieldValue.increment(amount));
|
||||||
|
updates.put("xp_hoje", com.google.firebase.firestore.FieldValue.increment(amount));
|
||||||
|
|
||||||
|
// Check Level Up logic inside observe once
|
||||||
com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
||||||
.collection("users").document(currentUser.getUid())
|
.collection("users").document(currentUser.getUid()).get()
|
||||||
.update("xp", com.google.firebase.firestore.FieldValue.increment(amount));
|
.addOnSuccessListener(snapshot -> {
|
||||||
|
Usuario user = snapshot.toObject(Usuario.class);
|
||||||
|
if (user != null) {
|
||||||
|
int currentLevel = user.level;
|
||||||
|
int nextLevelThreshold = currentLevel * 500;
|
||||||
|
if (user.xp + amount >= nextLevelThreshold) {
|
||||||
|
updates.put("level", currentLevel + 1);
|
||||||
|
showLevelUpAnimation(currentLevel + 1);
|
||||||
|
}
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUser.getUid(), updates);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateProgress() {
|
private void updateTodayCard() {
|
||||||
int total = currentTasks.size();
|
int total = currentTasks.size();
|
||||||
int completed = 0;
|
int completed = 0;
|
||||||
for (Task task : currentTasks) {
|
for (Task task : currentTasks) {
|
||||||
if (task.completed) completed++;
|
if (task.completed) completed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tvProgressText != null) {
|
if (tvTodayTasksCount != null) {
|
||||||
tvProgressText.setText(completed + " de " + total + " concluídos");
|
tvTodayTasksCount.setText(completed + "/" + (total == 0 ? 3 : total));
|
||||||
}
|
}
|
||||||
if (pbDailyTasks != null && total > 0) {
|
|
||||||
int progress = (completed * 100) / total;
|
if (pbDailyTasksProgress != null) {
|
||||||
pbDailyTasks.setProgress(progress);
|
int progress = (total == 0) ? 0 : (completed * 100) / total;
|
||||||
updatePathProgress(progress);
|
pbDailyTasksProgress.setProgress(progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (total == 0) {
|
||||||
|
tvNoTasksIncentive.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
tvNoTasksIncentive.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDailyReset(Usuario user) {
|
||||||
|
String today = new java.text.SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new java.util.Date());
|
||||||
|
if (!today.equals(user.last_active_date)) {
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
if (user.tasks_concluidas_hoje == 0 && !user.last_active_date.isEmpty()) {
|
||||||
|
updates.put("streak", 0);
|
||||||
|
}
|
||||||
|
updates.put("xp_hoje", 0);
|
||||||
|
updates.put("tasks_concluidas_hoje", 0);
|
||||||
|
updates.put("tempo_foco_hoje", 0);
|
||||||
|
updates.put("last_active_date", today);
|
||||||
|
FirestoreManager.getInstance().updateUserStats(user.id_usuario, updates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startObservingRanking() {
|
||||||
|
rankingListener = com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
||||||
|
.collection("users")
|
||||||
|
.orderBy("xp_hoje", com.google.firebase.firestore.Query.Direction.DESCENDING)
|
||||||
|
.limit(3)
|
||||||
|
.addSnapshotListener((snapshots, e) -> {
|
||||||
|
if (e != null || snapshots == null) return;
|
||||||
|
miniRankingContainer.removeAllViews();
|
||||||
|
int rank = 1;
|
||||||
|
for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
|
||||||
|
Usuario u = doc.toObject(Usuario.class);
|
||||||
|
if (u != null) {
|
||||||
|
addMiniRankingItem(rank++, u.usuario, u.xp_hoje);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMiniRankingItem(int rank, String name, int xp) {
|
||||||
|
View item = getLayoutInflater().inflate(android.R.layout.simple_list_item_2, miniRankingContainer, false);
|
||||||
|
TextView tv1 = item.findViewById(android.R.id.text1);
|
||||||
|
TextView tv2 = item.findViewById(android.R.id.text2);
|
||||||
|
tv1.setText(rank + ". " + name);
|
||||||
|
tv1.setTextSize(14);
|
||||||
|
tv1.setTextColor(getResources().getColor(R.color.text_primary));
|
||||||
|
tv2.setText(xp + " XP hoje");
|
||||||
|
tv2.setTextSize(12);
|
||||||
|
miniRankingContainer.addView(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initProgressPath() {
|
private void initProgressPath() {
|
||||||
@@ -246,20 +374,92 @@ public class InicioFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void completeTask(Task task) {
|
||||||
|
task.completed = true;
|
||||||
|
FirestoreManager.getInstance().updateTask(task);
|
||||||
|
addXP(30);
|
||||||
|
updateUserTaskCount();
|
||||||
|
boolean allDone = true;
|
||||||
|
for (Task t : currentTasks) {
|
||||||
|
if (!t.completed && !t.id.equals(task.id)) {
|
||||||
|
allDone = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allDone && currentTasks.size() > 0) {
|
||||||
|
addXP(20);
|
||||||
|
Toast.makeText(getContext(), "Todas as tarefas concluídas! +20 XP Bónus", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
triggerVibration();
|
||||||
|
showXpPopup("+30 XP");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUserTaskCount() {
|
||||||
|
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||||
|
if (currentUser != null) {
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("tasks_concluidas_hoje", com.google.firebase.firestore.FieldValue.increment(1));
|
||||||
|
updates.put("total_tasks_concluidas", com.google.firebase.firestore.FieldValue.increment(1));
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUser.getUid(), updates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLevelUpAnimation(int newLevel) {
|
||||||
|
if (getContext() == null) return;
|
||||||
|
View dialogView = getLayoutInflater().inflate(R.layout.dialog_level_up, null);
|
||||||
|
TextView tvNewLevel = dialogView.findViewById(R.id.tvNewLevel);
|
||||||
|
tvNewLevel.setText(String.valueOf(newLevel));
|
||||||
|
|
||||||
|
androidx.appcompat.app.AlertDialog dialog = new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
|
||||||
|
.setView(dialogView)
|
||||||
|
.setPositiveButton("Incrível!", null)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
triggerVibration();
|
||||||
|
NotificationHelper.showNotification(getContext(), "🎉 SUBISTE DE NÍVEL!", "Chegaste ao nível " + newLevel + "! Continua assim.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showXpPopup(String text) {
|
||||||
|
Toast.makeText(getContext(), text, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerVibration() {
|
||||||
|
android.os.Vibrator v = (android.os.Vibrator) getContext().getSystemService(android.content.Context.VIBRATOR_SERVICE);
|
||||||
|
if (v != null) {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||||
|
v.vibrate(android.os.VibrationEffect.createOneShot(100, android.os.VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
|
} else {
|
||||||
|
v.vibrate(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void claimDailyReward() {
|
||||||
|
addXP(100);
|
||||||
|
btnClaimReward.setEnabled(false);
|
||||||
|
btnClaimReward.setText("Resgatado");
|
||||||
|
Toast.makeText(getContext(), "Recompensa diária resgatada! +100 XP", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDeleteConfirmDialog(Task task) {
|
||||||
|
new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
|
||||||
|
.setTitle("Eliminar Tarefa")
|
||||||
|
.setMessage("Tens a certeza que queres eliminar esta tarefa?")
|
||||||
|
.setNegativeButton("Cancelar", null)
|
||||||
|
.setPositiveButton("Eliminar", (dialog, which) -> {
|
||||||
|
FirestoreManager.getInstance().deleteTask(task.id);
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
private void updatePathProgress(int progress) {
|
private void updatePathProgress(int progress) {
|
||||||
if (currentDayIndex >= dayNodes.size()) return;
|
if (currentDayIndex >= dayNodes.size()) return;
|
||||||
|
|
||||||
View todayNode = dayNodes.get(currentDayIndex);
|
View todayNode = dayNodes.get(currentDayIndex);
|
||||||
ProgressBar nodeProgress = todayNode.findViewById(R.id.nodeProgress);
|
ProgressBar nodeProgress = todayNode.findViewById(R.id.nodeProgress);
|
||||||
View nodeCircle = todayNode.findViewById(R.id.nodeCircle);
|
View nodeCircle = todayNode.findViewById(R.id.nodeCircle);
|
||||||
TextView nodeDayInitial = todayNode.findViewById(R.id.nodeDayInitial);
|
if (nodeProgress != null) nodeProgress.setProgress(progress);
|
||||||
|
|
||||||
if (nodeProgress != null) {
|
|
||||||
nodeProgress.setProgress(progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progress == 100) {
|
if (progress == 100) {
|
||||||
// Task completion animation
|
|
||||||
nodeCircle.getBackground().setTint(getResources().getColor(R.color.success_green));
|
nodeCircle.getBackground().setTint(getResources().getColor(R.color.success_green));
|
||||||
triggerSuccessAnimation(todayNode);
|
triggerSuccessAnimation(todayNode);
|
||||||
} else {
|
} else {
|
||||||
@@ -282,31 +482,47 @@ public class InicioFragment extends Fragment {
|
|||||||
|
|
||||||
|
|
||||||
private void startTimer() {
|
private void startTimer() {
|
||||||
|
if (selectedTaskForFocus == null) return;
|
||||||
countDownTimer = new CountDownTimer(timeLeftInMillis, 1000) {
|
countDownTimer = new CountDownTimer(timeLeftInMillis, 1000) {
|
||||||
@Override
|
@Override
|
||||||
public void onTick(long millisUntilFinished) {
|
public void onTick(long millisUntilFinished) {
|
||||||
timeLeftInMillis = millisUntilFinished;
|
timeLeftInMillis = millisUntilFinished;
|
||||||
updateCountDownText();
|
updateCountDownText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFinish() {
|
public void onFinish() {
|
||||||
isTimerRunning = false;
|
isTimerRunning = false;
|
||||||
if(getContext() != null) {
|
btnStartFocus.setText("Começar Foco");
|
||||||
Toast.makeText(getContext(), "Foco concluído! +50 XP", Toast.LENGTH_LONG).show();
|
completeTask(selectedTaskForFocus);
|
||||||
addXP(50);
|
addXP(50);
|
||||||
|
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||||
|
if (currentUser != null) {
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
|
updates.put("tempo_foco_total", com.google.firebase.firestore.FieldValue.increment(selectedTaskForFocus.duration));
|
||||||
|
updates.put("tempo_foco_hoje", com.google.firebase.firestore.FieldValue.increment(selectedTaskForFocus.duration));
|
||||||
|
updates.put("sessoes_foco_completas", com.google.firebase.firestore.FieldValue.increment(1));
|
||||||
|
FirestoreManager.getInstance().updateUserStats(currentUser.getUid(), updates);
|
||||||
}
|
}
|
||||||
|
selectedTaskForFocus = null;
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
|
|
||||||
isTimerRunning = true;
|
isTimerRunning = true;
|
||||||
|
btnStartFocus.setText("Pausar Foco");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pauseTimer() {
|
private void pauseTimerWithWarning() {
|
||||||
if (countDownTimer != null) {
|
new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
|
||||||
countDownTimer.cancel();
|
.setTitle("Sair do Foco?")
|
||||||
}
|
.setMessage("Se saíres agora, não ganharás o XP de foco.")
|
||||||
isTimerRunning = false;
|
.setNegativeButton("Continuar Focado", null)
|
||||||
|
.setPositiveButton("Sair", (dialog, which) -> {
|
||||||
|
if (countDownTimer != null) countDownTimer.cancel();
|
||||||
|
isTimerRunning = false;
|
||||||
|
btnStartFocus.setText("Começar Foco");
|
||||||
|
timeLeftInMillis = 25 * 60 * 1000;
|
||||||
|
updateCountDownText();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -321,85 +537,47 @@ public class InicioFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
if (tasksListener != null) {
|
if (tasksListener != null) tasksListener.remove();
|
||||||
tasksListener.remove();
|
if (userListener != null) userListener.remove();
|
||||||
}
|
if (rankingListener != null) rankingListener.remove();
|
||||||
if (userListener != null) {
|
if (countDownTimer != null) countDownTimer.cancel();
|
||||||
userListener.remove();
|
|
||||||
}
|
|
||||||
pauseTimer(); // Parar o timer se a view for destruída
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showAddTaskDialog() {
|
private void showAddTaskDialog() {
|
||||||
if (getContext() == null) return;
|
View dialogView = getLayoutInflater().inflate(R.layout.dialog_add_task, null);
|
||||||
|
android.widget.EditText etTitle = dialogView.findViewById(R.id.etTaskTitle);
|
||||||
// Build input field
|
android.widget.EditText etDuration = dialogView.findViewById(R.id.etTaskDuration);
|
||||||
android.widget.EditText editText = new android.widget.EditText(getContext());
|
|
||||||
editText.setHint("Escreve o teu desafio…");
|
|
||||||
editText.setInputType(android.text.InputType.TYPE_CLASS_TEXT
|
|
||||||
| android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
|
|
||||||
editText.setTextSize(16);
|
|
||||||
editText.setSingleLine(false);
|
|
||||||
editText.setMaxLines(3);
|
|
||||||
|
|
||||||
// Wrap with padding
|
|
||||||
android.widget.FrameLayout container = new android.widget.FrameLayout(getContext());
|
|
||||||
android.widget.FrameLayout.LayoutParams lp = new android.widget.FrameLayout.LayoutParams(
|
|
||||||
android.widget.FrameLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
int dp24 = (int) (24 * getResources().getDisplayMetrics().density);
|
|
||||||
lp.setMargins(dp24, dp24 / 2, dp24, 0);
|
|
||||||
editText.setLayoutParams(lp);
|
|
||||||
container.addView(editText);
|
|
||||||
|
|
||||||
androidx.appcompat.app.AlertDialog dialog = new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
|
androidx.appcompat.app.AlertDialog dialog = new com.google.android.material.dialog.MaterialAlertDialogBuilder(getContext())
|
||||||
.setTitle("Novo Desafio")
|
.setTitle("Nova Tarefa")
|
||||||
.setView(container)
|
.setView(dialogView)
|
||||||
.setNegativeButton("Cancelar", null)
|
.setNegativeButton("Cancelar", null)
|
||||||
.setPositiveButton("Adicionar", null)
|
.setPositiveButton("Guardar", null)
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
|
|
||||||
dialog.setOnShowListener(d -> {
|
dialog.setOnShowListener(d -> {
|
||||||
// Style buttons
|
dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||||
android.widget.Button btnAdd = dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE);
|
String title = etTitle.getText().toString().trim();
|
||||||
android.widget.Button btnCancel = dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEGATIVE);
|
String durationStr = etDuration.getText().toString().trim();
|
||||||
if (btnAdd != null) btnAdd.setTextColor(getResources().getColor(R.color.primary_purple));
|
if (title.isEmpty()) {
|
||||||
if (btnCancel != null) btnCancel.setTextColor(getResources().getColor(R.color.text_secondary));
|
etTitle.setError("Obrigatório");
|
||||||
|
return;
|
||||||
// Override positive so it validates before dismissing
|
}
|
||||||
if (btnAdd != null) {
|
int duration = 25;
|
||||||
btnAdd.setOnClickListener(v -> {
|
if (!durationStr.isEmpty()) duration = Integer.parseInt(durationStr);
|
||||||
String title = editText.getText().toString().trim();
|
saveNewTask(title, duration);
|
||||||
if (title.isEmpty()) {
|
dialog.dismiss();
|
||||||
editText.setError("Escreve um desafio");
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
saveNewTask(title);
|
|
||||||
dialog.dismiss();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
editText.requestFocus();
|
|
||||||
if (dialog.getWindow() != null) {
|
|
||||||
dialog.getWindow().setSoftInputMode(
|
|
||||||
android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveNewTask(String title) {
|
private void saveNewTask(String title, int duration) {
|
||||||
com.google.firebase.auth.FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||||
if (currentUser == null) return;
|
if (currentUser == null) return;
|
||||||
|
|
||||||
String uid = currentUser.getUid();
|
String uid = currentUser.getUid();
|
||||||
String taskId = com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
String taskId = com.google.firebase.firestore.FirebaseFirestore.getInstance().collection("tasks").document().getId();
|
||||||
.collection("tasks").document().getId();
|
Task task = new Task(taskId, title, 30, duration, uid);
|
||||||
|
|
||||||
Task task = new Task(taskId, title, 10, uid);
|
|
||||||
FirestoreManager.getInstance().addTask(task);
|
FirestoreManager.getInstance().addTask(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,71 +16,153 @@ import com.google.firebase.firestore.ListenerRegistration;
|
|||||||
|
|
||||||
public class ProfileFragment extends Fragment {
|
public class ProfileFragment extends Fragment {
|
||||||
|
|
||||||
private TextView tvUsername, tvHandle, tvStreakValue, tvTotalXP, tvLeagueName, tvAchievementsCount;
|
private TextView tvProfileName, tvProfileTitle, tvProfileBio;
|
||||||
private ListenerRegistration userListener;
|
private GridLayout badgesGrid;
|
||||||
|
private LinearLayout friendsListContainer;
|
||||||
|
private ListenerRegistration userListener, friendsListener;
|
||||||
|
private View view;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_profile, container, false);
|
View view = inflater.inflate(R.layout.fragment_profile, container, false);
|
||||||
|
|
||||||
ImageButton btnSettings = view.findViewById(R.id.btnSettings);
|
initViews(view);
|
||||||
btnSettings.setOnClickListener(v -> {
|
setupListeners(view);
|
||||||
Intent intent = new Intent(getActivity(), SettingsActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
|
|
||||||
View btnInviteCard = view.findViewById(R.id.btnInviteCard);
|
|
||||||
btnInviteCard.setOnClickListener(v -> openFindFriends());
|
|
||||||
|
|
||||||
View btnInviteFriends = view.findViewById(R.id.btnInviteFriends);
|
|
||||||
btnInviteFriends.setOnClickListener(v -> openFindFriends());
|
|
||||||
|
|
||||||
// Initialize UI components
|
|
||||||
tvUsername = view.findViewById(R.id.tvUsername);
|
|
||||||
tvHandle = view.findViewById(R.id.tvHandle);
|
|
||||||
tvStreakValue = view.findViewById(R.id.tvStreakValue);
|
|
||||||
tvTotalXP = view.findViewById(R.id.tvTotalXP);
|
|
||||||
tvLeagueName = view.findViewById(R.id.tvLeagueName);
|
|
||||||
tvAchievementsCount = view.findViewById(R.id.tvAchievementsCount);
|
|
||||||
View cardLeague = view.findViewById(R.id.cardLeague);
|
|
||||||
cardLeague.setOnClickListener(v -> {
|
|
||||||
Intent intent = new Intent(getActivity(), TrophiesActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
|
|
||||||
startObservingUser();
|
startObservingUser();
|
||||||
|
startObservingFriends();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initViews(View view) {
|
||||||
|
tvProfileName = view.findViewById(R.id.tvProfileName);
|
||||||
|
tvProfileTitle = view.findViewById(R.id.tvProfileTitle);
|
||||||
|
tvProfileBio = view.findViewById(R.id.tvProfileBio);
|
||||||
|
badgesGrid = view.findViewById(R.id.badgesGrid);
|
||||||
|
friendsListContainer = view.findViewById(R.id.friendsListContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupListeners(View view) {
|
||||||
|
view.findViewById(R.id.btnEditProfile).setOnClickListener(v -> {
|
||||||
|
startActivity(new Intent(getActivity(), SettingsActivity.class));
|
||||||
|
});
|
||||||
|
|
||||||
|
view.findViewById(R.id.btnManageFriends).setOnClickListener(v -> {
|
||||||
|
startActivity(new Intent(getActivity(), FindFriendsActivity.class));
|
||||||
|
});
|
||||||
|
|
||||||
|
view.findViewById(R.id.btnViewStatsDetails).setOnClickListener(v -> {
|
||||||
|
startActivity(new Intent(getActivity(), StatisticsActivity.class));
|
||||||
|
});
|
||||||
|
|
||||||
|
view.findViewById(R.id.btnLogout).setOnClickListener(v -> {
|
||||||
|
AuthManager.getInstance().signOut();
|
||||||
|
Intent intent = new Intent(getActivity(), LoginActivity.class);
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void startObservingUser() {
|
private void startObservingUser() {
|
||||||
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
FirebaseUser currentUser = AuthManager.getInstance().getCurrentUser();
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
userListener = FirestoreManager.getInstance().observeUser(currentUser.getUid(), this::updateUI);
|
userListener = FirestoreManager.getInstance().observeUser(currentUser.getUid(), user -> {
|
||||||
|
if (getContext() == null || view == null) return;
|
||||||
|
updateStats(view, user);
|
||||||
|
tvProfileName.setText(user.usuario);
|
||||||
|
tvProfileBio.setText(user.bio.isEmpty() ? "Sem bio definida." : user.bio);
|
||||||
|
tvProfileTitle.setText(determineTitle(user));
|
||||||
|
renderBadges(user);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUI(Usuario user) {
|
private void updateStats(View view, Usuario user) {
|
||||||
if (getContext() == null) return;
|
setStat(view.findViewById(R.id.statXP), "⚡", String.valueOf(user.xp), "Total XP");
|
||||||
tvUsername.setText(user.usuario);
|
setStat(view.findViewById(R.id.statStreak), "🔥", String.valueOf(user.streak), "Ofensiva");
|
||||||
tvHandle.setText(user.handle);
|
setStat(view.findViewById(R.id.statLevel), "⭐", String.valueOf(user.level), "Nível");
|
||||||
tvStreakValue.setText(String.valueOf(user.streak));
|
setStat(view.findViewById(R.id.statTasks), "✅", String.valueOf(user.total_tasks_concluidas), "Tarefas");
|
||||||
tvTotalXP.setText(String.valueOf(user.xp));
|
setStat(view.findViewById(R.id.statFocusTime), "⏱️", user.tempo_foco_total + "m", "Foco");
|
||||||
tvLeagueName.setText(user.league);
|
setStat(view.findViewById(R.id.statSessions), "🎯", String.valueOf(user.sessoes_foco_completas), "Sessões");
|
||||||
tvAchievementsCount.setText(String.valueOf(user.achievementsCount));
|
}
|
||||||
|
|
||||||
|
private void setStat(View statView, String icon, String value, String label) {
|
||||||
|
if (statView == null) return;
|
||||||
|
((TextView) statView.findViewById(R.id.tvStatIcon)).setText(icon);
|
||||||
|
((TextView) statView.findViewById(R.id.tvStatValue)).setText(value);
|
||||||
|
((TextView) statView.findViewById(R.id.tvStatLabel)).setText(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String determineTitle(Usuario user) {
|
||||||
|
if (user.level > 50) return "Mestre do Foco";
|
||||||
|
if (user.level > 20) return "Guerreiro Produtivo";
|
||||||
|
if (user.level > 10) return "Focado Regular";
|
||||||
|
return "Iniciante de Foco";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderBadges(Usuario user) {
|
||||||
|
badgesGrid.removeAllViews();
|
||||||
|
if (user.streak >= 7) addBadge("🔥", "Semana Ativa");
|
||||||
|
if (user.total_tasks_concluidas >= 50) addBadge("🏆", "Executor");
|
||||||
|
if (user.tempo_foco_total >= 600) addBadge("🧘", "Zen");
|
||||||
|
if (user.level >= 5) addBadge("⭐", "Nível 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBadge(String emoji, String name) {
|
||||||
|
LinearLayout badge = new LinearLayout(getContext());
|
||||||
|
badge.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
badge.setGravity(android.view.Gravity.CENTER);
|
||||||
|
badge.setPadding(8, 8, 8, 8);
|
||||||
|
|
||||||
|
TextView tvEmoji = new TextView(getContext());
|
||||||
|
tvEmoji.setText(emoji);
|
||||||
|
tvEmoji.setTextSize(24);
|
||||||
|
|
||||||
|
TextView tvName = new TextView(getContext());
|
||||||
|
tvName.setText(name);
|
||||||
|
tvName.setTextSize(8);
|
||||||
|
tvName.setGravity(android.view.Gravity.CENTER);
|
||||||
|
|
||||||
|
badge.addView(tvEmoji);
|
||||||
|
badge.addView(tvName);
|
||||||
|
badgesGrid.addView(badge);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startObservingFriends() {
|
||||||
|
friendsListener = com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
||||||
|
.collection("users")
|
||||||
|
.limit(3)
|
||||||
|
.addSnapshotListener((snapshots, e) -> {
|
||||||
|
if (e != null || snapshots == null) return;
|
||||||
|
friendsListContainer.removeAllViews();
|
||||||
|
for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
|
||||||
|
Usuario f = doc.toObject(Usuario.class);
|
||||||
|
if (f != null && !f.id_usuario.equals(AuthManager.getInstance().getCurrentUser().getUid())) {
|
||||||
|
addFriendItem(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFriendItem(Usuario friend) {
|
||||||
|
View view = getLayoutInflater().inflate(R.layout.item_ranking_user, friendsListContainer, false);
|
||||||
|
((TextView) view.findViewById(R.id.tvRankingName)).setText(friend.usuario);
|
||||||
|
((TextView) view.findViewById(R.id.tvRankingXP)).setText(friend.xp + " XP");
|
||||||
|
view.findViewById(R.id.ivRankingTrend).setVisibility(View.GONE);
|
||||||
|
friendsListContainer.addView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
this.view = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
if (userListener != null) {
|
if (userListener != null) userListener.remove();
|
||||||
userListener.remove();
|
if (friendsListener != null) friendsListener.remove();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openFindFriends() {
|
|
||||||
Intent intent = new Intent(getActivity(), FindFriendsActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,155 +15,101 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import com.google.android.material.switchmaterial.SwitchMaterial;
|
import com.google.android.material.switchmaterial.SwitchMaterial;
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
import com.google.firebase.auth.FirebaseUser;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class SettingsActivity extends AppCompatActivity {
|
public class SettingsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private ImageButton btnBack;
|
|
||||||
private SwitchMaterial switchDarkMode, switchPrivacy, switchNotifications;
|
|
||||||
private TextView tvEmail, btnChangePassword;
|
|
||||||
private Spinner spinnerLanguage;
|
|
||||||
private View btnLogout;
|
|
||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
private static final String PREFS_NAME = "FluxupSettings";
|
private static final String PREFS_NAME = "FluxupSettings";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
sharedPreferences = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
sharedPreferences = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
// Aplicar o idioma guardado antes de carregar o layout
|
|
||||||
String lang = sharedPreferences.getString("language", "pt");
|
|
||||||
updateLocaleSilent(lang);
|
|
||||||
|
|
||||||
// Aplicar tema guardado
|
|
||||||
applyTheme(sharedPreferences.getBoolean("darkMode", false));
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_settings);
|
setContentView(R.layout.activity_settings);
|
||||||
|
|
||||||
initViews();
|
initSettings();
|
||||||
setupListeners();
|
findViewById(R.id.btnBack).setOnClickListener(v -> finish());
|
||||||
loadSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyTheme(boolean isDarkMode) {
|
private void initSettings() {
|
||||||
if (isDarkMode) {
|
// --- CONTA ---
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
setupClickable(R.id.settingEmail, "Email", FirebaseAuth.getInstance().getCurrentUser().getEmail(), null);
|
||||||
} else {
|
setupClickable(R.id.settingPassword, "Alterar Palavra-passe", "********", v -> resetPassword());
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
setupSwitch(R.id.settingPublicProfile, "Perfil Público", "public_profile", true);
|
||||||
|
|
||||||
|
// --- APARÊNCIA ---
|
||||||
|
setupSwitch(R.id.settingDarkMode, "Modo Escuro", "darkMode", false, (v, isChecked) -> {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(isChecked ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
});
|
||||||
|
setupClickable(R.id.settingThemeColor, "Cor do Tema", "Roxo (Padrão)", null);
|
||||||
|
|
||||||
|
// --- NOTIFICAÇÕES ---
|
||||||
|
setupSwitch(R.id.settingDailyReminders, "Lembretes Diários", "daily_reminders", true);
|
||||||
|
setupSwitch(R.id.settingAntiProcrastination, "Anti-Procrastinação", "anti_procrastination", true);
|
||||||
|
|
||||||
|
// --- FOCO ---
|
||||||
|
setupClickable(R.id.settingFocusDuration, "Duração do Foco", sharedPreferences.getInt("focus_duration", 25) + " min", v -> showDurationDialog("focus_duration", "Duração do Foco", 25));
|
||||||
|
setupClickable(R.id.settingBreakDuration, "Duração da Pausa", sharedPreferences.getInt("break_duration", 5) + " min", v -> showDurationDialog("break_duration", "Duração da Pausa", 5));
|
||||||
|
|
||||||
|
// --- PRIVACIDADE ---
|
||||||
|
setupSwitch(R.id.settingIncognito, "Modo Incógnito", "incognito", false);
|
||||||
|
setupClickable(R.id.settingExportData, "Exportar Dados", "JSON/CSV", null);
|
||||||
|
|
||||||
|
findViewById(R.id.btnDeleteAccount).setOnClickListener(v -> {
|
||||||
|
Toast.makeText(this, "Funcionalidade disponível em breve", Toast.LENGTH_SHORT).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupClickable(int id, String title, String value, View.OnClickListener listener) {
|
||||||
|
View view = findViewById(id);
|
||||||
|
if (view == null) return;
|
||||||
|
((TextView) view.findViewById(R.id.tvSettingTitle)).setText(title);
|
||||||
|
((TextView) view.findViewById(R.id.tvSettingValue)).setText(value);
|
||||||
|
if (listener != null) view.setOnClickListener(listener);
|
||||||
|
else view.findViewById(R.id.ivChevron).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSwitch(int id, String title, String key, boolean defValue) {
|
||||||
|
setupSwitch(id, title, key, defValue, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSwitch(int id, String title, String key, boolean defValue, android.widget.CompoundButton.OnCheckedChangeListener extraListener) {
|
||||||
|
View view = findViewById(id);
|
||||||
|
if (view == null) return;
|
||||||
|
((TextView) view.findViewById(R.id.tvSettingTitle)).setText(title);
|
||||||
|
SwitchMaterial sw = view.findViewById(R.id.switchSetting);
|
||||||
|
sw.setChecked(sharedPreferences.getBoolean(key, defValue));
|
||||||
|
sw.setOnCheckedChangeListener((v, isChecked) -> {
|
||||||
|
sharedPreferences.edit().putBoolean(key, isChecked).apply();
|
||||||
|
if (extraListener != null) extraListener.onCheckedChanged(v, isChecked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetPassword() {
|
||||||
|
String email = FirebaseAuth.getInstance().getCurrentUser().getEmail();
|
||||||
|
if (email != null) {
|
||||||
|
FirebaseAuth.getInstance().sendPasswordResetEmail(email)
|
||||||
|
.addOnSuccessListener(aVoid -> Toast.makeText(this, "Email enviado!", Toast.LENGTH_SHORT).show());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initViews() {
|
private void showDurationDialog(String key, String title, int current) {
|
||||||
btnBack = findViewById(R.id.btnBack);
|
EditText input = new EditText(this);
|
||||||
switchDarkMode = findViewById(R.id.switchDarkMode);
|
input.setInputType(android.text.InputType.TYPE_CLASS_NUMBER);
|
||||||
switchPrivacy = findViewById(R.id.switchPrivacy);
|
input.setText(String.valueOf(sharedPreferences.getInt(key, current)));
|
||||||
switchNotifications = findViewById(R.id.switchNotifications);
|
|
||||||
tvEmail = findViewById(R.id.tvEmail);
|
|
||||||
btnChangePassword = findViewById(R.id.btnChangePassword);
|
|
||||||
spinnerLanguage = findViewById(R.id.spinnerLanguage);
|
|
||||||
btnLogout = findViewById(R.id.btnLogout);
|
|
||||||
|
|
||||||
// Configurar Spinner de Idiomas
|
new AlertDialog.Builder(this)
|
||||||
String[] languages = {"Português", "English", "Español"};
|
.setTitle(title)
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, languages);
|
.setView(input)
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
.setPositiveButton("Guardar", (dialog, which) -> {
|
||||||
spinnerLanguage.setAdapter(adapter);
|
int val = Integer.parseInt(input.getText().toString());
|
||||||
|
sharedPreferences.edit().putInt(key, val).apply();
|
||||||
// Mostrar email do utilizador
|
recreate();
|
||||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
})
|
||||||
if (user != null) {
|
.setNegativeButton("Cancelar", null)
|
||||||
tvEmail.setText(user.getEmail());
|
.show();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupListeners() {
|
|
||||||
btnBack.setOnClickListener(v -> finish());
|
|
||||||
|
|
||||||
switchDarkMode.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
|
||||||
applyTheme(isChecked);
|
|
||||||
saveSetting("darkMode", isChecked);
|
|
||||||
});
|
|
||||||
|
|
||||||
switchPrivacy.setOnCheckedChangeListener((buttonView, isChecked) -> saveSetting("privacy", isChecked));
|
|
||||||
switchNotifications.setOnCheckedChangeListener((buttonView, isChecked) -> saveSetting("notifications", isChecked));
|
|
||||||
|
|
||||||
btnLogout.setOnClickListener(v -> {
|
|
||||||
FirebaseAuth.getInstance().signOut();
|
|
||||||
Intent intent = new Intent(SettingsActivity.this, LoginActivity.class);
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
||||||
startActivity(intent);
|
|
||||||
finish();
|
|
||||||
});
|
|
||||||
|
|
||||||
spinnerLanguage.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
String selectedLang = "pt";
|
|
||||||
if (position == 1) selectedLang = "en";
|
|
||||||
else if (position == 2) selectedLang = "es";
|
|
||||||
|
|
||||||
String currentLang = sharedPreferences.getString("language", "pt");
|
|
||||||
if (!selectedLang.equals(currentLang)) {
|
|
||||||
saveSetting("language", selectedLang);
|
|
||||||
updateLocale(selectedLang);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
btnChangePassword.setOnClickListener(v -> {
|
|
||||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
|
||||||
if (user != null && user.getEmail() != null) {
|
|
||||||
FirebaseAuth.getInstance().sendPasswordResetEmail(user.getEmail())
|
|
||||||
.addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
Toast.makeText(this, "Email de redefinição enviado!", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadSettings() {
|
|
||||||
switchDarkMode.setChecked(sharedPreferences.getBoolean("darkMode", false));
|
|
||||||
switchPrivacy.setChecked(sharedPreferences.getBoolean("privacy", false));
|
|
||||||
switchNotifications.setChecked(sharedPreferences.getBoolean("notifications", true));
|
|
||||||
|
|
||||||
String lang = sharedPreferences.getString("language", "pt");
|
|
||||||
if (lang.equals("en")) spinnerLanguage.setSelection(1, false);
|
|
||||||
else if (lang.equals("es")) spinnerLanguage.setSelection(2, false);
|
|
||||||
else spinnerLanguage.setSelection(0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateLocaleSilent(String langCode) {
|
|
||||||
Locale locale = new Locale(langCode);
|
|
||||||
Locale.setDefault(locale);
|
|
||||||
Resources resources = getResources();
|
|
||||||
Configuration config = resources.getConfiguration();
|
|
||||||
config.setLocale(locale);
|
|
||||||
resources.updateConfiguration(config, resources.getDisplayMetrics());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateLocale(String langCode) {
|
|
||||||
updateLocaleSilent(langCode);
|
|
||||||
Intent intent = new Intent(this, SettingsActivity.class);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
startActivity(intent);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveSetting(String key, boolean value) {
|
|
||||||
sharedPreferences.edit().putBoolean(key, value).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveSetting(String key, String value) {
|
|
||||||
sharedPreferences.edit().putString(key, value).apply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,18 @@ public class Task {
|
|||||||
public String title;
|
public String title;
|
||||||
public boolean completed;
|
public boolean completed;
|
||||||
public int xpReward;
|
public int xpReward;
|
||||||
|
public int duration; // em minutos
|
||||||
public String userId;
|
public String userId;
|
||||||
|
public Long completedDate;
|
||||||
|
|
||||||
public Task() {}
|
public Task() {}
|
||||||
|
|
||||||
public Task(String id, String title, int xpReward, String userId) {
|
public Task(String id, String title, int xpReward, int duration, String userId) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.completed = false;
|
this.completed = false;
|
||||||
this.xpReward = xpReward;
|
this.xpReward = xpReward;
|
||||||
|
this.duration = duration;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,16 @@ import com.google.firebase.firestore.ListenerRegistration;
|
|||||||
|
|
||||||
public class TrophiesActivity extends AppCompatActivity {
|
public class TrophiesActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private TextView tvDivisionTitle, tvTimeRemaining, tvTrophyProgress, tvMotivational;
|
private TextView tvLeagueName, tvLeagueTimeRemaining, tvLeagueObjective;
|
||||||
|
private TextView tvUserRankingStatus, tvXPToNextPosition, tvPastPerformance;
|
||||||
|
private ProgressBar pbWeeklyProgress;
|
||||||
|
private LinearLayout rankingContainer;
|
||||||
private ImageButton btnBack;
|
private ImageButton btnBack;
|
||||||
private LinearLayout trophyContainer, inactiveState;
|
private Button btnEarnXpNow;
|
||||||
private ImageView trophy1, trophy2, trophy3;
|
|
||||||
|
|
||||||
private FirestoreManager firestoreManager;
|
private FirestoreManager firestoreManager;
|
||||||
private FirebaseAuth mAuth;
|
private FirebaseAuth mAuth;
|
||||||
private ListenerRegistration userListener;
|
private ListenerRegistration rankingListener, userListener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -34,107 +36,162 @@ public class TrophiesActivity extends AppCompatActivity {
|
|||||||
initViews();
|
initViews();
|
||||||
setupListeners();
|
setupListeners();
|
||||||
observeUserData();
|
observeUserData();
|
||||||
|
observeRanking("global");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initViews() {
|
private void initViews() {
|
||||||
tvDivisionTitle = findViewById(R.id.tvDivisionTitle);
|
tvLeagueName = findViewById(R.id.tvLeagueName);
|
||||||
tvTimeRemaining = findViewById(R.id.tvTimeRemaining);
|
tvLeagueTimeRemaining = findViewById(R.id.tvLeagueTimeRemaining);
|
||||||
tvTrophyProgress = findViewById(R.id.tvTrophyProgress);
|
tvLeagueObjective = findViewById(R.id.tvLeagueObjective);
|
||||||
tvMotivational = findViewById(R.id.tvMotivational);
|
tvUserRankingStatus = findViewById(R.id.tvUserRankingStatus);
|
||||||
|
tvXPToNextPosition = findViewById(R.id.tvXPToNextPosition);
|
||||||
|
tvPastPerformance = findViewById(R.id.tvPastPerformance);
|
||||||
|
pbWeeklyProgress = findViewById(R.id.pbWeeklyProgress);
|
||||||
|
rankingContainer = findViewById(R.id.rankingContainer);
|
||||||
btnBack = findViewById(R.id.btnBack);
|
btnBack = findViewById(R.id.btnBack);
|
||||||
trophyContainer = findViewById(R.id.trophyContainer);
|
btnEarnXpNow = findViewById(R.id.btnEarnXpNow);
|
||||||
inactiveState = findViewById(R.id.inactiveState);
|
|
||||||
trophy1 = findViewById(R.id.trophy1);
|
updateTimeRemaining();
|
||||||
trophy2 = findViewById(R.id.trophy2);
|
|
||||||
trophy3 = findViewById(R.id.trophy3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupListeners() {
|
private void setupListeners() {
|
||||||
btnBack.setOnClickListener(v -> finish());
|
btnBack.setOnClickListener(v -> finish());
|
||||||
|
btnEarnXpNow.setOnClickListener(v -> finish()); // Go back to Home
|
||||||
|
|
||||||
|
findViewById(R.id.btnRankingGlobal).setOnClickListener(v -> observeRanking("global"));
|
||||||
|
findViewById(R.id.btnRankingAmigos).setOnClickListener(v -> observeRanking("friends"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTimeRemaining() {
|
||||||
|
// Simple logic for week end (Sunday 23:59)
|
||||||
|
Calendar now = Calendar.getInstance();
|
||||||
|
int daysLeft = Calendar.SUNDAY - now.get(Calendar.DAY_OF_WEEK);
|
||||||
|
if (daysLeft < 0) daysLeft += 7;
|
||||||
|
int hoursLeft = 23 - now.get(Calendar.HOUR_OF_DAY);
|
||||||
|
tvLeagueTimeRemaining.setText(daysLeft + " dias " + hoursLeft + "h restantes");
|
||||||
|
|
||||||
|
int progress = (7 - daysLeft) * 100 / 7;
|
||||||
|
pbWeeklyProgress.setProgress(progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void observeUserData() {
|
private void observeUserData() {
|
||||||
String uid = mAuth.getUid();
|
String uid = mAuth.getUid();
|
||||||
if (uid != null) {
|
if (uid != null) {
|
||||||
userListener = firestoreManager.observeUser(uid, this::updateUI);
|
userListener = firestoreManager.observeUser(uid, this::updateUserUI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUI(Usuario user) {
|
private void updateUserUI(Usuario user) {
|
||||||
if (user == null) return;
|
if (user == null) return;
|
||||||
|
tvLeagueName.setText("Divisão " + user.league);
|
||||||
|
tvLeagueObjective.setText("Fica no TOP 3 para subir para " + getNextLeague(user.league));
|
||||||
|
}
|
||||||
|
|
||||||
tvDivisionTitle.setText("Divisão " + user.league);
|
private String getNextLeague(String current) {
|
||||||
|
switch (current) {
|
||||||
|
case "Bronze": return "Prata";
|
||||||
|
case "Prata": return "Ouro";
|
||||||
|
case "Ouro": return "Platina";
|
||||||
|
default: return "Diamante";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Trophy logic based on streak
|
private void observeRanking(String filter) {
|
||||||
int currentStreak = user.streak;
|
if (rankingListener != null) rankingListener.remove();
|
||||||
int trophies = user.trophiesCount;
|
|
||||||
|
|
||||||
// Update trophy visuals
|
com.google.firebase.firestore.Query query = com.google.firebase.firestore.FirebaseFirestore.getInstance()
|
||||||
updateTrophyIcons(trophies);
|
.collection("users")
|
||||||
|
.orderBy("xp_semanal", com.google.firebase.firestore.Query.Direction.DESCENDING)
|
||||||
|
.limit(20);
|
||||||
|
|
||||||
// Progress message
|
rankingListener = query.addSnapshotListener((snapshots, e) -> {
|
||||||
int daysToNext = 30 - (currentStreak % 30);
|
if (e != null || snapshots == null) return;
|
||||||
if (daysToNext == 30 && currentStreak > 0) {
|
rankingContainer.removeAllViews();
|
||||||
tvTrophyProgress.setText("Troféu conquistado! Mantém a ofensiva.");
|
|
||||||
|
int position = 1;
|
||||||
|
String myUid = mAuth.getUid();
|
||||||
|
Usuario me = null;
|
||||||
|
Usuario next = null;
|
||||||
|
int myPos = -1;
|
||||||
|
|
||||||
|
for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
|
||||||
|
Usuario user = doc.toObject(Usuario.class);
|
||||||
|
if (user == null) continue;
|
||||||
|
|
||||||
|
if (user.id_usuario.equals(myUid)) {
|
||||||
|
me = user;
|
||||||
|
myPos = position;
|
||||||
|
} else if (me == null) {
|
||||||
|
next = user; // The one above me
|
||||||
|
}
|
||||||
|
|
||||||
|
addRankingItem(position++, user, myUid);
|
||||||
|
|
||||||
|
// Add zone separators
|
||||||
|
if (position == 4) addZoneSeparator("🟢 ZONA DE PROMOÇÃO", R.color.success_green);
|
||||||
|
if (position == 11) addZoneSeparator("⚪ ZONA DE MANUTENÇÃO", R.color.text_secondary);
|
||||||
|
if (position == 16) addZoneSeparator("🔴 ZONA DE DESPROMOÇÃO", R.color.error_red);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (me != null) {
|
||||||
|
updateUserProgressCard(myPos, me, next);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRankingItem(int pos, Usuario user, String myUid) {
|
||||||
|
View view = getLayoutInflater().inflate(R.layout.item_ranking_user, rankingContainer, false);
|
||||||
|
|
||||||
|
TextView tvPos = view.findViewById(R.id.tvRankPosition);
|
||||||
|
TextView tvName = view.findViewById(R.id.tvRankingName);
|
||||||
|
TextView tvXP = view.findViewById(R.id.tvRankingXP);
|
||||||
|
TextView tvTu = view.findViewById(R.id.tvRankingLabelTu);
|
||||||
|
ImageView ivAvatar = view.findViewById(R.id.ivRankingAvatar);
|
||||||
|
androidx.cardview.widget.CardView card = view.findViewById(R.id.cardRankingUser);
|
||||||
|
|
||||||
|
tvPos.setText("#" + pos);
|
||||||
|
tvName.setText(user.usuario);
|
||||||
|
tvXP.setText(user.xp_semanal + " XP");
|
||||||
|
|
||||||
|
if (user.id_usuario.equals(myUid)) {
|
||||||
|
tvTu.setVisibility(View.VISIBLE);
|
||||||
|
card.setCardBackgroundColor(getResources().getColor(R.color.background_light));
|
||||||
|
card.setCardElevation(4f);
|
||||||
|
}
|
||||||
|
|
||||||
|
rankingContainer.addView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addZoneSeparator(String text, int colorRes) {
|
||||||
|
TextView tv = new TextView(this);
|
||||||
|
tv.setText(text);
|
||||||
|
tv.setTextSize(10);
|
||||||
|
tv.setPadding(0, 24, 0, 8);
|
||||||
|
tv.setTextColor(getResources().getColor(colorRes));
|
||||||
|
tv.setGravity(android.view.Gravity.CENTER);
|
||||||
|
tv.setAlpha(0.7f);
|
||||||
|
rankingContainer.addView(tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUserProgressCard(int myPos, Usuario me, Usuario aboveMe) {
|
||||||
|
if (myPos <= 3) {
|
||||||
|
tvUserRankingStatus.setText("Estás na zona de promoção! 🎉");
|
||||||
|
tvXPToNextPosition.setText("Mantém o foco para subir de liga.");
|
||||||
} else {
|
} else {
|
||||||
tvTrophyProgress.setText("Faltam " + daysToNext + " dias para o próximo troféu");
|
int needed = (aboveMe != null) ? (aboveMe.xp_semanal - me.xp_semanal + 1) : 0;
|
||||||
}
|
tvUserRankingStatus.setText("Faltam " + needed + " XP para subires de posição");
|
||||||
|
tvXPToNextPosition.setText("Estás em #" + myPos);
|
||||||
// Handle inactive state
|
|
||||||
if (currentStreak == 0) {
|
|
||||||
inactiveState.setVisibility(View.VISIBLE);
|
|
||||||
trophyContainer.setAlpha(0.5f);
|
|
||||||
tvMotivational.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
inactiveState.setVisibility(View.GONE);
|
|
||||||
trophyContainer.setAlpha(1.0f);
|
|
||||||
tvMotivational.setVisibility(View.VISIBLE);
|
|
||||||
tvMotivational.setText("Estás a progredir bem na Divisão " + user.league + "!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple scale animation for the active trophy
|
|
||||||
animateActiveTrophy(trophies);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTrophyIcons(int count) {
|
|
||||||
// Reset alphas
|
|
||||||
trophy1.setAlpha(0.3f);
|
|
||||||
trophy2.setAlpha(0.3f);
|
|
||||||
trophy3.setAlpha(0.3f);
|
|
||||||
|
|
||||||
if (count >= 1) trophy1.setAlpha(1.0f);
|
|
||||||
if (count >= 2) trophy2.setAlpha(1.0f);
|
|
||||||
if (count >= 3) trophy3.setAlpha(1.0f);
|
|
||||||
|
|
||||||
// Highlight current progress (the next one)
|
|
||||||
if (count == 0) highlightTrophy(trophy1);
|
|
||||||
else if (count == 1) highlightTrophy(trophy2);
|
|
||||||
else if (count == 2) highlightTrophy(trophy3);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void highlightTrophy(ImageView trophy) {
|
|
||||||
trophy.setAlpha(0.6f);
|
|
||||||
trophy.setBackgroundResource(R.drawable.circle_bg);
|
|
||||||
trophy.setBackgroundTintList(android.content.res.ColorStateList.valueOf(getResources().getColor(R.color.primary_purple)));
|
|
||||||
trophy.setPadding(12, 12, 12, 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateActiveTrophy(int count) {
|
|
||||||
ImageView target = null;
|
|
||||||
if (count == 0) target = trophy1;
|
|
||||||
else if (count == 1) target = trophy2;
|
|
||||||
else if (count == 2) target = trophy3;
|
|
||||||
|
|
||||||
if (target != null) {
|
|
||||||
ScaleAnimation scale = new ScaleAnimation(1f, 1.1f, 1f, 1.1f,
|
|
||||||
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
|
||||||
scale.setDuration(1000);
|
|
||||||
scale.setRepeatCount(Animation.INFINITE);
|
|
||||||
scale.setRepeatMode(Animation.REVERSE);
|
|
||||||
target.startAnimation(scale);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (rankingListener != null) rankingListener.remove();
|
||||||
|
if (userListener != null) userListener.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|||||||
@@ -11,13 +11,28 @@ public class Usuario {
|
|||||||
public String updated_at;
|
public String updated_at;
|
||||||
|
|
||||||
// Stats and Social
|
// Stats and Social
|
||||||
public int xp = 0;
|
public int level = 1;
|
||||||
|
public int xp = 0; // Total XP
|
||||||
|
public int xp_hoje = 0;
|
||||||
|
public int xp_semanal = 0;
|
||||||
public int streak = 0;
|
public int streak = 0;
|
||||||
|
public int melhor_streak = 0;
|
||||||
public String league = "Bronze";
|
public String league = "Bronze";
|
||||||
public String handle = "";
|
public String handle = "";
|
||||||
public String bio = "";
|
public String bio = "";
|
||||||
|
public String titulo = "Iniciante";
|
||||||
public int achievementsCount = 0;
|
public int achievementsCount = 0;
|
||||||
public int trophiesCount = 0;
|
public int trophiesCount = 0;
|
||||||
|
public int tasks_concluidas_hoje = 0;
|
||||||
|
public int total_tasks_concluidas = 0;
|
||||||
|
public int meta_diaria_tarefas = 3;
|
||||||
|
public int meta_diaria_foco = 60; // em minutos
|
||||||
|
public int tempo_foco_hoje = 0; // em minutos
|
||||||
|
public int tempo_foco_total = 0; // em minutos
|
||||||
|
public int sessoes_foco_completas = 0;
|
||||||
|
public int dias_ativos = 1;
|
||||||
|
public String last_active_date = ""; // YYYY-MM-DD
|
||||||
|
public String avatar_url = "";
|
||||||
|
|
||||||
public Usuario() {}
|
public Usuario() {}
|
||||||
|
|
||||||
|
|||||||
@@ -6,174 +6,70 @@
|
|||||||
android:background="@color/background_light"
|
android:background="@color/background_light"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header with Search -->
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="64dp"
|
android:layout_height="wrap_content"
|
||||||
android:paddingHorizontal="16dp">
|
android:background="@color/card_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
<ImageButton
|
<RelativeLayout
|
||||||
android:id="@+id/btnClose"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_close"
|
|
||||||
app:tint="@color/text_primary" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true"
|
android:layout_marginBottom="16dp">
|
||||||
android:text="Encontre os seus amigos"
|
<ImageButton
|
||||||
android:textColor="@color/text_primary"
|
android:id="@+id/btnClose"
|
||||||
android:textSize="18sp"
|
android:layout_width="48dp"
|
||||||
android:textStyle="bold" />
|
android:layout_height="48dp"
|
||||||
</RelativeLayout>
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_back"
|
||||||
|
app:tint="@color/text_primary" />
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:text="Amigos"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearchFriends"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:background="@drawable/edit_text_bg"
|
||||||
|
android:drawableStart="@drawable/ic_search"
|
||||||
|
android:drawablePadding="12dp"
|
||||||
|
android:hint="Procurar por nome ou ID..."
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tabLayoutFriends"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:tabIndicatorColor="@color/primary_purple"
|
||||||
|
app:tabSelectedTextColor="@color/primary_purple"
|
||||||
|
app:tabTextColor="@color/text_secondary">
|
||||||
|
<com.google.android.material.tabs.TabItem android:text="Sugestões" />
|
||||||
|
<com.google.android.material.tabs.TabItem android:text="Os meus amigos" />
|
||||||
|
<com.google.android.material.tabs.TabItem android:text="Pendentes" />
|
||||||
|
</com.google.android.material.tabs.TabLayout>
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/friendsResultsContainer"
|
||||||
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"
|
||||||
android:padding="20dp">
|
android:padding="16dp">
|
||||||
|
<!-- Results will be loaded here -->
|
||||||
<!-- Main Options Cards -->
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/btnContacts"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
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="20dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:src="@drawable/ic_contacts"
|
|
||||||
app:tint="@color/primary_purple" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:text="Escolher nos contactos"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/btnSearchByName"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
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="20dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:src="@drawable/ic_search"
|
|
||||||
app:tint="@color/streak_blue" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:text="Buscar por nome"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/btnProfileLink"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
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="20dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:src="@drawable/ic_share"
|
|
||||||
app:tint="@color/success_green" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:text="Link do seu perfil"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<!-- Suggestions Section -->
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Sugestões de amigos"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/btnViewAll"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:text="VER TODOS"
|
|
||||||
android:textColor="@color/primary_purple"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/rvSuggestions"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
|||||||
@@ -12,25 +12,23 @@
|
|||||||
android:layout_height="64dp"
|
android:layout_height="64dp"
|
||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
android:background="@color/card_background"
|
android:background="@color/card_background"
|
||||||
android:elevation="4dp">
|
android:elevation="0dp">
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btnBack"
|
android:id="@+id/btnBack"
|
||||||
android:layout_width="40dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="48dp"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:src="@drawable/ic_back"
|
android:src="@drawable/ic_back"
|
||||||
app:tint="@color/text_secondary" />
|
app:tint="@color/text_primary" />
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:text="@string/settings"
|
android:text="Configurações"
|
||||||
android:textColor="@color/text_primary"
|
android:textColor="@color/text_primary"
|
||||||
|
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
@@ -45,272 +43,78 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="20dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
<!-- Preferences Section -->
|
<!-- 👤 CONTA -->
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
style="@style/SettingsSectionHeader"
|
||||||
android:layout_height="wrap_content"
|
android:text="Conta" />
|
||||||
android:text="@string/preferences"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
|
|
||||||
android:textAllCaps="true"
|
<androidx.cardview.widget.CardView style="@style/SettingsCard">
|
||||||
android:textSize="12sp"
|
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">
|
||||||
android:textStyle="bold"
|
<include layout="@layout/item_settings_clickable" android:id="@+id/settingEmail" />
|
||||||
android:layout_marginBottom="8dp" />
|
<include layout="@layout/item_settings_clickable" android:id="@+id/settingPassword" />
|
||||||
|
<include layout="@layout/item_settings_switch" android:id="@+id/settingPublicProfile" />
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="0dp"
|
|
||||||
android:backgroundTint="@color/card_background"
|
|
||||||
android:layout_marginBottom="24dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/dark_mode"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/switchDarkMode"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true" />
|
|
||||||
</RelativeLayout>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<!-- Account Section -->
|
<!-- 🎨 APARÊNCIA -->
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
style="@style/SettingsSectionHeader"
|
||||||
android:layout_height="wrap_content"
|
android:text="Aparência" />
|
||||||
android:text="@string/account"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
|
|
||||||
android:textAllCaps="true"
|
<androidx.cardview.widget.CardView style="@style/SettingsCard">
|
||||||
android:textSize="12sp"
|
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">
|
||||||
android:textStyle="bold"
|
<include layout="@layout/item_settings_switch" android:id="@+id/settingDarkMode" />
|
||||||
android:layout_marginBottom="8dp" />
|
<include layout="@layout/item_settings_clickable" android:id="@+id/settingThemeColor" />
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="0dp"
|
|
||||||
android:backgroundTint="@color/card_background"
|
|
||||||
android:layout_marginBottom="24dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvEmail"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="email@exemplo.com"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:background="@color/border_color"
|
|
||||||
|
|
||||||
android:layout_marginVertical="12dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/btnChangePassword"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/change_password"
|
|
||||||
android:textColor="@color/primary_purple"
|
|
||||||
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<!-- Privacy Section -->
|
<!-- 🔔 NOTIFICAÇÕES -->
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
style="@style/SettingsSectionHeader"
|
||||||
android:layout_height="wrap_content"
|
android:text="Notificações" />
|
||||||
android:text="@string/privacy"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
|
|
||||||
android:textAllCaps="true"
|
<androidx.cardview.widget.CardView style="@style/SettingsCard">
|
||||||
android:textSize="12sp"
|
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">
|
||||||
android:textStyle="bold"
|
<include layout="@layout/item_settings_switch" android:id="@+id/settingDailyReminders" />
|
||||||
android:layout_marginBottom="8dp" />
|
<include layout="@layout/item_settings_switch" android:id="@+id/settingAntiProcrastination" />
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="0dp"
|
|
||||||
android:backgroundTint="@color/card_background"
|
|
||||||
android:layout_marginBottom="24dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/private_account"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/switchPrivacy"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true" />
|
|
||||||
</RelativeLayout>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<!-- Notifications Section -->
|
<!-- 🎯 PREFERÊNCIAS DE FOCO -->
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
style="@style/SettingsSectionHeader"
|
||||||
android:layout_height="wrap_content"
|
android:text="Preferências de Foco" />
|
||||||
android:text="@string/notifications"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
|
|
||||||
android:textAllCaps="true"
|
<androidx.cardview.widget.CardView style="@style/SettingsCard">
|
||||||
android:textSize="12sp"
|
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">
|
||||||
android:textStyle="bold"
|
<include layout="@layout/item_settings_clickable" android:id="@+id/settingFocusDuration" />
|
||||||
android:layout_marginBottom="8dp" />
|
<include layout="@layout/item_settings_clickable" android:id="@+id/settingBreakDuration" />
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="0dp"
|
|
||||||
android:backgroundTint="@color/card_background"
|
|
||||||
android:layout_marginBottom="24dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/notifications"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:id="@+id/switchNotifications"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true" />
|
|
||||||
</RelativeLayout>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<!-- App Section -->
|
<!-- 🛡️ PRIVACIDADE -->
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
style="@style/SettingsSectionHeader"
|
||||||
android:layout_height="wrap_content"
|
android:text="Privacidade" />
|
||||||
android:text="@string/app_section"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
|
|
||||||
android:textAllCaps="true"
|
<androidx.cardview.widget.CardView style="@style/SettingsCard">
|
||||||
android:textSize="12sp"
|
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">
|
||||||
android:textStyle="bold"
|
<include layout="@layout/item_settings_switch" android:id="@+id/settingIncognito" />
|
||||||
android:layout_marginBottom="8dp" />
|
<include layout="@layout/item_settings_clickable" android:id="@+id/settingExportData" />
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="0dp"
|
|
||||||
android:backgroundTint="@color/card_background"
|
|
||||||
android:layout_marginBottom="40dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/language"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/spinnerLanguage"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:textAlignment="viewEnd" />
|
|
||||||
</RelativeLayout>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<!-- Logout Button -->
|
<!-- Danger Zone -->
|
||||||
<com.google.android.material.button.MaterialButton
|
<Button
|
||||||
android:id="@+id/btnLogout"
|
android:id="@+id/btnDeleteAccount"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="60dp"
|
android:layout_height="56dp"
|
||||||
android:text="@string/logout"
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="Eliminar Conta"
|
||||||
android:textColor="@color/error_red"
|
android:textColor="@color/error_red"
|
||||||
android:textStyle="bold"
|
android:backgroundTint="#10EF4444"
|
||||||
app:backgroundTint="#FEE2E2"
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
app:cornerRadius="16dp"
|
android:layout_marginBottom="40dp"/>
|
||||||
app:elevation="0dp"
|
|
||||||
app:icon="@drawable/ic_back"
|
|
||||||
app:iconGravity="textStart"
|
|
||||||
app:iconTint="@color/error_red"
|
|
||||||
|
|
||||||
android:layout_marginBottom="20dp"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|||||||
@@ -26,10 +26,11 @@
|
|||||||
app:tint="@color/text_primary" />
|
app:tint="@color/text_primary" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/tvToolbarTitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:text="Troféus"
|
android:text="Ligas"
|
||||||
android:textColor="@color/text_primary"
|
android:textColor="@color/text_primary"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
@@ -44,134 +45,206 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="24dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
<!-- Division Info -->
|
<!-- 1. 🥇 HEADER DA LIGA -->
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvDivisionTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Divisão Prata"
|
|
||||||
android:textColor="@color/primary_purple"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvTimeRemaining"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:text="3 dias restantes"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<!-- Trophy Progression Container -->
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginBottom="20dp"
|
||||||
android:layout_marginBottom="32dp"
|
app:cardCornerRadius="16dp"
|
||||||
app:cardCornerRadius="@dimen/radius_duo"
|
|
||||||
app:cardElevation="2dp">
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="32dp">
|
android:padding="20dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
<LinearLayout
|
<ImageView
|
||||||
android:id="@+id/trophyContainer"
|
android:id="@+id/ivLeagueBadge"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="80dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="80dp"
|
||||||
android:orientation="horizontal"
|
android:src="@drawable/ic_trophy_bronze"
|
||||||
android:gravity="center">
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
<!-- Trophies will be added here dynamically or statically -->
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/trophy1"
|
|
||||||
android:layout_width="60dp"
|
|
||||||
android:layout_height="60dp"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:src="@drawable/ic_trophy_bronze" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/trophy2"
|
|
||||||
android:layout_width="80dp"
|
|
||||||
android:layout_height="80dp"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:src="@drawable/ic_trophy_silver"
|
|
||||||
android:padding="4dp"
|
|
||||||
android:background="@drawable/circle_bg"
|
|
||||||
android:backgroundTint="#1A6200EE" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/trophy3"
|
|
||||||
android:layout_width="60dp"
|
|
||||||
android:layout_height="60dp"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:alpha="0.3"
|
|
||||||
android:src="@drawable/ic_trophy_gold" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvTrophyProgress"
|
android:id="@+id/tvLeagueName"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:text="Divisão Bronze"
|
||||||
android:text="Faltam 15 dias para o próximo troféu"
|
|
||||||
android:textColor="@color/text_primary"
|
android:textColor="@color/text_primary"
|
||||||
android:textSize="16sp"
|
android:textSize="22sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvLeagueTimeRemaining"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="2 dias 14h restantes"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/pbWeeklyProgress"
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="8dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="60"
|
||||||
|
android:progressDrawable="@drawable/progress_bar_duo" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<!-- Inactive State (Initially Hidden) -->
|
<!-- 2. 🎯 OBJETIVO DA LIGA -->
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:id="@+id/inactiveState"
|
android:id="@+id/tvLeagueObjective"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:text="Fica no TOP 3 para subir para Prata"
|
||||||
android:orientation="vertical"
|
android:textAlignment="center"
|
||||||
android:visibility="gone">
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_marginBottom="24dp"/>
|
||||||
|
|
||||||
<ImageView
|
<!-- 8. 👥 FILTRO DE RANKING -->
|
||||||
android:layout_width="150dp"
|
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||||
android:layout_height="150dp"
|
android:id="@+id/toggleRankingFilter"
|
||||||
android:src="@drawable/ic_sleeping_char" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="Hora de voltar!"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Completa tarefas para competir esta semana."
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Active Message -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvMotivational"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Estás a progredir bem na Divisão Prata!"
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
app:singleSelection="true"
|
||||||
|
app:checkedButton="@+id/btnRankingGlobal">
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnRankingGlobal"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Global"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnRankingAmigos"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Amigos"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||||
|
|
||||||
|
<!-- 3. 📊 RANKING DA SEMANA -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/rankingContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginBottom="24dp"/>
|
||||||
|
|
||||||
|
<!-- 5. ⚡ PROGRESSO DO UTILIZADOR -->
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvUserRankingStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Faltam 120 XP para entrares no TOP 3"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvXPToNextPosition"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Diferença para o próximo: 45 XP"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- 6. 🎁 RECOMPENSAS DA SEMANA -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Recompensas da semana"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
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="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="12dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🥇 1º lugar: +300 XP + Badge Exclusiva"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textColor="@color/text_primary"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🥈 2º lugar: +200 XP"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textColor="@color/text_primary"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🥉 3º lugar: +100 XP"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textColor="@color/text_primary"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- 7. 📈 HISTÓRICO DE PERFORMANCE -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPastPerformance"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Semana passada: 2º lugar (subiste)"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textColor="@color/text_secondary"
|
android:textColor="@color/text_secondary"
|
||||||
android:textSize="16sp" />
|
android:textSize="14sp"
|
||||||
|
android:layout_marginBottom="32dp"/>
|
||||||
|
|
||||||
|
<!-- 9. 🔘 BOTÃO DE ACÇÃO -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnEarnXpNow"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:text="Ganhar XP agora"
|
||||||
|
android:background="@drawable/button_primary"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
app:backgroundTint="@null"
|
||||||
|
android:layout_marginBottom="40dp"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="20dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- 1. 👋 HEADER -->
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -27,28 +27,34 @@
|
|||||||
android:id="@+id/tvGreeting"
|
android:id="@+id/tvGreeting"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Olá, Jvitor!"
|
android:text="Olá, Utilizador!"
|
||||||
android:textColor="@color/text_primary"
|
android:textColor="@color/text_primary"
|
||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/tvMotivationalSubtitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:text="Pronto para ser produtivo?"
|
android:text="Só precisas de começar."
|
||||||
android:textColor="@color/text_secondary"
|
android:textColor="@color/text_secondary"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/cardProfileAvatar"
|
||||||
android:layout_width="56dp"
|
android:layout_width="56dp"
|
||||||
android:layout_height="56dp"
|
android:layout_height="56dp"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
app:cardCornerRadius="28dp"
|
app:cardCornerRadius="28dp"
|
||||||
app:cardElevation="2dp">
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
android:id="@+id/ivUserAvatar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
@@ -57,60 +63,125 @@
|
|||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<!-- Progress Overview -->
|
<!-- 2. 🔥 CARD "HOJE" -->
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="24dp"
|
android:layout_marginBottom="24dp"
|
||||||
app:cardCornerRadius="@dimen/radius_duo"
|
app:cardCornerRadius="@dimen/radius_duo"
|
||||||
app:cardElevation="2dp"
|
app:cardElevation="4dp">
|
||||||
app:contentPadding="20dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
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"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="12dp">
|
android:orientation="horizontal"
|
||||||
|
android:weightSum="3">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Desafios Diários"
|
android:layout_weight="1"
|
||||||
android:textColor="@color/text_primary"
|
android:gravity="center"
|
||||||
android:textSize="18sp"
|
android:orientation="vertical">
|
||||||
android:textStyle="bold" />
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🔥 Streak"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_secondary"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvTodayStreak"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="0 dias"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/streak_orange"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/tvProgressText"
|
android:layout_width="0dp"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_weight="1"
|
||||||
android:text="1 de 3 concluídos"
|
android:gravity="center"
|
||||||
android:textColor="@color/text_secondary"
|
android:orientation="vertical">
|
||||||
android:textSize="14sp" />
|
<TextView
|
||||||
</RelativeLayout>
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="⚡ XP"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_secondary"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvTodayXP"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="0"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/primary_purple"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="✅ Tarefas"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_secondary"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvTodayTasksCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="0/3"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/success_green"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/border_color"
|
||||||
|
android:layout_marginVertical="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Meta Diária"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/pbDailyTasks"
|
android:id="@+id/pbDailyTasksProgress"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="12dp"
|
android:layout_height="12dp"
|
||||||
android:max="100"
|
android:max="100"
|
||||||
android:progress="33"
|
android:progress="0"
|
||||||
android:progressDrawable="@drawable/progress_bar_duo" />
|
android:progressDrawable="@drawable/progress_bar_duo" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<!-- Daily Challenges List -->
|
<!-- 3. 🎯 TAREFAS DO DIA -->
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
android:text="Meus Desafios"
|
android:text="Tarefas do Dia"
|
||||||
android:textColor="@color/text_primary"
|
android:textColor="@color/text_primary"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
@@ -122,24 +193,21 @@
|
|||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<Button
|
<TextView
|
||||||
android:id="@+id/btnAddTasks"
|
android:id="@+id/tvNoTasksIncentive"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="32dp"
|
android:text="Ainda não tens tarefas para hoje. Vamos começar?"
|
||||||
android:background="@drawable/button_primary"
|
android:textAlignment="center"
|
||||||
android:text="+ Adicionar Desafio"
|
android:layout_marginBottom="16dp"
|
||||||
android:textAllCaps="false"
|
android:visibility="gone"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/text_secondary"/>
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:backgroundTint="@null" />
|
|
||||||
|
|
||||||
<!-- Focus Mode Section -->
|
<!-- 4. ⏱️ MODO FOCO -->
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="40dp"
|
android:layout_marginBottom="24dp"
|
||||||
app:cardCornerRadius="@dimen/radius_duo"
|
app:cardCornerRadius="@dimen/radius_duo"
|
||||||
app:cardElevation="2dp"
|
app:cardElevation="2dp"
|
||||||
app:contentPadding="24dp">
|
app:contentPadding="24dp">
|
||||||
@@ -207,42 +275,15 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<!-- Daily Progress Path Section -->
|
<!-- 5. 📊 PROGRESSO DIÁRIO -->
|
||||||
<RelativeLayout
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="16dp">
|
android:layout_marginBottom="12dp"
|
||||||
|
android:text="Progresso Semanal"
|
||||||
<TextView
|
android:textColor="@color/text_primary"
|
||||||
android:layout_width="wrap_content"
|
android:textSize="20sp"
|
||||||
android:layout_height="wrap_content"
|
android:textStyle="bold" />
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:text="Caminho de Progresso Diário"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="19sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/btnStreak"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:foreground="?attr/selectableItemBackground"
|
|
||||||
app:cardBackgroundColor="@color/card_background"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="24dp"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:src="@drawable/ic_flame" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/progressPathContainer"
|
android:id="@+id/progressPathContainer"
|
||||||
@@ -253,7 +294,123 @@
|
|||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingVertical="32dp"
|
android:paddingVertical="32dp"
|
||||||
android:layout_marginBottom="40dp" />
|
android:layout_marginBottom="24dp" />
|
||||||
|
|
||||||
|
<!-- 6. 👥 MINI RANKING -->
|
||||||
|
<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="2dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Competição de hoje"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/miniRankingContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnViewFullRanking"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="Ver Ranking Completo"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- 7. 🎁 RECOMPENSA DIÁRIA -->
|
||||||
|
<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="2dp"
|
||||||
|
app:cardBackgroundColor="@color/primary_purple">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="20dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:text="🎁"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:gravity="center"/>
|
||||||
|
|
||||||
|
<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="16sp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDailyRewardGoal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Completa 3 tarefas hoje para ganhar +100 XP"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:textSize="12sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnClaimReward"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Resgatar"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:backgroundTint="@color/white"
|
||||||
|
android:enabled="false"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- 8. ➕ BOTÃO ADICIONAR TAREFA -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnAddTasks"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
android:background="@drawable/button_primary"
|
||||||
|
android:text="+ Adicionar Tarefa"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:backgroundTint="@null" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|||||||
@@ -12,414 +12,182 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="20dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
<!-- Top Header -->
|
<!-- 👤 HEADER -->
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="24dp">
|
android:layout_marginBottom="24dp">
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/btnSettings"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_settings"
|
|
||||||
app:tint="@color/primary_purple" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<!-- Profile Info -->
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_marginBottom="32dp">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="100dp"
|
android:id="@+id/cardAvatar"
|
||||||
android:layout_height="100dp"
|
android:layout_width="80dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_height="80dp"
|
||||||
app:cardCornerRadius="50dp"
|
app:cardCornerRadius="40dp"
|
||||||
app:cardElevation="4dp">
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/ivAvatar"
|
android:id="@+id/ivProfileAvatar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:src="@drawable/ic_nav_profile"
|
android:src="@drawable/ic_nav_profile" />
|
||||||
app:tint="@color/primary_purple" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvUsername"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Jvitor"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="26sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvHandle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@jvitor_prod"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:layout_marginTop="4dp"/>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Stats Section -->
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:text="Estatísticas"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<GridLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
android:columnCount="2"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<!-- Streak Card -->
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_columnWeight="1"
|
|
||||||
android:layout_margin="6dp"
|
|
||||||
app:cardCornerRadius="@dimen/radius_duo"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:contentPadding="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="🔥"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:layout_marginBottom="8dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvStreakValue"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="15"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Ofensiva"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="12sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<!-- XP Card -->
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_columnWeight="1"
|
|
||||||
android:layout_margin="6dp"
|
|
||||||
app:cardCornerRadius="@dimen/radius_duo"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:contentPadding="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="⚡"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:layout_marginBottom="8dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvTotalXP"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="2450"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Total de XP"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="12sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<!-- League Card -->
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/cardLeague"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_columnWeight="1"
|
|
||||||
android:layout_margin="6dp"
|
|
||||||
app:cardCornerRadius="@dimen/radius_duo"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:contentPadding="16dp"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:foreground="?attr/selectableItemBackground">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="🏆"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:layout_marginBottom="8dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvLeagueName"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Prata"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Liga Atual"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="12sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<!-- Achievements Card -->
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_columnWeight="1"
|
|
||||||
android:layout_margin="6dp"
|
|
||||||
app:cardCornerRadius="@dimen/radius_duo"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:contentPadding="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="🏅"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:layout_marginBottom="8dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvAchievementsCount"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="8"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Conquistas"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="12sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
</GridLayout>
|
|
||||||
|
|
||||||
<!-- Friends Section -->
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Amigos"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/btnViewAllFriends"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:text="Ver Todos"
|
|
||||||
android:textColor="@color/primary_purple"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<HorizontalScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
android:scrollbars="none">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:layout_toEndOf="@id/cardAvatar"
|
||||||
android:orientation="horizontal">
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<!-- Invite Item -->
|
<TextView
|
||||||
<LinearLayout
|
android:id="@+id/tvProfileName"
|
||||||
android:layout_width="80dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:text="Carregando..."
|
||||||
android:gravity="center"
|
android:textColor="@color/text_primary"
|
||||||
android:orientation="vertical">
|
android:textSize="22sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<TextView
|
||||||
android:id="@+id/btnInviteCard"
|
android:id="@+id/tvProfileTitle"
|
||||||
android:layout_width="60dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="60dp"
|
|
||||||
app:cardCornerRadius="30dp"
|
|
||||||
app:cardElevation="0dp"
|
|
||||||
app:cardBackgroundColor="@color/border_color"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:foreground="?attr/selectableItemBackground">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:text="+"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="32sp" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Convidar"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="12sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Sample Friend 1 -->
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="80dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:text="Iniciante de Foco"
|
||||||
android:gravity="center"
|
android:textColor="@color/primary_purple"
|
||||||
android:orientation="vertical">
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<TextView
|
||||||
android:layout_width="60dp"
|
android:id="@+id/tvProfileBio"
|
||||||
android:layout_height="60dp"
|
android:layout_width="wrap_content"
|
||||||
app:cardCornerRadius="30dp"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
android:src="@drawable/ic_nav_profile"
|
|
||||||
app:tint="@color/reward_yellow" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Maria"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="450 XP"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="10sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Sample Friend 2 -->
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="80dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:text="Sem bio definida."
|
||||||
android:gravity="center"
|
android:textColor="@color/text_secondary"
|
||||||
android:orientation="vertical">
|
android:textSize="12sp"
|
||||||
|
android:layout_marginTop="4dp" />
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="60dp"
|
|
||||||
android:layout_height="60dp"
|
|
||||||
app:cardCornerRadius="30dp"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
android:src="@drawable/ic_nav_profile"
|
|
||||||
app:tint="@color/success_green" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="João"
|
|
||||||
android:textColor="@color/text_primary"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="320 XP"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="10sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</HorizontalScrollView>
|
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnEditProfile"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_settings"
|
||||||
|
app:tint="@color/text_secondary" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<!-- 📊 ESTATÍSTICAS PRINCIPAIS -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Estatísticas"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnViewStatsDetails"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:text="Ver detalhes"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<GridLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="3"
|
||||||
|
android:layout_marginBottom="24dp">
|
||||||
|
|
||||||
|
<include layout="@layout/item_stat_small" android:id="@+id/statXP" />
|
||||||
|
<include layout="@layout/item_stat_small" android:id="@+id/statStreak" />
|
||||||
|
<include layout="@layout/item_stat_small" android:id="@+id/statLevel" />
|
||||||
|
<include layout="@layout/item_stat_small" android:id="@+id/statTasks" />
|
||||||
|
<include layout="@layout/item_stat_small" android:id="@+id/statFocusTime" />
|
||||||
|
<include layout="@layout/item_stat_small" android:id="@+id/statSessions" />
|
||||||
|
</GridLayout>
|
||||||
|
|
||||||
|
<!-- 🏆 CONQUISTAS (Badges) -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Conquistas"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnViewAllBadges"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:text="Ver todas"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="1dp">
|
||||||
|
<GridLayout
|
||||||
|
android:id="@+id/badgesGrid"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="4"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:gravity="center">
|
||||||
|
<!-- Badges will be added here -->
|
||||||
|
</GridLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- 👥 AMIGOS -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Amigos"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnManageFriends"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:text="Gerir"
|
||||||
|
android:textColor="@color/primary_purple"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/friendsListContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginBottom="24dp"/>
|
||||||
|
|
||||||
|
<!-- 🔘 BOTÕES DE ACÇÃO -->
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnInviteFriends"
|
android:id="@+id/btnLogout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="56dp"
|
||||||
android:background="@drawable/button_primary"
|
android:text="Terminar Sessão"
|
||||||
android:text="+ Encontrar Amigos"
|
android:backgroundTint="@color/error_red"
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp"
|
android:layout_marginBottom="40dp"/>
|
||||||
android:textStyle="bold"
|
|
||||||
app:backgroundTint="@null" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|||||||
@@ -34,5 +34,28 @@
|
|||||||
<string name="confirm_password_hint">Confirmar palavra-passe</string>
|
<string name="confirm_password_hint">Confirmar palavra-passe</string>
|
||||||
<string name="register_button">Registar</string>
|
<string name="register_button">Registar</string>
|
||||||
<string name="already_have_account">Já tens conta? </string>
|
<string name="already_have_account">Já tens conta? </string>
|
||||||
|
|
||||||
|
<!-- Statistics -->
|
||||||
|
<string name="statistics">Estatísticas</string>
|
||||||
|
<string name="view_details">Ver detalhes</string>
|
||||||
|
<string name="xp_progress">Progresso de XP</string>
|
||||||
|
<string name="focus_time_stats">Tempo de Foco (min)</string>
|
||||||
|
<string name="performance">Desempenho</string>
|
||||||
|
<string name="avg_focus">Média Foco</string>
|
||||||
|
<string name="tasks_per_day">Tarefas/Dia</string>
|
||||||
|
<string name="best_day">Melhor Dia</string>
|
||||||
|
<string name="total_sessions">Total Sessões</string>
|
||||||
|
|
||||||
|
<!-- Friends -->
|
||||||
|
<string name="friends">Amigos</string>
|
||||||
|
<string name="search_friends_hint">Procurar por nome ou ID...</string>
|
||||||
|
<string name="suggestions">Sugestões</string>
|
||||||
|
<string name="my_friends">Os meus amigos</string>
|
||||||
|
<string name="pending">Pendentes</string>
|
||||||
|
<string name="add_friend">Adicionar</string>
|
||||||
|
|
||||||
|
<!-- Gamification -->
|
||||||
|
<string name="level_up">NOVO NÍVEL!</string>
|
||||||
|
<string name="level_up_congrats">Estás a tornar-te uma lenda da produtividade!</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
|||||||
@@ -50,4 +50,23 @@
|
|||||||
|
|
||||||
<item name="android:layout_marginBottom">@dimen/spacing_md</item>
|
<item name="android:layout_marginBottom">@dimen/spacing_md</item>
|
||||||
</style>
|
</style>
|
||||||
|
<style name="SettingsSectionHeader">
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:textColor">@color/text_secondary</item>
|
||||||
|
<item name="android:textAllCaps">true</item>
|
||||||
|
<item name="android:textSize">12sp</item>
|
||||||
|
<item name="android:textStyle">bold</item>
|
||||||
|
<item name="android:layout_marginTop">24dp</item>
|
||||||
|
<item name="android:layout_marginBottom">8dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="SettingsCard" parent="CardView">
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="cardCornerRadius">16dp</item>
|
||||||
|
<item name="cardElevation">0dp</item>
|
||||||
|
<item name="cardBackgroundColor">@color/card_background</item>
|
||||||
|
<item name="android:layout_marginBottom">8dp</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -10,13 +10,6 @@ buildscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,11 @@
|
|||||||
rootProject.name = "Fluxup"
|
rootProject.name = "Fluxup"
|
||||||
include ':app'
|
include ':app'
|
||||||
|
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user