diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java index 2be05a74..19680329 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java @@ -5,6 +5,7 @@ import android.graphics.Color; import android.graphics.Typeface; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.TabLayout; @@ -13,6 +14,7 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.content.res.ResourcesCompat; import android.support.v4.view.ViewPager; +import android.support.v7.app.AppCompatDelegate; import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; @@ -92,6 +94,13 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment private String username; private int tabSelect; + //Fix for vector drawables on android <21 + static { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); 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 e2f92054..c4dff2d7 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 @@ -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; @@ -45,6 +45,7 @@ import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.utils.CustomLinearLayoutManager; import gr.thmmy.mthmmy.utils.HTMLUtils; +import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.viewmodel.TopicViewModel; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import timber.log.Timber; @@ -58,9 +59,7 @@ import static gr.thmmy.mthmmy.services.NotificationService.NEW_POST_TAG; * key {@link #BUNDLE_TOPIC_TITLE} for faster title rendering. */ @SuppressWarnings("unchecked") -public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskObserver, - DeleteTask.DeleteTaskCallbacks, ReplyTask.ReplyTaskCallbacks, PrepareForEditTask.PrepareForEditCallbacks, - EditTask.EditTaskCallbacks, PrepareForReply.PrepareForReplyCallbacks, TopicAdapter.OnPostFocusChangeListener { +public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFocusChangeListener { //Activity's variables /** * The key to use when putting topic's url String to {@link TopicActivity}'s Bundle. @@ -103,11 +102,6 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb * long click is held in either first or last buttons */ private static final int LARGE_STEP = 10; - /** - * Holds the value (index) of the page to be requested when a user interaction with bottom - * navigation bar occurs - */ - private Integer pageRequestValue; //Bottom navigation bar graphics related private LinearLayout bottomNavBar; @@ -116,6 +110,7 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb private TextView pageIndicator; private ImageButton nextPage; private ImageButton lastPage; + private Snackbar snackbar; private TopicViewModel viewModel; //Fix for vector drawables on android <21 @@ -129,14 +124,9 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_topic); - + // get TopicViewModel instance viewModel = ViewModelProviders.of(this).get(TopicViewModel.class); - viewModel.setTopicTaskObserver(this); - viewModel.setDeleteTaskCallbacks(this); - viewModel.setReplyFinishListener(this); - viewModel.setPrepareForEditCallbacks(this); - viewModel.setEditTaskCallbacks(this); - viewModel.setPrepareForReplyCallbacks(this); + subscribeUI(); Bundle extras = getIntent().getExtras(); String topicTitle = extras.getString(BUNDLE_TOPIC_TITLE); @@ -206,81 +196,7 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb paginationEnabled(false); - viewModel.getTopicTaskResult().observe(this, topicTaskResult -> { - if (topicTaskResult == null) { - progressBar.setVisibility(ProgressBar.VISIBLE); - } else { - switch (topicTaskResult.getResultCode()) { - case SUCCESS: - if (topicTitle == null || Objects.equals(topicTitle, "") - || !Objects.equals(topicTitle, topicTaskResult.getTopicTitle())) { - toolbarTitle.setText(topicTaskResult.getTopicTitle()); - } - - recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug - postsList.clear(); - postsList.addAll(topicTaskResult.getNewPostsList()); - topicAdapter.notifyDataSetChanged(); - - pageIndicator.setText(String.valueOf(topicTaskResult.getCurrentPageIndex()) + "/" + - String.valueOf(topicTaskResult.getPageCount())); - pageRequestValue = topicTaskResult.getCurrentPageIndex(); - paginationEnabled(true); - - if (topicTaskResult.getCurrentPageIndex() == topicTaskResult.getPageCount()) { - NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null) - notificationManager.cancel(NEW_POST_TAG, topicTaskResult.getLoadedPageTopicId()); - } - - progressBar.setVisibility(ProgressBar.GONE); - if (topicTaskResult.getReplyPageUrl() == null) - replyFAB.hide(); - else - replyFAB.show(); - recyclerView.scrollToPosition(topicTaskResult.getFocusedPostIndex()); - break; - case NETWORK_ERROR: - Toast.makeText(getBaseContext(), "Network Error", Toast.LENGTH_SHORT).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(); - break; - default: - //Parse failed - should never happen - Timber.d("Parse failed!"); //TODO report ParseException!!! - Toast.makeText(getBaseContext(), "Fatal Error", Toast.LENGTH_SHORT).show(); - finish(); - break; - } - } - - }); - viewModel.getPrepareForReplyResult().observe(this, prepareForReplyResult -> { - if (prepareForReplyResult != null) { - //prepare for a reply - postsList.add(Post.newQuickReply()); - topicAdapter.notifyItemInserted(postsList.size()); - recyclerView.scrollToPosition(postsList.size() - 1); - progressBar.setVisibility(ProgressBar.GONE); - replyFAB.hide(); - bottomNavBar.setVisibility(View.GONE); - } - - }); - viewModel.getPrepareForEditResult().observe(this, result -> { - 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); - } - }); - viewModel.initialLoad(topicPageUrl); + viewModel.loadUrl(topicPageUrl); } @Override @@ -310,18 +226,14 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb TextView usersViewing = infoDialog.findViewById(R.id.users_viewing); usersViewing.setText(new SpannableStringBuilder("Loading...")); usersViewing.setMovementMethod(LinkMovementMethod.getInstance()); - viewModel.getTopicTaskResult().observe(this, topicTaskResult -> { - if (topicTaskResult == null) { - usersViewing.setText(new SpannableStringBuilder("Loading...")); - treeAndMods.setText(new SpannableStringBuilder("Loading...")); - } else { - String treeAndModsString = topicTaskResult.getTopicTreeAndMods(); - treeAndMods.setText(HTMLUtils.getSpannableFromHtml(this, treeAndModsString)); - String topicViewersString = topicTaskResult.getTopicViewers(); - usersViewing.setText(HTMLUtils.getSpannableFromHtml(this, topicViewersString)); - } + viewModel.getTopicTreeAndMods().observe(this, topicTreeAndMods -> { + if (topicTreeAndMods == null) return; + treeAndMods.setText(HTMLUtils.getSpannableFromHtml(this, topicTreeAndMods)); + }); + viewModel.getTopicViewers().observe(this, topicViewers -> { + if (topicViewers == null) return; + usersViewing.setText(HTMLUtils.getSpannableFromHtml(this, topicViewers)); }); - builder.setView(infoDialog); AlertDialog dialog = builder.create(); dialog.show(); @@ -400,10 +312,10 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb public void run() { long REPEAT_DELAY = 250; if (autoIncrement) { - incrementPageRequestValue(step); + viewModel.incrementPageRequestValue(step, false); repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), REPEAT_DELAY); } else if (autoDecrement) { - decrementPageRequestValue(step); + viewModel.decrementPageRequestValue(step, false); repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), REPEAT_DELAY); } } @@ -443,11 +355,9 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb // Increment once for a click increment.setOnClickListener(v -> { if (!autoIncrement && step == LARGE_STEP) { - incrementPageRequestValue(viewModel.getPageCount()); - viewModel.changePage(viewModel.getPageCount() - 1); + viewModel.setPageIndicatorIndex(viewModel.getPageCount(), true); } else if (!autoIncrement) { - incrementPageRequestValue(step); - viewModel.changePage(pageRequestValue - 1); + viewModel.incrementPageRequestValue(step, true); } }); @@ -471,11 +381,11 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb } else if (rect != null && event.getAction() == MotionEvent.ACTION_UP && autoIncrement) { autoIncrement = false; paginationEnabled(true); - viewModel.changePage(pageRequestValue - 1); + viewModel.performPageChange(); } else if (rect != null && event.getAction() == MotionEvent.ACTION_MOVE) { if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { autoIncrement = false; - decrementPageRequestValue(pageRequestValue - viewModel.getCurrentPageIndex()); + viewModel.setPageIndicatorIndex(viewModel.getCurrentPageIndex(), false); paginationEnabled(true); } } @@ -489,11 +399,9 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb // Decrement once for a click decrement.setOnClickListener(v -> { if (!autoDecrement && step == LARGE_STEP) { - decrementPageRequestValue(viewModel.getPageCount()); - viewModel.changePage(0); + viewModel.setPageIndicatorIndex(1, true); } else if (!autoDecrement) { - decrementPageRequestValue(step); - viewModel.changePage(pageRequestValue - 1); + viewModel.decrementPageRequestValue(step, true); } }); @@ -517,12 +425,12 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb } else if (event.getAction() == MotionEvent.ACTION_UP && autoDecrement) { autoDecrement = false; paginationEnabled(true); - viewModel.changePage(pageRequestValue - 1); + viewModel.performPageChange(); } else if (event.getAction() == MotionEvent.ACTION_MOVE) { if (rect != null && !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { autoIncrement = false; - incrementPageRequestValue(viewModel.getCurrentPageIndex() - pageRequestValue); + viewModel.setPageIndicatorIndex(viewModel.getCurrentPageIndex(), false); paginationEnabled(true); } } @@ -531,126 +439,229 @@ public class TopicActivity extends BaseActivity implements TopicTask.TopicTaskOb }); } - private void incrementPageRequestValue(int step) { - if (pageRequestValue < viewModel.getPageCount() - step) { - pageRequestValue = pageRequestValue + step; - } else - pageRequestValue = viewModel.getPageCount(); - pageIndicator.setText(pageRequestValue + "/" + String.valueOf(viewModel.getPageCount())); - } - - private void decrementPageRequestValue(int step) { - if (pageRequestValue > step) - pageRequestValue = pageRequestValue - step; - else - pageRequestValue = 1; - pageIndicator.setText(pageRequestValue + "/" + String.valueOf(viewModel.getPageCount())); - } - //------------------------------------BOTTOM NAV BAR METHODS END------------------------------------ - @Override - public void onTopicTaskStarted() { - progressBar.setVisibility(ProgressBar.VISIBLE); - } - - @Override - public void onTopicTaskCancelled() { - progressBar.setVisibility(ProgressBar.GONE); - } - - @Override - public void onReplyTaskStarted() { - progressBar.setVisibility(ProgressBar.VISIBLE); - } - - @Override - public void onReplyTaskFinished(boolean success) { - View view = getCurrentFocus(); - if (view != null) { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - } - - postsList.remove(postsList.size() - 1); - topicAdapter.notifyItemRemoved(postsList.size()); - - progressBar.setVisibility(ProgressBar.GONE); - replyFAB.show(); - bottomNavBar.setVisibility(View.VISIBLE); - viewModel.setWritingReply(false); - - if (success) { - if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0) { - viewModel.loadUrl(viewModel.getBaseUrl() + "." + 2147483647); - } else { - viewModel.reloadPage(); + /** + * Binds the UI to its data + */ + private void subscribeUI() { + // Implement async task callbacks + viewModel.setTopicTaskObserver(new TopicTask.TopicTaskObserver() { + @Override + public void onTopicTaskStarted() { + progressBar.setVisibility(ProgressBar.VISIBLE); + if (snackbar != null) snackbar.dismiss(); } - } else { - Toast.makeText(TopicActivity.this, "Post failed!", Toast.LENGTH_SHORT).show(); - } - } - @Override - public void onPrepareForReplyStarted() { - progressBar.setVisibility(ProgressBar.VISIBLE); - } + @Override + public void onTopicTaskCancelled() { + progressBar.setVisibility(ProgressBar.GONE); + } + }); + viewModel.setDeleteTaskCallbacks(new DeleteTask.DeleteTaskCallbacks() { + @Override + public void onDeleteTaskStarted() { + progressBar.setVisibility(ProgressBar.VISIBLE); + } - @Override - public void onPrepareForReplyCancelled() { - progressBar.setVisibility(ProgressBar.GONE); - } + @Override + public void onDeleteTaskFinished(boolean result) { + progressBar.setVisibility(ProgressBar.GONE); - @Override - public void onDeleteTaskStarted() { - progressBar.setVisibility(ProgressBar.VISIBLE); - } + if (result) + viewModel.reloadPage(); + else + Toast.makeText(getBaseContext(), "Delete failed!", Toast.LENGTH_SHORT).show(); + } + }); + viewModel.setReplyFinishListener(new ReplyTask.ReplyTaskCallbacks() { + @Override + public void onReplyTaskStarted() { + progressBar.setVisibility(ProgressBar.VISIBLE); + } - @Override - public void onDeleteTaskFinished(boolean result) { - progressBar.setVisibility(ProgressBar.GONE); + @Override + public void onReplyTaskFinished(boolean success) { + View view = getCurrentFocus(); + if (view != null) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } - if (result) { - viewModel.reloadPage(); - } else { - Toast.makeText(TopicActivity.this, "Post deleted!", Toast.LENGTH_SHORT).show(); - } - } + progressBar.setVisibility(ProgressBar.GONE); - @Override - public void onPrepareEditStarted() { - progressBar.setVisibility(ProgressBar.VISIBLE); - } + if (success) { + replyFAB.show(); + bottomNavBar.setVisibility(View.VISIBLE); + viewModel.setWritingReply(false); + if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0) { + viewModel.loadUrl(ParseHelpers.getBaseURL(viewModel.getTopicUrl()) + "." + 2147483647); + } else { + viewModel.reloadPage(); + } + } else { + Toast.makeText(getBaseContext(), "Post failed!", Toast.LENGTH_SHORT).show(); + recyclerView.getChildAt(postsList.size() - 1).setAlpha(1); + recyclerView.getChildAt(postsList.size() - 1).setEnabled(true); + } + } + }); + viewModel.setPrepareForEditCallbacks(new PrepareForEditTask.PrepareForEditCallbacks() { + @Override + public void onPrepareEditStarted() { + progressBar.setVisibility(ProgressBar.VISIBLE); + } - @Override - public void onPrepareEditCancelled() { - progressBar.setVisibility(ProgressBar.GONE); - } + @Override + public void onPrepareEditCancelled() { + progressBar.setVisibility(ProgressBar.GONE); + } + }); + viewModel.setEditTaskCallbacks(new EditTask.EditTaskCallbacks() { + @Override + public void onEditTaskStarted() { + progressBar.setVisibility(ProgressBar.VISIBLE); + } - @Override - public void onEditTaskStarted() { - progressBar.setVisibility(ProgressBar.VISIBLE); - } + @Override + public void onEditTaskFinished(boolean result, int position) { + View view = getCurrentFocus(); + if (view != null) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } - @Override - public void onEditTaskFinished(boolean result, int position) { - View view = getCurrentFocus(); - if (view != null) { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - } + progressBar.setVisibility(ProgressBar.GONE); - postsList.get(position).setPostType(Post.TYPE_POST); - topicAdapter.notifyItemChanged(position); - viewModel.setEditingPost(false); - progressBar.setVisibility(ProgressBar.GONE); - replyFAB.show(); - bottomNavBar.setVisibility(View.VISIBLE); + if (result) { + postsList.get(position).setPostType(Post.TYPE_POST); + topicAdapter.notifyItemChanged(position); + replyFAB.show(); + bottomNavBar.setVisibility(View.VISIBLE); + viewModel.setEditingPost(false); + viewModel.reloadPage(); + } else { + Toast.makeText(getBaseContext(), "Edit failed!", Toast.LENGTH_SHORT).show(); + recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setAlpha(1); + recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setEnabled(true); + } + } + }); + viewModel.setPrepareForReplyCallbacks(new PrepareForReply.PrepareForReplyCallbacks() { + @Override + public void onPrepareForReplyStarted() { + progressBar.setVisibility(ProgressBar.VISIBLE); + } - if (result) { - viewModel.reloadPage(); - } else { - Toast.makeText(TopicActivity.this, "Edit failed!", Toast.LENGTH_SHORT).show(); - } + @Override + public void onPrepareForReplyCancelled() { + progressBar.setVisibility(ProgressBar.GONE); + } + }); + // observe the chages in data + viewModel.getPageIndicatorIndex().observe(this, pageIndicatorIndex -> { + if (pageIndicatorIndex == null) return; + pageIndicator.setText(String.valueOf(pageIndicatorIndex) + "/" + + String.valueOf(viewModel.getPageCount())); + }); + viewModel.getTopicTitle().observe(this, newTopicTitle -> { + if (newTopicTitle == null) return; + if (!TextUtils.equals(toolbarTitle.getText(), newTopicTitle)) + toolbarTitle.setText(newTopicTitle); + }); + viewModel.getPageTopicId().observe(this, pageTopicId -> { + if (pageTopicId == null) return; + if (viewModel.getCurrentPageIndex() == viewModel.getPageCount()) { + NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) + notificationManager.cancel(NEW_POST_TAG, pageTopicId); + } + }); + viewModel.getReplyPageUrl().observe(this, replyPageUrl -> { + if (replyPageUrl == null) + replyFAB.hide(); + else + replyFAB.show(); + }); + viewModel.getPostsList().observe(this, postList -> { + if (postList == null) progressBar.setVisibility(ProgressBar.VISIBLE); + recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug + postsList.clear(); + postsList.addAll(postList); + topicAdapter.notifyDataSetChanged(); + }); + viewModel.getFocusedPostIndex().observe(this, focusedPostIndex -> { + if (focusedPostIndex == null) return; + recyclerView.scrollToPosition(focusedPostIndex); + }); + viewModel.getTopicTaskResultCode().observe(this, resultCode -> { + if (resultCode == null) return; + progressBar.setVisibility(ProgressBar.GONE); + switch (resultCode) { + case SUCCESS: + paginationEnabled(true); + break; + case NETWORK_ERROR: + 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: + 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 + Timber.d("Parse failed!"); //TODO report ParseException!!! + Toast.makeText(getBaseContext(), "Fatal Error", Toast.LENGTH_SHORT).show(); + finish(); + break; + } + }); + viewModel.getPrepareForReplyResult().observe(this, prepareForReplyResult -> { + 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); + 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()); + replyFAB.hide(); + bottomNavBar.setVisibility(View.GONE); + } else { + Snackbar.make(findViewById(R.id.main_content), getString(R.string.generic_network_error), Snackbar.LENGTH_SHORT).show(); + } + }); } } \ 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 1bc85acc..07324e1d 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 @@ -46,6 +46,7 @@ import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyFile; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.utils.CircleTransform; +import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.viewmodel.TopicViewModel; import timber.log.Timber; @@ -284,8 +285,16 @@ class TopicAdapter extends RecyclerView.Adapter { holder.stars.setVisibility(View.VISIBLE); } else holder.stars.setVisibility(View.GONE); - //Special card for special member of the month! - if (mUserColor == TopicParser.USER_COLOR_PINK) { + + 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)); @@ -444,7 +453,7 @@ class TopicAdapter extends RecyclerView.Adapter { .transform(new CircleTransform()) .into(holder.thumbnail); holder.username.setText(getSessionManager().getUsername()); - holder.quickReplySubject.setText("Re: " + viewModel.getTopicTitle()); + holder.quickReplySubject.setText("Re: " + viewModel.getTopicTitle().getValue()); holder.quickReply.setText(viewModel.getBuildedQuotes()); @@ -452,15 +461,13 @@ class TopicAdapter extends RecyclerView.Adapter { holder.submitButton.setOnClickListener(view -> { if (holder.quickReplySubject.getText().toString().isEmpty()) return; if (holder.quickReply.getText().toString().isEmpty()) return; - holder.submitButton.setEnabled(false); + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + holder.itemView.setAlpha(0.5f); + holder.itemView.setEnabled(false); viewModel.postReply(context, holder.quickReplySubject.getText().toString(), holder.quickReply.getText().toString()); - - holder.quickReplySubject.getText().clear(); - holder.quickReplySubject.setText("Re: " + viewModel.getTopicTitle()); - holder.quickReply.getText().clear(); - holder.submitButton.setEnabled(true); }); @@ -490,13 +497,12 @@ class TopicAdapter extends RecyclerView.Adapter { holder.submitButton.setOnClickListener(view -> { if (holder.editSubject.getText().toString().isEmpty()) return; if (holder.editMessage.getText().toString().isEmpty()) return; - holder.submitButton.setEnabled(false); + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + holder.itemView.setAlpha(0.5f); + holder.itemView.setEnabled(false); viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editMessage.getText().toString()); - - holder.editSubject.getText().clear(); - holder.editSubject.setText(postsList.get(position).getSubject()); - holder.submitButton.setEnabled(true); }); if (backPressHidden) { @@ -628,8 +634,7 @@ class TopicAdapter extends RecyclerView.Adapter { if (target.is(ThmmyPage.PageCategory.TOPIC)) { //This url points to a topic //Checks if the page to be loaded is the one already shown - if (uriString.contains(viewModel.getBaseUrl())) { - Timber.e("reached here!"); + if (uriString.contains(ParseHelpers.getBaseURL(viewModel.getTopicUrl()))) { if (uriString.contains("topicseen#new") || uriString.contains("#new")) { if (viewModel.getCurrentPageIndex() == viewModel.getPageCount()) { //same page @@ -643,7 +648,6 @@ class TopicAdapter extends RecyclerView.Adapter { if (tmpUrlSbstr.contains("msg")) tmpUrlSbstr = tmpUrlSbstr.substring(0, tmpUrlSbstr.indexOf("msg") - 1); int testAgainst = Integer.parseInt(tmpUrlSbstr); - Timber.e("reached tthere! %s", testAgainst); for (int i = 0; i < postsList.size(); i++) { if (postsList.get(i).getPostIndex() == testAgainst) { //same page @@ -652,11 +656,11 @@ class TopicAdapter extends RecyclerView.Adapter { return true; } } - } else if ((Objects.equals(uriString, viewModel.getBaseUrl()) && viewModel.getCurrentPageIndex() == 1) || - Integer.parseInt(uriString.substring(viewModel.getBaseUrl().length() + 1)) / 15 + 1 == + } else if ((Objects.equals(uriString, ParseHelpers.getBaseURL(viewModel.getTopicUrl())) && + viewModel.getCurrentPageIndex() == 1) || + Integer.parseInt(uriString.substring(ParseHelpers.getBaseURL(viewModel.getTopicUrl()).length() + 1)) / 15 + 1 == viewModel.getCurrentPageIndex()) { //same page - Timber.e("ha"); return true; } } 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 dd354bf7..6563a715 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,7 +1,9 @@ package gr.thmmy.mthmmy.activities.topic; import android.graphics.Color; +import android.util.Log; +import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -13,7 +15,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.regex.Pattern; +import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.model.ThmmyFile; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; @@ -29,6 +33,10 @@ import timber.log.Timber; *
  • {@link #parseTopic(Document, ParseHelpers.Language)}
  • */ public class TopicParser { + private static Pattern mentionsPattern = Pattern. + compile("
    \\n\\s+?Quote from: " + + BaseActivity.getSessionManager().getUsername()); + //User colors private static final int USER_COLOR_BLACK = Color.parseColor("#000000"); private static final int USER_COLOR_RED = Color.parseColor("#F44336"); @@ -159,7 +167,7 @@ public class TopicParser { p_specialRank, p_gender, p_personalText, p_numberOfPosts, p_postLastEditDate, p_postURL, p_deletePostURL, p_editPostURL; int p_postNum, p_postIndex, p_numberOfStars, p_userColor; - boolean p_isDeleted = false; + boolean p_isDeleted = false, p_isUserMentionedInPost = false; ArrayList p_attachedFiles; //Initialize variables @@ -188,7 +196,7 @@ public class TopicParser { p_subject = thisRow.select("div[id^=subject_]").first().select("a").first().text(); //Finds post's link - p_postURL = thisRow.select("div[id^=subject_]").first().select("a").first() .attr("href"); + p_postURL = thisRow.select("div[id^=subject_]").first().select("a").first().attr("href"); //Finds post's text p_post = ParseHelpers.youtubeEmbeddedFix(thisRow.select("div").select(".post").first()); @@ -204,11 +212,11 @@ public class TopicParser { if (postIndex != null) { String tmp = postIndex.attr("name"); p_postIndex = Integer.parseInt(tmp.substring(tmp.indexOf("msg") + 3)); - } else{ + } else { postIndex = thisRow.select("div[id^=subject]").first(); if (postIndex == null) p_postIndex = NO_INDEX; - else{ + else { String tmp = postIndex.attr("id"); p_postIndex = Integer.parseInt(tmp.substring(tmp.indexOf("subject") + 8)); } @@ -237,7 +245,7 @@ public class TopicParser { //Finds post delete url Element postDelete = thisRow.select("a:has(img[alt='Διαγραφή'])").first(); - if (postDelete!=null){ + if (postDelete != null) { p_deletePostURL = postDelete.attr("href"); } @@ -303,7 +311,7 @@ public class TopicParser { //Finds post delete url Element postDelete = thisRow.select("a:has(img[alt='Remove message'])").first(); - if (postDelete!=null){ + if (postDelete != null) { p_deletePostURL = postDelete.attr("href"); } @@ -434,17 +442,25 @@ public class TopicParser { } } + //Checks post for mentions of this user (if the user is logged in) + if (BaseActivity.getSessionManager().isLoggedIn() && + mentionsPattern.matcher(p_post).find()) { + p_isUserMentionedInPost = true; + } + //Add new post in postsList, extended information needed parsedPostsList.add(new Post(p_thumbnailURL, p_userName, p_subject, p_post, p_postIndex , p_postNum, p_postDate, p_profileURL, p_rank, p_specialRank, p_gender , p_numberOfPosts, p_personalText, p_numberOfStars, p_userColor - , p_attachedFiles, p_postLastEditDate, p_postURL, p_deletePostURL, p_editPostURL, Post.TYPE_POST)); + , p_attachedFiles, p_postLastEditDate, p_postURL, p_deletePostURL, p_editPostURL + , p_isUserMentionedInPost, Post.TYPE_POST)); } else { //Deleted user //Add new post in postsList, only standard information needed parsedPostsList.add(new Post(p_thumbnailURL, p_userName, p_subject, p_post - , p_postIndex , p_postNum, p_postDate, p_userColor, p_attachedFiles - , p_postLastEditDate, p_postURL, p_deletePostURL, p_editPostURL, Post.TYPE_POST)); + , p_postIndex, p_postNum, p_postDate, p_userColor, p_attachedFiles + , p_postLastEditDate, p_postURL, p_deletePostURL, p_editPostURL + , p_isUserMentionedInPost, Post.TYPE_POST)); } } return parsedPostsList; 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 5d4a0531..f3523953 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 @@ -41,7 +41,7 @@ public class DeleteTask extends AsyncTask { 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."); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java index 42ea2774..2e0d7148 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java @@ -33,18 +33,13 @@ public class PrepareForReply extends AsyncTask { @Override protected TopicTaskResult doInBackground(String... strings) { - String topicTitle = null; - String topicTreeAndMods = ""; - String topicViewers = ""; - ArrayList newPostsList = null; - int loadedPageTopicId = -1; - int focusedPostIndex = 0; - SparseArray pagesUrls = new SparseArray<>(); - int currentPageIndex = 1; - int pageCount = 1; - String baseUrl = ""; - String lastPageLoadAttemptedUrl = ""; - Document topic = null; String newPageUrl = strings[0]; @@ -70,14 +58,9 @@ public class TopicTask extends AsyncTask { } } - lastPageLoadAttemptedUrl = newPageUrl; - if (strings[0].substring(0, strings[0].lastIndexOf(".")).contains("topic=")) - baseUrl = strings[0].substring(0, strings[0].lastIndexOf(".")); //New topic's base url - String replyPageUrl = null; Request request = new Request.Builder() .url(newPageUrl) .build(); - ResultCode resultCode; try { Response response = BaseApplication.getInstance().getClient().newCall(request).execute(); topic = Jsoup.parse(response.body().string()); @@ -85,17 +68,18 @@ public class TopicTask extends AsyncTask { ParseHelpers.Language language = ParseHelpers.Language.getLanguage(topic); //Finds topic's tree, mods and users viewing - topicTreeAndMods = topic.select("div.nav").first().html(); - topicViewers = TopicParser.parseUsersViewingThisTopic(topic, language); + String topicTreeAndMods = topic.select("div.nav").first().html(); + String topicViewers = TopicParser.parseUsersViewingThisTopic(topic, language); //Finds reply page url + String replyPageUrl = null; 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"); //Finds topic title if missing - topicTitle = topic.select("td[id=top_subject]").first().text(); + String topicTitle = topic.select("td[id=top_subject]").first().text(); if (topicTitle.contains("Topic:")) { topicTitle = topicTitle.substring(topicTitle.indexOf("Topic:") + 7 , topicTitle.indexOf("(Read") - 2); @@ -106,42 +90,39 @@ public class TopicTask extends AsyncTask { } //Finds current page's index - currentPageIndex = TopicParser.parseCurrentPageIndex(topic, language); + int currentPageIndex = TopicParser.parseCurrentPageIndex(topic, language); //Finds number of pages - pageCount = TopicParser.parseTopicNumberOfPages(topic, currentPageIndex, language); - - for (int i = 0; i < pageCount; i++) { - //Generate each page's url from topic's base url +".15*numberOfPage" - pagesUrls.put(i, baseUrl + "." + String.valueOf(i * 15)); - } + int pageCount = TopicParser.parseTopicNumberOfPages(topic, currentPageIndex, language); - newPostsList = TopicParser.parseTopic(topic, language); + ArrayList newPostsList = TopicParser.parseTopic(topic, language); - loadedPageTopicId = Integer.parseInt(ThmmyPage.getTopicId(lastPageLoadAttemptedUrl)); + 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) { focusedPostIndex = i; break; } } - resultCode = ResultCode.SUCCESS; + return new TopicTaskResult(ResultCode.SUCCESS, topicTitle, replyPageUrl, newPostsList, loadedPageTopicId, + currentPageIndex, pageCount, focusedPostIndex, topicTreeAndMods, topicViewers); } catch (IOException e) { Timber.i(e, "IO Exception"); - resultCode = ResultCode.NETWORK_ERROR; + return new TopicTaskResult(ResultCode.NETWORK_ERROR, null, null, null, + 0, 0, 0, 0, null, null); } catch (Exception e) { if (isUnauthorized(topic)) { - resultCode = ResultCode.UNAUTHORIZED; + return new TopicTaskResult(ResultCode.UNAUTHORIZED, null, null, null, + 0, 0, 0, 0, null, null); } else { Timber.e(e, "Parsing Error"); - resultCode = ResultCode.PARSING_ERROR; + return new TopicTaskResult(ResultCode.PARSING_ERROR, null, null, null, + 0, 0, 0, 0, null, null); } } - return new TopicTaskResult(resultCode, baseUrl, topicTitle, replyPageUrl, newPostsList, - loadedPageTopicId, currentPageIndex, pageCount, focusedPostIndex, topicTreeAndMods, - topicViewers, lastPageLoadAttemptedUrl, pagesUrls); } private boolean isUnauthorized(Document document) { 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 f9b76736..570160d3 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 @@ -9,13 +9,6 @@ import gr.thmmy.mthmmy.model.Post; public class TopicTaskResult { private final TopicTask.ResultCode resultCode; - /** - * Holds this topic's base url. For example a topic with url similar to - * "https://www.thmmy.gr/smf/index.php?topic=1.15;topicseen" or - * "https://www.thmmy.gr/smf/index.php?topic=1.msg1#msg1" - * has the base url "https://www.thmmy.gr/smf/index.php?topic=1" - */ - private final String baseUrl; /** * Holds this topic's title. At first this gets the value of the topic title that came with * bundle and is rendered in the toolbar while parsing this topic. Later, if a different topic @@ -46,18 +39,12 @@ public class TopicTaskResult { //Topic's info related private final String topicTreeAndMods; private final String topicViewers; - /** - * The url of the last page that was attempted to be loaded - */ - private final String lastPageLoadAttemptedUrl; - private final SparseArray pagesUrls; - public TopicTaskResult(TopicTask.ResultCode resultCode, String baseUrl, String topicTitle, + public TopicTaskResult(TopicTask.ResultCode resultCode, String topicTitle, String replyPageUrl, ArrayList newPostsList, int loadedPageTopicId, int currentPageIndex, int pageCount, int focusedPostIndex, String topicTreeAndMods, - String topicViewers, String lastPageLoadAttemptedUrl, SparseArray pagesUrls) { + String topicViewers) { this.resultCode = resultCode; - this.baseUrl = baseUrl; this.topicTitle = topicTitle; this.replyPageUrl = replyPageUrl; this.newPostsList = newPostsList; @@ -67,18 +54,12 @@ public class TopicTaskResult { this.focusedPostIndex = focusedPostIndex; this.topicTreeAndMods = topicTreeAndMods; this.topicViewers = topicViewers; - this.lastPageLoadAttemptedUrl = lastPageLoadAttemptedUrl; - this.pagesUrls = pagesUrls; } public TopicTask.ResultCode getResultCode() { return resultCode; } - public String getBaseUrl() { - return baseUrl; - } - public String getTopicTitle() { return topicTitle; } @@ -114,12 +95,4 @@ public class TopicTaskResult { public String getTopicViewers() { return topicViewers; } - - public String getLastPageLoadAttemptedUrl() { - return lastPageLoadAttemptedUrl; - } - - public SparseArray getPagesUrls() { - return pagesUrls; - } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java index 727b59b9..61a02884 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java @@ -2,10 +2,11 @@ package gr.thmmy.mthmmy.activities.upload; import android.app.Activity; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.ResolveInfo; import android.database.Cursor; +import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; @@ -42,8 +43,12 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.base.BaseActivity; @@ -134,46 +139,62 @@ public class UploadActivity extends BaseActivity { rootCategorySpinner.setOnItemSelectedListener(new CustomOnItemSelectedListener(uploadRootCategories)); titleDescriptionBuilderButton = findViewById(R.id.upload_title_description_builder); - titleDescriptionBuilderButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (categorySelected.equals("-1")) { - Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); - return; - } + titleDescriptionBuilderButton.setOnClickListener(view -> { + if (categorySelected.equals("-1")) { + Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); + return; + } - int numberOfSpinners = categoriesSpinners.getChildCount(); + int numberOfSpinners = categoriesSpinners.getChildCount(); - if (numberOfSpinners < 3) { + if (numberOfSpinners < 3) { + Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show(); + return; + } + + String maybeSemester = "", maybeCourse = ""; + + if (numberOfSpinners == 5) { + if (((AppCompatSpinnerWithoutDefault) categoriesSpinners.getChildAt(numberOfSpinners - 1)). + getSelectedItemPosition() == -1) { + maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 4)).getSelectedItem(); + maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem(); + } else { Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show(); - return; } - - String maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) + } else if (numberOfSpinners == 4) { + maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 3)).getSelectedItem(); + maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem(); + } else { + maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem(); - String maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) + maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem(); + } - if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) { - Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show(); - return; - } - if (maybeCourse == null) { - Toast.makeText(view.getContext(), "Please choose a course", Toast.LENGTH_SHORT).show(); - return; - } + if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) { + Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show(); + return; + } + if (maybeCourse == null) { + Toast.makeText(view.getContext(), "Please choose a course", Toast.LENGTH_SHORT).show(); + return; + } - //Fixes course and semester - maybeCourse = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim(); - maybeSemester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); + //Fixes course and semester + maybeCourse = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim(); + maybeSemester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); - Intent intent = new Intent(UploadActivity.this, UploadFieldsBuilderActivity.class); - Bundle extras = new Bundle(); - extras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE, maybeCourse); - extras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER, maybeSemester); - intent.putExtras(extras); - startActivityForResult(intent, REQUEST_CODE_FIELDS_BUILDER); - } + Intent intent = new Intent(UploadActivity.this, UploadFieldsBuilderActivity.class); + Bundle builderExtras = new Bundle(); + builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE, maybeCourse); + builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER, maybeSemester); + intent.putExtras(builderExtras); + startActivityForResult(intent, REQUEST_CODE_FIELDS_BUILDER); }); titleDescriptionBuilderButton.setEnabled(false); @@ -183,128 +204,132 @@ public class UploadActivity extends BaseActivity { selectFileButton = findViewById(R.id.upload_select_file_button); Drawable selectStartDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_insert_drive_file_white_24dp); selectFileButton.setCompoundDrawablesRelativeWithIntrinsicBounds(selectStartDrawable, null, null, null); - selectFileButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - final CharSequence[] options = {"Take photo", "Choose file", - "Cancel"}; - AlertDialog.Builder builder = new AlertDialog.Builder(UploadActivity.this); - builder.setTitle("Upload file"); - builder.setItems(options, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int item) { - if (options[item].equals("Take photo")) { - Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - startActivityForResult(intent, REQUEST_CODE_CAMERA); - } else if (options[item].equals("Choose file")) { - String[] mimeTypes = {"image/jpeg", "text/html", "image/png", "image/jpg", "image/gif", - "application/pdf", "application/rar", "application/x-tar", "application/zip", - "application/msword", "image/vnd.djvu", "application/gz", "application/tar.gz"}; - - Intent intent = new Intent(Intent.ACTION_GET_CONTENT) - //.setType("*/*") - .setType("image/jpeg") - .putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); - - startActivityForResult(intent, REQUEST_CODE_CHOOSE_FILE); - } else if (options[item].equals("Cancel")) { - dialog.dismiss(); - } + selectFileButton.setOnClickListener(v -> { + final CharSequence[] options = {"Take photo", "Choose file", + "Cancel"}; + AlertDialog.Builder builder = new AlertDialog.Builder(UploadActivity.this); + builder.setTitle("Upload file"); + builder.setItems(options, (dialog, item) -> { + if (options[item].equals("Take photo")) { + /*Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + startActivityForResult(intent, REQUEST_CODE_CAMERA);*/ + + Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + takePhotoIntent.putExtra("return-data", true); + takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(UploadsHelper.getTempFile(this))); + + Intent targetedIntent = new Intent(takePhotoIntent); + List resInfo = this.getPackageManager().queryIntentActivities(takePhotoIntent, 0); + for (ResolveInfo resolveInfo : resInfo) { + String packageName = resolveInfo.activityInfo.packageName; + targetedIntent.setPackage(packageName); } - }); - builder.show(); - } - }); + startActivityForResult(takePhotoIntent, REQUEST_CODE_CAMERA); - findViewById(R.id.upload_upload_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - String uploadTitleText = uploadTitle.getText().toString(); - String uploadDescriptionText = uploadDescription.getText().toString(); + } else if (options[item].equals("Choose file")) { + String[] mimeTypes = {"image/jpeg", "text/html", "image/png", "image/jpg", "image/gif", + "application/pdf", "application/rar", "application/x-tar", "application/zip", + "application/msword", "image/vnd.djvu", "application/gz", "application/tar.gz"}; - if (uploadTitleText.equals("")) { - uploadTitle.setError("Required"); - } - if (fileUri == null) { - selectFileButton.setError("Required"); - } - if (categorySelected.equals("-1")) { - Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); - } + Intent intent = new Intent(Intent.ACTION_GET_CONTENT) + //.setType("*/*") + .setType("image/jpeg") + .putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); - if (categorySelected.equals("-1") || uploadTitleText.equals("") || fileUri == null) { - return; + startActivityForResult(intent, REQUEST_CODE_CHOOSE_FILE); + } else if (options[item].equals("Cancel")) { + dialog.dismiss(); } + }); + builder.show(); + }); - String tmpDescriptionText = uploadDescriptionText; + findViewById(R.id.upload_upload_button).setOnClickListener(view -> { + String uploadTitleText = uploadTitle.getText().toString(); + String uploadDescriptionText = uploadDescription.getText().toString(); - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(view.getContext()); - if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) { - tmpDescriptionText += uploadedFrommThmmyPromptHtml; - } + if (uploadTitleText.equals("")) { + uploadTitle.setError("Required"); + } + if (fileUri == null) { + selectFileButton.setError("Required"); + } + if (categorySelected.equals("-1")) { + Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); + } - String tempFilePath = null; - if (uploadFilename != null) { - tempFilePath = createTempFile(uploadFilename); - if (tempFilePath == null) { - //Something went wrong, abort - return; - } - } + if (categorySelected.equals("-1") || uploadTitleText.equals("") || fileUri == null) { + return; + } - try { - final String finalTempFilePath = tempFilePath; - new MultipartUploadRequest(view.getContext(), uploadIndexUrl) - .setUtf8Charset() - .addParameter("tp-dluploadtitle", uploadTitleText) - .addParameter("tp-dluploadcat", categorySelected) - .addParameter("tp-dluploadtext", tmpDescriptionText) - .addFileToUpload(tempFilePath == null - ? fileUri.toString() - : tempFilePath - , "tp-dluploadfile") - .addParameter("tp_dluploadicon", fileIcon) - .addParameter("tp-uploaduser", uploaderProfileIndex) - .setNotificationConfig(new UploadNotificationConfig()) - .setMaxRetries(2) - .setDelegate(new UploadStatusDelegate() { - @Override - public void onProgress(Context context, UploadInfo uploadInfo) { - } + String tmpDescriptionText = uploadDescriptionText; - @Override - public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse, - Exception exception) { - Toast.makeText(context, "Upload failed", Toast.LENGTH_SHORT).show(); - if (finalTempFilePath != null) { - if (!deleteTempFile(finalTempFilePath)) { - Toast.makeText(context, "Failed to delete temp file", Toast.LENGTH_SHORT).show(); - } + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(view.getContext()); + if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) { + tmpDescriptionText += uploadedFrommThmmyPromptHtml; + } + + String tempFilePath = null; + if (uploadFilename != null) { + tempFilePath = UploadsHelper.createTempFile(this, fileUri, uploadFilename); + if (tempFilePath == null) { + //Something went wrong, abort + return; + } + } + + try { + final String finalTempFilePath = tempFilePath; + new MultipartUploadRequest(view.getContext(), uploadIndexUrl) + .setUtf8Charset() + .addParameter("tp-dluploadtitle", uploadTitleText) + .addParameter("tp-dluploadcat", categorySelected) + .addParameter("tp-dluploadtext", tmpDescriptionText) + .addFileToUpload(tempFilePath == null + ? fileUri.toString() + : tempFilePath + , "tp-dluploadfile") + .addParameter("tp_dluploadicon", fileIcon) + .addParameter("tp-uploaduser", uploaderProfileIndex) + .setNotificationConfig(new UploadNotificationConfig()) + .setMaxRetries(2) + .setDelegate(new UploadStatusDelegate() { + @Override + public void onProgress(Context context, UploadInfo uploadInfo) { + } + + @Override + public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse, + Exception exception) { + Toast.makeText(context, "Upload failed", Toast.LENGTH_SHORT).show(); + if (finalTempFilePath != null) { + if (!UploadsHelper.deleteTempFile(finalTempFilePath)) { + Toast.makeText(context, "Failed to delete temporary file", Toast.LENGTH_SHORT).show(); } } + } - @Override - public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) { - if (finalTempFilePath != null) { - if (!deleteTempFile(finalTempFilePath)) { - Toast.makeText(context, "Failed to delete temp file", Toast.LENGTH_SHORT).show(); - } + @Override + public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) { + if (finalTempFilePath != null) { + if (!UploadsHelper.deleteTempFile(finalTempFilePath)) { + Toast.makeText(context, "Failed to delete temporary file", Toast.LENGTH_SHORT).show(); } } + } - @Override - public void onCancelled(Context context, UploadInfo uploadInfo) { - if (finalTempFilePath != null) { - if (!deleteTempFile(finalTempFilePath)) { - Toast.makeText(context, "Failed to delete temp file", Toast.LENGTH_SHORT).show(); - } + @Override + public void onCancelled(Context context, UploadInfo uploadInfo) { + if (finalTempFilePath != null) { + if (!UploadsHelper.deleteTempFile(finalTempFilePath)) { + Toast.makeText(context, "Failed to delete temporary file", Toast.LENGTH_SHORT).show(); } } - }) - .startUpload(); - } catch (Exception exception) { - Timber.e(exception, "AndroidUploadService: %s", exception.getMessage()); - } + } + }) + .startUpload(); + } catch (Exception exception) { + Timber.e(exception, "AndroidUploadService: %s", exception.getMessage()); } }); @@ -326,7 +351,7 @@ public class UploadActivity extends BaseActivity { if (bundleCategory != null) { int bundleSelectionIndex = -1, currentIndex = 0; for (UploadCategory category : uploadRootCategories) { - if (category.getCategoryTitle().contains(bundleCategory.get(0))) { //TODO fix .contains, always false + if (bundleCategory.get(0).contains(category.getCategoryTitle())) { bundleSelectionIndex = currentIndex; break; } @@ -335,6 +360,7 @@ public class UploadActivity extends BaseActivity { if (bundleSelectionIndex != -1) { rootCategorySpinner.setSelection(bundleSelectionIndex, true); + bundleCategory.remove(0); } } @@ -373,7 +399,7 @@ public class UploadActivity extends BaseActivity { fileUri = data.getData(); if (fileUri != null) { - String filename = filenameFromUri(fileUri); + String filename = UploadsHelper.filenameFromUri(this, fileUri); selectFileButton.setText(filename); filename = filename.toLowerCase(); @@ -396,11 +422,37 @@ public class UploadActivity extends BaseActivity { fileIcon = "blank.gif"; } } - } else if (requestCode == REQUEST_CODE_CAMERA && data != null) { + } else if (requestCode == REQUEST_CODE_CAMERA) { if (resultCode == Activity.RESULT_CANCELED) { return; } - //TODO + + Bitmap bitmap; + File cacheImageFile = UploadsHelper.getTempFile(this); + if (resultCode == Activity.RESULT_OK) { + fileUri = Uri.fromFile(cacheImageFile); + fileIcon = "jpg_image.gif"; + + bitmap = UploadsHelper.getImageResized(this, fileUri); + int rotation = UploadsHelper.getRotation(this, fileUri); + bitmap = UploadsHelper.rotate(bitmap, rotation); + + try { + FileOutputStream out = new FileOutputStream(cacheImageFile); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); + out.flush(); + out.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + String newFilename = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE). + format(new Date()); + fileUri = Uri.parse(UploadsHelper.createTempFile(this, fileUri, newFilename)); + + newFilename += ".jpg"; + selectFileButton.setText(newFilename); + } } else if (requestCode == REQUEST_CODE_FIELDS_BUILDER) { if (resultCode == Activity.RESULT_CANCELED) { return; @@ -414,82 +466,6 @@ public class UploadActivity extends BaseActivity { } } - @NonNull - private String filenameFromUri(Uri uri) { - String filename = null; - if (uri.getScheme().equals("content")) { - try (Cursor cursor = getContentResolver().query(uri, null, null, null, null)) { - if (cursor != null && cursor.moveToFirst()) { - filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); - } - } - } - if (filename == null) { - filename = uri.getPath(); - int cut = filename.lastIndexOf('/'); - if (cut != -1) { - filename = filename.substring(cut + 1); - } - } - - return filename; - } - - @Nullable - private String createTempFile(String newFilename) { - String oldFilename = filenameFromUri(fileUri); - String fileExtension = oldFilename.substring(oldFilename.indexOf(".")); - String destinationFilename = android.os.Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + "~tmp_mThmmy_uploads" + File.separatorChar + newFilename + fileExtension; - - File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + "~tmp_mThmmy_uploads"); - - if (!tempDirectory.exists()) { - if (!tempDirectory.mkdirs()) { - Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName()); - Toast.makeText(this, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show(); - return null; - } - } - - InputStream inputStream; - BufferedInputStream bufferedInputStream = null; - BufferedOutputStream bufferedOutputStream = null; - - try { - inputStream = getContentResolver().openInputStream(fileUri); - if (inputStream == null) { - Timber.w("Input stream was null, %s", UploadActivity.class.getSimpleName()); - return null; - } - - bufferedInputStream = new BufferedInputStream(inputStream); - bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destinationFilename, false)); - byte[] buf = new byte[1024]; - bufferedInputStream.read(buf); - do { - bufferedOutputStream.write(buf); - } while (bufferedInputStream.read(buf) != -1); - } catch (IOException exception) { - exception.printStackTrace(); - } finally { - try { - if (bufferedInputStream != null) bufferedInputStream.close(); - if (bufferedOutputStream != null) bufferedOutputStream.close(); - } catch (IOException exception) { - exception.printStackTrace(); - } - } - - return destinationFilename; - } - - private boolean deleteTempFile(String destinationFilename) { - File file = new File(destinationFilename); - return file.delete(); - } - private class CustomOnItemSelectedListener implements AdapterView.OnItemSelectedListener { private ArrayList parentCategories, childCategories; @@ -534,10 +510,12 @@ public class UploadActivity extends BaseActivity { categoriesSpinners.addView(subSpinner); //Sets bundle selection - if (bundleCategory != null && viewIndex < bundleCategory.size()) { + if (bundleCategory != null && !bundleCategory.isEmpty()) { int bundleSelectionIndex = -1, currentIndex = 0; - for (UploadCategory category : parentCategories) { - if (category.getCategoryTitle().contains(bundleCategory.get(viewIndex))) { + + for (UploadCategory category : childCategories) { + if (bundleCategory.get(0).contains(category.getCategoryTitle() + .replace("-", "").trim())) { bundleSelectionIndex = currentIndex; break; } @@ -546,6 +524,7 @@ public class UploadActivity extends BaseActivity { if (bundleSelectionIndex != -1) { subSpinner.setSelection(bundleSelectionIndex, true); + bundleCategory.remove(0); } } } @@ -627,8 +606,9 @@ public class UploadActivity extends BaseActivity { //Sets bundle selection if (bundleCategory != null) { int bundleSelectionIndex = -1, currentIndex = 0; + for (UploadCategory category : uploadRootCategories) { - if (category.getCategoryTitle().contains(bundleCategory.get(0))) { //TODO fix .contains, always false + if (bundleCategory.get(0).contains(category.getCategoryTitle())) { bundleSelectionIndex = currentIndex; break; } @@ -637,6 +617,7 @@ public class UploadActivity extends BaseActivity { if (bundleSelectionIndex != -1) { rootCategorySpinner.setSelection(bundleSelectionIndex, true); + bundleCategory.remove(0); } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java index 73bf6962..0cd8eb70 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java @@ -96,44 +96,42 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity { semesterChooserLinear = findViewById(R.id.upload_fields_builder_choose_semester); semesterRadio = findViewById(R.id.upload_fields_builder_semester_radio_group); + semesterRadio.check(Integer.parseInt(semester) % 2 == 0 + ? R.id.upload_fields_builder_radio_button_jun + : R.id.upload_fields_builder_radio_button_feb); + year = findViewById(R.id.upload_fields_builder_year); year.addTextChangedListener(customYearWatcher); typeRadio = findViewById(R.id.upload_fields_builder_type_radio_group); - typeRadio.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - if (checkedId == R.id.upload_fields_builder_radio_button_notes) { - semesterChooserLinear.setVisibility(View.GONE); - } else { - semesterChooserLinear.setVisibility(View.VISIBLE); - } + typeRadio.setOnCheckedChangeListener((group, checkedId) -> { + if (checkedId == R.id.upload_fields_builder_radio_button_notes) { + semesterChooserLinear.setVisibility(View.GONE); + } else { + semesterChooserLinear.setVisibility(View.VISIBLE); } }); - findViewById(R.id.upload_fields_builder_submit).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - int typeId = typeRadio.getCheckedRadioButtonId(), - semesterId = semesterRadio.getCheckedRadioButtonId(); - if (typeId == -1) { - Toast.makeText(view.getContext(), "Please choose a type for the upload", Toast.LENGTH_SHORT).show(); - return; - } else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) { - Toast.makeText(view.getContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show(); - return; - } else if (year.getText().toString().isEmpty()) { - Toast.makeText(view.getContext(), "Please choose a year for the upload", Toast.LENGTH_SHORT).show(); - return; - } - - Intent returnIntent = new Intent(); - returnIntent.putExtra(RESULT_FILENAME, buildFilename()); - returnIntent.putExtra(RESULT_TITLE, buildTitle()); - returnIntent.putExtra(RESULT_DESCRIPTION, buildDescription()); - setResult(Activity.RESULT_OK, returnIntent); - finish(); + findViewById(R.id.upload_fields_builder_submit).setOnClickListener(view -> { + int typeId = typeRadio.getCheckedRadioButtonId(), + semesterId = semesterRadio.getCheckedRadioButtonId(); + if (typeId == -1) { + Toast.makeText(view.getContext(), "Please choose a type for the upload", Toast.LENGTH_SHORT).show(); + return; + } else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) { + Toast.makeText(view.getContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show(); + return; + } else if (year.getText().toString().isEmpty()) { + Toast.makeText(view.getContext(), "Please choose a year for the upload", Toast.LENGTH_SHORT).show(); + return; } + + Intent returnIntent = new Intent(); + returnIntent.putExtra(RESULT_FILENAME, buildFilename()); + returnIntent.putExtra(RESULT_TITLE, buildTitle()); + returnIntent.putExtra(RESULT_DESCRIPTION, buildDescription()); + setResult(Activity.RESULT_OK, returnIntent); + finish(); }); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java new file mode 100644 index 00000000..9a2be209 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java @@ -0,0 +1,179 @@ +package gr.thmmy.mthmmy.activities.upload; + +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.media.ExifInterface; +import android.net.Uri; +import android.provider.OpenableColumns; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.widget.Toast; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import timber.log.Timber; + +class UploadsHelper { + private static final int DEFAULT_MIN_WIDTH_QUALITY = 400; + private static final String TEMP_IMAGE_NAME = "tempUploadFile.jpg"; + + @NonNull + static String filenameFromUri(Context context, Uri uri) { + String filename = null; + if (uri.getScheme().equals("content")) { + try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) { + filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + } + } + } + if (filename == null) { + filename = uri.getPath(); + int cut = filename.lastIndexOf('/'); + if (cut != -1) { + filename = filename.substring(cut + 1); + } + } + + return filename; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Nullable + static String createTempFile(Context context, Uri fileUri, String newFilename) { + String oldFilename = filenameFromUri(context, fileUri); + String fileExtension = oldFilename.substring(oldFilename.indexOf(".")); + String destinationFilename = android.os.Environment.getExternalStorageDirectory().getPath() + + File.separatorChar + "~tmp_mThmmy_uploads" + File.separatorChar + newFilename + fileExtension; + + File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() + + File.separatorChar + "~tmp_mThmmy_uploads"); + + if (!tempDirectory.exists()) { + if (!tempDirectory.mkdirs()) { + Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName()); + Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show(); + return null; + } + } + + InputStream inputStream; + BufferedInputStream bufferedInputStream = null; + BufferedOutputStream bufferedOutputStream = null; + + try { + inputStream = context.getContentResolver().openInputStream(fileUri); + if (inputStream == null) { + Timber.w("Input stream was null, %s", UploadActivity.class.getSimpleName()); + return null; + } + + bufferedInputStream = new BufferedInputStream(inputStream); + bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destinationFilename, false)); + byte[] buf = new byte[1024]; + bufferedInputStream.read(buf); + do { + bufferedOutputStream.write(buf); + } while (bufferedInputStream.read(buf) != -1); + } catch (IOException exception) { + exception.printStackTrace(); + } finally { + try { + if (bufferedInputStream != null) bufferedInputStream.close(); + if (bufferedOutputStream != null) bufferedOutputStream.close(); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + + return destinationFilename; + } + + static File getTempFile(Context context) { + File imageFile = new File(context.getExternalCacheDir(), TEMP_IMAGE_NAME); + //noinspection ResultOfMethodCallIgnored + imageFile.getParentFile().mkdirs(); + return imageFile; + } + + static boolean deleteTempFile(String destinationFilename) { + File file = new File(destinationFilename); + return file.delete(); + } + + /** + * Resize to avoid using too much memory loading big images (e.g.: 2560*1920) + **/ + static Bitmap getImageResized(Context context, Uri selectedImage) { + Bitmap bm; + int[] sampleSizes = new int[]{5, 3, 2, 1}; + int i = 0; + do { + bm = decodeBitmap(context, selectedImage, sampleSizes[i]); + i++; + } while (bm.getWidth() < DEFAULT_MIN_WIDTH_QUALITY && i < sampleSizes.length); + return bm; + } + + private static Bitmap decodeBitmap(Context context, Uri theUri, int sampleSize) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = sampleSize; + + AssetFileDescriptor fileDescriptor = null; + try { + fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + assert fileDescriptor != null; + return BitmapFactory.decodeFileDescriptor( + fileDescriptor.getFileDescriptor(), null, options); + } + + static int getRotation(Context context, Uri imageUri) { + int rotation = 0; + try { + + context.getContentResolver().notifyChange(imageUri, null); + ExifInterface exif = new ExifInterface(imageUri.getPath()); + int orientation = exif.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL); + + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_270: + rotation = 270; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + rotation = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_90: + rotation = 90; + break; + } + } catch (Exception e) { + e.printStackTrace(); + } + return rotation; + } + + static Bitmap rotate(Bitmap bm, int rotation) { + if (rotation != 0) { + Matrix matrix = new Matrix(); + matrix.postRotate(rotation); + return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); + } + return bm; + } +} 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 8641f288..078386dc 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java @@ -45,6 +45,7 @@ public class Post { private final String numberOfPosts; private final String personalText; private final int numberOfStars; + private final boolean isUserMentionedInPost; // Suppresses default constructor @SuppressWarnings("unused") @@ -70,6 +71,7 @@ public class Post { postURL = null; postDeleteURL = null; postEditURL = null; + isUserMentionedInPost = false; postType = -1; } @@ -102,7 +104,8 @@ public class Post { , @Nullable String special_rank, @Nullable String gender, @Nullable String numberOfPosts , @Nullable String personalText, int numberOfStars, int userColor , @Nullable ArrayList attachedFiles, @Nullable String lastEdit, String postURL - , @Nullable String postDeleteURL, @Nullable String postEditURL, int postType) { + , @Nullable String postDeleteURL, @Nullable String postEditURL, boolean isUserMentionedInPost + , int postType) { if (Objects.equals(thumbnailUrl, "")) this.thumbnailUrl = null; else this.thumbnailUrl = thumbnailUrl; this.author = author; @@ -125,6 +128,7 @@ public class Post { this.postURL = postURL; this.postDeleteURL = postDeleteURL; this.postEditURL = postEditURL; + this.isUserMentionedInPost = isUserMentionedInPost; this.postType = postType; } @@ -143,12 +147,13 @@ public class Post { * @param userColor author's user color * @param attachedFiles post's attached files * @param lastEdit post's last edit date - * @param postURL post's URL + * @param postURL post's URL */ public Post(@Nullable String thumbnailUrl, String author, String subject, String content , int postIndex, int postNumber, String postDate, int userColor , @Nullable ArrayList attachedFiles, @Nullable String lastEdit, String postURL - , @Nullable String postDeleteURL, @Nullable String postEditURL, int postType) { + , @Nullable String postDeleteURL, @Nullable String postEditURL, boolean isUserMentionedInPost + , int postType) { if (Objects.equals(thumbnailUrl, "")) this.thumbnailUrl = null; else this.thumbnailUrl = thumbnailUrl; this.author = author; @@ -171,12 +176,13 @@ public class Post { this.postURL = postURL; this.postDeleteURL = postDeleteURL; this.postEditURL = postEditURL; + this.isUserMentionedInPost = isUserMentionedInPost; this.postType = postType; } public static Post newQuickReply() { return new Post(null, null, null, null, 0, 0, null, - 0, null, null, null, null, null, TYPE_QUICK_REPLY); + 0, null, null, null, null, null, false, TYPE_QUICK_REPLY); } //Getters @@ -390,6 +396,10 @@ public class Post { return postType; } + public boolean isUserMentionedInPost() { + return isUserMentionedInPost; + } + public void setPostType(int postType) { this.postType = postType; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java index d82e349b..9f843c20 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java +++ b/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 { @@ -48,4 +49,16 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.BehaviorWhen a nested ScrollView is scrolled down, the view will disappear. - * When the ScrollView is scrolled back up, the view will reappear.

    + * 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 + *

    */ @SuppressWarnings("unused") public class ScrollAwareLinearBehavior extends CoordinatorLayout.Behavior { @@ -111,4 +114,16 @@ public class ScrollAwareLinearBehavior extends CoordinatorLayout.Behavior 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; + } } \ No newline at end of file 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 307efab0..0034ffc4 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 @@ -6,6 +6,8 @@ import org.jsoup.select.Elements; import java.util.ArrayList; +import timber.log.Timber; + /** * This class consists exclusively of static classes (enums) and methods (excluding methods of inner * classes). It can be used to resolve a page's language and state or fix embedded videos html code. @@ -166,4 +168,22 @@ public class ParseHelpers { } return fixed; } + + /** + * Method that extracts the base URL from a topic's page URL. For example a topic with url similar to + * "https://www.thmmy.gr/smf/index.php?topic=1.15;topicseen" or + * "https://www.thmmy.gr/smf/index.php?topic=1.msg1#msg1" + * has the base url "https://www.thmmy.gr/smf/index.php?topic=1" + * + * @param topicURL a topic's page URL + * @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 ""; + } + } } 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 1fb97407..080414ab 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java +++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java @@ -21,6 +21,7 @@ import gr.thmmy.mthmmy.activities.topic.tasks.TopicTaskResult; import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.model.Post; import gr.thmmy.mthmmy.session.SessionManager; +import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted, PrepareForReply.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished { @@ -54,44 +55,58 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa private EditTask.EditTaskCallbacks editTaskCallbacks; private PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks; - private MutableLiveData topicTaskResult = new MutableLiveData<>(); + /** + * Holds the value (index) of the page to be requested when a user interaction with bottom + * navigation bar occurs, aka the value that the page indicator shows + */ + private MutableLiveData pageIndicatorIndex = new MutableLiveData<>(); + + private MutableLiveData replyPageUrl = new MutableLiveData<>(); + private MutableLiveData pageTopicId = new MutableLiveData<>(); + private MutableLiveData topicTitle = new MutableLiveData<>(); + private MutableLiveData> postsList = new MutableLiveData<>(); + private MutableLiveData focusedPostIndex = new MutableLiveData<>(); + private MutableLiveData topicTaskResultCode = new MutableLiveData<>(); + private MutableLiveData topicTreeAndMods = new MutableLiveData<>(); + private MutableLiveData topicViewers = new MutableLiveData<>(); + private String topicUrl; + private int currentPageIndex; + private int pageCount; + private MutableLiveData prepareForReplyResult = new MutableLiveData<>(); private MutableLiveData prepareForEditResult = new MutableLiveData<>(); - private String firstTopicUrl; - - public void initialLoad(String pageUrl) { - firstTopicUrl = pageUrl; - currentTopicTask = new TopicTask(topicTaskObserver, this); - currentTopicTask.execute(pageUrl); - } - public void loadUrl(String pageUrl) { stopLoading(); + topicUrl = pageUrl; currentTopicTask = new TopicTask(topicTaskObserver, this); currentTopicTask.execute(pageUrl); } public void reloadPage() { - if (topicTaskResult.getValue() == null) - throw new NullPointerException("No topic task has finished yet!"); - loadUrl(topicTaskResult.getValue().getLastPageLoadAttemptedUrl()); + if (topicUrl == null) throw new NullPointerException("No topic task has been requested yet!"); + loadUrl(topicUrl); } - public void changePage(int pageRequested) { - if (topicTaskResult.getValue() == null) + public void performPageChange() { + if (pageIndicatorIndex.getValue() == null) throw new NullPointerException("No page has been loaded yet!"); - if (pageRequested != topicTaskResult.getValue().getCurrentPageIndex() - 1) - loadUrl(topicTaskResult.getValue().getPagesUrls().get(pageRequested)); + int pageRequested = pageIndicatorIndex.getValue() - 1; + if (pageRequested != currentPageIndex - 1) { + loadUrl(ParseHelpers.getBaseURL(topicUrl) + "." + String.valueOf(pageRequested * 15)); + pageIndicatorIndex.setValue(pageRequested + 1); + } else { + stopLoading(); + } } public void prepareForReply() { - if (topicTaskResult.getValue() == null) + if (replyPageUrl.getValue() == null) throw new NullPointerException("Topic task has not finished yet!"); stopLoading(); - changePage(topicTaskResult.getValue().getPageCount() - 1); + setPageIndicatorIndex(pageCount, true); currentPrepareForReplyTask = new PrepareForReply(prepareForReplyCallbacks, this, - topicTaskResult.getValue().getReplyPageUrl()); + replyPageUrl.getValue()); currentPrepareForReplyTask.execute(toQuoteList.toArray(new Integer[0])); } @@ -116,11 +131,11 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa } public void prepareForEdit(int position, String postEditURL) { - if (topicTaskResult.getValue() == null) + if (replyPageUrl.getValue() == null) throw new NullPointerException("Topic task has not finished yet!"); stopLoading(); currentPrepareForEditTask = new PrepareForEditTask(prepareForEditCallbacks, this, position, - topicTaskResult.getValue().getReplyPageUrl()); + replyPageUrl.getValue()); currentPrepareForEditTask.execute(postEditURL); } @@ -140,6 +155,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa public void stopLoading() { if (currentTopicTask != null && currentTopicTask.getStatus() == AsyncTask.Status.RUNNING) { currentTopicTask.cancel(true); + pageIndicatorIndex.setValue(currentPageIndex); topicTaskObserver.onTopicTaskCancelled(); } if (currentPrepareForEditTask != null && currentPrepareForEditTask.getStatus() == AsyncTask.Status.RUNNING) { @@ -157,30 +173,108 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa // callbacks for viewmodel @Override public void onTopicTaskCompleted(TopicTaskResult result) { - topicTaskResult.setValue(result); if (result.getResultCode() == TopicTask.ResultCode.SUCCESS) { + currentPageIndex = result.getCurrentPageIndex(); + pageCount = result.getPageCount(); + topicTreeAndMods.setValue(result.getTopicTreeAndMods()); + topicViewers.setValue(result.getTopicViewers()); + pageTopicId.setValue(result.getLoadedPageTopicId()); + replyPageUrl.setValue(result.getReplyPageUrl()); + topicTitle.setValue(result.getTopicTitle()); + pageIndicatorIndex.setValue(result.getCurrentPageIndex()); + postsList.setValue(result.getNewPostsList()); + focusedPostIndex.setValue(result.getFocusedPostIndex()); isUserExtraInfoVisibile.clear(); for (int i = 0; i < result.getNewPostsList().size(); i++) { isUserExtraInfoVisibile.add(false); } } + topicTaskResultCode.setValue(result.getResultCode()); } @Override public void onPrepareForReplyFinished(PrepareForReplyResult result) { - writingReply = true; prepareForReplyResult.setValue(result); } @Override public void onPrepareEditFinished(PrepareForEditResult result, int position) { - editingPost = true; postBeingEditedPosition = position; prepareForEditResult.setValue(result); } + public void incrementPageRequestValue(int step, boolean changePage) { + if (pageIndicatorIndex.getValue() == null) + throw new NullPointerException("No page has been loaded yet!"); + int oldIndicatorIndex = pageIndicatorIndex.getValue(); + if (oldIndicatorIndex <= pageCount - step) { + pageIndicatorIndex.setValue(pageIndicatorIndex.getValue() + step); + } else + pageIndicatorIndex.setValue(pageCount); + if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) performPageChange(); + } + + public void decrementPageRequestValue(int step, boolean changePage) { + if (pageIndicatorIndex.getValue() == null) + throw new NullPointerException("No page has been loaded yet!"); + int oldIndicatorIndex = pageIndicatorIndex.getValue(); + if (oldIndicatorIndex > step) { + pageIndicatorIndex.setValue(pageIndicatorIndex.getValue() - step); + } else + pageIndicatorIndex.setValue(1); + if (changePage && oldIndicatorIndex != pageIndicatorIndex.getValue()) 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 && oldIndicatorIndex != this.pageIndicatorIndex.getValue()) performPageChange(); + } + // <-------------Just getters, setters and helper methods below here----------------> + public MutableLiveData getTopicViewers() { + return topicViewers; + } + + public MutableLiveData getTopicTreeAndMods() { + return topicTreeAndMods; + } + + public MutableLiveData getTopicTaskResultCode() { + return topicTaskResultCode; + } + + public MutableLiveData getFocusedPostIndex() { + return focusedPostIndex; + } + + public MutableLiveData> getPostsList() { + return postsList; + } + + public MutableLiveData getReplyPageUrl() { + return replyPageUrl; + } + + public MutableLiveData getPageTopicId() { + return pageTopicId; + } + + public MutableLiveData getTopicTitle() { + return topicTitle; + } + + public String getTopicUrl() { + return topicUrl; + } + + public MutableLiveData getPageIndicatorIndex() { + return pageIndicatorIndex; + } + public boolean isUserExtraInfoVisible(int position) { return isUserExtraInfoVisibile.get(position); } @@ -228,10 +322,6 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa this.prepareForReplyCallbacks = prepareForReplyCallbacks; } - public MutableLiveData getTopicTaskResult() { - return topicTaskResult; - } - public MutableLiveData getPrepareForReplyResult() { return prepareForReplyResult; } @@ -253,7 +343,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa } public boolean canReply() { - return topicTaskResult.getValue() != null && topicTaskResult.getValue().getReplyPageUrl() != null; + return replyPageUrl.getValue() != null; } public boolean isWritingReply() { @@ -264,40 +354,14 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa this.writingReply = writingReply; } - public String getBaseUrl() { - if (topicTaskResult.getValue() != null) { - return topicTaskResult.getValue().getBaseUrl(); - } else { - return ""; - } - } - - public String getTopicUrl() { - if (topicTaskResult.getValue() != null) { - return topicTaskResult.getValue().getLastPageLoadAttemptedUrl(); - } else { - // topic task has not finished yet (log? disable menu button until load is finished?) - return firstTopicUrl; - } - } - - public String getTopicTitle() { - if (topicTaskResult.getValue() == null) - throw new NullPointerException("Topic task has not finished yet!"); - return topicTaskResult.getValue().getTopicTitle(); - } - public int getCurrentPageIndex() { - if (topicTaskResult.getValue() == null) - throw new NullPointerException("No page has been loaded yet!"); - return topicTaskResult.getValue().getCurrentPageIndex(); + if (currentPageIndex == 0) throw new NullPointerException("No page has been loaded yet!"); + return currentPageIndex; } public int getPageCount() { - if (topicTaskResult.getValue() == null) - throw new NullPointerException("No page has been loaded yet!"); - - return topicTaskResult.getValue().getPageCount(); + if (pageCount == 0) throw new NullPointerException("No page has been loaded yet!"); + return pageCount; } public String getPostBeingEditedText() { @@ -307,10 +371,8 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa } public String getBuildedQuotes() { - if (prepareForReplyResult.getValue() != null) { - return prepareForReplyResult.getValue().getBuildedQuotes(); - } else { - return ""; - } + if (prepareForReplyResult.getValue() == null) + throw new NullPointerException("Reply preparation was not found"); + return prepareForReplyResult.getValue().getBuildedQuotes(); } } diff --git a/app/src/main/res/drawable/mention_card.xml b/app/src/main/res/drawable/mention_card.xml new file mode 100644 index 00000000..974d3d67 --- /dev/null +++ b/app/src/main/res/drawable/mention_card.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_topic.xml b/app/src/main/res/layout/activity_topic.xml index cb98088e..cb3a1072 100644 --- a/app/src/main/res/layout/activity_topic.xml +++ b/app/src/main/res/layout/activity_topic.xml @@ -47,6 +47,16 @@ app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + + android:text="@string/post_delete_button" + android:textColor="@color/primary_text" /> \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 257e7f22..ddf97608 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -17,6 +17,7 @@ #3C3F41 #8B8B8B #FF9800 + #FAA61A #FFFFFF #CCCCCC diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ed3f9c00..993d0ef1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -55,6 +55,10 @@ Subject… Submit Message… + Could not connect to thmmy.gr \n\n Tap to retry + Network error + retry + This topic is either missing or off limits to you Username