Added images to reports and better layout experience. Fixed colors

This commit is contained in:
Pau 2025-05-30 08:54:13 +02:00
parent e102308827
commit 2309dcf02c
8 changed files with 507 additions and 25 deletions

View File

@ -10,13 +10,19 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.acloc.model.Report;
import com.example.acloc.utility.Constants;
import com.example.acloc.utility.AccessibilityHelper;
import com.google.android.material.card.MaterialCardView;
import com.ieslamar.acloc.R;
import com.squareup.picasso.Picasso;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.ArrayList;
import java.util.List;
@ -25,12 +31,21 @@ public class PlaceReportsAdapter extends RecyclerView.Adapter<PlaceReportsAdapte
public static final String TAG = PlaceReportsAdapter.class.getSimpleName();
private final Context context;
private List<Report> reportList;
private OnViewAllClickListener onViewAllClickListener;
public interface OnViewAllClickListener {
void onViewAllClick();
}
public PlaceReportsAdapter(Context context, List<Report> reportList) {
this.context = context;
this.reportList = reportList;
}
public void setOnViewAllClickListener(OnViewAllClickListener listener) {
this.onViewAllClickListener = listener;
}
@SuppressLint("NotifyDataSetChanged")
public void updateReportsList(List<Report> reportList) {
if (reportList != null) {
@ -63,6 +78,9 @@ public class PlaceReportsAdapter extends RecyclerView.Adapter<PlaceReportsAdapte
// Set rating
setRatingDisplay(holder, report.getReportRating());
// Setup report image
setupReportImage(holder, report);
// Setup accessibility tags for multiple report types
setupAccessibilityTags(holder.rvAccessibilityTags, report, position);
@ -71,23 +89,72 @@ public class PlaceReportsAdapter extends RecyclerView.Adapter<PlaceReportsAdapte
}
}
private void setupReportImage(ViewHolder holder, Report report) {
if (report.getImage() != null && !report.getImage().isEmpty() && !report.getImage().equals("[]")) {
try {
JSONArray imageArray = new JSONArray(report.getImage());
if (imageArray.length() > 0) {
String imageUrl = imageArray.getString(0);
// Ensure the URL is properly formatted
if (!imageUrl.startsWith("http")) {
imageUrl = Constants.BASE_URL + "public/" + imageUrl;
}
holder.cvReportImage.setVisibility(View.VISIBLE);
Picasso.get()
.load(imageUrl)
.placeholder(R.drawable.place_header)
.error(R.drawable.place_header)
.fit()
.centerCrop()
.into(holder.ivReportImage);
Log.d(TAG, "Loading report image: " + imageUrl);
} else {
holder.cvReportImage.setVisibility(View.GONE);
}
} catch (JSONException e) {
Log.e(TAG, "Error parsing report image", e);
holder.cvReportImage.setVisibility(View.GONE);
}
} else {
holder.cvReportImage.setVisibility(View.GONE);
}
}
// Solo necesitas modificar el método setRatingDisplay en tu código existente:
private void setRatingDisplay(ViewHolder holder, int rating) {
int color;
switch (rating) {
case Constants.BAD_RATING:
holder.tvRating.setText(context.getString(R.string.Rating_BAD));
holder.ivRating.setImageResource(R.drawable.ic_thumbs_down);
color = ContextCompat.getColor(context, R.color.red);
break;
case Constants.AVERAGE_RATING:
holder.tvRating.setText(context.getString(R.string.Rating_AVERAGE));
holder.ivRating.setImageResource(R.drawable.ic_thumb_up_average);
color = ContextCompat.getColor(context, R.color.yellow);
break;
case Constants.GOOD_RATING:
holder.tvRating.setText(context.getString(R.string.Rating_GOOD));
holder.ivRating.setImageResource(R.drawable.ic_thumbs_up);
color = ContextCompat.getColor(context, R.color.green);
break;
default:
color = ContextCompat.getColor(context, R.color.yellow);
break;
}
// Apply color to text and icon
holder.tvRating.setTextColor(color);
holder.ivRating.setColorFilter(color);
}
private void setupAccessibilityTags(RecyclerView rvTags, Report report, int position) {
// Clear any existing adapter first to avoid conflicts
rvTags.setAdapter(null);
@ -158,7 +225,6 @@ public class PlaceReportsAdapter extends RecyclerView.Adapter<PlaceReportsAdapte
return reportList.size();
}
// Add this to ensure proper recycling
@Override
public long getItemId(int position) {
return position;
@ -170,16 +236,20 @@ public class PlaceReportsAdapter extends RecyclerView.Adapter<PlaceReportsAdapte
}
public static class ViewHolder extends RecyclerView.ViewHolder {
final TextView tvDescription, tvRating;
final ImageView ivRating;
final TextView tvDescription, tvRating, tvReportDate;
final ImageView ivRating, ivReportImage;
final RecyclerView rvAccessibilityTags;
final MaterialCardView cvReportImage;
public ViewHolder(@NonNull View itemView) {
super(itemView);
tvDescription = itemView.findViewById(R.id.tvDescription);
tvRating = itemView.findViewById(R.id.tvRating);
tvReportDate = itemView.findViewById(R.id.tvReportDate);
ivRating = itemView.findViewById(R.id.ivRating);
ivReportImage = itemView.findViewById(R.id.ivReportImage);
rvAccessibilityTags = itemView.findViewById(R.id.rvAccessibilityTags);
cvReportImage = itemView.findViewById(R.id.cvReportImage);
}
}
}

View File

@ -0,0 +1,242 @@
package com.example.acloc.adapter;
import android.content.Context;
import android.content.res.ColorStateList;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.acloc.model.ReportType;
import com.example.acloc.utility.AccessibilityHelper;
import com.google.android.material.card.MaterialCardView;
import com.ieslamar.acloc.R;
import java.util.ArrayList;
import java.util.List;
public class ReportTypeAdapter extends RecyclerView.Adapter<ReportTypeAdapter.ViewHolder> {
private final Context context;
private final List<ReportType> reportTypesList;
private OnReportTypeClickListener onReportTypeClickListener;
private List<Integer> selectedPositions = new ArrayList<>(); // Track multiple selected items
public interface OnReportTypeClickListener {
void onReportTypeClick(ReportType reportType, int position, boolean isSelected);
void onSelectionChanged(List<ReportType> selectedReportTypes);
}
public ReportTypeAdapter(Context context, List<ReportType> reportTypesList) {
this.context = context;
this.reportTypesList = reportTypesList;
}
public void setOnReportTypeClickListener(OnReportTypeClickListener listener) {
this.onReportTypeClickListener = listener;
}
public void clearSelection() {
List<Integer> previousSelected = new ArrayList<>(selectedPositions);
selectedPositions.clear();
// Update all previously selected items
for (int position : previousSelected) {
if (position < reportTypesList.size()) {
reportTypesList.get(position).setSelected(false);
notifyItemChanged(position);
}
}
if (onReportTypeClickListener != null) {
onReportTypeClickListener.onSelectionChanged(getSelectedReportTypes());
}
}
public void setSelectedPositions(List<Integer> positions) {
// Clear previous selections
clearSelection();
// Set new selections
for (int position : positions) {
if (position >= 0 && position < reportTypesList.size()) {
selectedPositions.add(position);
reportTypesList.get(position).setSelected(true);
notifyItemChanged(position);
}
}
if (onReportTypeClickListener != null) {
onReportTypeClickListener.onSelectionChanged(getSelectedReportTypes());
}
}
public void setSelectedReportTypes(List<String> reportTypeUuids) {
clearSelection();
if (reportTypeUuids != null) {
for (int i = 0; i < reportTypesList.size(); i++) {
ReportType reportType = reportTypesList.get(i);
if (reportTypeUuids.contains(reportType.getUuid())) {
selectedPositions.add(i);
reportType.setSelected(true);
notifyItemChanged(i);
}
}
}
if (onReportTypeClickListener != null) {
onReportTypeClickListener.onSelectionChanged(getSelectedReportTypes());
}
}
public List<Integer> getSelectedPositions() {
return new ArrayList<>(selectedPositions);
}
public List<ReportType> getSelectedReportTypes() {
List<ReportType> selected = new ArrayList<>();
for (int position : selectedPositions) {
if (position < reportTypesList.size()) {
selected.add(reportTypesList.get(position));
}
}
return selected;
}
public List<String> getSelectedReportTypeUuids() {
List<String> uuids = new ArrayList<>();
for (ReportType reportType : getSelectedReportTypes()) {
uuids.add(reportType.getUuid());
}
return uuids;
}
private void toggleSelection(int position) {
if (position < 0 || position >= reportTypesList.size()) return;
ReportType reportType = reportTypesList.get(position);
if (selectedPositions.contains(position)) {
// Deselect
selectedPositions.remove(Integer.valueOf(position));
reportType.setSelected(false);
} else {
// Select
selectedPositions.add(position);
reportType.setSelected(true);
}
notifyItemChanged(position);
if (onReportTypeClickListener != null) {
onReportTypeClickListener.onReportTypeClick(reportType, position, reportType.isSelected());
onReportTypeClickListener.onSelectionChanged(getSelectedReportTypes());
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_report_type_selector, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ReportType reportType = reportTypesList.get(position);
boolean isSelected = selectedPositions.contains(position);
// Ensure the model state matches the adapter state
reportType.setSelected(isSelected);
// Get icon and display name using AccessibilityHelper
int iconResource = AccessibilityHelper.getIconForReportType(reportType.getName());
String displayName = AccessibilityHelper.getDisplayName(context, reportType.getName());
// Set icon and text
holder.ivIcon.setImageResource(iconResource);
holder.tvTypeName.setText(displayName);
// Set selection state
if (isSelected) {
// Selected state - use theme colors
TypedValue typedValue = new TypedValue();
context.getTheme().resolveAttribute(com.google.android.material.R.attr.colorPrimary, typedValue, true);
int primaryColor = typedValue.data;
context.getTheme().resolveAttribute(com.google.android.material.R.attr.colorOnPrimary, typedValue, true);
int onPrimaryColor = typedValue.data;
holder.card.setCardBackgroundColor(primaryColor);
holder.card.setStrokeColor(primaryColor);
holder.card.setStrokeWidth(4);
holder.tvTypeName.setTextColor(onPrimaryColor);
holder.ivIcon.setImageTintList(ColorStateList.valueOf(onPrimaryColor));
holder.ivSelected.setVisibility(View.VISIBLE);
} else {
// Unselected state - use theme colors
TypedValue typedValue = new TypedValue();
context.getTheme().resolveAttribute(com.google.android.material.R.attr.colorSurface, typedValue, true);
int surfaceColor = typedValue.data;
context.getTheme().resolveAttribute(com.google.android.material.R.attr.colorOutline, typedValue, true);
int outlineColor = typedValue.data;
context.getTheme().resolveAttribute(com.google.android.material.R.attr.colorOnSurface, typedValue, true);
int onSurfaceColor = typedValue.data;
context.getTheme().resolveAttribute(com.google.android.material.R.attr.colorOnSurfaceVariant, typedValue, true);
int onSurfaceVariantColor = typedValue.data;
holder.card.setCardBackgroundColor(surfaceColor);
holder.card.setStrokeColor(outlineColor);
holder.card.setStrokeWidth(2);
holder.tvTypeName.setTextColor(onSurfaceColor);
holder.ivIcon.setImageTintList(ColorStateList.valueOf(onSurfaceVariantColor));
holder.ivSelected.setVisibility(View.GONE);
}
holder.itemView.setOnClickListener(v -> toggleSelection(position));
String selectionState = isSelected ?
context.getString(R.string.selected) :
context.getString(R.string.tap_to_select);
String contentDescription = displayName + ", " + selectionState;
if (!selectedPositions.isEmpty()) {
contentDescription += ". " + selectedPositions.size() + " " +
context.getString(R.string.selected);
}
holder.itemView.setContentDescription(contentDescription);
}
@Override
public int getItemCount() {
return reportTypesList != null ? reportTypesList.size() : 0;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
MaterialCardView card;
ImageView ivIcon;
TextView tvTypeName;
ImageView ivSelected;
public ViewHolder(@NonNull View itemView) {
super(itemView);
card = itemView.findViewById(R.id.reportTypeCard);
ivIcon = itemView.findViewById(R.id.reportTypeIcon);
tvTypeName = itemView.findViewById(R.id.tvReportTypeName);
ivSelected = itemView.findViewById(R.id.ivSelected);
}
}
}

View File

@ -15,6 +15,7 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.google.gson.JsonArray;
import com.ieslamar.acloc.R;
import com.example.acloc.adapter.MyReportsAdapter;
import com.example.acloc.api.LocationApiClient;
@ -154,36 +155,58 @@ public class MyReportsFragment extends Fragment {
report.setPlaceName(reportObject.get("place_name").getAsString());
report.setPlaceUuid(reportObject.get("place_uuid").getAsString());
// Imágenes
// Images
if (reportObject.has("images") && !reportObject.get("images").isJsonNull()) {
report.setImage(reportObject.get("images").getAsString());
JsonElement imagesElement = reportObject.get("images");
if (imagesElement.isJsonArray()) {
report.setImage(imagesElement.toString());
} else {
report.setImage(imagesElement.getAsString());
}
}
// Report Type UUIDs
if (reportObject.has("report_type_uuids") && !reportObject.get("report_type_uuids").isJsonNull()) {
String uuidsString = reportObject.get("report_type_uuids").getAsString();
JsonElement typeUuidsElement = reportObject.get("report_type_uuids");
List<String> uuids = new ArrayList<>();
if (uuidsString != null && !uuidsString.trim().isEmpty()) {
String[] uuidArray = uuidsString.split(",");
for (String uuid : uuidArray) {
if (!uuid.trim().isEmpty()) {
uuids.add(uuid.trim());
if (typeUuidsElement.isJsonArray()) {
JsonArray uuidsArray = typeUuidsElement.getAsJsonArray();
for (JsonElement uuidElement : uuidsArray) {
uuids.add(uuidElement.getAsString());
}
} else if (typeUuidsElement.isJsonPrimitive()) {
String uuidsString = typeUuidsElement.getAsString();
if (uuidsString != null && !uuidsString.trim().isEmpty()) {
String[] uuidArray = uuidsString.split(",");
for (String uuid : uuidArray) {
if (!uuid.trim().isEmpty()) {
uuids.add(uuid.trim());
}
}
}
}
report.setReportTypeUuids(uuids);
Log.d(TAG, "Report " + report.getUuid() + " has types: " + uuids);
}
// Report Type Names
if (reportObject.has("report_type_names") && !reportObject.get("report_type_names").isJsonNull()) {
String namesString = reportObject.get("report_type_names").getAsString();
JsonElement typeNamesElement = reportObject.get("report_type_names");
List<String> names = new ArrayList<>();
if (namesString != null && !namesString.trim().isEmpty()) {
String[] nameArray = namesString.split(",");
for (String name : nameArray) {
if (!name.trim().isEmpty()) {
names.add(name.trim());
if (typeNamesElement.isJsonArray()) {
JsonArray namesArray = typeNamesElement.getAsJsonArray();
for (JsonElement nameElement : namesArray) {
names.add(nameElement.getAsString());
}
} else if (typeNamesElement.isJsonPrimitive()) {
String namesString = typeNamesElement.getAsString();
if (namesString != null && !namesString.trim().isEmpty()) {
String[] nameArray = namesString.split(",");
for (String name : nameArray) {
if (!name.trim().isEmpty()) {
names.add(name.trim());
}
}
}
}

View File

@ -1,5 +1,8 @@
package com.example.acloc.model;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@ -99,4 +102,66 @@ public class Report implements Serializable {
public void setImage(String image) {
this.image = image;
}
/**
* Establishes report type UUIDs from a JsonElement
* Can parse both JSON arrays and comma-separated strings
*/
public void setReportTypeUuidsFromJsonElement(JsonElement element) {
reportTypeUuids.clear();
if (element == null || element.isJsonNull()) {
return;
}
if (element.isJsonArray()) {
// Json Array
JsonArray uuidsArray = element.getAsJsonArray();
for (JsonElement uuidElement : uuidsArray) {
reportTypeUuids.add(uuidElement.getAsString());
}
} else if (element.isJsonPrimitive()) {
// Comma separated strings
String uuidsString = element.getAsString();
if (uuidsString != null && !uuidsString.trim().isEmpty()) {
String[] uuidArray = uuidsString.split(",");
for (String uuid : uuidArray) {
if (!uuid.trim().isEmpty()) {
reportTypeUuids.add(uuid.trim());
}
}
}
}
}
/**
* Establishes report type names from a JsonElement
* Can parse both JSON arrays and comma-separated strings
*/
public void setReportTypeNamesFromJsonElement(JsonElement element) {
reportTypeNames.clear();
if (element == null || element.isJsonNull()) {
return;
}
if (element.isJsonArray()) {
// Json Array case
JsonArray namesArray = element.getAsJsonArray();
for (JsonElement nameElement : namesArray) {
reportTypeNames.add(nameElement.getAsString());
}
} else if (element.isJsonPrimitive()) {
// Comma separated string case
String namesString = element.getAsString();
if (namesString != null && !namesString.trim().isEmpty()) {
String[] nameArray = namesString.split(",");
for (String name : nameArray) {
if (!name.trim().isEmpty()) {
reportTypeNames.add(name.trim());
}
}
}
}
}
}

View File

@ -189,7 +189,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
app:tint="?attr/colorOnSurfaceVariant"
android:src="@drawable/ic_thumb_up_average"
android:src="@drawable/ic_thumb_side_border"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="16dp"
android:contentDescription="@string/AVERAGE" />

View File

@ -110,7 +110,7 @@
android:paddingEnd="48dp" />
</com.google.android.material.textfield.TextInputLayout>
<!-- Forgot Password Link -->
<!-- Forgot Password Link
<TextView
android:id="@+id/tvForgotPassword"
android:layout_width="wrap_content"
@ -123,7 +123,7 @@
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:padding="4dp" />
android:padding="4dp" />-->
<!-- Login Button -->
<com.google.android.material.button.MaterialButton

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/reportTypeCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardCornerRadius="20dp"
app:cardElevation="2dp"
app:strokeWidth="2dp"
app:cardBackgroundColor="?attr/colorSurface"
app:strokeColor="?attr/colorOutline">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
android:minWidth="100dp">
<ImageView
android:id="@+id/reportTypeIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="8dp"
android:contentDescription="@string/accessibility_icon"
app:tint="?attr/colorOnSurfaceVariant"
tools:src="@drawable/ic_accessible" />
<TextView
android:id="@+id/tvReportTypeName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="?attr/colorOnSurface"
android:singleLine="true"
tools:text="Silla de ruedas" />
<ImageView
android:id="@+id/ivSelected"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="8dp"
android:src="@drawable/ic_check_circle"
android:visibility="gone"
app:tint="?attr/colorPrimary" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View File

@ -6,7 +6,8 @@
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
app:cardElevation="2dp"
app:cardBackgroundColor="?attr/colorSurface">
<LinearLayout
android:layout_width="match_parent"
@ -22,7 +23,8 @@
android:ellipsize="end"
android:maxLines="3"
android:textSize="14sp"
android:textColor="@android:color/black"
android:textColor="?attr/colorOnSurface"
android:lineSpacingExtra="2dp"
tools:text="Este lugar tiene buena accesibilidad para sillas de ruedas y personas con discapacidad visual." />
<!-- Rating and Accessibility Tags Container -->
@ -67,7 +69,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="11sp"
android:textColor="@android:color/darker_gray"
android:textColor="?attr/colorOnSurfaceVariant"
tools:text="12/01/2024" />
</LinearLayout>
@ -86,6 +88,27 @@
</LinearLayout>
<!-- Report Image (small, at the bottom) -->
<com.google.android.material.card.MaterialCardView
android:id="@+id/cvReportImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:visibility="gone"
app:cardCornerRadius="8dp"
app:cardElevation="2dp"
tools:visibility="visible">
<ImageView
android:id="@+id/ivReportImage"
android:layout_width="match_parent"
android:layout_height="120dp"
android:scaleType="centerCrop"
android:contentDescription="@string/place_image"
tools:src="@drawable/place_header" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.cardview.widget.CardView>