diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java index ceb41e13..a85eca23 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java @@ -187,13 +187,13 @@ public class ForumFragment extends BaseFragment { private class ForumTask extends NewParseTask> { private HttpUrl forumUrl = SessionManager.forumUrl; //may change upon collapse/expand - public ForumTask(OnParseTaskStartedListener onParseTaskStartedListener, - OnParseTaskFinishedListener> onParseTaskFinishedListener) { - super(onParseTaskStartedListener, onParseTaskFinishedListener); + public ForumTask(OnTaskStartedListener onTaskStartedListener, + OnNetworkTaskFinishedListener> onParseTaskFinishedListener) { + super(onTaskStartedListener, onParseTaskFinishedListener); } @Override - protected ArrayList parse(Document document) throws ParseException { + protected ArrayList parse(Document document, Response response) throws ParseException { Elements categoryBlocks = document.select(".tborder:not([style])>table[cellpadding=5]"); if (categoryBlocks.size() != 0) { ArrayList fetchedCategories = new ArrayList<>(); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java index 98ad7f69..65b0d315 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java @@ -157,13 +157,13 @@ public class RecentFragment extends BaseFragment { //---------------------------------------ASYNC TASK----------------------------------- private class RecentTask extends NewParseTask> { - public RecentTask(OnParseTaskStartedListener onParseTaskStartedListener, - OnParseTaskFinishedListener> onParseTaskFinishedListener) { - super(onParseTaskStartedListener, onParseTaskFinishedListener); + public RecentTask(OnTaskStartedListener onTaskStartedListener, + OnNetworkTaskFinishedListener> onParseTaskFinishedListener) { + super(onTaskStartedListener, onParseTaskFinishedListener); } @Override - protected ArrayList parse(Document document) throws ParseException { + protected ArrayList parse(Document document, Response response) throws ParseException { ArrayList fetchedRecent = new ArrayList<>(); Elements recent = document.select("#block8 :first-child div"); if (!recent.isEmpty()) { diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java index 0681c4af..0747b4bf 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java @@ -185,12 +185,12 @@ public class UnreadFragment extends BaseFragment { private class UnreadTask extends NewParseTask { - UnreadTask(OnParseTaskStartedListener onParseTaskStartedListener, OnParseTaskFinishedListener onParseTaskFinishedListener) { - super(onParseTaskStartedListener, onParseTaskFinishedListener); + UnreadTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener onParseTaskFinishedListener) { + super(onTaskStartedListener, onParseTaskFinishedListener); } @Override - protected Void parse(Document document) throws ParseException { + protected Void parse(Document document, Response response) throws ParseException { Elements unread = document.select("table.bordercolor[cellspacing=1] tr:not(.titlebg)"); if (!unread.isEmpty()) { //topicSummaries.clear(); 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 00e16471..c21a5b05 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 @@ -48,9 +48,11 @@ import gr.thmmy.mthmmy.editorview.EmojiKeyboard; 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.utils.CustomLinearLayoutManager; import gr.thmmy.mthmmy.utils.HTMLUtils; import gr.thmmy.mthmmy.utils.NetworkResultCodes; +import gr.thmmy.mthmmy.utils.NetworkTask; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.viewmodel.TopicViewModel; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; @@ -84,7 +86,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo /** * Holds a list of this topic's posts */ - private ArrayList postsList; + private ArrayList topicItems; //Reply related private FloatingActionButton replyFAB; //Topic's pages related @@ -169,7 +171,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo progressBar = findViewById(R.id.progressBar); emojiKeyboard = findViewById(R.id.emoji_keyboard); - postsList = new ArrayList<>(); + topicItems = new ArrayList<>(); recyclerView = findViewById(R.id.topic_recycler_view); recyclerView.setHasFixedSize(true); @@ -178,7 +180,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo getApplicationContext(), topicPageUrl); recyclerView.setLayoutManager(layoutManager); - topicAdapter = new TopicAdapter(this, postsList); + topicAdapter = new TopicAdapter(this, topicItems); recyclerView.setAdapter(topicAdapter); replyFAB = findViewById(R.id.topic_fab); @@ -279,15 +281,15 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo } return; } else if (viewModel.isWritingReply()) { - postsList.remove(postsList.size() - 1); - topicAdapter.notifyItemRemoved(postsList.size()); + topicItems.remove(topicItems.size() - 1); + topicAdapter.notifyItemRemoved(topicItems.size()); topicAdapter.setBackButtonHidden(); viewModel.setWritingReply(false); replyFAB.show(); bottomNavBar.setVisibility(View.VISIBLE); return; } else if (viewModel.isEditingPost()) { - postsList.get(viewModel.getPostBeingEditedPosition()).setPostType(Post.TYPE_POST); + ((Post) topicItems.get(viewModel.getPostBeingEditedPosition())).setPostType(Post.TYPE_POST); topicAdapter.notifyItemChanged(viewModel.getPostBeingEditedPosition()); topicAdapter.setBackButtonHidden(); viewModel.setEditingPost(false); @@ -420,7 +422,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo } else if (rect != null && event.getAction() == MotionEvent.ACTION_UP && autoIncrement) { autoIncrement = false; paginationEnabled(true); - viewModel.performPageChange(); + 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; @@ -464,7 +466,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo } else if (event.getAction() == MotionEvent.ACTION_UP && autoDecrement) { autoDecrement = false; paginationEnabled(true); - viewModel.performPageChange(); + viewModel.loadPageIndicated(); } else if (event.getAction() == MotionEvent.ACTION_MOVE) { if (rect != null && !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { @@ -529,7 +531,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo replyFAB.show(); bottomNavBar.setVisibility(View.VISIBLE); viewModel.setWritingReply(false); - if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0) { + if ((((Post) topicItems.get(topicItems.size() - 1)).getPostNumber() + 1) % 15 == 0) { Timber.i("Reply was posted in new page. Switching to last page."); viewModel.loadUrl(ParseHelpers.getBaseURL(viewModel.getTopicUrl()) + "." + 2147483647); } else { @@ -538,8 +540,8 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo } else { Timber.w("Post reply unsuccessful"); Toast.makeText(getBaseContext(), "Post failed!", Toast.LENGTH_SHORT).show(); - recyclerView.getChildAt(postsList.size() - 1).setAlpha(1); - recyclerView.getChildAt(postsList.size() - 1).setEnabled(true); + recyclerView.getChildAt(topicItems.size() - 1).setAlpha(1); + recyclerView.getChildAt(topicItems.size() - 1).setEnabled(true); } } }); @@ -572,7 +574,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo if (result) { Timber.i("Post edit successful"); - postsList.get(position).setPostType(Post.TYPE_POST); + ((Post) topicItems.get(position)).setPostType(Post.TYPE_POST); topicAdapter.notifyItemChanged(position); replyFAB.show(); bottomNavBar.setVisibility(View.VISIBLE); @@ -597,6 +599,30 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo progressBar.setVisibility(ProgressBar.GONE); } }); + viewModel.setVoteTaskStartedListener(() -> progressBar.setVisibility(ProgressBar.VISIBLE)); + viewModel.setVoteTaskFinishedListener((resultCode, data) -> { + progressBar.setVisibility(View.GONE); + if (resultCode == NetworkResultCodes.SUCCESSFUL) { + Timber.i("Vote sent"); + viewModel.resetPage(); + } + else { + Timber.w("Failed to send vote"); + Toast.makeText(this, "Failed to send vote", Toast.LENGTH_LONG).show(); + } + }); + viewModel.setRemoveVoteTaskStartedListener(() -> progressBar.setVisibility(ProgressBar.VISIBLE)); + viewModel.setRemoveVoteTaskFinishedListener((resultCode, data) -> { + progressBar.setVisibility(View.GONE); + if (resultCode == NetworkResultCodes.SUCCESSFUL) { + Timber.i("Vote removed"); + viewModel.resetPage(); + } + else { + Timber.w("Failed to remove vote"); + Toast.makeText(this, "Failed to remove vote", Toast.LENGTH_LONG).show(); + } + }); // observe the chages in data viewModel.getPageIndicatorIndex().observe(this, pageIndicatorIndex -> { if (pageIndicatorIndex == null) return; @@ -622,11 +648,11 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo else replyFAB.show(); }); - viewModel.getPostsList().observe(this, postList -> { + viewModel.getTopicItems().observe(this, postList -> { if (postList == null) progressBar.setVisibility(ProgressBar.VISIBLE); recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug - postsList.clear(); - postsList.addAll(postList); + topicItems.clear(); + topicItems.addAll(postList); topicAdapter.notifyDataSetChanged(); }); /*viewModel.getFocusedPostIndex().observe(this, focusedPostIndex -> { @@ -643,7 +669,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo break; case NETWORK_ERROR: Timber.w("Network error on loaded page"); - if (viewModel.getPostsList().getValue() == null) { + if (viewModel.getTopicItems().getValue() == null) { // no page has been loaded yet. Give user the ability to refresh recyclerView.setVisibility(View.GONE); TextView errorTextview = findViewById(R.id.error_textview); @@ -701,9 +727,9 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo Timber.i("Prepare for reply successful"); //prepare for a reply viewModel.setWritingReply(true); - postsList.add(Post.newQuickReply()); - topicAdapter.notifyItemInserted(postsList.size()); - recyclerView.scrollToPosition(postsList.size() - 1); + topicItems.add(Post.newQuickReply()); + topicAdapter.notifyItemInserted(topicItems.size()); + recyclerView.scrollToPosition(topicItems.size() - 1); replyFAB.hide(); bottomNavBar.setVisibility(View.GONE); } else { @@ -716,7 +742,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo if (result != null && result.isSuccessful()) { Timber.i("Prepare for edit successful"); viewModel.setEditingPost(true); - postsList.get(result.getPosition()).setPostType(Post.TYPE_EDIT); + ((Post) topicItems.get(result.getPosition())).setPostType(Post.TYPE_EDIT); topicAdapter.notifyItemChanged(result.getPosition()); recyclerView.scrollToPosition(result.getPosition()); replyFAB.hide(); 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 bf9f3b52..01759f34 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 @@ -15,9 +15,11 @@ import android.support.annotation.NonNull; import android.support.v4.content.res.ResourcesCompat; import android.support.v7.app.AlertDialog; import android.support.v7.content.res.AppCompatResources; +import android.support.v7.widget.AppCompatButton; import android.support.v7.widget.RecyclerView; import android.text.InputType; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -27,16 +29,26 @@ import android.view.inputmethod.InputMethodManager; import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupWindow; +import android.widget.RadioButton; +import android.widget.RadioGroup; import android.widget.RelativeLayout; import android.widget.TextView; +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; import com.squareup.picasso.Picasso; +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -46,9 +58,11 @@ import gr.thmmy.mthmmy.activities.profile.ProfileActivity; import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.editorview.EditorView; import gr.thmmy.mthmmy.editorview.EmojiKeyboard; +import gr.thmmy.mthmmy.model.Poll; import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyFile; import gr.thmmy.mthmmy.model.ThmmyPage; +import gr.thmmy.mthmmy.model.TopicItem; import gr.thmmy.mthmmy.utils.CircleTransform; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.viewmodel.TopicViewModel; @@ -76,16 +90,16 @@ class TopicAdapter extends RecyclerView.Adapter { private final Context context; private final OnPostFocusChangeListener postFocusListener; private final EmojiKeyboard.EmojiKeyboardOwner emojiKeyboardOwner; - private final List postsList; + private final List topicItems; private TopicViewModel viewModel; /** * @param context the context of the {@link RecyclerView} - * @param postsList List of {@link Post} objects to use + * @param topicItems List of {@link Post} objects to use */ - TopicAdapter(TopicActivity context, List postsList) { + TopicAdapter(TopicActivity context, List topicItems) { this.context = context; - this.postsList = postsList; + this.topicItems = topicItems; this.postFocusListener = context; this.emojiKeyboardOwner = context; @@ -96,7 +110,8 @@ class TopicAdapter extends RecyclerView.Adapter { @Override public int getItemViewType(int position) { - return postsList.get(position).getPostType(); + if (topicItems.get(position) instanceof Poll) return Poll.TYPE_POLL; + return ((Post) topicItems.get(position)).getPostType(); } @NonNull @@ -132,6 +147,10 @@ class TopicAdapter extends RecyclerView.Adapter { editPostEdittext.requestFocus(); return new EditMessageViewHolder(view); + } else if (viewType == Poll.TYPE_POLL) { + View view = LayoutInflater.from(parent.getContext()). + inflate(R.layout.activity_topic_poll, parent, false); + return new PollViewHolder(view); } else { throw new IllegalArgumentException("Unknown view type"); } @@ -141,414 +160,496 @@ class TopicAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder currentHolder, final int position) { - if (currentHolder instanceof PostViewHolder) { - final Post currentPost = postsList.get(position); - final PostViewHolder holder = (PostViewHolder) currentHolder; - - //Post's WebView parameters - holder.post.setClickable(true); - holder.post.setWebViewClient(new LinkLauncher()); - - //Avoids errors about layout having 0 width/height - holder.thumbnail.setMinimumWidth(1); - holder.thumbnail.setMinimumHeight(1); - //Sets thumbnail size - holder.thumbnail.setMaxWidth(THUMBNAIL_SIZE); - holder.thumbnail.setMaxHeight(THUMBNAIL_SIZE); - - //noinspection ConstantConditions - Picasso.with(context) - .load(currentPost.getThumbnailURL()) - .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) - .centerCrop() - .error(ResourcesCompat.getDrawable(context.getResources() - , R.drawable.ic_default_user_thumbnail_white_24dp, null)) - .placeholder(ResourcesCompat.getDrawable(context.getResources() - , R.drawable.ic_default_user_thumbnail_white_24dp, null)) - .transform(new CircleTransform()) - .into(holder.thumbnail); - - //Sets username,submit date, index number, subject, post's and attached files texts - holder.username.setText(currentPost.getAuthor()); - holder.postDate.setText(currentPost.getPostDate()); - if (currentPost.getPostNumber() != 0) - holder.postNum.setText(context.getString( - R.string.user_number_of_posts, currentPost.getPostNumber())); - else - holder.postNum.setText(""); - holder.subject.setText(currentPost.getSubject()); - holder.post.loadDataWithBaseURL("file:///android_asset/", currentPost.getContent(), "text/html", "UTF-8", null); - if ((currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0) - || (currentPost.getLastEdit() != null)) { - holder.bodyFooterDivider.setVisibility(View.VISIBLE); - holder.postFooter.removeAllViews(); - - if (currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0) { - int filesTextColor; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - filesTextColor = context.getResources().getColor(R.color.accent, null); - } else //noinspection deprecation - filesTextColor = context.getResources().getColor(R.color.accent); - - for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) { - final TextView attached = new TextView(context); - attached.setTextSize(10f); - attached.setClickable(true); - attached.setTypeface(Typeface.createFromAsset(context.getAssets() - , "fonts/fontawesome-webfont.ttf")); - attached.setText(faIconFromFilename(attachedFile.getFilename()) + " " - + attachedFile.getFilename() + attachedFile.getFileInfo()); - attached.setTextColor(filesTextColor); - attached.setPadding(0, 3, 0, 3); - - attached.setOnClickListener(view -> ((BaseActivity) context).downloadFile(attachedFile)); - - holder.postFooter.addView(attached); - } + if (currentHolder.getItemViewType() == Poll.TYPE_POLL) { + Poll poll = (Poll) topicItems.get(position); + Poll.Entry[] entries = poll.getEntries(); + PollViewHolder holder = (PollViewHolder) currentHolder; + holder.question.setText(poll.getQuestion()); + holder.optionsLayout.removeAllViews(); + if (poll.getAvailableVoteCount() > 1) { + for (Poll.Entry entry : entries) { + CheckBox checkBox = new CheckBox(context); + checkBox.setText(entry.getEntryName()); + checkBox.setTextColor(context.getResources().getColor(R.color.primary_text)); + holder.optionsLayout.addView(checkBox); } - if (currentPost.getLastEdit() != null && currentPost.getLastEdit().length() > 0) { - int lastEditTextColor; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - lastEditTextColor = context.getResources().getColor(R.color.white, null); - } else //noinspection deprecation - lastEditTextColor = context.getResources().getColor(R.color.white); - - final TextView lastEdit = new TextView(context); - lastEdit.setTextSize(12f); - lastEdit.setText(currentPost.getLastEdit()); - lastEdit.setTextColor(lastEditTextColor); - lastEdit.setPadding(0, 3, 0, 3); - holder.postFooter.addView(lastEdit); + holder.voteChart.setVisibility(View.GONE); + holder.optionsLayout.setVisibility(View.VISIBLE); + } else if (poll.getAvailableVoteCount() == 1) { + RadioGroup radioGroup = new RadioGroup(context); + for (int i = 0; i < entries.length; i++) { + RadioButton radioButton = new RadioButton(context); + radioButton.setId(i); + radioButton.setText(entries[i].getEntryName()); + radioButton.setTextColor(context.getResources().getColor(R.color.primary_text)); + radioGroup.addView(radioButton); } + holder.optionsLayout.addView(radioGroup); + holder.voteChart.setVisibility(View.GONE); + holder.optionsLayout.setVisibility(View.VISIBLE); } else { - holder.bodyFooterDivider.setVisibility(View.GONE); - holder.postFooter.removeAllViews(); + //Showing results + holder.optionsLayout.setVisibility(View.GONE); + List valuesToCompare = new ArrayList<>(); + for (int i = 0; i < entries.length; i++) { + valuesToCompare.add(new BarEntry(i, entries[i].getVotes())); + } + BarDataSet data = new BarDataSet(valuesToCompare, "Vote Results"); + data.setColor(context.getResources().getColor(R.color.accent)); + + YAxis yAxisLeft = holder.voteChart.getAxisLeft(); + yAxisLeft.setGranularity(1f); + yAxisLeft.setTextColor(context.getResources().getColor(R.color.primary_text)); + yAxisLeft.setAxisMinimum(0); + YAxis yAxisRight = holder.voteChart.getAxisRight(); + yAxisRight.setEnabled(false); + + XAxis xAxis = holder.voteChart.getXAxis(); + xAxis.setValueFormatter((value, axis) -> entries[(int) value].getEntryName()); + xAxis.setTextColor(context.getResources().getColor(R.color.primary_text)); + xAxis.setGranularity(1f); + xAxis.setDrawGridLines(false); + xAxis.setDrawAxisLine(false); + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); + + BarData barData = new BarData(data); + barData.setValueTextColor(context.getResources().getColor(R.color.accent)); + holder.voteChart.setData(barData); + holder.voteChart.getLegend().setEnabled(false); + holder.voteChart.getDescription().setEnabled(false); + int chartHeightdp = 10 + 30 * entries.length; + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + holder.voteChart.setMinimumHeight((int) (chartHeightdp * (metrics.densityDpi / 160f))); + holder.voteChart.invalidate(); + holder.voteChart.setVisibility(View.VISIBLE); } + if (poll.getRemoveVoteUrl() != null) { + holder.removeVotesButton.setOnClickListener(v -> viewModel.removeVote()); + holder.removeVotesButton.setVisibility(View.VISIBLE); + } else holder.removeVotesButton.setVisibility(View.GONE); + if (poll.getShowVoteResultsUrl() != null) { + holder.showPollResultsButton.setOnClickListener(v -> viewModel.loadUrl(poll.getShowVoteResultsUrl())); + holder.showPollResultsButton.setVisibility(View.VISIBLE); + } else holder.showPollResultsButton.setVisibility(View.GONE); + + if (poll.getShowOptionsUrl() != null) { + holder.hidePollResultsButton.setOnClickListener(v -> viewModel.loadUrl(poll.getShowOptionsUrl())); + holder.hidePollResultsButton.setVisibility(View.VISIBLE); + } else holder.hidePollResultsButton.setVisibility(View.GONE); + if (poll.getPollFormUrl() != null) { + holder.submitButton.setOnClickListener(v -> viewModel.submitVote(holder.optionsLayout)); + holder.submitButton.setVisibility(View.VISIBLE); + } else holder.submitButton.setVisibility(View.GONE); + } else { + Post currentPost = (Post) topicItems.get(position); + if (currentHolder instanceof PostViewHolder) { + final PostViewHolder holder = (PostViewHolder) currentHolder; + + //Post's WebView parameters + holder.post.setClickable(true); + holder.post.setWebViewClient(new LinkLauncher()); + + //Avoids errors about layout having 0 width/height + holder.thumbnail.setMinimumWidth(1); + holder.thumbnail.setMinimumHeight(1); + //Sets thumbnail size + holder.thumbnail.setMaxWidth(THUMBNAIL_SIZE); + holder.thumbnail.setMaxHeight(THUMBNAIL_SIZE); + + //noinspection ConstantConditions + Picasso.with(context) + .load(currentPost.getThumbnailURL()) + .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) + .centerCrop() + .error(ResourcesCompat.getDrawable(context.getResources() + , R.drawable.ic_default_user_thumbnail_white_24dp, null)) + .placeholder(ResourcesCompat.getDrawable(context.getResources() + , R.drawable.ic_default_user_thumbnail_white_24dp, null)) + .transform(new CircleTransform()) + .into(holder.thumbnail); + + //Sets username,submit date, index number, subject, post's and attached files texts + holder.username.setText(currentPost.getAuthor()); + holder.postDate.setText(currentPost.getPostDate()); + if (currentPost.getPostNumber() != 0) + holder.postNum.setText(context.getString( + R.string.user_number_of_posts, currentPost.getPostNumber())); + else + holder.postNum.setText(""); + holder.subject.setText(currentPost.getSubject()); + holder.post.loadDataWithBaseURL("file:///android_asset/", currentPost.getContent(), "text/html", "UTF-8", null); + if ((currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0) + || (currentPost.getLastEdit() != null)) { + holder.bodyFooterDivider.setVisibility(View.VISIBLE); + holder.postFooter.removeAllViews(); + + if (currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0) { + int filesTextColor; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + filesTextColor = context.getResources().getColor(R.color.accent, null); + } else //noinspection deprecation + filesTextColor = context.getResources().getColor(R.color.accent); + + for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) { + final TextView attached = new TextView(context); + attached.setTextSize(10f); + attached.setClickable(true); + attached.setTypeface(Typeface.createFromAsset(context.getAssets() + , "fonts/fontawesome-webfont.ttf")); + attached.setText(faIconFromFilename(attachedFile.getFilename()) + " " + + attachedFile.getFilename() + attachedFile.getFileInfo()); + attached.setTextColor(filesTextColor); + attached.setPadding(0, 3, 0, 3); + + attached.setOnClickListener(view -> ((BaseActivity) context).downloadFile(attachedFile)); + + holder.postFooter.addView(attached); + } + } + if (currentPost.getLastEdit() != null && currentPost.getLastEdit().length() > 0) { + int lastEditTextColor; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + lastEditTextColor = context.getResources().getColor(R.color.white, null); + } else //noinspection deprecation + lastEditTextColor = context.getResources().getColor(R.color.white); + + final TextView lastEdit = new TextView(context); + lastEdit.setTextSize(12f); + lastEdit.setText(currentPost.getLastEdit()); + lastEdit.setTextColor(lastEditTextColor); + lastEdit.setPadding(0, 3, 0, 3); + holder.postFooter.addView(lastEdit); + } + } else { + holder.bodyFooterDivider.setVisibility(View.GONE); + holder.postFooter.removeAllViews(); + } - String mSpecialRank, mRank, mGender, mNumberOfPosts, mPersonalText; - int mNumberOfStars, mUserColor; + String mSpecialRank, mRank, mGender, mNumberOfPosts, mPersonalText; + int mNumberOfStars, mUserColor; - if (!currentPost.isDeleted()) { //Sets user's extra info - mSpecialRank = currentPost.getSpecialRank(); - mRank = currentPost.getRank(); - mGender = currentPost.getGender(); - mNumberOfPosts = currentPost.getNumberOfPosts(); - mPersonalText = currentPost.getPersonalText(); - mNumberOfStars = currentPost.getNumberOfStars(); - } else { - mSpecialRank = null; - mRank = null; - mGender = null; - mNumberOfPosts = null; - mPersonalText = null; - mNumberOfStars = 0; - } - mUserColor = currentPost.getUserColor(); - - if (!Objects.equals(mSpecialRank, "") && mSpecialRank != null) { - holder.specialRank.setText(mSpecialRank); - holder.specialRank.setVisibility(View.VISIBLE); - } else - holder.specialRank.setVisibility(View.GONE); - if (!Objects.equals(mRank, "") && mRank != null) { - holder.rank.setText(mRank); - holder.rank.setVisibility(View.VISIBLE); - } else - holder.rank.setVisibility(View.GONE); - if (!Objects.equals(mGender, "") && mGender != null) { - holder.gender.setText(mGender); - holder.gender.setVisibility(View.VISIBLE); - } else - holder.gender.setVisibility(View.GONE); - if (!Objects.equals(mNumberOfPosts, "") && mNumberOfPosts != null) { - holder.numberOfPosts.setText(mNumberOfPosts); - holder.numberOfPosts.setVisibility(View.VISIBLE); - } else - holder.numberOfPosts.setVisibility(View.GONE); - if (!Objects.equals(mPersonalText, "") && mPersonalText != null) { - holder.personalText.setText("\"" + mPersonalText + "\""); - holder.personalText.setVisibility(View.VISIBLE); - } else - holder.personalText.setVisibility(View.GONE); - if (mUserColor != USER_COLOR_YELLOW) { - holder.username.setTextColor(mUserColor); - } else { - holder.username.setTextColor(USER_COLOR_WHITE); - } - if (mNumberOfStars > 0) { - holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets() - , "fonts/fontawesome-webfont.ttf")); - - String aStar = context.getResources().getString(R.string.fa_icon_star); - StringBuilder usersStars = new StringBuilder(); - for (int i = 0; i < mNumberOfStars; ++i) { - usersStars.append(aStar); + if (!currentPost.isDeleted()) { //Sets user's extra info + mSpecialRank = currentPost.getSpecialRank(); + mRank = currentPost.getRank(); + mGender = currentPost.getGender(); + mNumberOfPosts = currentPost.getNumberOfPosts(); + mPersonalText = currentPost.getPersonalText(); + mNumberOfStars = currentPost.getNumberOfStars(); + } else { + mSpecialRank = null; + mRank = null; + mGender = null; + mNumberOfPosts = null; + mPersonalText = null; + mNumberOfStars = 0; } - holder.stars.setText(usersStars.toString()); - holder.stars.setTextColor(mUserColor); - holder.stars.setVisibility(View.VISIBLE); - } else - holder.stars.setVisibility(View.GONE); - - if (currentPost.isUserMentionedInPost()) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - holder.cardChildLinear.setBackground(context.getResources(). - getDrawable(R.drawable.mention_card, null)); - } else //noinspection deprecation - holder.cardChildLinear.setBackground(context.getResources(). - getDrawable(R.drawable.mention_card)); - } else if (mUserColor == TopicParser.USER_COLOR_PINK) { - //Special card for special member of the month! - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - holder.cardChildLinear.setBackground(context.getResources(). - getDrawable(R.drawable.member_of_the_month_card, null)); - } else //noinspection deprecation - holder.cardChildLinear.setBackground(context.getResources(). - getDrawable(R.drawable.member_of_the_month_card)); - } else holder.cardChildLinear.setBackground(null); - - //Avoid's view's visibility recycling - if (!currentPost.isDeleted() && viewModel.isUserExtraInfoVisible(holder.getAdapterPosition())) { - holder.userExtraInfo.setVisibility(View.VISIBLE); - holder.userExtraInfo.setAlpha(1.0f); - - holder.username.setMaxLines(Integer.MAX_VALUE); - holder.username.setEllipsize(null); - - holder.subject.setTextColor(Color.parseColor("#FFFFFF")); - holder.subject.setMaxLines(Integer.MAX_VALUE); - holder.subject.setEllipsize(null); - } else { - holder.userExtraInfo.setVisibility(View.GONE); - holder.userExtraInfo.setAlpha(0.0f); + mUserColor = currentPost.getUserColor(); + + if (!Objects.equals(mSpecialRank, "") && mSpecialRank != null) { + holder.specialRank.setText(mSpecialRank); + holder.specialRank.setVisibility(View.VISIBLE); + } else + holder.specialRank.setVisibility(View.GONE); + if (!Objects.equals(mRank, "") && mRank != null) { + holder.rank.setText(mRank); + holder.rank.setVisibility(View.VISIBLE); + } else + holder.rank.setVisibility(View.GONE); + if (!Objects.equals(mGender, "") && mGender != null) { + holder.gender.setText(mGender); + holder.gender.setVisibility(View.VISIBLE); + } else + holder.gender.setVisibility(View.GONE); + if (!Objects.equals(mNumberOfPosts, "") && mNumberOfPosts != null) { + holder.numberOfPosts.setText(mNumberOfPosts); + holder.numberOfPosts.setVisibility(View.VISIBLE); + } else + holder.numberOfPosts.setVisibility(View.GONE); + if (!Objects.equals(mPersonalText, "") && mPersonalText != null) { + holder.personalText.setText("\"" + mPersonalText + "\""); + holder.personalText.setVisibility(View.VISIBLE); + } else + holder.personalText.setVisibility(View.GONE); + if (mUserColor != USER_COLOR_YELLOW) { + holder.username.setTextColor(mUserColor); + } else { + holder.username.setTextColor(USER_COLOR_WHITE); + } + if (mNumberOfStars > 0) { + holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets() + , "fonts/fontawesome-webfont.ttf")); + + String aStar = context.getResources().getString(R.string.fa_icon_star); + StringBuilder usersStars = new StringBuilder(); + for (int i = 0; i < mNumberOfStars; ++i) { + usersStars.append(aStar); + } + holder.stars.setText(usersStars.toString()); + holder.stars.setTextColor(mUserColor); + holder.stars.setVisibility(View.VISIBLE); + } else + holder.stars.setVisibility(View.GONE); + + if (currentPost.isUserMentionedInPost()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + holder.cardChildLinear.setBackground(context.getResources(). + getDrawable(R.drawable.mention_card, null)); + } else //noinspection deprecation + holder.cardChildLinear.setBackground(context.getResources(). + getDrawable(R.drawable.mention_card)); + } else if (mUserColor == TopicParser.USER_COLOR_PINK) { + //Special card for special member of the month! + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + holder.cardChildLinear.setBackground(context.getResources(). + getDrawable(R.drawable.member_of_the_month_card, null)); + } else //noinspection deprecation + holder.cardChildLinear.setBackground(context.getResources(). + getDrawable(R.drawable.member_of_the_month_card)); + } else holder.cardChildLinear.setBackground(null); - holder.username.setMaxLines(1); - holder.username.setEllipsize(TextUtils.TruncateAt.END); + //Avoid's view's visibility recycling + if (!currentPost.isDeleted() && viewModel.isUserExtraInfoVisible(holder.getAdapterPosition())) { + holder.userExtraInfo.setVisibility(View.VISIBLE); + holder.userExtraInfo.setAlpha(1.0f); - holder.subject.setTextColor(Color.parseColor("#757575")); - holder.subject.setMaxLines(1); - holder.subject.setEllipsize(TextUtils.TruncateAt.END); - } - if (!currentPost.isDeleted()) { - //Sets graphics behavior - holder.thumbnail.setOnClickListener(view -> { - //Clicking the thumbnail opens user's profile - Intent intent = new Intent(context, ProfileActivity.class); - Bundle extras = new Bundle(); - extras.putString(BUNDLE_PROFILE_URL, currentPost.getProfileURL()); - if (currentPost.getThumbnailURL() == null) - extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, ""); - else - extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, currentPost.getThumbnailURL()); - extras.putString(BUNDLE_PROFILE_USERNAME, currentPost.getAuthor()); - intent.putExtras(extras); - intent.setFlags(FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - }); - holder.header.setOnClickListener(v -> { - //Clicking the header makes it expand/collapse - viewModel.toggleUserInfo(holder.getAdapterPosition()); - TopicAnimations.animateUserExtraInfoVisibility(holder.username, - holder.subject, Color.parseColor("#FFFFFF"), - Color.parseColor("#757575"), holder.userExtraInfo); - }); - //Clicking the expanded part of a header (the extra info) makes it collapse - holder.userExtraInfo.setOnClickListener(v -> { - viewModel.hideUserInfo(holder.getAdapterPosition()); - TopicAnimations.animateUserExtraInfoVisibility(holder.username, - holder.subject, Color.parseColor("#FFFFFF"), - Color.parseColor("#757575"), (LinearLayout) v); - }); - } else { - holder.header.setOnClickListener(null); - holder.userExtraInfo.setOnClickListener(null); - } + holder.username.setMaxLines(Integer.MAX_VALUE); + holder.username.setEllipsize(null); - holder.overflowButton.setOnClickListener(view -> { - //Inflates the popup menu content - LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - if (layoutInflater == null) { - return; - } - View popUpContent = layoutInflater.inflate(R.layout.activity_topic_overflow_menu, null); - - //Creates the PopupWindow - final PopupWindow popUp = new PopupWindow(holder.overflowButton.getContext()); - popUp.setContentView(popUpContent); - popUp.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT); - popUp.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT); - popUp.setFocusable(true); - - TextView shareButton = popUpContent.findViewById(R.id.post_share_button); - Drawable shareStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_share_white_24dp); - shareButton.setCompoundDrawablesRelativeWithIntrinsicBounds(shareStartDrawable, null, null, null); - shareButton.setOnClickListener(v -> { - Intent sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.setType("text/plain"); - sendIntent.putExtra(Intent.EXTRA_TEXT, currentPost.getPostURL()); - context.startActivity(Intent.createChooser(sendIntent, "Share via")); - popUp.dismiss(); - }); + holder.subject.setTextColor(Color.parseColor("#FFFFFF")); + holder.subject.setMaxLines(Integer.MAX_VALUE); + holder.subject.setEllipsize(null); + } else { + holder.userExtraInfo.setVisibility(View.GONE); + holder.userExtraInfo.setAlpha(0.0f); - final TextView editPostButton = popUpContent.findViewById(R.id.edit_post); - Drawable editStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_edit_white_24dp); - editPostButton.setCompoundDrawablesRelativeWithIntrinsicBounds(editStartDrawable, null, null, null); + holder.username.setMaxLines(1); + holder.username.setEllipsize(TextUtils.TruncateAt.END); - if (viewModel.isEditingPost() || currentPost.getPostEditURL() == null || currentPost.getPostEditURL().equals("")) { - editPostButton.setVisibility(View.GONE); - } else { - editPostButton.setOnClickListener(v -> { - viewModel.prepareForEdit(position, postsList.get(position).getPostEditURL()); - popUp.dismiss(); + holder.subject.setTextColor(Color.parseColor("#757575")); + holder.subject.setMaxLines(1); + holder.subject.setEllipsize(TextUtils.TruncateAt.END); + } + if (!currentPost.isDeleted()) { + //Sets graphics behavior + holder.thumbnail.setOnClickListener(view -> { + //Clicking the thumbnail opens user's profile + Intent intent = new Intent(context, ProfileActivity.class); + Bundle extras = new Bundle(); + extras.putString(BUNDLE_PROFILE_URL, currentPost.getProfileURL()); + if (currentPost.getThumbnailURL() == null) + extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, ""); + else + extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, currentPost.getThumbnailURL()); + extras.putString(BUNDLE_PROFILE_USERNAME, currentPost.getAuthor()); + intent.putExtras(extras); + intent.setFlags(FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + }); + holder.header.setOnClickListener(v -> { + //Clicking the header makes it expand/collapse + viewModel.toggleUserInfo(holder.getAdapterPosition()); + TopicAnimations.animateUserExtraInfoVisibility(holder.username, + holder.subject, Color.parseColor("#FFFFFF"), + Color.parseColor("#757575"), holder.userExtraInfo); }); + //Clicking the expanded part of a header (the extra info) makes it collapse + holder.userExtraInfo.setOnClickListener(v -> { + viewModel.hideUserInfo(holder.getAdapterPosition()); + TopicAnimations.animateUserExtraInfoVisibility(holder.username, + holder.subject, Color.parseColor("#FFFFFF"), + Color.parseColor("#757575"), (LinearLayout) v); + }); + } else { + holder.header.setOnClickListener(null); + holder.userExtraInfo.setOnClickListener(null); } - TextView deletePostButton = popUpContent.findViewById(R.id.delete_post); - - if (currentPost.getPostDeleteURL() == null || currentPost.getPostDeleteURL().equals("")) { - deletePostButton.setVisibility(View.GONE); - } else { - Drawable deleteStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_delete_white_24dp); - deletePostButton.setCompoundDrawablesRelativeWithIntrinsicBounds(deleteStartDrawable, null, null, null); - popUpContent.findViewById(R.id.delete_post).setOnClickListener(v -> { - new AlertDialog.Builder(holder.overflowButton.getContext()) - .setTitle("Delete post") - .setMessage("Do you really want to delete this post?") - .setIcon(android.R.drawable.ic_dialog_alert) - .setPositiveButton(android.R.string.yes, (dialog, whichButton) -> viewModel.deletePost(currentPost.getPostDeleteURL())) - .setNegativeButton(android.R.string.no, null).show(); + holder.overflowButton.setOnClickListener(view -> { + //Inflates the popup menu content + LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + if (layoutInflater == null) { + return; + } + View popUpContent = layoutInflater.inflate(R.layout.activity_topic_overflow_menu, null); + + //Creates the PopupWindow + final PopupWindow popUp = new PopupWindow(holder.overflowButton.getContext()); + popUp.setContentView(popUpContent); + popUp.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT); + popUp.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT); + popUp.setFocusable(true); + + TextView shareButton = popUpContent.findViewById(R.id.post_share_button); + Drawable shareStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_share_white_24dp); + shareButton.setCompoundDrawablesRelativeWithIntrinsicBounds(shareStartDrawable, null, null, null); + shareButton.setOnClickListener(v -> { + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.setType("text/plain"); + sendIntent.putExtra(Intent.EXTRA_TEXT, currentPost.getPostURL()); + context.startActivity(Intent.createChooser(sendIntent, "Share via")); popUp.dismiss(); }); - } - //Displays the popup - popUp.showAsDropDown(holder.overflowButton); - }); + final TextView editPostButton = popUpContent.findViewById(R.id.edit_post); + Drawable editStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_edit_white_24dp); + editPostButton.setCompoundDrawablesRelativeWithIntrinsicBounds(editStartDrawable, null, null, null); + + if (viewModel.isEditingPost() || currentPost.getPostEditURL() == null || currentPost.getPostEditURL().equals("")) { + editPostButton.setVisibility(View.GONE); + } else { + editPostButton.setOnClickListener(v -> { + viewModel.prepareForEdit(position, currentPost.getPostEditURL()); + popUp.dismiss(); + }); + } - //noinspection PointlessBooleanExpression,ConstantConditions - if (!BaseActivity.getSessionManager().isLoggedIn() || !viewModel.canReply()) { - holder.quoteToggle.setVisibility(View.GONE); - } else { - if (viewModel.getToQuoteList().contains(currentPost.getPostIndex())) - holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked_accent_24dp); - else - holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked_24dp); - //Sets graphics behavior - holder.quoteToggle.setOnClickListener(view -> { - viewModel.postIndexToggle(currentPost.getPostIndex()); + TextView deletePostButton = popUpContent.findViewById(R.id.delete_post); + + if (currentPost.getPostDeleteURL() == null || currentPost.getPostDeleteURL().equals("")) { + deletePostButton.setVisibility(View.GONE); + } else { + Drawable deleteStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_delete_white_24dp); + deletePostButton.setCompoundDrawablesRelativeWithIntrinsicBounds(deleteStartDrawable, null, null, null); + popUpContent.findViewById(R.id.delete_post).setOnClickListener(v -> { + new AlertDialog.Builder(holder.overflowButton.getContext()) + .setTitle("Delete post") + .setMessage("Do you really want to delete this post?") + .setIcon(android.R.drawable.ic_dialog_alert) + .setPositiveButton(android.R.string.yes, (dialog, whichButton) -> viewModel.deletePost(currentPost.getPostDeleteURL())) + .setNegativeButton(android.R.string.no, null).show(); + popUp.dismiss(); + }); + } + + //Displays the popup + popUp.showAsDropDown(holder.overflowButton); + }); + + //noinspection PointlessBooleanExpression,ConstantConditions + if (!BaseActivity.getSessionManager().isLoggedIn() || !viewModel.canReply()) { + holder.quoteToggle.setVisibility(View.GONE); + } else { if (viewModel.getToQuoteList().contains(currentPost.getPostIndex())) holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked_accent_24dp); else holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked_24dp); - }); - } - } else if (currentHolder instanceof QuickReplyViewHolder) { - final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder; - - //noinspection ConstantConditions - Picasso.with(context) - .load(getSessionManager().getAvatarLink()) - .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) - .centerCrop() - .error(ResourcesCompat.getDrawable(context.getResources() - , R.drawable.ic_default_user_thumbnail_white_24dp, null)) - .placeholder(ResourcesCompat.getDrawable(context.getResources() - , R.drawable.ic_default_user_thumbnail_white_24dp, null)) - .transform(new CircleTransform()) - .into(holder.thumbnail); - holder.username.setText(getSessionManager().getUsername()); - holder.quickReplySubject.setText("Re: " + viewModel.getTopicTitle().getValue()); - holder.quickReplySubject.setRawInputType(InputType.TYPE_CLASS_TEXT); - holder.quickReplySubject.setImeOptions(EditorInfo.IME_ACTION_DONE); - - holder.replyEditor.setEmojiKeyboardOwner(emojiKeyboardOwner); - InputConnection ic = holder.replyEditor.getInputConnection(); - emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic); - holder.replyEditor.updateEmojiKeyboardVisibility(); - holder.replyEditor.getEditText().setOnFocusChangeListener((v, hasFocus) -> { - InputConnection ic12 = holder.replyEditor.getInputConnection(); - emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic12); - holder.replyEditor.updateEmojiKeyboardVisibility(); - }); - - holder.replyEditor.setText(viewModel.getBuildedQuotes()); - holder.replyEditor.setOnSubmitListener(view -> { - if (holder.quickReplySubject.getText().toString().isEmpty()) return; - if (holder.replyEditor.getText().toString().isEmpty()) { - holder.replyEditor.setError("Required"); - return; - } - InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - holder.itemView.setAlpha(0.5f); - holder.itemView.setEnabled(false); - emojiKeyboardOwner.setEmojiKeyboardVisible(false); - - viewModel.postReply(context, holder.quickReplySubject.getText().toString(), - holder.replyEditor.getText().toString()); - }); - holder.replyEditor.setOnClickListener(view -> holder.replyEditor.setError(null)); - - if (backPressHidden) { - holder.replyEditor.requestFocus(); - backPressHidden = false; - } - } else if (currentHolder instanceof EditMessageViewHolder) { - final EditMessageViewHolder holder = (EditMessageViewHolder) currentHolder; - - //noinspection ConstantConditions - Picasso.with(context) - .load(getSessionManager().getAvatarLink()) - .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) - .centerCrop() - .error(ResourcesCompat.getDrawable(context.getResources() - , R.drawable.ic_default_user_thumbnail_white_24dp, null)) - .placeholder(ResourcesCompat.getDrawable(context.getResources() - , R.drawable.ic_default_user_thumbnail_white_24dp, null)) - .transform(new CircleTransform()) - .into(holder.thumbnail); - holder.username.setText(getSessionManager().getUsername()); - holder.editSubject.setText(postsList.get(position).getSubject()); - holder.editSubject.setRawInputType(InputType.TYPE_CLASS_TEXT); - holder.editSubject.setImeOptions(EditorInfo.IME_ACTION_DONE); - - holder.editEditor.setEmojiKeyboardOwner(emojiKeyboardOwner); - InputConnection ic = holder.editEditor.getInputConnection(); - emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic); - holder.editEditor.updateEmojiKeyboardVisibility(); - holder.editEditor.setText(viewModel.getPostBeingEditedText()); - holder.editEditor.getEditText().setOnFocusChangeListener((v, hasFocus) -> { - if (hasFocus) { - InputConnection ic1 = holder.editEditor.getInputConnection(); - emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic1); - holder.editEditor.updateEmojiKeyboardVisibility(); + //Sets graphics behavior + holder.quoteToggle.setOnClickListener(view -> { + viewModel.postIndexToggle(currentPost.getPostIndex()); + if (viewModel.getToQuoteList().contains(currentPost.getPostIndex())) + holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked_accent_24dp); + else + holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked_24dp); + }); } - }); - holder.editEditor.setOnSubmitListener(view -> { - if (holder.editSubject.getText().toString().isEmpty()) return; - if (holder.editEditor.getText().toString().isEmpty()) { - holder.editEditor.setError("Required"); - return; + } else if (currentHolder instanceof QuickReplyViewHolder) { + final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder; + + //noinspection ConstantConditions + Picasso.with(context) + .load(getSessionManager().getAvatarLink()) + .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) + .centerCrop() + .error(ResourcesCompat.getDrawable(context.getResources() + , R.drawable.ic_default_user_thumbnail_white_24dp, null)) + .placeholder(ResourcesCompat.getDrawable(context.getResources() + , R.drawable.ic_default_user_thumbnail_white_24dp, null)) + .transform(new CircleTransform()) + .into(holder.thumbnail); + holder.username.setText(getSessionManager().getUsername()); + holder.quickReplySubject.setText("Re: " + viewModel.getTopicTitle().getValue()); + holder.quickReplySubject.setRawInputType(InputType.TYPE_CLASS_TEXT); + holder.quickReplySubject.setImeOptions(EditorInfo.IME_ACTION_DONE); + + holder.replyEditor.setEmojiKeyboardOwner(emojiKeyboardOwner); + InputConnection ic = holder.replyEditor.getInputConnection(); + emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic); + holder.replyEditor.updateEmojiKeyboardVisibility(); + holder.replyEditor.getEditText().setOnFocusChangeListener((v, hasFocus) -> { + InputConnection ic12 = holder.replyEditor.getInputConnection(); + emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic12); + holder.replyEditor.updateEmojiKeyboardVisibility(); + }); + + holder.replyEditor.setText(viewModel.getBuildedQuotes()); + holder.replyEditor.setOnSubmitListener(view -> { + if (holder.quickReplySubject.getText().toString().isEmpty()) return; + if (holder.replyEditor.getText().toString().isEmpty()) { + holder.replyEditor.setError("Required"); + return; + } + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + holder.itemView.setAlpha(0.5f); + holder.itemView.setEnabled(false); + emojiKeyboardOwner.setEmojiKeyboardVisible(false); + + viewModel.postReply(context, holder.quickReplySubject.getText().toString(), + holder.replyEditor.getText().toString()); + }); + holder.replyEditor.setOnClickListener(view -> holder.replyEditor.setError(null)); + + if (backPressHidden) { + holder.replyEditor.requestFocus(); + backPressHidden = false; } - InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - holder.itemView.setAlpha(0.5f); - holder.itemView.setEnabled(false); - emojiKeyboardOwner.setEmojiKeyboardVisible(false); + } else if (currentHolder instanceof EditMessageViewHolder) { + final EditMessageViewHolder holder = (EditMessageViewHolder) currentHolder; + + //noinspection ConstantConditions + Picasso.with(context) + .load(getSessionManager().getAvatarLink()) + .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) + .centerCrop() + .error(ResourcesCompat.getDrawable(context.getResources() + , R.drawable.ic_default_user_thumbnail_white_24dp, null)) + .placeholder(ResourcesCompat.getDrawable(context.getResources() + , R.drawable.ic_default_user_thumbnail_white_24dp, null)) + .transform(new CircleTransform()) + .into(holder.thumbnail); + holder.username.setText(getSessionManager().getUsername()); + holder.editSubject.setText(currentPost.getSubject()); + holder.editSubject.setRawInputType(InputType.TYPE_CLASS_TEXT); + holder.editSubject.setImeOptions(EditorInfo.IME_ACTION_DONE); + + holder.editEditor.setEmojiKeyboardOwner(emojiKeyboardOwner); + InputConnection ic = holder.editEditor.getInputConnection(); + emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic); + holder.editEditor.updateEmojiKeyboardVisibility(); + holder.editEditor.setText(viewModel.getPostBeingEditedText()); + holder.editEditor.getEditText().setOnFocusChangeListener((v, hasFocus) -> { + if (hasFocus) { + InputConnection ic1 = holder.editEditor.getInputConnection(); + emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic1); + holder.editEditor.updateEmojiKeyboardVisibility(); + } + }); + holder.editEditor.setOnSubmitListener(view -> { + if (holder.editSubject.getText().toString().isEmpty()) return; + if (holder.editEditor.getText().toString().isEmpty()) { + holder.editEditor.setError("Required"); + return; + } + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + holder.itemView.setAlpha(0.5f); + holder.itemView.setEnabled(false); + emojiKeyboardOwner.setEmojiKeyboardVisible(false); - viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editEditor.getText().toString()); - }); + viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editEditor.getText().toString()); + }); - if (backPressHidden) { - holder.editEditor.requestFocus(); - backPressHidden = false; + if (backPressHidden) { + holder.editEditor.requestFocus(); + backPressHidden = false; + } } } } @Override public int getItemCount() { - return postsList.size(); + return topicItems.size(); } /** @@ -637,6 +738,27 @@ class TopicAdapter extends RecyclerView.Adapter { } } + static class PollViewHolder extends RecyclerView.ViewHolder { + final TextView question, errorTooManySelected; + final LinearLayout optionsLayout; + final AppCompatButton submitButton; + final AppCompatButton removeVotesButton, showPollResultsButton, hidePollResultsButton; + final HorizontalBarChart voteChart; + + public PollViewHolder(View itemView) { + super(itemView); + + question = itemView.findViewById(R.id.question_textview); + optionsLayout = itemView.findViewById(R.id.options_layout); + submitButton = itemView.findViewById(R.id.submit_button); + removeVotesButton = itemView.findViewById(R.id.remove_vote_button); + showPollResultsButton = itemView.findViewById(R.id.show_poll_results_button); + hidePollResultsButton = itemView.findViewById(R.id.show_poll_options_button); + errorTooManySelected = itemView.findViewById(R.id.error_too_many_checked); + voteChart = itemView.findViewById(R.id.vote_chart); + } + } + /** * This class is used to handle link clicks in WebViews. When link url is one that the app can * handle internally, it does. Otherwise user is prompt to open the link in a browser. @@ -680,8 +802,8 @@ class TopicAdapter extends RecyclerView.Adapter { if (tmpUrlSbstr.contains("msg")) tmpUrlSbstr = tmpUrlSbstr.substring(0, tmpUrlSbstr.indexOf("msg") - 1); int testAgainst = Integer.parseInt(tmpUrlSbstr); - for (int i = 0; i < postsList.size(); i++) { - if (postsList.get(i).getPostIndex() == testAgainst) { + for (int i = 0; i < topicItems.size(); i++) { + if (topicItems.get(i) instanceof Post && ((Post) topicItems.get(i)).getPostIndex() == testAgainst) { //same page Timber.e(Integer.toString(i)); postFocusListener.onPostFocusChange(i); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java index b0132a14..9c987964 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java @@ -1,6 +1,7 @@ package gr.thmmy.mthmmy.activities.topic; import android.graphics.Color; +import android.util.Log; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -13,11 +14,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.regex.Matcher; import java.util.regex.Pattern; import gr.thmmy.mthmmy.base.BaseActivity; +import gr.thmmy.mthmmy.model.Poll; import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyFile; +import gr.thmmy.mthmmy.model.TopicItem; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import timber.log.Timber; @@ -146,10 +150,16 @@ public class TopicParser { * @return {@link ArrayList} of {@link Post}s * @see org.jsoup.Jsoup Jsoup */ - public static ArrayList parseTopic(Document topic, ParseHelpers.Language language) { + public static ArrayList parseTopic(Document topic, ParseHelpers.Language language) { //Method's variables final int NO_INDEX = -1; - ArrayList parsedPostsList = new ArrayList<>(); + + ArrayList parsedPostsList = new ArrayList<>(); + + Poll poll = findPoll(topic); + if (poll != null) + parsedPostsList.add(poll); + Elements postRows; //Each row is a post @@ -468,6 +478,88 @@ public class TopicParser { return parsedPostsList; } + private static Poll findPoll(Document topic) { + Pattern integerPattern = Pattern.compile("[0-9]+"); + Elements tables = topic.select("table"); + for (int i = 0; i < tables.size(); i++) { + try { + Element image = tables.get(i).child(0).child(0).child(0); + if (image.html().contains("Poll") || image.html().contains("Ψηφοφορία")) { + // has poll in english + String question; + ArrayList entries = new ArrayList<>(); + int availableVoteCount = 0; + String pollFormUrl = null, sc = null, removeVoteUrl = null, showVoteResultsUrl = null, + showOptionsUrl = null; + + Element secondRow = tables.get(i).select("tr[class=windowbg]").first(); + Element secondColumn = secondRow.child(1); + String columnString = secondColumn.outerHtml(); + question = columnString.substring(columnString.indexOf('>') + 1, columnString.indexOf('<', 2)).trim(); + + Element form = secondColumn.select("form").first(); + if (form != null) { + // english poll in vote mode + pollFormUrl = form.attr("action"); + sc = form.select("input[name=sc]").first().attr("value"); + + int rowIndex = -1; + Elements possibleEntriesRows = form.child(0).child(0).children(); + for (int j = 0; j < possibleEntriesRows.size(); j++) { + if (possibleEntriesRows.get(j).select("input").size() > 0) { + rowIndex = j; + break; + } + } + String entriesRaw = form.child(0).child(0).child(rowIndex).child(0).html(); + Matcher entryMatcher = Pattern.compile(">[^<]+ 0) { + showVoteResultsUrl = links.first().attr("href"); + } + } else { + // english poll in results mode + Elements optionRows = secondColumn.child(0).child(0).select("table").first().child(0).children(); + for (int j = 0; j < optionRows.size(); j++) { + String optionName = optionRows.get(j).child(0).text(); + String voteCountDescription = optionRows.get(j).child(1).text(); + Matcher integerMatcher = integerPattern.matcher(voteCountDescription); + integerMatcher.find(); + int voteCount = Integer.parseInt(voteCountDescription.substring(integerMatcher.start(), + integerMatcher.end())); + entries.add(new Poll.Entry(optionName, voteCount)); + } + + Elements links = secondColumn.select("a"); + if (links != null && links.size() > 0) { + if (links.first().text().equals("Remove Vote") || links.first().text().equals("Αφαίρεση ψήφου")) + removeVoteUrl = links.first().attr("href"); + else if (links.first().text().equals("Voting options") || links.first().text().equals("Επιλογές ψηφοφορίας")) + showOptionsUrl = links.first().attr("href"); + } + } + return new Poll(question, entries.toArray(new Poll.Entry[0]), availableVoteCount, + pollFormUrl, sc, removeVoteUrl, showVoteResultsUrl, showOptionsUrl); + } + } catch (Exception e) { + Timber.v(e, "Could not parse a poll"); + } + } + return null; + } + /** * Returns the color of a user according to user's rank on forum. * diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java index 41563ae5..b277d2bd 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java @@ -12,8 +12,8 @@ import okhttp3.Response; public class DeleteTask extends NetworkTask { - public DeleteTask(OnParseTaskStartedListener onParseTaskStartedListener, OnParseTaskFinishedListener onParseTaskFinishedListener) { - super(onParseTaskStartedListener, onParseTaskFinishedListener); + public DeleteTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener onParseTaskFinishedListener) { + super(onTaskStartedListener, onParseTaskFinishedListener); } @Override @@ -28,7 +28,7 @@ public class DeleteTask extends NetworkTask { } @Override - protected Void performTask(Document document) { + protected Void performTask(Document document, Response response) { return null; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/RemoveVoteTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/RemoveVoteTask.java new file mode 100644 index 00000000..f94ae027 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/RemoveVoteTask.java @@ -0,0 +1,20 @@ +package gr.thmmy.mthmmy.activities.topic.tasks; + +import org.jsoup.nodes.Document; + +import gr.thmmy.mthmmy.utils.NetworkResultCodes; +import gr.thmmy.mthmmy.utils.NetworkTask; +import okhttp3.Response; + +public class RemoveVoteTask extends NetworkTask { + + @Override + protected Void performTask(Document document, Response response) { + return null; + } + + @Override + protected int getResultCode(Response response, Void data) { + return NetworkResultCodes.SUCCESSFUL; + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/SubmitVoteTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/SubmitVoteTask.java new file mode 100644 index 00000000..ec3773f7 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/SubmitVoteTask.java @@ -0,0 +1,48 @@ +package gr.thmmy.mthmmy.activities.topic.tasks; + +import org.jsoup.nodes.Document; + +import java.io.IOException; + +import gr.thmmy.mthmmy.utils.NetworkResultCodes; +import gr.thmmy.mthmmy.utils.NetworkTask; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class SubmitVoteTask extends NetworkTask { + + private int[] votes; + + public SubmitVoteTask(int... votes) { + this.votes = votes; + } + + @Override + protected Response sendRequest(OkHttpClient client, String... input) throws IOException { + MultipartBody.Builder postBodyBuilder = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("sc", input[1]); + for (int vote : votes) { + postBodyBuilder.addFormDataPart("options[]", Integer.toString(vote)); + } + + Request voteRequest = new Request.Builder() + .url(input[0]) + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") + .post(postBodyBuilder.build()) + .build(); + return client.newCall(voteRequest).execute(); + } + + @Override + protected Void performTask(Document document, Response response) { + return null; + } + + @Override + protected int getResultCode(Response response, Void data) { + return NetworkResultCodes.SUCCESSFUL; + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java index 7c5e5d1f..fa4f9d2d 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java @@ -13,6 +13,7 @@ import gr.thmmy.mthmmy.activities.topic.TopicParser; import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyPage; +import gr.thmmy.mthmmy.model.TopicItem; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import okhttp3.Request; import okhttp3.Response; @@ -92,14 +93,14 @@ public class TopicTask extends AsyncTask { //Finds number of pages int pageCount = TopicParser.parseTopicNumberOfPages(topic, currentPageIndex, language); - ArrayList newPostsList = TopicParser.parseTopic(topic, language); + ArrayList newPostsList = TopicParser.parseTopic(topic, language); int loadedPageTopicId = Integer.parseInt(ThmmyPage.getTopicId(newPageUrl)); //Finds the position of the focused message if present int focusedPostIndex = 0; for (int i = 0; i < newPostsList.size(); ++i) { - if (newPostsList.get(i).getPostIndex() == postFocus) { + if (newPostsList.get(i) instanceof Post && ((Post) newPostsList.get(i)).getPostIndex() == postFocus) { focusedPostIndex = i; break; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTaskResult.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTaskResult.java index c713da1f..3a4080fb 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTaskResult.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTaskResult.java @@ -3,6 +3,7 @@ package gr.thmmy.mthmmy.activities.topic.tasks; import java.util.ArrayList; import gr.thmmy.mthmmy.model.Post; +import gr.thmmy.mthmmy.model.TopicItem; public class TopicTaskResult { private final TopicTask.ResultCode resultCode; @@ -16,7 +17,7 @@ public class TopicTaskResult { * This topic's reply url */ private final String replyPageUrl; - private final ArrayList newPostsList; + private final ArrayList newPostsList; /** * The topicId of the loaded page */ @@ -38,7 +39,7 @@ public class TopicTaskResult { private final String topicViewers; public TopicTaskResult(TopicTask.ResultCode resultCode, String topicTitle, - String replyPageUrl, ArrayList newPostsList, int loadedPageTopicId, + String replyPageUrl, ArrayList newPostsList, int loadedPageTopicId, int currentPageIndex, int pageCount, int focusedPostIndex, String topicTreeAndMods, String topicViewers) { this.resultCode = resultCode; @@ -65,7 +66,7 @@ public class TopicTaskResult { return replyPageUrl; } - public ArrayList getNewPostsList() { + public ArrayList getNewPostsList() { return newPostsList; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Poll.java b/app/src/main/java/gr/thmmy/mthmmy/model/Poll.java new file mode 100644 index 00000000..071557e9 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/model/Poll.java @@ -0,0 +1,108 @@ +package gr.thmmy.mthmmy.model; + +import java.text.DecimalFormat; + +public class Poll extends TopicItem { + public static final int TYPE_POLL = 3; + + private final String question; + private Entry[] entries; + private int availableVoteCount; + private String pollFormUrl, sc, removeVoteUrl, showVoteResultsUrl, showOptionsUrl; + + public Poll(String question, Entry[] entries, int availableVoteCount, String pollFormUrl, String sc, + String removeVoteUrl, String showVoteResultsUrl, String showOptionsUrl) { + this.question = question; + this.entries = entries; + this.availableVoteCount = availableVoteCount; + this.pollFormUrl = pollFormUrl; + this.sc = sc; + this.removeVoteUrl = removeVoteUrl; + this.showVoteResultsUrl = showVoteResultsUrl; + this.showOptionsUrl = showOptionsUrl; + } + + public int totalVotes() { + int sum = 0; + for (Entry entry : entries) { + sum += entry.votes; + } + return sum; + } + + public String getVotePercentage(int index) { + DecimalFormat format = new DecimalFormat(".#"); + double percentage = 100 * ((double) entries[index].votes / (double) totalVotes()); + return format.format(percentage); + } + + public String getQuestion() { + return question; + } + + public Entry[] getEntries() { + return entries; + } + + public int getAvailableVoteCount() { + return availableVoteCount; + } + + public String getPollFormUrl() { + return pollFormUrl; + } + + public String getSc() { + return sc; + } + + public String getRemoveVoteUrl() { + return removeVoteUrl; + } + + public String getShowVoteResultsUrl() { + return showVoteResultsUrl; + } + + public String getShowOptionsUrl() { + return showOptionsUrl; + } + + public static class Entry { + private final String entryName; + private int votes; + + public Entry(String entryName, int votes) { + this.entryName = entryName; + this.votes = votes; + } + + /** + * Constructor for entry with unknown number of votes + * + * @param entryName + * The name of the entry + */ + public Entry(String entryName) { + this.entryName = entryName; + votes = -1; + } + + public String getEntryName() { + return entryName; + } + + public int getVotes() { + return votes; + } + + public void setVotes(int votes) { + this.votes = votes; + } + + @Override + public String toString() { + return "Vote label:" + entryName + ", num votes:" + votes; + } + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java index 078386dc..1113ffbc 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java @@ -15,7 +15,7 @@ import java.util.Objects; * gender, number of posts, personal text and number of start to be described in addition to * previous fields.

*/ -public class Post { +public class Post extends TopicItem { public static final int TYPE_POST = 0; public static final int TYPE_QUICK_REPLY = 1; public static final int TYPE_EDIT = 2; diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java b/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java index bc451fd1..c500c50a 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java @@ -3,6 +3,8 @@ package gr.thmmy.mthmmy.model; import android.net.Uri; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import timber.log.Timber; @@ -180,10 +182,10 @@ public class ThmmyPage { public static String getTopicId(String topicUrl) { if (resolvePageCategory(Uri.parse(topicUrl)) == PageCategory.TOPIC) { - String returnString = topicUrl.substring(topicUrl.indexOf("topic=") + 6); - if (returnString.contains(".")) - returnString = returnString.substring(0, returnString.indexOf(".")); - return returnString; + Matcher topicIdMatcher = Pattern.compile("topic=[0-9]+").matcher(topicUrl); + if (topicIdMatcher.find()) { + return topicUrl.substring(topicIdMatcher.start() + 6, topicIdMatcher.end()); + } else return null; } return null; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/TopicItem.java b/app/src/main/java/gr/thmmy/mthmmy/model/TopicItem.java new file mode 100644 index 00000000..17a8c6ef --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/model/TopicItem.java @@ -0,0 +1,5 @@ +package gr.thmmy.mthmmy.model; + +public abstract class TopicItem { + +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ExternalAsyncTask.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ExternalAsyncTask.java index 82b535b6..d20d29dc 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ExternalAsyncTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ExternalAsyncTask.java @@ -4,77 +4,77 @@ import android.os.AsyncTask; public abstract class ExternalAsyncTask extends AsyncTask { - protected OnParseTaskStartedListener onParseTaskStartedListener; - protected OnParseTaskCancelledListener onParseTaskCancelledListener; - protected OnParseTaskFinishedListener onParseTaskFinishedListener; + protected OnTaskStartedListener onTaskStartedListener; + protected OnTaskCancelledListener onTaskCancelledListener; + protected OnTaskFinishedListener onTaskFinishedListener; @Override protected void onPreExecute() { - if (onParseTaskStartedListener != null) - onParseTaskStartedListener.onParseStart(); + if (onTaskStartedListener != null) + onTaskStartedListener.onTaskStarted(); else super.onPreExecute(); } @Override protected void onCancelled() { - if (onParseTaskCancelledListener != null) - onParseTaskCancelledListener.onParseCancel(); + if (onTaskCancelledListener != null) + onTaskCancelledListener.onTaskCanceled(); else super.onCancelled(); } @Override protected void onCancelled(V v) { - if (onParseTaskCancelledListener != null) - onParseTaskCancelledListener.onParseCancel(); + if (onTaskCancelledListener != null) + onTaskCancelledListener.onTaskCanceled(); else super.onCancelled(); } @Override protected void onPostExecute(V v) { - if (onParseTaskFinishedListener != null) - onParseTaskFinishedListener.onParseFinish(v); + if (onTaskFinishedListener != null) + onTaskFinishedListener.onTaskFinished(v); else super.onPostExecute(v); } - public ExternalAsyncTask(OnParseTaskStartedListener onParseTaskStartedListener, OnParseTaskCancelledListener onParseTaskCancelledListener, - OnParseTaskFinishedListener onParseTaskFinishedListener) { - this.onParseTaskStartedListener = onParseTaskStartedListener; - this.onParseTaskCancelledListener = onParseTaskCancelledListener; - this.onParseTaskFinishedListener = onParseTaskFinishedListener; + public ExternalAsyncTask(OnTaskStartedListener onTaskStartedListener, OnTaskCancelledListener onTaskCancelledListener, + OnTaskFinishedListener onTaskFinishedListener) { + this.onTaskStartedListener = onTaskStartedListener; + this.onTaskCancelledListener = onTaskCancelledListener; + this.onTaskFinishedListener = onTaskFinishedListener; } - public ExternalAsyncTask(OnParseTaskStartedListener onParseTaskStartedListener, OnParseTaskFinishedListener onParseTaskFinishedListener) { - this.onParseTaskStartedListener = onParseTaskStartedListener; - this.onParseTaskFinishedListener = onParseTaskFinishedListener; + public ExternalAsyncTask(OnTaskStartedListener onTaskStartedListener, OnTaskFinishedListener onTaskFinishedListener) { + this.onTaskStartedListener = onTaskStartedListener; + this.onTaskFinishedListener = onTaskFinishedListener; } public ExternalAsyncTask() { } - public void setOnParseTaskStartedListener(OnParseTaskStartedListener onParseTaskStartedListener) { - this.onParseTaskStartedListener = onParseTaskStartedListener; + public void setOnTaskStartedListener(OnTaskStartedListener onTaskStartedListener) { + this.onTaskStartedListener = onTaskStartedListener; } - public void setOnParseTaskCancelledListener(OnParseTaskCancelledListener onParseTaskCancelledListener) { - this.onParseTaskCancelledListener = onParseTaskCancelledListener; + public void setOnTaskCancelledListener(OnTaskCancelledListener onTaskCancelledListener) { + this.onTaskCancelledListener = onTaskCancelledListener; } - public void setOnParseTaskFinishedListener(OnParseTaskFinishedListener onParseTaskFinishedListener) { - this.onParseTaskFinishedListener = onParseTaskFinishedListener; + public void setOnTaskFinishedListener(OnTaskFinishedListener onTaskFinishedListener) { + this.onTaskFinishedListener = onTaskFinishedListener; } - public interface OnParseTaskStartedListener { - void onParseStart(); + public interface OnTaskStartedListener { + void onTaskStarted(); } - public interface OnParseTaskCancelledListener { - void onParseCancel(); + public interface OnTaskCancelledListener { + void onTaskCanceled(); } - public interface OnParseTaskFinishedListener { - void onParseFinish(V result); + public interface OnTaskFinishedListener { + void onTaskFinished(V result); } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/NetworkTask.java b/app/src/main/java/gr/thmmy/mthmmy/utils/NetworkTask.java index 6793120a..cb613f26 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/NetworkTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/NetworkTask.java @@ -14,17 +14,17 @@ import timber.log.Timber; public abstract class NetworkTask extends ExternalAsyncTask> { - protected OnParseTaskFinishedListener onParseTaskFinishedListener; + protected OnNetworkTaskFinishedListener onNetworkTaskFinishedListener; - public NetworkTask(OnParseTaskStartedListener onParseTaskStartedListener, OnParseTaskCancelledListener onParseTaskCancelledListener, - OnParseTaskFinishedListener onParseTaskFinishedListener) { - super(onParseTaskStartedListener, onParseTaskCancelledListener, null); - this.onParseTaskFinishedListener = onParseTaskFinishedListener; + public NetworkTask(OnTaskStartedListener onTaskStartedListener, OnTaskCancelledListener onTaskCancelledListener, + OnNetworkTaskFinishedListener onNetworkTaskFinishedListener) { + super(onTaskStartedListener, onTaskCancelledListener, null); + this.onNetworkTaskFinishedListener = onNetworkTaskFinishedListener; } - public NetworkTask(OnParseTaskStartedListener onParseTaskStartedListener, OnParseTaskFinishedListener onParseTaskFinishedListener) { - super(onParseTaskStartedListener, null); - this.onParseTaskFinishedListener = onParseTaskFinishedListener; + public NetworkTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener onNetworkTaskFinishedListener) { + super(onTaskStartedListener, null); + this.onNetworkTaskFinishedListener = onNetworkTaskFinishedListener; } public NetworkTask() {} @@ -49,7 +49,7 @@ public abstract class NetworkTask extends ExternalAsyncTask return new Parcel<>(NetworkResultCodes.NETWORK_ERROR, null); } try { - T data = performTask(Jsoup.parse(responseBodyString)); + T data = performTask(Jsoup.parse(responseBodyString), response); int resultCode = getResultCode(response, data); return new Parcel<>(resultCode, data); } catch (ParseException pe) { @@ -63,8 +63,8 @@ public abstract class NetworkTask extends ExternalAsyncTask @Override protected void onPostExecute(Parcel tParcel) { - if (onParseTaskFinishedListener != null) - onParseTaskFinishedListener.onParseFinish(tParcel.getResultCode(), tParcel.getData()); + if (onNetworkTaskFinishedListener != null) + onNetworkTaskFinishedListener.onNetworkTaskFinished(tParcel.getResultCode(), tParcel.getData()); else super.onPostExecute(tParcel); } @@ -77,15 +77,15 @@ public abstract class NetworkTask extends ExternalAsyncTask return client.newCall(request).execute(); } - protected abstract T performTask(Document document); + protected abstract T performTask(Document document, Response response); protected abstract int getResultCode(Response response, T data); - public void setOnParseTaskFinishedListener(OnParseTaskFinishedListener onParseTaskFinishedListener) { - this.onParseTaskFinishedListener = onParseTaskFinishedListener; + public void setOnNetworkTaskFinishedListener(OnNetworkTaskFinishedListener onNetworkTaskFinishedListener) { + this.onNetworkTaskFinishedListener = onNetworkTaskFinishedListener; } - public interface OnParseTaskFinishedListener { - void onParseFinish(int resultCode, T data); + public interface OnNetworkTaskFinishedListener { + void onNetworkTaskFinished(int resultCode, T data); } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NewParseTask.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NewParseTask.java index 51749f2a..def14c2f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NewParseTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NewParseTask.java @@ -3,28 +3,29 @@ package gr.thmmy.mthmmy.utils.parsing; import org.jsoup.nodes.Document; import gr.thmmy.mthmmy.utils.NetworkTask; +import okhttp3.Response; public abstract class NewParseTask extends NetworkTask { - public NewParseTask(OnParseTaskStartedListener onParseTaskStartedListener, OnParseTaskCancelledListener onParseTaskCancelledListener, - OnParseTaskFinishedListener onParseTaskFinishedListener) { - super(onParseTaskStartedListener, onParseTaskCancelledListener, onParseTaskFinishedListener); + public NewParseTask(OnTaskStartedListener onTaskStartedListener, OnTaskCancelledListener onTaskCancelledListener, + OnNetworkTaskFinishedListener onParseTaskFinishedListener) { + super(onTaskStartedListener, onTaskCancelledListener, onParseTaskFinishedListener); } - public NewParseTask(OnParseTaskStartedListener onParseTaskStartedListener, OnParseTaskFinishedListener onParseTaskFinishedListener) { - super(onParseTaskStartedListener, onParseTaskFinishedListener); + public NewParseTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener onParseTaskFinishedListener) { + super(onTaskStartedListener, onParseTaskFinishedListener); } public NewParseTask() {} @Override - protected final T performTask(Document document) { + protected final T performTask(Document document, Response response) { try { - return parse(document); + return parse(document, response); } catch (Exception e) { throw new ParseException("Parse failed.", e); } } - protected abstract T parse (Document document) throws ParseException; + protected abstract T parse (Document document, Response response) throws ParseException; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java index 0034ffc4..52669582 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java @@ -5,6 +5,8 @@ import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import timber.log.Timber; @@ -179,11 +181,10 @@ public class ParseHelpers { * @return the base URL of the given topic */ public static String getBaseURL(String topicURL) { - if (topicURL.substring(0, topicURL.lastIndexOf(".")).contains("topic=")) - return topicURL.substring(0, topicURL.lastIndexOf(".")); - else { - Timber.wtf(new ParseException("Could not parse base URL of topic")); - return ""; - } + String forumUrl = "https://www.thmmy.gr/smf/index.php?"; + Matcher baseUrlMatcher = Pattern.compile("topic=[0-9]+").matcher(topicURL); + if (baseUrlMatcher.find()) + return forumUrl + topicURL.substring(baseUrlMatcher.start(), baseUrlMatcher.end()); + else return ""; } } 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 78284b11..1175980a 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java +++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java @@ -5,6 +5,9 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.AsyncTask; import android.preference.PreferenceManager; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.RadioGroup; import java.util.ArrayList; @@ -15,12 +18,18 @@ import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditResult; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReply; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyResult; +import gr.thmmy.mthmmy.activities.topic.tasks.RemoveVoteTask; import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask; +import gr.thmmy.mthmmy.activities.topic.tasks.SubmitVoteTask; import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask; import gr.thmmy.mthmmy.activities.topic.tasks.TopicTaskResult; 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.session.SessionManager; +import gr.thmmy.mthmmy.utils.ExternalAsyncTask; +import gr.thmmy.mthmmy.utils.NetworkTask; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import timber.log.Timber; @@ -50,12 +59,16 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa //callbacks for topic activity private TopicTask.TopicTaskObserver topicTaskObserver; - private DeleteTask.OnParseTaskStartedListener deleteTaskStartedListener; - private DeleteTask.OnParseTaskFinishedListener deleteTaskFinishedListener; + private ExternalAsyncTask.OnTaskStartedListener deleteTaskStartedListener; + private NetworkTask.OnNetworkTaskFinishedListener deleteTaskFinishedListener; private ReplyTask.ReplyTaskCallbacks replyFinishListener; private PrepareForEditTask.PrepareForEditCallbacks prepareForEditCallbacks; private EditTask.EditTaskCallbacks editTaskCallbacks; private PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks; + private ExternalAsyncTask.OnTaskStartedListener voteTaskStartedListener; + private NetworkTask.OnNetworkTaskFinishedListener voteTaskFinishedListener; + private ExternalAsyncTask.OnTaskStartedListener removeVoteTaskStartedListener; + private NetworkTask.OnNetworkTaskFinishedListener removeVoteTaskFinishedListener; /** * Holds the value (index) of the page to be requested when a user interaction with bottom @@ -66,7 +79,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa private MutableLiveData replyPageUrl = new MutableLiveData<>(); private MutableLiveData pageTopicId = new MutableLiveData<>(); private MutableLiveData topicTitle = new MutableLiveData<>(); - private MutableLiveData> postsList = new MutableLiveData<>(); + private MutableLiveData> topicItems = new MutableLiveData<>(); private MutableLiveData focusedPostIndex = new MutableLiveData<>(); private MutableLiveData topicTaskResultCode = new MutableLiveData<>(); private MutableLiveData topicTreeAndMods = new MutableLiveData<>(); @@ -91,7 +104,17 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa loadUrl(topicUrl); } - public void performPageChange() { + /** + * In contrasto to {@link TopicViewModel#reloadPage()} this method gets rid of any arguements + * in the url before refreshing + */ + public void resetPage() { + if (topicUrl == null) throw new NullPointerException("No topic task has been requested yet!"); + Timber.i("Reseting page"); + loadUrl(ParseHelpers.getBaseURL(topicUrl) + "." + String.valueOf(currentPageIndex * 15)); + } + + public void loadPageIndicated() { if (pageIndicatorIndex.getValue() == null) throw new NullPointerException("No page has been loaded yet!"); int pageRequested = pageIndicatorIndex.getValue() - 1; @@ -104,6 +127,35 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa } } + public void submitVote(LinearLayout optionsLayout) { + if (topicItems.getValue() == null) throw new NullPointerException("Topic task has not finished yet!"); + ArrayList votes = new ArrayList<>(); + if (optionsLayout.getChildAt(0) instanceof RadioGroup) { + RadioGroup optionsRadioGroup = (RadioGroup) optionsLayout.getChildAt(0); + votes.add(optionsRadioGroup.getCheckedRadioButtonId()); + } else if (optionsLayout.getChildAt(0) instanceof CheckBox) { + for (int i = 0; i < optionsLayout.getChildCount(); i++) { + if (((CheckBox) optionsLayout.getChildAt(i)).isChecked()) + votes.add(i); + } + } + int[] votesArray = new int[votes.size()]; + for (int i = 0; i < votes.size(); i++) votesArray[i] = votes.get(i); + Poll poll = (Poll) topicItems.getValue().get(0); + SubmitVoteTask submitVoteTask = new SubmitVoteTask(votesArray); + submitVoteTask.setOnTaskStartedListener(voteTaskStartedListener); + submitVoteTask.setOnNetworkTaskFinishedListener(voteTaskFinishedListener); + submitVoteTask.execute(poll.getPollFormUrl(), poll.getSc()); + } + + public void removeVote() { + if (topicItems.getValue() == null) throw new NullPointerException("Topic task has not finished yet!"); + RemoveVoteTask removeVoteTask = new RemoveVoteTask(); + removeVoteTask.setOnTaskStartedListener(removeVoteTaskStartedListener); + removeVoteTask.setOnNetworkTaskFinishedListener(removeVoteTaskFinishedListener); + removeVoteTask.execute(((Poll) topicItems.getValue().get(0)).getRemoveVoteUrl()); + } + public void prepareForReply() { if (replyPageUrl.getValue() == null) throw new NullPointerException("Topic task has not finished yet!"); @@ -194,7 +246,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa replyPageUrl.setValue(result.getReplyPageUrl()); topicTitle.setValue(result.getTopicTitle()); pageIndicatorIndex.setValue(result.getCurrentPageIndex()); - postsList.setValue(result.getNewPostsList()); + topicItems.setValue(result.getNewPostsList()); focusedPostIndex.setValue(result.getFocusedPostIndex()); isUserExtraInfoVisibile.clear(); for (int i = 0; i < result.getNewPostsList().size(); i++) { @@ -223,7 +275,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa pageIndicatorIndex.setValue(pageIndicatorIndex.getValue() + step); } else pageIndicatorIndex.setValue(pageCount); - if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) performPageChange(); + if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) loadPageIndicated(); } public void decrementPageRequestValue(int step, boolean changePage) { @@ -234,7 +286,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa pageIndicatorIndex.setValue(pageIndicatorIndex.getValue() - step); } else pageIndicatorIndex.setValue(1); - if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) performPageChange(); + if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) loadPageIndicated(); } public void setPageIndicatorIndex(int pageIndicatorIndex, boolean changePage) { @@ -242,11 +294,28 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa throw new NullPointerException("No page has been loaded yet!"); int oldIndicatorIndex = this.pageIndicatorIndex.getValue(); this.pageIndicatorIndex.setValue(pageIndicatorIndex); - if (changePage && oldIndicatorIndex != this.pageIndicatorIndex.getValue()) performPageChange(); + if (changePage && oldIndicatorIndex != this.pageIndicatorIndex.getValue()) loadPageIndicated(); } // <-------------Just getters, setters and helper methods below here----------------> + + public void setRemoveVoteTaskStartedListener(ExternalAsyncTask.OnTaskStartedListener removeVoteTaskStartedListener) { + this.removeVoteTaskStartedListener = removeVoteTaskStartedListener; + } + + public void setRemoveVoteTaskFinishedListener(NetworkTask.OnNetworkTaskFinishedListener removeVoteTaskFinishedListener) { + this.removeVoteTaskFinishedListener = removeVoteTaskFinishedListener; + } + + public void setVoteTaskStartedListener(ExternalAsyncTask.OnTaskStartedListener voteTaskStartedListener) { + this.voteTaskStartedListener = voteTaskStartedListener; + } + + public void setVoteTaskFinishedListener(NetworkTask.OnNetworkTaskFinishedListener voteTaskFinishedListener) { + this.voteTaskFinishedListener = voteTaskFinishedListener; + } + public MutableLiveData getTopicViewers() { return topicViewers; } @@ -263,8 +332,8 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa return focusedPostIndex; } - public MutableLiveData> getPostsList() { - return postsList; + public MutableLiveData> getTopicItems() { + return topicItems; } public MutableLiveData getReplyPageUrl() { @@ -315,11 +384,11 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa } - public void setDeleteTaskStartedListener(DeleteTask.OnParseTaskStartedListener deleteTaskStartedListener) { + public void setDeleteTaskStartedListener(ExternalAsyncTask.OnTaskStartedListener deleteTaskStartedListener) { this.deleteTaskStartedListener = deleteTaskStartedListener; } - public void setDeleteTaskFinishedListener(DeleteTask.OnParseTaskFinishedListener deleteTaskFinishedListener) { + public void setDeleteTaskFinishedListener(NetworkTask.OnNetworkTaskFinishedListener deleteTaskFinishedListener) { this.deleteTaskFinishedListener = deleteTaskFinishedListener; } @@ -394,8 +463,8 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa } public int postCount() { - if (postsList.getValue() == null) + if (topicItems.getValue() == null) throw new NullPointerException("No page has been loaded yet!"); - return postsList.getValue().size(); + return topicItems.getValue().size(); } } diff --git a/app/src/main/res/layout/activity_topic_poll.xml b/app/src/main/res/layout/activity_topic_poll.xml new file mode 100644 index 00000000..84a7e275 --- /dev/null +++ b/app/src/main/res/layout/activity_topic_poll.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c52cf546..071bb9dc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -59,6 +59,10 @@ Network error retry This topic is either missing or off limits to you + Remove vote + show results + You may only select %d options + hide results Username