Adds removing a monster from the dashboard via long press.
This commit is contained in:
@@ -21,8 +21,6 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.navigation.ui.AppBarConfiguration;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.gson.Gson;
|
||||
import com.majinnaibu.monstercards.helpers.MonsterImportHelper;
|
||||
@@ -31,6 +29,7 @@ import com.majinnaibu.monstercards.importers.BinderImporter;
|
||||
import com.majinnaibu.monstercards.importers.DnDBeyondImporter;
|
||||
import com.majinnaibu.monstercards.init.AppCenterInitializer;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
import com.majinnaibu.monstercards.utils.ToastHelper;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@@ -141,24 +140,24 @@ public class MainActivity extends AppCompatActivity {
|
||||
public void importMonsterFromInputAndNavigate(@NonNull String input) {
|
||||
BinderImporter binderImporter = new BinderImporter();
|
||||
if (binderImporter.canImport(input)) {
|
||||
Toast.makeText(this, R.string.toast_importing_url, Toast.LENGTH_SHORT).show();
|
||||
ToastHelper.showShort(this, R.string.toast_importing_url);
|
||||
Single.fromCallable(() -> binderImporter.parse(input))
|
||||
.flatMapCompletable(binder -> ((MonsterCardsApplication) getApplication()).getMonsterRepository().importBinder(binder))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {
|
||||
Toast.makeText(this, R.string.snackbar_import_binder_success, Toast.LENGTH_LONG).show();
|
||||
ToastHelper.showLong(this, R.string.snackbar_import_binder_success);
|
||||
NavHostFragment navHostFragment = Objects.requireNonNull((NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment));
|
||||
NavController navController = navHostFragment.getNavController();
|
||||
navController.navigate(R.id.navigation_library);
|
||||
}, throwable -> {
|
||||
Logger.logError("Failed to import binder from input", throwable);
|
||||
Toast.makeText(this, R.string.failed_to_import_url, Toast.LENGTH_LONG).show();
|
||||
ToastHelper.showLong(this, R.string.failed_to_import_url);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(this, R.string.toast_importing_url, Toast.LENGTH_SHORT).show();
|
||||
ToastHelper.showShort(this, R.string.toast_importing_url);
|
||||
Single.fromCallable(() -> MonsterImportHelper.fromJSON(input))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
@@ -170,7 +169,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
navController.navigate(navAction);
|
||||
}, throwable -> {
|
||||
Logger.logError("Failed to import monster from input", throwable);
|
||||
Toast.makeText(this, R.string.failed_to_import_url, Toast.LENGTH_LONG).show();
|
||||
ToastHelper.showLong(this, R.string.failed_to_import_url);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.majinnaibu.monstercards.data;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Transaction;
|
||||
import androidx.room.Update;
|
||||
|
||||
import com.majinnaibu.monstercards.models.DashboardMonster;
|
||||
import com.majinnaibu.monstercards.models.DashboardMonsterWithMonster;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
|
||||
import java.util.List;
|
||||
@@ -19,6 +21,10 @@ 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();
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM dashboard_monsters ORDER BY ordinal ASC, id ASC")
|
||||
Flowable<List<DashboardMonsterWithMonster>> getDashboardMonstersWithMonster();
|
||||
|
||||
@Query("SELECT * FROM dashboard_monsters ORDER BY ordinal ASC, id ASC")
|
||||
Flowable<List<DashboardMonster>> getDashboardMonsterEntries();
|
||||
|
||||
@@ -28,9 +34,12 @@ public interface DashboardDAO {
|
||||
@Query("DELETE FROM dashboard_monsters WHERE id = :id")
|
||||
Completable removeDashboardMonsterById(long id);
|
||||
|
||||
@Query("DELETE FROM dashboard_monsters WHERE monster_id = :monsterId")
|
||||
@Query("DELETE FROM dashboard_monsters WHERE id = (SELECT id FROM dashboard_monsters WHERE monster_id = :monsterId LIMIT 1)")
|
||||
Completable removeMonsterFromDashboard(String monsterId);
|
||||
|
||||
@Query("DELETE FROM dashboard_monsters WHERE monster_id = :monsterId")
|
||||
Completable removeAllMonstersFromDashboard(String monsterId);
|
||||
|
||||
@Query("DELETE FROM dashboard_monsters")
|
||||
Completable clearDashboard();
|
||||
|
||||
|
||||
@@ -208,6 +208,13 @@ public class MonsterRepository {
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Flowable<List<com.majinnaibu.monstercards.models.DashboardMonsterWithMonster>> getDashboardMonstersWithMonster() {
|
||||
return m_db.dashboardDAO()
|
||||
.getDashboardMonstersWithMonster()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Flowable<List<DashboardMonster>> getDashboardMonsterEntries() {
|
||||
return m_db.dashboardDAO()
|
||||
.getDashboardMonsterEntries()
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.majinnaibu.monstercards.models;
|
||||
|
||||
import androidx.room.Embedded;
|
||||
import androidx.room.Relation;
|
||||
|
||||
public class DashboardMonsterWithMonster {
|
||||
@Embedded
|
||||
public DashboardMonster dashboardEntry;
|
||||
|
||||
@Relation(
|
||||
parentColumn = "monster_id",
|
||||
entityColumn = "id"
|
||||
)
|
||||
public Monster monster;
|
||||
|
||||
public DashboardMonsterWithMonster() {
|
||||
}
|
||||
|
||||
public DashboardMonsterWithMonster(DashboardMonster dashboardEntry, Monster monster) {
|
||||
this.dashboardEntry = dashboardEntry;
|
||||
this.monster = monster;
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,18 @@ import androidx.recyclerview.widget.GridLayoutManager;
|
||||
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.DashboardMonster;
|
||||
import com.majinnaibu.monstercards.models.DashboardMonsterWithMonster;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.ui.dashboard.DashboardRecyclerViewAdapter;
|
||||
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
import com.majinnaibu.monstercards.utils.SnackbarHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -80,7 +83,15 @@ public class CollectionDetailFragment extends MCFragment {
|
||||
|
||||
mViewModel.getMonsters().observe(getViewLifecycleOwner(), monsters -> {
|
||||
if (mAdapter != null) {
|
||||
mAdapter.submitList(monsters);
|
||||
List<DashboardMonsterWithMonster> items = new ArrayList<>();
|
||||
if (monsters != null) {
|
||||
for (int i = 0; i < monsters.size(); i++) {
|
||||
DashboardMonster dm = new DashboardMonster(monsters.get(i).id, i);
|
||||
dm.id = i + 1;
|
||||
items.add(new DashboardMonsterWithMonster(dm, monsters.get(i)));
|
||||
}
|
||||
}
|
||||
mAdapter.submitList(items);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -113,9 +124,9 @@ public class CollectionDetailFragment extends MCFragment {
|
||||
GridLayoutManager layoutManager = new GridLayoutManager(context, columnCount);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
mAdapter = new DashboardRecyclerViewAdapter(monster -> {
|
||||
if (monster != null) {
|
||||
navigateToMonsterDetail(monster.id);
|
||||
mAdapter = new DashboardRecyclerViewAdapter(item -> {
|
||||
if (item != null && item.monster != null) {
|
||||
navigateToMonsterDetail(item.monster.id);
|
||||
} else {
|
||||
Logger.logError("Can't navigate to MonsterDetailFragment with a null monster");
|
||||
}
|
||||
@@ -133,7 +144,7 @@ public class CollectionDetailFragment extends MCFragment {
|
||||
if (monsters.isEmpty()) {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_failed_to_create_monster));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -162,11 +173,7 @@ public class CollectionDetailFragment extends MCFragment {
|
||||
if (view != null) {
|
||||
Collection collection = mViewModel.getCollection().getValue();
|
||||
String collectionName = collection != null ? collection.name : "";
|
||||
Snackbar.make(
|
||||
view,
|
||||
getString(R.string.snackbar_monster_added_to_collection, monster.name, collectionName),
|
||||
Snackbar.LENGTH_LONG)
|
||||
.show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_monster_added_to_collection, monster.name, collectionName));
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
@@ -219,7 +226,7 @@ public class CollectionDetailFragment extends MCFragment {
|
||||
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();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_collection_added_to_dashboard, collectionName));
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
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.ui.shared.MCFragment;
|
||||
import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
import com.majinnaibu.monstercards.utils.SnackbarHelper;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -150,10 +150,7 @@ public class CollectionsFragment extends MCFragment {
|
||||
public void onComplete() {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(
|
||||
view,
|
||||
getString(R.string.snackbar_collection_created, collection.name),
|
||||
Snackbar.LENGTH_LONG)
|
||||
SnackbarHelper.makeLong(view, getString(R.string.snackbar_collection_created, collection.name))
|
||||
.setAction("View", v -> navigateToCollectionDetail(collection.id))
|
||||
.show();
|
||||
}
|
||||
@@ -164,7 +161,7 @@ public class CollectionsFragment extends MCFragment {
|
||||
Logger.logError("Error creating collection", e);
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_failed_to_create_collection), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_failed_to_create_collection));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,13 +21,13 @@ 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 com.majinnaibu.monstercards.utils.SnackbarHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -69,7 +69,7 @@ public class DashboardFragment extends MCFragment {
|
||||
|
||||
private void loadDashboardMonsters() {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.getDashboardMonsters()
|
||||
mDisposables.add(repository.getDashboardMonstersWithMonster()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(monsters -> mViewModel.setMonsters(monsters), Logger::logError));
|
||||
@@ -82,14 +82,21 @@ public class DashboardFragment extends MCFragment {
|
||||
GridLayoutManager layoutManager = new GridLayoutManager(context, columnCount);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
LiveData<List<Monster>> monsterData = mViewModel.getMonsters();
|
||||
mAdapter = new DashboardRecyclerViewAdapter(monster -> {
|
||||
if (monster != null) {
|
||||
navigateToMonsterDetail(monster);
|
||||
} else {
|
||||
Logger.logError("Can't navigate to MonsterDetailFragment with a null monster");
|
||||
}
|
||||
});
|
||||
LiveData<List<com.majinnaibu.monstercards.models.DashboardMonsterWithMonster>> monsterData = mViewModel.getMonsters();
|
||||
mAdapter = new DashboardRecyclerViewAdapter(
|
||||
item -> {
|
||||
if (item != null && item.monster != null) {
|
||||
navigateToMonsterDetail(item.monster);
|
||||
} else {
|
||||
Logger.logError("Can't navigate to MonsterDetailFragment with a null monster");
|
||||
}
|
||||
},
|
||||
item -> {
|
||||
if (item != null) {
|
||||
showCardOptionsMenu(item);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (monsterData != null) {
|
||||
monsterData.observe(getViewLifecycleOwner(), monsters -> {
|
||||
mAdapter.submitList(monsters);
|
||||
@@ -113,9 +120,9 @@ public class DashboardFragment extends MCFragment {
|
||||
int fromPos = viewHolder.getAdapterPosition();
|
||||
int toPos = target.getAdapterPosition();
|
||||
if (fromPos != RecyclerView.NO_POSITION && toPos != RecyclerView.NO_POSITION) {
|
||||
List<Monster> currentList = new ArrayList<>(mAdapter.getCurrentList());
|
||||
List<com.majinnaibu.monstercards.models.DashboardMonsterWithMonster> currentList = new ArrayList<>(mAdapter.getCurrentList());
|
||||
if (fromPos < currentList.size() && toPos < currentList.size()) {
|
||||
Monster moved = currentList.remove(fromPos);
|
||||
com.majinnaibu.monstercards.models.DashboardMonsterWithMonster moved = currentList.remove(fromPos);
|
||||
currentList.add(toPos, moved);
|
||||
mAdapter.submitList(currentList);
|
||||
}
|
||||
@@ -127,10 +134,10 @@ public class DashboardFragment extends MCFragment {
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
List<Monster> currentList = mAdapter.getCurrentList();
|
||||
List<com.majinnaibu.monstercards.models.DashboardMonsterWithMonster> currentList = mAdapter.getCurrentList();
|
||||
if (position < currentList.size()) {
|
||||
Monster monster = currentList.get(position);
|
||||
removeMonsterFromDashboard(monster);
|
||||
com.majinnaibu.monstercards.models.DashboardMonsterWithMonster item = currentList.get(position);
|
||||
removeDashboardMonsterEntry(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,6 +149,7 @@ public class DashboardFragment extends MCFragment {
|
||||
String[] options = new String[]{
|
||||
getString(R.string.action_add_single_monster),
|
||||
getString(R.string.action_add_collection_option),
|
||||
getString(R.string.action_remove_single_monster),
|
||||
getString(R.string.action_clear_dashboard)
|
||||
};
|
||||
new AlertDialog.Builder(requireContext())
|
||||
@@ -152,6 +160,8 @@ public class DashboardFragment extends MCFragment {
|
||||
} else if (which == 1) {
|
||||
showAddCollectionPicker();
|
||||
} else if (which == 2) {
|
||||
showRemoveMonsterPicker();
|
||||
} else if (which == 3) {
|
||||
clearDashboard();
|
||||
}
|
||||
})
|
||||
@@ -159,6 +169,68 @@ public class DashboardFragment extends MCFragment {
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showRemoveMonsterPicker() {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.getDashboardMonsters()
|
||||
.firstOrError()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(monsters -> {
|
||||
if (monsters.isEmpty()) {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_dashboard_already_empty));
|
||||
}
|
||||
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_remove_single_monster)
|
||||
.setItems(names, (dialog, which) -> {
|
||||
Monster selected = monsters.get(which);
|
||||
removeMonsterFromDashboard(selected);
|
||||
})
|
||||
.setNegativeButton(R.string.dialog_cancel, null)
|
||||
.show();
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
private void showCardOptionsMenu(@NonNull com.majinnaibu.monstercards.models.DashboardMonsterWithMonster item) {
|
||||
String[] options = new String[]{
|
||||
getString(R.string.action_view_details),
|
||||
getString(R.string.action_remove_from_dashboard)
|
||||
};
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle(item.monster.name)
|
||||
.setItems(options, (dialog, which) -> {
|
||||
if (which == 0) {
|
||||
navigateToMonsterDetail(item.monster);
|
||||
} else if (which == 1) {
|
||||
removeDashboardMonsterEntry(item);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.dialog_cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void removeDashboardMonsterEntry(@NonNull com.majinnaibu.monstercards.models.DashboardMonsterWithMonster item) {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.removeDashboardMonsterById(item.dashboardEntry.id)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
SnackbarHelper.makeLong(view, getString(R.string.snackbar_removed_from_dashboard, item.monster.name))
|
||||
.setAction(R.string.action_undo, v -> addMonsterToDashboard(item.monster))
|
||||
.show();
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
private void showAddMonsterPicker() {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.getMonsters()
|
||||
@@ -169,7 +241,7 @@ public class DashboardFragment extends MCFragment {
|
||||
if (monsters.isEmpty()) {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.no_monsters_available), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.no_monsters_available));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -198,7 +270,7 @@ public class DashboardFragment extends MCFragment {
|
||||
if (collections.isEmpty()) {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.no_collections_available), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.no_collections_available));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -225,7 +297,7 @@ public class DashboardFragment extends MCFragment {
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_added_to_dashboard, monster.name), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_added_to_dashboard, monster.name));
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
@@ -238,7 +310,7 @@ public class DashboardFragment extends MCFragment {
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_collection_added_to_dashboard, collection.name), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_collection_added_to_dashboard, collection.name));
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
@@ -248,7 +320,14 @@ public class DashboardFragment extends MCFragment {
|
||||
mDisposables.add(repository.removeMonsterFromDashboard(monster.id)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {}, Logger::logError));
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
SnackbarHelper.makeLong(view, getString(R.string.snackbar_removed_from_dashboard, monster.name))
|
||||
.setAction(R.string.action_undo, v -> addMonsterToDashboard(monster))
|
||||
.show();
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
private void clearDashboard() {
|
||||
@@ -259,7 +338,7 @@ public class DashboardFragment extends MCFragment {
|
||||
.subscribe(() -> {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_dashboard_cleared), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_dashboard_cleared));
|
||||
}
|
||||
}, Logger::logError));
|
||||
}
|
||||
@@ -283,6 +362,9 @@ public class DashboardFragment extends MCFragment {
|
||||
} else if (item.getItemId() == R.id.menu_action_add_collection_option) {
|
||||
showAddCollectionPicker();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_action_remove_single_monster) {
|
||||
showRemoveMonsterPicker();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_action_clear_dashboard) {
|
||||
clearDashboard();
|
||||
return true;
|
||||
|
||||
@@ -18,29 +18,44 @@ import com.majinnaibu.monstercards.data.enums.ChallengeRating;
|
||||
import com.majinnaibu.monstercards.data.enums.ProficiencyType;
|
||||
import com.majinnaibu.monstercards.databinding.CardMonsterBinding;
|
||||
import com.majinnaibu.monstercards.helpers.CommonMarkHelper;
|
||||
import com.majinnaibu.monstercards.models.DashboardMonsterWithMonster;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.models.Trait;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class DashboardRecyclerViewAdapter extends ListAdapter<Monster, DashboardRecyclerViewAdapter.ViewHolder> {
|
||||
private static final DiffUtil.ItemCallback<Monster> DIFF_CALLBACK = new DiffUtil.ItemCallback<Monster>() {
|
||||
public class DashboardRecyclerViewAdapter extends ListAdapter<DashboardMonsterWithMonster, DashboardRecyclerViewAdapter.ViewHolder> {
|
||||
private static final DiffUtil.ItemCallback<DashboardMonsterWithMonster> DIFF_CALLBACK = new DiffUtil.ItemCallback<DashboardMonsterWithMonster>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) {
|
||||
return oldItem.id.equals(newItem.id);
|
||||
public boolean areItemsTheSame(@NonNull DashboardMonsterWithMonster oldItem, @NonNull DashboardMonsterWithMonster newItem) {
|
||||
if (oldItem.dashboardEntry != null && newItem.dashboardEntry != null) {
|
||||
return oldItem.dashboardEntry.id == newItem.dashboardEntry.id;
|
||||
}
|
||||
return oldItem.monster.id.equals(newItem.monster.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull Monster oldItem, @NonNull Monster newItem) {
|
||||
return oldItem.equals(newItem);
|
||||
public boolean areContentsTheSame(@NonNull DashboardMonsterWithMonster oldItem, @NonNull DashboardMonsterWithMonster newItem) {
|
||||
if (oldItem.dashboardEntry != null && newItem.dashboardEntry != null) {
|
||||
return oldItem.dashboardEntry.id == newItem.dashboardEntry.id
|
||||
&& oldItem.dashboardEntry.ordinal == newItem.dashboardEntry.ordinal
|
||||
&& (oldItem.monster != null && oldItem.monster.equals(newItem.monster));
|
||||
}
|
||||
return (oldItem.monster != null && oldItem.monster.equals(newItem.monster));
|
||||
}
|
||||
};
|
||||
private final ItemCallback mOnClick;
|
||||
private final ItemCallback mOnLongClick;
|
||||
|
||||
public DashboardRecyclerViewAdapter(ItemCallback onClick) {
|
||||
public DashboardRecyclerViewAdapter(ItemCallback onClick, ItemCallback onLongClick) {
|
||||
super(DIFF_CALLBACK);
|
||||
mOnClick = onClick;
|
||||
mOnLongClick = onLongClick;
|
||||
}
|
||||
|
||||
public DashboardRecyclerViewAdapter(ItemCallback onClick) {
|
||||
this(onClick, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -52,7 +67,9 @@ public class DashboardRecyclerViewAdapter extends ListAdapter<Monster, Dashboard
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Logger.logUnimplementedMethod();
|
||||
Monster monster = getItem(position);
|
||||
DashboardMonsterWithMonster item = getItem(position);
|
||||
Monster monster = item.monster;
|
||||
holder.item = item;
|
||||
holder.monster = monster;
|
||||
holder.name.setText(monster.name);
|
||||
holder.meta.setText(monster.getMeta());
|
||||
@@ -120,16 +137,25 @@ public class DashboardRecyclerViewAdapter extends ListAdapter<Monster, Dashboard
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (mOnClick != null) {
|
||||
mOnClick.onItemCallback(holder.monster);
|
||||
mOnClick.onItemCallback(holder.item);
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
if (mOnLongClick != null) {
|
||||
mOnLongClick.onItemCallback(holder.item);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public interface ItemCallback {
|
||||
void onItemCallback(Monster monster);
|
||||
void onItemCallback(DashboardMonsterWithMonster item);
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public DashboardMonsterWithMonster item;
|
||||
public final TextView name;
|
||||
public final TextView meta;
|
||||
public final View action1Group;
|
||||
|
||||
@@ -4,23 +4,23 @@ import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.models.DashboardMonsterWithMonster;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DashboardViewModel extends ViewModel {
|
||||
private final MutableLiveData<List<Monster>> mMonsters;
|
||||
private final MutableLiveData<List<DashboardMonsterWithMonster>> mMonsters;
|
||||
|
||||
public DashboardViewModel() {
|
||||
mMonsters = new MutableLiveData<>(new ArrayList<>());
|
||||
}
|
||||
|
||||
public LiveData<List<Monster>> getMonsters() {
|
||||
public LiveData<List<DashboardMonsterWithMonster>> getMonsters() {
|
||||
return mMonsters;
|
||||
}
|
||||
|
||||
public void setMonsters(List<Monster> monsters) {
|
||||
public void setMonsters(List<DashboardMonsterWithMonster> monsters) {
|
||||
mMonsters.setValue(monsters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
import com.majinnaibu.monstercards.data.enums.StringType;
|
||||
@@ -26,6 +25,7 @@ import com.majinnaibu.monstercards.data.enums.TraitType;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
import com.majinnaibu.monstercards.utils.SnackbarHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -124,8 +124,7 @@ public class EditMonsterFragment extends MCFragment {
|
||||
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
|
||||
Logger.logError("Error saving monster", e);
|
||||
assert view != null;
|
||||
Snackbar.make(view, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG)
|
||||
.setAction("Action", null).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_failed_to_create_monster));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.majinnaibu.monstercards.MainActivity;
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
@@ -35,6 +34,7 @@ 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;
|
||||
import com.majinnaibu.monstercards.utils.SnackbarHelper;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -206,7 +206,7 @@ public class LibraryFragment extends MCFragment {
|
||||
Logger.logError("Error creating monster", e);
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_failed_to_create_monster));
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -31,7 +31,6 @@ import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
import com.majinnaibu.monstercards.helpers.CommonMarkHelper;
|
||||
@@ -40,6 +39,7 @@ 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 com.majinnaibu.monstercards.utils.SnackbarHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@@ -252,7 +252,7 @@ public class MonsterDetailFragment extends MCFragment {
|
||||
Logger.logError("Failed to share monster", e);
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, R.string.failed_to_share_monster, Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, R.string.failed_to_share_monster);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,7 @@ public class MonsterDetailFragment extends MCFragment {
|
||||
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();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_added_to_dashboard, monsterName));
|
||||
}
|
||||
}, Logger::logError);
|
||||
}
|
||||
@@ -291,7 +291,7 @@ public class MonsterDetailFragment extends MCFragment {
|
||||
if (collections.isEmpty()) {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, getString(R.string.no_collections_available), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.no_collections_available));
|
||||
}
|
||||
dispose();
|
||||
return;
|
||||
@@ -330,11 +330,7 @@ public class MonsterDetailFragment extends MCFragment {
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
String monsterName = mViewModel.getName().getValue();
|
||||
Snackbar.make(
|
||||
view,
|
||||
getString(R.string.snackbar_monster_added_to_collection, monsterName, collection.name),
|
||||
Snackbar.LENGTH_LONG)
|
||||
.show();
|
||||
SnackbarHelper.showLong(view, getString(R.string.snackbar_monster_added_to_collection, monsterName, collection.name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.majinnaibu.monstercards.MonsterCardsApplication;
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
@@ -34,6 +33,7 @@ import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.ui.library.LibraryFragmentDirections;
|
||||
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
import com.majinnaibu.monstercards.utils.SnackbarHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@@ -174,7 +174,7 @@ public class MonsterImportFragment extends MCFragment {
|
||||
@Override
|
||||
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
|
||||
Logger.logError("Error creating monster", e);
|
||||
Snackbar.make(mHolder.root, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(mHolder.root, getString(R.string.snackbar_failed_to_create_monster));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -19,11 +19,11 @@ import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.majinnaibu.monstercards.MonsterCardsApplication;
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
import com.majinnaibu.monstercards.utils.SnackbarHelper;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -87,7 +87,7 @@ public class MCFragment extends Fragment {
|
||||
Logger.logError("Failed to launch document creation picker", e);
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, R.string.snackbar_export_failed, Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, R.string.snackbar_export_failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,14 +103,14 @@ public class MCFragment extends Fragment {
|
||||
if (view != null) {
|
||||
String fileName = getFileNameFromUri(context, uri);
|
||||
String message = getString(R.string.snackbar_export_success, fileName);
|
||||
Snackbar.make(view, message, Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, message);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.logError("Failed to write export content to URI", e);
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, R.string.snackbar_export_failed, Snackbar.LENGTH_LONG).show();
|
||||
SnackbarHelper.showLong(view, R.string.snackbar_export_failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.majinnaibu.monstercards.utils;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
/**
|
||||
* Centralized helper for managing Snackbar notifications and durations across the app.
|
||||
*/
|
||||
public class SnackbarHelper {
|
||||
// Configurable default durations in milliseconds
|
||||
public static int DEFAULT_SHORT_DURATION = 2500; // 2.5 seconds
|
||||
public static int DEFAULT_LONG_DURATION = 4000; // 4.0 seconds (e.g. for Undo or action messages)
|
||||
|
||||
public static Snackbar makeShort(@NonNull View view, @StringRes int resId) {
|
||||
return make(view, view.getContext().getString(resId), DEFAULT_SHORT_DURATION);
|
||||
}
|
||||
|
||||
public static Snackbar makeShort(@NonNull View view, @NonNull CharSequence message) {
|
||||
return make(view, message, DEFAULT_SHORT_DURATION);
|
||||
}
|
||||
|
||||
public static Snackbar makeLong(@NonNull View view, @StringRes int resId) {
|
||||
return make(view, view.getContext().getString(resId), DEFAULT_LONG_DURATION);
|
||||
}
|
||||
|
||||
public static Snackbar makeLong(@NonNull View view, @NonNull CharSequence message) {
|
||||
return make(view, message, DEFAULT_LONG_DURATION);
|
||||
}
|
||||
|
||||
public static Snackbar make(@NonNull View view, @NonNull CharSequence message, int durationMs) {
|
||||
return Snackbar.make(view, message, durationMs);
|
||||
}
|
||||
|
||||
public static void showShort(@NonNull View view, @StringRes int resId) {
|
||||
makeShort(view, resId).show();
|
||||
}
|
||||
|
||||
public static void showShort(@NonNull View view, @NonNull CharSequence message) {
|
||||
makeShort(view, message).show();
|
||||
}
|
||||
|
||||
public static void showLong(@NonNull View view, @StringRes int resId) {
|
||||
makeLong(view, resId).show();
|
||||
}
|
||||
|
||||
public static void showLong(@NonNull View view, @NonNull CharSequence message) {
|
||||
makeLong(view, message).show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.majinnaibu.monstercards.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
/**
|
||||
* Centralized helper for managing Toast notifications and durations across the app.
|
||||
*/
|
||||
public class ToastHelper {
|
||||
// Configurable default durations
|
||||
public static int DEFAULT_SHORT_DURATION = Toast.LENGTH_SHORT; // ~2.0 seconds
|
||||
public static int DEFAULT_LONG_DURATION = Toast.LENGTH_LONG; // ~3.5 seconds
|
||||
|
||||
private static Toast sCurrentToast;
|
||||
|
||||
public static void showShort(@NonNull Context context, @StringRes int resId) {
|
||||
show(context, context.getString(resId), DEFAULT_SHORT_DURATION);
|
||||
}
|
||||
|
||||
public static void showShort(@NonNull Context context, @NonNull String message) {
|
||||
show(context, message, DEFAULT_SHORT_DURATION);
|
||||
}
|
||||
|
||||
public static void showLong(@NonNull Context context, @StringRes int resId) {
|
||||
show(context, context.getString(resId), DEFAULT_LONG_DURATION);
|
||||
}
|
||||
|
||||
public static void showLong(@NonNull Context context, @NonNull String message) {
|
||||
show(context, message, DEFAULT_LONG_DURATION);
|
||||
}
|
||||
|
||||
public static void show(@NonNull Context context, @NonNull String message, int duration) {
|
||||
if (sCurrentToast != null) {
|
||||
sCurrentToast.cancel();
|
||||
}
|
||||
sCurrentToast = Toast.makeText(context.getApplicationContext(), message, duration);
|
||||
sCurrentToast.show();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user