Refactoring dialog filtro ultime consegne

This commit is contained in:
2025-05-27 17:48:41 +02:00
parent 20783026b8
commit 6edeba3246
14 changed files with 777 additions and 351 deletions

View File

@@ -105,6 +105,8 @@ import it.integry.integrywmsnative.gest.spedizione.dialogs.row_info.prod_fabbiso
import it.integry.integrywmsnative.gest.spedizione.dialogs.row_info.prod_fabbisogno_linee_prod.DialogRowInfoProdFabbisognoLineeProdModule;
import it.integry.integrywmsnative.gest.ultime_consegne_cliente.UltimeConsegneClienteComponent;
import it.integry.integrywmsnative.gest.ultime_consegne_cliente.UltimeConsegneClienteModule;
import it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog.DialogUltimeConsegneFiltroAvanzatoComponent;
import it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog.DialogUltimeConsegneFiltroAvanzatoModule;
import it.integry.integrywmsnative.gest.ultimi_arrivi_fornitore.UltimiArriviFornitoreComponent;
import it.integry.integrywmsnative.gest.ultimi_arrivi_fornitore.UltimiArriviFornitoreModule;
import it.integry.integrywmsnative.gest.versamento_merce.VersamentoMerceComponent;
@@ -251,7 +253,8 @@ import it.integry.integrywmsnative.view.dialogs.update_available.DialogUpdateAva
VerificaGiacenzeModule.class,
DialogExtraInfoModule.class,
DialogAskDepositoModule.class,
DialogChooseArtFromListaArtsModule.class
DialogChooseArtFromListaArtsModule.class,
DialogUltimeConsegneFiltroAvanzatoModule.class
})
public interface MainApplicationComponent {
@@ -422,6 +425,8 @@ public interface MainApplicationComponent {
DialogChooseArtFromListaArtsComponent.Factory dialogChooseArtFromListaArtsComponent();
DialogUltimeConsegneFiltroAvanzatoComponent.Factory dialogDialogUltimeConsegneFiltroAvanzatoComponent();
void inject(MainApplication mainApplication);
void inject(AppContext mainApplication);

View File

@@ -26,6 +26,7 @@ import androidx.databinding.Observable;
import androidx.databinding.ObservableField;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewTreeLifecycleOwner;
@@ -540,6 +541,124 @@ public class Converters {
view.setText(UtilityDate.formatDate(newValue, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN));
}
@BindingAdapter(value = {"binding", "parentView", "warningOnOldDates"}, requireAll = false)
public static void bindTextInputEditTextDate(TextInputEditText view, final MutableLiveData<Date> liveDataDate, BaseDialogFragment parentFragment, boolean warningOnOldDates) {
// Ottieni il LifecycleOwner dalla view
LifecycleOwner lifecycleOwner = ViewTreeLifecycleOwner.get(view);
if (lifecycleOwner == null) {
// Non è possibile osservare LiveData senza un LifecycleOwner
// Potresti loggare un errore o gestire questo caso come preferisci
Log.e("BindingAdapters", "LifecycleOwner not found for view " + view.getId());
return;
}
// Rimuovi eventuali TextWatcher o listener precedenti per evitare duplicazioni
// Questo è importante se il BindingAdapter viene chiamato più volte per la stessa view
Object oldTag = view.getTag(R.id.bound_observable_date_listener);
if (oldTag instanceof TextWatcherAdapter) {
view.removeTextChangedListener((TextWatcherAdapter) oldTag);
}
Object oldLiveDataTag = view.getTag(R.id.bound_observable_date_livedata);
if (oldLiveDataTag instanceof MutableLiveData) {
// Se c'era un observer precedente su un altro LiveData, rimuovilo
// Questo scenario è meno comune ma è bene gestirlo
Observer<Date> existingObserver = (Observer<Date>) view.getTag(R.id.bound_observable_date_observer);
if (existingObserver != null) {
((MutableLiveData<Date>) oldLiveDataTag).removeObserver(existingObserver);
}
}
// Imposta il listener per il click per aprire il DatePicker
RunnableArgs<View> onClick = v -> {
Calendar c = UtilityDate.getCalendarInstance();
if (liveDataDate.getValue() != null) {
c.setTime(liveDataDate.getValue());
}
MaterialDatePicker<Long> datePicker =
MaterialDatePicker.Builder.datePicker()
.setSelection(c.getTimeInMillis())
.build();
datePicker.addOnPositiveButtonClickListener(selection -> {
Calendar calendar = UtilityDate.getCalendarInstance();
calendar.setTimeInMillis(selection);
Date selectedDate = calendar.getTime();
if (parentFragment != null && warningOnOldDates && calendar.before(UtilityDate.getCalendarInstance())) {
DialogSimpleMessageView
.makeWarningDialog(new SpannableString("Hai scelto una data precedente a quella odierna. Continuare?"), null, () -> {
liveDataDate.postValue(selectedDate);
}, () -> {
// Non fare nulla se l'utente annulla
})
.show(parentFragment.requireActivity().getSupportFragmentManager(), "DatePickerWarningDialog");
} else {
liveDataDate.postValue(selectedDate);
}
});
datePicker.addOnNegativeButtonClickListener(dialog -> {
// Utente ha annullato, potresti voler resettare o non fare nulla
});
datePicker.addOnCancelListener(dialog -> {
// Utente ha cancellato (es. premendo back), potresti voler resettare o non fare nulla
});
datePicker.show(parentFragment.requireActivity().getSupportFragmentManager(), "MaterialDatePicker");
};
view.setOnClickListener(onClick::run);
if (view.getParent() != null && view.getParent().getParent() != null && view.getParent().getParent() instanceof TextInputLayout) {
((FrameLayout) view.getParent()).setOnClickListener(onClick::run);
((TextInputLayout) view.getParent().getParent()).setOnClickListener(onClick::run);
}
// Osserva il LiveData per aggiornare la view quando il valore cambia
Observer<Date> dateObserver = date -> {
String formattedDate = UtilityDate.formatDate(date, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN);
if (!view.getText().toString().equals(formattedDate)) {
view.setText(formattedDate);
}
};
// Rimuovi l'observer precedente se stiamo ri-bindando allo stesso LiveData
Observer<Date> existingObserver = (Observer<Date>) view.getTag(R.id.bound_observable_date_observer);
if (view.getTag(R.id.bound_observable_date_livedata) == liveDataDate && existingObserver != null) {
liveDataDate.removeObserver(existingObserver);
}
liveDataDate.observe(lifecycleOwner, dateObserver);
// Salva il LiveData e l'observer nel tag per poterli rimuovere in futuro se necessario
view.setTag(R.id.bound_observable_date_livedata, liveDataDate);
view.setTag(R.id.bound_observable_date_observer, dateObserver);
// Imposta il valore iniziale
Date initialDate = liveDataDate.getValue();
view.setText(UtilityDate.formatDate(initialDate, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN));
// Aggiungi un TextWatcher fittizio se necessario per coerenza con altri binding,
// ma in questo caso il LiveData guida l'aggiornamento.
// Se non hai bisogno di reagire ai cambiamenti di testo *dalla view al LiveData*
// (perché il DatePicker lo fa già), questo TextWatcher potrebbe non essere strettamente necessario.
// Tuttavia, per mantenere la struttura simile agli altri, lo includiamo.
TextWatcherAdapter watcher = new TextWatcherAdapter() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Normalmente qui si aggiornerebbe il LiveData,
// ma il DatePicker gestisce l'aggiornamento.
// Potresti aggiungere logica qui se l'utente può modificare il testo direttamente
// e vuoi che ciò si rifletta nel LiveData (richiederebbe parsing della data).
}
};
view.setTag(R.id.bound_observable_date_listener, watcher); // Salva il watcher nel tag
// view.addTextChangedListener(watcher); // Aggiungi solo se necessario
}
@BindingAdapter(value = {"binding", "parentView", "warningOnOldDates"}, requireAll = false)
public static void bindEditTextDate(EditText view, final ObservableField<LocalDate> observableDate, BaseDialogFragment parentFragment, boolean warningOnOldDates) {
Pair<ObservableField<LocalDate>, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
@@ -606,6 +725,67 @@ public class Converters {
view.setText(UtilityDate.formatDate(newValue, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN));
}
@BindingAdapter("binding")
public static void bindAutoCompleteTextView(AutoCompleteTextView view, final MutableLiveData<String> liveDataString) {
LifecycleOwner lifecycleOwner = ViewTreeLifecycleOwner.get(view);
if (lifecycleOwner == null) {
Log.e("BindingAdapters", "LifecycleOwner not found for view " + view.getId());
return;
}
// Rimuovi TextWatcher precedente
Object oldWatcherTag = view.getTag(R.id.bound_observable_actv_watcher);
if (oldWatcherTag instanceof TextWatcherAdapter) {
view.removeTextChangedListener((TextWatcherAdapter) oldWatcherTag);
}
// Rimuovi Observer precedente se il LiveData è cambiato
Object oldLiveDataTag = view.getTag(R.id.bound_observable_actv_livedata);
if (oldLiveDataTag instanceof MutableLiveData && oldLiveDataTag != liveDataString) {
Observer<String> existingObserver = (Observer<String>) view.getTag(R.id.bound_observable_actv_observer);
if (existingObserver != null) {
((MutableLiveData<String>) oldLiveDataTag).removeObserver(existingObserver);
}
}
// TextWatcher per aggiornare il LiveData quando il testo cambia nella View
TextWatcherAdapter watcher = new TextWatcherAdapter() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (liveDataString.getValue() == null || !liveDataString.getValue().equals(s.toString())) {
liveDataString.setValue(s.toString());
}
}
};
view.addTextChangedListener(watcher);
view.setTag(R.id.bound_observable_actv_watcher, watcher);
// Observer per aggiornare la View quando il LiveData cambia
Observer<String> observer = newValue -> {
if (!view.getText().toString().equals(newValue)) {
view.setText(newValue, false); // Il 'false' evita di far scattare il dropdown
}
};
// Rimuovi l'observer precedente se stiamo ri-bindando allo stesso LiveData
Observer<String> existingObserver = (Observer<String>) view.getTag(R.id.bound_observable_actv_observer);
if (view.getTag(R.id.bound_observable_actv_livedata) == liveDataString && existingObserver != null) {
liveDataString.removeObserver(existingObserver);
}
liveDataString.observe(lifecycleOwner, observer);
// Salva il LiveData e l'observer nel tag
view.setTag(R.id.bound_observable_actv_livedata, liveDataString);
view.setTag(R.id.bound_observable_actv_observer, observer);
// Imposta il valore iniziale
String initialValue = liveDataString.getValue();
if (initialValue != null && !view.getText().toString().equals(initialValue)) {
view.setText(initialValue);
}
}
@BindingAdapter("binding")
public static void bindAutoCompleteTextView(AutoCompleteTextView view, final BindableString bindableString) {
Pair<BindableString, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);

View File

@@ -1,10 +1,10 @@
package it.integry.integrywmsnative.core.expansion;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.NonNull;
@@ -47,6 +47,14 @@ public abstract class BaseDialogFragment extends DialogFragment implements Dialo
this.cancelable = cancelable;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
// getDialog().setOn
return super.onCreateDialog(savedInstanceState);
}
@Override
public void show(FragmentManager manager, String tag) {
try {
@@ -58,11 +66,6 @@ public abstract class BaseDialogFragment extends DialogFragment implements Dialo
}
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.initialize();
}
public void onLoadingStarted() {
this.openProgress();

View File

@@ -14,7 +14,6 @@ import androidx.databinding.DataBindingUtil;
import androidx.databinding.ObservableArrayList;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.annimon.stream.Optional;
import com.annimon.stream.Stream;
import java.util.ArrayList;
@@ -39,7 +38,7 @@ import it.integry.integrywmsnative.core.settings.SettingsManager;
import it.integry.integrywmsnative.core.utility.UtilityExceptions;
import it.integry.integrywmsnative.databinding.FragmentMainUltimeConsegneClienteBinding;
import it.integry.integrywmsnative.gest.picking_resi.PickingResiActivity;
import it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog.DialogUltimeConsegneFiltroAvanzato;
import it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog.DialogUltimeConsegneFiltroAvanzatoView;
import it.integry.integrywmsnative.ui.ElevatedToolbar;
public class UltimeConsegneClienteFragment extends BaseFragment implements UltimeConsegneClienteViewModel.Listener, ITitledFragment, IScrollableFragment, IFilterableFragment {
@@ -54,8 +53,6 @@ public class UltimeConsegneClienteFragment extends BaseFragment implements Ultim
@Inject
UltimeConsegneClienteViewModel mViewModel;
private DialogUltimeConsegneFiltroAvanzato.DialogUltimeConsegneFiltroAvanzatoViewModel mAppliedFilterViewModel;
private FragmentMainUltimeConsegneClienteBinding mBindings;
public UltimeConsegneClienteFragment() {
@@ -114,10 +111,13 @@ public class UltimeConsegneClienteFragment extends BaseFragment implements Ultim
this.mViewModel.loadCodAnagClienti(gtbAnags -> {
handler.post(() -> {
DialogUltimeConsegneFiltroAvanzato.make(getActivity(), gtbAnags, mAppliedFilterViewModel, (filter) -> {
mAppliedFilterViewModel = filter;
refreshItems();
}).show();
DialogUltimeConsegneFiltroAvanzatoView.newInstance(gtbAnags, filterResult -> {
if (filterResult == null || filterResult.isAborted()) {
} else refreshConsegne(filterResult.getGtbAnag());
})
.show(requireActivity().getSupportFragmentManager(), "dialog-filtro-ultime-consegne");
});
});
@@ -133,7 +133,6 @@ public class UltimeConsegneClienteFragment extends BaseFragment implements Ultim
mBindings.recyclerView.setHasFixedSize(true);
mBindings.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mBindings.recyclerView.setAdapter(mAdapter);
// mBindings.fastscroll.setRecyclerView(mBindings.recyclerView);
if (mToolbar != null)
@@ -162,21 +161,10 @@ public class UltimeConsegneClienteFragment extends BaseFragment implements Ultim
this.mDocumentiMutableData.addAll(sort(documentList));
}
private void refreshItems() {
private void refreshConsegne(GtbAnag gtbAnag) {
this.onLoadingStarted();
String currentAnagFilter = mAppliedFilterViewModel != null ? mAppliedFilterViewModel.ragSoc.get() : null;
String currentCodAnagFilter = null;
Optional<GtbAnag> singleGtbAnag = Stream.of(this.mViewModel.getGtbAnagClienti().getValue())
.filter(x -> (x.getCodAnag() + " - " + x.getRagSoc()).equals(currentAnagFilter))
.findSingle();
if (singleGtbAnag.isPresent()) {
currentCodAnagFilter = singleGtbAnag.get().getCodAnag();
}
this.mViewModel.loadConsegneClienti(currentCodAnagFilter,
this.mViewModel.loadConsegneClienti(gtbAnag.getCodAnag(),
null,
-1,
548

View File

@@ -1,192 +0,0 @@
package it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog;
import android.app.DatePickerDialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import androidx.appcompat.app.AlertDialog;
import com.annimon.stream.Stream;
import com.annimon.stream.function.Predicate;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.di.BindableString;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.model.GtbAnag;
import it.integry.integrywmsnative.core.utility.UtilityDate;
import it.integry.integrywmsnative.core.utility.UtilityString;
import it.integry.integrywmsnative.databinding.DialogUltimeConsegneFiltroAvanzatoBinding;
import it.integry.integrywmsnative.ui.adapter.SimpleAutoCompleteDropdownAdapter;
public class DialogUltimeConsegneFiltroAvanzato {
private AlertDialog mAlert;
private Context mContext;
private DialogUltimeConsegneFiltroAvanzatoViewModel mBaseViewModel;
private RunnableArgs<DialogUltimeConsegneFiltroAvanzatoViewModel> mOnDismiss;
private ArrayAdapter<String> arrayAdapterRagSoc;
private List<GtbAnag> mItems;
private List<GtbAnag> mFilteredItems;
private Predicate<GtbAnag> currentRagSocPredicate = null;
private Predicate<GtbAnag> currentDataDocPredicate = null;
public static AlertDialog make(final Context context,
final List<GtbAnag> items,
DialogUltimeConsegneFiltroAvanzatoViewModel baseViewModel,
RunnableArgs<DialogUltimeConsegneFiltroAvanzatoViewModel> onDismiss) {
return new DialogUltimeConsegneFiltroAvanzato(context, items, baseViewModel, onDismiss).mAlert;
}
public DialogUltimeConsegneFiltroAvanzato(final Context context,
final List<GtbAnag> items,
DialogUltimeConsegneFiltroAvanzatoViewModel baseViewModel,
RunnableArgs<DialogUltimeConsegneFiltroAvanzatoViewModel> onDismiss) {
mContext = context;
mItems = items;
mBaseViewModel = baseViewModel;
mOnDismiss = onDismiss;
DialogUltimeConsegneFiltroAvanzatoViewModel viewModel = baseViewModel != null ? baseViewModel : new DialogUltimeConsegneFiltroAvanzatoViewModel();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
DialogUltimeConsegneFiltroAvanzatoBinding binding = DialogUltimeConsegneFiltroAvanzatoBinding.inflate(inflater, null, false);
final AlertDialog.Builder dialog = new AlertDialog.Builder(context)
.setView(binding.getRoot());
binding.setViewmodel(viewModel);
initViewModelNew(viewModel);
initView(binding, viewModel);
mAlert = dialog.create();
mAlert.setCanceledOnTouchOutside(false);
mAlert.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
mAlert.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
binding.positiveBtn.setOnClickListener(view -> {
mAlert.dismiss();
});
binding.neutralBtn.setOnClickListener(view -> {
resetAll(viewModel);
});
mAlert.setOnDismissListener(dialogInterface -> {
if(mOnDismiss != null) mOnDismiss.run(viewModel);
});
refreshList();
}
private void resetAll(DialogUltimeConsegneFiltroAvanzatoViewModel viewModel) {
viewModel.ragSoc.set(null);
viewModel.dataDoc.set(null);
}
private void initView(DialogUltimeConsegneFiltroAvanzatoBinding bindings, DialogUltimeConsegneFiltroAvanzatoViewModel viewModel) {
arrayAdapterRagSoc = new SimpleAutoCompleteDropdownAdapter<>(mContext, R.layout.dialog_vendita_filtro_avanzato__single_item, getAvailableRagSocs(false));
arrayAdapterRagSoc.addAll();
bindings.filledExposedDropdownRagSoc.setAdapter(arrayAdapterRagSoc);
bindings.filledExposedDropdownDataDoc.setOnClickListener(view -> {
Calendar c = UtilityDate.getCalendarInstance();
if(viewModel.dataDocDate != null) {
c.setTime(viewModel.dataDocDate);
}
int mYear = c.get(Calendar.YEAR);
int mMonth = c.get(Calendar.MONTH);
int mDay = c.get(Calendar.DAY_OF_MONTH);
DatePickerDialog datePickerDialog = new DatePickerDialog(mContext,
(view2, year, month, day) -> {
viewModel.dataDocDate = new GregorianCalendar(year, month, day).getTime();
viewModel.dataDoc.set(UtilityDate.formatDate(viewModel.dataDocDate, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN));
}, mYear, mMonth, mDay);
datePickerDialog.show();
});
viewModel.ragSoc.refresh();
viewModel.dataDoc.refresh();
}
private void initViewModelNew(DialogUltimeConsegneFiltroAvanzatoViewModel viewModel) {
BindableString.resetListeners(viewModel.ragSoc);
BindableString.registerListener(viewModel.ragSoc, value -> {
if (UtilityString.isNullOrEmpty(value)) currentRagSocPredicate = null;
else {
currentRagSocPredicate = o -> {
return o.getRagSoc().equalsIgnoreCase(viewModel.ragSoc.get());
};
}
refreshList();
});
}
private List<String> getAvailableRagSocs(boolean skipRecalc) {
if(currentDataDocPredicate == null){
mFilteredItems = mItems;
} else if(!skipRecalc){
Stream<GtbAnag> tmpStream = Stream.of(mItems)
.filter(x ->
(currentDataDocPredicate == null || (currentDataDocPredicate.test(x)))
);
mFilteredItems = tmpStream.toList();
}
return Stream.of(mFilteredItems).map(x -> x.getCodAnag() + " - " + x.getRagSoc()).distinct().withoutNulls().toList();
}
private void refreshList() {
if(currentRagSocPredicate == null && currentDataDocPredicate == null){
mFilteredItems = mItems;
} else {
Stream<GtbAnag> tmpStream = Stream.of(mItems)
.filter(x ->
(currentRagSocPredicate == null || (currentRagSocPredicate.test(x))) &&
(currentDataDocPredicate== null || (currentDataDocPredicate.test(x)))
);
mFilteredItems = tmpStream.toList();
}
}
public class DialogUltimeConsegneFiltroAvanzatoViewModel {
public BindableString ragSoc = new BindableString();
public BindableString dataDoc = new BindableString();
public Date dataDocDate;
}
}

View File

@@ -0,0 +1,14 @@
package it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog;
import dagger.Subcomponent;
@Subcomponent
public interface DialogUltimeConsegneFiltroAvanzatoComponent {
@Subcomponent.Factory
interface Factory {
DialogUltimeConsegneFiltroAvanzatoComponent create();
}
void inject(DialogUltimeConsegneFiltroAvanzatoView dialogUltimeConsegneFiltroAvanzatoView);
}

View File

@@ -0,0 +1,14 @@
package it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog;
import dagger.Module;
import dagger.Provides;
@Module(subcomponents = DialogUltimeConsegneFiltroAvanzatoComponent.class)
public class DialogUltimeConsegneFiltroAvanzatoModule {
@Provides
DialogUltimeConsegneFiltroAvanzatoViewModel providesDialogDialogUltimeConsegneFiltroAvanzatoViewModel() {
return new DialogUltimeConsegneFiltroAvanzatoViewModel();
}
}

View File

@@ -0,0 +1,158 @@
package it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import it.integry.integrywmsnative.MainApplication;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.expansion.BaseDialogFragment;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.model.GtbAnag;
import it.integry.integrywmsnative.databinding.DialogUltimeConsegneFiltroAvanzatoBinding;
import it.integry.integrywmsnative.ui.adapter.SimpleAutoCompleteDropdownAdapter;
public class DialogUltimeConsegneFiltroAvanzatoView extends BaseDialogFragment {
@Inject
DialogUltimeConsegneFiltroAvanzatoViewModel mViewModel;
private DialogUltimeConsegneFiltroAvanzatoBinding mBindings;
private Context mContext;
private SimpleAutoCompleteDropdownAdapter<String> arrayAdapterRagSoc;
private final RunnableArgs<Result> onConfirm;
public static DialogUltimeConsegneFiltroAvanzatoView newInstance(final List<GtbAnag> items, RunnableArgs<Result> onConfirm) {
return new DialogUltimeConsegneFiltroAvanzatoView(items, onConfirm);
}
private DialogUltimeConsegneFiltroAvanzatoView(final List<GtbAnag> items, RunnableArgs<Result> onConfirm) {
super();
this.onConfirm = onConfirm;
MainApplication.appComponent
.dialogDialogUltimeConsegneFiltroAvanzatoComponent()
.create()
.inject(this);
mViewModel.init(items);
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
this.mContext = requireContext();
mBindings = DialogUltimeConsegneFiltroAvanzatoBinding.inflate(LayoutInflater.from(this.mContext), null, false);
mBindings.setLifecycleOwner(this); // Necessario per LiveData e binding
mBindings.setView(this);
mBindings.setViewmodel(mViewModel);
setCancelable(false);
var alertDialog = new MaterialAlertDialogBuilder(this.mContext)
.setView(mBindings.getRoot())
.setCancelable(isCancelable())
.setPositiveButton(R.string.ok, (dialog, which) -> onOk())
.create();
alertDialog.setCanceledOnTouchOutside(isCancelable());
alertDialog.setOnShowListener(this); // Rimosso se non specificamente necessario per altro
this.initDropDowns();
return alertDialog;
}
@Override
public void dismiss() {
if (getDialog() != null) getDialog().dismiss();
super.dismiss();
}
private void initDropDowns() {
// Usa il LiveData delle stringhe formattate dal ViewModel
arrayAdapterRagSoc = new SimpleAutoCompleteDropdownAdapter<>(
mContext,
R.layout.dialog_vendita_filtro_avanzato__single_item, // Assicurati che questo layout sia un semplice TextView
mViewModel.getAvailableRagSocsStrings(), // LiveData<List<String>>
this // LifecycleOwner
);
mBindings.filledExposedDropdownRagSoc.setAdapter(arrayAdapterRagSoc);
// Gestisci la selezione di un item
mBindings.filledExposedDropdownRagSoc.setOnItemClickListener((parent, view, position, id) -> {
String selectedFormattedString = arrayAdapterRagSoc.getItem(position);
if (selectedFormattedString != null) {
GtbAnag selectedAnag = mViewModel.findGtbAnagByFormattedString(selectedFormattedString);
mViewModel.setRagSoc(selectedAnag); // Aggiorna il GtbAnag nel ViewModel
}
});
}
public void reset() {
if (mViewModel != null) {
mViewModel.resetFilters();
}
// I campi testo dovrebbero aggiornarsi automaticamente grazie al data binding
// e agli observer sui LiveData (ragSocTextBindable e dataDocBindable).
}
public void onOk() {
if (onConfirm != null)
onConfirm.run(Result.completed(mViewModel.getRagSocBindable().getValue(), mViewModel.getDataDocBindable().getValue()));
// dismiss();
}
@Override
public void onDestroyView() {
super.onDestroyView();
mBindings = null; // Per evitare memory leaks
}
public static class Result {
private final GtbAnag gtbAnag;
private final Date dataDoc;
private final boolean isAborted;
private Result(GtbAnag gtbAnag, Date dataDoc, boolean isAborted) {
this.gtbAnag = gtbAnag;
this.dataDoc = dataDoc;
this.isAborted = isAborted;
}
public static Result completed(GtbAnag gtbAnag, Date dataDoc) {
return new Result(gtbAnag, dataDoc, false);
}
public static Result aborted() {
return new Result(null, null, true);
}
public boolean isAborted() {
return isAborted;
}
public GtbAnag getGtbAnag() {
return gtbAnag;
}
public Date getDataDoc() {
return dataDoc;
}
}
}

View File

@@ -0,0 +1,216 @@
package it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import it.integry.integrywmsnative.core.model.GtbAnag;
public class DialogUltimeConsegneFiltroAvanzatoViewModel {
private final MutableLiveData<GtbAnag> ragSoc = new MutableLiveData<>();
private final MutableLiveData<Date> dataDoc = new MutableLiveData<>();
private List<GtbAnag> availableItems; // Lista completa di GtbAnag originali
private final MutableLiveData<List<GtbAnag>> filteredItems = new MutableLiveData<>();
// LiveData per il testo dell'AutoCompleteTextView della Ragione Sociale
private final MutableLiveData<String> ragSocText = new MutableLiveData<>();
// LiveData per popolare l'adapter dell'AutoCompleteTextView
private final MutableLiveData<List<String>> availableRagSocsStrings = new MutableLiveData<>();
private final MutableLiveData<Predicate<GtbAnag>> currentRagSocPredicate = new MutableLiveData<>();
private final MutableLiveData<Predicate<GtbAnag>> currentDataDocPredicate = new MutableLiveData<>();
public void init(List<GtbAnag> availableItems) {
this.availableItems = availableItems;
// Imposta il valore iniziale di filteredItems e availableRagSocsStrings
// assicurandosi che i predicati siano null all'inizio se non diversamente specificato.
if (this.currentRagSocPredicate.getValue() == null && this.currentDataDocPredicate.getValue() == null) {
this.filteredItems.setValue(availableItems);
} else {
// Se ci sono predicati pre-impostati, applicali.
// Questa logica è più per completezza, normalmente all'init i filtri sono resettati.
refreshList();
}
// Calcola availableRagSocsStrings dopo aver impostato availableItems e potenzialmente i predicati.
this.availableRagSocsStrings.setValue(calcAvailableRagSocsStrings());
this.initBindable();
}
private void initBindable() {
// Quando il GtbAnag selezionato (ragSoc) cambia
this.ragSoc.observeForever(selectedAnag -> {
String textToShow = "";
Predicate<GtbAnag> newPredicate = null;
if (selectedAnag != null) {
textToShow = formatGtbAnagToString(selectedAnag);
newPredicate = o -> o.getCodAnag().equals(selectedAnag.getCodAnag());
}
if (!Objects.equals(ragSocText.getValue(), textToShow)) {
ragSocText.postValue(textToShow);
}
if (!Objects.equals(currentRagSocPredicate.getValue(), newPredicate)) {
currentRagSocPredicate.postValue(newPredicate);
} else if (newPredicate == null && currentRagSocPredicate.getValue() != null) {
currentRagSocPredicate.postValue(null);
}
});
// Quando il testo nell'AutoCompleteTextView (ragSocText) cambia
this.ragSocText.observeForever(text -> {
GtbAnag currentAnagSelection = ragSoc.getValue();
if (text == null || text.isEmpty()) {
if (currentAnagSelection != null) {
this.ragSoc.postValue(null);
}
} else {
if (currentAnagSelection != null) {
if (!text.equals(formatGtbAnagToString(currentAnagSelection))) {
this.ragSoc.postValue(null);
}
}
}
});
// Quando la data del documento (dataDoc) cambia
this.dataDoc.observeForever(date -> {
Predicate<GtbAnag> newDatePredicate = null;
if (date != null) {
// TODO: Implementare la logica effettiva per creare un predicato GtbAnag basato sulla data.
// Poiché GtbAnag è un'anagrafica, questo filtro potrebbe non applicarsi direttamente
// ad essa, ma a una lista di transazioni/documenti che usano GtbAnag.
// Se 'dataDoc' NON deve filtrare la lista di GtbAnag, allora 'currentDataDocPredicate'
// dovrebbe essere gestito diversamente o non essere legato a questo 'dataDoc'.
// Per ora, l'effetto principale del reset di 'dataDoc' (impostandolo a null)
// sarà quello di impostare 'currentDataDocPredicate' a null.
// Esempio concettuale se GtbAnag avesse un campo data:
// newDatePredicate = gtbAnag -> UtilityDate.isSameDay(gtbAnag.getDataDocumento(), date);
}
// Aggiorna currentDataDocPredicate solo se il suo stato logico cambia
boolean predicateEffectivelyChanged = !Objects.equals(currentDataDocPredicate.getValue(), newDatePredicate) ||
(newDatePredicate == null && currentDataDocPredicate.getValue() != null) ||
(newDatePredicate != null && currentDataDocPredicate.getValue() == null);
if (predicateEffectivelyChanged) {
currentDataDocPredicate.postValue(newDatePredicate);
}
});
this.currentRagSocPredicate.observeForever(ragSocPredicate -> refreshList());
this.currentDataDocPredicate.observeForever(dataDocPredicate -> refreshList());
// Calcola availableRagSocsStrings inizialmente dopo che gli observer sono impostati
// e availableItems è disponibile.
if (this.availableItems != null) {
refreshList(); // Questo chiamerà anche calcAvailableRagSocsStrings
}
}
private void refreshList() {
Stream<GtbAnag> tmpStream;
if (availableItems == null) {
filteredItems.postValue(List.of());
availableRagSocsStrings.postValue(List.of());
return;
}
Predicate<GtbAnag> ragSocPred = currentRagSocPredicate.getValue();
Predicate<GtbAnag> dataDocPred = currentDataDocPredicate.getValue();
if (ragSocPred == null && dataDocPred == null) {
tmpStream = availableItems.stream();
} else {
tmpStream = availableItems.stream()
.filter(x -> (ragSocPred == null || ragSocPred.test(x)) &&
(dataDocPred == null || dataDocPred.test(x)));
}
filteredItems.postValue(tmpStream.collect(Collectors.toList()));
availableRagSocsStrings.postValue(calcAvailableRagSocsStrings());
}
private List<String> calcAvailableRagSocsStrings() {
if (availableItems == null) return List.of();
Predicate<GtbAnag> dataFilterPredicate = currentDataDocPredicate.getValue();
return availableItems.stream()
.filter(Objects::nonNull)
// Filtra la lista per l'adapter dell'AutoCompleteTextView solo per data,
// il filtro testuale lo fa l'adapter stesso.
.filter(x -> (dataFilterPredicate == null || dataFilterPredicate.test(x)))
.map(this::formatGtbAnagToString)
.distinct()
.sorted() // Opzionale: ordina alfabeticamente
.collect(Collectors.toList());
}
private String formatGtbAnagToString(GtbAnag anag) {
if (anag == null) return "";
return anag.getCodAnag() + " - " + anag.getRagSoc();
}
public GtbAnag findGtbAnagByFormattedString(String formattedString) {
if (formattedString == null || availableItems == null) {
return null;
}
for (GtbAnag anag : availableItems) {
if (anag != null && formattedString.equals(formatGtbAnagToString(anag))) {
return anag;
}
}
return null;
}
public LiveData<List<String>> getAvailableRagSocsStrings() {
return availableRagSocsStrings;
}
public MutableLiveData<String> getRagSocTextBindable() {
return ragSocText;
}
public MutableLiveData<GtbAnag> getRagSocBindable() {
return ragSoc;
}
public MutableLiveData<Date> getDataDocBindable() {
return dataDoc;
}
public DialogUltimeConsegneFiltroAvanzatoViewModel setRagSoc(GtbAnag ragSocItem) {
if (!Objects.equals(this.ragSoc.getValue(), ragSocItem)) {
this.ragSoc.postValue(ragSocItem);
}
return this;
}
public DialogUltimeConsegneFiltroAvanzatoViewModel setDataDoc(Date dataDocItem) {
// L'observer su this.dataDoc gestirà l'aggiornamento di currentDataDocPredicate.
if (!Objects.equals(this.dataDoc.getValue(), dataDocItem)) {
this.dataDoc.postValue(dataDocItem);
}
return this;
}
/**
* Resetta tutti i filtri ai loro stati predefiniti.
*/
public void resetFilters() {
// Impostando i LiveData a null, gli observer associati (in initBindable)
// si occuperanno di resettare i testi e i predicati.
setRagSoc(null);
setDataDoc(null); // Questo chiamerà l'observer di dataDoc che resetterà currentDataDocPredicate
// refreshList() sarà chiamato automaticamente dagli observer dei predicati.
}
}

View File

@@ -1,119 +1,189 @@
package it.integry.integrywmsnative.ui.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.ObservableArrayList;
import androidx.databinding.ObservableList;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import java.util.ArrayList;
import java.util.List;
public class SimpleAutoCompleteDropdownAdapter<T> extends ArrayAdapter<T> {
private final ObservableArrayList<T> objects;
private final LayoutInflater mInflater;
private int mDropDownResource;
private final List<T> originalItems = new ArrayList<>();
private final Object originalItemsLock = new Object();
public SimpleAutoCompleteDropdownAdapter(@NonNull Context context, int resource, @NonNull List<T> objects) {
this(context, resource, new ObservableArrayList<>());
this.objects.addAll(objects);
/**
* Costruttore per una lista statica di oggetti.
*
* @param context Il contesto corrente.
* @param resource La risorsa ID per una risorsa di layout contenente un TextView da popolare.
* @param staticObjects Gli oggetti da rappresentare nella ListView.
*/
public SimpleAutoCompleteDropdownAdapter(@NonNull Context context, int resource, @NonNull List<T> staticObjects) {
super(context, resource, new ArrayList<>(staticObjects)); // Usa una copia per la lista visualizzata
synchronized (originalItemsLock) {
this.originalItems.addAll(staticObjects); // Salva una copia per il filtraggio
}
}
public SimpleAutoCompleteDropdownAdapter(@NonNull Context context, int resource, @NonNull ObservableArrayList<T> objects) {
super(context, resource, new ArrayList<>(objects));
mInflater = LayoutInflater.from(context);
mDropDownResource = resource;
this.objects = objects;
/**
* Costruttore per una ObservableArrayList di oggetti.
* L'adapter si aggiornerà automaticamente quando la lista osservabile cambia.
*
* @param context Il contesto corrente.
* @param resource La risorsa ID per una risorsa di layout contenente un TextView da popolare.
* @param observableList La ObservableArrayList di oggetti.
*/
public SimpleAutoCompleteDropdownAdapter(@NonNull Context context, int resource, @NonNull ObservableArrayList<T> observableList) {
super(context, resource, new ArrayList<>(observableList)); // Inizializza con lo stato corrente
synchronized (originalItemsLock) {
this.originalItems.addAll(observableList); // Salva lo stato corrente per il filtraggio
}
observableList.addOnListChangedCallback(new ObservableList.OnListChangedCallback<ObservableList<T>>() {
private void updateLists() {
// Si assume che observableList sia modificata sul thread UI o che la sincronizzazione sia gestita esternamente.
List<T> currentData = new ArrayList<>(observableList); // Crea uno snapshot
synchronized (originalItemsLock) {
originalItems.clear();
originalItems.addAll(currentData); // Aggiorna la lista originale per il filtraggio
}
// Aggiorna la lista visualizzata dall'adapter
clear();
addAll(currentData); // Usa lo snapshot
// notifyDataSetChanged(); // ArrayAdapter.addAll non chiama notifyDataSetChanged, quindi è necessario se non chiamato da clear()
// Tuttavia, clear() e addAll() di ArrayAdapter gestiscono la notifica se mNotifyOnChange è true (default).
// Per sicurezza e coerenza con il codice originale, lo manteniamo.
notifyDataSetChanged();
}
// Listener per aggiornare l'adapter quando la lista cambia
this.objects.addOnListChangedCallback(new ObservableList.OnListChangedCallback<ObservableList<T>>() {
@Override
public void onChanged(ObservableList<T> sender) {
updateAdapter();
updateLists();
}
@Override
public void onItemRangeChanged(ObservableList<T> sender, int positionStart, int itemCount) {
updateAdapter();
updateLists();
}
@Override
public void onItemRangeInserted(ObservableList<T> sender, int positionStart, int itemCount) {
updateAdapter();
updateLists();
}
@Override
public void onItemRangeMoved(ObservableList<T> sender, int fromPosition, int toPosition, int itemCount) {
updateAdapter();
updateLists();
}
@Override
public void onItemRangeRemoved(ObservableList<T> sender, int positionStart, int itemCount) {
updateAdapter();
updateLists();
}
});
}
private void updateAdapter() {
clear();
addAll(objects);
notifyDataSetChanged();
/**
* Costruttore per LiveData contenente una lista di oggetti.
* L'adapter si aggiornerà automaticamente quando il LiveData emette una nuova lista.
*
* @param context Il contesto corrente.
* @param resource La risorsa ID per una risorsa di layout contenente un TextView da popolare.
* @param liveData Il LiveData che contiene la lista di oggetti.
* @param lifecycleOwner Il LifecycleOwner usato per osservare il LiveData.
*/
public SimpleAutoCompleteDropdownAdapter(@NonNull Context context, int resource,
@NonNull LiveData<List<T>> liveData,
@NonNull LifecycleOwner lifecycleOwner) {
super(context, resource, new ArrayList<>()); // Inizia vuoto, LiveData popolerà
liveData.observe(lifecycleOwner, newList -> {
// L'observer di LiveData viene eseguito sul thread UI
List<T> dataToUse = (newList == null) ? new ArrayList<>() : new ArrayList<>(newList);
synchronized (originalItemsLock) {
originalItems.clear();
originalItems.addAll(dataToUse); // Aggiorna la lista originale per il filtraggio
}
// Aggiorna la lista visualizzata dall'adapter
clear();
addAll(dataToUse); // addAll gestisce correttamente una collezione vuota se newList è null
// notifyDataSetChanged(); // Come sopra, gestito da clear/addAll se mNotifyOnChange è true. Mantenuto per coerenza.
notifyDataSetChanged();
});
}
@NonNull
@Override
public Filter getFilter() {
return new StringFilter(objects);
return new StringFilter();
}
private class StringFilter extends Filter {
private final List<T> sourceObjects;
public StringFilter(List<T> objects) {
this.sourceObjects = objects;
}
@Override
protected FilterResults performFiltering(CharSequence chars) {
String filterSeq = chars == null ? "" : chars.toString().toLowerCase();
FilterResults result = new FilterResults();
List<T> itemsToFilter;
// Accedi alla lista originale in modo thread-safe
synchronized (originalItemsLock) {
itemsToFilter = new ArrayList<>(originalItems); // Usa una copia della lista originale
}
if (filterSeq.isEmpty()) {
result.values = new ArrayList<>(sourceObjects);
result.count = sourceObjects.size();
result.values = itemsToFilter; // Restituisce tutti gli elementi originali
result.count = itemsToFilter.size();
} else {
ArrayList<T> filtered = new ArrayList<>();
for (T object : sourceObjects) {
String stringObject = object.toString();
if (stringObject.toLowerCase().contains(filterSeq)) {
filtered.add(object);
ArrayList<T> filteredItems = new ArrayList<>();
for (T object : itemsToFilter) { // Filtra dalla lista originale completa
if (object != null) {
String stringObject = object.toString();
if (stringObject.toLowerCase().contains(filterSeq)) {
filteredItems.add(object);
}
}
}
result.values = filtered;
result.count = filtered.size();
result.values = filteredItems;
result.count = filteredItems.size();
}
return result;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
List<T> filtered = (List<T>) results.values;
if (filtered != null) {
synchronized (this) {
clear();
addAll(filtered);
notifyDataSetChanged();
}
protected void publishResults(CharSequence constraint, @Nullable FilterResults results) {
List<T> newValues = null;
if (results != null && results.values != null) {
newValues = (List<T>) results.values;
}
// Aggiorna la lista visualizzata dall'adapter (mObjects)
// Non modificare originalItems qui
SimpleAutoCompleteDropdownAdapter.this.clear();
if (newValues != null) {
SimpleAutoCompleteDropdownAdapter.this.addAll(newValues);
}
// ArrayAdapter.clear() e ArrayAdapter.addAll() dovrebbero chiamare notifyDataSetChanged()
// se mNotifyOnChange è true (default).
// Tuttavia, la logica originale usava una notifica esplicita, quindi la manteniamo.
if (results != null && results.count > 0) {
SimpleAutoCompleteDropdownAdapter.this.notifyDataSetChanged();
} else {
// Se non ci sono risultati o il filtro è vuoto e non ha prodotto risultati (improbabile con la logica sopra)
// notifyDataSetInvalidated indica che i dati non sono validi e la vista dovrebbe essere resettata.
// notifyDataSetChanged è generalmente sufficiente se la lista è semplicemente vuota.
SimpleAutoCompleteDropdownAdapter.this.notifyDataSetInvalidated();
}
}
}
}

View File

@@ -1,65 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
<layout 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">
<data>
<variable
name="view"
type="it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog.DialogUltimeConsegneFiltroAvanzatoView" />
<variable
name="viewmodel"
type="it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog.DialogUltimeConsegneFiltroAvanzato.DialogUltimeConsegneFiltroAvanzatoViewModel" />
type="it.integry.integrywmsnative.gest.ultime_consegne_cliente.dialog.DialogUltimeConsegneFiltroAvanzatoViewModel" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="0dp">
<ScrollView
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
<LinearLayout
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
android:paddingHorizontal="16dp"
android:paddingVertical="16dp">
<androidx.appcompat.widget.AppCompatTextView
<TextView
android:id="@+id/title_text"
style="@style/MaterialAlertDialog.Material3.Title.Text.CenterStacked"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextViewMaterial.Dialog.HeadlineText"
android:text="@string/dialog_vendita_filtro_avanzato"
android:layout_marginBottom="16dp"
android:gravity="center_horizontal"
android:layout_marginBottom="12dp"/>
android:text="@string/dialog_vendita_filtro_avanzato" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_cod_rag_soc"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/rag_soc"
android:imeOptions="actionNext"
android:nextFocusDown="@id/input_data_doc"
android:hint="@string/rag_soc">
android:nextFocusDown="@id/input_data_doc">
<androidx.appcompat.widget.AppCompatAutoCompleteTextView
android:id="@+id/filled_exposed_dropdown_rag_soc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:imeOptions="actionNext"
android:nextFocusDown="@id/input_data_doc"
app:binding="@{viewmodel.ragSoc}" />
android:singleLine="true"
android:text="@={viewmodel.ragSocTextBindable}" /> <!-- Binding bidirezionale -->
</com.google.android.material.textfield.TextInputLayout>
@@ -68,6 +68,7 @@
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="@string/document_date">
@@ -75,62 +76,25 @@
android:id="@+id/filled_exposed_dropdown_data_doc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textNoSuggestions"
android:focusable="false"
android:inputType="textNoSuggestions"
android:singleLine="true"
app:binding="@{viewmodel.dataDoc}"/>
android:textAppearance="?attr/textAppearanceSubtitle1"
android:minHeight="?attr/listPreferredItemHeightSmall"
app:binding="@{viewmodel.dataDocBindable}"
app:parentView="@{view}" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
<Button
style="@style/Button.PrimaryOutline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginTop="8dp">
android:layout_gravity="center_horizontal"
android:layout_marginTop="12dp"
android:text="@string/reset"
android:onClick="@{() -> view.reset()}"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/buttons_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/neutral_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
style="@style/Button.PrimaryOutline"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/buttons_guideline"
app:strokeColor="?colorPrimary"
android:text="@string/reset"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/positive_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
style="@style/Button.PrimaryFull"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="@id/buttons_guideline"
app:layout_constraintEnd_toEndOf="parent"
android:text="@string/confirm"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</ScrollView>
</androidx.cardview.widget.CardView>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.cardview.widget.CardView>
</layout>

View File

@@ -8,8 +8,8 @@
<!-- <item name="android:textAllCaps">false</item>-->
<!-- <item name="android:paddingTop">8dp</item>-->
<!-- <item name="android:paddingBottom">8dp</item>-->
<item name="iconTint">@color/colorPrimary</item>
<item name="strokeColor">@color/colorPrimary</item>
<item name="iconTint">?colorPrimary</item>
<item name="strokeColor">?colorPrimary</item>
</style>
<style name="Button.PrimaryFull" parent="Widget.Material3.Button.UnelevatedButton">
@@ -18,7 +18,7 @@
<!-- <item name="android:textAllCaps">false</item>-->
<!-- <item name="android:paddingTop">8dp</item>-->
<!-- <item name="android:paddingBottom">8dp</item>-->
<item name="backgroundTint">@color/colorPrimary</item>
<item name="backgroundTint">?colorPrimary</item>
<item name="iconTint">@android:color/white</item>
</style>

View File

@@ -2,8 +2,14 @@
<resources>
<item name="bound_observable" type="id" />
<item name="bound_observable_date_listener" type="id" />
<item name="bound_observable_date_livedata" type="id" />
<item name="bound_observable_date_observer" type="id" />
<item name="bound_observable_visibility" type="id" />
<item name="bound_reverse_visibility" type="id" />
<item name="bound_observable_actv_watcher" type="id" />
<item name="bound_observable_actv_livedata" type="id" />
<item name="bound_observable_actv_observer" type="id" />

Binary file not shown.