This commit is contained in:
MeuNome
2026-05-11 15:00:18 +01:00
parent 851cd64f10
commit d1a9183fbc
3 changed files with 462 additions and 40 deletions

View File

@@ -1464,6 +1464,18 @@
<option name="screenX" value="1856" /> <option name="screenX" value="1856" />
<option name="screenY" value="2160" /> <option name="screenY" value="2160" />
</PersistentDeviceSelectionData> </PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="36" />
<option name="brand" value="samsung" />
<option name="codename" value="q7mq" />
<option name="id" value="q7mq" />
<option name="labId" value="google" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Z TriFold" />
<option name="screenDensity" value="320" />
<option name="screenX" value="2160" />
<option name="screenY" value="1584" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData> <PersistentDeviceSelectionData>
<option name="api" value="36" /> <option name="api" value="36" />
<option name="brand" value="samsung" /> <option name="brand" value="samsung" />

View File

@@ -1,56 +1,333 @@
package com.fluxup.app; package com.fluxup.app;
import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import android.widget.ListView; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.formatter.ValueFormatter;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class SearchFragment extends Fragment { public class SearchFragment extends Fragment {
private ListView recyclerView;
private EditText searchBar; private EditText searchBar;
private ArrayAdapter<String> adapter; private BarChart weeklyChart;
private ArrayList<String> allItems; private TextView tvEmptyState;
private RecyclerView rvChallenges, rvFeaturedUsers;
private ChallengeAdapter challengeAdapter;
private FeaturedUserAdapter userAdapter;
private final FirestoreManager firestoreManager = FirestoreManager.getInstance();
private final String currentUserId = FirebaseAuth.getInstance().getUid();
@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_search, container, false); View view = inflater.inflate(R.layout.fragment_search, container, false);
recyclerView = view.findViewById(R.id.recycler_view); initViews(view);
searchBar = view.findViewById(R.id.search_bar); setupCharts();
setupRecyclerViews();
allItems = new ArrayList<>(Arrays.asList("Item 1", "Item 2", "Item 3", "Another Item", "More Items")); loadData();
setupSearch();
adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, allItems);
recyclerView.setAdapter(adapter);
searchBar.addTextChangedListener(new 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) {
adapter.getFilter().filter(s);
}
@Override
public void afterTextChanged(Editable s) {
}
});
return view; return view;
} }
private void initViews(View view) {
searchBar = view.findViewById(R.id.search_bar);
weeklyChart = view.findViewById(R.id.weeklyChart);
tvEmptyState = view.findViewById(R.id.tvEmptyState);
rvChallenges = view.findViewById(R.id.rvChallenges);
rvFeaturedUsers = view.findViewById(R.id.rvFeaturedUsers);
}
private void setupCharts() {
weeklyChart.getDescription().setEnabled(false);
weeklyChart.getLegend().setEnabled(false);
weeklyChart.setDrawGridBackground(false);
weeklyChart.setDrawBarShadow(false);
weeklyChart.setDrawValueAboveBar(true);
weeklyChart.setTouchEnabled(false);
XAxis xAxis = weeklyChart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(false);
xAxis.setGranularity(1f);
xAxis.setTextColor(Color.parseColor("#94A3B8"));
String[] days = {"SEG", "TER", "QUA", "QUI", "SEX", "SAB", "DOM"};
xAxis.setValueFormatter(new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
int index = (int) value;
if (index >= 0 && index < days.length) return days[index];
return "";
}
});
weeklyChart.getAxisLeft().setDrawGridLines(true);
weeklyChart.getAxisLeft().setGridColor(Color.parseColor("#E2E8F0"));
weeklyChart.getAxisLeft().setTextColor(Color.parseColor("#94A3B8"));
weeklyChart.getAxisRight().setEnabled(false);
}
private void setupRecyclerViews() {
// Challenges
rvChallenges.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
challengeAdapter = new ChallengeAdapter(getMockChallenges());
rvChallenges.setAdapter(challengeAdapter);
// Featured Users
rvFeaturedUsers.setLayoutManager(new LinearLayoutManager(getContext()));
userAdapter = new FeaturedUserAdapter(new ArrayList<>());
rvFeaturedUsers.setAdapter(userAdapter);
}
private void setupSearch() {
searchBar.addTextChangedListener(new 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) {
filterUsers(s.toString());
}
@Override
public void afterTextChanged(Editable s) {}
});
}
private void loadData() {
if (currentUserId == null) return;
// Load Weekly Progress
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
Date startDate = cal.getTime();
cal.add(Calendar.DAY_OF_WEEK, 6);
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
Date endDate = cal.getTime();
firestoreManager.getDailyProgress(currentUserId, startDate, endDate, 3, progressMap -> {
updateChart(progressMap);
});
// Load Featured Users (mocking top users from Firestore)
FirebaseFirestore.getInstance().collection("users")
.orderBy("xp", Query.Direction.DESCENDING)
.limit(10)
.get()
.addOnSuccessListener(snapshots -> {
List<Usuario> users = new ArrayList<>();
for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots) {
Usuario u = doc.toObject(Usuario.class);
if (u != null) users.add(u);
}
userAdapter.updateList(users);
});
}
private void updateChart(Map<String, DailyProgress> progressMap) {
if (progressMap.isEmpty()) {
weeklyChart.setVisibility(View.GONE);
tvEmptyState.setVisibility(View.VISIBLE);
return;
}
weeklyChart.setVisibility(View.VISIBLE);
tvEmptyState.setVisibility(View.GONE);
List<BarEntry> entries = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
int todayIndex = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 2;
if (todayIndex < 0) todayIndex = 6; // Sunday fix
List<Integer> colors = new ArrayList<>();
for (int i = 0; i < 7; i++) {
String dateStr = sdf.format(cal.getTime());
DailyProgress dp = progressMap.get(dateStr);
int xp = (dp != null) ? dp.xp : 0;
entries.add(new BarEntry(i, xp));
if (i == todayIndex) {
colors.add(Color.parseColor("#7C3AED")); // Roxo principal destaque
} else {
colors.add(Color.parseColor("#DDD6FE")); // Roxo claro
}
cal.add(Calendar.DAY_OF_WEEK, 1);
}
BarDataSet dataSet = new BarDataSet(entries, "XP");
dataSet.setColors(colors);
dataSet.setDrawValues(true);
dataSet.setValueTextColor(Color.parseColor("#7C3AED"));
dataSet.setValueTextSize(10f);
BarData data = new BarData(dataSet);
data.setBarWidth(0.6f);
weeklyChart.setData(data);
weeklyChart.animateY(1000);
weeklyChart.invalidate();
}
private void filterUsers(String query) {
userAdapter.filter(query);
}
private List<Challenge> getMockChallenges() {
List<Challenge> list = new ArrayList<>();
list.add(new Challenge("🎯", "7 dias sem procrastinar", 500));
list.add(new Challenge("📚", "Ler 20 min por dia", 300));
list.add(new Challenge("⏱️", "30 min foco diário", 400));
return list;
}
// --- ADAPTERS ---
private static class Challenge {
String icon, name;
int xp;
Challenge(String icon, String name, int xp) {
this.icon = icon; this.name = name; this.xp = xp;
}
}
private class ChallengeAdapter extends RecyclerView.Adapter<ChallengeAdapter.ViewHolder> {
private final List<Challenge> items;
ChallengeAdapter(List<Challenge> items) { this.items = items; }
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_challenge, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Challenge item = items.get(position);
holder.icon.setText(item.icon);
holder.name.setText(item.name);
holder.xp.setText("+" + item.xp + " XP");
holder.btn.setOnClickListener(v -> Toast.makeText(getContext(), "Desafio iniciado!", Toast.LENGTH_SHORT).show());
}
@Override
public int getItemCount() { return items.size(); }
class ViewHolder extends RecyclerView.ViewHolder {
TextView icon, name, xp;
View btn;
ViewHolder(View v) {
super(v);
icon = v.findViewById(R.id.tvChallengeIcon);
name = v.findViewById(R.id.tvChallengeName);
xp = v.findViewById(R.id.tvChallengeXP);
btn = v.findViewById(R.id.btnStartChallenge);
}
}
}
private class FeaturedUserAdapter extends RecyclerView.Adapter<FeaturedUserAdapter.ViewHolder> {
private List<Usuario> items;
private List<Usuario> fullList;
FeaturedUserAdapter(List<Usuario> items) {
this.items = items;
this.fullList = new ArrayList<>(items);
}
void updateList(List<Usuario> newList) {
this.items = newList;
this.fullList = new ArrayList<>(newList);
notifyDataSetChanged();
}
void filter(String query) {
if (query.isEmpty()) {
items = new ArrayList<>(fullList);
} else {
items = new ArrayList<>();
for (Usuario u : fullList) {
if (u.usuario.toLowerCase().contains(query.toLowerCase())) {
items.add(u);
}
}
}
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_featured_user, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Usuario user = items.get(position);
holder.name.setText(user.usuario);
holder.stats.setText("🔥 " + user.streak + " dias • " + user.league);
// holder.avatar setup if needed
holder.btnAdd.setOnClickListener(v -> Toast.makeText(getContext(), "Pedido enviado!", Toast.LENGTH_SHORT).show());
}
@Override
public int getItemCount() { return items.size(); }
class ViewHolder extends RecyclerView.ViewHolder {
ImageView avatar;
TextView name, stats;
View btnAdd;
ViewHolder(View v) {
super(v);
avatar = v.findViewById(R.id.ivUserAvatar);
name = v.findViewById(R.id.tvUserName);
stats = v.findViewById(R.id.tvUserStats);
btnAdd = v.findViewById(R.id.btnAddFriend);
}
}
}
} }

View File

@@ -1,18 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="@color/background_light"
android:fillViewport="true">
<EditText <LinearLayout
android:id="@+id/search_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="Search..." /> android:orientation="vertical"
android:paddingHorizontal="20dp"
android:paddingTop="20dp"
android:paddingBottom="80dp">
<ListView <!-- 1. BARRA DE PESQUISA MODERNA -->
android:id="@+id/recycler_view" <androidx.cardview.widget.CardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="56dp"
android:layout_below="@id/search_bar" /> android:layout_marginBottom="24dp"
app:cardCornerRadius="28dp"
app:cardElevation="2dp">
</RelativeLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_search"
app:tint="@color/text_secondary" />
<EditText
android:id="@+id/search_bar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:background="@null"
android:hint="Pesquisar tarefas, desafios ou utilizadores..."
android:imeOptions="actionSearch"
android:inputType="text"
android:textColor="@color/text_primary"
android:textColorHint="@color/text_secondary"
android:textSize="15sp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- 2. CARD "TEU PROGRESSO" -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Teu progresso esta semana"
android:textColor="@color/text_primary"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="Continua consistente 🔥"
android:textColor="@color/text_secondary"
android:textSize="14sp" />
<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">
<com.github.mikephil.charting.charts.BarChart
android:id="@+id/weeklyChart"
android:layout_width="match_parent"
android:layout_height="200dp" />
<TextView
android:id="@+id/tvEmptyState"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="32dp"
android:text="Completa tarefas para veres teu progresso 📈"
android:textColor="@color/text_secondary"
android:visibility="gone" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- 3. DESAFIOS POPULARES -->
<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="Desafios Populares"
android:textColor="@color/text_primary"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/btnSeeAllChallenges"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="Ver todos"
android:textColor="@color/primary_purple"
android:textSize="14sp"
android:textStyle="bold" />
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvChallenges"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:clipToPadding="false"
android:orientation="horizontal" />
<!-- 4. UTILIZADORES EM DESTAQUE -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:text="Utilizadores em Destaque"
android:textColor="@color/text_primary"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvFeaturedUsers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>