diff --git a/reactiveandroid/src/main/java/com/reactiveandroid/query/api/Pagination.java b/reactiveandroid/src/main/java/com/reactiveandroid/query/api/Pagination.java new file mode 100644 index 0000000..d218a12 --- /dev/null +++ b/reactiveandroid/src/main/java/com/reactiveandroid/query/api/Pagination.java @@ -0,0 +1,65 @@ +package com.reactiveandroid.query.api; + +//pull request - bendothall +//Pagination API + +import android.util.Log; + +import com.reactiveandroid.query.QueryBase; +import com.reactiveandroid.query.Select; + +import java.util.ArrayList; +import java.util.List; + +public class Pagination extends QueryBase { + + private Pagination() { + super(null, null); + } + + public static int getTableRowCount(Class table) { + + if (table == null) { + throw new IllegalArgumentException("Database Table referenced not found."); + } + + return Select.from(table).count(); + } + + + public static List loadPaginationData( + String tableColumn, + int offset, + int limit, + String orderBy, + Class table + ){ + + if (tableColumn == null) { + throw new IllegalArgumentException("Database Table must be set"); + } + + if (limit == 0) { + throw new IllegalArgumentException("Limit cannot be set to 0"); + } + + if (orderBy == null || !(orderBy.contains("DESC") || orderBy.contains("ASC")) ) { + throw new IllegalArgumentException("Order must be set to DESC or ASC " + orderBy); + } + + if (table == null) { + throw new IllegalArgumentException("Database Table Class referenced not found."); + } + + List result = new ArrayList<>(); + result = Select.from(table) + .orderBy(tableColumn + " " + orderBy) + .limit(limit) + .offset(offset) + .fetch(); + + return result; + } + + +} diff --git a/reactiveandroid/src/main/java/com/reactiveandroid/query/api/Transactions.java b/reactiveandroid/src/main/java/com/reactiveandroid/query/api/Transactions.java new file mode 100644 index 0000000..e8598d9 --- /dev/null +++ b/reactiveandroid/src/main/java/com/reactiveandroid/query/api/Transactions.java @@ -0,0 +1,27 @@ +package com.reactiveandroid.query.api; + +import com.reactiveandroid.ReActiveAndroid; + +public final class Transactions { + + public static void BeginTransactions(Class databaseClass) { + + if (databaseClass == null) { + throw new IllegalArgumentException("Database Class referenced not found."); + } + + ReActiveAndroid.getDatabase(databaseClass).beginTransaction(); + + } + + public static void EndTransactions(Class databaseClass) { + + if (databaseClass == null) { + throw new IllegalArgumentException("Database Class referenced not found."); + } + + ReActiveAndroid.getDatabase(databaseClass).getWritableDatabase().setTransactionSuccessful(); + ReActiveAndroid.getDatabase(databaseClass).endTransaction(); + } + +} diff --git a/sample-app/src/main/AndroidManifest.xml b/sample-app/src/main/AndroidManifest.xml index 292adee..8363ad5 100644 --- a/sample-app/src/main/AndroidManifest.xml +++ b/sample-app/src/main/AndroidManifest.xml @@ -21,6 +21,16 @@ + + + + + + + + \ No newline at end of file diff --git a/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/MainActivity.java b/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/MainActivity.java index c4ec75b..3bea60f 100644 --- a/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/MainActivity.java +++ b/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/MainActivity.java @@ -1,5 +1,6 @@ package com.reactiveandroid.sample.ui.activities; +import android.content.Intent; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v7.widget.RecyclerView; @@ -7,6 +8,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.widget.Toast; import com.arellomobile.mvp.MvpAppCompatActivity; import com.arellomobile.mvp.presenter.InjectPresenter; @@ -74,11 +76,24 @@ public void openNoteDetailsScreen(long noteId) { startActivity(NoteDetailsActivity.buildIntent(this, noteId)); } + //pull request - bendothall + //Transactions API + public void openTransactionsAPIActivityScreen() { + startActivity(new Intent(MainActivity.this, TransactionsAPIActivity.class)); + } + + //pull request - bendothall + //Pagination API + public void openPaginationAPIActivityScreen() { + startActivity(new Intent(MainActivity.this, PaginationAPIActivity.class)); + } + @Override public void openFoldersEditScreen() { startActivity(FoldersEditActivity.buildIntent(this)); } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); @@ -95,6 +110,18 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.delete_all_notes: presenter.onDeleteAllNotesClicked(); break; + + //pull request - bendothall + //Transactions API menu item action + case R.id.transaction_api: + openTransactionsAPIActivityScreen(); + break; + + //pull request - bendothall + //Transactions API menu item action + case R.id.pagination_api: + openPaginationAPIActivityScreen(); + break; } return super.onOptionsItemSelected(item); } diff --git a/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/PaginationAPIActivity.java b/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/PaginationAPIActivity.java new file mode 100644 index 0000000..433e6ca --- /dev/null +++ b/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/PaginationAPIActivity.java @@ -0,0 +1,355 @@ +package com.reactiveandroid.sample.ui.activities; + +//pull request - bendothall +//Pagination API Activity + +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.reactiveandroid.query.api.Pagination; +import com.reactiveandroid.sample.R; +import com.reactiveandroid.sample.mvp.models.Note; +import com.reactiveandroid.sample.ui.adapters.pagination.PaginationAPIAdapter; +import com.reactiveandroid.sample.ui.adapters.pagination.PaginationAPIAdapterCallback; +import com.reactiveandroid.sample.ui.adapters.pagination.PaginationAPIScrollListener; + +import java.util.List; + +import static com.reactiveandroid.query.api.Pagination.getTableRowCount; +import static com.reactiveandroid.query.api.Pagination.loadPaginationData; + +public class PaginationAPIActivity extends AppCompatActivity implements PaginationAPIAdapterCallback { + + PaginationAPIAdapter + adapter; + + LinearLayoutManager + linearLayoutManager; + + RecyclerView + recycler; + + private boolean + hasFirstPageLoaded = false; + + private boolean + isLoading = false; + + private boolean + isLastPage = false; + + Integer + totalAdapterItems = 0, + AdapterItemsReturned = 0, + limit = 20, + offset = 0, + delayLoading = 500; + + String + orderBy = "ASC", + column = "id"; + + List + fetchPaginationResults; + + ActionBar + actionBar; + + LinearLayout + notesNotFoundMessage; + + TextView + currentPaginationType; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.pagination_api_activity); + + actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle("Pagination API"); + actionBar.setDisplayHomeAsUpEnabled(true); + } + + notesNotFoundMessage = (LinearLayout) findViewById(R.id.notes_not_found); + notesNotFoundMessage.setVisibility(View.GONE); + + currentPaginationType = (TextView) findViewById(R.id.currentPaginationType); + + //region recycler + recycler = (RecyclerView) findViewById(R.id.recycler); + adapter = new PaginationAPIAdapter(this); + linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); + recycler.setLayoutManager(linearLayoutManager); + recycler.setItemAnimator(new DefaultItemAnimator()); + recycler.setAdapter(adapter); + recycler.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); + recycler.addOnScrollListener(new PaginationAPIScrollListener(linearLayoutManager) { + @Override + protected void loadMoreItems() { + if(hasFirstPageLoaded) { + if(isLastPage){ + isLoading = false; + }else{ + isLoading = true; + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + new loadNextDataAsyncTask().execute(); + } + }, delayLoading); + } + } + } + + @Override + public int getTotalPageCount() { + return 0; + } + + @Override + public boolean isLastPage() { + return isLastPage; + } + + @Override + public boolean isLoading() { + return isLoading; + } + + }); + //endregion recycler + + new loadFirstDataAsyncTask().execute(); + + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.pagination_api_menu, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case R.id.filterById: + column = "id"; + currentPaginationType.setText("Filtering By Column: id"); + resetUIView(); + break; + + case R.id.filterByTitle: + column = "title"; + currentPaginationType.setText("Filtering By Column: title"); + resetUIView(); + break; + + case R.id.filterByText: + column = "text"; + currentPaginationType.setText("Filtering By Column: text"); + resetUIView(); + break; + + case R.id.filterByColor: + column = "color"; + currentPaginationType.setText("Filtering By Column: color"); + resetUIView(); + break; + + case R.id.filterByCreatedDate: + column = "created_at"; + currentPaginationType.setText("Filtering By Column: created_at"); + resetUIView(); + break; + + case R.id.filterByUpdatedDate: + column = "updated_at"; + currentPaginationType.setText("Filtering By Column: updated_at"); + resetUIView(); + break; + + } + return super.onOptionsItemSelected(item); + } + + public void showNotesNotFoundMessage() { + notesNotFoundMessage.setVisibility(View.VISIBLE); + } + + public void hideNotesNotFoundMessage() { + notesNotFoundMessage.setVisibility(View.GONE); + } + + public void resetUIView() { + offset = 0; + isLoading = false; + hasFirstPageLoaded = false; + isLastPage = false; + totalAdapterItems = 0; + AdapterItemsReturned = 0; + adapter.clear(); + new loadFirstDataAsyncTask().execute(); + } + + public class loadFirstDataAsyncTask extends AsyncTask { + + @Override + protected Boolean doInBackground(Boolean ... params) { + + boolean result = false; + + fetchPaginationResults = loadPaginationData(column, 0, limit, orderBy, Note.class); + AdapterItemsReturned = fetchPaginationResults.size(); + if(fetchPaginationResults.size() > 0){ + result = true; + totalAdapterItems = totalAdapterItems + fetchPaginationResults.size(); + } + + return result; + } + + @Override + protected void onPostExecute(Boolean result) { + + if(result) { + + hasFirstPageLoaded = true; + adapter.addAll(fetchPaginationResults); + if (totalAdapterItems == getTableRowCount(Note.class)) { + isLastPage = true; + adapter.removeLoadingFooter(); + }else{ + isLoading = false; + adapter.addLoadingFooter(); + } + + hideNotesNotFoundMessage(); + actionBar.setSubtitle("Displaying " + totalAdapterItems + " of " + getTableRowCount(Note.class) + " Items"); + + }else{ + + showNotesNotFoundMessage(); + actionBar.setSubtitle("Displaying " + totalAdapterItems + " of " + getTableRowCount(Note.class) + " Items"); + isLastPage = true; + } + + logToConsole(); + + } + + @Override + protected void onPreExecute() { + + } + + @Override + protected void onProgressUpdate(Boolean... values) { + + + } + } + + public class loadNextDataAsyncTask extends AsyncTask { + + @Override + protected Boolean doInBackground(Boolean ... params) { + + boolean result = false; + + offset = offset + limit; + fetchPaginationResults = loadPaginationData(column, offset, limit, orderBy, Note.class); + AdapterItemsReturned = fetchPaginationResults.size(); + if(fetchPaginationResults.size() > 0){ + result = true; + totalAdapterItems = totalAdapterItems + fetchPaginationResults.size(); + } + + return result; + } + + @Override + protected void onPostExecute(Boolean result) { + + if(result) { + + adapter.removeLoadingFooter(); + isLoading = false; + + adapter.addAll(fetchPaginationResults); + if (totalAdapterItems == getTableRowCount(Note.class)) { + isLastPage = true; + }else{ + isLoading = false; + adapter.addLoadingFooter(); + } + actionBar.setSubtitle("Displaying " + totalAdapterItems + " of " + getTableRowCount(Note.class) + " Items"); + + }else{ + + isLastPage = true; + adapter.removeLoadingFooter(); + isLoading = false; + + } + hideNotesNotFoundMessage(); + logToConsole(); + } + + @Override + protected void onPreExecute() { + + } + + @Override + protected void onProgressUpdate(Boolean... values) { + + + } + } + + public void retryPageLoad() { + new loadNextDataAsyncTask().execute(); + } + + public void logToConsole() { + + Log.e("Pagination API", "*******************"); + Log.e("Pagination API", "orderBy: " + orderBy); + Log.e("Pagination API", "offset: " + offset); + Log.e("Pagination API", "limit: " + limit); + Log.e("Pagination API", " ------------------- "); + Log.e("Pagination API", "AdapterItemsReturned: " + AdapterItemsReturned); + Log.e("Pagination API", "totalAdapterItems: " + totalAdapterItems); + Log.e("Pagination API", "getTableRowCount(Note.class): " + getTableRowCount(Note.class)); + Log.e("Pagination API", "adapter.getItemCount(): " + adapter.getItemCount()); + Log.e("Pagination API", " ------------------- "); + Log.e("Pagination API", "isLastPage: " + isLastPage); + Log.e("Pagination API", "isLoading: " + isLoading); + + } + +} diff --git a/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/TransactionsAPIActivity.java b/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/TransactionsAPIActivity.java new file mode 100644 index 0000000..de99a4d --- /dev/null +++ b/sample-app/src/main/java/com/reactiveandroid/sample/ui/activities/TransactionsAPIActivity.java @@ -0,0 +1,331 @@ +package com.reactiveandroid.sample.ui.activities; + +//pull request - bendothall +//Transactions API Activity + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.amulyakhare.textdrawable.util.ColorGenerator; +import com.reactiveandroid.ReActiveAndroid; +import com.reactiveandroid.query.Delete; +import com.reactiveandroid.query.Select; +import com.reactiveandroid.query.api.Transactions; +import com.reactiveandroid.sample.AppDatabase; +import com.reactiveandroid.sample.R; +import com.reactiveandroid.sample.mvp.models.Note; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +public class TransactionsAPIActivity extends AppCompatActivity { + + CheckBox + purge_data, + use_transactions; + + EditText + transactions_entries; + + Button + process, + purgeOnlyAction; + + ProgressBar + overallProgress; + + TextView + messages; + + public static DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); + public static Date date = new Date(); + Date startTime = new Date(); + Date endTime = new Date(); + String startTimeHumanFriendly = ""; + String endTimeHumanFriendly = ""; + + int getPassedDataIndex = 0; + int rowProcessedCount = 0; + int actualNotesCount = 0; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.transactions_api_activity); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle("Transactions API"); + actionBar.setDisplayHomeAsUpEnabled(true); + } + + purge_data = (CheckBox)findViewById(R.id.purge_data); + use_transactions = (CheckBox)findViewById(R.id.use_transactions); + + transactions_entries = (EditText)findViewById(R.id.transactions_entries); + + process = (Button)findViewById(R.id.process); + process.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + UIIsBusy(); + + doAsyncTask beginAsyncTask = new doAsyncTask(); + beginAsyncTask.execute(); + + } + }); + + purgeOnlyAction = (Button)findViewById(R.id.purgeOnlyAction); + purgeOnlyAction.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + UIIsBusy(); + Delete.from(Note.class).execute(); + String originalMessageContent = messages.getText().toString(); + messages.setText( + "-----------------------------------\n\n" + + "Action: Purge Only\n" + + "Notes Count: " + Select.from(Note.class).count() + "\n" + + originalMessageContent); + UIIsFinished(); + + } + }); + + + messages = (TextView)findViewById(R.id.messages); + messages.setText(""); + + overallProgress = (ProgressBar)findViewById(R.id.overallProgress); + overallProgress.setVisibility(View.GONE); + + } + + @Override + public void onResume() { + + super.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + } + + public void UIIsBusy() { + + purge_data.setEnabled(false); + use_transactions.setEnabled(false); + transactions_entries.setEnabled(false); + process.setEnabled(false); + overallProgress.setVisibility(View.VISIBLE); + + } + + public void UIIsFinished() { + + purge_data.setEnabled(true); + use_transactions.setEnabled(true); + transactions_entries.setEnabled(true); + process.setEnabled(true); + overallProgress.setVisibility(View.GONE); + + } + + public void updateUIActions() { + + + + } + + public Boolean beginTransactions() { + + boolean isCompleted = true; + + + try { + + List buildPassedData = new ArrayList(); + int wordLength = 25; + actualNotesCount = 0; + + //setDefault & convert EditText to int + int addRowsAmount = 15000; + + try { + + if(transactions_entries.getText().toString() !=null){ + addRowsAmount = Integer.parseInt(transactions_entries.getText().toString()); + } + + }catch(Exception error) { + Log.e("Exception", "beginTransactions().Exception().convertEditText(): " + error.toString()); + } + + //region foreach + for (int rowIndex = 0; rowIndex < addRowsAmount; rowIndex++) { + + //https://www.baeldung.com/java-random-string + int leftLimit = 97; // letter 'a' + int rightLimit = 122; // letter 'z' + int targetStringLength = 10; + Random random = new Random(); + StringBuilder buffer = new StringBuilder(targetStringLength); + + for (int i = 0; i < targetStringLength; i++) { + int randomLimitedInt = leftLimit + (int) + (random.nextFloat() * (rightLimit - leftLimit + 1)); + buffer.append((char) randomLimitedInt); + } + + String generatedString = buffer.toString(); + buildPassedData.add(generatedString); + + if(addRowsAmount == rowIndex){ + break; + } + + } + //endregion foreach + + //region SQL processing ... + startTime = new Date(); + startTimeHumanFriendly = dateFormat.format(startTime); + + if(use_transactions.isChecked()){ + Transactions.BeginTransactions(AppDatabase.class); + } + + + if(purge_data.isChecked()){ + Delete.from(Note.class).execute(); + } + + getPassedDataIndex = 0; + rowProcessedCount = 0; + for(String item : buildPassedData) { + + getPassedDataIndex++; + + String title = ""; + title = item; + + if(!title.isEmpty()){ + + if(Select.from(Note.class).where("title = ?", title).count() == 1){ + + //update + Note updateNote = Select.from(Note.class).where("title = ?", title).fetchSingle(); + updateNote.setTitle(title); + updateNote.setText(title); + + if(updateNote.save() > 0){ + rowProcessedCount++; + } + + }else{ + + + //create + Note createNote = new Note(title, title, ColorGenerator.MATERIAL.getRandomColor()); + if(createNote.save() > 0){ + rowProcessedCount++; + } + + } + + } + + } + + actualNotesCount = Select.from(Note.class).count(); + + if(use_transactions.isChecked()){ + Transactions.EndTransactions(AppDatabase.class); + } + + endTime = new Date(); + endTimeHumanFriendly = dateFormat.format(endTime); + isCompleted = true; + //endregion SQL processing ... + + }catch (Exception error) { + Log.e("Exception", "beginTransactions().Exception(): " + error.toString()); + isCompleted = false; + } + + return isCompleted; + + } + + public class doAsyncTask extends AsyncTask { + + @Override + protected Boolean doInBackground(Boolean... params) { + + Boolean result = false; + if(beginTransactions()){ + result = true; + } + return result; + } + + @Override + protected void onPostExecute(Boolean result) { + + + String originalMessageContent = messages.getText().toString(); + messages.setText( + "-----------------------------------\n\n" + + "Purge Data: " + purge_data.isChecked() + "\n" + + "Using Transaction API: " + use_transactions.isChecked() + "\n\n" + + "Started At: " + startTimeHumanFriendly + "\n" + + "Ended At: " + endTimeHumanFriendly + "\n" + + "Execution Time: " + getExecutionTime(startTime,endTime,TimeUnit.SECONDS) + "\n\n" + + "Notes Count: " + actualNotesCount + "\n" + + "Rows Validated: " + getPassedDataIndex + "\n" + + "Rows Processed: " + rowProcessedCount + "\n\n" + + originalMessageContent); + + UIIsFinished(); + + } + + @Override + protected void onPreExecute() { + + } + + @Override + protected void onProgressUpdate(Boolean... values) { + + + } + } + + public static long getExecutionTime(Date startTime, Date endTime, TimeUnit timeUnit) { + long diffInSeconds = endTime.getTime() - startTime.getTime(); + return timeUnit.convert(diffInSeconds, TimeUnit.MILLISECONDS); + } + +} diff --git a/sample-app/src/main/java/com/reactiveandroid/sample/ui/adapters/pagination/PaginationAPIAdapter.java b/sample-app/src/main/java/com/reactiveandroid/sample/ui/adapters/pagination/PaginationAPIAdapter.java new file mode 100644 index 0000000..4b2a832 --- /dev/null +++ b/sample-app/src/main/java/com/reactiveandroid/sample/ui/adapters/pagination/PaginationAPIAdapter.java @@ -0,0 +1,283 @@ +package com.reactiveandroid.sample.ui.adapters.pagination; + +//pull request - bendothall +//Pagination API PaginationAPIAdapter + +import android.content.Context; +import android.content.res.Resources; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.amulyakhare.textdrawable.TextDrawable; +import com.reactiveandroid.sample.R; +import com.reactiveandroid.sample.mvp.models.Note; + +import java.util.ArrayList; +import java.util.List; +import java.text.SimpleDateFormat; + +public class PaginationAPIAdapter extends RecyclerView.Adapter { + + private List + Notes; + + private Context + context; + + private boolean isLoadingAdded = false; + private boolean retryPageLoad = false; + // View Types + private static final int ITEM = 0; + private static final int LOADING = 1; + + private PaginationAPIAdapterCallback + mCallback; + + private String + errorMsg; + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMM d, yyyy H:mm"); + + public PaginationAPIAdapter(Context context) { + this.context = context; + this.mCallback = (PaginationAPIAdapterCallback) context; + setHasStableIds(true); + Notes = new ArrayList<>(); + } + + public List getNotes() { + return Notes; + } + + public void setNotes(List Notes) { + this.Notes = Notes; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + RecyclerView.ViewHolder viewHolder = null; + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + + switch (viewType) { + + case ITEM: + View viewItem = inflater.inflate(R.layout.pagination_api_adapter_view, parent, false); + viewHolder = new ItemVH(viewItem); + break; + + case LOADING: + View viewLoading = inflater.inflate(R.layout.pagination_api_adapter_progress_view, parent, false); + viewHolder = new LoadingVH(viewLoading); + break; + + } + + return viewHolder; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + Note result = Notes.get(position); + + switch (getItemViewType(position)) { + + case ITEM: + + final ItemVH itemVH = (ItemVH)holder; + if(result.getId() != null){ + + itemVH.itemView.setTag(itemVH.getAdapterPosition()); + + itemVH.position.setText("" + (position + 1)); + itemVH.id.setText(result.getId().toString()); + + TextDrawable noteDrawable = TextDrawable.builder().buildRound(result.getTitle().substring(0, 1), result.getColor()); + itemVH.note_drawable.setImageDrawable(noteDrawable); + + itemVH.title.setText(result.getTitle()); + itemVH.text.setText(result.getText()); + + itemVH.note_updated_time.setText(DATE_FORMAT.format(result.getUpdatedAt())); + + } + + break; + + case LOADING: + + LoadingVH loadingVH = (LoadingVH) holder; + + if (retryPageLoad) { + loadingVH.mErrorLayout.setVisibility(View.VISIBLE); + loadingVH.mProgressBar.setVisibility(View.GONE); + + loadingVH.mErrorTxt.setText( + errorMsg != null ? + errorMsg : + context.getString(R.string.error_msg_unknown)); + + } else { + loadingVH.mErrorLayout.setVisibility(View.GONE); + loadingVH.mProgressBar.setVisibility(View.VISIBLE); + } + break; + } + + } + + @Override + public int getItemCount() { + return Notes == null ? 0 : Notes.size(); + } + + @Override + public int getItemViewType(int position) { + return (position == Notes.size() - 1 && isLoadingAdded) ? LOADING : ITEM; + } + + @Override + public long getItemId(int position) { + return position; + } + + public void add(Note note) { + Notes.add(note); + notifyItemInserted(Notes.size() - 1); + } + + public void addAll(List notes) { + for (Note result : notes) { + add(result); + } + } + + public void remove(Note note) { + int position = Notes.indexOf(note); + if (position > -1) { + Notes.remove(position); + notifyItemRemoved(position); + } + } + + public void clear() { + while (getItemCount() > 0) { + remove(getItem(0)); + } + } + + public void addLoadingFooter() { + isLoadingAdded = true; + add(new Note()); + } + + public void removeLoadingFooter() { + isLoadingAdded = false; + + int position = Notes.size() - 1; + Note result = getItem(position); + + if (result != null) { + Notes.remove(position); + notifyItemRemoved(position); + } + } + + public boolean isEmpty() { + return getItemCount() == 0; + } + + public Note getItem(int position) { + return Notes.get(position); + } + + /** + * Displays Pagination retry footer view along with appropriate errorMsg + * + * @param show + * @param errorMsg to display if page load fails + */ + public void showRetry(boolean show, @Nullable String errorMsg) { + retryPageLoad = show; + notifyItemChanged(Notes.size() - 1); + + if (errorMsg != null) this.errorMsg = errorMsg; + } + + protected class ItemVH extends RecyclerView.ViewHolder { + + public TextView + position, + id, + title, + text, + note_updated_time; + + public ImageView + note_drawable; + + public ItemVH(final View itemView) { + super(itemView); + + position = (TextView) itemView.findViewById(R.id.position); + id = (TextView) itemView.findViewById(R.id.id); + note_drawable = (ImageView) itemView.findViewById(R.id.note_drawable); + title = (TextView) itemView.findViewById(R.id.title); + text = (TextView) itemView.findViewById(R.id.text); + note_updated_time = (TextView) itemView.findViewById(R.id.note_updated_time); + + } + } + + protected class LoadingVH extends RecyclerView.ViewHolder { + + private ProgressBar mProgressBar; + private ImageButton mRetryBtn; + private TextView mErrorTxt; + private LinearLayout mErrorLayout; + + public LoadingVH(View itemView) { + super(itemView); + + mProgressBar = (ProgressBar) itemView.findViewById(R.id.loadmore_progress); + mRetryBtn = (ImageButton) itemView.findViewById(R.id.loadmore_retry); + mErrorTxt = (TextView) itemView.findViewById(R.id.loadmore_errortxt); + mErrorLayout = (LinearLayout) itemView.findViewById(R.id.loadmore_errorlayout); + + //mRetryBtn.setOnClickListener(this); + //mErrorLayout.setOnClickListener(this); + + + mRetryBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + + } + }); + + mErrorLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + showRetry(false, null); + mCallback.retryPageLoad(); + + } + }); + + + } + + + } + +} \ No newline at end of file diff --git a/sample-app/src/main/java/com/reactiveandroid/sample/ui/adapters/pagination/PaginationAPIAdapterCallback.java b/sample-app/src/main/java/com/reactiveandroid/sample/ui/adapters/pagination/PaginationAPIAdapterCallback.java new file mode 100644 index 0000000..87ed276 --- /dev/null +++ b/sample-app/src/main/java/com/reactiveandroid/sample/ui/adapters/pagination/PaginationAPIAdapterCallback.java @@ -0,0 +1,8 @@ +package com.reactiveandroid.sample.ui.adapters.pagination; + +//pull request - bendothall +//Pagination API PaginationAPIAdapterCallback + +public interface PaginationAPIAdapterCallback { + void retryPageLoad(); +} diff --git a/sample-app/src/main/java/com/reactiveandroid/sample/ui/adapters/pagination/PaginationAPIScrollListener.java b/sample-app/src/main/java/com/reactiveandroid/sample/ui/adapters/pagination/PaginationAPIScrollListener.java new file mode 100644 index 0000000..a091098 --- /dev/null +++ b/sample-app/src/main/java/com/reactiveandroid/sample/ui/adapters/pagination/PaginationAPIScrollListener.java @@ -0,0 +1,47 @@ +package com.reactiveandroid.sample.ui.adapters.pagination; + +//pull request - bendothall +//Pagination API PaginationAPIScrollListener + +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; + +public abstract class PaginationAPIScrollListener extends RecyclerView.OnScrollListener { + + LinearLayoutManager layoutManager; + + /** + * Supporting only LinearLayoutManager for now. + * + * @param layoutManager + */ + public PaginationAPIScrollListener(LinearLayoutManager layoutManager) { + this.layoutManager = layoutManager; + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + + int visibleItemCount = layoutManager.getChildCount(); + int totalItemCount = layoutManager.getItemCount(); + int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition(); + + if (!isLoading() && !isLastPage()) { + if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount + && firstVisibleItemPosition >= 0) { + loadMoreItems(); + } + } + + } + + protected abstract void loadMoreItems(); + + public abstract int getTotalPageCount(); + + public abstract boolean isLastPage(); + + public abstract boolean isLoading(); + +} \ No newline at end of file diff --git a/sample-app/src/main/res/drawable/circle_color_grey_24dp.xml b/sample-app/src/main/res/drawable/circle_color_grey_24dp.xml new file mode 100644 index 0000000..90743b2 --- /dev/null +++ b/sample-app/src/main/res/drawable/circle_color_grey_24dp.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/sample-app/src/main/res/layout/pagination_api_activity.xml b/sample-app/src/main/res/layout/pagination_api_activity.xml new file mode 100644 index 0000000..cf303ce --- /dev/null +++ b/sample-app/src/main/res/layout/pagination_api_activity.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample-app/src/main/res/layout/pagination_api_adapter_progress_view.xml b/sample-app/src/main/res/layout/pagination_api_adapter_progress_view.xml new file mode 100644 index 0000000..33e1036 --- /dev/null +++ b/sample-app/src/main/res/layout/pagination_api_adapter_progress_view.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample-app/src/main/res/layout/pagination_api_adapter_view.xml b/sample-app/src/main/res/layout/pagination_api_adapter_view.xml new file mode 100644 index 0000000..30857a3 --- /dev/null +++ b/sample-app/src/main/res/layout/pagination_api_adapter_view.xml @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample-app/src/main/res/layout/transactions_api_activity.xml b/sample-app/src/main/res/layout/transactions_api_activity.xml new file mode 100644 index 0000000..1fd5524 --- /dev/null +++ b/sample-app/src/main/res/layout/transactions_api_activity.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +