ViewModel refactoring.

This commit is contained in:
2021-06-20 18:22:13 -07:00
committed by Tom Hicks
parent f6a032844c
commit 5bded410d2
24 changed files with 869 additions and 320 deletions

View File

@@ -1,6 +1,7 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="FieldCanBeLocal" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TrivialIf" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="TrivialIf" enabled="false" level="WARNING" enabled_by_default="false" />
</profile> </profile>
</component> </component>

View File

@@ -8,20 +8,19 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders; import androidx.lifecycle.ViewModelProvider;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
public class CollectionsFragment extends Fragment { public class CollectionsFragment extends MCFragment {
private CollectionsViewModel collectionsViewModel; private CollectionsViewModel collectionsViewModel;
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
collectionsViewModel = collectionsViewModel = new ViewModelProvider(this).get(CollectionsViewModel.class);
ViewModelProviders.of(this).get(CollectionsViewModel.class);
View root = inflater.inflate(R.layout.fragment_collections, container, false); View root = inflater.inflate(R.layout.fragment_collections, container, false);
final TextView textView = root.findViewById(R.id.text_collections); final TextView textView = root.findViewById(R.id.text_collections);
collectionsViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() { collectionsViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {

View File

@@ -7,29 +7,21 @@ import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.lifecycle.ViewModelProvider;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
public class DashboardFragment extends Fragment { public class DashboardFragment extends MCFragment {
private DashboardViewModel dashboardViewModel; private DashboardViewModel dashboardViewModel;
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
dashboardViewModel = dashboardViewModel = new ViewModelProvider(this).get(DashboardViewModel.class);
ViewModelProviders.of(this).get(DashboardViewModel.class);
View root = inflater.inflate(R.layout.fragment_dashboard, container, false); View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
final TextView textView = root.findViewById(R.id.text_dashboard); final TextView textView = root.findViewById(R.id.text_dashboard);
dashboardViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() { dashboardViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
@Override
public void onChanged(@Nullable String s) {
textView.setText(s);
}
});
return root; return root;
} }
} }

View File

@@ -5,8 +5,7 @@ import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
public class DashboardViewModel extends ViewModel { public class DashboardViewModel extends ViewModel {
private final MutableLiveData<String> mText;
private MutableLiveData<String> mText;
public DashboardViewModel() { public DashboardViewModel() {
mText = new MutableLiveData<>(); mText = new MutableLiveData<>();

View File

@@ -13,6 +13,7 @@ import androidx.navigation.Navigation;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.ui.components.Stepper; import com.majinnaibu.monstercards.ui.components.Stepper;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
public class EditAbilityScoresFragment extends Fragment { public class EditAbilityScoresFragment extends Fragment {
private final String ABILITY_SCORE_FORMAT = "%d (%+d)"; private final String ABILITY_SCORE_FORMAT = "%d (%+d)";

View File

@@ -13,7 +13,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.SwitchCompat; import androidx.appcompat.widget.SwitchCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController; import androidx.navigation.NavController;
@@ -22,10 +21,10 @@ import androidx.navigation.Navigation;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.data.enums.ArmorType; import com.majinnaibu.monstercards.data.enums.ArmorType;
import com.majinnaibu.monstercards.helpers.ArrayHelper; import com.majinnaibu.monstercards.helpers.ArrayHelper;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.utils.TextChangedListener; import com.majinnaibu.monstercards.utils.TextChangedListener;
@SuppressWarnings("FieldCanBeLocal") public class EditArmorFragment extends MCFragment {
public class EditArmorFragment extends Fragment {
private EditMonsterViewModel mViewModel; private EditMonsterViewModel mViewModel;
private ViewHolder mHolder; private ViewHolder mHolder;

View File

@@ -13,10 +13,9 @@ import androidx.navigation.Navigation;
import com.google.android.material.switchmaterial.SwitchMaterial; import com.google.android.material.switchmaterial.SwitchMaterial;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.ui.MCFragment; import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.utils.TextChangedListener; import com.majinnaibu.monstercards.utils.TextChangedListener;
@SuppressWarnings("FieldCanBeLocal")
public class EditBasicInfoFragment extends MCFragment { public class EditBasicInfoFragment extends MCFragment {
private EditMonsterViewModel mViewModel; private EditMonsterViewModel mViewModel;
private ViewHolder mHolder; private ViewHolder mHolder;

View File

@@ -12,7 +12,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController; import androidx.navigation.NavController;
@@ -21,9 +20,10 @@ import androidx.navigation.Navigation;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.data.enums.ChallengeRating; import com.majinnaibu.monstercards.data.enums.ChallengeRating;
import com.majinnaibu.monstercards.helpers.ArrayHelper; import com.majinnaibu.monstercards.helpers.ArrayHelper;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.utils.TextChangedListener; import com.majinnaibu.monstercards.utils.TextChangedListener;
public class EditChallengeRatingFragment extends Fragment { public class EditChallengeRatingFragment extends MCFragment {
private EditMonsterViewModel mViewModel; private EditMonsterViewModel mViewModel;
private ViewHolder mHolder; private ViewHolder mHolder;

View File

@@ -0,0 +1,95 @@
package com.majinnaibu.monstercards.ui.editmonster;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback;
import com.majinnaibu.monstercards.utils.Logger;
import org.jetbrains.annotations.NotNull;
/**
* A fragment representing a list of Items.
*/
public class EditConditionImmunitiesFragment extends Fragment {
private EditMonsterViewModel mViewModel;
private ViewHolder mHolder;
private void navigateToEditConditionImmunity(String condition) {
NavDirections action = EditConditionImmunitiesFragmentDirections.actionEditConditionImmunitiesFragmentToEditConditionImmunity(condition);
View view = getView();
assert view != null;
Navigation.findNavController(view).navigate(action);
}
@Nullable
@Override
public View onCreateView(@NonNull @NotNull LayoutInflater inflater, @Nullable @org.jetbrains.annotations.Nullable ViewGroup container, @Nullable @org.jetbrains.annotations.Nullable 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);
View root = inflater.inflate(R.layout.fragment_edit_condition_immunities_list, container, false);
mHolder = new ViewHolder(root);
setupRecyclerView(mHolder.list);
setupAddConditionImmunityButton(mHolder.addConditionImmunity);
return root;
}
private void setupRecyclerView(@NonNull RecyclerView recyclerView) {
Context context = requireContext();
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);
mViewModel.getConditionImmunities().observe(getViewLifecycleOwner(), conditionImmunities -> {
EditConditionImmunitiesRecyclerViewAdapter adapter = new EditConditionImmunitiesRecyclerViewAdapter(mViewModel.getConditionImmunitiesArray(), condition -> {
if (condition != null) {
navigateToEditConditionImmunity(condition);
} else {
Logger.logError("Can't navigate to EditConditionImmunityFragment with a null condition");
}
});
recyclerView.setAdapter(adapter);
});
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, mViewModel::removeConditionImmunity));
itemTouchHelper.attachToRecyclerView(recyclerView);
}
private void setupAddConditionImmunityButton(@NonNull FloatingActionButton fab) {
fab.setOnClickListener(view -> {
String condition = mViewModel.addNewConditionImmunity();
navigateToEditConditionImmunity(condition);
});
}
private static class ViewHolder {
RecyclerView list;
FloatingActionButton addConditionImmunity;
ViewHolder(View root) {
list = root.findViewById(R.id.list);
addConditionImmunity = root.findViewById(R.id.add_condition_immunity);
}
}
}

View File

@@ -20,8 +20,8 @@ import com.google.android.material.snackbar.Snackbar;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.data.MonsterRepository; import com.majinnaibu.monstercards.data.MonsterRepository;
import com.majinnaibu.monstercards.models.Monster; import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.ui.MCFragment;
import com.majinnaibu.monstercards.ui.monster.MonsterDetailFragmentArgs; import com.majinnaibu.monstercards.ui.monster.MonsterDetailFragmentArgs;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.Logger;
import java.util.Objects; import java.util.Objects;
@@ -32,7 +32,6 @@ import io.reactivex.rxjava3.observers.DisposableCompletableObserver;
import io.reactivex.rxjava3.observers.DisposableSingleObserver; import io.reactivex.rxjava3.observers.DisposableSingleObserver;
import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.schedulers.Schedulers;
@SuppressWarnings("FieldCanBeLocal")
public class EditMonsterFragment extends MCFragment { public class EditMonsterFragment extends MCFragment {
private EditMonsterViewModel mViewModel; private EditMonsterViewModel mViewModel;

View File

@@ -3,7 +3,6 @@ package com.majinnaibu.monstercards.ui.editmonster;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.majinnaibu.monstercards.data.enums.AbilityScore; import com.majinnaibu.monstercards.data.enums.AbilityScore;
import com.majinnaibu.monstercards.data.enums.AdvantageType; import com.majinnaibu.monstercards.data.enums.AdvantageType;
@@ -15,22 +14,22 @@ import com.majinnaibu.monstercards.models.Language;
import com.majinnaibu.monstercards.models.Monster; import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.models.Skill; import com.majinnaibu.monstercards.models.Skill;
import com.majinnaibu.monstercards.models.Trait; import com.majinnaibu.monstercards.models.Trait;
import com.majinnaibu.monstercards.ui.shared.ChangeTrackedViewModel;
import com.majinnaibu.monstercards.utils.ChangeTrackedLiveData; import com.majinnaibu.monstercards.utils.ChangeTrackedLiveData;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
@SuppressWarnings({"ConstantConditions", "unused"}) @SuppressWarnings({"ConstantConditions", "unused"})
public class EditMonsterViewModel extends ViewModel { public class EditMonsterViewModel extends ChangeTrackedViewModel {
private final ChangeTrackedLiveData<UUID> mMonsterId; private final ChangeTrackedLiveData<UUID> mMonsterId;
private final MutableLiveData<Boolean> mHasError; private final MutableLiveData<Boolean> mHasError;
private final MutableLiveData<Boolean> mHasLoaded; private final MutableLiveData<Boolean> mHasLoaded;
private final MutableLiveData<Boolean> mHasChanges;
private final ChangeTrackedLiveData<Boolean> mHasCustomHitPoints; private final ChangeTrackedLiveData<Boolean> mHasCustomHitPoints;
private final ChangeTrackedLiveData<Boolean> mHasShield; private final ChangeTrackedLiveData<Boolean> mHasShield;
private final ChangeTrackedLiveData<Boolean> mCanHover; private final ChangeTrackedLiveData<Boolean> mCanHover;
@@ -78,83 +77,82 @@ public class EditMonsterViewModel extends ViewModel {
private final ChangeTrackedLiveData<String> mUnderstandsButDescription; private final ChangeTrackedLiveData<String> mUnderstandsButDescription;
private final ChangeTrackedLiveData<List<Skill>> mSkills; private final ChangeTrackedLiveData<List<Skill>> mSkills;
private final ChangeTrackedLiveData<List<String>> mSenses; private final ChangeTrackedLiveData<List<String>> mSenses;
private final ChangeTrackedLiveData<Set<String>> mDamageImmunities; private final ChangeTrackedLiveData<List<String>> mDamageImmunities;
private final ChangeTrackedLiveData<Set<String>> mDamageResistances; private final ChangeTrackedLiveData<List<String>> mDamageResistances;
private final ChangeTrackedLiveData<Set<String>> mDamageVulnerabilities; private final ChangeTrackedLiveData<List<String>> mDamageVulnerabilities;
private final ChangeTrackedLiveData<Set<String>> mConditionImmunities; private final ChangeTrackedLiveData<List<String>> mConditionImmunities;
private final ChangeTrackedLiveData<Set<Language>> mLanguages; private final ChangeTrackedLiveData<List<Language>> mLanguages;
private final ChangeTrackedLiveData<Set<Trait>> mAbilities; private final ChangeTrackedLiveData<List<Trait>> mAbilities;
private final ChangeTrackedLiveData<Set<Trait>> mActions; private final ChangeTrackedLiveData<List<Trait>> mActions;
private final ChangeTrackedLiveData<Set<Trait>> mReactions; private final ChangeTrackedLiveData<List<Trait>> mReactions;
private final ChangeTrackedLiveData<Set<Trait>> mLairActions; private final ChangeTrackedLiveData<List<Trait>> mLairActions;
private final ChangeTrackedLiveData<Set<Trait>> mLegendaryActions; private final ChangeTrackedLiveData<List<Trait>> mLegendaryActions;
private final ChangeTrackedLiveData<Set<Trait>> mRegionalActions; private final ChangeTrackedLiveData<List<Trait>> mRegionalActions;
public EditMonsterViewModel() { public EditMonsterViewModel() {
super();
mErrorMessage = new MutableLiveData<>(""); mErrorMessage = new MutableLiveData<>("");
mHasError = new MutableLiveData<>(false); mHasError = new MutableLiveData<>(false);
mHasLoaded = new MutableLiveData<>(false); mHasLoaded = new MutableLiveData<>(false);
mHasChanges = new MutableLiveData<>(false);
ChangeTrackedLiveData.OnValueDirtiedCallback onDirtied = () -> mHasChanges.setValue(true);
mName = new ChangeTrackedLiveData<>("", onDirtied); mName = new ChangeTrackedLiveData<>("", this::makeDirty);
mMonsterId = new ChangeTrackedLiveData<>(UUID.randomUUID(), onDirtied); mMonsterId = new ChangeTrackedLiveData<>(UUID.randomUUID(), this::makeDirty);
mSize = new ChangeTrackedLiveData<>("", onDirtied); mSize = new ChangeTrackedLiveData<>("", this::makeDirty);
mType = new ChangeTrackedLiveData<>("", onDirtied); mType = new ChangeTrackedLiveData<>("", this::makeDirty);
mSubtype = new ChangeTrackedLiveData<>("", onDirtied); mSubtype = new ChangeTrackedLiveData<>("", this::makeDirty);
mAlignment = new ChangeTrackedLiveData<>("", onDirtied); mAlignment = new ChangeTrackedLiveData<>("", this::makeDirty);
mCustomHitPoints = new ChangeTrackedLiveData<>("", onDirtied); mCustomHitPoints = new ChangeTrackedLiveData<>("", this::makeDirty);
mHitDice = new ChangeTrackedLiveData<>(0, onDirtied); mHitDice = new ChangeTrackedLiveData<>(0, this::makeDirty);
mNaturalArmorBonus = new ChangeTrackedLiveData<>(0, onDirtied); mNaturalArmorBonus = new ChangeTrackedLiveData<>(0, this::makeDirty);
mHasCustomHitPoints = new ChangeTrackedLiveData<>(false, onDirtied); mHasCustomHitPoints = new ChangeTrackedLiveData<>(false, this::makeDirty);
mArmorType = new ChangeTrackedLiveData<>(ArmorType.NONE, onDirtied); mArmorType = new ChangeTrackedLiveData<>(ArmorType.NONE, this::makeDirty);
mHasShield = new ChangeTrackedLiveData<>(false, onDirtied); mHasShield = new ChangeTrackedLiveData<>(false, this::makeDirty);
mShieldBonus = new ChangeTrackedLiveData<>(0, onDirtied); mShieldBonus = new ChangeTrackedLiveData<>(0, this::makeDirty);
mCustomArmor = new ChangeTrackedLiveData<>("", onDirtied); mCustomArmor = new ChangeTrackedLiveData<>("", this::makeDirty);
mWalkSpeed = new ChangeTrackedLiveData<>(0, onDirtied); mWalkSpeed = new ChangeTrackedLiveData<>(0, this::makeDirty);
mBurrowSpeed = new ChangeTrackedLiveData<>(0, onDirtied); mBurrowSpeed = new ChangeTrackedLiveData<>(0, this::makeDirty);
mClimbSpeed = new ChangeTrackedLiveData<>(0, onDirtied); mClimbSpeed = new ChangeTrackedLiveData<>(0, this::makeDirty);
mFlySpeed = new ChangeTrackedLiveData<>(0, onDirtied); mFlySpeed = new ChangeTrackedLiveData<>(0, this::makeDirty);
mSwimSpeed = new ChangeTrackedLiveData<>(0, onDirtied); mSwimSpeed = new ChangeTrackedLiveData<>(0, this::makeDirty);
mCanHover = new ChangeTrackedLiveData<>(false, onDirtied); mCanHover = new ChangeTrackedLiveData<>(false, this::makeDirty);
mHasCustomSpeed = new ChangeTrackedLiveData<>(false, onDirtied); mHasCustomSpeed = new ChangeTrackedLiveData<>(false, this::makeDirty);
mCustomSpeed = new ChangeTrackedLiveData<>("", onDirtied); mCustomSpeed = new ChangeTrackedLiveData<>("", this::makeDirty);
mStrength = new ChangeTrackedLiveData<>(10, onDirtied); mStrength = new ChangeTrackedLiveData<>(10, this::makeDirty);
mDexterity = new ChangeTrackedLiveData<>(10, onDirtied); mDexterity = new ChangeTrackedLiveData<>(10, this::makeDirty);
mConstitution = new ChangeTrackedLiveData<>(10, onDirtied); mConstitution = new ChangeTrackedLiveData<>(10, this::makeDirty);
mIntelligence = new ChangeTrackedLiveData<>(10, onDirtied); mIntelligence = new ChangeTrackedLiveData<>(10, this::makeDirty);
mWisdom = new ChangeTrackedLiveData<>(10, onDirtied); mWisdom = new ChangeTrackedLiveData<>(10, this::makeDirty);
mCharisma = new ChangeTrackedLiveData<>(10, onDirtied); mCharisma = new ChangeTrackedLiveData<>(10, this::makeDirty);
mStrengthProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, onDirtied); mStrengthProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, this::makeDirty);
mStrengthAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, onDirtied); mStrengthAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, this::makeDirty);
mDexterityProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, onDirtied); mDexterityProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, this::makeDirty);
mDexterityAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, onDirtied); mDexterityAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, this::makeDirty);
mConstitutionProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, onDirtied); mConstitutionProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, this::makeDirty);
mConstitutionAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, onDirtied); mConstitutionAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, this::makeDirty);
mIntelligenceProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, onDirtied); mIntelligenceProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, this::makeDirty);
mIntelligenceAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, onDirtied); mIntelligenceAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, this::makeDirty);
mWisdomProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, onDirtied); mWisdomProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, this::makeDirty);
mWisdomAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, onDirtied); mWisdomAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, this::makeDirty);
mCharismaProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, onDirtied); mCharismaProficiency = new ChangeTrackedLiveData<>(ProficiencyType.NONE, this::makeDirty);
mCharismaAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, onDirtied); mCharismaAdvantage = new ChangeTrackedLiveData<>(AdvantageType.NONE, this::makeDirty);
mChallengeRating = new ChangeTrackedLiveData<>(ChallengeRating.ONE_EIGHTH, onDirtied); mChallengeRating = new ChangeTrackedLiveData<>(ChallengeRating.ONE_EIGHTH, this::makeDirty);
mCustomChallengeRatingDescription = new ChangeTrackedLiveData<>("", onDirtied); mCustomChallengeRatingDescription = new ChangeTrackedLiveData<>("", this::makeDirty);
mCustomProficiencyBonus = new ChangeTrackedLiveData<>(0, onDirtied); mCustomProficiencyBonus = new ChangeTrackedLiveData<>(0, this::makeDirty);
mTelepathyRange = new ChangeTrackedLiveData<>(0, onDirtied); mTelepathyRange = new ChangeTrackedLiveData<>(0, this::makeDirty);
mUnderstandsButDescription = new ChangeTrackedLiveData<>("", onDirtied); mUnderstandsButDescription = new ChangeTrackedLiveData<>("", this::makeDirty);
mSkills = new ChangeTrackedLiveData<>(new ArrayList<>(), onDirtied); mSkills = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mSenses = new ChangeTrackedLiveData<>(new ArrayList<>(), onDirtied); mSenses = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mDamageImmunities = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mDamageImmunities = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mDamageResistances = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mDamageResistances = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mDamageVulnerabilities = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mDamageVulnerabilities = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mConditionImmunities = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mConditionImmunities = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mLanguages = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mLanguages = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mAbilities = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mAbilities = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mActions = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mActions = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mReactions = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mReactions = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mLairActions = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mLairActions = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mLegendaryActions = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mLegendaryActions = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
mRegionalActions = new ChangeTrackedLiveData<>(new HashSet<>(), onDirtied); mRegionalActions = new ChangeTrackedLiveData<>(new ArrayList<>(), this::makeDirty);
} }
public void copyFromMonster(Monster monster) { public void copyFromMonster(Monster monster) {
@@ -210,18 +208,28 @@ public class EditMonsterViewModel extends ViewModel {
ArrayList<String> senses = new ArrayList<>(monster.senses); ArrayList<String> senses = new ArrayList<>(monster.senses);
Collections.sort(senses, String::compareToIgnoreCase); Collections.sort(senses, String::compareToIgnoreCase);
mSenses.resetValue(senses); mSenses.resetValue(senses);
mDamageImmunities.resetValue(monster.damageImmunities); ArrayList<String> damageImmunities = new ArrayList<>(monster.damageImmunities);
mDamageResistances.resetValue(monster.damageResistances); Collections.sort(damageImmunities, String::compareToIgnoreCase);
mDamageVulnerabilities.resetValue(monster.damageVulnerabilities); mDamageImmunities.resetValue(damageImmunities);
mConditionImmunities.resetValue(monster.conditionImmunities); ArrayList<String> damageResistances = new ArrayList<>(monster.damageResistances);
mLanguages.resetValue(monster.languages); Collections.sort(damageResistances, String::compareToIgnoreCase);
mAbilities.resetValue(monster.abilities); mDamageResistances.resetValue(damageResistances);
mActions.resetValue(monster.actions); ArrayList<String> damageVulnerabilities = new ArrayList<>(monster.damageVulnerabilities);
mReactions.resetValue(monster.reactions); Collections.sort(damageVulnerabilities, String::compareToIgnoreCase);
mLairActions.resetValue(monster.lairActions); mDamageVulnerabilities.resetValue(damageVulnerabilities);
mLegendaryActions.resetValue(monster.legendaryActions); ArrayList<String> conditionImmunities = new ArrayList<>(monster.conditionImmunities);
mRegionalActions.resetValue(monster.regionalActions); Collections.sort(conditionImmunities, String::compareToIgnoreCase);
mHasChanges.setValue(false); mConditionImmunities.resetValue(conditionImmunities);
ArrayList<Language> languages = new ArrayList<>(monster.languages);
Collections.sort(languages, (lang1, lang2) -> lang1.getName().compareToIgnoreCase(lang2.getName()));
mLanguages.resetValue(languages);
mAbilities.resetValue(new ArrayList<>(monster.abilities));
mActions.resetValue(new ArrayList<>(monster.actions));
mReactions.resetValue(new ArrayList<>(monster.reactions));
mLairActions.resetValue(new ArrayList<>(monster.lairActions));
mLegendaryActions.resetValue(new ArrayList<>(monster.legendaryActions));
mRegionalActions.resetValue(new ArrayList<>(monster.regionalActions));
makeClean();
} }
public LiveData<String> getName() { public LiveData<String> getName() {
@@ -308,14 +316,6 @@ public class EditMonsterViewModel extends ViewModel {
mCustomHitPoints.setValue(customHitPoints); mCustomHitPoints.setValue(customHitPoints);
} }
public LiveData<Boolean> getHasChanges() {
return mHasChanges;
}
public boolean hasChanges() {
return mHasChanges.getValue();
}
public LiveData<Integer> getHitDice() { public LiveData<Integer> getHitDice() {
return mHitDice; return mHitDice;
} }
@@ -756,13 +756,295 @@ public class EditMonsterViewModel extends ViewModel {
return mSkills; return mSkills;
} }
public List<Skill> getSkillsArray() {
return mSkills.getValue();
}
public Skill addNewSkill() {
Skill newSkill = new Skill("Unnamed Skill", AbilityScore.DEXTERITY);
ArrayList<Skill> newSkills = new ArrayList<>(mSkills.getValue());
newSkills.add(newSkill);
Collections.sort(newSkills, (skill1, skill2) -> skill1.name.compareToIgnoreCase(skill2.name));
mSkills.setValue(newSkills);
return newSkill;
}
public void removeSkill(int position) {
List<Skill> skills = mSkills.getValue();
ArrayList<Skill> newSkills = new ArrayList<>(skills);
newSkills.remove(position);
mSkills.setValue(newSkills);
}
public void replaceSkill(Skill newSkill, Skill oldSkill) {
List<Skill> oldSkills = mSkills.getValue();
if (oldSkills == null) {
oldSkills = new ArrayList<>();
}
boolean hasReplaced = false;
ArrayList<Skill> newSkills = new ArrayList<>(oldSkills.size());
for (Skill skill : oldSkills) {
if (Objects.equals(skill, oldSkill)) {
newSkills.add(newSkill);
hasReplaced = true;
} else {
newSkills.add(skill);
}
}
if (!hasReplaced) {
newSkills.add(newSkill);
}
Collections.sort(newSkills, (skill1, skill2) -> skill1.name.compareToIgnoreCase(skill2.name));
mSkills.setValue(newSkills);
}
public LiveData<List<String>> getSenses() {
return mSenses;
}
public List<String> getSensesArray() { public List<String> getSensesArray() {
return mSenses.getValue(); return mSenses.getValue();
} }
// TODO: add getters and setters for lists of strings (Senses, Damage Immunities, Damage Resistances, Damage Vulnerabilities, and Condition Immunities) public String addNewSense() {
// TODO: add getters and setters for Languages return Helpers.addItemToList(mSenses, "", String::compareToIgnoreCase);
// TODO: add getters and setters for traits (Abilities, Actions, Reactions, Lair Actions, Legendary Actions, and Regional Actions) }
public void removeSense(int position) {
Helpers.removeFromList(mSenses, position);
}
public void replaceSense(String oldSense, String newSense) {
Helpers.replaceItemInList(mSenses, oldSense, newSense, String::compareToIgnoreCase);
}
public LiveData<List<String>> getDamageImmunities() {
return mDamageImmunities;
}
public List<String> getDamageImmunitiesArray() {
return mDamageImmunities.getValue();
}
public String addNewDamageImmunity() {
return Helpers.addStringToList("", mDamageImmunities);
}
public void removeDamageImmunity(int position) {
Helpers.removeFromList(mDamageImmunities, position);
}
public void replaceDamageImmunity(String oldDamageType, String newDamageType) {
Helpers.replaceItemInList(mDamageImmunities, oldDamageType, newDamageType, String::compareToIgnoreCase);
}
public LiveData<List<String>> getDamageResistances() {
return mDamageResistances;
}
public List<String> getDamageResistancesArray() {
return mDamageResistances.getValue();
}
public String addNewDamageResistance() {
return Helpers.addStringToList("", mDamageResistances);
}
public void removeDamageResistance(int position) {
Helpers.removeFromList(mDamageResistances, position);
}
public void replaceDamageResistance(String oldDamageType, String newDamageType) {
Helpers.replaceItemInList(mDamageResistances, oldDamageType, newDamageType, String::compareToIgnoreCase);
}
public LiveData<List<String>> getDamageVulnerabilities() {
return mDamageVulnerabilities;
}
public List<String> getDamageVulnerabilitiesArray() {
return mDamageVulnerabilities.getValue();
}
public String addNewDamageVulnerability() {
return Helpers.addStringToList("", mDamageVulnerabilities);
}
public void removeDamageVulnerability(int position) {
Helpers.removeFromList(mDamageVulnerabilities, position);
}
public void replaceDamageVulnerability(String oldDamageType, String newDamageType) {
Helpers.replaceItemInList(mDamageVulnerabilities, oldDamageType, newDamageType, String::compareToIgnoreCase);
}
public LiveData<List<String>> getConditionImmunities() {
return mConditionImmunities;
}
public List<String> getConditionImmunitiesArray() {
return mConditionImmunities.getValue();
}
public String addNewConditionImmunity() {
return Helpers.addStringToList("", mConditionImmunities);
}
public void removeConditionImmunity(int position) {
Helpers.removeFromList(mConditionImmunities, position);
}
public void replaceConditionImmunity(String oldDamageType, String newDamageType) {
Helpers.replaceItemInList(mConditionImmunities, oldDamageType, newDamageType, String::compareToIgnoreCase);
}
public LiveData<List<Language>> getLanguages() {
return mLanguages;
}
public List<Language> getLanguagesArray() {
return mLanguages.getValue();
}
public Language addNewLanguage() {
Language newLanguage = new Language("", true);
return Helpers.addItemToList(mLanguages, newLanguage, Language::compareTo);
}
public void removeLanguage(int position) {
Helpers.removeFromList(mLanguages, position);
}
public void replaceLanguage(Language oldLanguage, Language newLanguage) {
Helpers.replaceItemInList(mLanguages, oldLanguage, newLanguage, Language::compareTo);
}
public LiveData<List<Trait>> getAbilities() {
return mAbilities;
}
public List<Trait> getAbilitiesArray() {
return mAbilities.getValue();
}
public Trait addNewAbility() {
Trait newAbility = new Trait("", "");
return Helpers.addItemToList(mAbilities, newAbility, Trait::compareTo);
}
public void removeAbility(int position) {
Helpers.removeFromList(mAbilities, position);
}
public void replaceAbility(Trait oldAbility, Trait newAbility) {
Helpers.replaceItemInList(mAbilities, oldAbility, newAbility);
}
public LiveData<List<Trait>> getActions() {
return mActions;
}
public List<Trait> getActionsArray() {
return mActions.getValue();
}
public Trait addNewAction() {
Trait newAction = new Trait("", "");
return Helpers.addItemToList(mActions, newAction, Trait::compareTo);
}
public void removeAction(int position) {
Helpers.removeFromList(mActions, position);
}
public void replaceAction(Trait oldAction, Trait newAction) {
Helpers.replaceItemInList(mActions, oldAction, newAction);
}
public LiveData<List<Trait>> getReactions() {
return mReactions;
}
public List<Trait> getReactionsArray() {
return mReactions.getValue();
}
public Trait addNewReaction() {
Trait newReaction = new Trait("", "");
return Helpers.addItemToList(mReactions, newReaction, Trait::compareTo);
}
public void removeReaction(int position) {
Helpers.removeFromList(mReactions, position);
}
public void replaceReaction(Trait oldReaction, Trait newReaction) {
Helpers.replaceItemInList(mReactions, oldReaction, newReaction);
}
public LiveData<List<Trait>> getLairActions() {
return mLairActions;
}
public List<Trait> getLairActionsArray() {
return mLairActions.getValue();
}
public Trait addNewLairAction() {
Trait newLairAction = new Trait("", "");
return Helpers.addItemToList(mLairActions, newLairAction, Trait::compareTo);
}
public void removeLairAction(int position) {
Helpers.removeFromList(mLairActions, position);
}
public void replaceLairAction(Trait oldLairAction, Trait newLairAction) {
Helpers.replaceItemInList(mLairActions, oldLairAction, newLairAction);
}
public LiveData<List<Trait>> getLegendaryActions() {
return mLegendaryActions;
}
public List<Trait> getLegendaryActionsArray() {
return mLegendaryActions.getValue();
}
public Trait addNewLegendaryAction() {
Trait newLegendaryAction = new Trait("", "");
return Helpers.addItemToList(mLegendaryActions, newLegendaryAction, Trait::compareTo);
}
public void removeLegendaryAction(int position) {
Helpers.removeFromList(mLegendaryActions, position);
}
public void replaceLegendaryAction(Trait oldLegendaryAction, Trait newLegendaryAction) {
Helpers.replaceItemInList(mLegendaryActions, oldLegendaryAction, newLegendaryAction);
}
public LiveData<List<Trait>> getRegionalActions() {
return mRegionalActions;
}
public List<Trait> getRegionalActionsArray() {
return mRegionalActions.getValue();
}
public Trait addNewRegionalAction() {
Trait newRegionalAction = new Trait("", "");
return Helpers.addItemToList(mRegionalActions, newRegionalAction, Trait::compareTo);
}
public void removeRegionalAction(int position) {
Helpers.removeFromList(mRegionalActions, position);
}
public void replaceRegionalAction(Trait oldRegionalAction, Trait newRegionalAction) {
Helpers.replaceItemInList(mRegionalActions, oldRegionalAction, newRegionalAction);
}
public Monster buildMonster() { public Monster buildMonster() {
Monster monster = new Monster(); Monster monster = new Monster();
@@ -813,60 +1095,99 @@ public class EditMonsterViewModel extends ViewModel {
monster.understandsButDescription = mUnderstandsButDescription.getValue(); monster.understandsButDescription = mUnderstandsButDescription.getValue();
monster.skills = new HashSet<>(mSkills.getValue()); monster.skills = new HashSet<>(mSkills.getValue());
monster.senses = new HashSet<>(mSenses.getValue()); monster.senses = new HashSet<>(mSenses.getValue());
monster.damageImmunities = mDamageImmunities.getValue(); monster.damageImmunities = new HashSet<>(mDamageImmunities.getValue());
monster.damageResistances = mDamageResistances.getValue(); monster.damageResistances = new HashSet<>(mDamageResistances.getValue());
monster.damageVulnerabilities = mDamageVulnerabilities.getValue(); monster.damageVulnerabilities = new HashSet<>(mDamageVulnerabilities.getValue());
monster.conditionImmunities = mConditionImmunities.getValue(); monster.conditionImmunities = new HashSet<>(mConditionImmunities.getValue());
monster.languages = mLanguages.getValue(); monster.languages = new HashSet<>(mLanguages.getValue());
monster.abilities = mAbilities.getValue(); monster.abilities = new HashSet<>(mAbilities.getValue());
monster.actions = mActions.getValue(); monster.actions = new HashSet<>(mActions.getValue());
monster.reactions = mReactions.getValue(); monster.reactions = new HashSet<>(mReactions.getValue());
monster.lairActions = mLairActions.getValue(); monster.lairActions = new HashSet<>(mLairActions.getValue());
monster.legendaryActions = mLegendaryActions.getValue(); monster.legendaryActions = new HashSet<>(mLegendaryActions.getValue());
monster.regionalActions = mRegionalActions.getValue(); monster.regionalActions = new HashSet<>(mRegionalActions.getValue());
return monster; return monster;
} }
public List<Skill> getSkillsArray() { @SuppressWarnings("SameParameterValue")
return mSkills.getValue(); private static class Helpers {
} static String addStringToList(String newString, MutableLiveData<List<String>> strings) {
return addItemToList(strings, newString, String::compareToIgnoreCase);
public Skill addNewSkill() {
Skill newSkill = new Skill("Unnamed Skill", AbilityScore.DEXTERITY);
ArrayList<Skill> newSkills = new ArrayList<>(mSkills.getValue());
newSkills.add(newSkill);
Collections.sort(newSkills, (skill1, skill2) -> skill1.name.compareToIgnoreCase(skill2.name));
mSkills.setValue(newSkills);
return newSkill;
}
public void removeSkill(int position) {
List<Skill> skills = mSkills.getValue();
ArrayList<Skill> newSkills = new ArrayList<>(skills);
newSkills.remove(position);
mSkills.setValue(newSkills);
}
public void replaceSkill(Skill newSkill, Skill oldSkill) {
List<Skill> oldSkills = mSkills.getValue();
if (oldSkills == null) {
oldSkills = new ArrayList<>();
} }
boolean hasReplaced = false;
ArrayList<Skill> newSkills = new ArrayList<>(oldSkills.size()); static <T> T addItemToList(MutableLiveData<List<T>> listData, T newItem, Comparator<? super T> comparator) {
for (Skill skill : oldSkills) { ArrayList<T> newList = new ArrayList<>(listData.getValue());
if (Objects.equals(skill, oldSkill)) { newList.add(newItem);
newSkills.add(newSkill); if (comparator != null) {
hasReplaced = true; Collections.sort(newList, comparator);
} else {
newSkills.add(skill);
} }
listData.setValue(newList);
return newItem;
} }
if (!hasReplaced) {
newSkills.add(newSkill); static <T> void removeFromList(MutableLiveData<List<T>> listData, int position) {
List<T> oldList = listData.getValue();
ArrayList<T> newList = new ArrayList<>(oldList);
newList.remove(position);
listData.setValue(newList);
}
static <T> void replaceItemInList(MutableLiveData<List<T>> listData, int position, T newItem, Comparator<? super T> comparator) {
List<T> oldList = listData.getValue();
if (oldList == null) {
oldList = new ArrayList<>();
}
int size = oldList.size();
boolean hasReplaced = false;
ArrayList<T> newList = new ArrayList<>(size);
for (int index = 0; index < size; index++) {
if (index == position) {
newList.add(newItem);
hasReplaced = true;
} else {
newList.add(oldList.get(index));
}
}
if (!hasReplaced) {
newList.add(newItem);
}
if (comparator != null) {
Collections.sort(newList, comparator);
}
listData.setValue(newList);
}
static <T> void replaceItemInList(MutableLiveData<List<T>> listData, int position, T newItem) {
replaceItemInList(listData, position, newItem, null);
}
static <T> void replaceItemInList(MutableLiveData<List<T>> listData, T oldItem, T newItem, Comparator<? super T> comparator) {
List<T> oldList = listData.getValue();
if (oldList == null) {
oldList = new ArrayList<>();
}
boolean hasReplaced = false;
ArrayList<T> newList = new ArrayList<>(oldList.size());
for (T item : oldList) {
if (Objects.equals(item, oldItem)) {
newList.add(newItem);
hasReplaced = true;
} else {
newList.add(item);
}
}
if (!hasReplaced) {
newList.add(newItem);
}
if (comparator != null) {
Collections.sort(newList, comparator);
}
listData.setValue(newList);
}
static <T> void replaceItemInList(MutableLiveData<List<T>> listData, T oldItem, T newItem) {
replaceItemInList(listData, oldItem, newItem, null);
} }
Collections.sort(newSkills, (skill1, skill2) -> skill1.name.compareToIgnoreCase(skill2.name));
mSkills.setValue(newSkills);
} }
} }

View File

@@ -14,6 +14,7 @@ import androidx.navigation.Navigation;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.ui.components.AdvantagePicker; import com.majinnaibu.monstercards.ui.components.AdvantagePicker;
import com.majinnaibu.monstercards.ui.components.ProficiencyPicker; import com.majinnaibu.monstercards.ui.components.ProficiencyPicker;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
public class EditSavingThrowsFragment extends Fragment { public class EditSavingThrowsFragment extends Fragment {
private EditMonsterViewModel mViewModel; private EditMonsterViewModel mViewModel;

View File

@@ -0,0 +1,77 @@
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.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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.shared.MCFragment;
import com.majinnaibu.monstercards.utils.Logger;
import com.majinnaibu.monstercards.utils.TextChangedListener;
public class EditSenseFragment extends MCFragment {
private EditMonsterViewModel mEditMonsterViewModel;
private EditStringViewModel mViewModel;
private ViewHolder mHolder;
private String mOldSense;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
mViewModel = new ViewModelProvider(this).get(EditStringViewModel.class);
if (getArguments() != null) {
EditSenseFragmentArgs args = EditSenseFragmentArgs.fromBundle(getArguments());
mOldSense = args.getSense();
mViewModel.resetValue(mOldSense);
} else {
Logger.logWTF("EditSenseFragment needs arguments");
mOldSense = null;
}
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment);
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation);
mEditMonsterViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class);
View root = inflater.inflate(R.layout.fragment_edit_sense, container, false);
mHolder = new ViewHolder(root);
mHolder.description.setText(mViewModel.getValueAsString());
mHolder.description.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setValue(s.toString())));
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (mViewModel.hasChanges()) {
mEditMonsterViewModel.replaceSense(mOldSense, mViewModel.getValueAsString());
}
Navigation.findNavController(requireView()).navigateUp();
}
});
return root;
}
private static class ViewHolder {
EditText description;
ViewHolder(View root) {
description = root.findViewById(R.id.name);
}
}
}

View File

@@ -0,0 +1,92 @@
package com.majinnaibu.monstercards.ui.editmonster;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback;
import com.majinnaibu.monstercards.utils.Logger;
/**
* A fragment representing a list of Items.
*/
public class EditSensesFragment extends MCFragment {
private EditMonsterViewModel mViewModel;
private ViewHolder mHolder;
private void navigateToEditSense(String sense) {
NavDirections action = EditSensesFragmentDirections.actionEditSensesFragmentToEditSenseFragment(sense);
View view = getView();
assert view != null;
Navigation.findNavController(view).navigate(action);
}
@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);
View root = inflater.inflate(R.layout.fragment_edit_senses_list, container, false);
mHolder = new ViewHolder(root);
setupRecyclerView(mHolder.list);
setupAddSenseButton(mHolder.addSense);
return root;
}
private void setupRecyclerView(@NonNull RecyclerView recyclerView) {
Context context = requireContext();
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);
mViewModel.getSenses().observe(getViewLifecycleOwner(), senses -> {
EditSensesRecyclerViewAdapter adapter = new EditSensesRecyclerViewAdapter(mViewModel.getSensesArray(), sense -> {
if (sense != null) {
navigateToEditSense(sense);
} else {
Logger.logError("Can't navigate to EditSense with a null sense");
}
});
recyclerView.setAdapter(adapter);
});
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, mViewModel::removeSense));
itemTouchHelper.attachToRecyclerView(recyclerView);
}
private void setupAddSenseButton(@NonNull FloatingActionButton fab) {
fab.setOnClickListener(view -> {
String newSense = mViewModel.addNewSense();
navigateToEditSense(newSense);
});
}
private static class ViewHolder {
RecyclerView list;
FloatingActionButton addSense;
ViewHolder(View root) {
list = root.findViewById(R.id.list);
addSense = root.findViewById(R.id.add_sense);
}
}
}

View File

@@ -20,6 +20,7 @@ import com.majinnaibu.monstercards.models.Skill;
import com.majinnaibu.monstercards.ui.components.AbilityScorePicker; import com.majinnaibu.monstercards.ui.components.AbilityScorePicker;
import com.majinnaibu.monstercards.ui.components.AdvantagePicker; import com.majinnaibu.monstercards.ui.components.AdvantagePicker;
import com.majinnaibu.monstercards.ui.components.ProficiencyPicker; import com.majinnaibu.monstercards.ui.components.ProficiencyPicker;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.Logger;
import com.majinnaibu.monstercards.utils.TextChangedListener; import com.majinnaibu.monstercards.utils.TextChangedListener;
@@ -37,7 +38,7 @@ public class EditSkillFragment extends Fragment {
mViewModel.copyFromSkill(args.getName(), args.getAbilityScore(), args.getProficiency(), args.getAdvantage()); mViewModel.copyFromSkill(args.getName(), args.getAbilityScore(), args.getProficiency(), args.getAdvantage());
mOldSkill = new Skill(args.getName(), args.getAbilityScore(), args.getAdvantage(), args.getProficiency()); mOldSkill = new Skill(args.getName(), args.getAbilityScore(), args.getAdvantage(), args.getProficiency());
} else { } else {
Logger.logWTF("This should never happen. EditSkillFragment needs arguments."); Logger.logWTF("EditSkillFragment needs arguments.");
mOldSkill = null; mOldSkill = null;
} }
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);

View File

@@ -1,43 +1,28 @@
package com.majinnaibu.monstercards.ui.editmonster; package com.majinnaibu.monstercards.ui.editmonster;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.majinnaibu.monstercards.data.enums.AbilityScore; import com.majinnaibu.monstercards.data.enums.AbilityScore;
import com.majinnaibu.monstercards.data.enums.AdvantageType; import com.majinnaibu.monstercards.data.enums.AdvantageType;
import com.majinnaibu.monstercards.data.enums.ProficiencyType; import com.majinnaibu.monstercards.data.enums.ProficiencyType;
import com.majinnaibu.monstercards.models.Skill; import com.majinnaibu.monstercards.models.Skill;
import com.majinnaibu.monstercards.ui.shared.ChangeTrackedViewModel;
import com.majinnaibu.monstercards.utils.ChangeTrackedLiveData; import com.majinnaibu.monstercards.utils.ChangeTrackedLiveData;
public class EditSkillViewModel extends ViewModel { public class EditSkillViewModel extends ChangeTrackedViewModel {
private final ChangeTrackedLiveData<AbilityScore> mAbilityScore; private final ChangeTrackedLiveData<AbilityScore> mAbilityScore;
private final ChangeTrackedLiveData<AdvantageType> mAdvantageType; private final ChangeTrackedLiveData<AdvantageType> mAdvantageType;
private final MutableLiveData<Boolean> mHasChanges;
private final ChangeTrackedLiveData<ProficiencyType> mProficiencyType; private final ChangeTrackedLiveData<ProficiencyType> mProficiencyType;
private final ChangeTrackedLiveData<String> mName; private final ChangeTrackedLiveData<String> mName;
private final ChangeTrackedLiveData<Skill> mSkill; private final ChangeTrackedLiveData<Skill> mSkill;
public EditSkillViewModel() { public EditSkillViewModel() {
mHasChanges = new MutableLiveData<>(false); super();
ChangeTrackedLiveData.OnValueDirtiedCallback onDirtied = () -> mHasChanges.setValue(true); mAbilityScore = new ChangeTrackedLiveData<>(AbilityScore.STRENGTH, this::makeDirty);
mAdvantageType = new ChangeTrackedLiveData<>(AdvantageType.NONE, this::makeDirty);
mAbilityScore = new ChangeTrackedLiveData<>(AbilityScore.STRENGTH, onDirtied); mProficiencyType = new ChangeTrackedLiveData<>(ProficiencyType.NONE, this::makeDirty);
mAdvantageType = new ChangeTrackedLiveData<>(AdvantageType.NONE, onDirtied); mName = new ChangeTrackedLiveData<>("Unknown Skill", this::makeDirty);
mProficiencyType = new ChangeTrackedLiveData<>(ProficiencyType.NONE, onDirtied); mSkill = new ChangeTrackedLiveData<>(makeSkill(), this::makeDirty);
mName = new ChangeTrackedLiveData<>("Unknown Skill", onDirtied);
mSkill = new ChangeTrackedLiveData<>(makeSkill(), onDirtied);
}
public EditSkillViewModel(Skill skill) {
mHasChanges = new MutableLiveData<>(false);
ChangeTrackedLiveData.OnValueDirtiedCallback onDirtied = () -> mHasChanges.setValue(true);
mAbilityScore = new ChangeTrackedLiveData<>(skill.abilityScore, onDirtied);
mAdvantageType = new ChangeTrackedLiveData<>(skill.advantageType, onDirtied);
mProficiencyType = new ChangeTrackedLiveData<>(skill.proficiencyType, onDirtied);
mName = new ChangeTrackedLiveData<>(skill.name, onDirtied);
mSkill = new ChangeTrackedLiveData<>(makeSkill(), onDirtied);
} }
public void copyFromSkill(Skill skill) { public void copyFromSkill(Skill skill) {
@@ -47,13 +32,6 @@ public class EditSkillViewModel extends ViewModel {
mName.resetValue(skill.name); mName.resetValue(skill.name);
} }
public void copyFromSkill(String name, AbilityScore abilityScore, ProficiencyType proficiency, AdvantageType advantage) {
mAbilityScore.resetValue(abilityScore);
mAdvantageType.resetValue(advantage);
mProficiencyType.resetValue(proficiency);
mName.resetValue(name);
}
public LiveData<Skill> getSkill() { public LiveData<Skill> getSkill() {
return mSkill; return mSkill;
} }
@@ -94,14 +72,6 @@ public class EditSkillViewModel extends ViewModel {
mSkill.setValue(makeSkill()); mSkill.setValue(makeSkill());
} }
public LiveData<Boolean> getHasChanges() {
return mHasChanges;
}
public boolean hasChanges() {
return mHasChanges.getValue();
}
private Skill makeSkill() { private Skill makeSkill() {
return new Skill(mName.getValue(), mAbilityScore.getValue(), mAdvantageType.getValue(), mProficiencyType.getValue()); return new Skill(mName.getValue(), mAbilityScore.getValue(), mAdvantageType.getValue(), mProficiencyType.getValue());
} }

View File

@@ -7,7 +7,6 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController; import androidx.navigation.NavController;
@@ -21,14 +20,14 @@ import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.models.Skill; import com.majinnaibu.monstercards.models.Skill;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback; import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback;
import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.Logger;
/** /**
* A fragment representing a list of Items. * A fragment representing a list of Items.
*/ */
@SuppressWarnings("FieldCanBeLocal") public class EditSkillsFragment extends MCFragment {
public class EditSkillsFragment extends Fragment {
private EditMonsterViewModel mViewModel; private EditMonsterViewModel mViewModel;
private ViewHolder mHolder; private ViewHolder mHolder;
@@ -67,7 +66,7 @@ public class EditSkillsFragment extends Fragment {
} else { } else {
Logger.logError("Can't navigate to EditSkill with a null skill"); Logger.logError("Can't navigate to EditSkill with a null skill");
} }
}, null); });
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
}); });

View File

@@ -15,6 +15,7 @@ import androidx.navigation.Navigation;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.ui.components.Stepper; import com.majinnaibu.monstercards.ui.components.Stepper;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.utils.TextChangedListener; import com.majinnaibu.monstercards.utils.TextChangedListener;
public class EditSpeedFragment extends Fragment { public class EditSpeedFragment extends Fragment {

View File

@@ -9,49 +9,45 @@ import android.widget.EditText;
import androidx.activity.OnBackPressedCallback; import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.data.enums.StringType;
import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.Logger;
import com.majinnaibu.monstercards.utils.TextChangedListener; import com.majinnaibu.monstercards.utils.TextChangedListener;
public class EditStringFragment extends MCFragment { public class EditConditionImmunityFragment extends Fragment {
private EditMonsterViewModel mEditMonsterViewModel; private EditMonsterViewModel mEditMonsterViewModel;
private EditStringViewModel mViewModel; private EditStringViewModel mViewModel;
private ViewHolder mHolder; private ViewHolder mHolder;
private String mOldValue; private String mOldValue;
private StringType mStringType;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
mViewModel = new ViewModelProvider(this).get(EditStringViewModel.class); mViewModel = new ViewModelProvider(this).get(EditStringViewModel.class);
if (getArguments() != null) { if (getArguments() != null) {
EditStringFragmentArgs args = EditStringFragmentArgs.fromBundle(getArguments()); EditConditionImmunityFragmentArgs args = EditConditionImmunityFragmentArgs.fromBundle(getArguments());
mOldValue = args.getValue(); mOldValue = args.getCondition();
mViewModel.setValue(mOldValue); mViewModel.resetValue(mOldValue);
mStringType = args.getStringType();
} else { } else {
Logger.logWTF("EditStringFragment needs arguments"); Logger.logWTF("EditConditionImmunityFragment needs arguments");
mOldValue = null; mOldValue = null;
} }
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
} }
@Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment);
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation); NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation);
mEditMonsterViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class); mEditMonsterViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class);
View root = inflater.inflate(R.layout.fragment_edit_string, container, false); View root = inflater.inflate(R.layout.fragment_edit_condition_immunity, container, false);
mHolder = new ViewHolder(root); mHolder = new ViewHolder(root);
setTitle(getTitleForStringType(mStringType));
mHolder.description.setText(mViewModel.getValueAsString()); mHolder.description.setText(mViewModel.getValueAsString());
mHolder.description.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setValue(s.toString()))); mHolder.description.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setValue(s.toString())));
@@ -59,7 +55,7 @@ public class EditStringFragment extends MCFragment {
@Override @Override
public void handleOnBackPressed() { public void handleOnBackPressed() {
if (mViewModel.hasChanges()) { if (mViewModel.hasChanges()) {
mEditMonsterViewModel.replaceString(mStringType, mOldValue, mViewModel.getValueAsString()); mEditMonsterViewModel.replaceConditionImmunity(mOldValue, mViewModel.getValueAsString());
} }
Navigation.findNavController(requireView()).navigateUp(); Navigation.findNavController(requireView()).navigateUp();
} }
@@ -68,35 +64,11 @@ public class EditStringFragment extends MCFragment {
return root; return root;
} }
@NonNull
private String getTitleForStringType(@NonNull StringType type) {
switch (type) {
case CONDITION_IMMUNITY:
return getString(R.string.title_editConditionImmunity);
case DAMAGE_IMMUNITY:
return getString(R.string.title_editDamageImmunity);
case DAMAGE_RESISTANCE:
return getString(R.string.title_editDamageResistance);
case DAMAGE_VULNERABILITY:
return getString(R.string.title_editDamageVulnerability);
case SENSE:
return getString(R.string.title_editSense);
default:
return getString(R.string.title_editString);
}
}
@Override
public void onStart() {
super.onStart();
mHolder.description.requestFocus();
}
private static class ViewHolder { private static class ViewHolder {
EditText description; EditText description;
ViewHolder(@NonNull View root) { ViewHolder(View root) {
description = root.findViewById(R.id.description); description = root.findViewById(R.id.description);
} }
} }
} }

View File

@@ -4,30 +4,34 @@ import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.majinnaibu.monstercards.databinding.FragmentEditStringsListItemBinding; import com.majinnaibu.monstercards.databinding.FragmentEditConditionImmunitiesListItemBinding;
import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public class EditStringsRecyclerViewAdapter extends RecyclerView.Adapter<EditStringsRecyclerViewAdapter.ViewHolder> { /**
* {@link RecyclerView.Adapter} that can display a {@link String}.
*/
public class EditConditionImmunitiesRecyclerViewAdapter extends RecyclerView.Adapter<EditConditionImmunitiesRecyclerViewAdapter.ViewHolder> {
private final List<String> mValues; private final List<String> mValues;
private final ItemCallback mOnClick; private final ItemCallback mOnClick;
public EditStringsRecyclerViewAdapter(List<String> items, ItemCallback onClick) { public EditConditionImmunitiesRecyclerViewAdapter(List<String> items, ItemCallback onClick) {
mValues = items; mValues = items;
mOnClick = onClick; mOnClick = onClick;
} }
@NonNull @NotNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) {
return new ViewHolder(FragmentEditStringsListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); return new ViewHolder(FragmentEditConditionImmunitiesListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
} }
@Override @Override
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position); holder.mItem = mValues.get(position);
holder.mContentView.setText(mValues.get(position)); holder.mContentView.setText(mValues.get(position));
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v -> {
@@ -43,22 +47,22 @@ public class EditStringsRecyclerViewAdapter extends RecyclerView.Adapter<EditStr
} }
public interface ItemCallback { public interface ItemCallback {
void onItemCallback(String value); void onItemCallback(String condition);
} }
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
public final TextView mContentView; public final TextView mContentView;
public String mItem; public String mItem;
public ViewHolder(@NonNull FragmentEditStringsListItemBinding binding) { public ViewHolder(FragmentEditConditionImmunitiesListItemBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
mContentView = binding.content; mContentView = binding.content;
} }
@NonNull @NotNull
@Override @Override
public String toString() { public String toString() {
return super.toString() + " '" + mContentView.getText() + "'"; return super.toString() + " '" + mContentView.getText() + "'";
} }
} }
} }

View File

@@ -1,5 +1,6 @@
package com.majinnaibu.monstercards.ui.library; package com.majinnaibu.monstercards.ui.library;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -8,6 +9,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@@ -17,14 +19,14 @@ import com.google.android.material.snackbar.Snackbar;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.data.MonsterRepository; import com.majinnaibu.monstercards.data.MonsterRepository;
import com.majinnaibu.monstercards.models.Monster; import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.ui.MCFragment; import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.ui.MonsterListRecyclerViewAdapter;
import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback; import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback;
import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.Logger;
import java.util.UUID; import java.util.UUID;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.observers.DisposableCompletableObserver;
import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.schedulers.Schedulers;
public class LibraryFragment extends MCFragment { public class LibraryFragment extends MCFragment {
@@ -45,57 +47,77 @@ public class LibraryFragment extends MCFragment {
} }
private void setupRecyclerView(@NonNull RecyclerView recyclerView) { private void setupRecyclerView(@NonNull RecyclerView recyclerView) {
Context context = requireContext();
MonsterRepository repository = this.getMonsterRepository(); MonsterRepository repository = this.getMonsterRepository();
boolean mTwoPane = false;
MonsterListRecyclerViewAdapter adapter = new MonsterListRecyclerViewAdapter( MonsterListRecyclerViewAdapter adapter = new MonsterListRecyclerViewAdapter(
this, context,
repository.getMonsters(), repository.getMonsters(),
(monster) -> { (monster) -> navigateToMonsterDetail(monster.id),
repository (monster) -> repository
.deleteMonster(monster) .deleteMonster(monster)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> { .subscribe(new DisposableCompletableObserver() {
Logger.logDebug("deleted"); @Override
}, Logger::logError); public void onComplete() {
}, }
mTwoPane);
@Override
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
Logger.logError(e);
}
}));
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(getContext(), (position) -> { LinearLayoutManager layoutManager = new LinearLayoutManager(context);
adapter.deleteItem(position); recyclerView.setLayoutManager(layoutManager);
}));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(requireContext(), adapter::deleteItem));
itemTouchHelper.attachToRecyclerView(recyclerView); itemTouchHelper.attachToRecyclerView(recyclerView);
} }
private void setupAddMonsterButton(@NonNull FloatingActionButton fab) { private void setupAddMonsterButton(@NonNull FloatingActionButton fab) {
fab.setOnClickListener(view -> { fab.setOnClickListener(view -> {
Monster monster = new Monster(); Monster monster = new Monster();
monster.name = "Unnamed Monster"; monster.name = getString(R.string.default_monster_name);
MonsterRepository repository = this.getMonsterRepository(); MonsterRepository repository = this.getMonsterRepository();
repository.addMonster(monster) repository.addMonster(monster)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> { .subscribe(
Snackbar.make( new DisposableCompletableObserver() {
getView(), @Override
String.format("%s created", monster.name), public void onComplete() {
Snackbar.LENGTH_LONG) View view = getView();
.setAction("Action", (_view) -> { assert view != null;
navigateToMonsterDetail(monster.id); Snackbar.make(
}) view,
.show(); getString(R.string.snackbar_monster_created, monster.name),
}, throwable -> { Snackbar.LENGTH_LONG)
Logger.logError("Error creating monster", throwable); .setAction("Action", (_view) -> navigateToMonsterDetail(monster.id))
Snackbar.make(getView(), "Failed to create monster", Snackbar.LENGTH_LONG) .show();
.setAction("Action", null).show(); }
});
});
@Override
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
Logger.logError("Error creating monster", e);
View view = getView();
assert view != null;
Snackbar.make(view, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
});
} }
protected void navigateToMonsterDetail(UUID monsterId) { protected void navigateToMonsterDetail(UUID monsterId) {
NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString()); NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString());
Navigation.findNavController(getView()).navigate(action); View view = getView();
assert view != null;
Navigation.findNavController(view).navigate(action);
} }
} }

View File

@@ -26,7 +26,7 @@ import com.majinnaibu.monstercards.data.MonsterRepository;
import com.majinnaibu.monstercards.helpers.CommonMarkHelper; import com.majinnaibu.monstercards.helpers.CommonMarkHelper;
import com.majinnaibu.monstercards.helpers.StringHelper; import com.majinnaibu.monstercards.helpers.StringHelper;
import com.majinnaibu.monstercards.models.Monster; import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.ui.MCFragment; import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.Logger;
import java.util.UUID; import java.util.UUID;
@@ -241,10 +241,15 @@ public class MonsterDetailFragment extends MCFragment {
@Override @Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) { public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.menu_action_edit_monster) { if (item.getItemId() == R.id.menu_action_edit_monster) {
NavDirections action = MonsterDetailFragmentDirections.actionNavigationMonsterToEditMonsterFragment(monsterDetailViewModel.getId().toString()); UUID monsterId = monsterDetailViewModel.getId().getValue();
View view = getView(); if (monsterId != null) {
assert view != null; NavDirections action = MonsterDetailFragmentDirections.actionNavigationMonsterToEditMonsterFragment(monsterId.toString());
Navigation.findNavController(view).navigate(action); View view = getView();
assert view != null;
Navigation.findNavController(view).navigate(action);
} else {
Logger.logWTF("monsterId cannot be null.");
}
return true; return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);

View File

@@ -14,12 +14,10 @@ import androidx.recyclerview.widget.RecyclerView;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.data.MonsterRepository; import com.majinnaibu.monstercards.data.MonsterRepository;
import com.majinnaibu.monstercards.ui.MCFragment; import com.majinnaibu.monstercards.ui.shared.MCFragment;
public class SearchFragment extends MCFragment { public class SearchFragment extends MCFragment {
private SearchViewModel searchViewModel;
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_search, container, false); View root = inflater.inflate(R.layout.fragment_search, container, false);

View File

@@ -13,6 +13,8 @@ import com.majinnaibu.monstercards.data.MonsterRepository;
import com.majinnaibu.monstercards.models.Monster; import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -20,16 +22,11 @@ import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.disposables.Disposable;
public class SearchResultsRecyclerViewAdapter extends RecyclerView.Adapter<SearchResultsRecyclerViewAdapter.ViewHolder> { public class SearchResultsRecyclerViewAdapter extends RecyclerView.Adapter<SearchResultsRecyclerViewAdapter.ViewHolder> {
public interface ItemCallback {
void onItem(Monster monster);
}
private final MonsterRepository mRepository; private final MonsterRepository mRepository;
private final ItemCallback mOnClickHandler;
private String mSearchText; private String mSearchText;
private List<Monster> mValues; private List<Monster> mValues;
private Disposable mSubscriptionHandler; private Disposable mSubscriptionHandler;
private final ItemCallback mOnClickHandler;
public SearchResultsRecyclerViewAdapter(MonsterRepository repository, public SearchResultsRecyclerViewAdapter(MonsterRepository repository,
ItemCallback onClick) { ItemCallback onClick) {
mRepository = repository; mRepository = repository;
@@ -54,6 +51,7 @@ public class SearchResultsRecyclerViewAdapter extends RecyclerView.Adapter<Searc
throwable -> Logger.logError("Error performing search", throwable)); throwable -> Logger.logError("Error performing search", throwable));
} }
@NotNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()) View view = LayoutInflater.from(parent.getContext())
@@ -79,7 +77,11 @@ public class SearchResultsRecyclerViewAdapter extends RecyclerView.Adapter<Searc
return mValues.size(); return mValues.size();
} }
class ViewHolder extends RecyclerView.ViewHolder { public interface ItemCallback {
void onItem(Monster monster);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
final TextView mIdView; final TextView mIdView;
final TextView mContentView; final TextView mContentView;