From 025e749ce309bd398133745f1936ad1d6b25f47a Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Sat, 14 Mar 2026 03:02:18 -0600 Subject: [PATCH] Edited the views so application looks consistant -edited appointment, adoption, product, inventory xmls to look consistant - added search feature to Pets, services, and suppliers - made it so the application will resume where you left off when the application is switched to the background and back --- app/src/main/AndroidManifest.xml | 1 + .../petstoremobile/PetStoreApplication.java | 13 + .../activities/HomeActivity.java | 8 +- .../activities/MainActivity.java | 12 +- .../petstoremobile/api/RetrofitClient.java | 1 - .../listfragments/AdoptionFragment.java | 14 + .../listfragments/AppointmentFragment.java | 14 + .../listfragments/InventoryFragment.java | 14 + .../fragments/listfragments/PetFragment.java | 82 +++-- .../listfragments/ProductFragment.java | 14 + .../listfragments/ServiceFragment.java | 74 +++-- .../listfragments/SupplierFragment.java | 74 ++++- .../AdoptionDetailFragment.java | 18 +- .../AppointmentDetailFragment.java | 19 +- .../detailfragments/PetDetailFragment.java | 20 +- app/src/main/res/layout/activity_main.xml | 6 +- app/src/main/res/layout/fragment_adoption.xml | 40 ++- .../res/layout/fragment_adoption_detail.xml | 263 ++++++++++----- .../main/res/layout/fragment_appointment.xml | 40 ++- .../layout/fragment_appointment_detail.xml | 260 ++++++++++----- app/src/main/res/layout/fragment_chat.xml | 3 +- .../main/res/layout/fragment_inventory.xml | 41 ++- .../res/layout/fragment_inventory_detail.xml | 311 +++++++++-------- app/src/main/res/layout/fragment_pet.xml | 87 +++-- .../main/res/layout/fragment_pet_detail.xml | 43 +-- app/src/main/res/layout/fragment_product.xml | 41 ++- .../res/layout/fragment_product_detail.xml | 313 ++++++++++-------- app/src/main/res/layout/fragment_service.xml | 87 +++-- .../res/layout/fragment_service_detail.xml | 48 ++- app/src/main/res/layout/fragment_supplier.xml | 87 +++-- .../res/layout/fragment_supplier_detail.xml | 25 +- app/src/main/res/values-night/themes.xml | 2 + app/src/main/res/values/themes.xml | 11 +- 33 files changed, 1386 insertions(+), 700 deletions(-) create mode 100644 app/src/main/java/com/example/petstoremobile/PetStoreApplication.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 946a685e..633ea6ba 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ android:required="false" /> { diff --git a/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java b/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java index 30f53385..d710d927 100644 --- a/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java +++ b/app/src/main/java/com/example/petstoremobile/activities/MainActivity.java @@ -36,6 +36,15 @@ public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + // Check if user is already logged in + if (TokenManager.getInstance(this).isLoggedIn()) { + Intent intent = new Intent(this, HomeActivity.class); + startActivity(intent); + finish(); + return; + } + EdgeToEdge.enable(this); setContentView(R.layout.activity_main); @@ -53,9 +62,6 @@ public class MainActivity extends AppCompatActivity { //clear login status tvLoginStatus.setText(""); - // Clear old login data before logging in - TokenManager.getInstance(this).clearLoginData(); - //Set click listener for login button btnLogin.setOnClickListener(v -> { //Get user name and password from text fields diff --git a/app/src/main/java/com/example/petstoremobile/api/RetrofitClient.java b/app/src/main/java/com/example/petstoremobile/api/RetrofitClient.java index 36a2f04e..69e50d82 100644 --- a/app/src/main/java/com/example/petstoremobile/api/RetrofitClient.java +++ b/app/src/main/java/com/example/petstoremobile/api/RetrofitClient.java @@ -14,7 +14,6 @@ public class RetrofitClient { //base URL public static final String BASE_URL = "http://10.0.2.2:8080/api/"; //for emulator testing change to computer ip if using hardware to test - private static Retrofit retrofit = null; public static Retrofit getClient(Context context) { 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 index bec68a2f..f72d6836 100644 --- a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AdoptionFragment.java @@ -14,6 +14,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; +import android.widget.ImageButton; + import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.AdoptionAdapter; import com.example.petstoremobile.fragments.ListFragment; @@ -30,12 +32,15 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop private AdoptionAdapter adapter; private SwipeRefreshLayout swipeRefreshLayout; private EditText etSearch; + private ImageButton hamburger; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_adoption, container, false); + hamburger = view.findViewById(R.id.btnHamburger); + loadAdoptionData(); // Replace with actual API call when backend is ready setupRecyclerView(view); @@ -45,6 +50,15 @@ public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdop FloatingActionButton fabAddAdoption = view.findViewById(R.id.fabAddAdoption); fabAddAdoption.setOnClickListener(v -> openAdoptionDetails(-1)); + //Make the hamburger button open the drawer from listFragment + hamburger.setOnClickListener(v -> { + ListFragment listFragment = (ListFragment) getParentFragment(); + //if list fragment is found then use its helper function to open the drawer + if (listFragment != null) { + listFragment.openDrawer(); + } + }); + return view; } diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java index a1bce0b2..09ad489b 100644 --- a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/AppointmentFragment.java @@ -14,6 +14,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; +import android.widget.ImageButton; + import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.AppointmentAdapter; import com.example.petstoremobile.fragments.ListFragment; @@ -30,12 +32,15 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. private AppointmentAdapter adapter; private SwipeRefreshLayout swipeRefreshLayout; private EditText etSearch; + private ImageButton hamburger; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_appointment, container, false); + hamburger = view.findViewById(R.id.btnHamburger); + loadAppointmentData(); // TODO: Replace with actual API call when backend is ready setupRecyclerView(view); setupSearch(view); @@ -44,6 +49,15 @@ public class AppointmentFragment extends Fragment implements AppointmentAdapter. FloatingActionButton fabAddAppointment = view.findViewById(R.id.fabAddAppointment); fabAddAppointment.setOnClickListener(v -> openAppointmentDetails(-1)); + //Make the hamburger button open the drawer from listFragment + hamburger.setOnClickListener(v -> { + ListFragment listFragment = (ListFragment) getParentFragment(); + //if list fragment is found then use its helper function to open the drawer + if (listFragment != null) { + listFragment.openDrawer(); + } + }); + return view; } diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java index 12baebe4..38546e83 100644 --- a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/InventoryFragment.java @@ -14,6 +14,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; +import android.widget.ImageButton; + import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.InventoryAdapter; import com.example.petstoremobile.fragments.ListFragment; @@ -30,12 +32,15 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn private InventoryAdapter adapter; private SwipeRefreshLayout swipeRefreshLayout; private EditText etSearch; + private ImageButton hamburger; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_inventory, container, false); + hamburger = view.findViewById(R.id.btnHamburger); + loadInventoryData(); // TODO: Replace with actual API call when backend is ready setupRecyclerView(view); setupSearch(view); @@ -44,6 +49,15 @@ public class InventoryFragment extends Fragment implements InventoryAdapter.OnIn FloatingActionButton fabAddInventory = view.findViewById(R.id.fabAddInventory); fabAddInventory.setOnClickListener(v -> openInventoryDetails(-1)); + //Make the hamburger button open the drawer from listFragment + hamburger.setOnClickListener(v -> { + ListFragment listFragment = (ListFragment) getParentFragment(); + //if list fragment is found then use its helper function to open the drawer + if (listFragment != null) { + listFragment.openDrawer(); + } + }); + return view; } diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java index b774e104..fee68f02 100644 --- a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/PetFragment.java @@ -5,11 +5,15 @@ 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.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.EditText; import android.widget.ImageButton; import android.widget.Toast; @@ -34,9 +38,12 @@ import retrofit2.Response; public class PetFragment extends Fragment implements PetAdapter.OnPetClickListener { private List petList = new ArrayList<>(); + private List filteredList = new ArrayList<>(); private ImageButton hamburger; private PetAdapter adapter; private PetApi api; + private SwipeRefreshLayout swipeRefreshLayout; + private EditText etSearch; //load pet view @Override @@ -50,7 +57,9 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen hamburger = view.findViewById(R.id.btnHamburger); setupRecyclerView(view); - loadPetData(); //TODO: Replace this with actual data when backend is working + setupSearch(view); + setupSwipeRefresh(view); + loadPetData(); //Add button to opens the add dialog @@ -69,13 +78,48 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen return view; } + private void setupSearch(View view) { + etSearch = view.findViewById(R.id.etSearchPet); + 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) { + filterPets(s.toString()); + } + @Override public void afterTextChanged(Editable s) {} + }); + } + + private void filterPets(String query) { + filteredList.clear(); + if (query.isEmpty()) { + filteredList.addAll(petList); + } else { + String lower = query.toLowerCase(); + for (Pet p : petList) { + if (p.getPetName().toLowerCase().contains(lower) + || p.getPetSpecies().toLowerCase().contains(lower) + || p.getPetBreed().toLowerCase().contains(lower)) { + filteredList.add(p); + } + } + } + adapter.notifyDataSetChanged(); + } + + private void setupSwipeRefresh(View view) { + swipeRefreshLayout = view.findViewById(R.id.swipeRefreshPet); + swipeRefreshLayout.setOnRefreshListener(() -> { + loadPetData(); + }); + } + //Open pet profile private void openPetProfile(int position) { PetProfileFragment profileFragment = new PetProfileFragment(); //Make a bundle to pass data to the profile fragment Bundle args = new Bundle(); - Pet pet = petList.get(position); + Pet pet = filteredList.get(position); args.putInt("petId", pet.getPetId()); args.putString("petName", pet.getPetName()); args.putString("petSpecies", pet.getPetSpecies()); @@ -89,7 +133,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen //get ListFragment to load the the pet profile view ListFragment listFragment = (ListFragment) getParentFragment(); - if (profileFragment != null) { + if (listFragment != null) { listFragment.loadFragment(profileFragment); } } @@ -105,23 +149,6 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen } } - // Called by PetDetailFragment when save or delete is done - //TODO: REPLACE THIS WITH JUST RELOADING THE LIST BY FETCHING FROM THE BACKEND -// public void onPetSaved(int position, Pet pet) { -// if (position == -1) { -// petList.add(pet); -// adapter.notifyItemInserted(petList.size() - 1); -// } else { -// petList.set(position, pet); -// adapter.notifyItemChanged(position); -// } -// } -// -// public void onPetDeleted(int position) { -// petList.remove(position); -// adapter.notifyItemRemoved(position); -// } - // Called by PetAdapter when a row is clicked to open the details view @Override public void onPetClick(int position) { @@ -130,9 +157,15 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen // Helper function to get a list of all pets from the backend private void loadPetData() { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(true); + } api.getAllPets(0, 100).enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } if (response.isSuccessful() && response.body() != null) { petList.clear(); @@ -143,7 +176,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen .collect(java.util.stream.Collectors.toList()); petList.addAll(pets); - adapter.notifyDataSetChanged(); + filterPets(etSearch.getText().toString()); } else { Log.e("onResponse: ", response.message()); @@ -151,7 +184,10 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen } @Override - public void onFailure(Call> call, Throwable t) { // 4. here + public void onFailure(Call> call, Throwable t) { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } Toast.makeText(getContext(), "Failed to load pets", Toast.LENGTH_SHORT).show(); Log.e("onFailure: ", t.getMessage()); @@ -162,7 +198,7 @@ public class PetFragment extends Fragment implements PetAdapter.OnPetClickListen //set up the recyclerview and adapter private void setupRecyclerView(View view) { RecyclerView recyclerView = view.findViewById(R.id.recyclerViewPets); - adapter = new PetAdapter(petList, this); + adapter = new PetAdapter(filteredList, this); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setAdapter(adapter); } diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java index 14117a12..65e14d4b 100644 --- a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ProductFragment.java @@ -14,6 +14,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; +import android.widget.ImageButton; + import com.example.petstoremobile.R; import com.example.petstoremobile.adapters.ProductAdapter; import com.example.petstoremobile.fragments.ListFragment; @@ -30,12 +32,15 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc private ProductAdapter adapter; private SwipeRefreshLayout swipeRefreshLayout; private EditText etSearch; + private ImageButton hamburger; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_product, container, false); + hamburger = view.findViewById(R.id.btnHamburger); + loadProductData(); // TODO: Replace with actual API call when backend is ready setupRecyclerView(view); setupSearch(view); @@ -44,6 +49,15 @@ public class ProductFragment extends Fragment implements ProductAdapter.OnProduc FloatingActionButton fabAddProduct = view.findViewById(R.id.fabAddProduct); fabAddProduct.setOnClickListener(v -> openProductDetails(-1)); + //Make the hamburger button open the drawer from listFragment + hamburger.setOnClickListener(v -> { + ListFragment listFragment = (ListFragment) getParentFragment(); + //if list fragment is found then use its helper function to open the drawer + if (listFragment != null) { + listFragment.openDrawer(); + } + }); + return view; } diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java index b3e61600..24266527 100644 --- a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/ServiceFragment.java @@ -5,11 +5,15 @@ 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.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.EditText; import android.widget.ImageButton; import android.widget.Toast; @@ -34,9 +38,12 @@ import retrofit2.Response; public class ServiceFragment extends Fragment implements ServiceAdapter.OnServiceClickListener { private List serviceList = new ArrayList<>(); + private List filteredList = new ArrayList<>(); private ServiceAdapter adapter; private ImageButton hamburger; private ServiceApi api; + private SwipeRefreshLayout swipeRefreshLayout; + private EditText etSearch; //load service view @Override @@ -48,6 +55,8 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic hamburger = view.findViewById(R.id.btnHamburger); setupRecyclerView(view); + setupSearch(view); + setupSwipeRefresh(view); loadServiceData(); //Add button to opens the add dialog @@ -66,6 +75,40 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic return view; } + private void setupSearch(View view) { + etSearch = view.findViewById(R.id.etSearchService); + 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) { + filterServices(s.toString()); + } + @Override public void afterTextChanged(Editable s) {} + }); + } + + private void filterServices(String query) { + filteredList.clear(); + if (query.isEmpty()) { + filteredList.addAll(serviceList); + } else { + String lower = query.toLowerCase(); + for (Service s : serviceList) { + if (s.getServiceName().toLowerCase().contains(lower) + || s.getServiceDesc().toLowerCase().contains(lower)) { + filteredList.add(s); + } + } + } + adapter.notifyDataSetChanged(); + } + + private void setupSwipeRefresh(View view) { + swipeRefreshLayout = view.findViewById(R.id.swipeRefreshService); + swipeRefreshLayout.setOnRefreshListener(() -> { + loadServiceData(); + }); + } + //Open the service detail view depending on the mode private void openServiceDetails(int position) { ServiceDetailFragment detailFragment = new ServiceDetailFragment(); @@ -76,7 +119,7 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic //if editing a service, add the service data to the bundle if (position != -1) { - Service service = serviceList.get(position); + Service service = filteredList.get(position); args.putInt("serviceId", service.getServiceId()); args.putString("serviceName", service.getServiceName()); args.putString("serviceDesc", service.getServiceDesc()); @@ -96,22 +139,6 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic } } - // Called by ServiceDetailFragment when save or delete is done - public void onServiceSaved(int position, Service service) { - if (position == -1) { - serviceList.add(service); - adapter.notifyItemInserted(serviceList.size() - 1); - } else { - serviceList.set(position, service); - adapter.notifyItemChanged(position); - } - } - - public void onServiceDeleted(int position) { - serviceList.remove(position); - adapter.notifyItemRemoved(position); - } - // Called by ServiceAdapter when a row is clicked to open the details view @Override public void onServiceClick(int position) { @@ -120,9 +147,15 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic // Helper function to get a list of all services from the backend private void loadServiceData() { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(true); + } api.getAllServices(0, 100).enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } if (response.isSuccessful() && response.body() != null) { serviceList.clear(); @@ -133,7 +166,7 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic .collect(java.util.stream.Collectors.toList()); serviceList.addAll(services); - adapter.notifyDataSetChanged(); + filterServices(etSearch.getText().toString()); } else { Log.e("onResponse: ", response.message()); @@ -142,6 +175,9 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic @Override public void onFailure(Call> call, Throwable t) { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } if (getContext() != null) { Toast.makeText(getContext(), "Failed to load services", Toast.LENGTH_SHORT).show(); @@ -154,7 +190,7 @@ public class ServiceFragment extends Fragment implements ServiceAdapter.OnServic //set up the recyclerview and adapter private void setupRecyclerView(View view) { RecyclerView recyclerView = view.findViewById(R.id.recyclerViewServices); - adapter = new ServiceAdapter(serviceList, this); + adapter = new ServiceAdapter(filteredList, this); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setAdapter(adapter); } diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java index c2a82d4a..545aa026 100644 --- a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/SupplierFragment.java @@ -5,11 +5,15 @@ 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.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.EditText; import android.widget.ImageButton; import android.widget.Toast; @@ -34,9 +38,12 @@ import retrofit2.Response; public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupplierClickListener { private List supplierList = new ArrayList<>(); + private List filteredList = new ArrayList<>(); private SupplierAdapter adapter; private ImageButton hamburger; private SupplierApi api; + private SwipeRefreshLayout swipeRefreshLayout; + private EditText etSearch; //load supplier view @Override @@ -48,6 +55,8 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp hamburger = view.findViewById(R.id.btnHamburger); setupRecyclerView(view); + setupSearch(view); + setupSwipeRefresh(view); loadSupplierData(); //Add button to opens the add dialog @@ -66,6 +75,41 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp return view; } + private void setupSearch(View view) { + etSearch = view.findViewById(R.id.etSearchSupplier); + 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) { + filterSuppliers(s.toString()); + } + @Override public void afterTextChanged(Editable s) {} + }); + } + + private void filterSuppliers(String query) { + filteredList.clear(); + if (query.isEmpty()) { + filteredList.addAll(supplierList); + } else { + String lower = query.toLowerCase(); + for (Supplier s : supplierList) { + if (s.getSupCompany().toLowerCase().contains(lower) + || s.getSupContactFirstName().toLowerCase().contains(lower) + || s.getSupContactLastName().toLowerCase().contains(lower)) { + filteredList.add(s); + } + } + } + adapter.notifyDataSetChanged(); + } + + private void setupSwipeRefresh(View view) { + swipeRefreshLayout = view.findViewById(R.id.swipeRefreshSupplier); + swipeRefreshLayout.setOnRefreshListener(() -> { + loadSupplierData(); + }); + } + //Open the supplier detail view depending on the mode private void openSupplierDetails(int position) { SupplierDetailFragment detailFragment = new SupplierDetailFragment(); @@ -76,7 +120,7 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp //if editing a supplier, add the supplier data to the bundle if (position != -1) { - Supplier supplier = supplierList.get(position); + Supplier supplier = filteredList.get(position); args.putInt("supId", supplier.getSupId()); args.putString("supCompany", supplier.getSupCompany()); args.putString("supContactFirstName", supplier.getSupContactFirstName()); @@ -97,21 +141,6 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp } } - // Called by SupplierDetailFragment when save or delete is done - public void onSupplierSaved(int position, Supplier supplier) { - if (position == -1) { - supplierList.add(supplier); - adapter.notifyItemInserted(supplierList.size() - 1); - } else { - supplierList.set(position, supplier); - adapter.notifyItemChanged(position); - } - } - - public void onSupplierDeleted(int position) { - supplierList.remove(position); - adapter.notifyItemRemoved(position); - } // Called by SupplierAdapter when a row is clicked to open the details view @Override @@ -121,9 +150,15 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp // Helper function to get a list of all suppliers from the backend private void loadSupplierData() { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(true); + } api.getAllSuppliers(0, 100).enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } if (response.isSuccessful() && response.body() != null) { supplierList.clear(); @@ -134,7 +169,7 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp .collect(java.util.stream.Collectors.toList()); supplierList.addAll(suppliers); - adapter.notifyDataSetChanged(); + filterSuppliers(etSearch.getText().toString()); } else { Log.e("onResponse: ", response.message()); @@ -143,6 +178,9 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp @Override public void onFailure(Call> call, Throwable t) { + if (swipeRefreshLayout != null) { + swipeRefreshLayout.setRefreshing(false); + } if (getContext() != null) { Toast.makeText(getContext(), "Failed to load suppliers", Toast.LENGTH_SHORT).show(); @@ -155,7 +193,7 @@ public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupp //set up the recyclerview and adapter private void setupRecyclerView(View view) { RecyclerView recyclerView = view.findViewById(R.id.recyclerViewSuppliers); - adapter = new SupplierAdapter(supplierList, this); + adapter = new SupplierAdapter(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 index 6bdf9ad5..32c06996 100644 --- 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 @@ -4,6 +4,10 @@ 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.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; @@ -149,9 +153,19 @@ public class AdoptionDetailFragment extends Fragment { } private void setupSpinner() { - ArrayAdapter adapter = new ArrayAdapter<>(requireContext(), + ArrayAdapter adapter = new ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, - new String[]{"Approved", "Pending", "Rejected"}); + new String[]{"Approved", "Pending", "Rejected"}) { + + //Override the getView method for the spinner to make the text color darker for more readability + @NonNull + @Override + public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { + View view = super.getView(position, convertView, parent); + ((TextView) view).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_dark)); + return view; + } + }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerAdoptionStatus.setAdapter(adapter); } diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java index 3b60a70f..bfd2d6b5 100644 --- a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/AppointmentDetailFragment.java @@ -3,7 +3,12 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments; // Uses InputValidator for detailed field validation and ActivityLogger to log all changes. +import android.content.res.Configuration; import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; @@ -154,9 +159,19 @@ public class AppointmentDetailFragment extends Fragment { } private void setupSpinner() { - ArrayAdapter adapter = new ArrayAdapter<>(requireContext(), + ArrayAdapter adapter = new ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, - new String[]{"Confirmed", "Pending", "Cancelled"}); + new String[]{"Confirmed", "Pending", "Cancelled"}) { + + //Override the getView method for the spinner to make the text color darker for more readability + @NonNull + @Override + public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { + View view = super.getView(position, convertView, parent); + ((TextView) view).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_dark)); + return view; + } + }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerStatus.setAdapter(adapter); } diff --git a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java index b7b89614..f7f53bcd 100644 --- a/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java +++ b/app/src/main/java/com/example/petstoremobile/fragments/listfragments/detailfragments/PetDetailFragment.java @@ -2,6 +2,9 @@ package com.example.petstoremobile.fragments.listfragments.detailfragments; import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; @@ -51,6 +54,7 @@ public class PetDetailFragment extends Fragment { listFragment.getChildFragmentManager().popBackStack(); } }); + //set save and delete button to run the appropriate method btnSavePet.setOnClickListener(v -> savePet()); btnDeletePet.setOnClickListener(v -> deletePet()); @@ -122,11 +126,21 @@ public class PetDetailFragment extends Fragment { //helper function to set up the spinner menu for pet status private void setupSpinner() { - ArrayAdapter adapter = new ArrayAdapter<>(requireContext(), + ArrayAdapter adapter = new ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, - new String[]{"Available", "Adopted"}); + new String[]{"Available", "Adopted"}) { + + //Override the getView method for the spinner to make the text color darker for more readability + @NonNull + @Override + public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { + View view = super.getView(position, convertView, parent); + ((TextView) view).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_dark)); + return view; + } + }; adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerPetStatus.setAdapter(adapter); } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index afe1e884..9322e775 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -79,7 +79,8 @@ android:layout_height="wrap_content" android:hint="Enter username" android:inputType="text" - android:layout_marginBottom="16dp"/> + android:layout_marginBottom="16dp" + android:textColor="@color/text_dark"/> + android:layout_marginBottom="24dp" + android:textColor="@color/text_dark"/>