Browse Source

Code cleanup and commenting.

pull/24/head
Apostolos Fanakis 8 years ago
parent
commit
9b6e8da793
  1. 1
      app/src/main/AndroidManifest.xml
  2. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
  3. 11
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  4. 307
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  5. 3
      app/src/main/java/gr/thmmy/mthmmy/utils/FileManager/ThmmyFile.java
  6. 9
      app/src/main/res/layout/activity_profile.xml
  7. 8
      app/src/main/res/layout/activity_topic.xml

1
app/src/main/AndroidManifest.xml

@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application <application
android:allowBackup="true" android:allowBackup="true"

5
app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java

@ -33,6 +33,7 @@ import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.LoginActivity; import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.base.BaseActivity; import gr.thmmy.mthmmy.activities.base.BaseActivity;
import gr.thmmy.mthmmy.utils.CircleTransform; import gr.thmmy.mthmmy.utils.CircleTransform;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import mthmmy.utils.Report; import mthmmy.utils.Report;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@ -54,7 +55,7 @@ public class ProfileActivity extends BaseActivity {
private TextView userName; private TextView userName;
private TextView personalText; private TextView personalText;
private LinearLayout mainContent; private LinearLayout mainContent;
private ProgressBar progressBar; private MaterialProgressBar progressBar;
private FloatingActionButton replyFAB; private FloatingActionButton replyFAB;
//Other variables //Other variables
@ -94,7 +95,7 @@ public class ProfileActivity extends BaseActivity {
createDrawer(); createDrawer();
progressBar = (ProgressBar) findViewById(R.id.progressBar); progressBar = (MaterialProgressBar) findViewById(R.id.progressBar);
userThumbnail = (ImageView) findViewById(R.id.user_thumbnail); userThumbnail = (ImageView) findViewById(R.id.user_thumbnail);
userName = (TextView) findViewById(R.id.profile_act_username); userName = (TextView) findViewById(R.id.profile_act_username);

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

@ -61,7 +61,7 @@ public class TopicActivity extends BaseActivity {
*/ */
public static final String EXTRAS_TOPIC_TITLE = "TOPIC_TITLE"; public static final String EXTRAS_TOPIC_TITLE = "TOPIC_TITLE";
static String PACKAGE_NAME; static String PACKAGE_NAME;
private TopicTask topicTask; private static TopicTask topicTask;
//About posts //About posts
private List<Post> postsList; private List<Post> postsList;
static final int NO_POST_FOCUS = -1; static final int NO_POST_FOCUS = -1;
@ -124,7 +124,7 @@ public class TopicActivity extends BaseActivity {
recyclerView.setHasFixedSize(true); recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getApplicationContext()); layoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(new TopicAdapter(getApplicationContext(), postsList)); recyclerView.setAdapter(new TopicAdapter(getApplicationContext(), progressBar, postsList));
replyFAB = (FloatingActionButton) findViewById(R.id.topic_fab); replyFAB = (FloatingActionButton) findViewById(R.id.topic_fab);
replyFAB.setEnabled(false); replyFAB.setEnabled(false);
@ -209,6 +209,7 @@ public class TopicActivity extends BaseActivity {
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
recyclerView.setAdapter(null);
if (topicTask != null && topicTask.getStatus() != AsyncTask.Status.RUNNING) if (topicTask != null && topicTask.getStatus() != AsyncTask.Status.RUNNING)
topicTask.cancel(true); topicTask.cancel(true);
} }
@ -311,7 +312,7 @@ public class TopicActivity extends BaseActivity {
if (topicTask != null && topicTask.getStatus() != AsyncTask.Status.RUNNING) if (topicTask != null && topicTask.getStatus() != AsyncTask.Status.RUNNING)
topicTask.cancel(true); topicTask.cancel(true);
topicTask = new TopicTask(); //topicTask = new TopicTask();
topicTask.execute(pagesUrls.get(pageRequested)); //Attempt data parsing topicTask.execute(pagesUrls.get(pageRequested)); //Attempt data parsing
} }
@ -326,7 +327,7 @@ public class TopicActivity extends BaseActivity {
* <p>Calling ProfileTask's {@link AsyncTask#execute execute} method needs to have profile's url * <p>Calling ProfileTask's {@link AsyncTask#execute execute} method needs to have profile's url
* as String parameter!</p> * as String parameter!</p>
*/ */
public class TopicTask extends AsyncTask<String, Void, Integer> { class TopicTask extends AsyncTask<String, Void, Integer> {
//Class variables //Class variables
/** /**
* Debug Tag for logging debug output to LogCat * Debug Tag for logging debug output to LogCat
@ -380,7 +381,7 @@ public class TopicActivity extends BaseActivity {
case SUCCESS: case SUCCESS:
progressBar.setVisibility(ProgressBar.INVISIBLE); progressBar.setVisibility(ProgressBar.INVISIBLE);
recyclerView.swapAdapter(new TopicAdapter(getApplicationContext(), postsList), false); recyclerView.swapAdapter(new TopicAdapter(getApplicationContext(), progressBar, postsList), false);
//Set post focus //Set post focus
if (postFocus != NO_POST_FOCUS) { if (postFocus != NO_POST_FOCUS) {
for (int i = postsList.size() - 1; i >= 0; --i) { for (int i = postsList.size() - 1; i >= 0; --i) {

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

@ -11,6 +11,7 @@ import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.PowerManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.content.res.ResourcesCompat; import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.widget.CardView; import android.support.v7.widget.CardView;
@ -28,8 +29,10 @@ import android.widget.FrameLayout;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
@ -44,6 +47,7 @@ import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
import gr.thmmy.mthmmy.data.Post; import gr.thmmy.mthmmy.data.Post;
import gr.thmmy.mthmmy.utils.CircleTransform; import gr.thmmy.mthmmy.utils.CircleTransform;
import gr.thmmy.mthmmy.utils.FileManager.ThmmyFile; import gr.thmmy.mthmmy.utils.FileManager.ThmmyFile;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import mthmmy.utils.Report; import mthmmy.utils.Report;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@ -54,18 +58,49 @@ import static gr.thmmy.mthmmy.activities.topic.TopicActivity.base_url;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.postFocus; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.postFocus;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.toQuoteList; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.toQuoteList;
/**
* Custom {@link android.support.v7.widget.RecyclerView.Adapter} used for topics.
*/
class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> { class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
/**
* Debug Tag for logging debug output to LogCat
*/
private static final String TAG = "TopicAdapter"; private static final String TAG = "TopicAdapter";
/**
* Int that holds thumbnail's size defined in R.dimen
*/
private static int THUMBNAIL_SIZE; private static int THUMBNAIL_SIZE;
private final Context context; private final Context context;
private final List<Post> postsList; private final List<Post> postsList;
/**
* True if there is a post to focus to, false otherwise
*/
private boolean foundPostFocus = false; private boolean foundPostFocus = false;
/**
* Used to hold the state of visibility and other attributes for views that are animated or
* otherwise changed. Used in combination with {@link #isPostDateAndNumberVisibile},
* {@link #isUserExtraInfoVisibile} and {@link #isQuoteButtonChecked}.
*/
private final ArrayList<boolean[]> viewProperties = new ArrayList<>(); private final ArrayList<boolean[]> viewProperties = new ArrayList<>();
/**
* Index of state indicator in the boolean array. If true post is expanded and post's date and
* number are visible.
*/
private static final int isPostDateAndNumberVisibile = 0; private static final int isPostDateAndNumberVisibile = 0;
/**
* Index of state indicator in the boolean array. If true user's extra info are expanded and
* visible.
*/
private static final int isUserExtraInfoVisibile = 1; private static final int isUserExtraInfoVisibile = 1;
/**
* Index of state indicator in the boolean array. If true quote button for this post is checked.
*/
private static final int isQuoteButtonChecked = 2; private static final int isQuoteButtonChecked = 2;
private final MaterialProgressBar progressBar;
/**
* Custom {@link RecyclerView.ViewHolder} implementation
*/
class MyViewHolder extends RecyclerView.ViewHolder { class MyViewHolder extends RecyclerView.ViewHolder {
final CardView cardView; final CardView cardView;
final FrameLayout postDateAndNumberExp; final FrameLayout postDateAndNumberExp;
@ -82,9 +117,8 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
MyViewHolder(View view) { MyViewHolder(View view) {
super(view); super(view);
//Initializes layout's graphic elements
//Initialize layout's graphic elements //Standard stuff
//Basic stuff
cardView = (CardView) view.findViewById(R.id.card_view); cardView = (CardView) view.findViewById(R.id.card_view);
postDateAndNumberExp = (FrameLayout) view.findViewById(R.id.post_date_and_number_exp); postDateAndNumberExp = (FrameLayout) view.findViewById(R.id.post_date_and_number_exp);
postDate = (TextView) view.findViewById(R.id.post_date); postDate = (TextView) view.findViewById(R.id.post_date);
@ -98,7 +132,7 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
bodyFooterDivider = view.findViewById(R.id.body_footer_divider); bodyFooterDivider = view.findViewById(R.id.body_footer_divider);
postFooter = (LinearLayout) view.findViewById(R.id.post_footer); postFooter = (LinearLayout) view.findViewById(R.id.post_footer);
//User's extra //User's extra info
header = (RelativeLayout) view.findViewById(R.id.header); header = (RelativeLayout) view.findViewById(R.id.header);
userExtraInfo = (LinearLayout) view.findViewById(R.id.user_extra_info); userExtraInfo = (LinearLayout) view.findViewById(R.id.user_extra_info);
specialRank = (TextView) view.findViewById(R.id.special_rank); specialRank = (TextView) view.findViewById(R.id.special_rank);
@ -110,29 +144,39 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
} }
/** /**
* Possible cleanup needed (like so:) * Cancels all pending Picasso requests
* https://stackoverflow.com/questions/24897441/picasso-how-to-cancel-all-image-requests-made-in-an-adapter
* TODO
*/ */
void cleanup() {
Picasso.with(context).cancelRequest(thumbnail);
thumbnail.setImageDrawable(null);
}
} }
/**
TopicAdapter(Context context, List<Post> postsList) { * @param context the context of the {@link RecyclerView}
* @param postsList List of {@link Post} objects to use
*/
TopicAdapter(Context context, MaterialProgressBar progressBar, List<Post> postsList) {
this.context = context; this.context = context;
this.postsList = postsList; this.postsList = postsList;
THUMBNAIL_SIZE = (int) context.getResources().getDimension(R.dimen.thumbnail_size); THUMBNAIL_SIZE = (int) context.getResources().getDimension(R.dimen.thumbnail_size);
for (int i = 0; i < postsList.size(); ++i) { for (int i = 0; i < postsList.size(); ++i) {
//Initialize properties, array's values will be false by default //Initializes properties, array's values will be false by default
viewProperties.add(new boolean[3]); viewProperties.add(new boolean[3]);
} }
this.progressBar = progressBar;
}
@Override
public void onViewRecycled(final MyViewHolder holder) {
holder.cleanup();
} }
@Override @Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()) View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.activity_topic_post_row, parent, false); .inflate(R.layout.activity_topic_post_row, parent, false);
return new MyViewHolder(itemView); return new MyViewHolder(itemView);
} }
@ -141,15 +185,15 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
public void onBindViewHolder(final MyViewHolder holder, int position) { public void onBindViewHolder(final MyViewHolder holder, int position) {
final Post currentPost = postsList.get(position); final Post currentPost = postsList.get(position);
//Post's WebView parameters set //Post's WebView parameters
holder.post.setClickable(true); holder.post.setClickable(true);
holder.post.setWebViewClient(new LinkLauncher()); holder.post.setWebViewClient(new LinkLauncher());
holder.post.getSettings().setJavaScriptEnabled(true); holder.post.getSettings().setJavaScriptEnabled(true);
//Avoiding errors about layout having 0 width/height //Avoids errors about layout having 0 width/height
holder.thumbnail.setMinimumWidth(1); holder.thumbnail.setMinimumWidth(1);
holder.thumbnail.setMinimumHeight(1); holder.thumbnail.setMinimumHeight(1);
//Set thumbnail size //Sets thumbnail size
holder.thumbnail.setMaxWidth(THUMBNAIL_SIZE); holder.thumbnail.setMaxWidth(THUMBNAIL_SIZE);
holder.thumbnail.setMaxHeight(THUMBNAIL_SIZE); holder.thumbnail.setMaxHeight(THUMBNAIL_SIZE);
@ -165,42 +209,16 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
.transform(new CircleTransform()) .transform(new CircleTransform())
.into(holder.thumbnail); .into(holder.thumbnail);
//Username set //Sets username,submit date, index number, subject, post's and attached files texts
holder.username.setText(currentPost.getAuthor()); holder.username.setText(currentPost.getAuthor());
//Post's submit date set
holder.postDate.setText(currentPost.getPostDate()); holder.postDate.setText(currentPost.getPostDate());
//Post's index number set
if (currentPost.getPostNumber() != 0) if (currentPost.getPostNumber() != 0)
holder.postNum.setText(context.getString( holder.postNum.setText(context.getString(
R.string.user_number_of_posts, currentPost.getPostNumber())); R.string.user_number_of_posts, currentPost.getPostNumber()));
else else
holder.postNum.setText(""); holder.postNum.setText("");
//Post's subject set
holder.subject.setText(currentPost.getSubject()); holder.subject.setText(currentPost.getSubject());
//Post's text set
holder.post.loadDataWithBaseURL("file:///android_asset/", currentPost.getContent(), "text/html", "UTF-8", null); holder.post.loadDataWithBaseURL("file:///android_asset/", currentPost.getContent(), "text/html", "UTF-8", null);
holder.quoteToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (view.isSelected()) {
if (toQuoteList.contains(currentPost.getPostNumber())) {
toQuoteList.remove(toQuoteList.indexOf(currentPost.getPostNumber()));
view.setSelected(false);
} else
Report.i(TAG, "An error occurred while trying to exclude post from" +
"toQuoteList, post wasn't there!");
} else {
toQuoteList.add(currentPost.getPostNumber());
view.setSelected(true);
}
}
});
if (currentPost.getAttachedFiles().size() != 0) { if (currentPost.getAttachedFiles().size() != 0) {
holder.bodyFooterDivider.setVisibility(View.VISIBLE); holder.bodyFooterDivider.setVisibility(View.VISIBLE);
int filesTextColor; int filesTextColor;
@ -215,7 +233,7 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
attached.setClickable(true); attached.setClickable(true);
attached.setTypeface(Typeface.createFromAsset(context.getAssets() attached.setTypeface(Typeface.createFromAsset(context.getAssets()
, "fonts/fontawesome-webfont.ttf")); , "fonts/fontawesome-webfont.ttf"));
attached.setText(faIconFromExtension(attachedFile.getFilename()) + " " attached.setText(faIconFromFilename(attachedFile.getFilename()) + " "
+ attachedFile.getFilename() + attachedFile.getFileInfo()); + attachedFile.getFilename() + attachedFile.getFileInfo());
attached.setTextColor(filesTextColor); attached.setTextColor(filesTextColor);
attached.setPadding(0, 3, 0, 3); attached.setPadding(0, 3, 0, 3);
@ -235,94 +253,83 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
holder.postFooter.removeAllViews(); holder.postFooter.removeAllViews();
} }
//If user is not deleted then we have more to do if (!currentPost.isDeleted()) { //Sets user's extra info
if (!currentPost.isDeleted()) { //Set extra info String mSpecialRank = currentPost.getSpecialRank(), mRank = currentPost.getRank(), mGender = currentPost.getGender(), mNumberOfPosts = currentPost.getNumberOfPosts(), mPersonalText = currentPost.getPersonalText();
//Variables with content int mNumberOfStars = currentPost.getNumberOfStars(), mUserColor = currentPost.getUserColor();
String c_specialRank = currentPost.getSpecialRank(), c_rank = currentPost.getRank(), c_gender = currentPost.getGender(), c_numberOfPosts = currentPost.getNumberOfPosts(), c_personalText = currentPost.getPersonalText();
int c_numberOfStars = currentPost.getNumberOfStars(), c_userColor = currentPost.getUserColor();
if (!Objects.equals(c_specialRank, "") && c_specialRank != null) { if (!Objects.equals(mSpecialRank, "") && mSpecialRank != null) {
holder.specialRank.setText(c_specialRank); holder.specialRank.setText(mSpecialRank);
holder.specialRank.setVisibility(View.VISIBLE); holder.specialRank.setVisibility(View.VISIBLE);
} else } else
holder.specialRank.setVisibility(View.GONE); holder.specialRank.setVisibility(View.GONE);
if (!Objects.equals(c_rank, "") && c_rank != null) { if (!Objects.equals(mRank, "") && mRank != null) {
holder.rank.setText(c_rank); holder.rank.setText(mRank);
holder.rank.setVisibility(View.VISIBLE); holder.rank.setVisibility(View.VISIBLE);
} else } else
holder.rank.setVisibility(View.GONE); holder.rank.setVisibility(View.GONE);
if (!Objects.equals(c_gender, "") && c_gender != null) { if (!Objects.equals(mGender, "") && mGender != null) {
holder.gender.setText(c_gender); holder.gender.setText(mGender);
holder.gender.setVisibility(View.VISIBLE); holder.gender.setVisibility(View.VISIBLE);
} else } else
holder.gender.setVisibility(View.GONE); holder.gender.setVisibility(View.GONE);
if (!Objects.equals(c_numberOfPosts, "") && c_numberOfPosts != null) { if (!Objects.equals(mNumberOfPosts, "") && mNumberOfPosts != null) {
holder.numberOfPosts.setText(c_numberOfPosts); holder.numberOfPosts.setText(mNumberOfPosts);
holder.numberOfPosts.setVisibility(View.VISIBLE); holder.numberOfPosts.setVisibility(View.VISIBLE);
} else } else
holder.numberOfPosts.setVisibility(View.GONE); holder.numberOfPosts.setVisibility(View.GONE);
if (!Objects.equals(c_personalText, "") && c_personalText != null) { if (!Objects.equals(mPersonalText, "") && mPersonalText != null) {
holder.personalText.setText("\"" + c_personalText + "\""); holder.personalText.setText("\"" + mPersonalText + "\"");
holder.personalText.setVisibility(View.VISIBLE); holder.personalText.setVisibility(View.VISIBLE);
} else } else
holder.personalText.setVisibility(View.GONE); holder.personalText.setVisibility(View.GONE);
if (mNumberOfStars != 0) {
if (c_numberOfStars != 0) {
holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets() holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets()
, "fonts/fontawesome-webfont.ttf")); , "fonts/fontawesome-webfont.ttf"));
String aStar = context.getResources().getString(R.string.fa_icon_star); String aStar = context.getResources().getString(R.string.fa_icon_star);
String usersStars = ""; String usersStars = "";
for (int i = 0; i < c_numberOfStars; ++i) { for (int i = 0; i < mNumberOfStars; ++i) {
usersStars += aStar; usersStars += aStar;
} }
holder.stars.setText(usersStars); holder.stars.setText(usersStars);
holder.stars.setTextColor(c_userColor); holder.stars.setTextColor(mUserColor);
holder.stars.setVisibility(View.VISIBLE); holder.stars.setVisibility(View.VISIBLE);
} else } else
holder.stars.setVisibility(View.GONE); holder.stars.setVisibility(View.GONE);
/* --Header expand/collapse functionality-- */ //Avoid's view's visibility recycling
if (viewProperties.get(position)[isUserExtraInfoVisibile]) {
//Check if current post's header is expanded
if (viewProperties.get(position)[isUserExtraInfoVisibile]) { //Expanded
holder.userExtraInfo.setVisibility(View.VISIBLE); holder.userExtraInfo.setVisibility(View.VISIBLE);
holder.userExtraInfo.setAlpha(1.0f); holder.userExtraInfo.setAlpha(1.0f);
} else { //Collapsed } else {
holder.userExtraInfo.setVisibility(View.GONE); holder.userExtraInfo.setVisibility(View.GONE);
holder.userExtraInfo.setAlpha(0.0f); holder.userExtraInfo.setAlpha(0.0f);
} }
//Sets graphics behavior
holder.header.setOnClickListener(new View.OnClickListener() { holder.header.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (viewProperties.get(holder.getAdapterPosition())[isUserExtraInfoVisibile] && //Clicking an expanded header starts profile activity
!currentPost.isDeleted()) { if (viewProperties.get(holder.getAdapterPosition())[isUserExtraInfoVisibile]) {
Intent intent = new Intent(context, ProfileActivity.class); Intent intent = new Intent(context, ProfileActivity.class);
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putString(EXTRAS_PROFILE_URL, currentPost.getProfileURL()); //Profile url b.putString(EXTRAS_PROFILE_URL, currentPost.getProfileURL());
intent.putExtras(b); //Put url to next Intent intent.putExtras(b);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
//((Activity) context).overridePendingTransition(
//R.anim.push_right_in, R.anim.push_left_out);
} }
//Change post's viewProperties accordingly
boolean[] tmp = viewProperties.get(holder.getAdapterPosition()); boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
tmp[isUserExtraInfoVisibile] = !tmp[isUserExtraInfoVisibile]; tmp[isUserExtraInfoVisibile] = !tmp[isUserExtraInfoVisibile];
viewProperties.set(holder.getAdapterPosition(), tmp); viewProperties.set(holder.getAdapterPosition(), tmp);
TopicAnimations.animateUserExtraInfoVisibility(holder.userExtraInfo); TopicAnimations.animateUserExtraInfoVisibility(holder.userExtraInfo);
} }
}); });
//Clicking the expanded part of a header (the extra info) makes it collapse
//Clicking the expanded part of a header should collapse the extra info
holder.userExtraInfo.setOnClickListener(new View.OnClickListener() { holder.userExtraInfo.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
//Change post's viewProperties accordingly
boolean[] tmp = viewProperties.get(holder.getAdapterPosition()); boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
tmp[1] = false; tmp[1] = false;
viewProperties.set(holder.getAdapterPosition(), tmp); viewProperties.set(holder.getAdapterPosition(), tmp);
@ -330,12 +337,8 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
TopicAnimations.animateUserExtraInfoVisibility(v); TopicAnimations.animateUserExtraInfoVisibility(v);
} }
}); });
/* --Header expand/collapse functionality end-- */ }//End of deleted profiles
} //Avoid's view's visibility recycling
/* --Card expand/collapse functionality-- */
//Check if current post is expanded
if (viewProperties.get(position)[isPostDateAndNumberVisibile]) { //Expanded if (viewProperties.get(position)[isPostDateAndNumberVisibile]) { //Expanded
holder.postDateAndNumberExp.setVisibility(View.VISIBLE); holder.postDateAndNumberExp.setVisibility(View.VISIBLE);
holder.postDateAndNumberExp.setAlpha(1.0f); holder.postDateAndNumberExp.setAlpha(1.0f);
@ -359,8 +362,24 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
holder.subject.setMaxLines(1); holder.subject.setMaxLines(1);
holder.subject.setEllipsize(TextUtils.TruncateAt.END); holder.subject.setEllipsize(TextUtils.TruncateAt.END);
} }
//Sets graphics behavior
//Should expand/collapse when card is touched holder.quoteToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (view.isSelected()) {
if (toQuoteList.contains(currentPost.getPostNumber())) {
toQuoteList.remove(toQuoteList.indexOf(currentPost.getPostNumber()));
view.setSelected(false);
} else
Report.i(TAG, "An error occurred while trying to exclude post from" +
"toQuoteList, post wasn't there!");
} else {
toQuoteList.add(currentPost.getPostNumber());
view.setSelected(true);
}
}
});
//Card expand/collapse when card is touched
holder.cardView.setOnClickListener(new View.OnClickListener() { holder.cardView.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -375,10 +394,10 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
, Color.parseColor("#757575")); , Color.parseColor("#757575"));
} }
}); });
//Also when post is clicked //Also when post is clicked
holder.post.setOnTouchListener(new CustomTouchListener(holder.post, holder.cardView, holder.quoteToggle)); holder.post.setOnTouchListener(new CustomTouchListener(holder.post, holder.cardView, holder.quoteToggle));
//Focuses
if (postFocus != NO_POST_FOCUS && !foundPostFocus) { if (postFocus != NO_POST_FOCUS && !foundPostFocus) {
if (currentPost.getPostIndex() == postFocus) { if (currentPost.getPostIndex() == postFocus) {
holder.cardView.requestFocus(); holder.cardView.requestFocus();
@ -392,16 +411,13 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
return postsList.size(); return postsList.size();
} }
//--------------------------------------CUSTOM TOUCH LISTENER---------------------------------------
/** /**
* This class is a gesture detector for WebViews. * This class is a gesture detector for WebViews. It handles post's clicks, long clicks and
* It handles post's clicks, long clicks and touch and drag. * touch and drag.
*/ */
private class CustomTouchListener implements View.OnTouchListener { private class CustomTouchListener implements View.OnTouchListener {
//Long press handling //Long press handling
private final int LONG_PRESS_DURATION = 650; private final int LONG_PRESS_REQUIRED_DURATION = 650;
private final Handler webViewLongClickHandler = new Handler(); private final Handler webViewLongClickHandler = new Handler();
private boolean wasLongClick = false; private boolean wasLongClick = false;
private float downCoordinateX; private float downCoordinateX;
@ -434,65 +450,58 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
@Override @Override
public boolean onTouch(View view, MotionEvent motionEvent) { public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) { switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
//Logs XY
downCoordinateX = motionEvent.getX(); downCoordinateX = motionEvent.getX();
downCoordinateY = motionEvent.getY(); downCoordinateY = motionEvent.getY();
if (fingerState == FINGER_RELEASED) if (fingerState == FINGER_RELEASED)
fingerState = FINGER_TOUCHED; fingerState = FINGER_TOUCHED;
else else
fingerState = FINGER_UNDEFINED; fingerState = FINGER_UNDEFINED;
//Start long click runnable
webViewLongClickHandler.postDelayed(WebViewLongClick webViewLongClickHandler.postDelayed(WebViewLongClick
, LONG_PRESS_DURATION); , LONG_PRESS_REQUIRED_DURATION);
break; break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
webViewLongClickHandler.removeCallbacks(WebViewLongClick); webViewLongClickHandler.removeCallbacks(WebViewLongClick);
if (!wasLongClick && fingerState != FINGER_DRAGGING) { if (!wasLongClick && fingerState != FINGER_DRAGGING) {
//If this was a link don't expand the card //Doesn't expand the card if this was a link
WebView.HitTestResult htResult = post.getHitTestResult(); WebView.HitTestResult htResult = post.getHitTestResult();
if (htResult.getExtra() != null if (htResult.getExtra() != null
&& htResult.getExtra() != null) && htResult.getExtra() != null) {
fingerState = FINGER_RELEASED;
return false; return false;
//Else expand/collapse card }
cardView.performClick(); cardView.performClick();
} else } else
wasLongClick = false; wasLongClick = false;
fingerState = FINGER_RELEASED; fingerState = FINGER_RELEASED;
break; break;
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
//If finger moved too much, cancel long click //Cancels long click if finger moved too much
if (((Math.abs(downCoordinateX - motionEvent.getX()) > SCROLL_THRESHOLD || if (((Math.abs(downCoordinateX - motionEvent.getX()) > SCROLL_THRESHOLD ||
Math.abs(downCoordinateY - motionEvent.getY()) > SCROLL_THRESHOLD))) { Math.abs(downCoordinateY - motionEvent.getY()) > SCROLL_THRESHOLD))) {
webViewLongClickHandler.removeCallbacks(WebViewLongClick); webViewLongClickHandler.removeCallbacks(WebViewLongClick);
fingerState = FINGER_DRAGGING; fingerState = FINGER_DRAGGING;
} else fingerState = FINGER_UNDEFINED; } else fingerState = FINGER_UNDEFINED;
break; break;
default: default:
fingerState = FINGER_UNDEFINED; fingerState = FINGER_UNDEFINED;
} }
return false; return false;
} }
} }
//------------------------------------CUSTOM TOUCH LISTENER END-------------------------------------
//--------------------------------------CUSTOM WEBVIEW CLIENT---------------------------------------
/** /**
* This class is used to handle link clicks in WebViews. * This class is used to handle link clicks in WebViews. When link url is one that the app can
* When link url is one that the app can handle internally, it does. * handle internally, it does. Otherwise user is prompt to open the link in a browser.
* Otherwise user is prompt to open the link in a browser.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private class LinkLauncher extends WebViewClient { //Used to handle link clicks private class LinkLauncher extends WebViewClient {
//Older versions
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public boolean shouldOverrideUrlLoading(WebView view, String url) { public boolean shouldOverrideUrlLoading(WebView view, String url) {
@ -500,7 +509,6 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
return handleUri(uri); return handleUri(uri);
} }
//Newest versions
@TargetApi(Build.VERSION_CODES.N) @TargetApi(Build.VERSION_CODES.N)
@Override @Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
@ -508,32 +516,24 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
return handleUri(uri); return handleUri(uri);
} }
//Handle url clicks
@SuppressWarnings("SameReturnValue") @SuppressWarnings("SameReturnValue")
private boolean handleUri(final Uri uri) { private boolean handleUri(final Uri uri) {
//Method always returns true as we don't want any url to be loaded in WebViews final String host = uri.getHost();
Report.i(TAG, "Uri clicked = " + uri);
final String host = uri.getHost(); //Get requested url's host
final String uriString = uri.toString(); final String uriString = uri.toString();
//Determine if you are going to pass the url to a //Checks if app can handle this url
//host's application activity or load it in a browser.
if (Objects.equals(host, "www.thmmy.gr")) { if (Objects.equals(host, "www.thmmy.gr")) {
//This is my web site, so figure out what Activity should launch
if (uriString.contains("topic=")) { //This url points to a topic if (uriString.contains("topic=")) { //This url points to a topic
//Is the link pointing to current topic? //Checks if this is the current topic
if (Objects.equals( if (Objects.equals(uriString.substring(0, uriString.lastIndexOf(".")), base_url)) {
uriString.substring(0, uriString.lastIndexOf(".")), base_url)) { //Gets uri's targeted message's index number
//Get uri's targeted message's index number
String msgIndexReq = uriString.substring(uriString.indexOf("msg") + 3); String msgIndexReq = uriString.substring(uriString.indexOf("msg") + 3);
if (msgIndexReq.contains("#")) if (msgIndexReq.contains("#"))
msgIndexReq = msgIndexReq.substring(0, msgIndexReq.indexOf("#")); msgIndexReq = msgIndexReq.substring(0, msgIndexReq.indexOf("#"));
else else
msgIndexReq = msgIndexReq.substring(0, msgIndexReq.indexOf(";")); msgIndexReq = msgIndexReq.substring(0, msgIndexReq.indexOf(";"));
//Is this post already shown now? (is it in current page?) //Checks if this post is in the current topic's page
for (Post post : postsList) { for (Post post : postsList) {
if (post.getPostIndex() == Integer.parseInt(msgIndexReq)) { if (post.getPostIndex() == Integer.parseInt(msgIndexReq)) {
//Don't restart Activity //Don't restart Activity
@ -548,18 +548,25 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
} }
return true; return true;
} }
//Otherwise, the link is not for a page on my site, so launch
//another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, uri); Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
//Method always returns true as no url should be loaded in the WebViews
return true; return true;
} }
} }
//------------------------------------CUSTOM WEBVIEW CLIENT END-------------------------------------
/**
* Returns a String with a single FontAwesome typeface character corresponding to this file's
* extension.
*
* @param filename String with filename <b>containing file's extension</b>
* @return FontAwesome character according to file's type
* @see <a href="http://fontawesome.io/">FontAwesome</a>
*/
@NonNull @NonNull
private String faIconFromExtension(String filename) { private String faIconFromFilename(String filename) {
filename = filename.toLowerCase(); filename = filename.toLowerCase();
if (filename.contains("jpg") || filename.contains("gif") || filename.contains("jpeg") if (filename.contains("jpg") || filename.contains("gif") || filename.contains("jpeg")
@ -583,30 +590,54 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
return context.getResources().getString(R.string.fa_file); return context.getResources().getString(R.string.fa_file);
} }
private class DownloadTask extends AsyncTask<ThmmyFile, Void, Void> { private class DownloadTask extends AsyncTask<ThmmyFile, Void, String> {
//Class variables //Class variables
/** /**
* Debug Tag for logging debug output to LogCat * Debug Tag for logging debug output to LogCat
*/ */
private static final String TAG = "DownloadTask"; //Separate tag for AsyncTask private static final String TAG = "DownloadTask"; //Separate tag for AsyncTask
private PowerManager.WakeLock mWakeLock;
protected Void doInBackground(ThmmyFile... files) { @Override
protected void onPreExecute() {
super.onPreExecute();
//Locks CPU to prevent going off
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
progressBar.setVisibility(View.VISIBLE);
}
@Override
protected String doInBackground(ThmmyFile... files) {
try { try {
File tmpFile = files[0].download(PACKAGE_NAME); File tempFile = files[0].download(PACKAGE_NAME);
if (tmpFile != null) { if (tempFile != null) {
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension( String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
files[0].getExtension()); files[0].getExtension());
Intent intent = new Intent(); Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW); intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(tmpFile), mime); intent.setDataAndType(Uri.fromFile(tempFile), mime);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
} }
} catch (IOException e) { } catch (IOException e) {
Report.e(TAG, "Error while trying to download a file", e); Report.e(TAG, "Error while trying to download a file", e);
return e.toString();
} }
return null; return null;
} }
@Override
protected void onPostExecute(String result) {
mWakeLock.release();
if (result != null)
Toast.makeText(context, "Error! Download not complete.", Toast.LENGTH_SHORT).show();
else
Toast.makeText(context, "Download complete", Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.INVISIBLE);
}
} }
} }

3
app/src/main/java/gr/thmmy/mthmmy/utils/FileManager/ThmmyFile.java

@ -162,8 +162,7 @@ public class ThmmyFile {
// using Environment.getExternalStorageState() before doing this. // using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStorageDirectory() File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
+ "/Android/data/gr.thmmy.mthmmy/" + "/Android/data/gr.thmmy.mthmmy/"
+ "Downloads/" + "Downloads/");
+ packageName);
// This location works best if you want the created files to be shared // This location works best if you want the created files to be shared
// between applications and persist after your app has been uninstalled. // between applications and persist after your app has been uninstalled.

9
app/src/main/res/layout/activity_profile.xml

@ -72,16 +72,17 @@
</android.support.v7.widget.Toolbar> </android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<ProgressBar <me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progressBar" android:id="@+id/progressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:indeterminate="true" android:indeterminate="true"
android:theme="@style/AppTheme"
android:visibility="invisible" android:visibility="invisible"
app:layout_anchor="@id/appbar" app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|center"/> app:layout_anchorGravity="bottom|center"
app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/>
<android.support.v4.widget.NestedScrollView <android.support.v4.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"

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

@ -93,15 +93,15 @@
<me.zhanghai.android.materialprogressbar.MaterialProgressBar <me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progressBar" android:id="@+id/progressBar"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="invisible"
app:layout_anchor="@id/appbar" app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|center" app:layout_anchorGravity="bottom|center"
app:mpb_indeterminateTint="@color/accent" app:mpb_indeterminateTint="@color/accent"
android:visibility="invisible" app:mpb_progressStyle="horizontal"/>
android:indeterminate="true"
app:mpb_progressStyle="horizontal"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding" />
<android.support.design.widget.FloatingActionButton <android.support.design.widget.FloatingActionButton
android:id="@+id/topic_fab" android:id="@+id/topic_fab"

Loading…
Cancel
Save