Makes the dashboard editable.
This commit is contained in:
@@ -5,6 +5,7 @@ import androidx.room.RoomDatabase;
|
||||
import androidx.room.TypeConverters;
|
||||
|
||||
import com.majinnaibu.monstercards.data.CollectionDAO;
|
||||
import com.majinnaibu.monstercards.data.DashboardDAO;
|
||||
import com.majinnaibu.monstercards.data.MonsterDAO;
|
||||
import com.majinnaibu.monstercards.data.converters.ArmorTypeConverter;
|
||||
import com.majinnaibu.monstercards.data.converters.ChallengeRatingConverter;
|
||||
@@ -15,10 +16,11 @@ import com.majinnaibu.monstercards.data.converters.SetOfStringConverter;
|
||||
import com.majinnaibu.monstercards.data.converters.UUIDConverter;
|
||||
import com.majinnaibu.monstercards.models.Collection;
|
||||
import com.majinnaibu.monstercards.models.CollectionMonster;
|
||||
import com.majinnaibu.monstercards.models.DashboardMonster;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.models.MonsterFTS;
|
||||
|
||||
@Database(entities = {Monster.class, MonsterFTS.class, Collection.class, CollectionMonster.class}, version = 4)
|
||||
@Database(entities = {Monster.class, MonsterFTS.class, Collection.class, CollectionMonster.class, DashboardMonster.class}, version = 5)
|
||||
@TypeConverters({
|
||||
ArmorTypeConverter.class,
|
||||
ChallengeRatingConverter.class,
|
||||
@@ -31,4 +33,5 @@ import com.majinnaibu.monstercards.models.MonsterFTS;
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
public abstract MonsterDAO monsterDAO();
|
||||
public abstract CollectionDAO collectionDAO();
|
||||
public abstract DashboardDAO dashboardDAO();
|
||||
}
|
||||
|
||||
@@ -44,6 +44,14 @@ public class MonsterCardsApplication extends Application {
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_collection_monsters_monster_id` ON `collection_monsters` (`monster_id`)");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_4_5 = new Migration(4, 5) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `dashboard_monsters` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `monster_id` TEXT NOT NULL, `ordinal` INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(`monster_id`) REFERENCES `monsters`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE)");
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS `index_dashboard_monsters_monster_id` ON `dashboard_monsters` (`monster_id`)");
|
||||
database.execSQL("INSERT INTO dashboard_monsters(monster_id, ordinal) SELECT id, 0 FROM monsters");
|
||||
}
|
||||
};
|
||||
private MonsterRepository m_monsterLibraryRepository;
|
||||
|
||||
|
||||
@@ -66,6 +74,7 @@ public class MonsterCardsApplication extends Application {
|
||||
.addMigrations(MIGRATION_1_2)
|
||||
.addMigrations(MIGRATION_2_3)
|
||||
.addMigrations(MIGRATION_3_4)
|
||||
.addMigrations(MIGRATION_4_5)
|
||||
.fallbackToDestructiveMigrationOnDowngrade()
|
||||
// .fallbackToDestructiveMigration()
|
||||
.build();
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.majinnaibu.monstercards.data;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import com.majinnaibu.monstercards.models.DashboardMonster;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
|
||||
@Dao
|
||||
public interface DashboardDAO {
|
||||
|
||||
@Query("SELECT monsters.* FROM monsters INNER JOIN dashboard_monsters ON monsters.id = dashboard_monsters.monster_id ORDER BY dashboard_monsters.ordinal ASC, dashboard_monsters.id ASC")
|
||||
Flowable<List<Monster>> getDashboardMonsters();
|
||||
|
||||
@Query("SELECT * FROM dashboard_monsters ORDER BY ordinal ASC, id ASC")
|
||||
Flowable<List<DashboardMonster>> getDashboardMonsterEntries();
|
||||
|
||||
@Insert
|
||||
Completable addMonsterToDashboard(DashboardMonster... dashboardMonsters);
|
||||
|
||||
@Query("DELETE FROM dashboard_monsters WHERE id = :id")
|
||||
Completable removeDashboardMonsterById(long id);
|
||||
|
||||
@Query("DELETE FROM dashboard_monsters WHERE monster_id = :monsterId")
|
||||
Completable removeMonsterFromDashboard(String monsterId);
|
||||
|
||||
@Query("DELETE FROM dashboard_monsters")
|
||||
Completable clearDashboard();
|
||||
|
||||
@Update
|
||||
Completable updateDashboardMonsters(List<DashboardMonster> dashboardMonsters);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.majinnaibu.monstercards.helpers.StringHelper;
|
||||
import com.majinnaibu.monstercards.models.Collection;
|
||||
import com.majinnaibu.monstercards.models.CollectionMonster;
|
||||
import com.majinnaibu.monstercards.models.CollectionWithCount;
|
||||
import com.majinnaibu.monstercards.models.DashboardMonster;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.models.SearchResultItem;
|
||||
|
||||
@@ -197,6 +198,68 @@ public class MonsterRepository {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Flowable<List<Monster>> getDashboardMonsters() {
|
||||
return m_db.dashboardDAO()
|
||||
.getDashboardMonsters()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Flowable<List<DashboardMonster>> getDashboardMonsterEntries() {
|
||||
return m_db.dashboardDAO()
|
||||
.getDashboardMonsterEntries()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Completable addMonsterToDashboard(@NonNull UUID monsterId) {
|
||||
DashboardMonster entry = new DashboardMonster(monsterId, 0);
|
||||
Completable result = m_db.dashboardDAO().addMonsterToDashboard(entry);
|
||||
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||
return result;
|
||||
}
|
||||
|
||||
public Completable addCollectionToDashboard(@NonNull UUID collectionId) {
|
||||
return m_db.collectionDAO().getMonstersForCollection(collectionId.toString())
|
||||
.firstOrError()
|
||||
.flatMapCompletable(monsters -> {
|
||||
if (monsters.isEmpty()) {
|
||||
return Completable.complete();
|
||||
}
|
||||
DashboardMonster[] entries = new DashboardMonster[monsters.size()];
|
||||
for (int i = 0; i < monsters.size(); i++) {
|
||||
entries[i] = new DashboardMonster(monsters.get(i).id, i);
|
||||
}
|
||||
return m_db.dashboardDAO().addMonsterToDashboard(entries);
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Completable removeMonsterFromDashboard(@NonNull UUID monsterId) {
|
||||
Completable result = m_db.dashboardDAO().removeMonsterFromDashboard(monsterId.toString());
|
||||
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||
return result;
|
||||
}
|
||||
|
||||
public Completable removeDashboardMonsterById(long id) {
|
||||
Completable result = m_db.dashboardDAO().removeDashboardMonsterById(id);
|
||||
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||
return result;
|
||||
}
|
||||
|
||||
public Completable clearDashboard() {
|
||||
Completable result = m_db.dashboardDAO().clearDashboard();
|
||||
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||
return result;
|
||||
}
|
||||
|
||||
public Completable updateDashboardMonsters(List<DashboardMonster> items) {
|
||||
Completable result = m_db.dashboardDAO().updateDashboardMonsters(items);
|
||||
result.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class Helpers {
|
||||
static boolean monsterMatchesSearch(Monster monster, String searchText) {
|
||||
if (StringHelper.isNullOrEmpty(searchText)) {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.majinnaibu.monstercards.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.ForeignKey;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity(
|
||||
tableName = "dashboard_monsters",
|
||||
foreignKeys = @ForeignKey(
|
||||
entity = Monster.class,
|
||||
parentColumns = "id",
|
||||
childColumns = "monster_id",
|
||||
onDelete = ForeignKey.CASCADE
|
||||
),
|
||||
indices = @Index(value = {"monster_id"})
|
||||
)
|
||||
public class DashboardMonster {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public long id;
|
||||
|
||||
@NonNull
|
||||
@ColumnInfo(name = "monster_id")
|
||||
public UUID monsterId;
|
||||
|
||||
@ColumnInfo(name = "ordinal", defaultValue = "0")
|
||||
public int ordinal;
|
||||
|
||||
public DashboardMonster() {
|
||||
this.monsterId = UUID.fromString("00000000-0000-0000-0000-000000000000");
|
||||
this.ordinal = 0;
|
||||
}
|
||||
|
||||
public DashboardMonster(@NonNull UUID monsterId, int ordinal) {
|
||||
this.monsterId = monsterId;
|
||||
this.ordinal = ordinal;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,9 @@ import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
@@ -44,6 +47,7 @@ public class CollectionDetailFragment extends MCFragment {
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
setHasOptionsMenu(true);
|
||||
View root = inflater.inflate(R.layout.fragment_collection_detail, container, false);
|
||||
|
||||
Bundle arguments = getArguments();
|
||||
@@ -172,6 +176,36 @@ public class CollectionDetailFragment extends MCFragment {
|
||||
Navigation.findNavController(requireView()).navigate(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.collection_detail_menu, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == R.id.menu_action_add_collection_to_dashboard) {
|
||||
addCollectionToDashboard();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void addCollectionToDashboard() {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.addCollectionToDashboard(mCollectionId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Collection collection = mViewModel.getCollection().getValue();
|
||||
String collectionName = collection != null ? collection.name : "";
|
||||
Snackbar.make(view, getString(R.string.snackbar_collection_added_to_dashboard, collectionName), Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -3,52 +3,77 @@ package com.majinnaibu.monstercards.ui.dashboard;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
import com.majinnaibu.monstercards.models.Collection;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class DashboardFragment extends MCFragment {
|
||||
private DashboardViewModel mViewModel;
|
||||
private ViewHolder mHolder;
|
||||
private DashboardRecyclerViewAdapter mAdapter;
|
||||
private final CompositeDisposable mDisposables = new CompositeDisposable();
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
setHasOptionsMenu(true);
|
||||
mViewModel = new ViewModelProvider(this).get(DashboardViewModel.class);
|
||||
View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
|
||||
mHolder = new ViewHolder(root);
|
||||
|
||||
setupRecyclerView(mHolder.list);
|
||||
FloatingActionButton fab = root.findViewById(R.id.fab_add_to_dashboard);
|
||||
if (fab != null) {
|
||||
fab.setOnClickListener(v -> showAddToDashboardOptionsDialog());
|
||||
}
|
||||
|
||||
getMonsterRepository()
|
||||
.getMonsters()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(monsters -> mViewModel.setMonsters(monsters));
|
||||
setupRecyclerView(mHolder.list);
|
||||
loadDashboardMonsters();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private void loadDashboardMonsters() {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.getDashboardMonsters()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(monsters -> mViewModel.setMonsters(monsters), Logger::logError));
|
||||
}
|
||||
|
||||
private void setupRecyclerView(@NonNull RecyclerView recyclerView) {
|
||||
int columnCount = Math.max(1, getResources().getConfiguration().screenWidthDp / 396);
|
||||
int columnCount = Math.max(1, getResources().getConfiguration().screenWidthDp / 360);
|
||||
Logger.logWTF(String.format(Locale.US, "Setting column count to %d", columnCount));
|
||||
Context context = requireContext();
|
||||
GridLayoutManager layoutManager = new GridLayoutManager(context, columnCount);
|
||||
@@ -66,6 +91,165 @@ public class DashboardFragment extends MCFragment {
|
||||
monsterData.observe(getViewLifecycleOwner(), monsters -> mAdapter.submitList(monsters));
|
||||
}
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
|
||||
ItemTouchHelper.SimpleCallback touchHelperCallback = new ItemTouchHelper.SimpleCallback(
|
||||
ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT,
|
||||
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT
|
||||
) {
|
||||
@Override
|
||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
||||
int fromPos = viewHolder.getAdapterPosition();
|
||||
int toPos = target.getAdapterPosition();
|
||||
if (fromPos != RecyclerView.NO_POSITION && toPos != RecyclerView.NO_POSITION) {
|
||||
List<Monster> currentList = new ArrayList<>(mAdapter.getCurrentList());
|
||||
if (fromPos < currentList.size() && toPos < currentList.size()) {
|
||||
Monster moved = currentList.remove(fromPos);
|
||||
currentList.add(toPos, moved);
|
||||
mAdapter.submitList(currentList);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
List<Monster> currentList = mAdapter.getCurrentList();
|
||||
if (position < currentList.size()) {
|
||||
Monster monster = currentList.get(position);
|
||||
removeMonsterFromDashboard(monster);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
new ItemTouchHelper(touchHelperCallback).attachToRecyclerView(recyclerView);
|
||||
}
|
||||
|
||||
private void showAddToDashboardOptionsDialog() {
|
||||
String[] options = new String[]{
|
||||
getString(R.string.action_add_single_monster),
|
||||
getString(R.string.action_add_collection_option),
|
||||
getString(R.string.action_clear_dashboard)
|
||||
};
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.dialog_add_to_dashboard)
|
||||
.setItems(options, (dialog, which) -> {
|
||||
if (which == 0) {
|
||||
showAddMonsterPicker();
|
||||
} else if (which == 1) {
|
||||
showAddCollectionPicker();
|
||||
} else if (which == 2) {
|
||||
clearDashboard();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.dialog_cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showAddMonsterPicker() {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.getMonsters()
|
||||
.firstOrError()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(monsters -> {
|
||||
if (monsters.isEmpty()) {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.no_monsters_available), Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
return;
|
||||
}
|
||||
String[] names = new String[monsters.size()];
|
||||
for (int i = 0; i < monsters.size(); i++) {
|
||||
names[i] = monsters.get(i).name;
|
||||
}
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.action_add_single_monster)
|
||||
.setItems(names, (dialog, which) -> {
|
||||
Monster selected = monsters.get(which);
|
||||
addMonsterToDashboard(selected);
|
||||
})
|
||||
.setNegativeButton(R.string.dialog_cancel, null)
|
||||
.show();
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
private void showAddCollectionPicker() {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.getCollections()
|
||||
.firstOrError()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(collections -> {
|
||||
if (collections.isEmpty()) {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.no_collections_available), Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
return;
|
||||
}
|
||||
String[] names = new String[collections.size()];
|
||||
for (int i = 0; i < collections.size(); i++) {
|
||||
names[i] = collections.get(i).name;
|
||||
}
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.action_add_collection_option)
|
||||
.setItems(names, (dialog, which) -> {
|
||||
Collection selected = collections.get(which);
|
||||
addCollectionToDashboard(selected);
|
||||
})
|
||||
.setNegativeButton(R.string.dialog_cancel, null)
|
||||
.show();
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
private void addMonsterToDashboard(@NonNull Monster monster) {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.addMonsterToDashboard(monster.id)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_added_to_dashboard, monster.name), Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
private void addCollectionToDashboard(@NonNull Collection collection) {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.addCollectionToDashboard(collection.id)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_collection_added_to_dashboard, collection.name), Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
private void removeMonsterFromDashboard(@NonNull Monster monster) {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.removeMonsterFromDashboard(monster.id)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {}, Logger::logError));
|
||||
}
|
||||
|
||||
private void clearDashboard() {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.clearDashboard()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_dashboard_cleared), Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
private void navigateToMonsterDetail(Monster monster) {
|
||||
@@ -73,6 +257,33 @@ public class DashboardFragment extends MCFragment {
|
||||
Navigation.findNavController(requireView()).navigate(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.dashboard_menu, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == R.id.menu_action_add_single_monster) {
|
||||
showAddMonsterPicker();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_action_add_collection_option) {
|
||||
showAddCollectionPicker();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_action_clear_dashboard) {
|
||||
clearDashboard();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mDisposables.clear();
|
||||
}
|
||||
|
||||
private static class ViewHolder {
|
||||
final RecyclerView list;
|
||||
|
||||
@@ -80,5 +291,4 @@ public class DashboardFragment extends MCFragment {
|
||||
list = root.findViewById(R.id.list);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
@@ -19,6 +20,7 @@ import com.google.android.material.snackbar.Snackbar;
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.ui.monster.MonsterDetailFragmentDirections;
|
||||
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
||||
import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
@@ -92,23 +94,16 @@ public class LibraryFragment extends MCFragment {
|
||||
new DisposableCompletableObserver() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
View view = getView();
|
||||
assert view != null;
|
||||
Snackbar.make(
|
||||
view,
|
||||
getString(R.string.snackbar_monster_created, monster.name),
|
||||
Snackbar.LENGTH_LONG)
|
||||
.setAction("Action", (_view) -> navigateToMonsterDetail(monster.id))
|
||||
.show();
|
||||
navigateToEditMonster(monster.id);
|
||||
}
|
||||
|
||||
@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();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -118,4 +113,12 @@ public class LibraryFragment extends MCFragment {
|
||||
NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString());
|
||||
Navigation.findNavController(requireView()).navigate(action);
|
||||
}
|
||||
|
||||
protected void navigateToEditMonster(@NonNull UUID monsterId) {
|
||||
NavController navController = Navigation.findNavController(requireView());
|
||||
NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString());
|
||||
navController.navigate(action);
|
||||
action = MonsterDetailFragmentDirections.actionNavigationMonsterToEditMonsterFragment(monsterId.toString());
|
||||
navController.navigate(action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,10 +181,31 @@ public class MonsterDetailFragment extends MCFragment {
|
||||
} else if (item.getItemId() == R.id.menu_action_add_to_collection) {
|
||||
showAddToCollectionDialog();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_action_add_to_dashboard) {
|
||||
addCurrentMonsterToDashboard();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void addCurrentMonsterToDashboard() {
|
||||
UUID monsterId = mViewModel.getId().getValue();
|
||||
if (monsterId == null) {
|
||||
return;
|
||||
}
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
repository.addMonsterToDashboard(monsterId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
String monsterName = mViewModel.getName().getValue();
|
||||
Snackbar.make(view, getString(R.string.snackbar_added_to_dashboard, monsterName), Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}, Logger::logError);
|
||||
}
|
||||
|
||||
private void showAddToCollectionDialog() {
|
||||
UUID monsterId = mViewModel.getId().getValue();
|
||||
if (monsterId == null) {
|
||||
|
||||
@@ -20,4 +20,16 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:listitem="@layout/card_monster" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_add_to_dashboard"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
android:contentDescription="@string/action_add_to_dashboard"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:srcCompat="@android:drawable/ic_input_add"
|
||||
app:tint="@android:color/white" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
9
Android/app/src/main/res/menu/collection_detail_menu.xml
Normal file
9
Android/app/src/main/res/menu/collection_detail_menu.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_action_add_collection_to_dashboard"
|
||||
android:title="@string/action_add_collection_to_dashboard"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
19
Android/app/src/main/res/menu/dashboard_menu.xml
Normal file
19
Android/app/src/main/res/menu/dashboard_menu.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_action_add_single_monster"
|
||||
android:title="@string/action_add_single_monster"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_action_add_collection_option"
|
||||
android:title="@string/action_add_collection_option"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_action_clear_dashboard"
|
||||
android:title="@string/action_clear_dashboard"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
@@ -3,14 +3,17 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_action_add_to_collection"
|
||||
android:icon="@android:drawable/ic_input_add"
|
||||
android:title="@string/action_add_to_collection"
|
||||
app:showAsAction="ifRoom" />
|
||||
android:id="@+id/menu_action_edit_monster"
|
||||
android:title="@string/action_edit"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_action_edit_monster"
|
||||
android:icon="@drawable/ic_edit_24"
|
||||
android:title="@string/action_edit"
|
||||
app:showAsAction="always" />
|
||||
android:id="@+id/menu_action_add_to_collection"
|
||||
android:title="@string/action_add_to_collection"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_action_add_to_dashboard"
|
||||
android:title="@string/action_add_to_dashboard"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
||||
@@ -152,4 +152,14 @@
|
||||
<string name="no_collections_available">No collections available. Create one first!</string>
|
||||
<string name="format_monster_count_one">1 monster</string>
|
||||
<string name="format_monster_count_other">%d monsters</string>
|
||||
<string name="action_add_to_dashboard">Add to Dashboard</string>
|
||||
<string name="action_add_collection_to_dashboard">Add Collection to Dashboard</string>
|
||||
<string name="action_add_single_monster">Add Monster</string>
|
||||
<string name="action_add_collection_option">Add Collection</string>
|
||||
<string name="action_clear_dashboard">Clear Dashboard</string>
|
||||
<string name="dialog_add_to_dashboard">Add to Dashboard</string>
|
||||
<string name="snackbar_added_to_dashboard">Added %1$s to Dashboard</string>
|
||||
<string name="snackbar_collection_added_to_dashboard">Added collection %1$s to Dashboard</string>
|
||||
<string name="snackbar_dashboard_cleared">Dashboard cleared</string>
|
||||
<string name="no_monsters_available">No monsters available in library. Create one first!</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user