Browse Source

add/improve topic's behavior on network errors

pull/43/head
Thodoris1999 6 years ago
parent
commit
55afa63852
  1. 50
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  2. 2
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java
  3. 13
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java
  4. 8
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyResult.java
  5. 15
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java
  6. 17
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java
  7. 18
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java
  8. 10
      app/src/main/res/layout/activity_topic.xml
  9. 4
      app/src/main/res/values/strings.xml

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

@ -11,6 +11,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.RecyclerView;
@ -30,7 +31,6 @@ import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.topic.tasks.DeleteTask;
@ -110,6 +110,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
private TextView pageIndicator;
private ImageButton nextPage;
private ImageButton lastPage;
private Snackbar snackbar;
private TopicViewModel viewModel;
//Fix for vector drawables on android <21
@ -449,6 +450,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
@Override
public void onTopicTaskStarted() {
progressBar.setVisibility(ProgressBar.VISIBLE);
if (snackbar != null) snackbar.dismiss();
}
@Override
@ -469,7 +471,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
if (result)
viewModel.reloadPage();
else
Toast.makeText(TopicActivity.this, "Post deleted!", Toast.LENGTH_SHORT).show();
Toast.makeText(getBaseContext(), "Delete failed!", Toast.LENGTH_SHORT).show();
}
});
viewModel.setReplyFinishListener(new ReplyTask.ReplyTaskCallbacks() {
@ -498,7 +500,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
viewModel.reloadPage();
}
} else {
Toast.makeText(TopicActivity.this, "Post failed!", Toast.LENGTH_SHORT).show();
Toast.makeText(getBaseContext(), "Post failed!", Toast.LENGTH_SHORT).show();
recyclerView.getChildAt(postsList.size() - 1).setAlpha(1);
recyclerView.getChildAt(postsList.size() - 1).setEnabled(true);
}
@ -539,7 +541,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
viewModel.setEditingPost(false);
viewModel.reloadPage();
} else {
Toast.makeText(TopicActivity.this, "Edit failed!", Toast.LENGTH_SHORT).show();
Toast.makeText(getBaseContext(), "Edit failed!", Toast.LENGTH_SHORT).show();
recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setAlpha(1);
recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setEnabled(true);
}
@ -594,17 +596,37 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
});
viewModel.getTopicTaskResultCode().observe(this, resultCode -> {
if (resultCode == null) return;
progressBar.setVisibility(ProgressBar.GONE);
switch (resultCode) {
case SUCCESS:
paginationEnabled(true);
progressBar.setVisibility(ProgressBar.GONE);
break;
case NETWORK_ERROR:
Toast.makeText(getBaseContext(), "Network Error", Toast.LENGTH_SHORT).show();
if (viewModel.getPostsList().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);
errorTextview.setText(getString(R.string.network_error_retry_prompt));
errorTextview.setVisibility(View.VISIBLE);
errorTextview.setOnClickListener(view -> {
viewModel.reloadPage();
errorTextview.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
});
} else {
// a page has already been loaded
viewModel.setPageIndicatorIndex(viewModel.getCurrentPageIndex(), false);
snackbar = Snackbar.make(findViewById(R.id.main_content),
R.string.generic_network_error, Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(R.string.retry, view -> viewModel.reloadPage());
snackbar.show();
}
break;
case UNAUTHORIZED:
progressBar.setVisibility(ProgressBar.GONE);
Toast.makeText(getBaseContext(), "This topic is either missing or off limits to you", Toast.LENGTH_SHORT).show();
recyclerView.setVisibility(View.GONE);
TextView errorTextview = findViewById(R.id.error_textview);
errorTextview.setText(getString(R.string.unauthorized_topic_error));
errorTextview.setVisibility(View.VISIBLE);
break;
default:
//Parse failed - should never happen
@ -615,26 +637,30 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
}
});
viewModel.getPrepareForReplyResult().observe(this, prepareForReplyResult -> {
if (prepareForReplyResult != null) {
progressBar.setVisibility(ProgressBar.GONE);
if (prepareForReplyResult != null && prepareForReplyResult.isSuccessful()) {
//prepare for a reply
viewModel.setWritingReply(true);
postsList.add(Post.newQuickReply());
topicAdapter.notifyItemInserted(postsList.size());
recyclerView.scrollToPosition(postsList.size() - 1);
progressBar.setVisibility(ProgressBar.GONE);
replyFAB.hide();
bottomNavBar.setVisibility(View.GONE);
} else {
Snackbar.make(findViewById(R.id.main_content), getString(R.string.generic_network_error), Snackbar.LENGTH_SHORT).show();
}
});
viewModel.getPrepareForEditResult().observe(this, result -> {
progressBar.setVisibility(ProgressBar.GONE);
if (result != null && result.isSuccessful()) {
viewModel.setEditingPost(true);
postsList.get(result.getPosition()).setPostType(Post.TYPE_EDIT);
topicAdapter.notifyItemChanged(result.getPosition());
recyclerView.scrollToPosition(result.getPosition());
progressBar.setVisibility(ProgressBar.GONE);
replyFAB.hide();
bottomNavBar.setVisibility(View.GONE);
} else {
Snackbar.make(findViewById(R.id.main_content), getString(R.string.generic_network_error), Snackbar.LENGTH_SHORT).show();
}
});
}

2
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java

@ -41,7 +41,7 @@ public class DeleteTask extends AsyncTask<String, Void, Boolean> {
return true;
default:
Timber.e("Something went wrong. Request string: %s", delete.toString());
return true;
return false;
}
} catch (IOException e) {
Timber.e(e, "Delete failed.");

13
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java

@ -33,18 +33,13 @@ public class PrepareForReply extends AsyncTask<Integer, Void, PrepareForReplyRes
@Override
protected PrepareForReplyResult doInBackground(Integer... postIndices) {
String numReplies = null;
String seqnum = null;
String sc = null;
String topic = null;
StringBuilder buildedQuotes = new StringBuilder("");
Document document;
Request request = new Request.Builder()
.url(replyPageUrl + ";wap2")
.build();
OkHttpClient client = BaseApplication.getInstance().getClient();
String numReplies, seqnum, sc, topic;
try {
Response response = client.newCall(request).execute();
document = Jsoup.parse(response.body().string());
@ -55,14 +50,15 @@ public class PrepareForReply extends AsyncTask<Integer, Void, PrepareForReplyRes
topic = document.select("input[name=topic]").first().attr("value");
} catch (IOException | Selector.SelectorParseException e) {
Timber.e(e, "Prepare failed.");
return new PrepareForReplyResult(false, null, null, null, null, null);
}
StringBuilder buildedQuotes = new StringBuilder("");
for (Integer postIndex : postIndices) {
request = new Request.Builder()
.url("https://www.thmmy.gr/smf/index.php?action=quotefast;quote=" +
postIndex + ";" + "sesc=" + sc + ";xml")
.build();
try {
Response response = client.newCall(request).execute();
String body = response.body().string();
@ -70,9 +66,10 @@ public class PrepareForReply extends AsyncTask<Integer, Void, PrepareForReplyRes
buildedQuotes.append("\n\n");
} catch (IOException | Selector.SelectorParseException e) {
Timber.e(e, "Quote building failed.");
return new PrepareForReplyResult(false, null, null, null, null, null);
}
}
return new PrepareForReplyResult(numReplies, seqnum, sc, topic, buildedQuotes.toString());
return new PrepareForReplyResult(true, numReplies, seqnum, sc, topic, buildedQuotes.toString());
}
@Override

8
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyResult.java

@ -2,9 +2,11 @@ package gr.thmmy.mthmmy.activities.topic.tasks;
public class PrepareForReplyResult {
private final String numReplies, seqnum, sc, topic, buildedQuotes;
private boolean successful;
public PrepareForReplyResult(String numReplies, String seqnum, String sc, String topic, String buildedQuotes) {
public PrepareForReplyResult(boolean successful, String numReplies, String seqnum, String sc, String topic, String buildedQuotes) {
this.successful = successful;
this.numReplies = numReplies;
this.seqnum = seqnum;
this.sc = sc;
@ -31,4 +33,8 @@ public class PrepareForReplyResult {
public String getBuildedQuotes() {
return buildedQuotes;
}
public boolean isSuccessful() {
return successful;
}
}

15
app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java

@ -4,13 +4,14 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
/**
* Extends FloatingActionButton's behavior so the button will hide when scrolling down and show
* otherwise.
* otherwise. It also lifts the {@link FloatingActionButton} when a {@link Snackbar} is shown.
*/
@SuppressWarnings("unused")
public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
@ -48,4 +49,16 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingA
child.show();
}
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
}

17
app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java

@ -4,6 +4,7 @@ import android.animation.Animator;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.AttributeSet;
@ -13,7 +14,9 @@ import android.view.ViewPropertyAnimator;
/**
* Extends LinearLayout's behavior. Used for bottom navigation bar.
* <p>When a nested ScrollView is scrolled down, the view will disappear.
* When the ScrollView is scrolled back up, the view will reappear.</p>
* When the ScrollView is scrolled back up, the view will reappear. It also pushes the
* {@link android.widget.LinearLayout} up when a {@link Snackbar} is shown
* </p>
*/
@SuppressWarnings("unused")
public class ScrollAwareLinearBehavior extends CoordinatorLayout.Behavior<View> {
@ -111,4 +114,16 @@ public class ScrollAwareLinearBehavior extends CoordinatorLayout.Behavior<View>
animator.start();
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
}

18
app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java

@ -84,7 +84,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
}
public void reloadPage() {
if (topicUrl == null) throw new NullPointerException("No topic task has finished yet!");
if (topicUrl == null) throw new NullPointerException("No topic task has been requested yet!");
loadUrl(topicUrl);
}
@ -194,7 +194,6 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
@Override
public void onPrepareForReplyFinished(PrepareForReplyResult result) {
writingReply = true;
prepareForReplyResult.setValue(result);
}
@ -207,26 +206,31 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
public void incrementPageRequestValue(int step, boolean changePage) {
if (pageIndicatorIndex.getValue() == null)
throw new NullPointerException("No page has been loaded yet!");
if (pageIndicatorIndex.getValue() <= pageCount - step) {
int oldIndicatorIndex = pageIndicatorIndex.getValue();
if (oldIndicatorIndex <= pageCount - step) {
pageIndicatorIndex.setValue(pageIndicatorIndex.getValue() + step);
} else
pageIndicatorIndex.setValue(pageCount);
if (changePage) performPageChange();
if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) performPageChange();
}
public void decrementPageRequestValue(int step, boolean changePage) {
if (pageIndicatorIndex.getValue() == null)
throw new NullPointerException("No page has been loaded yet!");
if (pageIndicatorIndex.getValue() >= step) {
int oldIndicatorIndex = pageIndicatorIndex.getValue();
if (oldIndicatorIndex > step) {
pageIndicatorIndex.setValue(pageIndicatorIndex.getValue() - step);
} else
pageIndicatorIndex.setValue(1);
if (changePage) performPageChange();
if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) performPageChange();
}
public void setPageIndicatorIndex(int pageIndicatorIndex, boolean changePage) {
if (this.pageIndicatorIndex.getValue() == null)
throw new NullPointerException("No page has been loaded yet!");
int oldIndicatorIndex = this.pageIndicatorIndex.getValue();
this.pageIndicatorIndex.setValue(pageIndicatorIndex);
if (changePage) performPageChange();
if (changePage && oldIndicatorIndex != this.pageIndicatorIndex.getValue()) performPageChange();
}
// <-------------Just getters, setters and helper methods below here---------------->

10
app/src/main/res/layout/activity_topic.xml

@ -47,6 +47,16 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v7.widget.RecyclerView>
<TextView
android:id="@+id/error_textview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/network_error_retry_prompt"
android:textSize="32sp"
android:textColor="@color/white"
android:gravity="center"
android:visibility="gone" />
<LinearLayout
android:id="@+id/bottom_navigation_bar"
android:layout_width="match_parent"

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

@ -53,6 +53,10 @@
<string name="subject">Subject&#8230;</string>
<string name="submit">Submit</string>
<string name="post_message">Message&#8230;</string>
<string name="network_error_retry_prompt">Could not connect to thmmy.gr \n\n Tap to retry</string>
<string name="generic_network_error">Network error</string>
<string name="retry">retry</string>
<string name="unauthorized_topic_error">This topic is either missing or off limits to you</string>
<!--Profile Activity-->
<string name="username">Username</string>

Loading…
Cancel
Save