diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/create_pm/CreatePMActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/create_pm/CreatePMActivity.java index b8d74f45..90017dda 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/create_pm/CreatePMActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/create_pm/CreatePMActivity.java @@ -30,6 +30,10 @@ public class CreatePMActivity extends BaseActivity implements ExternalAsyncTask. private TextInputLayout subjectInput; private EmojiKeyboard emojiKeyboard; private String username, sendPmUrl; + /** + * Used for example in quotes to pre-populate the EditorView with quoted text + */ + private String defaultContent; public static final String BUNDLE_SEND_PM_URL = "send-pm-url"; public static final String BUNDLE_PM_CONTENT = "pm-content"; @@ -42,6 +46,7 @@ public class CreatePMActivity extends BaseActivity implements ExternalAsyncTask. Intent callingIntent = getIntent(); username = callingIntent.getStringExtra(ProfileActivity.BUNDLE_PROFILE_USERNAME); sendPmUrl = callingIntent.getStringExtra(BUNDLE_SEND_PM_URL); + defaultContent = callingIntent.getStringExtra(BUNDLE_PM_CONTENT); //Initialize toolbar toolbar = findViewById(R.id.toolbar); @@ -86,6 +91,8 @@ public class CreatePMActivity extends BaseActivity implements ExternalAsyncTask. sendPMTask.execute(sendPmUrl, subjectInput.getEditText().getText().toString(), contentEditor.getText().toString()); }); + if (defaultContent != null) + contentEditor.setText(defaultContent); } @Override diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/inbox/InboxActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/inbox/InboxActivity.java index 022a7f7c..a6cea947 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/inbox/InboxActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/inbox/InboxActivity.java @@ -2,7 +2,12 @@ package gr.thmmy.mthmmy.activities.inbox; import android.os.Bundle; import android.view.View; +import android.widget.ImageButton; +import android.widget.TextView; +import android.widget.Toast; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProviders; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -46,7 +51,7 @@ public class InboxActivity extends BaseActivity { inboxAdapter = new InboxAdapter(this); pmRecyclerview.setAdapter(inboxAdapter); - inboxViewModel = ViewModelProviders.of(this).get(InboxViewModel.class); + inboxViewModel =new ViewModelProvider(this).get(InboxViewModel.class); subscribeUI(); inboxViewModel.loadInbox(); @@ -60,7 +65,9 @@ public class InboxActivity extends BaseActivity { Timber.i("Successfully loaded inbox"); inboxAdapter.notifyDataSetChanged(); } else { - + Timber.w("Failed to load inbox"); + Toast.makeText(this, "Failed to load inbox", Toast.LENGTH_SHORT).show(); + finish(); } }); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/inbox/InboxAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/inbox/InboxAdapter.java index 9d8b5f91..b36ac448 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/inbox/InboxAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/inbox/InboxAdapter.java @@ -228,11 +228,7 @@ public class InboxAdapter extends RecyclerView.Adapter quoteButton.setCompoundDrawablesRelativeWithIntrinsicBounds(quoteDrawable, null, null, null); quoteButton.setOnClickListener(v -> { Toast.makeText(context, "TODO", Toast.LENGTH_SHORT).show(); - // TODO: Create quote PM task - Intent sendPMIntent = new Intent(context, CreatePMActivity.class); - sendPMIntent.putExtra(CreatePMActivity.BUNDLE_SEND_PM_URL, currentPM.getQuoteUrl()); - context.startActivity(sendPMIntent); - popUp.dismiss(); + // TODO: Create delete PM task }); final TextView replyButton = popupContent.findViewById(R.id.pm_reply_button); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java index e21244a8..8ed89ece 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java @@ -1,17 +1,14 @@ package gr.thmmy.mthmmy.activities.topic; -import android.annotation.SuppressLint; import android.app.NotificationManager; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.Handler; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -21,10 +18,8 @@ import android.text.style.ForegroundColorSpan; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.InputMethodManager; -import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; @@ -33,7 +28,7 @@ import android.widget.Toast; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatDelegate; import androidx.core.content.res.ResourcesCompat; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.floatingactionbutton.FloatingActionButton; @@ -54,6 +49,7 @@ import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.model.TopicItem; +import gr.thmmy.mthmmy.pagination.BottomPaginationView; import gr.thmmy.mthmmy.utils.CustomLinearLayoutManager; import gr.thmmy.mthmmy.utils.HTMLUtils; import gr.thmmy.mthmmy.utils.NetworkResultCodes; @@ -93,35 +89,9 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo //Reply related private FloatingActionButton replyFAB; //Topic's pages related - //Page select related - /** - * Used for handling bottom navigation bar's buttons long click user interactions - */ - private final Handler repeatUpdateHandler = new Handler(); - /** - * Holds the initial time delay before a click on bottom navigation bar is considered long - */ - private final long INITIAL_DELAY = 500; - private boolean autoIncrement = false; - private boolean autoDecrement = false; - /** - * Holds the number of pages to be added or subtracted from current page on each step while a - * long click is held in either next or previous buttons - */ - private static final int SMALL_STEP = 1; - /** - * Holds the number of pages to be added or subtracted from current page on each step while a - * long click is held in either first or last buttons - */ - private static final int LARGE_STEP = 10; //Bottom navigation bar graphics related - private LinearLayout bottomNavBar; - private ImageButton firstPage; - private ImageButton previousPage; - private TextView pageIndicator; - private ImageButton nextPage; - private ImageButton lastPage; + private BottomPaginationView bottomPagination; private Snackbar snackbar; private TopicViewModel viewModel; private EmojiKeyboard emojiKeyboard; @@ -139,7 +109,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo super.onCreate(savedInstanceState); setContentView(R.layout.activity_topic); // get TopicViewModel instance - viewModel = ViewModelProviders.of(this).get(TopicViewModel.class); + viewModel = new ViewModelProvider(this).get(TopicViewModel.class); subscribeUI(); Bundle extras = getIntent().getExtras(); @@ -191,7 +161,6 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo replyFAB = findViewById(R.id.topic_fab); replyFAB.hide(); replyFAB.setTag(false); - bottomNavBar = findViewById(R.id.bottom_navigation_bar); if (!sessionManager.isLoggedIn()) { replyFAB.hide(); replyFAB.setTag(false); @@ -204,18 +173,8 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo } //Sets bottom navigation bar - firstPage = findViewById(R.id.page_first_button); - previousPage = findViewById(R.id.page_previous_button); - pageIndicator = findViewById(R.id.page_indicator); - nextPage = findViewById(R.id.page_next_button); - lastPage = findViewById(R.id.page_last_button); - - initDecrementButton(firstPage, LARGE_STEP); - initDecrementButton(previousPage, SMALL_STEP); - initIncrementButton(nextPage, SMALL_STEP); - initIncrementButton(lastPage, LARGE_STEP); - - paginationEnabled(false); + bottomPagination = findViewById(R.id.bottom_pagination); + bottomPagination.setOnPageRequestedListener(viewModel); Timber.i("Starting initial topic load"); viewModel.loadUrl(topicPageUrl); @@ -291,7 +250,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo viewModel.setWritingReply(false); replyFAB.show(); replyFAB.setTag(true); - bottomNavBar.setVisibility(View.VISIBLE); + bottomPagination.setVisibility(View.VISIBLE); return; } else if (viewModel.isEditingPost()) { ((Post) topicItems.get(viewModel.getPostBeingEditedPosition())).setPostType(Post.TYPE_POST); @@ -300,7 +259,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo viewModel.setEditingPost(false); replyFAB.show(); replyFAB.setTag(true); - bottomNavBar.setVisibility(View.VISIBLE); + bottomPagination.setVisibility(View.VISIBLE); return; } super.onBackPressed(); @@ -340,152 +299,6 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo recyclerView.scrollToPosition(position); } - //--------------------------------------BOTTOM NAV BAR METHODS---------------------------------- - - /** - * This class is used to implement the repetitive incrementPageRequestValue/decrementPageRequestValue - * of page value when long pressing one of the page navigation buttons. - */ - private class RepetitiveUpdater implements Runnable { - private final int step; - - /** - * @param step number of pages to add/subtract on each repetition - */ - RepetitiveUpdater(int step) { - this.step = step; - } - - public void run() { - long REPEAT_DELAY = 250; - if (autoIncrement) { - viewModel.incrementPageRequestValue(step, false); - repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), REPEAT_DELAY); - } else if (autoDecrement) { - viewModel.decrementPageRequestValue(step, false); - repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), REPEAT_DELAY); - } - } - } - - private void paginationEnabled(boolean enabled) { - firstPage.setEnabled(enabled); - previousPage.setEnabled(enabled); - nextPage.setEnabled(enabled); - lastPage.setEnabled(enabled); - } - - private void paginationDisable(View exception) { - if (exception == firstPage) { - previousPage.setEnabled(false); - nextPage.setEnabled(false); - lastPage.setEnabled(false); - } else if (exception == previousPage) { - firstPage.setEnabled(false); - nextPage.setEnabled(false); - lastPage.setEnabled(false); - } else if (exception == nextPage) { - firstPage.setEnabled(false); - previousPage.setEnabled(false); - lastPage.setEnabled(false); - } else if (exception == lastPage) { - firstPage.setEnabled(false); - previousPage.setEnabled(false); - nextPage.setEnabled(false); - } else { - paginationEnabled(false); - } - } - - @SuppressLint("ClickableViewAccessibility") - private void initIncrementButton(ImageButton increment, final int step) { - // Increment once for a click - increment.setOnClickListener(v -> { - if (!autoIncrement && step == LARGE_STEP) { - viewModel.setPageIndicatorIndex(viewModel.getPageCount(), true); - } else if (!autoIncrement) { - viewModel.incrementPageRequestValue(step, true); - } - }); - - // Auto increment for a long click - increment.setOnLongClickListener( - arg0 -> { - paginationDisable(arg0); - autoIncrement = true; - repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY); - return false; - } - ); - - // When the button is released - increment.setOnTouchListener(new View.OnTouchListener() { - private Rect rect; - - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); - } else if (rect != null && event.getAction() == MotionEvent.ACTION_UP && autoIncrement) { - autoIncrement = false; - paginationEnabled(true); - viewModel.loadPageIndicated(); - } else if (rect != null && event.getAction() == MotionEvent.ACTION_MOVE) { - if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { - autoIncrement = false; - viewModel.setPageIndicatorIndex(viewModel.getCurrentPageIndex(), false); - paginationEnabled(true); - } - } - return false; - } - }); - } - - @SuppressLint("ClickableViewAccessibility") - private void initDecrementButton(ImageButton decrement, final int step) { - // Decrement once for a click - decrement.setOnClickListener(v -> { - if (!autoDecrement && step == LARGE_STEP) { - viewModel.setPageIndicatorIndex(1, true); - } else if (!autoDecrement) { - viewModel.decrementPageRequestValue(step, true); - } - }); - - // Auto decrement for a long click - decrement.setOnLongClickListener( - arg0 -> { - paginationDisable(arg0); - autoDecrement = true; - repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY); - return false; - } - ); - - // When the button is released - decrement.setOnTouchListener(new View.OnTouchListener() { - private Rect rect; - - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); - } else if (event.getAction() == MotionEvent.ACTION_UP && autoDecrement) { - autoDecrement = false; - paginationEnabled(true); - viewModel.loadPageIndicated(); - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (rect != null && - !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { - autoIncrement = false; - viewModel.setPageIndicatorIndex(viewModel.getCurrentPageIndex(), false); - paginationEnabled(true); - } - } - return false; - } - }); - } - //------------------------------------BOTTOM NAV BAR METHODS END------------------------------------ /** @@ -538,7 +351,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo Timber.i("Post reply successful"); replyFAB.show(); replyFAB.setTag(true); - bottomNavBar.setVisibility(View.VISIBLE); + bottomPagination.setVisibility(View.VISIBLE); viewModel.setWritingReply(false); SharedPreferences drafts = getSharedPreferences(getString(R.string.pref_topic_drafts_key), @@ -615,7 +428,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo topicAdapter.notifyItemChanged(position); replyFAB.show(); replyFAB.setTag(true); - bottomNavBar.setVisibility(View.VISIBLE); + bottomPagination.setVisibility(View.VISIBLE); viewModel.setEditingPost(false); viewModel.reloadPage(); } else { @@ -664,8 +477,11 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo // observe the changes in data viewModel.getPageIndicatorIndex().observe(this, pageIndicatorIndex -> { if (pageIndicatorIndex == null) return; - pageIndicator.setText(String.valueOf(pageIndicatorIndex) + "/" + - String.valueOf(viewModel.getPageCount())); + bottomPagination.setIndicatedPageIndex(pageIndicatorIndex); + }); + viewModel.getPageCount().observe(this, pageCount -> { + if (pageCount == null) return; + bottomPagination.setTotalPageCount(pageCount); }); viewModel.getTopicTitle().observe(this, newTopicTitle -> { if (newTopicTitle == null) return; @@ -674,7 +490,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo }); viewModel.getPageTopicId().observe(this, pageTopicId -> { if (pageTopicId == null) return; - if (viewModel.getCurrentPageIndex() == viewModel.getPageCount()) { + if (viewModel.getCurrentPageIndex() == viewModel.getPageCount().getValue()) { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null) notificationManager.cancel(NEW_POST_TAG, pageTopicId); @@ -696,6 +512,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo topicItems.addAll(postList); topicAdapter.notifyDataSetChanged(); }); + // Scroll to position does not work because WebView size is unknown initially /*viewModel.getFocusedPostIndex().observe(this, focusedPostIndex -> { if (focusedPostIndex == null) return; recyclerView.scrollToPosition(focusedPostIndex); @@ -706,7 +523,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo switch (resultCode) { case SUCCESS: Timber.i("Successfully loaded a topic"); - paginationEnabled(true); + bottomPagination.setEnabled(true); break; case NETWORK_ERROR: Timber.w("Network error on loaded page"); @@ -731,7 +548,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo }); } else { // a page has already been loaded - viewModel.setPageIndicatorIndex(viewModel.getCurrentPageIndex(), false); + bottomPagination.setIndicatedPageIndex(viewModel.getCurrentPageIndex()); snackbar = Snackbar.make(findViewById(R.id.main_content), R.string.generic_network_error, Snackbar.LENGTH_INDEFINITE); snackbar.setAction(R.string.retry, view -> viewModel.reloadPage()); @@ -773,7 +590,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo recyclerView.scrollToPosition(topicItems.size() - 1); replyFAB.hide(); replyFAB.setTag(false); - bottomNavBar.setVisibility(View.GONE); + bottomPagination.setVisibility(View.GONE); } else { Timber.i("Prepare for reply unsuccessful"); Snackbar.make(findViewById(R.id.main_content), getString(R.string.generic_network_error), Snackbar.LENGTH_SHORT).show(); @@ -789,7 +606,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo recyclerView.scrollToPosition(result.getPosition()); replyFAB.hide(); replyFAB.setTag(false); - bottomNavBar.setVisibility(View.GONE); + bottomPagination.setVisibility(View.GONE); } else { Timber.i("Prepare for edit unsuccessful"); Snackbar.make(findViewById(R.id.main_content), getString(R.string.generic_network_error), Snackbar.LENGTH_SHORT).show(); @@ -797,9 +614,11 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo }); } - /**This method sets a long click listener on the title of the topic. Once the + /** + * This method sets a long click listener on the title of the topic. Once the * listener gets triggered, it copies the link url of the topic in the clipboard. - * This method is getting called on the onCreate() of the TopicActivity*/ + * This method is getting called on the onCreate() of the TopicActivity + */ void setToolbarOnLongClickListener(String url) { toolbar.setOnLongClickListener(view -> { //Try to set the clipboard text diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java index 1d8f1ed2..1edcd3a5 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java @@ -968,7 +968,7 @@ class TopicAdapter extends RecyclerView.Adapter { //Checks if the page to be loaded is the one already shown if (uriString.contains(StringUtils.getBaseURL(viewModel.getTopicUrl()))) { if (uriString.contains("topicseen#new") || uriString.contains("#new")) { - if (viewModel.getCurrentPageIndex() == viewModel.getPageCount()) { + if (viewModel.getCurrentPageIndex() == viewModel.getPageCount().getValue()) { //same page postFocusListener.onPostFocusChange(getItemCount() - 1); Timber.e("new"); diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/PM.java b/app/src/main/java/gr/thmmy/mthmmy/model/PM.java index 22a4a750..a4ffcd8d 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/PM.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/PM.java @@ -1,5 +1,8 @@ package gr.thmmy.mthmmy.model; +import gr.thmmy.mthmmy.utils.parsing.StringUtils; +import gr.thmmy.mthmmy.utils.parsing.ThmmyDateTimeParser; + public class PM { private String thumbnailUrl; diff --git a/app/src/main/java/gr/thmmy/mthmmy/pagination/BottomPaginationView.java b/app/src/main/java/gr/thmmy/mthmmy/pagination/BottomPaginationView.java new file mode 100644 index 00000000..b4ea7f66 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/pagination/BottomPaginationView.java @@ -0,0 +1,263 @@ +package gr.thmmy.mthmmy.pagination; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Rect; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import gr.thmmy.mthmmy.R; + +public class BottomPaginationView extends LinearLayout { + + /** + * Holds the initial time delay before a click on bottom navigation bar is considered long + */ + private final long INITIAL_DELAY = 500; + private boolean autoIncrement = false; + private boolean autoDecrement = false; + /** + * Holds the number of pages to be added or subtracted from current page on each step while a + * long click is held in either next or previous buttons + */ + private static final int SMALL_STEP = 1; + /** + * Holds the number of pages to be added or subtracted from current page on each step while a + * long click is held in either first or last buttons + */ + private static final int LARGE_STEP = 10; + /** + * Used for handling bottom navigation bar's buttons long click user interactions + */ + private final Handler repeatUpdateHandler = new Handler(); + + private ImageButton firstPage, previousPage, nextPage, lastPage; + private TextView pageIndicator; + + private int onDownPageIndex, indicatorPageIndex, totalPageCount; + + private OnPageRequestedListener onPageRequestedListener; + + public interface OnPageRequestedListener { + void onPageRequested(int index); + } + + public BottomPaginationView(Context context) { + this(context, null); + } + + public BottomPaginationView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + + LayoutInflater inflater = LayoutInflater.from(context); + inflater.inflate(R.layout.pagination, this, true); + + firstPage = findViewById(R.id.page_first_button); + previousPage = findViewById(R.id.page_previous_button); + pageIndicator = findViewById(R.id.page_indicator); + nextPage = findViewById(R.id.page_next_button); + lastPage = findViewById(R.id.page_last_button); + + initDecrementButton(firstPage, LARGE_STEP); + initDecrementButton(previousPage, SMALL_STEP); + initIncrementButton(nextPage, SMALL_STEP); + initIncrementButton(lastPage, LARGE_STEP); + } + + public void setOnPageRequestedListener(OnPageRequestedListener onPageRequestedListener) { + this.onPageRequestedListener = onPageRequestedListener; + } + + public boolean setIndicatedPageIndex(int index) { + if (index != indicatorPageIndex) { + this.indicatorPageIndex = index; + updateUI(); + return true; + } else return false; + } + + public void setTotalPageCount(int totalPageCount) { + this.totalPageCount = totalPageCount; + updateUI(); + } + + public void updateUI() { + pageIndicator.setText(indicatorPageIndex + "/" + totalPageCount); + } + + /** + * This class is used to implement the repetitive incrementPageRequestValue/decrementPageRequestValue + * of page value when long pressing one of the page navigation buttons. + */ + private class RepetitiveUpdater implements Runnable { + private final int step; + + /** + * @param step number of pages to add/subtract on each repetition + */ + RepetitiveUpdater(int step) { + this.step = step; + } + + public void run() { + long REPEAT_DELAY = 250; + if (autoIncrement) { + setIndicatedPageIndex(indicatorPageIndex+step); + repeatUpdateHandler.postDelayed(new BottomPaginationView.RepetitiveUpdater(step), REPEAT_DELAY); + } else if (autoDecrement) { + setIndicatedPageIndex(indicatorPageIndex-step); + repeatUpdateHandler.postDelayed(new BottomPaginationView.RepetitiveUpdater(step), REPEAT_DELAY); + } + } + } + + public boolean incrementPageIndicator(int step) { + int oldIndicatorIndex = indicatorPageIndex; + if (oldIndicatorIndex <= totalPageCount - step) + setIndicatedPageIndex(indicatorPageIndex + step); + else + setIndicatedPageIndex(totalPageCount); + return oldIndicatorIndex != indicatorPageIndex; + } + + public boolean decrementPageIndicator(int step) { + int oldIndicatorIndex = indicatorPageIndex; + if (oldIndicatorIndex > step) + setIndicatedPageIndex(indicatorPageIndex - step); + else + setIndicatedPageIndex(1); + return oldIndicatorIndex != indicatorPageIndex; + } + + @SuppressLint("ClickableViewAccessibility") + private void initIncrementButton(ImageButton increment, final int step) { + // Increment once for a click + increment.setOnClickListener(v -> { + if (!autoIncrement && step == LARGE_STEP) { + boolean indicatorChanged = setIndicatedPageIndex(totalPageCount); + if (indicatorChanged) onPageRequestedListener.onPageRequested(indicatorPageIndex); + } else if (!autoIncrement) { + boolean indicatorChanged = incrementPageIndicator(1); + if (indicatorChanged) onPageRequestedListener.onPageRequested(indicatorPageIndex); + } + }); + + // Auto increment for a long click + increment.setOnLongClickListener( + arg0 -> { + paginationDisable(arg0); + autoIncrement = true; + repeatUpdateHandler.postDelayed(new BottomPaginationView.RepetitiveUpdater(step), INITIAL_DELAY); + return false; + } + ); + + // When the button is released + increment.setOnTouchListener(new View.OnTouchListener() { + private Rect rect; + + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + onDownPageIndex = indicatorPageIndex; + rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); + } else if (rect != null && event.getAction() == MotionEvent.ACTION_UP && autoIncrement) { + autoIncrement = false; + paginationEnabled(true); + onPageRequestedListener.onPageRequested(indicatorPageIndex); + } else if (rect != null && event.getAction() == MotionEvent.ACTION_MOVE) { + if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { + autoIncrement = false; + setIndicatedPageIndex(onDownPageIndex); + paginationEnabled(true); + } + } + return false; + } + }); + } + + @SuppressLint("ClickableViewAccessibility") + private void initDecrementButton(ImageButton decrement, final int step) { + // Decrement once for a click + decrement.setOnClickListener(v -> { + if (!autoDecrement && step == LARGE_STEP) { + boolean indicatorChanged = setIndicatedPageIndex(1); + if (indicatorChanged) onPageRequestedListener.onPageRequested(indicatorPageIndex); + } else if (!autoDecrement) { + boolean indicatorChanged = decrementPageIndicator(1); + if (indicatorChanged) onPageRequestedListener.onPageRequested(indicatorPageIndex); + } + }); + + // Auto decrement for a long click + decrement.setOnLongClickListener( + arg0 -> { + paginationDisable(arg0); + autoDecrement = true; + repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY); + return false; + } + ); + + // When the button is released + decrement.setOnTouchListener(new View.OnTouchListener() { + private Rect rect; + + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + onDownPageIndex = indicatorPageIndex; + rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); + } else if (event.getAction() == MotionEvent.ACTION_UP && autoDecrement) { + autoDecrement = false; + paginationEnabled(true); + onPageRequestedListener.onPageRequested(indicatorPageIndex); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (rect != null && + !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { + autoIncrement = false; + setIndicatedPageIndex(onDownPageIndex); + paginationEnabled(true); + } + } + return false; + } + }); + } + + public void paginationDisable(View exception) { + if (exception == firstPage) { + previousPage.setEnabled(false); + nextPage.setEnabled(false); + lastPage.setEnabled(false); + } else if (exception == previousPage) { + firstPage.setEnabled(false); + nextPage.setEnabled(false); + lastPage.setEnabled(false); + } else if (exception == nextPage) { + firstPage.setEnabled(false); + previousPage.setEnabled(false); + lastPage.setEnabled(false); + } else if (exception == lastPage) { + firstPage.setEnabled(false); + previousPage.setEnabled(false); + nextPage.setEnabled(false); + } else { + paginationEnabled(false); + } + } + + public void paginationEnabled(boolean enabled) { + firstPage.setEnabled(enabled); + previousPage.setEnabled(enabled); + nextPage.setEnabled(enabled); + lastPage.setEnabled(enabled); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java index 4d0a83a7..a4196cc5 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java +++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java @@ -28,6 +28,7 @@ import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.model.Poll; import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.TopicItem; +import gr.thmmy.mthmmy.pagination.BottomPaginationView; import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.utils.ExternalAsyncTask; import gr.thmmy.mthmmy.utils.NetworkTask; @@ -35,7 +36,8 @@ import gr.thmmy.mthmmy.utils.parsing.StringUtils; import timber.log.Timber; public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted, - PrepareForReplyTask.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished { + PrepareForReplyTask.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished, + BottomPaginationView.OnPageRequestedListener { /** * topic state */ @@ -76,6 +78,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa * navigation bar occurs, aka the value that the page indicator shows */ private MutableLiveData pageIndicatorIndex = new MutableLiveData<>(); + private MutableLiveData pageCount = new MutableLiveData<>(); private MutableLiveData replyPageUrl = new MutableLiveData<>(); private MutableLiveData pageTopicId = new MutableLiveData<>(); @@ -87,7 +90,6 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa private MutableLiveData topicViewers = new MutableLiveData<>(); private String topicUrl; private int currentPageIndex; - private int pageCount; private MutableLiveData prepareForReplyResult = new MutableLiveData<>(); private MutableLiveData prepareForEditResult = new MutableLiveData<>(); @@ -126,7 +128,13 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa currentTopicTask.execute(StringUtils.getBaseURL(topicUrl) + "." + currentPageIndex * 15); } - public void loadPageIndicated() { + @Override + public void onPageRequested(int index) { + pageIndicatorIndex.setValue(index); + loadPageIndicated(); + } + + private void loadPageIndicated() { if (pageIndicatorIndex.getValue() == null) throw new NullPointerException("No page has been loaded yet!"); int pageRequested = pageIndicatorIndex.getValue() - 1; @@ -172,10 +180,14 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa } public void prepareForReply() { - if (replyPageUrl.getValue() == null) + if (replyPageUrl.getValue() == null || pageCount.getValue() == null || pageIndicatorIndex.getValue() ==null) throw new NullPointerException("Topic task has not finished yet!"); stopLoading(); - setPageIndicatorIndex(pageCount, true); + // go to last page for reply + if (currentPageIndex != pageCount.getValue()) { + this.pageIndicatorIndex.setValue(pageCount.getValue()); + loadPageIndicated(); + } Timber.i("Preparing for reply"); currentPrepareForReplyTask = new PrepareForReplyTask(prepareForReplyCallbacks, this, replyPageUrl.getValue()); @@ -252,9 +264,10 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa // callbacks for viewmodel @Override public void onTopicTaskCompleted(TopicTaskResult result) { + //sets Data if (result.getResultCode() == TopicTask.ResultCode.SUCCESS) { currentPageIndex = result.getCurrentPageIndex(); - pageCount = result.getPageCount(); + pageCount.setValue(result.getPageCount()); topicTreeAndMods.setValue(result.getTopicTreeAndMods()); topicViewers.setValue(result.getTopicViewers()); pageTopicId.setValue(result.getLoadedPageTopicId()); @@ -267,6 +280,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa for (int i = 0; i < result.getNewPostsList().size(); i++) isUserExtraInfoVisibile.add(false); } + // see also callback in TopicActivity, sets UI topicTaskResultCode.setValue(result.getResultCode()); } @@ -281,36 +295,6 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa prepareForEditResult.setValue(result); } - public void incrementPageRequestValue(int step, boolean changePage) { - if (pageIndicatorIndex.getValue() == null) - throw new NullPointerException("No page has been loaded yet!"); - int oldIndicatorIndex = pageIndicatorIndex.getValue(); - if (oldIndicatorIndex <= pageCount - step) - pageIndicatorIndex.setValue(pageIndicatorIndex.getValue() + step); - else - pageIndicatorIndex.setValue(pageCount); - if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) loadPageIndicated(); - } - - public void decrementPageRequestValue(int step, boolean changePage) { - if (pageIndicatorIndex.getValue() == null) - throw new NullPointerException("No page has been loaded yet!"); - int oldIndicatorIndex = pageIndicatorIndex.getValue(); - if (oldIndicatorIndex > step) - pageIndicatorIndex.setValue(pageIndicatorIndex.getValue() - step); - else - pageIndicatorIndex.setValue(1); - if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) loadPageIndicated(); - } - - public void setPageIndicatorIndex(int pageIndicatorIndex, boolean changePage) { - if (this.pageIndicatorIndex.getValue() == null) - throw new NullPointerException("No page has been loaded yet!"); - int oldIndicatorIndex = this.pageIndicatorIndex.getValue(); - this.pageIndicatorIndex.setValue(pageIndicatorIndex); - if (changePage && oldIndicatorIndex != this.pageIndicatorIndex.getValue()) loadPageIndicated(); - } - // <-------------Just getters, setters and helper methods below here----------------> public int getTopicId() { @@ -465,8 +449,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa return currentPageIndex; } - public int getPageCount() { - if (pageCount == 0) throw new NullPointerException("No page has been loaded yet!"); + public MutableLiveData getPageCount() { return pageCount; } diff --git a/app/src/main/res/layout/activity_topic.xml b/app/src/main/res/layout/activity_topic.xml index b61c41cf..7b576ffc 100644 --- a/app/src/main/res/layout/activity_topic.xml +++ b/app/src/main/res/layout/activity_topic.xml @@ -71,63 +71,14 @@ android:visibility="gone" /> - - - - - - - - - - - - + app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareLinearBehavior"/> + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index ec4e1de8..316b8d1a 100644 --- a/build.gradle +++ b/build.gradle @@ -9,11 +9,7 @@ buildscript { maven { url "https://jitpack.io" } } dependencies { -<<<<<<< HEAD - classpath 'com.android.tools.build:gradle:3.5.3' -======= - classpath 'com.android.tools.build:gradle:3.5.1' ->>>>>>> develop + classpath 'com.android.tools.build:gradle:3.6.1' classpath 'com.google.gms:google-services:4.3.2' classpath 'io.fabric.tools:gradle:1.29.0' classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7076a01e..dd0ac2c6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Sep 17 12:32:34 EEST 2019 +#Mon Mar 30 15:17:37 EEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip