feat: refs #6659 jetPackCompose
This commit is contained in:
parent
f55bd3768f
commit
5d600359fa
|
@ -5,6 +5,7 @@ plugins {
|
|||
id("com.google.gms.google-services")
|
||||
id("com.google.firebase.crashlytics")
|
||||
id("com.google.devtools.ksp")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -15,8 +16,8 @@ android {
|
|||
applicationId = "es.verdnatura"
|
||||
minSdk = 26
|
||||
targetSdk = 33 // se deja con target si no Play Protect la bloquea
|
||||
versionCode = 393 //JAF en informatica
|
||||
versionName = "25.0"
|
||||
versionCode = 397 //JAF 393 en informatica
|
||||
versionName = "25.6"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
@ -83,6 +84,9 @@ android {
|
|||
}
|
||||
*/
|
||||
dataBinding = true
|
||||
|
||||
compose = true
|
||||
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
@ -142,15 +146,39 @@ android {
|
|||
implementation(libs.androidx.datastore.core)
|
||||
implementation(libs.zxing.android.embedded) { isTransitive = false }
|
||||
implementation(libs.core)
|
||||
//implementation(libs.koin.androidx.compose)
|
||||
|
||||
// Compose
|
||||
//implementation(libs.androidx.compose.bom)
|
||||
/* implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.material:material")
|
||||
implementation("androidx.compose.runtime:runtime")
|
||||
implementation("androidx.activity:activity-compose:1.8.2")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview") */
|
||||
dependencies {
|
||||
|
||||
val composeBom = platform("androidx.compose:compose-bom:2024.10.01")
|
||||
implementation(composeBom)
|
||||
androidTestImplementation(composeBom)
|
||||
|
||||
// Choose one of the following:
|
||||
// Material Design 3
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.ui)
|
||||
// Android Studio Preview support
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
// UI Tests
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
// custom design system based on Foundation)
|
||||
//implementation(libs.androidx.material.icons.core)
|
||||
// Optional - Add full set of material icons
|
||||
implementation(libs.androidx.material.icons.extended)
|
||||
// Optional - Add window size utils
|
||||
implementation(libs.androidx.adaptive)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
implementation(libs.lottie.compose)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
testImplementation(libs.junit)
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
android:name=".presentation.view.feature.articulo.fragment.ImageViewActivity"
|
||||
android:configChanges="orientation"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".presentation.composable.ImageViewActivityComposable"
|
||||
android:configChanges="orientation"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".presentation.view.feature.restaurant.RestaurantActivity"
|
||||
android:configChanges="orientation"
|
||||
|
@ -69,6 +75,8 @@
|
|||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
|
||||
</manifest>
|
|
@ -5,7 +5,6 @@ import androidx.lifecycle.LifecycleObserver
|
|||
import androidx.lifecycle.ViewModel
|
||||
import es.verdnatura.MobileApplication
|
||||
import es.verdnatura.domain.SalixService
|
||||
import org.json.JSONObject
|
||||
|
||||
abstract class BaseViewModel : ViewModel, LifecycleObserver {
|
||||
lateinit var app: MobileApplication
|
||||
|
@ -18,23 +17,3 @@ abstract class BaseViewModel : ViewModel, LifecycleObserver {
|
|||
}
|
||||
}
|
||||
|
||||
fun nameofFunction(function: Any): String {
|
||||
return try {
|
||||
function.javaClass.enclosingMethod!!.name + "->"
|
||||
} catch (e: Exception) {
|
||||
"ActivityMain->"
|
||||
}
|
||||
}
|
||||
|
||||
fun getMessageFromAllResponse(callFunction: String, responseMessage: String): String {
|
||||
|
||||
var messageFromError: String = try {
|
||||
val answerError = JSONObject(responseMessage)
|
||||
answerError.get("Message").toString()
|
||||
} catch (e: Exception) {
|
||||
responseMessage
|
||||
}
|
||||
|
||||
return "$messageFromError.\r${"Callback: $callFunction."}"
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package es.verdnatura.presentation.base
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.ViewModel
|
||||
import es.verdnatura.MobileApplication
|
||||
import es.verdnatura.domain.SalixService
|
||||
|
||||
abstract class BaseViewModelCompose(application: Application) : ViewModel(), LifecycleObserver {
|
||||
protected val app: MobileApplication = application as MobileApplication
|
||||
protected val salix: SalixService = app.salix
|
||||
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
package es.verdnatura.presentation.view.commom.webview
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.VelocityTracker
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.OnBackPressedDispatcher
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import es.verdnatura.presentation.common.OnBackPressedListener
|
||||
import es.verdnatura.presentation.view.commom.webview.data.WebViewEvent
|
||||
import es.verdnatura.presentation.view.commom.webview.data.WebViewScreen
|
||||
import org.json.JSONObject
|
||||
import kotlin.math.abs
|
||||
|
||||
class WebFragmentCompose(
|
||||
var entryPoint: String = ""
|
||||
) : Fragment(), OnBackPressedListener {
|
||||
private val viewModel: WebViewViewModel by viewModels()
|
||||
private var velocityTracker: VelocityTracker? = null
|
||||
private lateinit var backDispatcher: OnBackPressedDispatcher
|
||||
private var webView: WebView? = null
|
||||
|
||||
companion object {
|
||||
private const val FLING_THRESHOLD_VELOCITY = 2000
|
||||
private const val FLING_THRESHOLD_VERTICAL = 1000
|
||||
fun newInstance(entryPoint: String) = WebFragmentCompose(entryPoint)
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
// Recolectar acciones del ViewModel
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.webViewActions.collect { event ->
|
||||
when (event) {
|
||||
is WebViewEvent.OnBackGesture -> {
|
||||
webView?.goBack()
|
||||
}
|
||||
// Manejar otros eventos si es necesario
|
||||
is WebViewEvent.LoadUrl -> println("")
|
||||
is WebViewEvent.OnPageFinished -> println("")
|
||||
is WebViewEvent.OnUrlLoading -> println("")
|
||||
is WebViewEvent.ToggleToolbar -> println("")
|
||||
is WebViewEvent.UpdateTitle -> println("")
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebViewScreen(
|
||||
state = state,
|
||||
onWebViewCreated = { webView ->
|
||||
this@WebFragmentCompose.webView = webView
|
||||
setupWebView(webView)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupWebView(webView: WebView) {
|
||||
webView.webViewClient = object : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
): Boolean {
|
||||
request?.url?.toString()?.let {
|
||||
viewModel.processEvent(WebViewEvent.OnUrlLoading(it))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
viewModel.processEvent(WebViewEvent.OnPageFinished(url ?: ""))
|
||||
viewModel.processEvent(WebViewEvent.UpdateTitle(webView.title ?: ""))
|
||||
}
|
||||
}
|
||||
|
||||
webView.setOnTouchListener { _, event ->
|
||||
handleTouchEvent(event)
|
||||
}
|
||||
|
||||
// Cargar URL inicial si existe
|
||||
// if (state.value.initialUrl.isNotEmpty()) {
|
||||
// }
|
||||
}
|
||||
|
||||
private fun handleTouchEvent(event: MotionEvent): Boolean {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
velocityTracker?.clear()
|
||||
velocityTracker = velocityTracker ?: VelocityTracker.obtain()
|
||||
velocityTracker?.addMovement(event)
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
velocityTracker?.addMovement(event)
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
velocityTracker?.let { tracker ->
|
||||
tracker.computeCurrentVelocity(1000)
|
||||
val velocityX = tracker.xVelocity
|
||||
val velocityY = abs(tracker.yVelocity)
|
||||
|
||||
if (velocityX > FLING_THRESHOLD_VELOCITY &&
|
||||
velocityY < FLING_THRESHOLD_VERTICAL &&
|
||||
webView?.canGoBack() == true
|
||||
) {
|
||||
viewModel.processEvent(WebViewEvent.OnBackGesture)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
velocityTracker?.recycle()
|
||||
velocityTracker = null
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
if (!onBackPressedHandled()) {
|
||||
isEnabled = false
|
||||
requireActivity().onBackPressedDispatcher
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
val dataScanned = JSONObject(entryPoint)
|
||||
viewModel.setInitialUrl(dataScanned.get("web").toString())
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
velocityTracker?.recycle()
|
||||
velocityTracker = null
|
||||
webView = null
|
||||
}
|
||||
|
||||
override fun onBackPressedHandled(): Boolean {
|
||||
|
||||
viewModel.processEvent(WebViewEvent.OnBackGesture)
|
||||
|
||||
/* if (webView!!.canGoBack()) {
|
||||
webView!!.goBack()
|
||||
} else {
|
||||
(context as MainActivity).onMyBackPressed()
|
||||
}*/
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package es.verdnatura.presentation.view.commom.webview
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import es.verdnatura.presentation.view.commom.webview.data.WebViewEvent
|
||||
import es.verdnatura.presentation.view.commom.webview.data.WebViewState
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class WebViewViewModel : ViewModel() {
|
||||
private val _state = MutableStateFlow(WebViewState())
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
private val _webViewActions = Channel<WebViewEvent>()
|
||||
val webViewActions = _webViewActions.receiveAsFlow()
|
||||
|
||||
fun processEvent(event: WebViewEvent) {
|
||||
when (event) {
|
||||
is WebViewEvent.LoadUrl -> {
|
||||
_state.update { it.copy(url = event.url, isLoading = true) }
|
||||
viewModelScope.launch {
|
||||
_webViewActions.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
is WebViewEvent.UpdateTitle -> {
|
||||
_state.update { it.copy(title = event.title) }
|
||||
}
|
||||
|
||||
is WebViewEvent.OnPageFinished -> {
|
||||
_state.update { it.copy(isLoading = false) }
|
||||
}
|
||||
|
||||
is WebViewEvent.ToggleToolbar -> {
|
||||
_state.update { it.copy(showToolbar = event.show) }
|
||||
}
|
||||
|
||||
is WebViewEvent.OnBackGesture -> {
|
||||
viewModelScope.launch {
|
||||
_webViewActions.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
is WebViewEvent.OnUrlLoading -> {
|
||||
processEvent(WebViewEvent.LoadUrl(event.url))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setInitialUrl(webURL: String) {
|
||||
try {
|
||||
|
||||
println("webURL $webURL")
|
||||
_state.update { it.copy(initialUrl = webURL) }
|
||||
processEvent(WebViewEvent.LoadUrl("https://salix.verdnatura.es"))
|
||||
} catch (e: Exception) {
|
||||
// Manejar error
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package es.verdnatura.presentation.view.commom.webview.data
|
||||
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebView
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun WebViewScreen(
|
||||
state: WebViewState,
|
||||
onWebViewCreated: (WebView) -> Unit
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize() // Asegura que ocupe toda la pantalla
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.statusBarsPadding() // Evita que la WebView quede debajo de la barra de estado
|
||||
) {
|
||||
AndroidView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
factory = { context ->
|
||||
WebView(context).apply {
|
||||
settings.apply {
|
||||
javaScriptEnabled = true
|
||||
domStorageEnabled = true
|
||||
loadWithOverviewMode = true
|
||||
useWideViewPort = true
|
||||
builtInZoomControls = true
|
||||
displayZoomControls = false
|
||||
setSupportZoom(true)
|
||||
allowFileAccess = true
|
||||
}
|
||||
webChromeClient = object : WebChromeClient() {
|
||||
override fun onProgressChanged(view: WebView?, newProgress: Int) {
|
||||
super.onProgressChanged(view, newProgress)
|
||||
|
||||
}
|
||||
}
|
||||
onWebViewCreated(this)
|
||||
}
|
||||
},
|
||||
update = { webView ->
|
||||
if (state.url.isNotEmpty() && state.url != webView.url) {
|
||||
webView.loadUrl(state.url)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (state.isLoading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package es.verdnatura.presentation.view.commom.webview.data
|
||||
|
||||
data class WebViewState(
|
||||
val title: String = "",
|
||||
val url: String = "",
|
||||
val isLoading: Boolean = false,
|
||||
val showToolbar: Boolean = true,
|
||||
val initialUrl: String = ""
|
||||
)
|
||||
|
||||
sealed class WebViewEvent {
|
||||
data class LoadUrl(val url: String) : WebViewEvent()
|
||||
data class UpdateTitle(val title: String) : WebViewEvent()
|
||||
data class OnPageFinished(val url: String) : WebViewEvent()
|
||||
data class ToggleToolbar(val show: Boolean) : WebViewEvent()
|
||||
data object OnBackGesture : WebViewEvent()
|
||||
data class OnUrlLoading(val url: String) : WebViewEvent()
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package es.verdnatura.presentation.view.feature.buscaritem.fragment
|
||||
|
||||
import es.verdnatura.domain.SalixCallback
|
||||
import es.verdnatura.domain.userCases.GetItemFromBarcodeUseCase
|
||||
import es.verdnatura.presentation.base.BaseViewModelCompose
|
||||
import es.verdnatura.presentation.view.feature.buscaritem.model.ItemShelvings
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import retrofit2.Response
|
||||
|
||||
data class UiState(
|
||||
val itemFk: String? = null,
|
||||
val items: List<ItemShelvings> = emptyList()
|
||||
)
|
||||
|
||||
class BuscarItemComposeViewModel(var application: android.app.Application) :
|
||||
BaseViewModelCompose(application) {
|
||||
private val getItemFromBarcodeUseCase = GetItemFromBarcodeUseCase(salix)
|
||||
|
||||
private val _uiState = MutableStateFlow(UiState())
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
/* private val _itemFk = MutableStateFlow<String?>(null)
|
||||
val itemFk = _itemFk.asStateFlow()*/
|
||||
|
||||
/* private val _itemShelvingsList = MutableStateFlow<ItemShelvingsList?>(null)
|
||||
val itemShelvingsList = _itemShelvingsList.asStateFlow()*/
|
||||
|
||||
/*val loadItemShelvingsList = _itemShelvingsList.map {
|
||||
it?.let { Event(it) }
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = null
|
||||
)*/
|
||||
|
||||
fun getIdFromCodeSalix(code: String) {
|
||||
_uiState.update { it.copy(itemFk = code) }
|
||||
//_itemFk.value = code
|
||||
getItemFromBarcodeUseCase.execute(code)
|
||||
.enqueue(object : SalixCallback<Int?>(application.applicationContext) {
|
||||
override fun onSuccess(response: Response<Int?>) {
|
||||
response.body()?.let { id ->
|
||||
itemshelvingsGet(id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun itemshelvingsGet(itemFk: Any) {
|
||||
salix.itemShelvingsGet(
|
||||
filter = """{"where":{"itemFk":$itemFk},"fields":["created","visible","available","shelvingFk"],
|
||||
|"include":[{"relation":"shelving","scope":{"fields":["code","priority","parkingFk"],
|
||||
|"include":{"relation":"parking","scope":{"fields":["code","sectorFk"]}}}}]}""".trimMargin()
|
||||
)
|
||||
.enqueue(object : SalixCallback<List<ItemShelvings>>(application.applicationContext) {
|
||||
override fun onSuccess(response: Response<List<ItemShelvings>>) {
|
||||
response.body()?.let { list ->
|
||||
//_itemShelvingsList.value = ItemShelvingsList(list)
|
||||
_uiState.update { it.copy(items = list) }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ class BuscarItemFragment(
|
|||
override fun getLayoutId(): Int = R.layout.fragment_buscar_item
|
||||
|
||||
override fun init() {
|
||||
binding.mainToolbar.toolbarTitle.text = getString(R.string.getubicaition)
|
||||
binding.mainToolbar.toolbarTitle.text = getString(R.string.getubication)
|
||||
setEvents()
|
||||
if (itemFk != null) {
|
||||
viewModel.getIdFromCodeSalix(itemFk!!.toString())
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
package es.verdnatura.presentation.view.feature.buscaritem.fragment
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import es.verdnatura.R
|
||||
import es.verdnatura.domain.toast
|
||||
import es.verdnatura.presentation.common.OnPasillerosItemClickListener
|
||||
import es.verdnatura.presentation.view.feature.main.activity.MainActivity
|
||||
import es.verdnatura.presentation.view.feature.pasillero.model.PasillerosItemVO
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
class BuscarItemFragmentCompose(
|
||||
var itemFk: Any? = null
|
||||
) : Fragment() {
|
||||
private var pasillerosItemClickListener: OnPasillerosItemClickListener? = null
|
||||
private val viewModel: BuscarItemComposeViewModel by viewModel()
|
||||
|
||||
companion object {
|
||||
fun newInstance(entryPoint: Int?) = BuscarItemFragmentCompose(entryPoint)
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
|
||||
if (context is OnPasillerosItemClickListener) pasillerosItemClickListener = context
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
SetView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
itemFk?.let {
|
||||
viewModel.getIdFromCodeSalix(it.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSector(item: LocationItem) {
|
||||
item.sector.toast(requireContext())
|
||||
|
||||
}
|
||||
|
||||
private fun openUbicadorFragment(item: LocationItem) {
|
||||
pasillerosItemClickListener!!.onPasillerosItemClickListener(
|
||||
PasillerosItemVO(
|
||||
title =
|
||||
R.string.titleUbicator
|
||||
), item.matricula
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SetView() {
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
LocationScreen(
|
||||
items = uiState.items.map {
|
||||
LocationItem(
|
||||
parking = it.shelving.parking.code,
|
||||
matricula = it.shelving.code,
|
||||
visible = it.visible.toString(),
|
||||
reserve = it.available.toString(),
|
||||
priority = it.shelving.priority.toString(),
|
||||
fecha = it.created,
|
||||
sector = it.shelving.parking.sector.description
|
||||
)
|
||||
},
|
||||
onTextChange = { input ->
|
||||
viewModel.getIdFromCodeSalix(input)
|
||||
},
|
||||
titleToolBar = if (uiState.items.isNotEmpty()) {
|
||||
getString(
|
||||
R.string.itemsTotal,
|
||||
uiState.itemFk,
|
||||
getString(R.string.visibleTotal),
|
||||
uiState.items.sumOf { it.visible }
|
||||
)
|
||||
} else getString(R.string.getubication),
|
||||
onBackClick = { (context as MainActivity).onMyBackPressed() },
|
||||
onLongClick = { item -> item.sector.toast(requireContext()) },
|
||||
onClick = { item ->
|
||||
pasillerosItemClickListener?.onPasillerosItemClickListener(
|
||||
PasillerosItemVO(title = R.string.titleUbicator),
|
||||
item.matricula
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
class BuscarItemViewModelFactory(private val context: Application) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(BuscarItemComposeViewModel::class.java)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return BuscarItemComposeViewModel(context) as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
}*/
|
|
@ -0,0 +1,207 @@
|
|||
package es.verdnatura.presentation.view.feature.buscaritem.fragment
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
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.lazy.items
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusEvent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import es.verdnatura.R
|
||||
import es.verdnatura.presentation.composable.CustomToolbar
|
||||
import es.verdnatura.presentation.composable.ScanLineTextSearch
|
||||
|
||||
@Composable
|
||||
fun LocationScreen(
|
||||
//viewModel: BuscarItemComposeViewModel = viewModel(), // Falta ver de arreglar con koin o viewmodel → No necesario, en el Fragment observa
|
||||
items: List<LocationItem>,
|
||||
onTextChange: (String) -> Unit,
|
||||
titleToolBar: String,
|
||||
onBackClick: () -> Unit = {},
|
||||
modifier: Modifier = Modifier,
|
||||
onLongClick: (LocationItem) -> Unit,
|
||||
onClick: (LocationItem) -> Unit
|
||||
) {
|
||||
var searchText by remember { mutableStateOf("") }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
var showLoading by remember { mutableStateOf(false) }
|
||||
|
||||
val onImeAction: () -> Unit = {
|
||||
onTextChange(searchText)
|
||||
searchText = ""
|
||||
keyboardController?.hide()
|
||||
}
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black)
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black)
|
||||
|
||||
) {
|
||||
if (showLoading) {
|
||||
LottieLoadingAnimation(true)
|
||||
}
|
||||
CustomToolbar(
|
||||
title = titleToolBar,
|
||||
subtitle = "",
|
||||
showBackButton = true,
|
||||
showSwitch = false,
|
||||
iconList = listOf(),
|
||||
onBackClick = onBackClick,
|
||||
onSwitchChange = { },
|
||||
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
|
||||
ScanLineTextSearch(
|
||||
value = searchText,
|
||||
onValueChange = {
|
||||
searchText = it
|
||||
showLoading = true
|
||||
},
|
||||
onImeAction,
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester)
|
||||
.onFocusEvent {
|
||||
if (it.isFocused) {
|
||||
keyboardController?.hide() // Asegura que el teclado no se abra
|
||||
}
|
||||
}
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
keyboardController?.hide()
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.Start
|
||||
) {
|
||||
listOf(
|
||||
R.string.parking,
|
||||
R.string.Matrícula,
|
||||
R.string.Visible,
|
||||
R.string.reserve,
|
||||
R.string.priority,
|
||||
R.string.Fecha
|
||||
).forEach { textRes ->
|
||||
Text(
|
||||
text = stringResource(id = textRes),
|
||||
color = Color.White,
|
||||
fontSize = 14.sp,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalDivider(thickness = 1.dp, color = Color.White)
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
|
||||
items(items) { item ->
|
||||
|
||||
LocationRow(
|
||||
item = item,
|
||||
onLongClick = { selectedItem ->
|
||||
onLongClick(selectedItem)
|
||||
},
|
||||
onClick = { selectedItem ->
|
||||
onClick(selectedItem)
|
||||
},
|
||||
)
|
||||
HorizontalDivider(thickness = 1.dp, color = Color.White)
|
||||
}
|
||||
}
|
||||
showLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun LocationRow(
|
||||
item: LocationItem, onLongClick: (LocationItem) -> Unit, onClick: (LocationItem) -> Unit
|
||||
) {
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
.combinedClickable(onClick = { onClick(item) }, onLongClick = { onLongClick(item) }
|
||||
|
||||
), horizontalArrangement = Arrangement.SpaceEvenly) {
|
||||
Text(text = item.parking, color = Color.White, modifier = Modifier.weight(1f))
|
||||
Text(text = item.matricula, color = Color.White, modifier = Modifier.weight(1f))
|
||||
Text(text = item.visible, color = Color.White, modifier = Modifier.weight(1f))
|
||||
Text(text = item.reserve, color = Color.White, modifier = Modifier.weight(1f))
|
||||
Text(text = item.priority, color = Color.White, modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
text = item.fecha,
|
||||
color = Color.White,
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Modelo de datos
|
||||
data class LocationItem(
|
||||
val parking: String,
|
||||
val matricula: String,
|
||||
val visible: String,
|
||||
val reserve: String,
|
||||
val priority: String,
|
||||
val fecha: String,
|
||||
val sector: String
|
||||
)
|
||||
|
||||
// Vista previa
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun PreviewLocationScreen() {
|
||||
LocationScreen(
|
||||
// viewModel = null,
|
||||
items = listOf(
|
||||
LocationItem("P1", "1234ABC", "Sí", "No", "Alta", "12/02/2025", "previa"),
|
||||
LocationItem("P2", "5678DEF", "No", "Sí", "Baja", "13/02/2025", "previa")
|
||||
),
|
||||
onTextChange = {},
|
||||
titleToolBar = "Buscar Item",
|
||||
onBackClick = {},
|
||||
onLongClick = {},
|
||||
onClick = {})
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package es.verdnatura.presentation.view.feature.buscaritem.fragment
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import com.airbnb.lottie.compose.LottieAnimation
|
||||
import com.airbnb.lottie.compose.LottieCompositionSpec
|
||||
import com.airbnb.lottie.compose.LottieConstants
|
||||
import com.airbnb.lottie.compose.rememberLottieComposition
|
||||
import es.verdnatura.R
|
||||
|
||||
@Composable
|
||||
fun LottieLoadingAnimation(isVisible: Boolean = true) {
|
||||
AnimatedVisibility(
|
||||
visible = isVisible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
val composition by rememberLottieComposition(
|
||||
spec = LottieCompositionSpec.RawRes(R.raw.orange_loading)
|
||||
)
|
||||
|
||||
LottieAnimation(
|
||||
composition = composition,
|
||||
iterations = LottieConstants.IterateForever,
|
||||
speed = 2f,
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.height(dimensionResource(id = R.dimen.verdnatura_logo_large_height))
|
||||
)
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@ buildscript {
|
|||
}
|
||||
plugins {
|
||||
alias(libs.plugins.google.devtools.ksp) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
alias(libs.plugins.compose.compiler) apply false
|
||||
/*alias(libs.plugins.jetbrainsCompose) apply false
|
||||
alias(libs.plugins.compose.compiler) apply false*/
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue