From 1360fa1592b4fe6a4a7c0f6e6b749eda80faba2a Mon Sep 17 00:00:00 2001 From: Alex <78383757+Lextical@users.noreply.github.com> Date: Thu, 26 Mar 2026 22:07:51 -0600 Subject: [PATCH] Added petprofile images and uploads in petprofilefragment --- .../example/petstoremobile/api/PetApi.java | 11 +++ .../PetProfileFragment.java | 95 ++++++++++++++++++- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/android/app/src/main/java/com/example/petstoremobile/api/PetApi.java b/android/app/src/main/java/com/example/petstoremobile/api/PetApi.java index 35fccfbc..e2eb3090 100644 --- a/android/app/src/main/java/com/example/petstoremobile/api/PetApi.java +++ b/android/app/src/main/java/com/example/petstoremobile/api/PetApi.java @@ -3,17 +3,23 @@ package com.example.petstoremobile.api; import com.example.petstoremobile.dtos.PageResponse; import com.example.petstoremobile.dtos.PetDTO; +import okhttp3.MultipartBody; import retrofit2.Call; import retrofit2.http.Body; import retrofit2.http.DELETE; import retrofit2.http.GET; +import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.PUT; +import retrofit2.http.Part; import retrofit2.http.Path; import retrofit2.http.Query; //api calls to CRUD pets public interface PetApi { + // endpoint for downloading the pet's image file + String PET_IMAGE_PATH = "api/v1/pets/%d/image"; + // Get all pets @GET("api/v1/pets") Call> getAllPets( @@ -37,4 +43,9 @@ public interface PetApi { @DELETE("api/v1/pets/{id}") Call deletePet(@Path("id") Long id); + // Upload pet image + @Multipart + @POST("api/v1/pets/{id}/image") + Call uploadPetImage(@Path("id") Long id, @Part MultipartBody.Part image); + } diff --git a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/listprofilefragments/PetProfileFragment.java b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/listprofilefragments/PetProfileFragment.java index 280789e8..454d263b 100644 --- a/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/listprofilefragments/PetProfileFragment.java +++ b/android/app/src/main/java/com/example/petstoremobile/fragments/listfragments/listprofilefragments/PetProfileFragment.java @@ -16,26 +16,42 @@ import androidx.core.content.FileProvider; import androidx.fragment.app.Fragment; import android.provider.MediaStore; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.example.petstoremobile.R; +import com.example.petstoremobile.api.PetApi; +import com.example.petstoremobile.api.RetrofitClient; import com.example.petstoremobile.fragments.ListFragment; import com.example.petstoremobile.fragments.listfragments.detailfragments.PetDetailFragment; import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; import java.util.Locale; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.RequestBody; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + public class PetProfileFragment extends Fragment { private TextView tvPetName, tvPetSpecies, tvPetBreed, tvPetAge, tvPetPrice; private Button btnBack, btnEditPet, btnChangePhoto; private ImageView imgPet; private Uri photoUri; + private int petId; // launchers for camera and gallery private ActivityResultLauncher galleryLauncher; @@ -53,8 +69,7 @@ public class PetProfileFragment extends Fragment { result -> { if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) { Uri selectedImage = result.getData().getData(); - imgPet.setImageURI(selectedImage); - // TODO: SAVE CHANGED PHOTO TO DATABASE + uploadPetImage(selectedImage); } } ); @@ -64,9 +79,7 @@ public class PetProfileFragment extends Fragment { new ActivityResultContracts.TakePicture(), success -> { if (success) { - imgPet.setImageURI(null); - imgPet.setImageURI(photoUri); - // TODO: SAVE CHANGED PHOTO TO DATABASE + uploadPetImage(photoUri); } } ); @@ -112,11 +125,15 @@ public class PetProfileFragment extends Fragment { // Set pet details to display if (getArguments() != null) { + petId = getArguments().getInt("petId"); tvPetName.setText(getArguments().getString("petName")); tvPetSpecies.setText(getArguments().getString("petSpecies")); tvPetBreed.setText(getArguments().getString("petBreed")); tvPetAge.setText(String.format(Locale.getDefault(), "%d yr(s)", getArguments().getInt("petAge"))); tvPetPrice.setText(String.format(Locale.getDefault(), "$%.2f", getArguments().getDouble("petPrice"))); + + // Load pet image from backend + loadPetImage(petId); } //set button click listeners @@ -169,6 +186,74 @@ public class PetProfileFragment extends Fragment { return view; } + // Helper function to load pet image from backend + private void loadPetImage(int petId) { + String imageUrl = RetrofitClient.BASE_URL + String.format(Locale.US, PetApi.PET_IMAGE_PATH, petId); + + Glide.with(this) + .load(imageUrl) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + .placeholder(R.drawable.placeholder) + .error(R.drawable.placeholder) + .into(imgPet); + } + + // Helper function to upload pet image to backend + private void uploadPetImage(Uri uri) { + try { + File file = getFileFromUri(uri); + if (file == null) return; + + // Create RequestBody for file upload + RequestBody requestFile = RequestBody.create(file, MediaType.parse(requireContext().getContentResolver().getType(uri))); + MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile); + + // Call the backend to upload the image + PetApi petApi = RetrofitClient.getPetApi(requireContext()); + petApi.uploadPetImage((long) petId, body).enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + Toast.makeText(requireContext(), "Pet photo updated successfully", Toast.LENGTH_SHORT).show(); + // Reload image after successful upload + loadPetImage(petId); + } else { + Toast.makeText(requireContext(), "Failed to upload pet photo", Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + Log.e("UPLOAD_PET_IMAGE", "Failure: " + t.getMessage()); + Toast.makeText(requireContext(), "Network error", Toast.LENGTH_SHORT).show(); + } + }); + } catch (Exception e) { + Log.e("UPLOAD_PET_IMAGE", "Error: " + e.getMessage()); + } + } + + // Helper function to create a temporary File object from a Uri for uploading + private File getFileFromUri(Uri uri) { + try { + InputStream inputStream = requireContext().getContentResolver().openInputStream(uri); + File tempFile = new File(requireContext().getCacheDir(), "upload_pet_image.jpg"); + FileOutputStream outputStream = new FileOutputStream(tempFile); + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, length); + } + outputStream.close(); + inputStream.close(); + return tempFile; + } catch (Exception e) { + Log.e("FILE_UTILS", "Error creating temp file", e); + return null; + } + } + private void launchCamera() { File photoFile = new File(requireContext().getCacheDir(), "pet_photo.jpg"); photoUri = FileProvider.getUriForFile(requireContext(), requireContext().getPackageName() + ".fileprovider", photoFile);