Library refactoring.
This commit is contained in:
		| @@ -1,7 +1,6 @@ | ||||
| package com.majinnaibu.monstercards; | ||||
|  | ||||
| import android.app.Application; | ||||
| import android.content.Context; | ||||
| import android.content.res.Configuration; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| @@ -14,17 +13,12 @@ import com.majinnaibu.monstercards.init.FlipperInitializer; | ||||
|  | ||||
| public class MonsterCardsApplication extends Application { | ||||
|  | ||||
|     private AppDatabase m_db; | ||||
|     private MonsterRepository m_monsterLibraryRepository; | ||||
|  | ||||
|     public MonsterRepository getMonsterRepository() { | ||||
|         return m_monsterLibraryRepository; | ||||
|     } | ||||
|  | ||||
|     public static MonsterCardsApplication getInstance(Context context) { | ||||
|         return (MonsterCardsApplication) context.getApplicationContext(); | ||||
|     } | ||||
|  | ||||
|     public MonsterCardsApplication() { | ||||
|     } | ||||
|  | ||||
| @@ -38,9 +32,11 @@ public class MonsterCardsApplication extends Application { | ||||
|  | ||||
|         FlipperInitializer.init(this); | ||||
|  | ||||
|         m_db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "monsters") | ||||
|         //                .fallbackToDestructiveMigration() | ||||
|         AppDatabase m_db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "monsters") | ||||
|                 .addMigrations(MIGRATION_1_2) | ||||
|                 .fallbackToDestructiveMigrationOnDowngrade() | ||||
| //                .fallbackToDestructiveMigration() | ||||
|                 .build(); | ||||
|         m_monsterLibraryRepository = new MonsterRepository(m_db); | ||||
|     } | ||||
|   | ||||
| @@ -18,13 +18,13 @@ import com.majinnaibu.monstercards.R; | ||||
| import com.majinnaibu.monstercards.data.MonsterRepository; | ||||
| import com.majinnaibu.monstercards.models.Monster; | ||||
| import com.majinnaibu.monstercards.ui.MCFragment; | ||||
| import com.majinnaibu.monstercards.ui.MonsterListRecyclerViewAdapter; | ||||
| import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback; | ||||
| import com.majinnaibu.monstercards.utils.Logger; | ||||
|  | ||||
| import java.util.UUID; | ||||
|  | ||||
| import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; | ||||
| import io.reactivex.rxjava3.observers.DisposableCompletableObserver; | ||||
| import io.reactivex.rxjava3.schedulers.Schedulers; | ||||
|  | ||||
| public class LibraryFragment extends MCFragment { | ||||
| @@ -46,25 +46,28 @@ public class LibraryFragment extends MCFragment { | ||||
|  | ||||
|     private void setupRecyclerView(@NonNull RecyclerView recyclerView) { | ||||
|         MonsterRepository repository = this.getMonsterRepository(); | ||||
|         boolean mTwoPane = false; | ||||
|         MonsterListRecyclerViewAdapter adapter = new MonsterListRecyclerViewAdapter( | ||||
|                 this, | ||||
|                 getContext(), | ||||
|                 repository.getMonsters(), | ||||
|                 (monster) -> { | ||||
|                     repository | ||||
|                             .deleteMonster(monster) | ||||
|                             .subscribeOn(Schedulers.io()) | ||||
|                             .observeOn(AndroidSchedulers.mainThread()) | ||||
|                             .subscribe(() -> { | ||||
|                 (monster) -> navigateToMonsterDetail(monster.id), | ||||
|                 (monster) -> repository | ||||
|                         .deleteMonster(monster) | ||||
|                         .subscribeOn(Schedulers.io()) | ||||
|                         .observeOn(AndroidSchedulers.mainThread()) | ||||
|                         .subscribe(new DisposableCompletableObserver() { | ||||
|                             @Override | ||||
|                             public void onComplete() { | ||||
|                                 Logger.logDebug("deleted"); | ||||
|                             }, Logger::logError); | ||||
|                 }, | ||||
|                 mTwoPane); | ||||
|                             } | ||||
|  | ||||
|                             @Override | ||||
|                             public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) { | ||||
|                                 Logger.logError(e); | ||||
|                             } | ||||
|                         })); | ||||
|         recyclerView.setAdapter(adapter); | ||||
|         recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(getContext(), (position) -> { | ||||
|             adapter.deleteItem(position); | ||||
|         })); | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(requireContext(), adapter::deleteItem)); | ||||
|         itemTouchHelper.attachToRecyclerView(recyclerView); | ||||
|     } | ||||
|  | ||||
| @@ -76,26 +79,36 @@ public class LibraryFragment extends MCFragment { | ||||
|             repository.addMonster(monster) | ||||
|                     .subscribeOn(Schedulers.io()) | ||||
|                     .observeOn(AndroidSchedulers.mainThread()) | ||||
|                     .subscribe(() -> { | ||||
|                         Snackbar.make( | ||||
|                                 getView(), | ||||
|                                 String.format("%s created", monster.name), | ||||
|                                 Snackbar.LENGTH_LONG) | ||||
|                                 .setAction("Action", (_view) -> { | ||||
|                                     navigateToMonsterDetail(monster.id); | ||||
|                                 }) | ||||
|                                 .show(); | ||||
|                     }, throwable -> { | ||||
|                         Logger.logError("Error creating monster", throwable); | ||||
|                         Snackbar.make(getView(), "Failed to create monster", Snackbar.LENGTH_LONG) | ||||
|                                 .setAction("Action", null).show(); | ||||
|                     }); | ||||
|         }); | ||||
|                     .subscribe( | ||||
|                             new DisposableCompletableObserver() { | ||||
|                                 @Override | ||||
|                                 public void onComplete() { | ||||
|                                     View view = getView(); | ||||
|                                     assert view != null; | ||||
|                                     Snackbar.make( | ||||
|                                             view, | ||||
|                                             String.format("%s created", monster.name), | ||||
|                                             Snackbar.LENGTH_LONG) | ||||
|                                             .setAction("Action", (_view) -> navigateToMonsterDetail(monster.id)) | ||||
|                                             .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, "Failed to create monster", Snackbar.LENGTH_LONG) | ||||
|                                             .setAction("Action", null).show(); | ||||
|                                 } | ||||
|                             }); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     protected void navigateToMonsterDetail(UUID monsterId) { | ||||
|         NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString()); | ||||
|         Navigation.findNavController(getView()).navigate(action); | ||||
|         View view = getView(); | ||||
|         assert view != null; | ||||
|         Navigation.findNavController(view).navigate(action); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package com.majinnaibu.monstercards.ui; | ||||
| package com.majinnaibu.monstercards.ui.library; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.view.LayoutInflater; | ||||
| @@ -6,87 +6,67 @@ import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.navigation.NavDirections; | ||||
| import androidx.navigation.Navigation; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| import com.majinnaibu.monstercards.R; | ||||
| import com.majinnaibu.monstercards.models.Monster; | ||||
| import com.majinnaibu.monstercards.ui.library.LibraryFragment; | ||||
| import com.majinnaibu.monstercards.ui.library.LibraryFragmentDirections; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; | ||||
| import io.reactivex.rxjava3.core.Flowable; | ||||
| import io.reactivex.rxjava3.disposables.Disposable; | ||||
| import io.reactivex.rxjava3.schedulers.Schedulers; | ||||
| 
 | ||||
| public class MonsterListRecyclerViewAdapter extends RecyclerView.Adapter<MonsterListRecyclerViewAdapter.ViewHolder> { | ||||
|     public interface ItemCallback { | ||||
|         void onItem(Monster monster); | ||||
|         void onItemCallback(Monster monster); | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Replace SimpleItemRecyclerViewAdapter with something better like MonsterListRecyclerViewAdapter that can be reused in search | ||||
| 
 | ||||
|     private final LibraryFragment mParentActivity; | ||||
|     private List<Monster> mValues; | ||||
|     private final boolean mTwoPane; | ||||
|     private final Context mContext; | ||||
|     private final ItemCallback mOnDelete; | ||||
|     private final ItemCallback mOnClick; | ||||
|     private Disposable mDisposable; | ||||
|     private final Flowable<List<Monster>> mItemsObservable; | ||||
|     private final View.OnClickListener mOnClickListener = new View.OnClickListener() { | ||||
|         @Override | ||||
|         public void onClick(View view) { | ||||
|             Monster monster = (Monster) view.getTag(); | ||||
|             // TODO: I would like to call navigateToMonsterDetail(item.id) here | ||||
|             if (mTwoPane) { | ||||
|                 // TODO: Figure out how to navigate to a MonsterDetailFragment when in two pane view. | ||||
| //                    Bundle arguments = new Bundle(); | ||||
| //                    arguments.putString(ItemDetailFragment.ARG_ITEM_ID, monster.id.toString()); | ||||
| //                    ItemDetailFragment fragment = new ItemDetailFragment(); | ||||
| //                    fragment.setArguments(arguments); | ||||
| //                    mParentActivity.getSupportFragmentManager().beginTransaction() | ||||
| //                            .replace(R.id.item_detail_container, fragment) | ||||
| //                            .commit(); | ||||
|             } else { | ||||
|                 NavDirections action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monster.id.toString()); | ||||
|                 Navigation.findNavController(view).navigate(action); | ||||
|             if (mOnClick != null) { | ||||
|                 mOnClick.onItemCallback(monster); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     public MonsterListRecyclerViewAdapter(LibraryFragment parent, | ||||
|     public MonsterListRecyclerViewAdapter(Context context, | ||||
|                                           Flowable<List<Monster>> itemsObservable, | ||||
|                                           ItemCallback onDelete, | ||||
|                                           boolean twoPane) { | ||||
|                                           ItemCallback onClick, | ||||
|                                           ItemCallback onDelete) { | ||||
|         mItemsObservable = itemsObservable; | ||||
|         mValues = new ArrayList<>(); | ||||
|         mParentActivity = parent; | ||||
|         mTwoPane = twoPane; | ||||
|         mContext = parent.getContext(); | ||||
|         mContext = context; | ||||
|         mOnDelete = onDelete; | ||||
| 
 | ||||
|         itemsObservable | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(monsters -> { | ||||
|                     mValues = monsters; | ||||
|                     notifyDataSetChanged(); | ||||
|                 }); | ||||
|         mOnClick = onClick; | ||||
|         mDisposable = null; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||||
|     @NonNull | ||||
|     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||
|         View view = LayoutInflater.from(parent.getContext()) | ||||
|                 .inflate(R.layout.monster_list_content, parent, false); | ||||
|         return new ViewHolder(view); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onBindViewHolder(final ViewHolder holder, int position) { | ||||
|         holder.mIdView.setText(mValues.get(position).id.toString().substring(0, 6)); | ||||
|         holder.mContentView.setText(mValues.get(position).name); | ||||
| 
 | ||||
|         holder.itemView.setTag(mValues.get(position)); | ||||
|     public void onBindViewHolder(final @NonNull ViewHolder holder, int position) { | ||||
|         Monster monster = mValues.get(position); | ||||
|         holder.mIdView.setText(monster.id.toString().substring(0, 6)); | ||||
|         holder.mContentView.setText(monster.name); | ||||
|         holder.itemView.setTag(monster); | ||||
|         holder.itemView.setOnClickListener(mOnClickListener); | ||||
|     } | ||||
| 
 | ||||
| @@ -99,7 +79,7 @@ public class MonsterListRecyclerViewAdapter extends RecyclerView.Adapter<Monster | ||||
|         return mContext; | ||||
|     } | ||||
| 
 | ||||
|     class ViewHolder extends RecyclerView.ViewHolder { | ||||
|     static class ViewHolder extends RecyclerView.ViewHolder { | ||||
|         final TextView mIdView; | ||||
|         final TextView mContentView; | ||||
| 
 | ||||
| @@ -110,10 +90,29 @@ public class MonsterListRecyclerViewAdapter extends RecyclerView.Adapter<Monster | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { | ||||
|         super.onAttachedToRecyclerView(recyclerView); | ||||
|         // TODO: consider moving this subscription out of the adapter and make the subscriber call setItems on the adapter | ||||
|         mDisposable = mItemsObservable | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(monsters -> { | ||||
|                     mValues = monsters; | ||||
|                     notifyDataSetChanged(); | ||||
|                 }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { | ||||
|         super.onDetachedFromRecyclerView(recyclerView); | ||||
|         mDisposable.dispose(); | ||||
|     } | ||||
| 
 | ||||
|     public void deleteItem(int position) { | ||||
|         if (mOnDelete != null) { | ||||
|             Monster monster = mValues.get(position); | ||||
|             mOnDelete.onItem(monster); | ||||
|             mOnDelete.onItemCallback(monster); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user