From 60812f9ee85ae7f4ead3ed04b15c36756a30757c Mon Sep 17 00:00:00 2001 From: Nikitha Date: Thu, 12 Mar 2026 20:50:12 -0600 Subject: [PATCH] Adoption list and details --- .../adapters/AdoptionAdapter.java | 79 +++++++++ .../listfragments/AdoptionFragment.java | 149 +++++++++++++++++ .../AdoptionDetailFragment.java | 158 ++++++++++++++++++ .../petstoremobile/models/Adoption.java | 75 +++++++++ app/src/main/res/layout/fragment_adoption.xml | 52 ++++++ .../res/layout/fragment_adoption_detail.xml | 100 +++++++++++ app/src/main/res/layout/item_adoption.xml | 64 +++++++ app/src/main/res/layout/item_inventory.xml | 81 +++++++++ app/src/main/res/layout/item_product.xml | 84 ++++++++++ settings.gradle.kts | 1 + 10 files changed, 843 insertions(+) create mode 100644 app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java create mode 100644 app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java create mode 100644 app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java create mode 100644 app/src/main/java/com/example/petstoremobile/models/Adoption.java create mode 100644 app/src/main/res/layout/fragment_adoption.xml create mode 100644 app/src/main/res/layout/fragment_adoption_detail.xml create mode 100644 app/src/main/res/layout/item_adoption.xml create mode 100644 app/src/main/res/layout/item_inventory.xml create mode 100644 app/src/main/res/layout/item_product.xml diff --git a/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java b/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java new file mode 100644 index 00000000..98e88256 --- /dev/null +++ b/app/src/main/java/com/example/petstoremobile/adapters/AdoptionAdapter.java @@ -0,0 +1,79 @@ +package com.example.petstoremobile.adapters; + + +import android.graphics.Color; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import com.example.petstoremobile.R; +import com.example.petstoremobile.models.Adoption; +import java.util.List; + +public class AdoptionAdapter extends RecyclerView.Adapter { + + private List adoptionList; + private OnAdoptionClickListener adoptionClickListener; + + // Interface for adoption click on recycler view + public interface OnAdoptionClickListener { + void onAdoptionClick(int position); + } + + // Constructor + public AdoptionAdapter(List adoptionList, OnAdoptionClickListener adoptionClickListener) { + this.adoptionList = adoptionList; + this.adoptionClickListener = adoptionClickListener; + } + + // Get the controls of each row in recycler view + public static class AdoptionViewHolder extends RecyclerView.ViewHolder { + TextView tvAdopterName, tvPetName, tvAdoptionDate, tvAdoptionStatus; + + public AdoptionViewHolder(@NonNull View v) { + super(v); + tvAdopterName = v.findViewById(R.id.tvAdopterName); + tvPetName = v.findViewById(R.id.tvAdoptionPetName); + tvAdoptionDate = v.findViewById(R.id.tvAdoptionDate); + tvAdoptionStatus = v.findViewById(R.id.tvAdoptionStatus); + } + } + + // Create a new row view + @NonNull + @Override + public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_adoption, parent, false); + return new AdoptionViewHolder(v); + } + + // Populate the row with adoption data + @Override + public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) { + Adoption adoption = adoptionList.get(position); + + holder.tvAdopterName.setText(adoption.getAdopterName()); + holder.tvPetName.setText("Pet: " + adoption.getPetName()); + holder.tvAdoptionDate.setText("Date: " + adoption.getAdoptionDate()); + holder.tvAdoptionStatus.setText(adoption.getStatus()); + + // Set the status color depending on adoption status + if (adoption.getStatus().equals("Approved")) { + holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#4CAF50")); + } else if (adoption.getStatus().equals("Pending")) { + holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#FF9800")); + } else { + holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#F44336")); + } + + // When a row is clicked, open the detail view + holder.itemView.setOnClickListener(v -> adoptionClickListener.onAdoptionClick(position)); + } + + @Override + public int getItemCount() { + return adoptionList.size(); + } +} diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java new file mode 100644 index 00000000..bec68a2f --- /dev/null +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java @@ -0,0 +1,149 @@ +package com.example.petstoremobile.fragments.listfragments; + +// Added search/filter bar to filter adoptions by adopter name or pet name. +// Added pull-to-refresh using SwipeRefreshLayout. + +import android.os.Bundle; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import com.example.petstoremobile.R; +import com.example.petstoremobile.adapters.AdoptionAdapter; +import com.example.petstoremobile.fragments.ListFragment; +import com.example.petstoremobile.fragments.listfragments.detailfragments.AdoptionDetailFragment; +import com.example.petstoremobile.models.Adoption; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import java.util.ArrayList; +import java.util.List; + +public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener { + + private List adoptionList = new ArrayList<>(); + private List filteredList = new ArrayList<>(); + private AdoptionAdapter adapter; + private SwipeRefreshLayout swipeRefreshLayout; + private EditText etSearch; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_adoption, container, false); + + loadAdoptionData(); + // Replace with actual API call when backend is ready + setupRecyclerView(view); + setupSearch(view); + setupSwipeRefresh(view); + + FloatingActionButton fabAddAdoption = view.findViewById(R.id.fabAddAdoption); + fabAddAdoption.setOnClickListener(v -> openAdoptionDetails(-1)); + + return view; + } + + // Filters adoption list by adopter name or pet name + private void setupSearch(View view) { + etSearch = view.findViewById(R.id.etSearchAdoption); + etSearch.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) { + filterAdoptions(s.toString()); + } + @Override public void afterTextChanged(Editable s) {} + }); + } + + private void filterAdoptions(String query) { + filteredList.clear(); + if (query.isEmpty()) { + filteredList.addAll(adoptionList); + } else { + String lower = query.toLowerCase(); + for (Adoption a : adoptionList) { + if (a.getAdopterName().toLowerCase().contains(lower) + || a.getPetName().toLowerCase().contains(lower) + || a.getStatus().toLowerCase().contains(lower)) { + filteredList.add(a); + } + } + } + adapter.notifyDataSetChanged(); + } + + private void setupSwipeRefresh(View view) { + swipeRefreshLayout = view.findViewById(R.id.swipeRefreshAdoption); + swipeRefreshLayout.setOnRefreshListener(() -> { + loadAdoptionData(); // TODO: Replace with actual API call + filterAdoptions(etSearch.getText().toString()); + swipeRefreshLayout.setRefreshing(false); + }); + } + + private void openAdoptionDetails(int position) { + AdoptionDetailFragment detailFragment = new AdoptionDetailFragment(); + Bundle args = new Bundle(); + args.putInt("position", position); + + if (position != -1) { + Adoption adoption = filteredList.get(position); + int realPosition = adoptionList.indexOf(adoption); + args.putInt("position", realPosition); + args.putInt("adoptionId", adoption.getAdoptionId()); + args.putString("adopterName", adoption.getAdopterName()); + args.putString("adopterEmail", adoption.getAdopterEmail()); + args.putString("adopterPhone", adoption.getAdopterPhone()); + args.putString("petName", adoption.getPetName()); + args.putString("adoptionDate", adoption.getAdoptionDate()); + args.putString("status", adoption.getStatus()); + } + + detailFragment.setArguments(args); + detailFragment.setAdoptionFragment(this); + + ListFragment listFragment = (ListFragment) getParentFragment(); + if (listFragment != null) listFragment.loadFragment(detailFragment); + } + + public void onAdoptionSaved(int position, Adoption adoption) { + if (position == -1) { + adoptionList.add(adoption); + } else { + adoptionList.set(position, adoption); + } + filterAdoptions(etSearch.getText().toString()); + } + + public void onAdoptionDeleted(int position) { + adoptionList.remove(position); + filterAdoptions(etSearch.getText().toString()); + } + + @Override + public void onAdoptionClick(int position) { + openAdoptionDetails(position); + } + + private void loadAdoptionData() { + adoptionList.clear(); + adoptionList.add(new Adoption(1, "Sarah Connor", "sarah@email.com", "555-1234", "Luna", "2026-03-01", "Approved")); + adoptionList.add(new Adoption(2, "Tom Hardy", "tom@email.com", "555-5678", "Bella", "2026-03-05", "Pending")); + adoptionList.add(new Adoption(3, "Emily Clark", "emily@email.com", "555-9012", "Charlie", "2026-03-07", "Pending")); + adoptionList.add(new Adoption(4, "Mike Ross", "mike@email.com", "555-3456", "Milo", "2026-02-20", "Rejected")); + filteredList.clear(); + filteredList.addAll(adoptionList); + } + + private void setupRecyclerView(View view) { + RecyclerView recyclerView = view.findViewById(R.id.recyclerViewAdoptions); + adapter = new AdoptionAdapter(filteredList, this); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + recyclerView.setAdapter(adapter); + } +} diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java new file mode 100644 index 00000000..6bdf9ad5 --- /dev/null +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AdoptionDetailFragment.java @@ -0,0 +1,158 @@ +package com.example.petstoremobile.fragments.listfragments.detailfragments; + + +// Uses InputValidator for detailed field validation and ActivityLogger to log all changes. + +import android.os.Bundle; +import androidx.fragment.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; +import com.example.petstoremobile.R; +import com.example.petstoremobile.fragments.ListFragment; +import com.example.petstoremobile.fragments.listfragments.AdoptionFragment; +import com.example.petstoremobile.models.Adoption; +import com.example.petstoremobile.utils.ActivityLogger; +import com.example.petstoremobile.utils.InputValidator; + +public class AdoptionDetailFragment extends Fragment { + + private TextView tvMode, tvAdoptionId; + private EditText etAdopterName, etAdopterEmail, etAdopterPhone, etPetName, etAdoptionDate; + private Spinner spinnerAdoptionStatus; + private Button btnSaveAdoption, btnDeleteAdoption, btnBack; + private int adoptionId; + private int position; + private boolean isEditing = false; + private AdoptionFragment adoptionFragment; + + // Set the adoption fragment as parent so we refer back when save or delete is done + public void setAdoptionFragment(AdoptionFragment fragment) { + this.adoptionFragment = fragment; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_adoption_detail, container, false); + + initViews(view); + setupSpinner(); + handleArguments(); + + btnBack.setOnClickListener(v -> { + ListFragment listFragment = (ListFragment) getParentFragment(); + if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); + }); + btnSaveAdoption.setOnClickListener(v -> saveAdoption()); + btnDeleteAdoption.setOnClickListener(v -> deleteAdoption()); + + return view; + } + + // Validates all fields using InputValidator, then saves the adoption record + private void saveAdoption() { + if (!InputValidator.isNotEmpty(etAdopterName, "Adopter Name")) return; + if (!InputValidator.isValidEmail(etAdopterEmail)) return; + if (!InputValidator.isValidPhone(etAdopterPhone)) return; + if (!InputValidator.isNotEmpty(etPetName, "Pet Name")) return; + if (!InputValidator.isValidDate(etAdoptionDate)) return; + + String adopterName = etAdopterName.getText().toString().trim(); + String adopterEmail = etAdopterEmail.getText().toString().trim(); + String adopterPhone = etAdopterPhone.getText().toString().trim(); + String petName = etPetName.getText().toString().trim(); + String adoptionDate = etAdoptionDate.getText().toString().trim(); + String status = spinnerAdoptionStatus.getSelectedItem().toString(); + + try { + if (isEditing) { + // TODO: Replace with actual API PUT call when backend is ready + Adoption updated = new Adoption(adoptionId, adopterName, adopterEmail, adopterPhone, petName, adoptionDate, status); + if (adoptionFragment != null) adoptionFragment.onAdoptionSaved(position, updated); + ActivityLogger.logChange(requireContext(), "Adoption", "UPDATED", adoptionId); + Toast.makeText(getContext(), "Adoption record updated.", Toast.LENGTH_SHORT).show(); + } else { + // TODO: Replace with actual API POST call when backend is ready + Adoption newAdoption = new Adoption(0, adopterName, adopterEmail, adopterPhone, petName, adoptionDate, status); + if (adoptionFragment != null) adoptionFragment.onAdoptionSaved(-1, newAdoption); + ActivityLogger.log(requireContext(), "Added new Adoption record for: " + adopterName + " adopting " + petName); + Toast.makeText(getContext(), "Adoption record added.", Toast.LENGTH_SHORT).show(); + } + ListFragment listFragment = (ListFragment) getParentFragment(); + if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); + } catch (Exception e) { + ActivityLogger.logException(requireContext(), "AdoptionDetailFragment.saveAdoption", e); + Toast.makeText(getContext(), "Error saving adoption record.", Toast.LENGTH_SHORT).show(); + } + } + + // Deletes the adoption record and logs the action + private void deleteAdoption() { + try { + // TODO: Replace with actual API DELETE call when backend is ready + if (adoptionFragment != null) adoptionFragment.onAdoptionDeleted(position); + ActivityLogger.logChange(requireContext(), "Adoption", "DELETED", adoptionId); + Toast.makeText(getContext(), "Adoption record deleted.", Toast.LENGTH_SHORT).show(); + ListFragment listFragment = (ListFragment) getParentFragment(); + if (listFragment != null) listFragment.getChildFragmentManager().popBackStack(); + } catch (Exception e) { + ActivityLogger.logException(requireContext(), "AdoptionDetailFragment.deleteAdoption", e); + Toast.makeText(getContext(), "Error deleting adoption record.", Toast.LENGTH_SHORT).show(); + } + } + + private void handleArguments() { + if (getArguments() != null && getArguments().containsKey("adoptionId")) { + isEditing = true; + adoptionId = getArguments().getInt("adoptionId"); + position = getArguments().getInt("position"); + tvMode.setText("Edit Adoption"); + tvAdoptionId.setText("ID: " + adoptionId); + etAdopterName.setText(getArguments().getString("adopterName")); + etAdopterEmail.setText(getArguments().getString("adopterEmail")); + etAdopterPhone.setText(getArguments().getString("adopterPhone")); + etPetName.setText(getArguments().getString("petName")); + etAdoptionDate.setText(getArguments().getString("adoptionDate")); + String status = getArguments().getString("status"); + if ("Approved".equals(status)) spinnerAdoptionStatus.setSelection(0); + else if ("Pending".equals(status)) spinnerAdoptionStatus.setSelection(1); + else spinnerAdoptionStatus.setSelection(2); + btnDeleteAdoption.setVisibility(View.VISIBLE); + } else { + isEditing = false; + tvMode.setText("Add Adoption"); + tvAdoptionId.setVisibility(View.GONE); + btnDeleteAdoption.setVisibility(View.GONE); + btnSaveAdoption.setText("Add"); + } + } + + private void initViews(View view) { + tvMode = view.findViewById(R.id.tvAdoptionMode); + tvAdoptionId = view.findViewById(R.id.tvAdoptionId); + etAdopterName = view.findViewById(R.id.etAdopterName); + etAdopterEmail = view.findViewById(R.id.etAdopterEmail); + etAdopterPhone = view.findViewById(R.id.etAdopterPhone); + etPetName = view.findViewById(R.id.etAdoptionPetName); + etAdoptionDate = view.findViewById(R.id.etAdoptionDate); + spinnerAdoptionStatus = view.findViewById(R.id.spinnerAdoptionStatus); + btnSaveAdoption = view.findViewById(R.id.btnSaveAdoption); + btnDeleteAdoption = view.findViewById(R.id.btnDeleteAdoption); + btnBack = view.findViewById(R.id.btnAdoptionBack); + } + + private void setupSpinner() { + ArrayAdapter adapter = new ArrayAdapter<>(requireContext(), + android.R.layout.simple_spinner_item, + new String[]{"Approved", "Pending", "Rejected"}); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinnerAdoptionStatus.setAdapter(adapter); + } +} diff --git a/app/src/main/java/com/example/petstoremobile/models/Adoption.java b/app/src/main/java/com/example/petstoremobile/models/Adoption.java new file mode 100644 index 00000000..1f7452ca --- /dev/null +++ b/app/src/main/java/com/example/petstoremobile/models/Adoption.java @@ -0,0 +1,75 @@ +package com.example.petstoremobile.models; + +public class Adoption { + private int adoptionId; + private String adopterName; + private String adopterEmail; + private String adopterPhone; + private String petName; + private String adoptionDate; + private String status; + + // Constructor + public Adoption(int adoptionId, String adopterName, String adopterEmail, String adopterPhone, String petName, String adoptionDate, String status) { + this.adoptionId = adoptionId; + this.adopterName = adopterName; + this.adopterEmail = adopterEmail; + this.adopterPhone = adopterPhone; + this.petName = petName; + this.adoptionDate = adoptionDate; + this.status = status; + } + + // Getters and setters + public int getAdoptionId() { + return adoptionId; + } + + public String getAdopterName() { + return adopterName; + } + + public void setAdopterName(String adopterName) { + this.adopterName = adopterName; + } + + public String getAdopterEmail() { + return adopterEmail; + } + + public void setAdopterEmail(String adopterEmail) { + this.adopterEmail = adopterEmail; + } + + public String getAdopterPhone() { + return adopterPhone; + } + + public void setAdopterPhone(String adopterPhone) { + this.adopterPhone = adopterPhone; + } + + public String getPetName() { + return petName; + } + + public void setPetName(String petName) { + this.petName = petName; + } + + public String getAdoptionDate() { + return adoptionDate; + } + + public void setAdoptionDate(String adoptionDate) { + this.adoptionDate = adoptionDate; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/app/src/main/res/layout/fragment_adoption.xml b/app/src/main/res/layout/fragment_adoption.xml new file mode 100644 index 00000000..370b27c3 --- /dev/null +++ b/app/src/main/res/layout/fragment_adoption.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_adoption_detail.xml b/app/src/main/res/layout/fragment_adoption_detail.xml new file mode 100644 index 00000000..c3424917 --- /dev/null +++ b/app/src/main/res/layout/fragment_adoption_detail.xml @@ -0,0 +1,100 @@ + + + + + + + + + + +