Added essential http request and methods for places image upload to server

This commit is contained in:
Pau 2025-05-22 23:03:40 +02:00
parent ef9092467f
commit d66f357753
7 changed files with 296 additions and 11 deletions

View File

@ -1,5 +1,7 @@
package com.example.acloc.activity;
import static com.example.acloc.utility.Constants.BASE_URL;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
@ -16,6 +18,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatButton;
import androidx.appcompat.widget.Toolbar;
import com.example.acloc.utility.UploadManager;
import com.ieslamar.acloc.R;
import com.example.acloc.api.LocationApiClient;
import com.example.acloc.model.Place;
@ -26,6 +29,10 @@ import com.example.acloc.utility.Helper;
import com.example.acloc.utility.SharedPref;
import com.google.android.material.textfield.TextInputEditText;
import com.google.gson.JsonObject;
import com.squareup.picasso.Picasso;
import org.json.JSONException;
import org.json.JSONObject;
import retrofit2.Call;
import retrofit2.Callback;
@ -46,6 +53,10 @@ public class AddNewPlaceActivity extends AppCompatActivity implements View.OnCli
private String place_uuid;
private static final int PICK_IMAGE_REQUEST = 200;
private Uri selectedImageUri;
private String imageUrl;
private String JSonString;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -140,14 +151,44 @@ public class AddNewPlaceActivity extends AppCompatActivity implements View.OnCli
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null) {
// Get the image URI
Uri selectedImageUri = data.getData();
selectedImageUri = data.getData();
// Set the image in the ImageView
ivPlacePhoto.setImageURI(selectedImageUri);
// Load image using Picasso
Picasso.get().load(selectedImageUri).into(ivPlacePhoto);
// Upload the image
uploadImageToServer(selectedImageUri);
}
}
private void uploadImageToServer(Uri imageUri) {
UploadManager.uploadImage(this, imageUri, new UploadManager.UploadCallback() {
@Override
public void onSuccess(String response) {
try {
JSONObject json = new JSONObject(response);
if (json.getBoolean("success")) {
String filename = json.getJSONObject("file").getString("filename");
imageUrl = BASE_URL + "public/" + filename;
JSonString = "[\"" + imageUrl + "\"]";
Helper.makeSnackBar(rlAddPlace, "Image uploaded successfully");
Log.d(TAG, "JsonString: " + JSonString);
} else {
Helper.makeSnackBar(rlAddPlace, "Upload failed");
}
} catch (JSONException e) {
Helper.makeSnackBar(rlAddPlace, "Response parsing error");
Log.e(TAG, "Failed to parse JSON", e);
}
}
@Override
public void onError(String message) {
Helper.makeSnackBar(rlAddPlace, "Upload failed: " + message);
Log.e(TAG, "Upload error:" + message);
}
});
}
private void onClickBtnSubmit() {
View[] views = {etPlaceName, etLatitude, etLongitude, etAddress, etPlaceDescription};
if (Helper.isEmptyFieldValidation(views)) {
@ -186,6 +227,7 @@ public class AddNewPlaceActivity extends AppCompatActivity implements View.OnCli
placeBody.addProperty("latitude", latitude);
placeBody.addProperty("longitude", longitude);
placeBody.addProperty("createdBy", createdBy);
placeBody.addProperty("images", JSonString);
String token = "Bearer " + SharedPref.getAccessToken(context);

View File

@ -1,20 +1,14 @@
package com.example.acloc.api;
import static com.example.acloc.utility.Constants.BASE_URL;
import com.example.acloc.service.AuthService;
import com.example.acloc.service.FavoriteService;
import com.example.acloc.service.PlaceService;
import com.example.acloc.service.ReportService;
import com.example.acloc.service.RoleService;
import com.example.acloc.service.UploadService;
import com.example.acloc.service.UserService;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Centralized API client that manages Retrofit instance and provides access to all service interfaces
@ -30,6 +24,7 @@ public class LocationApiClient {
private ReportService reportService;
private RoleService roleService;
private UserService userService;
private UploadService uploadService;
// Private constructor for singleton pattern
private LocationApiClient() {
@ -112,4 +107,11 @@ public class LocationApiClient {
}
return userService;
}
public UploadService getUploadService(){
if(uploadService == null){
uploadService = retrofit.create(UploadService.class);
}
return uploadService;
}
}

View File

@ -0,0 +1,14 @@
package com.example.acloc.service;
import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
public interface UploadService {
@Multipart
@POST("upload")
Call<ResponseBody> uploadImage(@Part MultipartBody.Part file);
}

View File

@ -0,0 +1 @@
package com.example.acloc.utility;

View File

@ -0,0 +1,47 @@
package com.example.acloc.utility;
import android.net.Uri;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
/**
* Utility class to handle image loading operations
* Separates image loading responsibility from activities
*/
public class ImageLoaderUtil {
/**
* Loads an image from a Uri into an ImageView
*
* @param imageUri Source Uri of the image
* @param targetView Target ImageView where the image will be displayed
*/
public static void loadImage(Uri imageUri, ImageView targetView) {
if (imageUri != null && targetView != null) {
Picasso.get().load(imageUri).into(targetView);
}
}
/**
* Loads an image from a Uri into an ImageView with placeholder and error handling
*
* @param imageUri Source Uri of the image
* @param targetView Target ImageView where the image will be displayed
* @param placeholderResId Resource ID for the placeholder image
* @param errorResId Resource ID for the error image
*/
public static void loadImageWithPlaceholder(
Uri imageUri,
ImageView targetView,
int placeholderResId,
int errorResId) {
if (imageUri != null && targetView != null) {
Picasso.get()
.load(imageUri)
.placeholder(placeholderResId)
.error(errorResId)
.into(targetView);
}
}
}

View File

@ -0,0 +1,106 @@
package com.example.acloc.utility;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
public class ImagePicker {
/**
* Get a file from a Uri.
*
* @param context The application context
* @param uri The Uri to get the file from
* @return The file
* @throws IOException
*/
public static File getFileFromUri(Context context, Uri uri) throws IOException {
String fileName = getFileName(context, uri);
File file = new File(context.getCacheDir(), fileName);
// Copy the file to the cache directory
try (InputStream inputStream = context.getContentResolver().openInputStream(uri);
OutputStream outputStream = new FileOutputStream(file)) {
if (inputStream == null) {
throw new IOException("Failed to open input stream.");
}
byte[] buffer = new byte[4 * 1024]; // 4k buffer
int read;
while ((read = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, read);
}
outputStream.flush();
return file;
}
}
/**
* Get the file name from a Uri
*
* @param context The application context
* @param uri The Uri to get the file name from
* @return The file name
*/
private static String getFileName(Context context, Uri uri) {
String result = null;
if (uri.getScheme().equals("content")) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (nameIndex != -1) {
result = cursor.getString(nameIndex);
}
}
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
/**
* Prepare a file part for a multipart request
*
* @param partName The name of the part
* @param file The file to upload
* @return The MultipartBody.Part
*/
public static MultipartBody.Part prepareFilePart(String partName, File file) {
// Get the MIME type
String extension = MimeTypeMap.getFileExtensionFromUrl(file.getPath());
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mimeType == null) {
// Default to image/jpeg if MIME type cannot be determined
mimeType = "image/jpeg";
}
// Create RequestBody instance from file
RequestBody requestFile = RequestBody.create(MediaType.parse(mimeType), file);
// MultipartBody.Part is used to send the actual file
return MultipartBody.Part.createFormData(partName, file.getName(), requestFile);
}
}

View File

@ -0,0 +1,73 @@
package com.example.acloc.utility;
import android.content.Context;
import android.net.Uri;
import com.example.acloc.api.LocationApiClient;
import com.example.acloc.service.UploadService;
import java.io.File;
import java.io.IOException;
import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class UploadManager {
public interface UploadCallback {
void onSuccess(String message);
void onError(String message);
}
/**
* Uploads an image to the server
*
* @param context Application context
* @param imageUri URI of the image to upload
* @param callback Callback to handle the upload result
*/
public static void uploadImage(Context context, Uri imageUri, UploadCallback callback) {
if (context == null || imageUri == null || callback == null) {
if (callback != null) {
callback.onError("Invalid parameters");
}
return;
}
try {
File file = ImagePicker.getFileFromUri(context, imageUri);
MultipartBody.Part filePart = ImagePicker.prepareFilePart("file", file);
performUpload(filePart, callback);
} catch (IOException e) {
callback.onError("Error preparing file: " + e.getMessage());
}
}
private static void performUpload(MultipartBody.Part filePart, UploadCallback callback) {
UploadService uploadService = LocationApiClient.getInstance().getUploadService();
uploadService.uploadImage(filePart).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
callback.onSuccess(response.body().string());
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
callback.onError("Server error: " + response.code());
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
callback.onError("Connection error: " + t.getMessage());
}
});
}
}