public interface MVP_Main {
/**
* Required View methods available to Presenter.
* A passive layer, responsible to show data
* and receive user interactions
*/
interface RequiredViewOps {
// View operations permitted to Presenter
Context getAppContext();
Context getActivityContext();
void notifyItemInserted(int layoutPosition);
void notifyItemRangeChanged(int positionStart, int itemCount);
}
/**
* Operations offered to View to communicate with Presenter.
* Processes user interactions, sends data requests to Model, etc.
*/
interface ProvidedPresenterOps {
// Presenter operations permitted to View
void clickNewNote(EditText editText);
// setting up recycler adapter
int getNotesCount();
NotesViewHolder createViewHolder(ViewGroup parent, int viewType);
void bindViewHolder(NotesViewHolder holder, int position);
}
/**
* Required Presenter methods available to Model.
*/
interface RequiredPresenterOps {
// Presenter operations permitted to Model
Context getAppContext();
Context getActivityContext();
}
/**
* Operations offered to Model to communicate with Presenter
* Handles all data business logic.
*/
interface ProvidedModelOps {
// Model operations permitted to Presenter
int getNotesCount();
Note getNote(int position);
int insertNote(Note note);
boolean loadData();
}
}
public class MainActivity
extends AppCompatActivity
implements View.OnClickListener, MVP_Main.RequiredViewOps {
private MVP_Main.ProvidedPresenterOps mPresenter;
private EditText mTextNewNote;
private ListNotes mListAdapter;
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.fab:{
// Adds a new note
mPresenter.clickNewNote(mTextNewNote);
}
}
}
@Override
public Context getActivityContext() {
return this;
}
@Override
public Context getAppContext() {
return getApplicationContext();
}
// Notify the RecyclerAdapter that a new item was inserted
@Override
public void notifyItemInserted(int adapterPos) {
mListAdapter.notifyItemInserted(adapterPos);
}
// notify the RecyclerAdapter that items has changed
@Override
public void notifyItemRangeChanged(int positionStart, int itemCount){
mListAdapter.notifyItemRangeChanged(positionStart, itemCount);
}
// notify the RecyclerAdapter that data set has changed
@Override
public void notifyDataSetChanged() {
mListAdapter.notifyDataSetChanged();
}
// Recycler adapter
// This class could have their own Presenter, but for the sake of
// simplicity, will use only one Presenter.
// The adapter is passive and all the processing occurs
// in the Presenter layer.
private class ListNotes extends RecyclerView.Adapter<NotesViewHolder>
{
@Override
public int getItemCount() {
return mPresenter.getNotesCount();
}
@Override
public NotesViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return mPresenter.createViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(NotesViewHolder holder, int position) {
mPresenter.bindViewHolder(holder, position);
}
}
}
public class MainPresenter implements MVP_Main.ProvidedPresenterOps, MVP_Main.RequiredPresenterOps {
// View reference. We use as a WeakReference
// because the Activity could be destroyed at any time
// and we don't want to create a memory leak
private WeakReference<MVP_Main.RequiredViewOps> mView;
// Model reference
private MVP_Main.ProvidedModelOps mModel;
/**
* Presenter Constructor
* @param view MainActivity
*/
public MainPresenter(MVP_Main.RequiredViewOps view) {
mView = new WeakReference<>(view);
}
/**
* Return the View reference.
* Throw an exception if the View is unavailable.
*/
private MVP_Main.RequiredViewOps getView() throws NullPointerException{
if ( mView != null )
return mView.get();
else
throw new NullPointerException("View is unavailable");
}
/**
* Retrieves total Notes count from Model
* @return Notes list size
*/
@Override
public int getNotesCount() {
return mModel.getNotesCount();
}
/**
* Creates the RecyclerView holder and setup its view
* @param parent Recycler viewGroup
* @param viewType Holder type
* @return Recycler ViewHolder
*/
@Override
public NotesViewHolder createViewHolder(ViewGroup parent, int viewType) {
NotesViewHolder viewHolder;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View viewTaskRow = inflater.inflate(R.layout.holder_notes, parent, false);
viewHolder = new NotesViewHolder(viewTaskRow);
return viewHolder;
}
/**
* Binds ViewHolder with RecyclerView
* @param holder Holder to bind
* @param position Position on Recycler adapter
*/
@Override
public void bindViewHolder(final NotesViewHolder holder, int position) {
final Note note = mModel.getNote(position);
holder.text.setText( note.getText() );
holder.date.setText( note.getDate() );
holder.btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clickDeleteNote(note, holder.getAdapterPosition(), holder.getLayoutPosition());
}
});
}
/**
* @return Application context
*/
@Override
public Context getAppContext() {
try {
return getView().getAppContext();
} catch (NullPointerException e) {
return null;
}
}
/**
* @return Activity context
*/
@Override
public Context getActivityContext() {
try {
return getView().getActivityContext();
} catch (NullPointerException e) {
return null;
}
}
/**
* Called by View when user clicks on new Note button.
* Creates a Note with text typed by the user and asks
* Model to insert it in DB.
* @param editText EditText with text typed by user
*/
@Override
public void clickNewNote(final EditText editText) {
getView().showProgress();
final String noteText = editText.getText().toString();
if ( !noteText.isEmpty() ) {
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... params) {
// Inserts note in Model, returning adapter position
return mModel.insertNote(makeNote(noteText));
}
@Override
protected void onPostExecute(Integer adapterPosition) {
try {
if (adapterPosition > -1) {
// Note inserted
getView().clearEditText();
getView().notifyItemInserted(adapterPosition + 1);
getView().notifyItemRangeChanged(adapterPosition, mModel.getNotesCount());
} else {
// Informs about error
getView().hideProgress();
getView().showToast(makeToast("Error creating note [" + noteText + "]"));
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}
}.execute();
} else {
try {
getView().showToast(makeToast("Cannot add a blank note!"));
} catch (NullPointerException e) {
e.printStackTrace();
}
}
}
/**
* Creates a Note object with given text
* @param noteText String with Note text
* @return A Note object
*/
public Note makeNote(String noteText) {
Note note = new Note();
note.setText( noteText );
note.setDate(getDate());
return note;
}
}
public class MainModel implements MVP_Main.ProvidedModelOps {
// Presenter reference
private MVP_Main.RequiredPresenterOps mPresenter;
private DAO mDAO;
// Recycler data
public ArrayList<Note> mNotes;
/**
* Main constructor, called by Activity during MVP setup
* @param presenter Presenter instance
*/
public MainModel(MVP_Main.RequiredPresenterOps presenter) {
this.mPresenter = presenter;
mDAO = new DAO( mPresenter.getAppContext() );
}
/**
* Inserts a note on DB
* @param note Note to insert
* @return Note's position on ArrayList
*/
@Override
public int insertNote(Note note) {
Note insertedNote = mDAO.insertNote(note);
if ( insertedNote != null ) {
loadData();
return getNotePosition(insertedNote);
}
return -1;
}
/**
* Loads all Data, getting notes from DB
* @return true with success
*/
@Override
public boolean loadData() {
mNotes = mDAO.getAllNotes();
return mNotes != null;
}
/**
* Gets a specific note from notes list using its array position
* @param position Array position
* @return Note from list
*/
@Override
public Note getNote(int position) {
return mNotes.get(position);
}
/**
* Get ArrayList size
* @return ArrayList size
*/
@Override
public int getNotesCount() {
if ( mNotes != null )
return mNotes.size();
return 0;
}
}
/**
* Setup Model View Presenter pattern
*/
private void setupMVP() {
// Create the Presenter
MainPresenter presenter = new MainPresenter(this);
// Create the Model
MainModel model = new MainModel(presenter);
// Set Presenter model
presenter.setModel(model);
// Set the Presenter as a interface
mPresenter = presenter;
}
public class MainActivity
extends AppCompatActivity
implements View.OnClickListener, MVP_Main.RequiredViewOps
{
// …
private void setupMVP() {
// Check if StateMaintainer has been created
if (mStateMaintainer.firstTimeIn()) {
// Create the Presenter
MainPresenter presenter = new MainPresenter(this);
// Create the Model
MainModel model = new MainModel(presenter);
// Set Presenter model
presenter.setModel(model);
// Add Presenter and Model to StateMaintainer
mStateMaintainer.put(presenter);
mStateMaintainer.put(model);
// Set the Presenter as a interface
// To limit the communication with it
mPresenter = presenter;
}
// get the Presenter from StateMaintainer
else {
// Get the Presenter
mPresenter = mStateMaintainer.get(MainPresenter.class.getName());
// Updated the View in Presenter
mPresenter.setView(this);
}
}
// …
}