diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/ReplyParser.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/ReplyParser.java
new file mode 100644
index 00000000..4950478b
--- /dev/null
+++ b/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("
");
+ 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;
+ }
+}
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 e6860ff0..7f0393a9 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
@@ -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 String
* containing this topics's url using the key {@link #BUNDLE_TOPIC_URL} and a String 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);
}
}
}
\ No newline at end of file
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 230388be..035933cf 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
@@ -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 {
*/
private static int THUMBNAIL_SIZE;
private final Context context;
+ private String topicTitle;
private ArrayList toQuoteList = new ArrayList<>();
private final List postsList;
/**
@@ -99,13 +103,16 @@ class TopicAdapter extends RecyclerView.Adapter {
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 postsList, TopicActivity.TopicTask topicTask) {
+ TopicAdapter(Context context, List postsList, TopicActivity.TopicTask topicTask
+ , String topicTitle) {
this.context = context;
this.postsList = postsList;
@@ -115,11 +122,11 @@ class TopicAdapter extends RecyclerView.Adapter {
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 {
} 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 {
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 {
* 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 {
}
+ 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.
diff --git a/app/src/main/res/layout/activity_topic_post_row.xml b/app/src/main/res/layout/activity_topic_post_row.xml
index 13e53227..677a58db 100644
--- a/app/src/main/res/layout/activity_topic_post_row.xml
+++ b/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"/>
@@ -22,32 +23,94 @@
+ android:orientation="vertical">
-
+ android:clickable="true"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="16dp">
+
+
+
+
+
+
+
-
+ 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"/>
+
-
+ android:orientation="horizontal"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp">
+
+
+
+
+
+
+
+
\ 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 b1d04389..d95e5e63 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -45,6 +45,7 @@
next
last
Quick reply…
+ Subject…
Submit