add pet image support
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -16,7 +17,10 @@ import android.widget.Spinner;
|
||||
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.auth.TokenManager;
|
||||
import com.example.petstoremobile.databinding.FragmentPetDetailBinding;
|
||||
import com.example.petstoremobile.dtos.DropdownDTO;
|
||||
@@ -24,18 +28,26 @@ import com.example.petstoremobile.dtos.PetDTO;
|
||||
import com.example.petstoremobile.utils.ActivityLogger;
|
||||
import com.example.petstoremobile.utils.DateTimeUtils;
|
||||
import com.example.petstoremobile.utils.DialogUtils;
|
||||
import com.example.petstoremobile.utils.FileUtils;
|
||||
import com.example.petstoremobile.utils.GlideUtils;
|
||||
import com.example.petstoremobile.utils.ImagePickerHelper;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.PetDetailViewModel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
/**
|
||||
* Fragment for displaying and editing pet details.
|
||||
@@ -45,14 +57,45 @@ public class PetDetailFragment extends Fragment {
|
||||
|
||||
private FragmentPetDetailBinding binding;
|
||||
private PetDetailViewModel viewModel;
|
||||
private ImagePickerHelper imagePickerHelper;
|
||||
private boolean isUpdatingUI = false;
|
||||
|
||||
private boolean hasImage = false;
|
||||
private boolean isImageChanged = false;
|
||||
private boolean isImageRemoved = false;
|
||||
private Uri photoUri;
|
||||
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(this).get(PetDetailViewModel.class);
|
||||
|
||||
imagePickerHelper = new ImagePickerHelper(this, "pet_photo.jpg", new ImagePickerHelper.ImagePickerListener() {
|
||||
@Override
|
||||
public void onImagePicked(Uri uri) {
|
||||
photoUri = uri;
|
||||
Glide.with(PetDetailFragment.this)
|
||||
.load(uri)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.skipMemoryCache(true)
|
||||
.into(binding.ivPetImage);
|
||||
hasImage = true;
|
||||
isImageChanged = true;
|
||||
isImageRemoved = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageRemoved() {
|
||||
photoUri = null;
|
||||
hasImage = false;
|
||||
isImageChanged = false;
|
||||
isImageRemoved = true;
|
||||
binding.ivPetImage.setImageResource(R.drawable.placeholder2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,6 +117,7 @@ public class PetDetailFragment extends Fragment {
|
||||
binding.btnBack.setOnClickListener(v -> navigateBack());
|
||||
binding.btnSavePet.setOnClickListener(v -> savePet());
|
||||
binding.btnDeletePet.setOnClickListener(v -> deletePet());
|
||||
binding.ivPetImage.setOnClickListener(v -> imagePickerHelper.showImagePickerDialog("Select Pet Image", hasImage));
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
@@ -197,14 +241,18 @@ public class PetDetailFragment extends Fragment {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
if (resource.data != null) {
|
||||
viewModel.setPetId(resource.data.getPetId());
|
||||
}
|
||||
String msg;
|
||||
if (viewModel.isEditing()) {
|
||||
ActivityLogger.logChange(requireContext(), "Pet", "UPDATED", (int) viewModel.getPetId());
|
||||
Toast.makeText(getContext(), "Pet updated successfully!", Toast.LENGTH_SHORT).show();
|
||||
msg = "Pet updated successfully!";
|
||||
} else {
|
||||
ActivityLogger.log(requireContext(), "Added new Pet: " + name);
|
||||
Toast.makeText(getContext(), "Pet added successfully!", Toast.LENGTH_SHORT).show();
|
||||
msg = "Pet added successfully!";
|
||||
}
|
||||
navigateToPetList();
|
||||
performPendingImageActions(msg);
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
Toast.makeText(getContext(), "Error: " + resource.message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
@@ -239,10 +287,75 @@ public class PetDetailFragment extends Fragment {
|
||||
if (getArguments() != null && getArguments().containsKey("petId")) {
|
||||
viewModel.setPetId(getArguments().getLong("petId"));
|
||||
loadPetData();
|
||||
loadPetImage();
|
||||
return;
|
||||
}
|
||||
|
||||
viewModel.setPetId(-1);
|
||||
hasImage = false;
|
||||
}
|
||||
|
||||
private void loadPetImage() {
|
||||
String imageUrl = baseUrl + String.format(Locale.US, PetApi.PET_IMAGE_PATH, viewModel.getPetId());
|
||||
String token = tokenManager.getToken();
|
||||
GlideUtils.loadImageWithToken(requireContext(), binding.ivPetImage, imageUrl, token, R.drawable.placeholder2, new GlideUtils.ImageLoadListener() {
|
||||
@Override
|
||||
public void onResourceReady() {
|
||||
hasImage = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFailed() {
|
||||
hasImage = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void performPendingImageActions(String successMsg) {
|
||||
if (isImageRemoved) {
|
||||
viewModel.deletePetImage().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(getContext(), successMsg + " (but image removal failed)", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
navigateToPetList();
|
||||
}
|
||||
});
|
||||
} else if (isImageChanged && photoUri != null) {
|
||||
uploadPetImageAndNavigate(photoUri, successMsg);
|
||||
} else {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
navigateToPetList();
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadPetImageAndNavigate(Uri uri, String successMsg) {
|
||||
File file = FileUtils.getFileFromUri(requireContext(), uri);
|
||||
if (file == null) {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
navigateToPetList();
|
||||
return;
|
||||
}
|
||||
|
||||
RequestBody requestFile = RequestBody.create(file, MediaType.parse(requireContext().getContentResolver().getType(uri)));
|
||||
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
|
||||
|
||||
viewModel.uploadPetImage(body).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource == null) return;
|
||||
setLoading(resource.status == Resource.Status.LOADING);
|
||||
if (resource.status != Resource.Status.LOADING) {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Toast.makeText(getContext(), successMsg, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(getContext(), successMsg + " (but image upload failed)", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
navigateToPetList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadPetData() {
|
||||
|
||||
@@ -239,6 +239,14 @@ public class PetDetailViewModel extends ViewModel {
|
||||
return petRepository.deletePet(petId);
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> uploadPetImage(okhttp3.MultipartBody.Part image) {
|
||||
return petRepository.uploadPetImage(petId, image);
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deletePetImage() {
|
||||
return petRepository.deletePetImage(petId);
|
||||
}
|
||||
|
||||
public LiveData<List<DropdownDTO>> getCustomerList() {
|
||||
return customerList;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,31 @@
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pet Image"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivPetImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/placeholder2"
|
||||
android:background="@color/text_light"
|
||||
android:clickable="true"
|
||||
android:focusable="true"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
Reference in New Issue
Block a user