A Comprehensive Guide to Storing Images with Supabase and Managing Data with Firebase Realtime database.
// Add the Retrofit library
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// Add the Volley library
implementation("com.android.volley:volley:1.2.0")
// Add the OkHttp library
implementation("com.squareup.okhttp3:okhttp:4.9.1")
implementation("com.github.bumptech.glide:glide:4.16.0")
//Circle image view
implementation("de.hdodenhof:circleimageview:3.1.0")
<uses-permission android:name="android.permission.INTERNET" />
<!-- Permissions for Android 12L (API level 32) or lower-->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<!-- Permissions for Android 13 (API level 33) or higher -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- Permissions for Android 14 (API level 34) or higher -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="<http://schemas.android.com/apk/res/android>"
xmlns:app="<http://schemas.android.com/apk/res-auto>"
xmlns:tools="<http://schemas.android.com/tools>"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".admin.CreateWorkshopActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:navigationIcon="@drawable/arrow_left"
app:title="Create Workshop" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:hint="Title">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ed_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_start_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:hint="Start Date">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ed_start_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:cursorVisible="false"
android:focusable="false"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/til_end_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:hint="End Date">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ed_end_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:cursorVisible="false"
android:focusable="false"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:hint="Description">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ed_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:lines="5" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
app:cardUseCompatPadding="true">
<ImageView
android:id="@+id/img_image"
android:layout_width="match_parent"
android:layout_height="150dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_select_image"
style="@style/Widget.Material3.Button.IconButton.Filled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:icon="@drawable/plus_circle" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButton
android:id="@+id/create"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create" />
</LinearLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
package com.example.techsupporthub.admin;
import android.Manifest;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import com.example.techsupporthub.R;
import com.example.techsupporthub.model.Workshop;
import com.example.techsupporthub.utils.Config;
import com.example.techsupporthub.utils.Constants;
import com.example.techsupporthub.utils.Utils;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.datepicker.MaterialDatePicker;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class CreateWorkshopActivity extends AppCompatActivity {
private static final int REQUEST_PERMISSION_CODE = 101;
private final OkHttpClient client = new OkHttpClient();
private ImageView imgImage;
private MaterialButton btnSelectImage;
private MaterialButton createButton;
private TextInputEditText edTitle;
private TextInputEditText edStartDate;
private TextInputEditText edEndDate;
private TextInputEditText edDescription;
private TextInputLayout tilStartDate;
private TextInputLayout tilEndDate;
private ActivityResultLauncher<String[]> requestPermissionLauncher;
private ActivityResultLauncher<Intent> pickImageLauncher;
private ProgressDialog progressDialog;
private Uri selectedImageUri;
private String startDate;
private String endDate;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_workshop);
MaterialToolbar toolbar = findViewById(R.id.toolbar);
toolbar.setNavigationOnClickListener(v -> finish());
imgImage = findViewById(R.id.img_image);
btnSelectImage = findViewById(R.id.btn_select_image);
createButton = findViewById(R.id.create);
edTitle = findViewById(R.id.ed_title);
edStartDate = findViewById(R.id.ed_start_date);
edEndDate = findViewById(R.id.ed_end_date);
edDescription = findViewById(R.id.ed_description);
tilStartDate = findViewById(R.id.til_start_date);
tilEndDate = findViewById(R.id.til_end_date);
// Initialize permission launcher
requestPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(),
permissions -> {
boolean allGranted = true;
for (boolean isGranted : permissions.values()) {
allGranted = allGranted && isGranted;
}
if (allGranted) {
openImagePicker();
} else {
Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
}
}
);
// Initialize image picker launcher
pickImageLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
selectedImageUri = result.getData().getData();
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), selectedImageUri);
imgImage.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "Failed to load image", Toast.LENGTH_SHORT).show();
}
}
}
);
btnSelectImage.setOnClickListener(v -> requestPermissionsBasedOnVersion());
edStartDate.setOnClickListener(v -> openStartDatePicker());
edEndDate.setOnClickListener(v -> openEndDatePicker());
createButton.setOnClickListener(v -> validateAndSendData());
}
private void requestPermissionsBasedOnVersion() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
requestPermissionLauncher.launch(new String[]{
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
});
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissionLauncher.launch(new String[]{
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_VIDEO
});
} else {
requestPermissionLauncher.launch(new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE
});
}
}
private void openImagePicker() {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
pickImageLauncher.launch(intent);
}
private void openStartDatePicker() {
MaterialDatePicker<Long> datePicker = MaterialDatePicker.Builder.datePicker()
.setTitleText("Select Start Date")
.setSelection(MaterialDatePicker.todayInUtcMilliseconds())
.build();
datePicker.show(getSupportFragmentManager(), "DATE_PICKER_START");
datePicker.addOnPositiveButtonClickListener(selection -> {
startDate = formatDate(selection);
edStartDate.setText(startDate);
});
}
private void openEndDatePicker() {
MaterialDatePicker<Long> datePicker = MaterialDatePicker.Builder.datePicker()
.setTitleText("Select End Date")
.setSelection(MaterialDatePicker.todayInUtcMilliseconds())
.build();
datePicker.show(getSupportFragmentManager(), "DATE_PICKER_END");
datePicker.addOnPositiveButtonClickListener(selection -> {
endDate = formatDate(selection);
edEndDate.setText(endDate);
});
}
private String formatDate(long timeInMillis) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
return dateFormat.format(new Date(timeInMillis));
}
private void validateAndSendData() {
String title = edTitle.getText().toString().trim();
String description = edDescription.getText().toString().trim();
if (title.isEmpty()) {
edTitle.setError("Title is required");
return;
}
if (startDate == null || startDate.isEmpty()) {
tilStartDate.setError("Start date is required");
return;
}
if (endDate == null || endDate.isEmpty()) {
tilEndDate.setError("End date is required");
return;
}
if (description.isEmpty()) {
edDescription.setError("Description is required");
return;
}
if (selectedImageUri == null) {
Toast.makeText(this, "Please select an image", Toast.LENGTH_SHORT).show();
return;
}
uploadImage(title, description, startDate, endDate);
}
private void uploadImage(String title, String description, String startDate, String endDate) {
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Uploading image...");
progressDialog.setCancelable(false);
progressDialog.show();
// Generate a unique file name
String uniqueFileName = UUID.randomUUID().toString() + getFileExtension(selectedImageUri);
// Convert Uri to byte array
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), selectedImageUri);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// Determine the MIME type and compress the bitmap accordingly
String mimeType = getContentResolver().getType(selectedImageUri);
if ("image/png".equals(mimeType)) {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
} else {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
}
byte[] byteArray = byteArrayOutputStream.toByteArray();
MediaType mediaType = MediaType.parse(mimeType);
RequestBody requestFile = RequestBody.create(mediaType, byteArray);
MultipartBody.Part body = MultipartBody.Part.createFormData("file", uniqueFileName, requestFile);
MultipartBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", uniqueFileName, requestFile)
.build();
String uploadUrl = Config.SUPABASE_URL + "/storage/v1/object/" + Config.STORAGE_BUCKET + "/" + uniqueFileName;
Request request = new Request.Builder()
.url(uploadUrl)
.post(requestBody)
.addHeader("Authorization", Config.AUTHORIZATION_TOKEN)
.addHeader("apiKey", Config.API_KEY)
.build();
client.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
runOnUiThread(() -> {
progressDialog.dismiss();
Toast.makeText(CreateWorkshopActivity.this, "Upload failed: " + e.getMessage(), Toast.LENGTH_SHORT).show();
});
}
@Override
public void onResponse(okhttp3.Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String responseBody = response.body().string();
Log.d("SUPABASE_ONE_RESPONSE", responseBody);
String imageUrl = Config.SUPABASE_URL + "/storage/v1/object/" + Config.STORAGE_BUCKET + "/" + uniqueFileName;
runOnUiThread(() -> {
progressDialog.dismiss();
Glide.with(CreateWorkshopActivity.this).load(imageUrl).into(imgImage);
sendToDB(title, description, startDate, endDate, imageUrl);
});
} else {
runOnUiThread(() -> {
progressDialog.dismiss();
Toast.makeText(CreateWorkshopActivity.this, "Upload failed", Toast.LENGTH_SHORT).show();
});
Log.d("SUPABASE_ONE", response.toString());
}
}
});
} catch (IOException e) {
e.printStackTrace();
runOnUiThread(() -> {
progressDialog.dismiss();
Toast.makeText(this, "Failed to load image", Toast.LENGTH_SHORT).show();
});
}
}
private String getFileExtension(Uri uri) {
ContentResolver contentResolver = getContentResolver();
String mimeType = contentResolver.getType(uri);
if ("image/png".equals(mimeType)) {
return ".png";
} else if ("image/jpeg".equals(mimeType) || "image/jpg".equals(mimeType)) {
return ".jpg";
} else {
return ".jpg"; // Default to JPEG if unknown
}
}
private void sendToDB(String title, String description, String startDate, String endDate, String imageUrl) {
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Uploading data...");
progressDialog.setCancelable(false);
progressDialog.show();
// Generate a unique ID for the workshop
String workshopId = Utils.getDatabaseReference(Constants.WORKSHOP_DB).push().getKey();
// Create a Workshop object
Workshop workshop = new Workshop(
workshopId,
title,
description,
startDate,
endDate,
imageUrl,
System.currentTimeMillis()
);
// Push the workshop to Firebase Realtime Database
if (workshopId != null) {
Utils.getDatabaseReference(Constants.WORKSHOP_DB).child(workshopId).setValue(workshop)
.addOnSuccessListener(aVoid -> {
progressDialog.dismiss();
Toast.makeText(CreateWorkshopActivity.this, "Data sent to DB", Toast.LENGTH_SHORT).show();
finish();
})
.addOnFailureListener(e -> {
progressDialog.dismiss();
Toast.makeText(CreateWorkshopActivity.this, "Failed to send data to DB: " + e.getMessage(), Toast.LENGTH_SHORT).show();
});
} else {
progressDialog.dismiss();
Toast.makeText(this, "Failed to generate a unique ID", Toast.LENGTH_SHORT).show();
}
}
}
package com.example.techsupporthub.utils;
public class Config {
// Supabase URL
public static final String SUPABASE_URL = "<https://wizudywwslwlpfxaajvf.supabase.co>";
// Authorization Token
public static final String AUTHORIZATION_TOKEN = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6IndpenVkeXd3c2x3bHBmeGFhanZmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzgzNTgzMjEsImV4cCI6MjA1MzkzNDMyMX0.10DBdAyiAk1Z-Q18GrcN1wixMekylL16OFpMFPdPnlI";
// API Key
public static final String API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6IndpenVkeXd3c2x3bHBmeGFhanZmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzgzNTgzMjEsImV4cCI6MjA1MzkzNDMyMX0.10DBdAyiAk1Z-Q18GrcN1wixMekylL16OFpMFPdPnlI";
// Storage Bucket
public static final String STORAGE_BUCKET = "rest_test";
}
package com.example.techsupporthub.utils;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
public class Utils {
/**
* Returns a DatabaseReference for the specified child/table.
*
* @param child The child or table name.
* @return A DatabaseReference for the specified child/table.
*/
public static DatabaseReference getDatabaseReference(String child) {
return FirebaseDatabase.getInstance().getReference(child);
}
}