Skip to content

Instantly share code, notes, and snippets.

@Sprajapati123
Last active June 13, 2025 06:38
Show Gist options
  • Save Sprajapati123/a00902213abce5e7a21545152a8fc97c to your computer and use it in GitHub Desktop.
Save Sprajapati123/a00902213abce5e7a21545152a8fc97c to your computer and use it in GitHub Desktop.
package com.example.c36b.view
import android.app.Activity
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Email
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.example.c36b.R
import com.example.c36b.model.ProductModel
import com.example.c36b.repository.ProductRepository
import com.example.c36b.repository.ProductRepositoryImpl
import com.example.c36b.repository.UserRepository
import com.example.c36b.utils.ImageUtils
import com.example.c36b.view.ui.theme.C36BmasterTheme
import com.example.c36b.viewmodel.ProductViewModel
class AddProductActivity : ComponentActivity() {
lateinit var imageUtils: ImageUtils
var selectedImageUri by mutableStateOf<Uri?>(null)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
imageUtils = ImageUtils(this, this)
imageUtils.registerLaunchers { uri ->
selectedImageUri = uri
}
setContent {
AddProductBody(
selectedImageUri = selectedImageUri,
onPickImage = { imageUtils.launchImagePicker() }
)
}
}
}
@Composable
fun AddProductBody(
selectedImageUri: Uri?,
onPickImage: () -> Unit
) {
var productName by remember { mutableStateOf("") }
var productPrice by remember { mutableStateOf("") }
var productDescription by remember { mutableStateOf("") }
val repo = remember { ProductRepositoryImpl() }
val viewModel = remember { ProductViewModel(repo) }
val context = LocalContext.current
val activity = context as? Activity
Scaffold { innerPadding ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
item {
Box(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
onPickImage()
}
.padding(10.dp)
) {
if (selectedImageUri != null) {
AsyncImage(
model = selectedImageUri,
contentDescription = "Selected Image",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
} else {
Image(
painterResource(R.drawable.img),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
}
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
shape = RoundedCornerShape(12.dp),
placeholder = { Text("Product Name") },
value = productName,
onValueChange = { productName = it }
)
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
shape = RoundedCornerShape(12.dp),
placeholder = { Text("Product Description") },
value = productDescription,
onValueChange = { productDescription = it }
)
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
shape = RoundedCornerShape(12.dp),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
placeholder = { Text("Product Price") },
value = productPrice,
onValueChange = { productPrice = it }
)
Button(
onClick = {
if (selectedImageUri != null) {
viewModel.uploadImage(context, selectedImageUri) { imageUrl ->
if (imageUrl != null) {
val model = ProductModel(
"",
productName,
productPrice.toIntOrNull() ?: 0,
productDescription,
imageUrl
)
viewModel.addProduct(model) { success, message ->
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
if (success) activity?.finish()
}
} else {
Log.e("Upload Error", "Failed to upload image to Cloudinary")
}
}
} else {
Toast.makeText(
context,
"Please select an image first",
Toast.LENGTH_SHORT
).show()
}
},
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
Text("Submit")
}
}
}
}
}
@Preview(showBackground = true)
@Composable
fun ProductBodyPreview() {
AddProductBody(
selectedImageUri = null, // or pass a mock Uri if needed
onPickImage = {} // no-op
)
}
<!-- Add Permission in your android manifest -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
implementation("com.cloudinary:cloudinary-android:2.1.0")
implementation("com.squareup.picasso:picasso:2.8")
implementation("io.coil-kt:coil-compose:2.2.2")
package com.example.c36b.utils
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.ActivityResultRegistryOwner
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import android.content.pm.PackageManager
class ImageUtils(private val activity: Activity, private val registryOwner: ActivityResultRegistryOwner) {
private lateinit var galleryLauncher: ActivityResultLauncher<Intent>
private lateinit var permissionLauncher: ActivityResultLauncher<String>
private var onImageSelectedCallback: ((Uri?) -> Unit)? = null
fun registerLaunchers(onImageSelected: (Uri?) -> Unit) {
onImageSelectedCallback = onImageSelected
// Register for selecting image from gallery
galleryLauncher = registryOwner.activityResultRegistry.register(
"galleryLauncher", ActivityResultContracts.StartActivityForResult()
) { result ->
val uri = result.data?.data
if (result.resultCode == Activity.RESULT_OK && uri != null) {
onImageSelectedCallback?.invoke(uri)
} else {
Log.e("ImageUtils", "Image selection cancelled or failed")
}
}
// Register permission request
permissionLauncher = registryOwner.activityResultRegistry.register(
"permissionLauncher", ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
openGallery()
} else {
Log.e("ImageUtils", "Permission denied")
}
}
}
fun launchImagePicker() {
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
permissionLauncher.launch(permission)
} else {
openGallery()
}
}
private fun openGallery() {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI).apply {
type = "image/*"
}
galleryLauncher.launch(intent)
}
}
package com.example.c36b.repository
import android.content.Context
import android.net.Uri
import com.example.c36b.model.ProductModel
interface ProductRepository {
fun addProduct(productModel: ProductModel, callback: (Boolean, String) -> Unit)
fun deleteProduct(productId: String, callback: (Boolean, String) -> Unit)
fun getProductById(productId: String, callback: (Boolean, String, ProductModel?) -> Unit)
fun getAllProduct(callback: (Boolean, String, List<ProductModel>?) -> Unit)
fun updateProduct(
productId: String,
data: MutableMap<String, Any?>,
callback: (Boolean, String) -> Unit
)
fun uploadImage(context: Context,imageUri: Uri, callback: (String?) -> Unit)
fun getFileNameFromUri(context: Context,uri: Uri): String?
}
package com.example.c36b.repository
import android.content.Context
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.provider.OpenableColumns
import com.cloudinary.Cloudinary
import com.cloudinary.utils.ObjectUtils
import com.example.c36b.model.ProductModel
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import java.io.InputStream
import java.util.concurrent.Executors
class ProductRepositoryImpl : ProductRepository {
val database = FirebaseDatabase.getInstance()
val ref = database.reference.child("products")
private val cloudinary = Cloudinary(
mapOf(
"cloud_name" to "dxwr7qza9",
"api_key" to "545811657876156",
"api_secret" to "Oo50NMS-vrURt3gETED4ibe21uo"
)
)
override fun uploadImage(context: Context, imageUri: Uri, callback: (String?) -> Unit) {
val executor = Executors.newSingleThreadExecutor()
executor.execute {
try {
val inputStream: InputStream? = context.contentResolver.openInputStream(imageUri)
var fileName = getFileNameFromUri(context, imageUri)
// ✅ Fix: Remove extensions from file name before upload
fileName = fileName?.substringBeforeLast(".") ?: "uploaded_image"
val response = cloudinary.uploader().upload(
inputStream, ObjectUtils.asMap(
"public_id", fileName,
"resource_type", "image"
)
)
var imageUrl = response["url"] as String?
imageUrl = imageUrl?.replace("http://", "https://")
// ✅ Run UI updates on the Main Thread
Handler(Looper.getMainLooper()).post {
callback(imageUrl)
}
} catch (e: Exception) {
e.printStackTrace()
Handler(Looper.getMainLooper()).post {
callback(null)
}
}
}
}
override fun getFileNameFromUri(context: Context, uri: Uri): String? {
var fileName: String? = null
val cursor = context.contentResolver.query(uri, null, null, null, null)
cursor?.use {
if (it.moveToFirst()) {
val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (nameIndex != -1) {
fileName = it.getString(nameIndex)
}
}
}
return fileName
}
override fun addProduct(
productModel: ProductModel,
callback: (Boolean, String) -> Unit
) {
var id = ref.push().key.toString()
productModel.productId = id
ref.child(id).setValue(productModel).addOnCompleteListener {
if (it.isSuccessful) {
callback(true, "product added")
} else {
callback(false, "${it.exception?.message}")
}
}
}
override fun deleteProduct(
productId: String,
callback: (Boolean, String) -> Unit
) {
ref.child(productId).removeValue().addOnCompleteListener {
if (it.isSuccessful) {
callback(true, "product deleted")
} else {
callback(false, "${it.exception?.message}")
}
}
}
override fun getProductById(
productId: String,
callback: (Boolean, String, ProductModel?) -> Unit
) {
ref.child(productId).addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
var products = snapshot.getValue(ProductModel::class.java)
if (products != null) {
callback(true, "product fetched", products)
}
}
}
override fun onCancelled(error: DatabaseError) {
callback(false, error.message, null)
}
})
}
override fun getAllProduct(
callback: (Boolean, String, List<ProductModel>?) -> Unit
) {
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
var allProducts = mutableListOf<ProductModel>()
if (snapshot.exists()) {
for (eachData in snapshot.children) {
var products = eachData.getValue(ProductModel::class.java)
if (products != null) {
allProducts.add(products)
}
}
callback(true, "fetched", allProducts)
}
}
override fun onCancelled(error: DatabaseError) {
callback(false, error.message, null)
}
})
}
override fun updateProduct(
productId: String,
data: MutableMap<String, Any?>,
callback: (Boolean, String) -> Unit
) {
ref.child(productId).setValue(data).addOnCompleteListener {
if (it.isSuccessful) {
callback(true, "product updated")
} else {
callback(false, "${it.exception?.message}")
}
}
}
}
package com.example.c36b.viewmodel
import android.content.Context
import android.net.Uri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.c36b.model.ProductModel
import com.example.c36b.repository.ProductRepository
class ProductViewModel(val repo: ProductRepository) : ViewModel() {
fun uploadImage(context: Context,imageUri: Uri, callback: (String?) -> Unit){
repo.uploadImage(context,imageUri,callback)
}
fun addProduct(productModel: ProductModel, callback: (Boolean, String) -> Unit) {
repo.addProduct(productModel, callback)
}
fun deleteProduct(productId: String, callback: (Boolean, String) -> Unit) {
repo.deleteProduct(productId, callback)
}
private val _products = MutableLiveData<ProductModel?>()
val products: LiveData<ProductModel?> get() = _products
private val _allProducts = MutableLiveData<List<ProductModel?>>()
val allProducts: LiveData<List<ProductModel?>> get() = _allProducts
private val _isLoading = MutableLiveData<Boolean>()
val isLoading: LiveData<Boolean> get() = _isLoading
fun getProductById(productId: String) {
repo.getProductById(productId) { success, message, products ->
if (success && products != null) {
_products.postValue(products)
} else {
_products.postValue(null)
}
}
}
fun getAllProduct() {
_isLoading.value = true
repo.getAllProduct { success, message, products ->
if (success && products != null) {
_allProducts.postValue(products)
_isLoading.value = false
} else {
_isLoading.value = false
_allProducts.postValue(emptyList())
}
}
}
fun updateProduct(
productId: String, data: MutableMap<String, Any?>, callback: (Boolean, String) -> Unit
) {
repo.updateProduct(productId, data, callback)
}
}
package com.example.c36b.view
import android.app.Activity
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.example.c36b.R
import com.example.c36b.model.ProductModel
import com.example.c36b.repository.ProductRepositoryImpl
import com.example.c36b.utils.ImageUtils
import com.example.c36b.view.ui.theme.C36BmasterTheme
import com.example.c36b.viewmodel.ProductViewModel
class UpdateProductActivity : ComponentActivity() {
lateinit var imageUtils: ImageUtils
var selectedImageUri by mutableStateOf<Uri?>(null)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
imageUtils = ImageUtils(this, this)
imageUtils.registerLaunchers { uri ->
selectedImageUri = uri
}
setContent {
UpdateProductBody(
selectedImageUri = selectedImageUri,
onPickImage = { imageUtils.launchImagePicker() }
)
}
}
}
@Composable
fun UpdateProductBody(
selectedImageUri: Uri?,
onPickImage: () -> Unit
) {
var productName by remember { mutableStateOf("") }
var productPrice by remember { mutableStateOf("") }
var productDescription by remember { mutableStateOf("") }
var repo = remember { ProductRepositoryImpl() }
val viewModel = remember { ProductViewModel(repo) }
val context = LocalContext.current
val activity = context as? Activity
val productId: String? = activity?.intent?.getStringExtra("productId")
val data = viewModel.products.observeAsState(initial = null)
LaunchedEffect(Unit) {
viewModel.getProductById(productId.toString())
}
productName = data.value?.name ?: ""
productPrice = data.value?.price.toString()
productDescription = data.value?.desc ?: ""
Scaffold { innerPadding ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
item {
Box(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
onPickImage()
}
.padding(10.dp)
) {
when {
selectedImageUri != null -> {
AsyncImage(
model = selectedImageUri,
contentDescription = "Selected Image",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
!data.value?.imageUrl.isNullOrEmpty() -> {
AsyncImage(
model = data.value!!.imageUrl,
contentDescription = "Existing Image from DB",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
else -> {
Image(
painter = painterResource(R.drawable.img),
contentDescription = "Placeholder Image",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
}
}
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
shape = RoundedCornerShape(12.dp),
placeholder = {
Text("Product Name")
},
value = productName,
onValueChange = { input ->
productName = input
})
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
shape = RoundedCornerShape(12.dp),
placeholder = {
Text("Product Name")
},
value = productDescription,
onValueChange = { input ->
productDescription = input
})
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
shape = RoundedCornerShape(12.dp),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
placeholder = {
Text("Product price")
},
value = productPrice,
onValueChange = { input ->
productPrice = input
})
Button(onClick = {
viewModel.uploadImage(context, selectedImageUri!!) { imageUrl ->
if(imageUrl != null){
var maps = mutableMapOf<String, Any?>()
maps["productId"] = productId
maps["name"] = productName
maps["desc"] = productDescription
maps["price"] = productPrice.toInt()
maps["imageUrl"] = imageUrl
viewModel.updateProduct(productId.toString(), maps) { success, message ->
if (success) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
activity?.finish()
} else {
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
}
}
}
}
}) {
Text("Submit")
}
}
}
}
}
@Preview(showBackground = true)
@Composable
fun UpdateBodyPreview() {
UpdateProductBody(
selectedImageUri = null, // or pass a mock Uri if needed
onPickImage = {})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment