grafico
This commit is contained in:
12
.idea/caches/deviceStreaming.xml
generated
12
.idea/caches/deviceStreaming.xml
generated
@@ -1464,6 +1464,18 @@
|
||||
<option name="screenX" value="1856" />
|
||||
<option name="screenY" value="2160" />
|
||||
</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>
|
||||
<option name="api" value="36" />
|
||||
<option name="brand" value="samsung" />
|
||||
|
||||
@@ -1,56 +1,333 @@
|
||||
|
||||
package com.fluxup.app;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
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.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 {
|
||||
|
||||
private ListView recyclerView;
|
||||
private EditText searchBar;
|
||||
private ArrayAdapter<String> adapter;
|
||||
private ArrayList<String> allItems;
|
||||
private BarChart weeklyChart;
|
||||
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
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_search, container, false);
|
||||
|
||||
recyclerView = view.findViewById(R.id.recycler_view);
|
||||
searchBar = view.findViewById(R.id.search_bar);
|
||||
|
||||
allItems = new ArrayList<>(Arrays.asList("Item 1", "Item 2", "Item 3", "Another Item", "More Items"));
|
||||
|
||||
adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, allItems);
|
||||
recyclerView.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) {
|
||||
}
|
||||
});
|
||||
initViews(view);
|
||||
setupCharts();
|
||||
setupRecyclerViews();
|
||||
loadData();
|
||||
setupSearch();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,151 @@
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_light"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="80dp">
|
||||
|
||||
<!-- 1. BARRA DE PESQUISA MODERNA -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
app:cardCornerRadius="28dp"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<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:hint="Search..." />
|
||||
android:layout_marginBottom="24dp"
|
||||
app:cardCornerRadius="@dimen/radius_duo"
|
||||
app:cardElevation="2dp">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/recycler_view"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/search_bar" />
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
</RelativeLayout>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user