From a19839b47375434843e9a32efbdd87f2f0c261ad Mon Sep 17 00:00:00 2001 From: Tom Hicks Date: Thu, 27 May 2021 04:49:26 -0700 Subject: [PATCH] Adds edit basic info screen with most string fields. Cleans up fonts/margins on edit screens. Makes the EditMonsterViewModel shared between edit monster fragments. --- .../ui/editmonster/EditBasicInfoFragment.java | 95 +++++++++++++++ .../ui/editmonster/EditMonsterFragment.java | 55 +++++---- .../ui/editmonster/EditMonsterViewModel.java | 108 ++++++++++++++++-- .../res/layout/fragment_edit_basic_info.xml | 106 +++++++++++++++++ .../main/res/layout/fragment_edit_monster.xml | 20 +++- .../main/res/navigation/mobile_navigation.xml | 30 +++-- app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 6 + 8 files changed, 386 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditBasicInfoFragment.java create mode 100644 app/src/main/res/layout/fragment_edit_basic_info.xml diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditBasicInfoFragment.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditBasicInfoFragment.java new file mode 100644 index 0000000..10a4377 --- /dev/null +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditBasicInfoFragment.java @@ -0,0 +1,95 @@ +package com.majinnaibu.monstercards.ui.editmonster; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; + +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavBackStackEntry; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; + +import com.majinnaibu.monstercards.R; +import com.majinnaibu.monstercards.ui.MCFragment; +import com.majinnaibu.monstercards.utils.Logger; +import com.majinnaibu.monstercards.utils.TextChangedListener; + +/** + * A simple {@link Fragment} subclass. + */ +public class EditBasicInfoFragment extends MCFragment { + private EditMonsterViewModel mViewModel; + private ViewHolder mHolder; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); + NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation); + mViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class); + + // Inflate the layout for this fragment + View root = inflater.inflate(R.layout.fragment_edit_basic_info, container, false); + + mHolder = new ViewHolder(root); + mHolder.name.setText(mViewModel.getName().getValue()); + mHolder.name.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { + mViewModel.setName(s.toString()); + Logger.logDebug(String.format("Monster Name changed to %s", mViewModel.getName().getValue())); + })); + + mHolder.size.setText(mViewModel.getSize().getValue()); + mHolder.size.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { + mViewModel.setSize(s.toString()); + Logger.logDebug(String.format("Monster Size changed to %s", mViewModel.getSize().getValue())); + })); + + mHolder.type.setText(mViewModel.getType().getValue()); + mHolder.type.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { + mViewModel.setType(s.toString()); + Logger.logDebug(String.format("Monster Type changed to %s", mViewModel.getType().getValue())); + })); + + mHolder.subtype.setText(mViewModel.getSubtype().getValue()); + mHolder.subtype.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { + mViewModel.setSubtype(s.toString()); + Logger.logDebug(String.format("Monster Subtype changed to %s", mViewModel.getSubtype().getValue())); + })); + + mHolder.alignment.setText(mViewModel.getAlignment().getValue()); + mHolder.alignment.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { + mViewModel.setAlignment(s.toString()); + Logger.logDebug(String.format("Monster Alignment changed to %s", mViewModel.getAlignment().getValue())); + })); + + mHolder.customHitPoints.setText(mViewModel.getCustomHitPoints().getValue()); + mHolder.customHitPoints.addTextChangedListener((new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { + mViewModel.setCustomHitPoints(s.toString()); + Logger.logDebug(String.format("Monster Custom Hit Points changed to %s", mViewModel.getCustomHitPoints().getValue())); + }))); + + return root; + } + + private static class ViewHolder { + private final EditText name; + private final EditText size; + private final EditText type; + private final EditText subtype; + private final EditText alignment; + private final EditText customHitPoints; + + ViewHolder(View root) { + name = root.findViewById(R.id.name); + size = root.findViewById(R.id.size); + type = root.findViewById(R.id.type); + subtype = root.findViewById(R.id.subtype); + alignment = root.findViewById(R.id.alignment); + customHitPoints = root.findViewById(R.id.customHitPoints); + // TODO: add hitDice, hasCustomHitPoints, and customHitPoints + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java index e0d03a1..af63ae3 100644 --- a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java @@ -9,6 +9,10 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavBackStackEntry; +import androidx.navigation.NavController; +import androidx.navigation.NavDirections; +import androidx.navigation.Navigation; import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.data.MonsterRepository; @@ -39,33 +43,46 @@ public class EditMonsterFragment extends MCFragment { assert arguments != null; UUID monsterId = UUID.fromString(MonsterDetailFragmentArgs.fromBundle(arguments).getMonsterId()); + NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); + NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation); + mViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class); + View root = inflater.inflate(R.layout.fragment_edit_monster, container, false); mHolder = new ViewHolder(root); requireAppCompatActivity().getSupportActionBar().setTitle(getString(R.string.title_edit_monster, getString(R.string.default_monster_name))); // TODO: Show a loading spinner until we have the monster loaded. - repository.getMonster(monsterId).toObservable() - .firstOrError() - .subscribe(new DisposableSingleObserver() { - @Override - public void onSuccess(@io.reactivex.rxjava3.annotations.NonNull Monster monster) { - Logger.logDebug(String.format("Monster loaded: %s", monster.name)); - mViewModel.copyFromMonster(monster); - requireAppCompatActivity().getSupportActionBar().setTitle(getString(R.string.title_edit_monster, monster.name)); - dispose(); - } - - @Override - public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) { - // TODO: Show an error state. - Logger.logError(e); - dispose(); - } - }); + if (mViewModel.hasError() || !mViewModel.hasLoaded() || !mViewModel.getMonsterId().getValue().equals(monsterId)) { + repository.getMonster(monsterId).toObservable() + .firstOrError() + .subscribe(new DisposableSingleObserver() { + @Override + public void onSuccess(@io.reactivex.rxjava3.annotations.NonNull Monster monster) { + Logger.logDebug(String.format("Monster loaded: %s", monster.name)); + mViewModel.setHasLoaded(true); + mViewModel.setHasError(false); + mViewModel.copyFromMonster(monster); + requireAppCompatActivity().getSupportActionBar().setTitle(getString(R.string.title_edit_monster, monster.name)); + dispose(); + } + @Override + public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) { + // TODO: Show an error state. + Logger.logError(e); + mViewModel.setHasError(true); + mViewModel.setErrorMessage(e.toString()); + dispose(); + } + }); + } mHolder.basicInfoButton.setOnClickListener(v -> { // TODO: Navigate to the EditBasicInfo fragment Logger.logDebug("Basic Info clicked"); + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditBasicInfoFragment(); + View view = getView(); + assert view != null; + Navigation.findNavController(view).navigate(action); }); return root; @@ -74,8 +91,6 @@ public class EditMonsterFragment extends MCFragment { @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mViewModel = new ViewModelProvider(this).get(EditMonsterViewModel.class); - // TODO: Use the ViewModel } private static class ViewHolder { diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java index a5c6aaa..a953409 100644 --- a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java @@ -10,22 +10,40 @@ import com.majinnaibu.monstercards.models.Monster; import java.util.UUID; public class EditMonsterViewModel extends ViewModel { - private MutableLiveData mName; - private MutableLiveData mMonsterId; + private final MutableLiveData mName; + private final MutableLiveData mMonsterId; + private final MutableLiveData mErrorMessage; + private final MutableLiveData mHasError; + private final MutableLiveData mHasLoaded; + private final MutableLiveData mSize; + private final MutableLiveData mType; + private final MutableLiveData mSubtype; + private final MutableLiveData mAlignment; + private final MutableLiveData mCustomHitPoints; public EditMonsterViewModel() { - mName = new MutableLiveData<>(); - mName.setValue(""); - - mMonsterId = new MutableLiveData<>(); - mMonsterId.setValue(UUID.randomUUID()); + mName = new MutableLiveData<>(""); + mMonsterId = new MutableLiveData<>(UUID.randomUUID()); + mErrorMessage = new MutableLiveData<>(""); + mHasError = new MutableLiveData<>(false); + mHasLoaded = new MutableLiveData<>(false); + mSize = new MutableLiveData<>(""); + mType = new MutableLiveData<>(""); + mSubtype = new MutableLiveData<>(""); + mAlignment = new MutableLiveData<>(""); + mCustomHitPoints = new MutableLiveData<>(""); } public void copyFromMonster(Monster monster) { // TODO: copy from monster to other fields mMonsterId.setValue(monster.id); mName.setValue(monster.name); + mSize.setValue(monster.size); + mType.setValue(monster.type); + mSubtype.setValue(monster.subtype); + mAlignment.setValue(monster.alignment); + mCustomHitPoints.setValue(monster.customHPDescription); } public LiveData getName() { @@ -36,4 +54,80 @@ public class EditMonsterViewModel extends ViewModel { mName.setValue(name); } + + public LiveData getMonsterId() { + return mMonsterId; + } + + public LiveData getErrorMessage() { + return mErrorMessage; + } + + public void setErrorMessage(@NonNull String errorMessage) { + mErrorMessage.setValue(errorMessage); + } + + public LiveData getHasError() { + return mHasError; + } + + public void setHasError(@NonNull Boolean hasError) { + mHasError.setValue(hasError); + } + + public boolean hasError() { + return getHasError().getValue(); + } + + public LiveData getHasLoaded() { + return mHasLoaded; + } + + public void setHasLoaded(@NonNull Boolean hasLoaded) { + mHasLoaded.setValue(hasLoaded); + } + + public boolean hasLoaded() { + return getHasLoaded().getValue(); + } + + public LiveData getSize() { + return mSize; + } + + public void setSize(@NonNull String size) { + mSize.setValue(size); + } + + public LiveData getType() { + return mType; + } + + public void setType(@NonNull String type) { + mType.setValue(type); + } + + public LiveData getSubtype() { + return mSubtype; + } + + public void setSubtype(@NonNull String subType) { + mSubtype.setValue(subType); + } + + public LiveData getAlignment() { + return mAlignment; + } + + public void setAlignment(@NonNull String alignment) { + mAlignment.setValue(alignment); + } + + public LiveData getCustomHitPoints() { + return mCustomHitPoints; + } + + public void setCustomHitPoints(String customHitPoints) { + mCustomHitPoints.setValue(customHitPoints); + } } diff --git a/app/src/main/res/layout/fragment_edit_basic_info.xml b/app/src/main/res/layout/fragment_edit_basic_info.xml new file mode 100644 index 0000000..865ce01 --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_basic_info.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_edit_monster.xml b/app/src/main/res/layout/fragment_edit_monster.xml index 46b9aa9..9ff084e 100644 --- a/app/src/main/res/layout/fragment_edit_monster.xml +++ b/app/src/main/res/layout/fragment_edit_monster.xml @@ -6,7 +6,6 @@ android:layout_height="match_parent"> diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index e2d33bc..e8eee2d 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -55,16 +55,32 @@ app:argType="string" /> + app:destination="@id/edit_monster_navigation" /> - + - + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 97467f9..c6c3b92 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,5 +2,6 @@ 16dp 16dp + 20sp \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a28ae5..e05ebba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,4 +38,10 @@ Unnamed Monster Failed to create monster %1$s created + Name + Size + Type + Subtype + Alignment + Custom HP \ No newline at end of file