mirror of
https://github.com/shaulascr/ecommerce_serang.git
synced 2025-08-10 09:22:21 +00:00
product gaselsai
This commit is contained in:
@ -38,14 +38,14 @@ android {
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
buildConfigField("String",
|
||||
"BASE_URL",
|
||||
"\"${localProperties["BASE_URL"] ?: "http://default-url.com/"}\"")
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
buildConfigField("String",
|
||||
"BASE_URL",
|
||||
"\"${localProperties["BASE_URL"] ?: "http://default-url.com/"}\"")
|
||||
}
|
||||
debug {
|
||||
buildConfigField("String",
|
||||
|
@ -6,6 +6,8 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
@ -29,7 +29,7 @@ data class Product(
|
||||
val categoryId: Int? = null,
|
||||
|
||||
@field:SerializedName("price")
|
||||
val price: String? = null,
|
||||
val price: Int? = null,
|
||||
|
||||
@field:SerializedName("name")
|
||||
val name: String? = null,
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.alya.ecommerce_serang.data.api.response
|
||||
package com.alya.ecommerce_serang.data.api.response.product
|
||||
|
||||
import com.alya.ecommerce_serang.data.api.response.product.Product
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class CreateProductResponse(
|
@ -7,6 +7,7 @@ import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class ApiConfig {
|
||||
companion object {
|
||||
|
@ -9,6 +9,7 @@ import com.alya.ecommerce_serang.data.api.dto.OrderRequestBuy
|
||||
import com.alya.ecommerce_serang.data.api.dto.OtpRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.RegisterRequest
|
||||
import com.alya.ecommerce_serang.data.api.dto.UpdateCart
|
||||
import com.alya.ecommerce_serang.data.api.response.product.CreateProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.ViewStoreProductsResponse
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
@ -34,9 +35,9 @@ import com.alya.ecommerce_serang.data.api.response.profile.ProfileResponse
|
||||
import retrofit2.Call
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Field
|
||||
import retrofit2.http.FormUrlEncoded
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.HeaderMap
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
@ -84,6 +85,24 @@ interface ApiService {
|
||||
@Path("id") storeId: Int
|
||||
): Response<DetailStoreProductResponse>
|
||||
|
||||
@POST("order")
|
||||
suspend fun postOrder(
|
||||
@Body request: OrderRequest
|
||||
): Response<CreateOrderResponse>
|
||||
|
||||
@POST("order")
|
||||
suspend fun postOrderBuyNow(
|
||||
@Body request: OrderRequestBuy
|
||||
): Response<CreateOrderResponse>
|
||||
|
||||
@GET("profile/address")
|
||||
suspend fun getAddress(
|
||||
): Response<AddressResponse>
|
||||
|
||||
@POST("profile/addaddress")
|
||||
suspend fun createAddress(
|
||||
@Body createAddressRequest: CreateAddressRequest
|
||||
): Response<CreateAddressResponse>
|
||||
|
||||
@GET("mystore")
|
||||
suspend fun getStore (): Response<StoreResponse>
|
||||
@ -106,11 +125,11 @@ interface ApiService {
|
||||
@Part("is_pre_order") isPreOrder: RequestBody,
|
||||
@Part("duration") duration: RequestBody,
|
||||
@Part("category_id") categoryId: RequestBody,
|
||||
@Part("is_active") isActive: RequestBody,
|
||||
@Part("status") status: RequestBody,
|
||||
@Part image: MultipartBody.Part?,
|
||||
@Part sppirt: MultipartBody.Part?,
|
||||
@Part halal: MultipartBody.Part?
|
||||
): Response<Unit>
|
||||
): Response<CreateProductResponse>
|
||||
|
||||
@GET("cart_item")
|
||||
suspend fun getCart (): Response<ListCartResponse>
|
||||
|
@ -4,11 +4,13 @@ import android.util.Log
|
||||
import com.alya.ecommerce_serang.data.api.dto.CartItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.CategoryItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.product.CreateProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.cart.AddCartResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.ProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.ReviewsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.product.StoreProduct
|
||||
import com.alya.ecommerce_serang.data.api.retrofit.ApiService
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
@ -158,51 +160,39 @@ class ProductRepository(private val apiService: ApiService) {
|
||||
isPreOrder: Boolean,
|
||||
duration: Int,
|
||||
categoryId: Int,
|
||||
isActive: Boolean,
|
||||
image: File?,
|
||||
sppirt: File?,
|
||||
halal: File?
|
||||
): Result<Unit> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val namePart = RequestBody.create("text/plain".toMediaTypeOrNull(), name)
|
||||
val descriptionPart = RequestBody.create("text/plain".toMediaTypeOrNull(), description)
|
||||
val pricePart = RequestBody.create("text/plain".toMediaTypeOrNull(), price.toString())
|
||||
val stockPart = RequestBody.create("text/plain".toMediaTypeOrNull(), stock.toString())
|
||||
val minOrderPart = RequestBody.create("text/plain".toMediaTypeOrNull(), minOrder.toString())
|
||||
val weightPart = RequestBody.create("text/plain".toMediaTypeOrNull(), weight.toString())
|
||||
val isPreOrderPart = RequestBody.create("text/plain".toMediaTypeOrNull(), isPreOrder.toString())
|
||||
val durationPart = RequestBody.create("text/plain".toMediaTypeOrNull(), duration.toString())
|
||||
val categoryIdPart = RequestBody.create("text/plain".toMediaTypeOrNull(), categoryId.toString())
|
||||
val isActivePart = RequestBody.create("text/plain".toMediaTypeOrNull(), if (isActive) "1" else "0")
|
||||
|
||||
val imagePart = image?.let {
|
||||
val req = RequestBody.create("image/*".toMediaTypeOrNull(), it)
|
||||
MultipartBody.Part.createFormData("image", it.name, req)
|
||||
}
|
||||
|
||||
val sppirtPart = sppirt?.let {
|
||||
val req = RequestBody.create("application/pdf".toMediaTypeOrNull(), it)
|
||||
MultipartBody.Part.createFormData("sppirt", it.name, req)
|
||||
}
|
||||
|
||||
val halalPart = halal?.let {
|
||||
val req = RequestBody.create("application/pdf".toMediaTypeOrNull(), it)
|
||||
MultipartBody.Part.createFormData("halal", it.name, req)
|
||||
}
|
||||
|
||||
status: String,
|
||||
imagePart: MultipartBody.Part?,
|
||||
sppirtPart: MultipartBody.Part?,
|
||||
halalPart: MultipartBody.Part?
|
||||
): Result<CreateProductResponse> {
|
||||
return try {
|
||||
val response = apiService.addProduct(
|
||||
namePart, descriptionPart, pricePart, stockPart, minOrderPart,
|
||||
weightPart, isPreOrderPart, durationPart, categoryIdPart, isActivePart,
|
||||
imagePart, sppirtPart, halalPart
|
||||
name = RequestBody.create("text/plain".toMediaTypeOrNull(), name),
|
||||
description = RequestBody.create("text/plain".toMediaTypeOrNull(), description),
|
||||
price = RequestBody.create("text/plain".toMediaTypeOrNull(), price.toString()),
|
||||
stock = RequestBody.create("text/plain".toMediaTypeOrNull(), stock.toString()),
|
||||
minOrder = RequestBody.create("text/plain".toMediaTypeOrNull(), minOrder.toString()),
|
||||
weight = RequestBody.create("text/plain".toMediaTypeOrNull(), weight.toString()),
|
||||
isPreOrder = RequestBody.create("text/plain".toMediaTypeOrNull(), isPreOrder.toString()),
|
||||
duration = RequestBody.create("text/plain".toMediaTypeOrNull(), duration.toString()),
|
||||
categoryId = RequestBody.create("text/plain".toMediaTypeOrNull(), categoryId.toString()),
|
||||
status = RequestBody.create("text/plain".toMediaTypeOrNull(), status),
|
||||
image = imagePart,
|
||||
sppirt = sppirtPart,
|
||||
halal = halalPart
|
||||
)
|
||||
|
||||
if (response.isSuccessful) Result.Success(Unit)
|
||||
else Result.Error(Exception("Failed: ${response.code()}"))
|
||||
if (response.isSuccessful) {
|
||||
Result.Success(response.body()!!)
|
||||
} else {
|
||||
Result.Error(Exception("Failed to create product: ${response.code()}"))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ProductRepository"
|
||||
}
|
||||
|
@ -5,8 +5,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
@ -23,11 +22,11 @@ import com.alya.ecommerce_serang.databinding.ActivityStoreProductDetailBinding
|
||||
import com.alya.ecommerce_serang.utils.viewmodel.ProductViewModel
|
||||
import com.alya.ecommerce_serang.utils.BaseViewModelFactory
|
||||
import com.alya.ecommerce_serang.utils.SessionManager
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import kotlin.getValue
|
||||
|
||||
class StoreProductDetailActivity : AppCompatActivity() {
|
||||
@ -62,7 +61,7 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
private val sppirtLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||
if (uri != null && isValidFile(uri)) {
|
||||
sppirtUri = uri
|
||||
binding.tvSppirtName.text = File(uri.path ?: "").name
|
||||
binding.tvSppirtName.text = getFileName(uri)
|
||||
binding.switcherSppirt.showNext()
|
||||
}
|
||||
}
|
||||
@ -70,7 +69,7 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
private val halalLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||
if (uri != null && isValidFile(uri)) {
|
||||
halalUri = uri
|
||||
binding.tvHalalName.text = File(uri.path ?: "").name
|
||||
binding.tvHalalName.text = getFileName(uri)
|
||||
binding.switcherHalal.showNext()
|
||||
}
|
||||
}
|
||||
@ -109,6 +108,12 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
imagePickerLauncher.launch(intent)
|
||||
}
|
||||
|
||||
binding.btnRemoveFoto.setOnClickListener {
|
||||
imageUri = null
|
||||
binding.switcherFotoProduk.showPrevious()
|
||||
validateForm()
|
||||
}
|
||||
|
||||
binding.layoutUploadSppirt.setOnClickListener { sppirtLauncher.launch("*/*") }
|
||||
binding.btnRemoveSppirt.setOnClickListener {
|
||||
sppirtUri = null
|
||||
@ -121,33 +126,10 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
binding.switcherHalal.showPrevious()
|
||||
}
|
||||
|
||||
binding.btnRemoveFoto.setOnClickListener {
|
||||
imageUri = null
|
||||
binding.switcherFotoProduk.showPrevious()
|
||||
validateForm()
|
||||
}
|
||||
|
||||
val watcher = object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) { validateForm() }
|
||||
}
|
||||
|
||||
listOf(
|
||||
binding.edtNamaProduk,
|
||||
binding.edtDeskripsiProduk,
|
||||
binding.edtHargaProduk,
|
||||
binding.edtStokProduk,
|
||||
binding.edtMinOrder,
|
||||
binding.edtBeratProduk,
|
||||
binding.edtDurasi
|
||||
).forEach { it.addTextChangedListener(watcher) }
|
||||
|
||||
validateForm()
|
||||
|
||||
binding.btnSaveProduct.setOnClickListener {
|
||||
if (!binding.btnSaveProduct.isEnabled) {
|
||||
focusFirstInvalidField()
|
||||
return@setOnClickListener
|
||||
}
|
||||
submitProduct()
|
||||
@ -159,6 +141,10 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
return listOf("application/pdf", "image/jpeg", "image/png", "image/jpg").contains(mimeType)
|
||||
}
|
||||
|
||||
private fun getFileName(uri: Uri): String {
|
||||
return uri.lastPathSegment?.split("/")?.last() ?: "unknown_file"
|
||||
}
|
||||
|
||||
private fun validateForm() {
|
||||
val valid = binding.edtNamaProduk.text.isNotBlank() &&
|
||||
binding.edtDeskripsiProduk.text.isNotBlank() &&
|
||||
@ -178,23 +164,15 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun focusFirstInvalidField() {
|
||||
when {
|
||||
binding.edtNamaProduk.text.isBlank() -> binding.edtNamaProduk.requestFocus()
|
||||
binding.edtDeskripsiProduk.text.isBlank() -> binding.edtDeskripsiProduk.requestFocus()
|
||||
binding.edtHargaProduk.text.isBlank() -> binding.edtHargaProduk.requestFocus()
|
||||
binding.edtStokProduk.text.isBlank() -> binding.edtStokProduk.requestFocus()
|
||||
binding.edtMinOrder.text.isBlank() -> binding.edtMinOrder.requestFocus()
|
||||
binding.edtBeratProduk.text.isBlank() -> binding.edtBeratProduk.requestFocus()
|
||||
binding.switchIsPreOrder.isChecked && binding.edtDurasi.text.isBlank() -> binding.edtDurasi.requestFocus()
|
||||
imageUri == null -> Toast.makeText(this, "Silakan unggah foto produk", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
private fun uriToNamedFile(uri: Uri, context: Context, prefix: String): File {
|
||||
val extension = context.contentResolver.getType(uri)?.substringAfter("/") ?: "jpg"
|
||||
val filename = "$prefix-${System.currentTimeMillis()}.$extension"
|
||||
val file = File(context.cacheDir, filename)
|
||||
|
||||
context.contentResolver.openInputStream(uri)?.use { input ->
|
||||
FileOutputStream(file).use { output -> input.copyTo(output) }
|
||||
}
|
||||
|
||||
private fun uriToFile(uri: Uri, context: Context): File {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
val file = File.createTempFile("upload_", ".tmp", context.cacheDir)
|
||||
inputStream?.use { input -> file.outputStream().use { input.copyTo(it) } }
|
||||
return file
|
||||
}
|
||||
|
||||
@ -207,30 +185,47 @@ class StoreProductDetailActivity : AppCompatActivity() {
|
||||
val weight = binding.edtBeratProduk.text.toString().toInt()
|
||||
val isPreOrder = binding.switchIsPreOrder.isChecked
|
||||
val duration = if (isPreOrder) binding.edtDurasi.text.toString().toInt() else 0
|
||||
val isActive = binding.switchIsActive.isChecked
|
||||
val status = if (binding.switchIsActive.isChecked) "active" else "inactive"
|
||||
val categoryId = categoryList.getOrNull(binding.spinnerKategoriProduk.selectedItemPosition)?.id ?: 0
|
||||
|
||||
val imageFile = imageUri?.let { uriToFile(it, this) }
|
||||
val sppirtFile = sppirtUri?.let { uriToFile(it, this) }
|
||||
val halalFile = halalUri?.let { uriToFile(it, this) }
|
||||
val imageFile = imageUri?.let { File(it.path) }
|
||||
val sppirtFile = sppirtUri?.let { uriToNamedFile(it, this, "sppirt") }
|
||||
val halalFile = halalUri?.let { uriToNamedFile(it, this, "halal") }
|
||||
|
||||
Log.d("File URI", "SPPIRT URI: ${sppirtUri.toString()}")
|
||||
Log.d("File URI", "Halal URI: ${halalUri.toString()}")
|
||||
|
||||
val imagePart = imageFile?.let { createPartFromFile("image", it) }
|
||||
val sppirtPart = sppirtFile?.let { createPartFromFile("sppirt", it) }
|
||||
val halalPart = halalFile?.let { createPartFromFile("halal", it) }
|
||||
|
||||
viewModel.addProduct(
|
||||
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, isActive, imageFile, sppirtFile, halalFile
|
||||
).observe(this) { result ->
|
||||
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, status, imagePart, sppirtPart, halalPart
|
||||
)
|
||||
|
||||
viewModel.productCreationResult.observe(this) { result ->
|
||||
when (result) {
|
||||
is Result.Loading -> binding.btnSaveProduct.isEnabled = false
|
||||
is Result.Success -> {
|
||||
Toast.makeText(this, "Produk berhasil ditambahkan!", Toast.LENGTH_SHORT).show()
|
||||
val product = result.data.product
|
||||
Toast.makeText(this, "Product Created: ${product?.productName}", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
is Result.Error -> {
|
||||
Toast.makeText(this, "Gagal: ${result.exception.message}", Toast.LENGTH_SHORT).show()
|
||||
Log.e("ProductDetailActivity", "Error: ${result.exception.message}")
|
||||
binding.btnSaveProduct.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createPartFromFile(field: String, file: File?): MultipartBody.Part? {
|
||||
return file?.let {
|
||||
val requestBody = RequestBody.create("application/octet-stream".toMediaTypeOrNull(), it)
|
||||
MultipartBody.Part.createFormData(field, it.name, requestBody)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupHeader() {
|
||||
binding.header.headerTitle.text = "Tambah Produk"
|
||||
binding.header.headerLeftIcon.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
||||
|
@ -3,20 +3,23 @@ package com.alya.ecommerce_serang.utils.viewmodel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.liveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.alya.ecommerce_serang.data.api.dto.CategoryItem
|
||||
import com.alya.ecommerce_serang.data.api.dto.ProductsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.product.CreateProductResponse
|
||||
import com.alya.ecommerce_serang.data.api.response.product.Product
|
||||
import com.alya.ecommerce_serang.data.api.response.product.ReviewsItem
|
||||
import com.alya.ecommerce_serang.data.api.response.product.StoreProduct
|
||||
import com.alya.ecommerce_serang.data.repository.ProductRepository
|
||||
import com.alya.ecommerce_serang.data.repository.Result
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
class ProductViewModel(private val repository: ProductRepository) : ViewModel() {
|
||||
|
||||
private val _productCreationResult = MutableLiveData<Result<CreateProductResponse>>()
|
||||
val productCreationResult: LiveData<Result<CreateProductResponse>> get() = _productCreationResult
|
||||
|
||||
private val _productDetail = MutableLiveData<Product?>()
|
||||
val productDetail: LiveData<Product?> get() = _productDetail
|
||||
|
||||
@ -75,13 +78,18 @@ class ProductViewModel(private val repository: ProductRepository) : ViewModel()
|
||||
isPreOrder: Boolean,
|
||||
duration: Int,
|
||||
categoryId: Int,
|
||||
isActive: Boolean,
|
||||
image: File?,
|
||||
sppirt: File?,
|
||||
halal: File?
|
||||
): LiveData<Result<Unit>> = liveData {
|
||||
emit(Result.Loading)
|
||||
emit(repository.addProduct(name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, isActive, image, sppirt, halal))
|
||||
status: String,
|
||||
imagePart: MultipartBody.Part?,
|
||||
sppirtPart: MultipartBody.Part?,
|
||||
halalPart: MultipartBody.Part?
|
||||
) {
|
||||
_productCreationResult.value = Result.Loading
|
||||
viewModelScope.launch {
|
||||
val result = repository.addProduct(
|
||||
name, description, price, stock, minOrder, weight, isPreOrder, duration, categoryId, status, imagePart, sppirtPart, halalPart
|
||||
)
|
||||
_productCreationResult.value = result
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: for store detail if you need it later
|
||||
|
@ -20,10 +20,12 @@ lifecycleViewmodelKtx = "2.8.7"
|
||||
fragmentKtx = "1.5.6"
|
||||
navigationFragmentKtx = "2.8.5"
|
||||
navigationUiKtx = "2.8.5"
|
||||
recyclerview = "1.4.0"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
androidx-hilt-lifecycle-viewmodel = { module = "androidx.hilt:hilt-lifecycle-viewmodel", version.ref = "hiltLifecycleViewmodel" }
|
||||
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
|
||||
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
|
Reference in New Issue
Block a user