Browse Source

Topic reply.

pull/24/head
Apostolos Fanakis 8 years ago
parent
commit
2a1860f05a
  1. 50
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/ReplyParser.java
  2. 43
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  3. 184
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  4. 3
      app/src/main/res/layout/activity_topic_post_row.xml
  5. 99
      app/src/main/res/layout/activity_topic_quick_reply_row.xml
  6. 1
      app/src/main/res/values/strings.xml

50
app/src/main/java/gr/thmmy/mthmmy/activities/topic/ReplyParser.java

@ -0,0 +1,50 @@
package gr.thmmy.mthmmy.activities.topic;
import android.util.Log;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import okhttp3.Response;
class ReplyParser {
/**
* Debug Tag for logging debug output to LogCat
*/
@SuppressWarnings("unused")
private static final String TAG = "ReplyParser";
enum REPLY_STATUS {
SUCCESSFUL, NO_SUBJECT, EMPTY_BODY, NEW_REPLY_WHILE_POSTING, NOT_FOUND, SESSION_ENDED, OTHER_ERROR
}
static REPLY_STATUS replyStatus(Response response) throws IOException {
if (response.code() == 404) return REPLY_STATUS.NOT_FOUND;
if (response.code() < 200 || response.code() >= 400) return REPLY_STATUS.OTHER_ERROR;
String finalUrl = response.request().url().toString();
if (finalUrl.contains("action=post")) {
Document postErrorPage = Jsoup.parse(response.body().string());
String[] errors = postErrorPage.select("tr[id=errors] div[id=error_list]").first()
.toString().split("<br>");
for (int i = 0; i < errors.length; ++i) { //TODO test
Log.d("TAG", String.valueOf(i));
Log.d("TAG", errors[i]);
}
for (String error : errors) {
if (error.contains("Your session timed out while posting") ||
error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή"))
return REPLY_STATUS.SESSION_ENDED;
if (error.contains("No subject was filled in")
|| error.contains("Δεν δόθηκε τίτλος"))
return REPLY_STATUS.NO_SUBJECT;
if (error.contains("The message body was left empty")
|| error.contains("Δεν δόθηκε κείμενο για το μήνυμα"))
return REPLY_STATUS.EMPTY_BODY;
}
return REPLY_STATUS.NEW_REPLY_WHILE_POSTING;
}
return REPLY_STATUS.SUCCESSFUL;
}
}

43
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java

@ -40,6 +40,8 @@ import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import static gr.thmmy.mthmmy.activities.topic.ReplyParser.replyStatus;
/**
* Activity for topics. When creating an Intent of this activity you need to bundle a <b>String</b>
* containing this topics's url using the key {@link #BUNDLE_TOPIC_URL} and a <b>String</b> containing
@ -84,6 +86,7 @@ public class TopicActivity extends BaseActivity {
private static final int LARGE_STEP = 10;
private Integer pageRequestValue;
//Bottom navigation graphics
LinearLayout bottomNavBar;
private ImageButton firstPage;
private ImageButton previousPage;
private TextView pageIndicator;
@ -136,12 +139,12 @@ public class TopicActivity extends BaseActivity {
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(layoutManager);
topicAdapter = new TopicAdapter(this, postsList, topicTask);
topicAdapter = new TopicAdapter(this, postsList, topicTask, topicTitle);
recyclerView.setAdapter(topicAdapter);
replyFAB = (FloatingActionButton) findViewById(R.id.topic_fab);
replyFAB.setEnabled(false);
final LinearLayout bottomNavBar = (LinearLayout) findViewById(R.id.bottom_navigation_bar);
bottomNavBar = (LinearLayout) findViewById(R.id.bottom_navigation_bar);
if (!sessionManager.isLoggedIn()) replyFAB.hide();
else {
replyFAB.setOnClickListener(new View.OnClickListener() {
@ -341,7 +344,7 @@ public class TopicActivity extends BaseActivity {
paginationEnabled(true);
changePage(pageRequestValue - 1);
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) {
if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { //TODO fix bug
autoIncrement = false;
incrementPageRequestValue(thisPage - pageRequestValue);
paginationEnabled(true);
@ -437,11 +440,12 @@ public class TopicActivity extends BaseActivity {
return SAME_PAGE;
}
}
} else if (Integer.parseInt(newPageUrl.substring(base_url.length() + 1)) / 15 + 1 == thisPage)
} else if (Integer.parseInt(newPageUrl.substring(base_url.length() + 1)) / 15 + 1 == thisPage) //TODO fix bug
return SAME_PAGE;
} else if (!Objects.equals(loadedPageUrl, "")) topicTitle = null;
loadedPageUrl = newPageUrl;
replyPageUrl = null;
Request request = new Request.Builder()
.url(newPageUrl)
.build();
@ -477,7 +481,7 @@ public class TopicActivity extends BaseActivity {
progressBar.setVisibility(ProgressBar.INVISIBLE);
topicAdapter.customNotifyDataSetChanged(new TopicTask());
if (replyPageUrl == null) replyFAB.setVisibility(View.GONE);
if (replyPageUrl == null) replyFAB.hide();
if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true);
//Set current page
@ -521,6 +525,8 @@ public class TopicActivity extends BaseActivity {
//Find reply page url
{
Element replyButton = topic.select("a:has(img[alt=Reply])").first();
if (replyButton == null)
replyButton = topic.select("a:has(img[alt=Απάντηση])").first();
if (replyButton != null) replyPageUrl = replyButton.attr("href");
}
@ -581,16 +587,14 @@ public class TopicActivity extends BaseActivity {
subject = document.select("input[name=subject]").first().attr("value");
topic = document.select("input[name=topic]").first().attr("value");
} catch (IOException e) {
Report.i(TAG, "Post failed.", e);
Report.e(TAG, "Post failed.", e);
return false;
} catch (Selector.SelectorParseException e) {
Report.e(TAG, "Post failed.", e);
return false;
}
RequestBody postBody = null;
postBody = new MultipartBody.Builder()
RequestBody postBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("message", message[0])
.addFormDataPart("num_replies", numReplies)
@ -608,10 +612,19 @@ public class TopicActivity extends BaseActivity {
try {
client.newCall(post).execute();
client.newCall(post).execute();
return true;
Response response = client.newCall(post).execute();
switch (replyStatus(response)) {
case SUCCESSFUL:
return true;
case NEW_REPLY_WHILE_POSTING:
//TODO this...
return true;
default:
Report.e(TAG, "Malformed post. Request string:\n" + post.toString());
return true;
}
} catch (IOException e) {
Report.i(TAG, "Post failed.", e);
Report.e(TAG, "Post failed.", e);
return false;
}
}
@ -619,6 +632,12 @@ public class TopicActivity extends BaseActivity {
@Override
protected void onPostExecute(Boolean result) {
progressBar.setVisibility(ProgressBar.GONE);
replyFAB.setVisibility(View.VISIBLE);
bottomNavBar.setVisibility(View.VISIBLE);
if (!result)
Toast.makeText(TopicActivity.this, "Post failed!", Toast.LENGTH_SHORT).show();
paginationEnabled(true);
replyFAB.setEnabled(true);
}
}
}

184
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java

@ -10,12 +10,14 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.widget.AppCompatImageButton;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextUtils;
import android.util.Log;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@ -59,6 +61,7 @@ import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_THUMBNAIL_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME;
import static gr.thmmy.mthmmy.base.BaseActivity.getSessionManager;
/**
* Custom {@link android.support.v7.widget.RecyclerView.Adapter} used for topics.
@ -73,6 +76,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
*/
private static int THUMBNAIL_SIZE;
private final Context context;
private String topicTitle;
private ArrayList<Integer> toQuoteList = new ArrayList<>();
private final List<Post> postsList;
/**
@ -99,13 +103,16 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private TopicActivity.ReplyTask replyTask;
private final int VIEW_TYPE_POST = 0;
private final int VIEW_TYPE_QUICK_REPLY = 1;
private boolean firstTime = false;
private String[] replyDataHolder = new String[2];
private int replySubject = 0, replyText = 1;
/**
* @param context the context of the {@link RecyclerView}
* @param postsList List of {@link Post} objects to use
*/
TopicAdapter(Context context, List<Post> postsList, TopicActivity.TopicTask topicTask) {
TopicAdapter(Context context, List<Post> postsList, TopicActivity.TopicTask topicTask
, String topicTitle) {
this.context = context;
this.postsList = postsList;
@ -115,11 +122,11 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
viewProperties.add(new boolean[3]);
}
this.topicTask = topicTask;
this.topicTitle = topicTitle;
}
void prepareForReply(TopicActivity.ReplyTask replyTask) {
this.replyTask = replyTask;
firstTime = true;
}
@Override
@ -136,12 +143,22 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
} else if (viewType == VIEW_TYPE_QUICK_REPLY) {
View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.activity_topic_quick_reply_row, parent, false);
return new QuickReplyViewHolder(view);
//Default post subject
replyDataHolder[replySubject] = "Re: " + topicTitle;
//Build quotes
String quotes = "";
for (int quotePosition : toQuoteList) {
quotes += buildQuote(quotePosition);
}
if (!Objects.equals(quotes, ""))
replyDataHolder[replyText] = quotes;
return new QuickReplyViewHolder(view, new CustomEditTextListener(replySubject),
new CustomEditTextListener(replyText));
}
return null;
}
@SuppressLint("SetJavaScriptEnabled")
@SuppressLint({"SetJavaScriptEnabled", "SetTextI18n"})
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder currentHolder, final int position) {
if (currentHolder instanceof PostViewHolder) {
@ -407,52 +424,24 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
holder.post.setOnTouchListener(new CustomTouchListener(holder.post, holder.cardView));
} else if (currentHolder instanceof QuickReplyViewHolder) {
final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder;
if (firstTime) {
//Build quotes
String quotes = "";
for (int quotePosition : toQuoteList) {
Date postDate = null;
{
String date = postsList.get(quotePosition).getPostDate();
if (date != null) {
DateFormat format = new SimpleDateFormat("MMMM d, yyyy, h:m:s a", Locale.ENGLISH);
if (date.contains("Today")) {
date = date.replace("Today at",
Calendar.getInstance().getDisplayName(Calendar.MONTH,
Calendar.LONG, Locale.ENGLISH)
+ " " + Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
+ ", " + Calendar.getInstance().get(Calendar.YEAR) + ",");
} else if (date.contains("Σήμερα")) {
date = date.replace("Σήμερα στις",
Calendar.getInstance().getDisplayName(Calendar.MONTH,
Calendar.LONG, Locale.ENGLISH)
+ " " + Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
+ ", " + Calendar.getInstance().get(Calendar.YEAR) + ",");
if (date.contains("πμ")) date = date.replace("πμ", "am");
if (date.contains("μμ")) date = date.replace("μμ", "pm");
}
Log.d(TAG, date);
try {
postDate = format.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
if (postsList.get(quotePosition).getPostIndex() != 0) {
assert postDate != null;
quotes += "[quote author=" + postsList.get(quotePosition).getAuthor()
+ " link=topic=68525.msg" + postsList.get(quotePosition).getPostIndex()
+ "#msg" + postsList.get(quotePosition).getPostIndex()
+ " date=" + postDate.getTime() / 1000 + "]"
+ "\n" + postsList.get(quotePosition).getContent()
+ "\n" + "[/quote]" + "\n\n";
}
}
holder.quickReply.setText(quotes);
}
//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, null))
.placeholder(ResourcesCompat.getDrawable(context.getResources()
, R.drawable.ic_default_user_thumbnail, null))
.transform(new CircleTransform())
.into(holder.thumbnail);
holder.username.setText(getSessionManager().getUsername());
holder.quickReplySubject.setText(replyDataHolder[replySubject]);
if (replyDataHolder[replyText] != null && !Objects.equals(replyDataHolder[replyText], ""))
holder.quickReply.setText(replyDataHolder[replyText]);
holder.submitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@ -531,12 +520,23 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
* Custom {@link RecyclerView.ViewHolder} implementation
*/
private static class QuickReplyViewHolder extends RecyclerView.ViewHolder {
final EditText quickReply;
final ImageView thumbnail;
final TextView username;
final EditText quickReply, quickReplySubject;
final AppCompatImageButton submitButton;
final CustomEditTextListener replySubject, replyText;
QuickReplyViewHolder(View quickReply) {
QuickReplyViewHolder(View quickReply, CustomEditTextListener replySubject
, CustomEditTextListener replyText) {
super(quickReply);
thumbnail = (ImageView) quickReply.findViewById(R.id.thumbnail);
username = (TextView) quickReply.findViewById(R.id.username);
this.quickReply = (EditText) quickReply.findViewById(R.id.quick_reply_text);
this.replyText = replyText;
this.quickReply.addTextChangedListener(replyText);
quickReplySubject = (EditText) quickReply.findViewById(R.id.quick_reply_subject);
this.replySubject = replySubject;
quickReplySubject.addTextChangedListener(replySubject);
submitButton = (AppCompatImageButton) quickReply.findViewById(R.id.quick_reply_submit);
}
}
@ -684,6 +684,82 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
}
private class CustomEditTextListener implements TextWatcher {
private int positionInDataHolder;
CustomEditTextListener(int positionInDataHolder) {
this.positionInDataHolder = positionInDataHolder;
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
replyDataHolder[positionInDataHolder] = charSequence.toString();
}
@Override
public void afterTextChanged(Editable editable) {
}
}
@Nullable
private String buildQuote(int quotePosition) {
Date postDate = null;
{
String date = postsList.get(quotePosition).getPostDate();
if (date != null) {
DateFormat format = new SimpleDateFormat("MMMM d, yyyy, h:m:s a", Locale.ENGLISH);
date = date.replace("Ιανουαρίου", "January");
date = date.replace("Φεβρουαρίου", "February");
date = date.replace("Μαρτίου", "March");
date = date.replace("Απριλίου", "April");
date = date.replace("Μαΐου", "May");
date = date.replace("Ιουνίου", "June");
date = date.replace("Ιουλίου", "July");
date = date.replace("Αυγούστου", "August");
date = date.replace("Σεπτεμβρίου", "September");
date = date.replace("Οκτωβρίου", "October");
date = date.replace("Νοεμβρίου", "November");
date = date.replace("Δεκεμβρίου", "December");
if (date.contains("Today")) {
date = date.replace("Today at",
Calendar.getInstance().getDisplayName(Calendar.MONTH,
Calendar.LONG, Locale.ENGLISH)
+ " " + Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
+ ", " + Calendar.getInstance().get(Calendar.YEAR) + ",");
} else if (date.contains("Σήμερα")) {
date = date.replace("Σήμερα στις",
Calendar.getInstance().getDisplayName(Calendar.MONTH,
Calendar.LONG, Locale.ENGLISH)
+ " " + Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
+ ", " + Calendar.getInstance().get(Calendar.YEAR) + ",");
if (date.contains("πμ")) date = date.replace("πμ", "am");
if (date.contains("μμ")) date = date.replace("μμ", "pm");
}
try {
postDate = format.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
if (postsList.get(quotePosition).getPostIndex() != 0) {
if (postDate != null) {
return "[quote author=" + postsList.get(quotePosition).getAuthor()
+ " link=topic=68525.msg" + postsList.get(quotePosition).getPostIndex()
+ "#msg" + postsList.get(quotePosition).getPostIndex()
+ " date=" + postDate.getTime() / 1000 + "]"
+ "\n" + postsList.get(quotePosition).getContent()
+ "\n" + "[/quote]" + "\n\n";
}
}
return null;
}
/**
* Returns a String with a single FontAwesome typeface character corresponding to this file's
* extension.

3
app/src/main/res/layout/activity_topic_post_row.xml

@ -115,8 +115,7 @@
android:layout_toEndOf="@+id/thumbnail_holder"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/post_subject"
/>
android:text="@string/post_subject"/>
</RelativeLayout>
<ImageButton

99
app/src/main/res/layout/activity_topic_quick_reply_row.xml

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:paddingEnd="4dp"
android:paddingStart="4dp">
@ -22,32 +23,94 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
<RelativeLayout
android:id="@+id/header"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
android:clickable="true"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp">
<FrameLayout
android:id="@+id/thumbnail_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:contentDescription="@string/post_thumbnail"
android:maxHeight="@dimen/thumbnail_size"
android:maxWidth="@dimen/thumbnail_size"
android:src="@drawable/ic_default_user_thumbnail"/>
</FrameLayout>
<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toEndOf="@+id/thumbnail_holder"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/post_author"
android:textColor="@color/primary_text"
android:textStyle="bold"/>
<EditText
android:id="@+id/quick_reply_text"
android:id="@+id/quick_reply_subject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/quick_reply"
android:inputType="textMultiLine"/>
</android.support.design.widget.TextInputLayout>
android:layout_below="@+id/username"
android:layout_toEndOf="@+id/thumbnail_holder"
android:hint="@string/quick_reply_subject"
android:inputType="textMultiLine"
android:maxLength="80"
android:textSize="10sp"/>
</RelativeLayout>
<android.support.v7.widget.AppCompatImageButton
android:id="@+id/quick_reply_submit"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="5dp"
android:layout_marginEnd="5dp"
android:background="@color/card_background"
android:contentDescription="@string/quick_reply_submit"
android:src="@drawable/ic_send"/>
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<android.support.design.widget.TextInputLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<EditText
android:id="@+id/quick_reply_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/quick_reply"
android:inputType="textMultiLine"/>
</android.support.design.widget.TextInputLayout>
<android.support.v7.widget.AppCompatImageButton
android:id="@+id/quick_reply_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="5dp"
android:layout_marginEnd="5dp"
android:background="@color/card_background"
android:contentDescription="@string/quick_reply_submit"
android:src="@drawable/ic_send"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</FrameLayout>

1
app/src/main/res/values/strings.xml

@ -45,6 +45,7 @@
<string name="button_next">next</string>
<string name="button_last">last</string>
<string name="quick_reply">Quick reply&#8230;</string>
<string name="quick_reply_subject">Subject&#8230;</string>
<string name="quick_reply_submit">Submit</string>
<!--Profile Activity-->

Loading…
Cancel
Save