From 2309dcf02c1ab6cc211a9645346a2d24c1e370be Mon Sep 17 00:00:00 2001 From: Pau Date: Fri, 30 May 2025 08:54:13 +0200 Subject: [PATCH] Added images to reports and better layout experience. Fixed colors --- .../acloc/adapter/PlaceReportsAdapter.java | 76 +++++- .../acloc/adapter/ReportTypeAdapter.java | 242 ++++++++++++++++++ .../acloc/fragments/MyReportsFragment.java | 53 ++-- .../java/com/example/acloc/model/Report.java | 65 +++++ .../main/res/layout/activity_add_report.xml | 2 +- app/src/main/res/layout/activity_login.xml | 4 +- .../res/layout/item_accessibility_tag.xml | 59 +++++ .../res/layout/list_view_place_reports.xml | 31 ++- 8 files changed, 507 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/com/example/acloc/adapter/ReportTypeAdapter.java create mode 100644 app/src/main/res/layout/item_accessibility_tag.xml diff --git a/app/src/main/java/com/example/acloc/adapter/PlaceReportsAdapter.java b/app/src/main/java/com/example/acloc/adapter/PlaceReportsAdapter.java index a4ae145..b48f06d 100644 --- a/app/src/main/java/com/example/acloc/adapter/PlaceReportsAdapter.java +++ b/app/src/main/java/com/example/acloc/adapter/PlaceReportsAdapter.java @@ -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 reportList; + private OnViewAllClickListener onViewAllClickListener; + + public interface OnViewAllClickListener { + void onViewAllClick(); + } public PlaceReportsAdapter(Context context, List reportList) { this.context = context; this.reportList = reportList; } + public void setOnViewAllClickListener(OnViewAllClickListener listener) { + this.onViewAllClickListener = listener; + } + @SuppressLint("NotifyDataSetChanged") public void updateReportsList(List reportList) { if (reportList != null) { @@ -63,6 +78,9 @@ public class PlaceReportsAdapter extends RecyclerView.Adapter 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 { + + private final Context context; + private final List reportTypesList; + private OnReportTypeClickListener onReportTypeClickListener; + private List selectedPositions = new ArrayList<>(); // Track multiple selected items + + public interface OnReportTypeClickListener { + void onReportTypeClick(ReportType reportType, int position, boolean isSelected); + void onSelectionChanged(List selectedReportTypes); + } + + public ReportTypeAdapter(Context context, List reportTypesList) { + this.context = context; + this.reportTypesList = reportTypesList; + } + + public void setOnReportTypeClickListener(OnReportTypeClickListener listener) { + this.onReportTypeClickListener = listener; + } + + public void clearSelection() { + List 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 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 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 getSelectedPositions() { + return new ArrayList<>(selectedPositions); + } + + public List getSelectedReportTypes() { + List selected = new ArrayList<>(); + for (int position : selectedPositions) { + if (position < reportTypesList.size()) { + selected.add(reportTypesList.get(position)); + } + } + return selected; + } + + public List getSelectedReportTypeUuids() { + List 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); + } + } +} diff --git a/app/src/main/java/com/example/acloc/fragments/MyReportsFragment.java b/app/src/main/java/com/example/acloc/fragments/MyReportsFragment.java index d9021fc..b3431cc 100644 --- a/app/src/main/java/com/example/acloc/fragments/MyReportsFragment.java +++ b/app/src/main/java/com/example/acloc/fragments/MyReportsFragment.java @@ -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 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 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()); + } } } } diff --git a/app/src/main/java/com/example/acloc/model/Report.java b/app/src/main/java/com/example/acloc/model/Report.java index 255ee75..0fd5361 100644 --- a/app/src/main/java/com/example/acloc/model/Report.java +++ b/app/src/main/java/com/example/acloc/model/Report.java @@ -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()); + } + } + } + } + } } diff --git a/app/src/main/res/layout/activity_add_report.xml b/app/src/main/res/layout/activity_add_report.xml index 5798f56..aac70e2 100644 --- a/app/src/main/res/layout/activity_add_report.xml +++ b/app/src/main/res/layout/activity_add_report.xml @@ -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" /> diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index b89eda3..f44daf9 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -110,7 +110,7 @@ android:paddingEnd="48dp" /> - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_view_place_reports.xml b/app/src/main/res/layout/list_view_place_reports.xml index 3c35562..f4ba265 100644 --- a/app/src/main/res/layout/list_view_place_reports.xml +++ b/app/src/main/res/layout/list_view_place_reports.xml @@ -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"> @@ -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" /> @@ -86,6 +88,27 @@ + + + + + + + - + \ No newline at end of file